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.6  2005/08/02 17:46:47  haraldkipp
00037  * Major API documentation update.
00038  *
00039  * Revision 1.5  2004/05/24 17:11:05  olereinhardt
00040  * dded terminal device driver for hd44780 compatible LCD displays directly
00041  * connected to the memory bus (memory mapped). See hd44780.c for more
00042  * information.Therefore some minor changed in include/dev/term.h and
00043  * dev/term.c are needet to
00044  * pass a base address to the lcd driver.
00045  *
00046  * Revision 1.4  2004/03/18 18:30:11  haraldkipp
00047  * Added Michael Fischer's TIOCGWINSZ ioctl
00048  *
00049  * Revision 1.3  2004/03/18 14:02:46  haraldkipp
00050  * Comments updated
00051  *
00052  * Revision 1.2  2004/03/16 16:48:27  haraldkipp
00053  * Added Jan Dubiec's H8/300 port.
00054  *
00055  * Revision 1.1.1.1  2003/05/09 14:40:52  haraldkipp
00056  * Initial using 3.2.1
00057  *
00058  * Revision 1.3  2003/05/06 18:34:22  harald
00059  * Cleanup
00060  *
00061  * Revision 1.2  2003/04/21 16:25:24  harald
00062  * Release prep
00063  *
00064  * Revision 1.1  2003/03/31 14:53:08  harald
00065  * Prepare release 3.1
00066  *
00067  */
00068 
00069 #include <dev/term.h>
00070 
00071 #include <stdlib.h>
00072 #include <string.h>
00073 #include <fcntl.h>
00074 
00080 
00081 static prog_char termid[] = "Term 1.0";
00082 
00083 static void TermRefreshLineEnd(CONST TERMDCB * dcb, u_char row, u_char col)
00084 {
00085     u_char i = col;
00086     u_char *cp = dcb->dcb_smem + row * dcb->dcb_vcols + col;
00087 
00088     /* Disable cursor to avoid flickering. */
00089     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00090         (*dcb->dss_cursor_mode) (0);
00091 
00092     /* Position cursor to the rwo and column to refresh. */
00093     (*dcb->dss_set_cursor) (row * dcb->dcb_ncols + col);
00094 
00095     /*
00096      * This loop looks weird. But it was the only way I found to get 
00097      * around a GCC bug in reload1.c:1920.
00098      */
00099     for (;;) {
00100         if (i++ >= dcb->dcb_vcols)
00101             break;
00102         (*dcb->dss_write) (*cp++);
00103     }
00104 
00105     /* Re-enable cursor. */
00106     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00107         (*dcb->dss_cursor_mode) (1);
00108 }
00109 
00110 void TermRefresh(TERMDCB * dcb)
00111 {
00112     u_char ir;
00113 
00114     for (ir = 0; ir < dcb->dcb_nrows; ir++)
00115         TermRefreshLineEnd(dcb, ir, 0);
00116     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00117 }
00118 
00119 static void TermClear(TERMDCB * dcb)
00120 {
00121     memset(dcb->dcb_smem, ' ', dcb->dcb_vcols * dcb->dcb_nrows);
00122     dcb->dcb_col = 0;
00123     dcb->dcb_row = 0;
00124     (*dcb->dss_clear) ();
00125 }
00126 
00127 static void TermDeleteLine(TERMDCB * dcb, u_char row)
00128 {
00129     u_char i;
00130     u_char *dcp;
00131 
00132     for (i = row; i < dcb->dcb_nrows - 1; i++) {
00133         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00134         memcpy(dcp, dcp + dcb->dcb_vcols, dcb->dcb_vcols);
00135     }
00136     memset(dcb->dcb_smem + (dcb->dcb_nrows - 1) * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00137     TermRefresh(dcb);
00138 }
00139 
00140 static void TermInsertLine(TERMDCB * dcb, u_char row)
00141 {
00142     u_char i;
00143     u_char *dcp;
00144 
00145     for (i = dcb->dcb_nrows - 1; i > row; i--) {
00146         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00147         memcpy(dcp, dcp - dcb->dcb_vcols, dcb->dcb_vcols);
00148     }
00149     memset(dcb->dcb_smem + row * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00150     TermRefresh(dcb);
00151 }
00152 
00153 static void TermCursorLeft(TERMDCB * dcb)
00154 {
00155     if (dcb->dcb_col) {
00156         (*dcb->dss_cursor_left) ();
00157         dcb->dcb_col--;
00158     }
00159 }
00160 
00161 static void TermCursorRight(TERMDCB * dcb)
00162 {
00163     if (++dcb->dcb_col < dcb->dcb_vcols)
00164         (*dcb->dss_cursor_right) ();
00165     else
00166         dcb->dcb_col = dcb->dcb_vcols - 1;
00167 }
00168 
00169 static void TermCursorUp(TERMDCB * dcb)
00170 {
00171     if (dcb->dcb_row) {
00172         dcb->dcb_row--;
00173         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00174     }
00175 }
00176 
00177 static void TermLinefeed(TERMDCB * dcb)
00178 {
00179     if (++dcb->dcb_row >= dcb->dcb_nrows) {
00180         dcb->dcb_row = dcb->dcb_nrows - 1;
00181         TermDeleteLine(dcb, 0);
00182     } else
00183         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00184 }
00185 
00186 static void TermReverseLinefeed(TERMDCB * dcb)
00187 {
00188     if (dcb->dcb_row--)
00189         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00190     else {
00191         dcb->dcb_row = 0;
00192         TermInsertLine(dcb, 0);
00193     }
00194 }
00195 
00196 static void TermEraseLineEnd(TERMDCB * dcb, u_char col)
00197 {
00198     if (col < dcb->dcb_vcols) {
00199         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col, ' ', dcb->dcb_vcols - col);
00200         TermRefresh(dcb);
00201     }
00202 }
00203 
00204 static void TermEraseEnd(TERMDCB * dcb)
00205 {
00206     u_char i;
00207 
00208     if (dcb->dcb_col < dcb->dcb_vcols)
00209         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col, ' ', dcb->dcb_vcols - dcb->dcb_col);
00210     for (i = dcb->dcb_row + 1; i < dcb->dcb_nrows; i++)
00211         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00212     TermRefresh(dcb);
00213 }
00214 
00215 static void TermEraseLineStart(TERMDCB * dcb)
00216 {
00217     if (dcb->dcb_col) {
00218         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00219         TermRefresh(dcb);
00220     }
00221 }
00222 
00223 static void TermEraseStart(TERMDCB * dcb)
00224 {
00225     u_char i;
00226 
00227     if (dcb->dcb_col)
00228         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00229     for (i = 0; i < dcb->dcb_row; i++)
00230         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00231     TermRefresh(dcb);
00232 }
00233 
00234 static void TermDeleteChar(TERMDCB * dcb, u_char col)
00235 {
00236     u_char i;
00237     u_char *cp = dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col;
00238 
00239     for (i = col; i < dcb->dcb_vcols - 1; i++, cp++)
00240         *cp = *(cp + 1);
00241     *cp = ' ';
00242     TermRefresh(dcb);
00243 }
00244 
00245 /*
00246  * Insert a space at the cursor position.
00247  */
00248 static void TermInsertSpace(TERMDCB * dcb)
00249 {
00250     u_char i;
00251     u_char *cp = dcb->dcb_smem + (dcb->dcb_row + 1) * dcb->dcb_vcols - 1;
00252 
00253     for (i = dcb->dcb_col; i < dcb->dcb_vcols - 1; i++, cp--)
00254         *cp = *(cp - 1);
00255     *cp = ' ';
00256     TermRefreshLineEnd(dcb, dcb->dcb_row, dcb->dcb_col);
00257     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00258 }
00259 
00260 /*
00261  * Clear display and print identification.
00262  */
00263 static void TermIdentify(TERMDCB * dcb)
00264 {
00265     PGM_P pcp = termid;
00266 
00267     TermClear(dcb);
00268     while (PRG_RDB(pcp)) {
00269         (*dcb->dss_write) (PRG_RDB(pcp));
00270         pcp++;
00271     }
00272 }
00273 
00311 int TermIOCtl(NUTDEVICE * dev, int req, void *conf)
00312 {
00313     TERMDCB *dcb = dev->dev_dcb;
00314     u_short usv;
00315     u_long ulv;
00316     WINSIZE *win_size;
00317 
00318     switch (req) {
00319     case LCD_CMDBYTE:
00320         (*dcb->dss_command) (*(u_char *)conf, 10);
00321         break;
00322     case LCD_CMDWORD16:
00323         usv = *(u_short *)conf;
00324         (*dcb->dss_command) ((u_char)(usv >> 8), 10);
00325         (*dcb->dss_command) ((u_char)usv, 10);
00326         break;
00327     case LCD_CMDWORD32:
00328         ulv = *(u_long *)conf;
00329         (*dcb->dss_command) ((u_char)(ulv >> 24), 10);
00330         (*dcb->dss_command) ((u_char)(ulv >> 16), 10);
00331         (*dcb->dss_command) ((u_char)(ulv >> 8), 10);
00332         (*dcb->dss_command) ((u_char)ulv, 10);
00333         break;
00334     case LCD_DATABYTE:
00335         (*dcb->dss_write) (*(u_char *)conf);
00336         break;
00337     case LCD_DATAWORD16:
00338         usv = *(u_short *)conf;
00339         (*dcb->dss_write) ((u_char)(usv >> 8));
00340         (*dcb->dss_write) ((u_char)usv);
00341         break;
00342     case LCD_DATAWORD32:
00343         ulv = *(u_long *)conf;
00344         (*dcb->dss_write) ((u_char)(ulv >> 24));
00345         (*dcb->dss_write) ((u_char)(ulv >> 16));
00346         (*dcb->dss_write) ((u_char)(ulv >> 8));
00347         (*dcb->dss_write) ((u_char)ulv);
00348         break;
00349     case LCD_SETCOOKEDMODE:
00350         if (*(u_long *)conf)
00351             dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00352         else
00353             dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00354         break;
00355     case LCD_GETCOOKEDMODE:
00356         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE)
00357             *(u_long *)conf = 1;
00358         else
00359             *(u_long *)conf = 0;
00360         break;
00361     case TIOCGWINSZ:
00362         win_size = (WINSIZE *)conf;
00363         win_size->ws_col    = dcb->dcb_nrows;
00364         win_size->ws_row    = dcb->dcb_vcols;
00365         win_size->ws_xpixel = 0;
00366         win_size->ws_ypixel = 0;
00367         break;
00368     }
00369     return 0;
00370 }
00371 
00372 
00385 int TermInit(NUTDEVICE * dev)
00386 {
00387     TERMDCB *dcb = dev->dev_dcb;
00388 
00389     /*
00390      * Initialize the display hardware.
00391      */
00392     (*dcb->dss_init) (dev);
00393 
00394     /*
00395      * Initialize driver control block.
00396      */
00397     dcb->dcb_smem = malloc(dcb->dcb_nrows * dcb->dcb_vcols);
00398     TermClear(dcb);
00399 
00400     return 0;
00401 }
00402 
00414 static int TermPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00415 {
00416     int rc;
00417     CONST u_char *cp;
00418     u_char ch;
00419     TERMDCB *dcb = dev->dev_dcb;
00420 
00421     /*
00422      * Call without data pointer is accepted.
00423      */
00424     if (buffer == 0)
00425         return 0;
00426 
00427     /*
00428      * Put characters in transmit buffer.
00429      */
00430     cp = buffer;
00431     for (rc = 0; rc < len; cp++, rc++) {
00432         ch = pflg ? PRG_RDB(cp) : *cp;
00433 
00434         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00435             /* Process special characters. */
00436             if (dcb->dcb_ctlseq == 0) {
00437                 /* Linefeed. */
00438                 if (ch == 10) {
00439                     dcb->dcb_col = 0;
00440                     TermLinefeed(dcb);
00441                     continue;
00442                 }
00443 
00444                 /* Carriage return. */
00445                 if (ch == 13) {
00446                     dcb->dcb_col = 0;
00447                     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols);
00448                     continue;
00449                 }
00450 
00451                 /* Escape. */
00452                 if (ch == 27) {
00453                     dcb->dcb_ctlseq = 1;
00454                     continue;
00455                 }
00456 
00457                 /* Backspace. */
00458                 if (ch == 8) {
00459                     if (dcb->dcb_col) {
00460                         dcb->dcb_col--;
00461                         TermDeleteChar(dcb, dcb->dcb_col);
00462                     }
00463                     continue;
00464                 }
00465 
00466                 /* Formfeed. */
00467                 if (ch == 12) {
00468                     TermClear(dcb);
00469                     continue;
00470                 }
00471             }
00472 
00473             /* Last character was ESC. */
00474             if (dcb->dcb_ctlseq == 1) {
00475                 dcb->dcb_ctlseq = 0;
00476 
00477                 switch (ch) {
00478                     /* Insert space. */
00479                 case '@':
00480                     TermInsertSpace(dcb);
00481                     break;
00482 
00483                     /* Cursor up. */
00484                 case 'A':
00485                     TermCursorUp(dcb);
00486                     break;
00487 
00488                     /* Cursor down. */
00489                 case 'B':
00490                     if (++dcb->dcb_row >= dcb->dcb_nrows)
00491                         dcb->dcb_row = dcb->dcb_nrows - 1;
00492                     else
00493                         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00494                     break;
00495 
00496                     /* Cursor right. */
00497                 case 'C':
00498                     TermCursorRight(dcb);
00499                     break;
00500 
00501                     /* Cursor left. */
00502                 case 'D':
00503                     TermCursorLeft(dcb);
00504                     break;
00505 
00506                     /* Clear screen and cursor home. */
00507                 case 'E':
00508                     TermClear(dcb);
00509                     break;
00510 
00511                     /* Cursor home. */
00512                 case 'H':
00513                     dcb->dcb_col = 0;
00514                     dcb->dcb_row = 0;
00515                     (*dcb->dss_cursor_home) ();
00516                     break;
00517 
00518                     /* Reverse linefeed. */
00519                 case 'I':
00520                     TermReverseLinefeed(dcb);
00521                     break;
00522 
00523                     /* Erase to end of screen. */
00524                 case 'J':
00525                     TermEraseEnd(dcb);
00526                     break;
00527 
00528                     /* Erase to end of line. */
00529                 case 'K':
00530                     TermEraseLineEnd(dcb, dcb->dcb_col);
00531                     break;
00532 
00533                     /* Insert line. */
00534                 case 'L':
00535                     TermInsertLine(dcb, dcb->dcb_row);
00536                     break;
00537 
00538                     /* Delete line. */
00539                 case 'M':
00540                     TermDeleteLine(dcb, dcb->dcb_row);
00541                     break;
00542 
00543                     /* Delete character. */
00544                 case 'P':
00545                     TermDeleteChar(dcb, dcb->dcb_col);
00546                     break;
00547 
00548                     /* Cursor position. */
00549                 case 'Y':
00550                     dcb->dcb_ctlseq = 2;
00551                     break;
00552 
00553                     /* Identify. */
00554                 case 'Z':
00555                     TermIdentify(dcb);
00556                     break;
00557 
00558                     /* Cursor on. */
00559                 case 'e':
00560                     dcb->dcb_modeflags |= LCD_MF_CURSORON;
00561                     (*dcb->dss_cursor_mode) (1);
00562                     break;
00563 
00564                     /* Cursor off. */
00565                 case 'f':
00566                     dcb->dcb_modeflags &= ~LCD_MF_CURSORON;
00567                     (*dcb->dss_cursor_mode) (0);
00568                     break;
00569 
00570                     /* Erase to start of screen. */
00571                 case 'd':
00572                     TermEraseStart(dcb);
00573                     break;
00574 
00575                     /* Erase to start of line. */
00576                 case 'o':
00577                     TermEraseLineStart(dcb);
00578                     break;
00579                 }
00580                 continue;
00581             }
00582 
00583             /* Receive cursor row position. */
00584             if (dcb->dcb_ctlseq == 2) {
00585                 dcb->dcb_ctlseq = 3;
00586                 if (ch < 32)
00587                     dcb->dcb_row = 0;
00588                 else if (ch - 32 >= dcb->dcb_nrows)
00589                     dcb->dcb_row = dcb->dcb_nrows - 1;
00590                 else
00591                     dcb->dcb_row = ch - 32;
00592                 continue;
00593             }
00594 
00595             /* Receive cursor column position. */
00596             if (dcb->dcb_ctlseq == 3) {
00597                 dcb->dcb_ctlseq = 0;
00598                 if (ch < 32)
00599                     dcb->dcb_col = 0;
00600                 else if (ch - 32 >= dcb->dcb_vcols)
00601                     dcb->dcb_col = dcb->dcb_vcols - 1;
00602                 else
00603                     dcb->dcb_col = ch - 32;
00604                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00605                 continue;
00606             }
00607         }
00608 
00609         /* 
00610          * Send any character to the LCD driver, which had been left 
00611          * unprocessed upto this point.
00612          */
00613         (*dcb->dss_write) (ch);
00614 
00615         /* Update shadow memory. */
00616         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00617             *(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col) = ch;
00618             if (++dcb->dcb_col >= dcb->dcb_vcols) {
00619                 dcb->dcb_col = dcb->dcb_vcols - 1;
00620                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00621             }
00622         }
00623     }
00624     return rc;
00625 }
00626 
00678 int TermWrite(NUTFILE * fp, CONST void *buffer, int len)
00679 {
00680     return TermPut(fp->nf_dev, buffer, len, 0);
00681 }
00682 
00697 #ifdef __HARVARD_ARCH__
00698 int TermWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00699 {
00700     return TermPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00701 }
00702 #endif
00703 
00721 NUTFILE *TermOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00722 {
00723     TERMDCB *dcb = dev->dev_dcb;
00724     NUTFILE *fp = malloc(sizeof(NUTFILE));
00725 
00726     if (fp == 0)
00727         return NUTFILE_EOF;
00728 
00729     if (mode & _O_BINARY)
00730         dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00731     else
00732         dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00733     fp->nf_next = 0;
00734     fp->nf_dev = dev;
00735     fp->nf_fcb = 0;
00736 
00737     return fp;
00738 }
00739 
00750 int TermClose(NUTFILE * fp)
00751 {
00752     if (fp && fp != NUTFILE_EOF) {
00753         free(fp);
00754         return 0;
00755     }
00756     return -1;
00757 }
00758 

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