Nut/OS  4.10.3
API Reference
chat.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2004 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  * $Log$
00035  * Revision 1.11  2009/02/13 14:52:05  haraldkipp
00036  * Include memdebug.h for heap management debugging support.
00037  *
00038  * Revision 1.10  2009/02/06 15:40:29  haraldkipp
00039  * Using newly available strdup() and calloc().
00040  * Replaced NutHeap routines by standard malloc/free.
00041  * Replaced pointer value 0 by NULL.
00042  *
00043  * Revision 1.9  2008/08/11 06:59:41  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.8  2007/08/17 11:34:00  haraldkipp
00047  * Default timeout needs to be multiplied by 1000.
00048  *
00049  * Revision 1.7  2005/04/30 16:42:41  chaac
00050  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00051  * is defined in NutConf, it will make effect where it is used.
00052  *
00053  * Revision 1.6  2004/03/16 16:48:27  haraldkipp
00054  * Added Jan Dubiec's H8/300 port.
00055  *
00056  * Revision 1.5  2004/03/08 11:12:59  haraldkipp
00057  * Debug output added.
00058  *
00059  * Revision 1.4  2004/01/15 08:02:35  drsung
00060  * Copyright added
00061  *
00062  */
00063 
00064 #include <cfg/os.h>
00065 #include <sys/timer.h>
00066 #include <dev/uart.h>
00067 
00068 #include <stdlib.h>
00069 #include <string.h>
00070 #include <io.h>
00071 #include <memdebug.h>
00072 
00073 #include <dev/chat.h>
00074 
00075 uint8_t *chat_report;
00076 
00077 #ifdef NUTDEBUG
00078 
00079 static FILE *__chat_trs;        
00080 static uint8_t __chat_trf;       
00089 void NutTraceChat(FILE * stream, uint8_t flags)
00090 {
00091     if (stream)
00092         __chat_trs = stream;
00093     if (__chat_trs) {
00094         static prog_char dbgfmt[] = "Chat trace flags=0x%02X\n";
00095         __chat_trf = flags;
00096         fprintf_P(__chat_trs, dbgfmt, flags);
00097     } else
00098         __chat_trf = 0;
00099 }
00100 
00101 #endif
00102 
00103 /*
00104  * Special version of strchr, handling escaped characters.
00105  */
00106 static char *strechr(CONST char *str, int c)
00107 {
00108     while (*str) {
00109         if (*str == '\\') {
00110             if (*++str)
00111                 str++;
00112         } else if (*str == c)
00113             return (char *) str;
00114         else
00115             str++;
00116     }
00117     return 0;
00118 }
00119 
00131 int NutChatExpectString(NUTCHAT * ci, char *str)
00132 {
00133     char ch;
00134     uint8_t m;
00135     uint8_t i;
00136     char *cp = str;
00137 
00138 #ifdef NUTDEBUG
00139     if (__chat_trf) {
00140         static prog_char dbgfmt[] = "Expect '%s', got '";
00141         fprintf_P(__chat_trs, dbgfmt, str);
00142     }
00143 #endif
00144 
00145     while (*cp) {
00146 
00147         /*
00148          * Read the next character. Return on timeout.
00149          */
00150         if (_read(ci->chat_fd, &ch, 1) != 1) {
00151 #ifdef NUTDEBUG
00152             if (__chat_trf) {
00153                 static prog_char dbgmsg[] = "' TIMEOUT\n";
00154                 fputs_P(dbgmsg, __chat_trs);
00155             }
00156 #endif
00157             return 3;
00158         }
00159 #ifdef NUTDEBUG
00160         if (__chat_trf) {
00161             if (ch > 31 && ch < 127) {
00162                 fputc(ch, __chat_trs);
00163             } else {
00164                 fprintf(__chat_trs, "\\x%02X", ch);
00165             }
00166         }
00167 #endif
00168         /*
00169          * If the character doesn't match the next expected one,
00170          * then restart from the beginning of the expected string.
00171          */
00172         if (ch != *cp) {
00173             cp = str;
00174         }
00175 
00176         /*
00177          * If the character matched, advance the pointer into
00178          * the expected string.
00179          */
00180         if (ch == *cp) {
00181             cp++;
00182         }
00183 
00184         /*
00185          * Check for abort strings.
00186          */
00187         for (i = 0; i < ci->chat_aborts; i++) {
00188             m = ci->chat_abomat[i];
00189             if (ch == ci->chat_abort[i][m]) {
00190                 if (ci->chat_abort[i][++m] == 0) {
00191 #ifdef NUTDEBUG
00192                     if (__chat_trf) {
00193                         static prog_char dbgmsg[] = "' ABORT\n";
00194                         fputs_P(dbgmsg, __chat_trs);
00195                     }
00196 #endif
00197                     return i + 4;
00198                 }
00199             } else
00200                 m = (ch == ci->chat_abort[i][0]);
00201             ci->chat_abomat[i] = m;
00202         }
00203 
00204         /*
00205          * Check for report strings.
00206          */
00207         if (ci->chat_report_state > 0) {
00208             m = ci->chat_repmat;
00209             if (ci->chat_report_state == 2) {
00210                 chat_report[m++] = ch;
00211             } else if (ch == ci->chat_report_search[m]) {
00212                 chat_report[m++] = ch;
00213                 if (ci->chat_report_search[m] == 0) {
00214                     ci->chat_report_state = 2;
00215                 }
00216             } else {
00217                 m = (ch == ci->chat_report_search[0]);
00218             }
00219             ci->chat_repmat = m;
00220         }
00221     }
00222 
00223     /*
00224      * Read the remainder of the string before NutChatSendString clears it
00225      */
00226     if (ci->chat_report_state == 2) {
00227         m = ci->chat_repmat;    /* not needed... (but not nice to remove it) */
00228         while (m < CHAT_MAX_REPORT_SIZE) {
00229             if (_read(ci->chat_fd, &ch, 1) != 1 || ch < ' ') {
00230                 break;
00231             }
00232             chat_report[m++] = ch;
00233 
00234 #ifdef NUTDEBUG
00235             if (__chat_trf) {
00236                 if (ch > 31 && ch < 127) {
00237                     fputc(ch, __chat_trs);
00238                 } else {
00239                     fprintf(__chat_trs, "\\x%02X", ch);
00240                 }
00241             }
00242 #endif
00243         }
00244         ci->chat_report_state = 0;      /* Only find first occurence */
00245         chat_report[m] = 0;
00246     }
00247 #ifdef NUTDEBUG
00248     if (__chat_trf) {
00249         static prog_char dbgmsg[] = "'\n";
00250         fputs_P(dbgmsg, __chat_trs);
00251     }
00252 #endif
00253 
00254     return 0;
00255 }
00256 
00257 /*
00258  * \return 0 on success or 2 in case of an I/O error.
00259  */
00260 static int NutChatSendString(int fd, char *str)
00261 {
00262     int rc = 0;
00263     uint8_t eol = 1;
00264     uint8_t skip;
00265     char ch;
00266 
00267 #ifdef NUTDEBUG
00268     if (__chat_trf) {
00269         static prog_char dbgfmt[] = "Send '%s'\n";
00270         fprintf_P(__chat_trs, dbgfmt, str);
00271     }
00272 #endif
00273 
00274     /* Flush input buffer. */
00275     _read(fd, 0, 0);
00276     while (*str && eol && rc == 0) {
00277         ch = *str++;
00278         skip = 0;
00279         if (ch == '^') {
00280             ch = *str++;
00281             ch &= 0x1f;
00282         } else if (ch == '\\') {
00283             ch = *str++;
00284             switch (ch) {
00285             case 'b':
00286                 ch = '\b';
00287                 break;
00288             case 'c':
00289                 eol = 0;
00290                 skip = 1;
00291                 break;
00292             case 'd':
00293                 NutSleep(1000);
00294                 skip = 1;
00295                 break;
00296             case 'n':
00297                 ch = '\n';
00298                 break;
00299             case 'N':
00300                 ch = 0;
00301                 break;
00302             case 'p':
00303                 NutDelay(100);
00304                 skip = 1;
00305                 break;
00306             case 'r':
00307                 ch = '\r';
00308                 break;
00309             case 's':
00310                 ch = ' ';
00311                 break;
00312             case 't':
00313                 ch = '\t';
00314                 break;
00315             default:
00316                 if (ch >= '0' && ch <= '7') {
00317                     ch &= 0x07;
00318                     if (*str >= '0' && *str <= '7') {
00319                         ch <<= 3;
00320                         ch |= *str++ & 0x07;
00321                         if (*str >= '0' && *str <= '7') {
00322                             ch <<= 3;
00323                             ch |= *str++ & 0x07;
00324                         }
00325                     }
00326                 }
00327                 break;
00328             }
00329         }
00330         if (skip)
00331             skip = 0;
00332         else {
00333             NutDelay(10);
00334             if (_write(fd, &ch, 1) != 1)
00335                 rc = 2;
00336             else
00337                 _write(fd, 0, 0);
00338         }
00339     }
00340     if (eol && rc == 0 && _write(fd, "\r", 1) != 1)
00341         rc = 2;
00342     else
00343         _write(fd, 0, 0);
00344 
00345     return rc;
00346 }
00347 
00348 /*
00349  * \param ci Pointer to a NUTCHAT structure, which must have been 
00350  *           created by NutChatCreate().
00351  *
00352  * \return 0 on success, 1 in case of invalid parameters, 2 in case
00353  *         of an I/O error, 3 in case of a timeout error while waiting
00354  *         for an expected string, or the index of an abort string plus
00355  *         4, if one has been received. 
00356  */
00357 int NutChatExpect(NUTCHAT * ci, char *str)
00358 {
00359     int rc = 0;
00360     char *reply;
00361     char *subexpect;
00362 
00363     /*
00364      * Process special keywords.
00365      */
00366     if (strcmp(str, "ABORT") == 0) {
00367         ci->chat_arg = CHAT_ARG_ABORT;
00368         return 0;
00369     }
00370     if (strcmp(str, "TIMEOUT") == 0) {
00371         ci->chat_arg = CHAT_ARG_TIMEOUT;
00372         return 0;
00373     }
00374     if (strcmp(str, "REPORT") == 0) {
00375         ci->chat_repmat = 0;    /* not needed ??? */
00376         ci->chat_report_state = 1;
00377         ci->chat_arg = CHAT_ARG_REPORT;
00378         return 0;
00379     }
00380 
00381     /*
00382      * Process expected string.
00383      */
00384     while (str) {
00385         if ((reply = strechr(str, '-')) != 0) {
00386             *reply++ = 0;
00387             if ((subexpect = strechr(reply, '-')) != 0)
00388                 *subexpect++ = 0;
00389         } else
00390             subexpect = 0;
00391 
00392         if ((rc = NutChatExpectString(ci, str)) != 3 || reply == 0)
00393             break;
00394         if ((rc = NutChatSendString(ci->chat_fd, reply)) != 0)
00395             break;
00396         str = subexpect;
00397     }
00398     return rc;
00399 }
00400 
00413 int NutChatSend(NUTCHAT * ci, char *str)
00414 {
00415     char *cp;
00416     char ch;
00417     long lv;
00418 
00419     /*
00420      * Add a chat abort string.
00421      */
00422     if (ci->chat_arg == CHAT_ARG_ABORT) {
00423         ci->chat_arg = CHAT_ARG_SEND;
00424 
00425         if (ci->chat_aborts >= CHAT_MAX_ABORTS)
00426             return 1;
00427         cp = malloc(strlen(str) + 1);
00428         ci->chat_abort[ci->chat_aborts++] = cp;
00429         while (*str) {
00430             ch = *str++;
00431             if (ch == '^')
00432                 *cp = *str++ & 0x1f;
00433             else if (ch == '\\') {
00434                 ch = *str++;
00435                 switch (ch) {
00436                 case 'b':
00437                     *cp++ = '\b';
00438                     break;
00439                 case 'n':
00440                     *cp++ = '\n';
00441                     break;
00442                 case 'r':
00443                     *cp++ = '\r';
00444                     break;
00445                 case 's':
00446                     *cp++ = ' ';
00447                     break;
00448                 case 't':
00449                     *cp++ = '\t';
00450                     break;
00451                 default:
00452                     if (ch >= '0' && ch <= '7') {
00453                         ch &= 0x07;
00454                         if (*str >= '0' && *str <= '7') {
00455                             ch <<= 3;
00456                             ch |= *str++ & 0x07;
00457                             if (*str >= '0' && *str <= '7') {
00458                                 ch <<= 3;
00459                                 ch |= *str++ & 0x07;
00460                             }
00461                         }
00462                     }
00463                     if (ch)
00464                         *cp++ = ch;
00465                     break;
00466                 }
00467             } else
00468                 *cp++ = ch;
00469         }
00470         *cp = 0;
00471         return 0;
00472     }
00473 
00474     /*
00475      * Set chat timeout.
00476      */
00477     if (ci->chat_arg == CHAT_ARG_TIMEOUT) {
00478         ci->chat_arg = CHAT_ARG_SEND;
00479 
00480         lv = atol(str) * 1000L;
00481         if (lv <= 0)
00482             lv = CHAT_DEFAULT_TIMEOUT * 1000L;
00483 
00484         _ioctl(ci->chat_fd, UART_SETREADTIMEOUT, &lv);
00485 
00486         return 0;
00487     }
00488 
00489     /*
00490      * Set report string
00491      */
00492     if (ci->chat_arg == CHAT_ARG_REPORT) {
00493         ci->chat_arg = CHAT_ARG_SEND;
00494         chat_report = malloc(CHAT_MAX_REPORT_SIZE + 1);
00495         cp = malloc(strlen(str) + 1);
00496         ci->chat_report_search = cp;
00497         while (*str)
00498             *cp++ = *str++;     /* Do it the easy way, not as smart and thorough as the abort string... */
00499         *cp = 0;
00500 
00501         return 0;
00502     }
00503 
00504     /*
00505      * Send the argument string.
00506      */
00507     return NutChatSendString(ci->chat_fd, str);
00508 }
00509 
00510 
00516 NUTCHAT *NutChatCreate(int fd)
00517 {
00518     NUTCHAT *ci;
00519 
00520     if ((ci = malloc(sizeof(NUTCHAT))) != 0) {
00521         memset(ci, 0, sizeof(NUTCHAT));
00522         ci->chat_fd = fd;
00523     }
00524     return ci;
00525 }
00526 
00533 void NutChatDestroy(NUTCHAT * ci)
00534 {
00535     uint8_t i;
00536 
00537     if (ci) {
00538         for (i = 0; i < ci->chat_aborts; i++)
00539             free(ci->chat_abort[i]);
00540         free(ci);
00541     }
00542 }
00543 
00552 static int NutChatProc(int fd, char *script)
00553 {
00554     int rc = 0;
00555     char sendflg = 0;
00556     NUTCHAT *ci;
00557     char *arg;
00558     uint32_t to;
00559     uint32_t irto;
00560     uint32_t iwto;
00561 
00562     /*
00563      * Initialize the chat info structure.
00564      */
00565     if ((ci = NutChatCreate(fd)) == 0)
00566         return 2;
00567 
00568     /*
00569      * Save current and set default timeouts.
00570      */
00571     _ioctl(fd, UART_GETREADTIMEOUT, &irto);
00572     _ioctl(fd, UART_GETWRITETIMEOUT, &iwto);
00573     to = 45000;
00574     _ioctl(fd, UART_SETREADTIMEOUT, &to);
00575     to = 5000;
00576     _ioctl(fd, UART_SETWRITETIMEOUT, &to);
00577 
00578     /*
00579      * This loop splits up the chat string into arguments and 
00580      * alternating calls NutChatSend and NutChatExpect.
00581      */
00582     while (*script && rc == 0) {
00583 
00584         /*
00585          * Skip leading spaces.
00586          */
00587         if (*script == ' ' || *script == '\t' || *script == '\r' || *script == '\n') {
00588             script++;
00589             continue;
00590         }
00591 
00592         /*
00593          * Collect a quoted argument.
00594          */
00595         if (*script == '"' || *script == '\'') {
00596             char quote = *script++;
00597 
00598             arg = script;
00599             while (*script != quote) {
00600                 if (*script == 0) {
00601                     rc = 1;
00602                     break;
00603                 }
00604                 if (*script++ == '\\') {
00605                     if (*script)
00606                         ++script;
00607                 }
00608             }
00609         }
00610 
00611         /*
00612          * Collect an argument upto the next space.
00613          */
00614         else {
00615             arg = script;
00616             while (*script && *script != ' ' && *script != '\t' && *script != '\r' && *script != '\n')
00617                 ++script;
00618         }
00619 
00620         if (*script)
00621             *script++ = 0;
00622 
00623         /*
00624          * Either send or expect the collected argument.
00625          */
00626         if (rc == 0) {
00627             if (sendflg)
00628                 rc = NutChatSend(ci, arg);
00629             else
00630                 rc = NutChatExpect(ci, arg);
00631             sendflg = !sendflg;
00632         }
00633     }
00634 
00635     /*
00636      * Restore initial timeout values.
00637      */
00638     _ioctl(fd, UART_SETREADTIMEOUT, &irto);
00639     _ioctl(fd, UART_SETWRITETIMEOUT, &iwto);
00640 
00641     /*
00642      * Release allocated memory.
00643      */
00644     NutChatDestroy(ci);
00645 
00646     return rc;
00647 }
00648 
00662 int NutChat(int fd, CONST char *script)
00663 {
00664     int rc = -1;
00665     char *buf;
00666 
00667     /*
00668      * Work with a local copy of the chat string.
00669      */
00670     if ((buf = strdup(script)) != NULL) {
00671         rc = NutChatProc(fd, buf);
00672         free(buf);
00673     }
00674     return rc;
00675 }
00676 
00688 #ifdef __HARVARD_ARCH__
00689 int NutChat_P(int fd, PGM_P script)
00690 {
00691     int rc = -1;
00692     char *buf;
00693 
00694     /*
00695      * Work with a local copy of the chat string.
00696      */
00697     if ((buf = malloc(strlen_P(script) + 1)) != 0) {
00698         strcpy_P(buf, script);
00699         rc = NutChatProc(fd, buf);
00700         free(buf);
00701     }
00702     return rc;
00703 }
00704 #endif