term.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: term.c,v $
00036  * Revision 1.7  2008/08/11 06:59:42  haraldkipp
00037  * BSD types replaced by stdint types (feature request #1282721).
00038  *
00039  * Revision 1.6  2005/08/02 17:46:47  haraldkipp
00040  * Major API documentation update.
00041  *
00042  * Revision 1.5  2004/05/24 17:11:05  olereinhardt
00043  * dded terminal device driver for hd44780 compatible LCD displays directly
00044  * connected to the memory bus (memory mapped). See hd44780.c for more
00045  * information.Therefore some minor changed in include/dev/term.h and
00046  * dev/term.c are needet to
00047  * pass a base address to the lcd driver.
00048  *
00049  * Revision 1.4  2004/03/18 18:30:11  haraldkipp
00050  * Added Michael Fischer's TIOCGWINSZ ioctl
00051  *
00052  * Revision 1.3  2004/03/18 14:02:46  haraldkipp
00053  * Comments updated
00054  *
00055  * Revision 1.2  2004/03/16 16:48:27  haraldkipp
00056  * Added Jan Dubiec's H8/300 port.
00057  *
00058  * Revision 1.1.1.1  2003/05/09 14:40:52  haraldkipp
00059  * Initial using 3.2.1
00060  *
00061  * Revision 1.3  2003/05/06 18:34:22  harald
00062  * Cleanup
00063  *
00064  * Revision 1.2  2003/04/21 16:25:24  harald
00065  * Release prep
00066  *
00067  * Revision 1.1  2003/03/31 14:53:08  harald
00068  * Prepare release 3.1
00069  *
00070  */
00071 
00072 #include <dev/term.h>
00073 
00074 #include <stdlib.h>
00075 #include <string.h>
00076 #include <fcntl.h>
00077 
00083 
00084 static prog_char termid[] = "Term 1.0";
00085 
00086 static void TermRefreshLineEnd(CONST TERMDCB * dcb, uint8_t row, uint8_t col)
00087 {
00088     uint8_t i = col;
00089     uint8_t *cp = dcb->dcb_smem + row * dcb->dcb_vcols + col;
00090 
00091     /* Disable cursor to avoid flickering. */
00092     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00093         (*dcb->dss_cursor_mode) (0);
00094 
00095     /* Position cursor to the rwo and column to refresh. */
00096     (*dcb->dss_set_cursor) (row * dcb->dcb_ncols + col);
00097 
00098     /*
00099      * This loop looks weird. But it was the only way I found to get 
00100      * around a GCC bug in reload1.c:1920.
00101      */
00102     for (;;) {
00103         if (i++ >= dcb->dcb_vcols)
00104             break;
00105         (*dcb->dss_write) (*cp++);
00106     }
00107 
00108     /* Re-enable cursor. */
00109     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00110         (*dcb->dss_cursor_mode) (1);
00111 }
00112 
00113 void TermRefresh(TERMDCB * dcb)
00114 {
00115     uint8_t ir;
00116 
00117     for (ir = 0; ir < dcb->dcb_nrows; ir++)
00118         TermRefreshLineEnd(dcb, ir, 0);
00119     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00120 }
00121 
00122 static void TermClear(TERMDCB * dcb)
00123 {
00124     memset(dcb->dcb_smem, ' ', dcb->dcb_vcols * dcb->dcb_nrows);
00125     dcb->dcb_col = 0;
00126     dcb->dcb_row = 0;
00127     (*dcb->dss_clear) ();
00128 }
00129 
00130 static void TermDeleteLine(TERMDCB * dcb, uint8_t row)
00131 {
00132     uint8_t i;
00133     uint8_t *dcp;
00134 
00135     for (i = row; i < dcb->dcb_nrows - 1; i++) {
00136         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00137         memcpy(dcp, dcp + dcb->dcb_vcols, dcb->dcb_vcols);
00138     }
00139     memset(dcb->dcb_smem + (dcb->dcb_nrows - 1) * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00140     TermRefresh(dcb);
00141 }
00142 
00143 static void TermInsertLine(TERMDCB * dcb, uint8_t row)
00144 {
00145     uint8_t i;
00146     uint8_t *dcp;
00147 
00148     for (i = dcb->dcb_nrows - 1; i > row; i--) {
00149         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00150         memcpy(dcp, dcp - dcb->dcb_vcols, dcb->dcb_vcols);
00151     }
00152     memset(dcb->dcb_smem + row * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00153     TermRefresh(dcb);
00154 }
00155 
00156 static void TermCursorLeft(TERMDCB * dcb)
00157 {
00158     if (dcb->dcb_col) {
00159         (*dcb->dss_cursor_left) ();
00160         dcb->dcb_col--;
00161     }
00162 }
00163 
00164 static void TermCursorRight(TERMDCB * dcb)
00165 {
00166     if (++dcb->dcb_col < dcb->dcb_vcols)
00167         (*dcb->dss_cursor_right) ();
00168     else
00169         dcb->dcb_col = dcb->dcb_vcols - 1;
00170 }
00171 
00172 static void TermCursorUp(TERMDCB * dcb)
00173 {
00174     if (dcb->dcb_row) {
00175         dcb->dcb_row--;
00176         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00177     }
00178 }
00179 
00180 static void TermLinefeed(TERMDCB * dcb)
00181 {
00182     if (++dcb->dcb_row >= dcb->dcb_nrows) {
00183         dcb->dcb_row = dcb->dcb_nrows - 1;
00184         TermDeleteLine(dcb, 0);
00185     } else
00186         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00187 }
00188 
00189 static void TermReverseLinefeed(TERMDCB * dcb)
00190 {
00191     if (dcb->dcb_row--)
00192         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00193     else {
00194         dcb->dcb_row = 0;
00195         TermInsertLine(dcb, 0);
00196     }
00197 }
00198 
00199 static void TermEraseLineEnd(TERMDCB * dcb, uint8_t col)
00200 {
00201     if (col < dcb->dcb_vcols) {
00202         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col, ' ', dcb->dcb_vcols - col);
00203         TermRefresh(dcb);
00204     }
00205 }
00206 
00207 static void TermEraseEnd(TERMDCB * dcb)
00208 {
00209     uint8_t i;
00210 
00211     if (dcb->dcb_col < dcb->dcb_vcols)
00212         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col, ' ', dcb->dcb_vcols - dcb->dcb_col);
00213     for (i = dcb->dcb_row + 1; i < dcb->dcb_nrows; i++)
00214         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00215     TermRefresh(dcb);
00216 }
00217 
00218 static void TermEraseLineStart(TERMDCB * dcb)
00219 {
00220     if (dcb->dcb_col) {
00221         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00222         TermRefresh(dcb);
00223     }
00224 }
00225 
00226 static void TermEraseStart(TERMDCB * dcb)
00227 {
00228     uint8_t i;
00229 
00230     if (dcb->dcb_col)
00231         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00232     for (i = 0; i < dcb->dcb_row; i++)
00233         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00234     TermRefresh(dcb);
00235 }
00236 
00237 static void TermDeleteChar(TERMDCB * dcb, uint8_t col)
00238 {
00239     uint8_t i;
00240     uint8_t *cp = dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col;
00241 
00242     for (i = col; i < dcb->dcb_vcols - 1; i++, cp++)
00243         *cp = *(cp + 1);
00244     *cp = ' ';
00245     TermRefresh(dcb);
00246 }
00247 
00248 /*
00249  * Insert a space at the cursor position.
00250  */
00251 static void TermInsertSpace(TERMDCB * dcb)
00252 {
00253     uint8_t i;
00254     uint8_t *cp = dcb->dcb_smem + (dcb->dcb_row + 1) * dcb->dcb_vcols - 1;
00255 
00256     for (i = dcb->dcb_col; i < dcb->dcb_vcols - 1; i++, cp--)
00257         *cp = *(cp - 1);
00258     *cp = ' ';
00259     TermRefreshLineEnd(dcb, dcb->dcb_row, dcb->dcb_col);
00260     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00261 }
00262 
00263 /*
00264  * Clear display and print identification.
00265  */
00266 static void TermIdentify(TERMDCB * dcb)
00267 {
00268     PGM_P pcp = termid;
00269 
00270     TermClear(dcb);
00271     while (PRG_RDB(pcp)) {
00272         (*dcb->dss_write) (PRG_RDB(pcp));
00273         pcp++;
00274     }
00275 }
00276 
00314 int TermIOCtl(NUTDEVICE * dev, int req, void *conf)
00315 {
00316     TERMDCB *dcb = dev->dev_dcb;
00317     uint16_t usv;
00318     uint32_t ulv;
00319     WINSIZE *win_size;
00320 
00321     switch (req) {
00322     case LCD_CMDBYTE:
00323         (*dcb->dss_command) (*(uint8_t *)conf, 10);
00324         break;
00325     case LCD_CMDWORD16:
00326         usv = *(uint16_t *)conf;
00327         (*dcb->dss_command) ((uint8_t)(usv >> 8), 10);
00328         (*dcb->dss_command) ((uint8_t)usv, 10);
00329         break;
00330     case LCD_CMDWORD32:
00331         ulv = *(uint32_t *)conf;
00332         (*dcb->dss_command) ((uint8_t)(ulv >> 24), 10);
00333         (*dcb->dss_command) ((uint8_t)(ulv >> 16), 10);
00334         (*dcb->dss_command) ((uint8_t)(ulv >> 8), 10);
00335         (*dcb->dss_command) ((uint8_t)ulv, 10);
00336         break;
00337     case LCD_DATABYTE:
00338         (*dcb->dss_write) (*(uint8_t *)conf);
00339         break;
00340     case LCD_DATAWORD16:
00341         usv = *(uint16_t *)conf;
00342         (*dcb->dss_write) ((uint8_t)(usv >> 8));
00343         (*dcb->dss_write) ((uint8_t)usv);
00344         break;
00345     case LCD_DATAWORD32:
00346         ulv = *(uint32_t *)conf;
00347         (*dcb->dss_write) ((uint8_t)(ulv >> 24));
00348         (*dcb->dss_write) ((uint8_t)(ulv >> 16));
00349         (*dcb->dss_write) ((uint8_t)(ulv >> 8));
00350         (*dcb->dss_write) ((uint8_t)ulv);
00351         break;
00352     case LCD_SETCOOKEDMODE:
00353         if (*(uint32_t *)conf)
00354             dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00355         else
00356             dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00357         break;
00358     case LCD_GETCOOKEDMODE:
00359         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE)
00360             *(uint32_t *)conf = 1;
00361         else
00362             *(uint32_t *)conf = 0;
00363         break;
00364     case TIOCGWINSZ:
00365         win_size = (WINSIZE *)conf;
00366         win_size->ws_col    = dcb->dcb_nrows;
00367         win_size->ws_row    = dcb->dcb_vcols;
00368         win_size->ws_xpixel = 0;
00369         win_size->ws_ypixel = 0;
00370         break;
00371     }
00372     return 0;
00373 }
00374 
00375 
00388 int TermInit(NUTDEVICE * dev)
00389 {
00390     TERMDCB *dcb = dev->dev_dcb;
00391 
00392     /*
00393      * Initialize the display hardware.
00394      */
00395     (*dcb->dss_init) (dev);
00396 
00397     /*
00398      * Initialize driver control block.
00399      */
00400     dcb->dcb_smem = malloc(dcb->dcb_nrows * dcb->dcb_vcols);
00401     TermClear(dcb);
00402 
00403     return 0;
00404 }
00405 
00417 static int TermPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00418 {
00419     int rc;
00420     CONST uint8_t *cp;
00421     uint8_t ch;
00422     TERMDCB *dcb = dev->dev_dcb;
00423 
00424     /*
00425      * Call without data pointer is accepted.
00426      */
00427     if (buffer == 0)
00428         return 0;
00429 
00430     /*
00431      * Put characters in transmit buffer.
00432      */
00433     cp = buffer;
00434     for (rc = 0; rc < len; cp++, rc++) {
00435         ch = pflg ? PRG_RDB(cp) : *cp;
00436 
00437         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00438             /* Process special characters. */
00439             if (dcb->dcb_ctlseq == 0) {
00440                 /* Linefeed. */
00441                 if (ch == 10) {
00442                     dcb->dcb_col = 0;
00443                     TermLinefeed(dcb);
00444                     continue;
00445                 }
00446 
00447                 /* Carriage return. */
00448                 if (ch == 13) {
00449                     dcb->dcb_col = 0;
00450                     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols);
00451                     continue;
00452                 }
00453 
00454                 /* Escape. */
00455                 if (ch == 27) {
00456                     dcb->dcb_ctlseq = 1;
00457                     continue;
00458                 }
00459 
00460                 /* Backspace. */
00461                 if (ch == 8) {
00462                     if (dcb->dcb_col) {
00463                         dcb->dcb_col--;
00464                         TermDeleteChar(dcb, dcb->dcb_col);
00465                     }
00466                     continue;
00467                 }
00468 
00469                 /* Formfeed. */
00470                 if (ch == 12) {
00471                     TermClear(dcb);
00472                     continue;
00473                 }
00474             }
00475 
00476             /* Last character was ESC. */
00477             if (dcb->dcb_ctlseq == 1) {
00478                 dcb->dcb_ctlseq = 0;
00479 
00480                 switch (ch) {
00481                     /* Insert space. */
00482                 case '@':
00483                     TermInsertSpace(dcb);
00484                     break;
00485 
00486                     /* Cursor up. */
00487                 case 'A':
00488                     TermCursorUp(dcb);
00489                     break;
00490 
00491                     /* Cursor down. */
00492                 case 'B':
00493                     if (++dcb->dcb_row >= dcb->dcb_nrows)
00494                         dcb->dcb_row = dcb->dcb_nrows - 1;
00495                     else
00496                         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00497                     break;
00498 
00499                     /* Cursor right. */
00500                 case 'C':
00501                     TermCursorRight(dcb);
00502                     break;
00503 
00504                     /* Cursor left. */
00505                 case 'D':
00506                     TermCursorLeft(dcb);
00507                     break;
00508 
00509                     /* Clear screen and cursor home. */
00510                 case 'E':
00511                     TermClear(dcb);
00512                     break;
00513 
00514                     /* Cursor home. */
00515                 case 'H':
00516                     dcb->dcb_col = 0;
00517                     dcb->dcb_row = 0;
00518                     (*dcb->dss_cursor_home) ();
00519                     break;
00520 
00521                     /* Reverse linefeed. */
00522                 case 'I':
00523                     TermReverseLinefeed(dcb);
00524                     break;
00525 
00526                     /* Erase to end of screen. */
00527                 case 'J':
00528                     TermEraseEnd(dcb);
00529                     break;
00530 
00531                     /* Erase to end of line. */
00532                 case 'K':
00533                     TermEraseLineEnd(dcb, dcb->dcb_col);
00534                     break;
00535 
00536                     /* Insert line. */
00537                 case 'L':
00538                     TermInsertLine(dcb, dcb->dcb_row);
00539                     break;
00540 
00541                     /* Delete line. */
00542                 case 'M':
00543                     TermDeleteLine(dcb, dcb->dcb_row);
00544                     break;
00545 
00546                     /* Delete character. */
00547                 case 'P':
00548                     TermDeleteChar(dcb, dcb->dcb_col);
00549                     break;
00550 
00551                     /* Cursor position. */
00552                 case 'Y':
00553                     dcb->dcb_ctlseq = 2;
00554                     break;
00555 
00556                     /* Identify. */
00557                 case 'Z':
00558                     TermIdentify(dcb);
00559                     break;
00560 
00561                     /* Cursor on. */
00562                 case 'e':
00563                     dcb->dcb_modeflags |= LCD_MF_CURSORON;
00564                     (*dcb->dss_cursor_mode) (1);
00565                     break;
00566 
00567                     /* Cursor off. */
00568                 case 'f':
00569                     dcb->dcb_modeflags &= ~LCD_MF_CURSORON;
00570                     (*dcb->dss_cursor_mode) (0);
00571                     break;
00572 
00573                     /* Erase to start of screen. */
00574                 case 'd':
00575                     TermEraseStart(dcb);
00576                     break;
00577 
00578                     /* Erase to start of line. */
00579                 case 'o':
00580                     TermEraseLineStart(dcb);
00581                     break;
00582                 }
00583                 continue;
00584             }
00585 
00586             /* Receive cursor row position. */
00587             if (dcb->dcb_ctlseq == 2) {
00588                 dcb->dcb_ctlseq = 3;
00589                 if (ch < 32)
00590                     dcb->dcb_row = 0;
00591                 else if (ch - 32 >= dcb->dcb_nrows)
00592                     dcb->dcb_row = dcb->dcb_nrows - 1;
00593                 else
00594                     dcb->dcb_row = ch - 32;
00595                 continue;
00596             }
00597 
00598             /* Receive cursor column position. */
00599             if (dcb->dcb_ctlseq == 3) {
00600                 dcb->dcb_ctlseq = 0;
00601                 if (ch < 32)
00602                     dcb->dcb_col = 0;
00603                 else if (ch - 32 >= dcb->dcb_vcols)
00604                     dcb->dcb_col = dcb->dcb_vcols - 1;
00605                 else
00606                     dcb->dcb_col = ch - 32;
00607                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00608                 continue;
00609             }
00610         }
00611 
00612         /* 
00613          * Send any character to the LCD driver, which had been left 
00614          * unprocessed upto this point.
00615          */
00616         (*dcb->dss_write) (ch);
00617 
00618         /* Update shadow memory. */
00619         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00620             *(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col) = ch;
00621             if (++dcb->dcb_col >= dcb->dcb_vcols) {
00622                 dcb->dcb_col = dcb->dcb_vcols - 1;
00623                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00624             }
00625         }
00626     }
00627     return rc;
00628 }
00629 
00681 int TermWrite(NUTFILE * fp, CONST void *buffer, int len)
00682 {
00683     return TermPut(fp->nf_dev, buffer, len, 0);
00684 }
00685 
00700 #ifdef __HARVARD_ARCH__
00701 int TermWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00702 {
00703     return TermPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00704 }
00705 #endif
00706 
00724 NUTFILE *TermOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00725 {
00726     TERMDCB *dcb = dev->dev_dcb;
00727     NUTFILE *fp = malloc(sizeof(NUTFILE));
00728 
00729     if (fp == 0)
00730         return NUTFILE_EOF;
00731 
00732     if (mode & _O_BINARY)
00733         dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00734     else
00735         dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00736     fp->nf_next = 0;
00737     fp->nf_dev = dev;
00738     fp->nf_fcb = 0;
00739 
00740     return fp;
00741 }
00742 
00753 int TermClose(NUTFILE * fp)
00754 {
00755     if (fp && fp != NUTFILE_EOF) {
00756         free(fp);
00757         return 0;
00758     }
00759     return -1;
00760 }
00761 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/