Nut/OS  4.10.3
API Reference
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$
00036  * Revision 1.8  2009/02/13 14:52:05  haraldkipp
00037  * Include memdebug.h for heap management debugging support.
00038  *
00039  * Revision 1.7  2008/08/11 06:59:42  haraldkipp
00040  * BSD types replaced by stdint types (feature request #1282721).
00041  *
00042  * Revision 1.6  2005/08/02 17:46:47  haraldkipp
00043  * Major API documentation update.
00044  *
00045  * Revision 1.5  2004/05/24 17:11:05  olereinhardt
00046  * dded terminal device driver for hd44780 compatible LCD displays directly
00047  * connected to the memory bus (memory mapped). See hd44780.c for more
00048  * information.Therefore some minor changed in include/dev/term.h and
00049  * dev/term.c are needet to
00050  * pass a base address to the lcd driver.
00051  *
00052  * Revision 1.4  2004/03/18 18:30:11  haraldkipp
00053  * Added Michael Fischer's TIOCGWINSZ ioctl
00054  *
00055  * Revision 1.3  2004/03/18 14:02:46  haraldkipp
00056  * Comments updated
00057  *
00058  * Revision 1.2  2004/03/16 16:48:27  haraldkipp
00059  * Added Jan Dubiec's H8/300 port.
00060  *
00061  * Revision 1.1.1.1  2003/05/09 14:40:52  haraldkipp
00062  * Initial using 3.2.1
00063  *
00064  * Revision 1.3  2003/05/06 18:34:22  harald
00065  * Cleanup
00066  *
00067  * Revision 1.2  2003/04/21 16:25:24  harald
00068  * Release prep
00069  *
00070  * Revision 1.1  2003/03/31 14:53:08  harald
00071  * Prepare release 3.1
00072  *
00073  */
00074 
00075 #include <dev/term.h>
00076 
00077 #include <stdlib.h>
00078 #include <string.h>
00079 #include <fcntl.h>
00080 #include <memdebug.h>
00081 
00087 
00088 static prog_char termid[] = "Term 1.0";
00089 
00090 static void TermRefreshLineEnd(CONST TERMDCB * dcb, uint8_t row, uint8_t col)
00091 {
00092     uint8_t i = col;
00093     uint8_t *cp = dcb->dcb_smem + row * dcb->dcb_vcols + col;
00094 
00095     /* Disable cursor to avoid flickering. */
00096     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00097         (*dcb->dss_cursor_mode) (0);
00098 
00099     /* Position cursor to the rwo and column to refresh. */
00100     (*dcb->dss_set_cursor) (row * dcb->dcb_ncols + col);
00101 
00102     /*
00103      * This loop looks weird. But it was the only way I found to get
00104      * around a GCC bug in reload1.c:1920.
00105      */
00106     for (;;) {
00107         if (i++ >= dcb->dcb_vcols)
00108             break;
00109         (*dcb->dss_write) (*cp++);
00110     }
00111 
00112     /* Re-enable cursor. */
00113     if (dcb->dcb_modeflags & LCD_MF_CURSORON)
00114         (*dcb->dss_cursor_mode) (1);
00115 }
00116 
00117 void TermRefresh(TERMDCB * dcb)
00118 {
00119     uint8_t ir;
00120 
00121     for (ir = 0; ir < dcb->dcb_nrows; ir++)
00122         TermRefreshLineEnd(dcb, ir, 0);
00123     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00124 }
00125 
00126 static void TermClear(TERMDCB * dcb)
00127 {
00128     memset(dcb->dcb_smem, ' ', dcb->dcb_vcols * dcb->dcb_nrows);
00129     dcb->dcb_col = 0;
00130     dcb->dcb_row = 0;
00131     (*dcb->dss_clear) ();
00132 }
00133 
00134 static void TermDeleteLine(TERMDCB * dcb, uint8_t row)
00135 {
00136     uint8_t i;
00137     uint8_t *dcp;
00138 
00139     for (i = row; i < dcb->dcb_nrows - 1; i++) {
00140         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00141         memcpy(dcp, dcp + dcb->dcb_vcols, dcb->dcb_vcols);
00142     }
00143     memset(dcb->dcb_smem + (dcb->dcb_nrows - 1) * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00144     TermRefresh(dcb);
00145 }
00146 
00147 static void TermInsertLine(TERMDCB * dcb, uint8_t row)
00148 {
00149     uint8_t i;
00150     uint8_t *dcp;
00151 
00152     for (i = dcb->dcb_nrows - 1; i > row; i--) {
00153         dcp = dcb->dcb_smem + i * dcb->dcb_vcols;
00154         memcpy(dcp, dcp - dcb->dcb_vcols, dcb->dcb_vcols);
00155     }
00156     memset(dcb->dcb_smem + row * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00157     TermRefresh(dcb);
00158 }
00159 
00160 static void TermCursorLeft(TERMDCB * dcb)
00161 {
00162     if (dcb->dcb_col) {
00163         (*dcb->dss_cursor_left) ();
00164         dcb->dcb_col--;
00165     }
00166 }
00167 
00168 static void TermCursorRight(TERMDCB * dcb)
00169 {
00170     if (++dcb->dcb_col < dcb->dcb_vcols)
00171         (*dcb->dss_cursor_right) ();
00172     else
00173         dcb->dcb_col = dcb->dcb_vcols - 1;
00174 }
00175 
00176 static void TermCursorUp(TERMDCB * dcb)
00177 {
00178     if (dcb->dcb_row) {
00179         dcb->dcb_row--;
00180         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00181     }
00182 }
00183 
00184 static void TermLinefeed(TERMDCB * dcb)
00185 {
00186     if (++dcb->dcb_row >= dcb->dcb_nrows) {
00187         dcb->dcb_row = dcb->dcb_nrows - 1;
00188         TermDeleteLine(dcb, 0);
00189     } else
00190         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00191 }
00192 
00193 static void TermReverseLinefeed(TERMDCB * dcb)
00194 {
00195     if (dcb->dcb_row--)
00196         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00197     else {
00198         dcb->dcb_row = 0;
00199         TermInsertLine(dcb, 0);
00200     }
00201 }
00202 
00203 static void TermEraseLineEnd(TERMDCB * dcb, uint8_t col)
00204 {
00205     if (col < dcb->dcb_vcols) {
00206         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col, ' ', dcb->dcb_vcols - col);
00207         TermRefresh(dcb);
00208     }
00209 }
00210 
00211 static void TermEraseEnd(TERMDCB * dcb)
00212 {
00213     uint8_t i;
00214 
00215     if (dcb->dcb_col < dcb->dcb_vcols)
00216         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col, ' ', dcb->dcb_vcols - dcb->dcb_col);
00217     for (i = dcb->dcb_row + 1; i < dcb->dcb_nrows; i++)
00218         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00219     TermRefresh(dcb);
00220 }
00221 
00222 static void TermEraseLineStart(TERMDCB * dcb)
00223 {
00224     if (dcb->dcb_col) {
00225         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00226         TermRefresh(dcb);
00227     }
00228 }
00229 
00230 static void TermEraseStart(TERMDCB * dcb)
00231 {
00232     uint8_t i;
00233 
00234     if (dcb->dcb_col)
00235         memset(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols, ' ', dcb->dcb_col);
00236     for (i = 0; i < dcb->dcb_row; i++)
00237         memset(dcb->dcb_smem + i * dcb->dcb_vcols, ' ', dcb->dcb_vcols);
00238     TermRefresh(dcb);
00239 }
00240 
00241 static void TermDeleteChar(TERMDCB * dcb, uint8_t col)
00242 {
00243     uint8_t i;
00244     uint8_t *cp = dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + col;
00245 
00246     for (i = col; i < dcb->dcb_vcols - 1; i++, cp++)
00247         *cp = *(cp + 1);
00248     *cp = ' ';
00249     TermRefresh(dcb);
00250 }
00251 
00252 /*
00253  * Insert a space at the cursor position.
00254  */
00255 static void TermInsertSpace(TERMDCB * dcb)
00256 {
00257     uint8_t i;
00258     uint8_t *cp = dcb->dcb_smem + (dcb->dcb_row + 1) * dcb->dcb_vcols - 1;
00259 
00260     for (i = dcb->dcb_col; i < dcb->dcb_vcols - 1; i++, cp--)
00261         *cp = *(cp - 1);
00262     *cp = ' ';
00263     TermRefreshLineEnd(dcb, dcb->dcb_row, dcb->dcb_col);
00264     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00265 }
00266 
00267 /*
00268  * Clear display and print identification.
00269  */
00270 static void TermIdentify(TERMDCB * dcb)
00271 {
00272     PGM_P pcp = termid;
00273 
00274     TermClear(dcb);
00275     while (PRG_RDB(pcp)) {
00276         (*dcb->dss_write) (PRG_RDB(pcp));
00277         pcp++;
00278     }
00279 }
00280 
00318 int TermIOCtl(NUTDEVICE * dev, int req, void *conf)
00319 {
00320     TERMDCB *dcb = dev->dev_dcb;
00321     uint16_t usv;
00322     uint32_t ulv;
00323     WINSIZE *win_size;
00324 
00325     switch (req) {
00326     case LCD_CMDBYTE:
00327         (*dcb->dss_command) (*(uint8_t *)conf, 10);
00328         break;
00329     case LCD_CMDWORD16:
00330         usv = *(uint16_t *)conf;
00331         (*dcb->dss_command) ((uint8_t)(usv >> 8), 10);
00332         (*dcb->dss_command) ((uint8_t)usv, 10);
00333         break;
00334     case LCD_CMDWORD32:
00335         ulv = *(uint32_t *)conf;
00336         (*dcb->dss_command) ((uint8_t)(ulv >> 24), 10);
00337         (*dcb->dss_command) ((uint8_t)(ulv >> 16), 10);
00338         (*dcb->dss_command) ((uint8_t)(ulv >> 8), 10);
00339         (*dcb->dss_command) ((uint8_t)ulv, 10);
00340         break;
00341     case LCD_DATABYTE:
00342         (*dcb->dss_write) (*(uint8_t *)conf);
00343         break;
00344     case LCD_DATAWORD16:
00345         usv = *(uint16_t *)conf;
00346         (*dcb->dss_write) ((uint8_t)(usv >> 8));
00347         (*dcb->dss_write) ((uint8_t)usv);
00348         break;
00349     case LCD_DATAWORD32:
00350         ulv = *(uint32_t *)conf;
00351         (*dcb->dss_write) ((uint8_t)(ulv >> 24));
00352         (*dcb->dss_write) ((uint8_t)(ulv >> 16));
00353         (*dcb->dss_write) ((uint8_t)(ulv >> 8));
00354         (*dcb->dss_write) ((uint8_t)ulv);
00355         break;
00356     case LCD_SETCOOKEDMODE:
00357         if (*(uint32_t *)conf)
00358             dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00359         else
00360             dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00361         break;
00362     case LCD_GETCOOKEDMODE:
00363         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE)
00364             *(uint32_t *)conf = 1;
00365         else
00366             *(uint32_t *)conf = 0;
00367         break;
00368 
00369     case LCD_SET_AUTOLF:
00370         if (*(uint32_t *)conf)
00371             dcb->dcb_modeflags |= LCD_MF_AUTOLF;
00372         else
00373             dcb->dcb_modeflags &= ~LCD_MF_AUTOLF;
00374         break;
00375     case LCD_GET_AUTOLF:
00376         if (dcb->dcb_modeflags & LCD_MF_AUTOLF)
00377             *(uint32_t *)conf = 1;
00378         else
00379             *(uint32_t *)conf = 0;
00380         break;
00381         
00382     case TIOCGWINSZ:
00383         win_size = (WINSIZE *)conf;
00384         win_size->ws_col    = dcb->dcb_nrows;
00385         win_size->ws_row    = dcb->dcb_vcols;
00386         win_size->ws_xpixel = 0;
00387         win_size->ws_ypixel = 0;
00388         break;
00389     }
00390     return 0;
00391 }
00392 
00393 
00406 int TermInit(NUTDEVICE * dev)
00407 {
00408     TERMDCB *dcb = dev->dev_dcb;
00409 
00410     /*
00411      * Check if initialisazion needed.
00412      */
00413     if( dcb->dss_init != NULL) {
00414         /*
00415          * Initialize the display hardware.
00416          */
00417         if(  dcb->dss_init(dev) != 0) {
00418             return -1;
00419         }
00420     }
00421     /*
00422      * Initialize driver control block.
00423      */
00424     dcb->dcb_smem = malloc(dcb->dcb_nrows * dcb->dcb_vcols);
00425     if( dcb->dcb_smem == NULL) {
00426         return -1;
00427     }
00428 
00429     TermClear(dcb);
00430 
00431     return 0;
00432 }
00433 
00445 static int TermPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00446 {
00447     int rc;
00448     CONST uint8_t *cp;
00449     uint8_t ch;
00450     TERMDCB *dcb = dev->dev_dcb;
00451 
00452     /*
00453      * Call without data pointer is accepted.
00454      */
00455     if (buffer == 0)
00456         return 0;
00457 
00458     /*
00459      * Put characters in transmit buffer.
00460      */
00461     cp = buffer;
00462     for (rc = 0; rc < len; cp++, rc++) {
00463         ch = pflg ? PRG_RDB(cp) : *cp;
00464 
00465         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00466             /* Process special characters. */
00467             if (dcb->dcb_ctlseq == 0) {
00468 
00469                 /* Linefeed. */
00470                 if (ch == 10) {
00471                     dcb->dcb_col = 0;
00472                     TermLinefeed(dcb);
00473                     continue;
00474                 }
00475 
00476                 /* Carriage return. */
00477                 if (ch == 13) {
00478                     dcb->dcb_col = 0;
00479                     (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols);
00480                     continue;
00481                 }
00482 
00483                 /* Escape. */
00484                 if (ch == 27) {
00485                     dcb->dcb_ctlseq = 1;
00486                     continue;
00487                 }
00488 
00489                 /* Backspace. */
00490                 if (ch == 8) {
00491                     if (dcb->dcb_col) {
00492                         dcb->dcb_col--;
00493                         TermDeleteChar(dcb, dcb->dcb_col);
00494                     }
00495                     continue;
00496                 }
00497 
00498                 /* Formfeed. */
00499                 if (ch == 12) {
00500                     TermClear(dcb);
00501                     continue;
00502                 }
00503             }
00504 
00505             /* Last character was ESC. */
00506             if (dcb->dcb_ctlseq == 1) {
00507                 dcb->dcb_ctlseq = 0;
00508 
00509                 switch (ch) {
00510                     /* Insert space. */
00511                 case '@':
00512                     TermInsertSpace(dcb);
00513                     break;
00514 
00515                     /* Cursor up. */
00516                 case 'A':
00517                     TermCursorUp(dcb);
00518                     break;
00519 
00520                     /* Cursor down. */
00521                 case 'B':
00522                     if (++dcb->dcb_row >= dcb->dcb_nrows)
00523                         dcb->dcb_row = dcb->dcb_nrows - 1;
00524                     else
00525                         (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00526                     break;
00527 
00528                     /* Cursor right. */
00529                 case 'C':
00530                     TermCursorRight(dcb);
00531                     break;
00532 
00533                     /* Cursor left. */
00534                 case 'D':
00535                     TermCursorLeft(dcb);
00536                     break;
00537 
00538                     /* Clear screen and cursor home. */
00539                 case 'E':
00540                     TermClear(dcb);
00541                     break;
00542 
00543                     /* Cursor home. */
00544                 case 'H':
00545                     dcb->dcb_col = 0;
00546                     dcb->dcb_row = 0;
00547                     (*dcb->dss_cursor_home) ();
00548                     break;
00549 
00550                     /* Reverse linefeed. */
00551                 case 'I':
00552                     TermReverseLinefeed(dcb);
00553                     break;
00554 
00555                     /* Erase to end of screen. */
00556                 case 'J':
00557                     TermEraseEnd(dcb);
00558                     break;
00559 
00560                     /* Erase to end of line. */
00561                 case 'K':
00562                     TermEraseLineEnd(dcb, dcb->dcb_col);
00563                     break;
00564 
00565                     /* Insert line. */
00566                 case 'L':
00567                     TermInsertLine(dcb, dcb->dcb_row);
00568                     break;
00569 
00570                     /* Delete line. */
00571                 case 'M':
00572                     TermDeleteLine(dcb, dcb->dcb_row);
00573                     break;
00574 
00575                     /* Delete character. */
00576                 case 'P':
00577                     TermDeleteChar(dcb, dcb->dcb_col);
00578                     break;
00579 
00580                     /* Cursor position. */
00581                 case 'Y':
00582                     dcb->dcb_ctlseq = 2;
00583                     break;
00584 
00585                     /* Identify. */
00586                 case 'Z':
00587                     TermIdentify(dcb);
00588                     break;
00589 
00590                     /* Cursor on. */
00591                 case 'e':
00592                     dcb->dcb_modeflags |= LCD_MF_CURSORON;
00593                     (*dcb->dss_cursor_mode) (1);
00594                     break;
00595 
00596                     /* Cursor off. */
00597                 case 'f':
00598                     dcb->dcb_modeflags &= ~LCD_MF_CURSORON;
00599                     (*dcb->dss_cursor_mode) (0);
00600                     break;
00601 
00602                     /* Erase to start of screen. */
00603                 case 'd':
00604                     TermEraseStart(dcb);
00605                     break;
00606 
00607                     /* Erase to start of line. */
00608                 case 'o':
00609                     TermEraseLineStart(dcb);
00610                     break;
00611                     
00612                 case 'i':
00613                     dcb->dcb_modeflags |= LCD_MF_INVERTED;
00614                     (*dcb->dss_cursor_mode) (3);
00615                     break;
00616                     
00617                 case 'n':
00618                     dcb->dcb_modeflags &= ~LCD_MF_INVERTED;
00619                     (*dcb->dss_cursor_mode) (2);
00620                     break;
00621                 }
00622                 continue;
00623             }
00624 
00625             /* Receive cursor row position. */
00626             if (dcb->dcb_ctlseq == 2) {
00627                 dcb->dcb_ctlseq = 3;
00628                 if (ch < 32)
00629                     dcb->dcb_row = 0;
00630                 else if (ch - 32 >= dcb->dcb_nrows)
00631                     dcb->dcb_row = dcb->dcb_nrows - 1;
00632                 else
00633                     dcb->dcb_row = ch - 32;
00634                 continue;
00635             }
00636 
00637             /* Receive cursor column position. */
00638             if (dcb->dcb_ctlseq == 3) {
00639                 dcb->dcb_ctlseq = 0;
00640                 if (ch < 32)
00641                     dcb->dcb_col = 0;
00642                 else if (ch - 32 >= dcb->dcb_vcols)
00643                     dcb->dcb_col = dcb->dcb_vcols - 1;
00644                 else
00645                     dcb->dcb_col = ch - 32;
00646                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00647                 continue;
00648             }
00649         }
00650 
00651         /*
00652          * Send any character to the LCD driver, which had been left
00653          * unprocessed upto this point.
00654          */
00655         (*dcb->dss_write) (ch);
00656 
00657         if (dcb->dcb_modeflags & LCD_MF_COOKEDMODE) {
00658             /* Update shadow memory. */
00659             *(dcb->dcb_smem + dcb->dcb_row * dcb->dcb_vcols + dcb->dcb_col) = ch;
00660 
00661             /* step cursor forward */
00662             if (++dcb->dcb_col >= dcb->dcb_vcols) {
00663                 if( dcb->dcb_modeflags & LCD_MF_AUTOLF) {
00664                     dcb->dcb_col = 0;
00665                     if( dcb->dcb_row < dcb->dcb_nrows) dcb->dcb_row++;
00666                 }
00667                 else
00668                     dcb->dcb_col = dcb->dcb_vcols - 1;
00669                 (*dcb->dss_set_cursor) (dcb->dcb_row * dcb->dcb_ncols + dcb->dcb_col);
00670             }
00671         }
00672     }
00673     return rc;
00674 }
00675 
00727 int TermWrite(NUTFILE * fp, CONST void *buffer, int len)
00728 {
00729     return TermPut(fp->nf_dev, buffer, len, 0);
00730 }
00731 
00746 #ifdef __HARVARD_ARCH__
00747 int TermWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00748 {
00749     return TermPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00750 }
00751 #endif
00752 
00770 NUTFILE *TermOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00771 {
00772     TERMDCB *dcb = dev->dev_dcb;
00773     NUTFILE *fp = malloc(sizeof(NUTFILE));
00774 
00775     if (fp == 0)
00776         return NUTFILE_EOF;
00777 
00778     if (mode & _O_BINARY)
00779         dcb->dcb_modeflags &= ~LCD_MF_COOKEDMODE;
00780     else
00781         dcb->dcb_modeflags |= LCD_MF_COOKEDMODE;
00782     fp->nf_next = 0;
00783     fp->nf_dev = dev;
00784     fp->nf_fcb = 0;
00785 
00786     return fp;
00787 }
00788 
00799 int TermClose(NUTFILE * fp)
00800 {
00801     if (fp && fp != NUTFILE_EOF) {
00802         free(fp);
00803         return 0;
00804     }
00805     return -1;
00806 }
00807