Milos RTOS v0.3.4a
Real Time Operating System
terminal.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  * terminal.c
00003  * (C) 2010 Ivan Meleca
00004  * Based on original code written by Ruben Meleca
00005  * www.milos.it
00006 
00007 #   This program is free software; you can redistribute it and/or modify
00008 #   it under the terms of the GNU General Public License as published by
00009 #   the Free Software Foundation; either version 2 of the License, or
00010 #   (at your option) any later version.
00011 #
00012 #   This program is distributed in the hope that it will be useful,
00013 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 #   GNU General Public License for more details.
00016 #
00017 #   You should have received a copy of the GNU General Public License
00018 #   along with this program; if not, write to the Free Software
00019 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 
00021 ***************************************************************************/
00022 
00023 #include "terminal.h"
00024 #include "lock.h"
00025 #include "heap.h"
00026 #include <common/string.h>
00027 #include <common/mem.h>
00028 #include "device.h"
00029 #include "system.h"
00030 #include "dbgterm.h"
00031 
00032 #if (__CONFIG_COMPILE_TERMINAL && __CONFIG_COMPILE_STRING)
00033 
00047 #define __SK_BASE           0x1000
00048 #define __SK_UNKNOWN        0xFFFF
00049 #define __SK_ESCAPE         __SK_BASE
00050 #define __SK_UPARROW        __SK_BASE + 1
00051 #define __SK_DOWNARROW      __SK_BASE + 2
00052 #define __SK_RIGHTARROW     __SK_BASE + 3
00053 #define __SK_LEFTARROW      __SK_BASE + 4
00054 #define __SK_BREAK          __SK_BASE + 5
00055 #define __SK_INS            __SK_BASE + 6
00056 #define __SK_HOME           __SK_BASE + 7
00057 #define __SK_PGUP           __SK_BASE + 8
00058 #define __SK_DEL            __SK_BASE + 9
00059 #define __SK_END            __SK_BASE + 10
00060 #define __SK_PGDN           __SK_BASE + 11
00061 #define __SK_F1             __SK_BASE + 12
00062 #define __SK_F2             __SK_BASE + 13
00063 #define __SK_F3             __SK_BASE + 14
00064 #define __SK_F4             __SK_BASE + 15
00065 #define __SK_F5             __SK_BASE + 16
00066 #define __SK_F6             __SK_BASE + 17
00067 #define __SK_F7             __SK_BASE + 18
00068 #define __SK_F8             __SK_BASE + 19
00069 #define __SK_F9             __SK_BASE + 20
00070 #define __SK_F10            __SK_BASE + 21
00071 #define __SK_F11            __SK_BASE + 22
00072 #define __SK_F12            __SK_BASE + 23
00073 #define __TERMINAL_PROMPT   "milos> "
00074 
00084 __CONST __STRING __terminalESC[]    = {0x1b, 0x5b};
00085 __CONST __STRING __terminalBackspace[]  = {0x1b, 0x5b, '1', 'D', 0x1b, 0x5b, '0', 'K', 0x00};
00086 __CONST __STRING __terminalClearScreen[] = {0x1b, 0x5b, '2', 'J', 0x00};
00087 __CONST __STRING __terminalShowCursor[]  = {0x1b, 0x5b, '?', '2', '5', 'h', 0x00};
00088 __CONST __STRING __terminalHideCursor[]  = {0x1b, 0x5b, '?', '2', '5', 'l', 0x00};
00089 
00090 #if __CONFIG_COMPILE_DBGTERM
00091 
00092 __TERMINALCMD __dbgCommands[] = {
00093         { "heap",       __dbgTerminalIn, __NULL},
00094         { "locks",      __dbgTerminalIn, __NULL},
00095         { "threads",    __dbgTerminalIn, __NULL},
00096         { "reset",      __dbgTerminalIn, __NULL},
00097         { "uptime",     __dbgTerminalIn, __NULL},
00098         { "devices",    __dbgTerminalIn, __NULL},
00099         { "defrag",     __dbgTerminalIn, __NULL},
00100 
00101 #if __CONFIG_COMPILE_FAT
00102         { "dir",        __dbgTerminalIn, __NULL},
00103 #endif /* __CONFIG_COMPILE_FAT */
00104 
00105 #if __CONFIG_COMPILE_NET
00106         { "ping",       __dbgTerminalIn, __NULL},
00107         { "arp",        __dbgTerminalIn, __NULL},
00108         { "netif",      __dbgTerminalIn, __NULL}
00109 #endif /* __CONFIG_COMPILE_NET */
00110 };
00111 
00112 #endif /* __CONFIG_COMPILE_DBGTERM */
00113 
00131 __STATIC __VOID __terminalBack(__PTERMINAL term, u8 qty)
00132 {
00133     __STRING num[4];
00134     __STRING buf[sizeof(num) + 5];
00135     u8 len;
00136 
00137     __strFmt(num, "%u", qty);
00138     len = __strLen(num);
00139     
00140     buf[0] = __terminalESC[0];
00141     buf[1] = __terminalESC[1];
00142     __memCpy(buf+2, num, len);
00143     buf[2+len] = 'D';
00144     buf[3+len] = 0x00;
00145     
00146     __terminalWrite(term, buf);
00147 } 
00148 
00157 __STATIC __VOID __terminalFoward(__PTERMINAL term, u8 qty)
00158 {
00159     __STRING num[4];
00160     __STRING buf[sizeof(num) + 5];
00161     u8 len;
00162 
00163     __strFmt(num, "%u", qty);
00164     len = __strLen(num);
00165 
00166     buf[0] = __terminalESC[0];
00167     buf[1] = __terminalESC[1];
00168     __memCpy(buf+2, num, len);
00169     buf[2+len] = 'C';
00170     buf[3+len] = 0x00;
00171     
00172     __terminalWrite(term, buf);
00173 }
00174 
00183 __STATIC __VOID __terminalShowPrompt(__PTERMINAL term)
00184 {
00185     if (__lockOwn(term->lock, 1000))
00186     {
00187         __deviceWrite(term->dv_out, term->prompt, __strLen(term->prompt));
00188         __deviceFlush(term->dv_out);
00189         __lockRelease(term->lock);
00190     }
00191 }
00192 
00204 __STATIC u16 __terminalParseEscaped(__PTERMINAL term)
00205 {
00206     u8  a = 0;
00207     u8  b = 0;
00208     u8  c = 0;
00209     u8  d = 0;
00210 
00211     /* Check for 0x5b */
00212     if (__deviceRead(term->dv_in, &a, 1) == 1)
00213     {
00214         /* Not 0x5b? Return and save last char received */
00215         if (a != 0x5b)
00216         {
00217             return a;
00218         }
00219     } else
00220     {
00221         return __SK_ESCAPE;
00222     }
00223                             
00224     /* 0x5b received, check for special chars */
00225     if (__deviceRead(term->dv_in, &b, 1) == 1)
00226     {
00227         switch (b) {
00228             /* Arrows */
00229             case 0x41:  
00230                 return __SK_UPARROW;
00231             case 0x42:  
00232                 return __SK_DOWNARROW;
00233             case 0x43:  
00234                 return __SK_RIGHTARROW;
00235             case 0x44:  
00236                 return __SK_LEFTARROW;
00237                                     
00238             /* Break */
00239             case 0x50:  
00240                 return __SK_BREAK;      
00241                 
00242                                                 
00243             /* Four and five char codes */
00244             case 0x5b:
00245             case 0x31:
00246             case 0x32:
00247             case 0x33:
00248             case 0x35:
00249             case 0x36:
00250             case 0x37:  
00251                 break;              
00252             default:
00253                 return __SK_UNKNOWN;
00254         }
00255         
00256         if (__deviceRead(term->dv_in, &c, 1) == 1)
00257         {
00258             /* Four characters codes ending with 0x7e*/
00259             if (c == 0x7e) {
00260                 switch (b) {
00261                     case 0x31:
00262                         return __SK_HOME;
00263                     case 0x32:
00264                         return __SK_INS;
00265                     case 0x33:
00266                         return __SK_DEL;
00267                     case 0x34:
00268                         return __SK_END;
00269                     case 0x35:
00270                         return __SK_PGUP;
00271                     case 0x36:
00272                         return __SK_PGDN;                       
00273                     default:
00274                         return __SK_UNKNOWN;
00275                 }
00276             }
00277             
00278             if (b != 0x31 && b != 0x32) return __SK_UNKNOWN;
00279             
00280             /* Five characters codes */
00281             if (__deviceRead(term->dv_in, &d, 1) == 1)
00282             {
00283                 if (d != 0x7e) return __SK_UNKNOWN;
00284                 
00285                 switch (b) {
00286                     case 0x31:
00287                         switch (c) {
00288                             case 0x31:
00289                                 return __SK_F1;
00290                             case 0x32:
00291                                 return __SK_F2;
00292                             case 0x33:
00293                                 return __SK_F3;
00294                             case 0x34:
00295                                 return __SK_F4;
00296                             case 0x35:
00297                                 return __SK_F5;
00298                             case 0x37:
00299                                 return __SK_F6;
00300                             case 0x38:
00301                                 return __SK_F7;
00302                             case 0x39:
00303                                 return __SK_F8;
00304                         }
00305                         return __SK_UNKNOWN;
00306                         
00307                     case 0x32:
00308                         switch (c) {
00309                             case 0x30:
00310                                 return __SK_F9;
00311                             case 0x31:
00312                                 return __SK_F10;
00313                             case 0x33:
00314                                 return __SK_F11;
00315                             case 0x34:
00316                                 return __SK_F12;
00317                         }
00318                         return __SK_UNKNOWN;
00319                         
00320                     default:
00321                         return __SK_UNKNOWN;
00322                 }
00323             }
00324         }
00325     }
00326     
00327     return __SK_UNKNOWN;
00328 }
00329 
00339 __STATIC __BOOL __terminalParseSpecial(__PTERMINAL term, u16 code) {
00340 
00341     if (code == __SK_UNKNOWN) return __FALSE;
00342     
00343     switch (code)
00344     {
00345         case __SK_LEFTARROW:        if (term->cursor)
00346                                     {
00347                                         __terminalBack(term, 1);
00348                                         /* back cursor */
00349                                         term->cursor--;
00350                                     }
00351                                     break;
00352                                     
00353         case __SK_RIGHTARROW:       if (__strLen(term->rx_line) > term->cursor)
00354                                     {
00355                                         __terminalFoward(term, '1');
00356                                         
00357                                         /* back cursor */
00358                                         term->cursor++;
00359                                     }
00360                                     break;
00361                                     
00362         case __SK_UPARROW:
00363                                     break;
00364                                     
00365         case __SK_END:              /* hide cursor */
00366                                     __terminalWrite(term, __terminalHideCursor);
00367         
00368                                     /* move cursor */
00369                                     __terminalBack(term, term->cursor);
00370                                     
00371                                     term->cursor = 0;
00372                                     
00373                                     /* show cursor */
00374                                     __terminalWrite(term, __terminalShowCursor);
00375                                     break;
00376                                     
00377         case __SK_HOME:             if (term->cursor == __strLen(term->rx_line)) break;
00378                                     
00379                                     /* hide cursor */
00380                                     __terminalWrite(term, __terminalHideCursor);
00381         
00382                                     /* move cursor */
00383                                     __terminalFoward(term, __strLen(term->rx_line) - term->cursor);
00384                                     
00385                                     term->cursor = __strLen(term->rx_line);
00386                                     
00387                                     /* show cursor */
00388                                     __terminalWrite(term, __terminalShowCursor);
00389                                     break;                              
00390     }
00391     
00392     return __FALSE;
00393 
00394 }
00395 
00405 __STATIC __VOID __terminalWriteChar(__PTERMINAL term, u8 c)
00406 {
00407     if (!(term->flags & __TERMINAL_STARTED)) return;
00408 
00409     if (__lockOwn(term->lock, 1000)) {
00410         __deviceWrite(term->dv_out, &c, 1);
00411         __deviceFlush(term->dv_out);
00412         __lockRelease(term->lock);
00413     }
00414 }
00415 
00425 __STATIC __BOOL __terminalReadLine(__PTERMINAL term)
00426 {
00427     u16 size = 0;
00428     u8  c = 0;
00429     u8  ret = 0;
00430     u8  echo;
00431     u8  save;
00432     u8  i = 0;
00433     u8  print = 0;
00434 
00435     for (;;)
00436     {
00437         size = __deviceSize(term->dv_in, __DEV_RXSIZE);
00438         while (size > 0)
00439         {
00440             if (__deviceRead(term->dv_in, &c, 1) == 1)
00441             {
00442                 size--;
00443 
00444                 echo    = __FALSE;
00445                 save    = __TRUE;
00446                 ret     = __FALSE;
00447 
00448                 /* Parse received char */
00449                 switch (c) {
00450 
00451                     /* ESCAPE */
00452                     case 0x1b:
00453                         save = __FALSE;
00454                         echo = __terminalParseSpecial(term, __terminalParseEscaped(term));
00455                         
00456                         /* Refresh size */
00457                         size = __deviceSize(term->dv_in, __DEV_RXSIZE);
00458                         break;
00459 
00460                     /* TAB */
00461                     case 0x09:
00462                         save = __FALSE;
00463                         echo = __FALSE;
00464                         break;
00465 
00466                     /* BACKSPACE */
00467                     case 0x08:
00468                         save = __FALSE;
00469                         echo = __FALSE;
00470 
00471                         if (!term->rxoffs) break;
00472                         if (!term->cursor) break;
00473 
00474                         /* Deleting a char between others */
00475                         if (term->cursor < term->rxoffs)
00476                         {
00477                             for (i = term->cursor; i < term->rxoffs; i++)
00478                             {
00479                                 term->rx_line[i-1] = term->rx_line[i];
00480                             }
00481                             
00482                             print = __TRUE;
00483                         }
00484                         
00485                         term->rxoffs--;
00486                         term->cursor--;
00487                         
00488                         term->rx_line[term->rxoffs] = 0;
00489                         
00490                         if (print)
00491                         {
00492                             /* hide cursor */
00493                             __terminalWrite(term, __terminalHideCursor);
00494                         
00495                             /* write a left and clear escape sequence */
00496                             __terminalWrite(term, __terminalBackspace);
00497                         
00498                             /* rewrite the rest of the line */
00499                             __terminalWrite(term, term->rx_line + term->cursor);
00500                             
00501                             /* move cursor back */
00502                             __terminalBack(term, term->rxoffs - term->cursor);
00503                             
00504                             /* show cursor */
00505                             __terminalWrite(term, __terminalShowCursor);
00506                         } else
00507                         {
00508                             /* write a left and clear escape sequence */
00509                             __terminalWrite(term, __terminalBackspace);
00510                         }
00511                         break;
00512 
00513                     /* CR */
00514                     case 0x0D:
00515                         save = __FALSE;
00516                         echo = __FALSE;
00517 
00518                         /* Send back a LF */
00519                         __terminalWrite(term, "\r\n");
00520                         ret = __TRUE;
00521                         break;
00522 
00523                     /* LF (ignore) */
00524                     case 0x0A:
00525                         save = __FALSE;
00526                         break;
00527 
00528                     default:
00529                         echo = __TRUE;
00530                         break;
00531                 }
00532 
00533                 /* Buffer received char? */
00534                 if (save) {
00535                     /* Overflow? */
00536                     if (term->rxoffs < term->linelen-1)
00537                     {
00538                         print = __FALSE;
00539                     
00540                         /* inserting char? */
00541                         if (term->cursor < term->rxoffs)
00542                         {
00543                             for (i = term->linelen - 1; i > term->cursor; i--)
00544                             {
00545                                 term->rx_line[i] = term->rx_line[i-1];
00546                             }
00547                             
00548                             print = __TRUE;
00549                         } 
00550                         
00551                         term->rx_line[term->cursor] = c;
00552                     
00553                         /* advance buffer ptr */
00554                         term->rxoffs++;
00555                         
00556                         /* advance cursor */
00557                         term->cursor++;
00558 
00559                         /* ensure last byte is __NULL */
00560                         term->rx_line[term->linelen - 1] = 0;
00561                         
00562                         if (print)
00563                         {
00564                             /* hide cursor */
00565                             __terminalWrite(term, __terminalHideCursor);
00566                             
00567                             __terminalWrite(term, term->rx_line + term->cursor - 1);
00568                             
00569                             /* move cursor back */
00570                             __terminalBack(term, term->rxoffs - term->cursor);
00571                             
00572                             /* show cursor */
00573                             __terminalWrite(term, __terminalShowCursor);
00574                             echo = __FALSE;
00575                         }
00576                     } else
00577                     {
00578                         echo = __FALSE;
00579                     }
00580                 }
00581                 
00582                 /* Has to send back ECHO? */
00583                 if (echo && (term->flags & __TERMINAL_ECHO_ENABLED))
00584                 {
00585                     __terminalWriteChar(term, c);
00586                 }
00587                 
00588                 /* End of line, return */
00589                 if (ret)
00590                 {
00591                     term->rxoffs = 0;
00592                     term->cursor = 0;
00593                     return __TRUE;
00594                 }
00595             }
00596         }
00597 
00598         __threadSleep(1);
00599     }
00600 
00601     return __FALSE;
00602 }
00603 
00611 __VOID __terminalThread(__VOID)
00612 {
00613     __PTERMINALCMD commands;
00614     __PSTRING   str;
00615     __BOOL      bKnown = __FALSE;
00616     __PTERMINAL term = __threadGetParameter();
00617 
00618     __terminalWriteLine(term, "\r\n\r\n");
00619     
00620     __terminalWriteLine(term, "\r\nMilos v3 - Terminal Ready\r\n");
00621     __terminalShowPrompt(term);
00622     
00623     for (;;)
00624     {
00625         __memSet(term->rx_line, 0, term->linelen);
00626         if (__terminalReadLine(term))
00627         {
00628             if (__strLen(term->rx_line))
00629             {
00630                 /* Cycle through all terminal commands */
00631                 commands = term->cmds;
00632                 bKnown = __FALSE;
00633                 
00634                 /* Command with parameters? */
00635                 str = __strChr(term->rx_line, 0x20);
00636 
00637                 while (commands && !bKnown)
00638                 {
00639                     if (str)
00640                     {
00641                         if (__strnCmp(term->rx_line, commands->cmd, (u16) (str - term->rx_line)) == 0)
00642                         {
00643                             if (commands->func) (commands->func) (term, term->rx_line, str);
00644                             bKnown = __TRUE;
00645                         }
00646 
00647                     } else {
00648                         /* No parameters, command must exactly match */
00649                         if (__strCmp(term->rx_line, commands->cmd) == 0)
00650                         {
00651                             if (commands->func) (commands->func) (term, term->rx_line, __NULL);
00652                             bKnown = __TRUE;
00653                         }
00654                     }
00655 
00656                     commands = commands->next;
00657                 }
00658 
00659                 /* Unknown Command? */
00660                 if (bKnown == __FALSE)
00661                 {
00662                     __terminalWriteLine(term, "Unknown Command\r\n");
00663                 }
00664             }
00665             
00666             __terminalShowPrompt(term);
00667         }
00668         
00669         __threadSleep(1);
00670     }
00671 }
00672 
00681 __BOOL __terminalStart(__PTERMINAL term)
00682 {
00683     /* Already started? */
00684     if (term->flags & __TERMINAL_STARTED) return __FALSE;
00685     
00686     /* Check for at least one device */
00687     if (!term->dv_in && !term->dv_out) return __FALSE;
00688 
00689     /* Check for devices opened and initialized */
00690     if (term->dv_in)
00691     {
00692         if (!term->dv_in->dv_initd || !term->dv_in->dv_opcnt) return __FALSE;
00693     }
00694 
00695     if (term->dv_out)
00696     {
00697         if (!term->dv_out->dv_initd || !term->dv_out->dv_opcnt) return __FALSE;
00698     }
00699 
00700     term->flags |= __TERMINAL_STARTED;
00701 
00702     /* Is the device has not an input device, do not start the terminal "read" thread */
00703     if (term->dv_in)
00704     {
00705         __threadCreate(term->name, __terminalThread, __CONFIG_PRIO_TERMTHREAD, 1024, 1, term);
00706     }
00707 
00708     return __TRUE;
00709 }
00710 
00724 __VOID __terminalWriteLine(__PTERMINAL term, __CONST __PSTRING str, ...)
00725 {
00726     __PSTRING args = ((__PSTRING) &str) + sizeof(__PSTRING);
00727     __terminalOut(term, __TRUE, str, args);
00728 }
00729 
00743 __VOID __terminalWrite(__PTERMINAL term, __CONST __PSTRING str, ...)
00744 {
00745     __PSTRING args = ((__PSTRING) &str) + sizeof(__PSTRING);
00746     __terminalOut(term, __FALSE, str, args);
00747 }
00748 
00749 __VOID __terminalOut(__PTERMINAL term, __BOOL crlf, __CONST __PSTRING str, __PSTRING args)
00750 {
00751     if (!(term->flags & __TERMINAL_STARTED)) return;
00752 
00753     if (__strLen(str) >= term->linelen) return;
00754 
00755     if (__lockOwn(term->lock, 1000))
00756     {
00757         if (args)
00758         {
00759             __strFmtEx(term->tx_line, str, args);
00760         } else {
00761             __strCpy(term->tx_line, str);
00762         }
00763 
00764         __deviceWrite(term->dv_out, term->tx_line, __strLen(term->tx_line));
00765 
00766         if (crlf)
00767         {
00768             __deviceWrite(term->dv_out, "\r\n", 2);
00769         }
00770 
00771         __deviceFlush(term->dv_out);
00772         __lockRelease(term->lock);
00773     }
00774 }
00775 
00776 
00799 __PTERMINAL __terminalCreate(__PSTRING name, __PDEVICE in, __PDEVICE out, u16 linelen, __PSTRING prompt, u8 flags)
00800 {
00801     __PTERMINAL term;
00802 
00803     if (!name) return __NULL;
00804 
00805     /* Check for at least one device */
00806     if (!in && !out) return __NULL;
00807 
00808     /* Create terminal */
00809     term = __heapAllocZero(sizeof(__TERMINAL));
00810     if (!term) return __NULL;
00811 
00812     /* Check line length */
00813     if (!linelen) linelen = __TERMINAL_DEF_LINELEN;
00814 
00815     /* Allocate RX line */
00816     term->rx_line = __heapAllocZero(linelen);
00817     if (!term->rx_line)
00818     {
00819         __heapFree(term);
00820         return __NULL;
00821     }
00822 
00823     /* Allocate TX line */
00824     term->tx_line = __heapAllocZero(linelen);
00825     if (!term->tx_line)
00826     {
00827         __heapFree(term);
00828         __heapFree(term->rx_line);
00829         return __NULL;
00830     }
00831 
00832     /* If an output device is provided, create a lock
00833      * for writing */
00834     if (out)
00835     {
00836         term->lock = __lockCreate();
00837         if (!term->lock)
00838         {
00839             __heapFree(term);
00840             __heapFree(term->rx_line);
00841             __heapFree(term->tx_line);
00842             return __NULL;
00843         }
00844     }
00845 
00846     term->linelen = linelen;
00847     term->name = name;
00848     term->dv_in = in;
00849     term->dv_out = out;
00850     term->flags = flags;
00851 
00852     if (!prompt)
00853     {
00854         term->prompt = __TERMINAL_PROMPT;
00855     } else {
00856         term->prompt = prompt;
00857     }
00858 
00859 #if __CONFIG_COMPILE_DBGTERM
00860 
00861     if (flags & __TERMINAL_DEBUG_COMMANDS)
00862     {
00863         __terminalAddCommand(term, __dbgCommands, sizeof(__dbgCommands) / sizeof(__TERMINALCMD));
00864     }
00865 
00866 #endif /* __CONFIG_COMPILE_DBGTERM */
00867 
00868     return term;
00869 }
00870 
00892 __VOID __terminalAddCommand(__PTERMINAL term, __PTERMINALCMD cmd, u8 count)
00893 {
00894     u8 i;
00895 
00896     if (!term || !cmd || !count) return;
00897 
00898     for (i = 0; i < count; i++)
00899     {
00900         cmd[i].next = term->cmds;
00901         term->cmds = &cmd[i];
00902     }
00903 }
00904 
00917 __VOID __terminalCreateCommand(__PTERMINAL term, __PSTRING cmd, __TERMINALRXCMD* func)
00918 {
00919     __PTERMINALCMD ptr;
00920 
00921     if (!term) return;
00922 
00923     ptr = __heapAllocZero(sizeof(__TERMINALCMD));
00924     if (!ptr) return;
00925 
00926     ptr->cmd = cmd;
00927     ptr->func = func;
00928     ptr->next = __NULL;
00929 
00930     __terminalAddCommand(term, ptr, 1);
00931 }
00932 
00945 #endif // __CONFIG_COMPILE_TERMINAL
00946 
 All Data Structures Files Functions Variables Typedefs Defines