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

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