| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- /*
- * Copyright (C) 2001-2004 by egnite Software GmbH. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*
- * $Id: chat.c 4610 2012-09-17 10:48:26Z haraldkipp $
- */
- #include <cfg/os.h>
- #include <sys/timer.h>
- #include <dev/uart.h>
- #include <stdlib.h>
- #include <string.h>
- #include <io.h>
- #include <memdebug.h>
- #include <dev/chat.h>
- uint8_t *chat_report;
- #ifdef NUTDEBUG_CHAT
- #include <stdio.h>
- static FILE *__chat_trs; /*!< \brief Chat trace output stream. */
- static uint8_t __chat_trf; /*!< \brief Chat trace flags. */
- /*!
- * \brief Control chat tracing.
- *
- * \param stream Pointer to a previously opened stream or null to
- * disable trace output.
- * \param flags Flags to enable specific traces.
- */
- void NutTraceChat(FILE * stream, uint8_t flags)
- {
- if (stream)
- __chat_trs = stream;
- if (__chat_trs) {
- __chat_trf = flags;
- fprintf(__chat_trs, "Chat trace flags=0x%02x\n", flags);
- } else
- __chat_trf = 0;
- }
- static INLINE void NutTracePrintf(const char *fmt, ...)
- {
- if (__chat_trf) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(__chat_trs, fmt, ap);
- va_end(ap);
- }
- }
- static INLINE void NutTracePutChar(int ch)
- {
- if (__chat_trf) {
- if (ch > 31 && ch < 127) {
- fputc(ch, __chat_trs);
- } else {
- fprintf(__chat_trs, "\\x%02x", ch);
- }
- }
- }
- #else
- #ifdef __GNUC__
- #define NutTracePrintf(fmt,...)
- #else
- static INLINE void NutTracePrintf(const char *fmt, ...) {}
- #endif
- #define NutTracePutChar(ch)
- #endif
- /*
- * Special version of strchr, handling escaped characters.
- */
- static char *strechr(const char *str, int c)
- {
- while (*str) {
- if (*str == '\\') {
- if (*++str)
- str++;
- } else if (*str == c)
- return (char *) str;
- else
- str++;
- }
- return 0;
- }
- /*!
- * \brief Wait for a specific string to appear.
- *
- * \param ci Pointer to a NUTCHAT structure, which must have been
- * created by NutChatCreate().
- * \param str Expected string. May be empty if nothing is expected.
- *
- * \return 0 on success, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string
- * plus 4, if one has been received.
- */
- int NutChatExpectString(NUTCHAT * ci, char *str)
- {
- char ch;
- uint8_t m;
- uint8_t i;
- char *cp = str;
- NutTracePrintf("Expect '%s', got '", str);
- while (*cp) {
- /*
- * Read the next character. Return on timeout.
- */
- if (_read(ci->chat_fd, &ch, 1) != 1) {
- NutTracePrintf("' TIMEOUT\n");
- return 3;
- }
- NutTracePutChar(ch);
- /*
- * If the character doesn't match the next expected one,
- * then restart from the beginning of the expected string.
- */
- if (ch != *cp) {
- cp = str;
- }
- /*
- * If the character matched, advance the pointer into
- * the expected string.
- */
- if (ch == *cp) {
- cp++;
- }
- /*
- * Check for abort strings.
- */
- for (i = 0; i < ci->chat_aborts; i++) {
- m = ci->chat_abomat[i];
- if (ch == ci->chat_abort[i][m]) {
- if (ci->chat_abort[i][++m] == 0) {
- NutTracePrintf("' ABORT\n");
- return i + 4;
- }
- } else
- m = (ch == ci->chat_abort[i][0]);
- ci->chat_abomat[i] = m;
- }
- /*
- * Check for report strings.
- */
- if (ci->chat_report_state > 0) {
- m = ci->chat_repmat;
- if (ci->chat_report_state == 2) {
- chat_report[m++] = ch;
- } else if (ch == ci->chat_report_search[m]) {
- chat_report[m++] = ch;
- if (ci->chat_report_search[m] == 0) {
- ci->chat_report_state = 2;
- }
- } else {
- m = (ch == ci->chat_report_search[0]);
- }
- ci->chat_repmat = m;
- }
- }
- /*
- * Read the remainder of the string before NutChatSendString clears it
- */
- if (ci->chat_report_state == 2) {
- m = ci->chat_repmat; /* not needed... (but not nice to remove it) */
- while (m < CHAT_MAX_REPORT_SIZE) {
- if (_read(ci->chat_fd, &ch, 1) != 1 || ch < ' ') {
- break;
- }
- chat_report[m++] = ch;
- NutTracePutChar(ch);
- }
- ci->chat_report_state = 0; /* Only find first occurence */
- chat_report[m] = 0;
- }
- NutTracePrintf("'\n");
- return 0;
- }
- /*
- * \return 0 on success or 2 in case of an I/O error.
- */
- static int NutChatSendString(int fd, char *str)
- {
- int rc = 0;
- uint8_t eol = 1;
- uint8_t skip;
- char ch;
- NutTracePrintf("Send '%s'\n", str);
- /* Flush input buffer. */
- _read(fd, 0, 0);
- while (*str && eol && rc == 0) {
- ch = *str++;
- skip = 0;
- if (ch == '^') {
- ch = *str++;
- ch &= 0x1f;
- } else if (ch == '\\') {
- ch = *str++;
- switch (ch) {
- case 'b':
- ch = '\b';
- break;
- case 'c':
- eol = 0;
- skip = 1;
- break;
- case 'd':
- NutSleep(1000);
- skip = 1;
- break;
- case 'n':
- ch = '\n';
- break;
- case 'N':
- ch = 0;
- break;
- case 'p':
- NutDelay(100);
- skip = 1;
- break;
- case 'r':
- ch = '\r';
- break;
- case 's':
- ch = ' ';
- break;
- case 't':
- ch = '\t';
- break;
- default:
- if (ch >= '0' && ch <= '7') {
- ch &= 0x07;
- if (*str >= '0' && *str <= '7') {
- ch <<= 3;
- ch |= *str++ & 0x07;
- if (*str >= '0' && *str <= '7') {
- ch <<= 3;
- ch |= *str++ & 0x07;
- }
- }
- }
- break;
- }
- }
- if (skip)
- skip = 0;
- else {
- NutDelay(10);
- if (_write(fd, &ch, 1) != 1)
- rc = 2;
- else
- _write(fd, 0, 0);
- }
- }
- if (eol && rc == 0 && _write(fd, "\r", 1) != 1)
- rc = 2;
- else
- _write(fd, 0, 0);
- return rc;
- }
- /*
- * \param ci Pointer to a NUTCHAT structure, which must have been
- * created by NutChatCreate().
- *
- * \return 0 on success, 1 in case of invalid parameters, 2 in case
- * of an I/O error, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string plus
- * 4, if one has been received.
- */
- int NutChatExpect(NUTCHAT * ci, char *str)
- {
- int rc = 0;
- char *reply;
- char *subexpect;
- /*
- * Process special keywords.
- */
- if (strcmp(str, "ABORT") == 0) {
- ci->chat_arg = CHAT_ARG_ABORT;
- return 0;
- }
- if (strcmp(str, "TIMEOUT") == 0) {
- ci->chat_arg = CHAT_ARG_TIMEOUT;
- return 0;
- }
- if (strcmp(str, "REPORT") == 0) {
- ci->chat_repmat = 0; /* not needed ??? */
- ci->chat_report_state = 1;
- ci->chat_arg = CHAT_ARG_REPORT;
- return 0;
- }
- /*
- * Process expected string.
- */
- while (str) {
- if ((reply = strechr(str, '-')) != 0) {
- *reply++ = 0;
- if ((subexpect = strechr(reply, '-')) != 0)
- *subexpect++ = 0;
- } else
- subexpect = 0;
- if ((rc = NutChatExpectString(ci, str)) != 3 || reply == 0)
- break;
- if ((rc = NutChatSendString(ci->chat_fd, reply)) != 0)
- break;
- str = subexpect;
- }
- return rc;
- }
- /*!
- * \brief Process a chat send argument.
- *
- * \param ci Pointer to a NUTCHAT structure, which must have been
- * created by NutChatCreate().
- * \param str String containing the chat send argument.
- *
- * \return 0 on success, 1 in case of invalid parameters, 2 in case
- * of an I/O error, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string plus
- * 4, if one has been received.
- */
- int NutChatSend(NUTCHAT * ci, char *str)
- {
- char *cp;
- char ch;
- long lv;
- /*
- * Add a chat abort string.
- */
- if (ci->chat_arg == CHAT_ARG_ABORT) {
- ci->chat_arg = CHAT_ARG_SEND;
- if (ci->chat_aborts >= CHAT_MAX_ABORTS)
- return 1;
- cp = malloc(strlen(str) + 1);
- ci->chat_abort[ci->chat_aborts++] = cp;
- while (*str) {
- ch = *str++;
- if (ch == '^')
- *cp = *str++ & 0x1f;
- else if (ch == '\\') {
- ch = *str++;
- switch (ch) {
- case 'b':
- *cp++ = '\b';
- break;
- case 'n':
- *cp++ = '\n';
- break;
- case 'r':
- *cp++ = '\r';
- break;
- case 's':
- *cp++ = ' ';
- break;
- case 't':
- *cp++ = '\t';
- break;
- default:
- if (ch >= '0' && ch <= '7') {
- ch &= 0x07;
- if (*str >= '0' && *str <= '7') {
- ch <<= 3;
- ch |= *str++ & 0x07;
- if (*str >= '0' && *str <= '7') {
- ch <<= 3;
- ch |= *str++ & 0x07;
- }
- }
- }
- if (ch)
- *cp++ = ch;
- break;
- }
- } else
- *cp++ = ch;
- }
- *cp = 0;
- return 0;
- }
- /*
- * Set chat timeout.
- */
- if (ci->chat_arg == CHAT_ARG_TIMEOUT) {
- ci->chat_arg = CHAT_ARG_SEND;
- lv = atol(str) * 1000L;
- if (lv <= 0)
- lv = CHAT_DEFAULT_TIMEOUT * 1000L;
- _ioctl(ci->chat_fd, UART_SETREADTIMEOUT, &lv);
- return 0;
- }
- /*
- * Set report string
- */
- if (ci->chat_arg == CHAT_ARG_REPORT) {
- ci->chat_arg = CHAT_ARG_SEND;
- chat_report = malloc(CHAT_MAX_REPORT_SIZE + 1);
- cp = malloc(strlen(str) + 1);
- ci->chat_report_search = cp;
- while (*str)
- *cp++ = *str++; /* Do it the easy way, not as smart and thorough as the abort string... */
- *cp = 0;
- return 0;
- }
- /*
- * Send the argument string.
- */
- return NutChatSendString(ci->chat_fd, str);
- }
- /*!
- * \brief Create a NUTCHAT structure.
- *
- * \return Pointer to a new NUTCHAT structure.
- */
- NUTCHAT *NutChatCreate(int fd)
- {
- NUTCHAT *ci;
- if ((ci = malloc(sizeof(NUTCHAT))) != 0) {
- memset(ci, 0, sizeof(NUTCHAT));
- ci->chat_fd = fd;
- }
- return ci;
- }
- /*!
- * \brief Destroy a previously created NUTCHAT structure.
- *
- * \param ci Pointer to a NUTCHAT structure, which must have been
- * created by NutChatCreate().
- */
- void NutChatDestroy(NUTCHAT * ci)
- {
- uint8_t i;
- if (ci) {
- for (i = 0; i < ci->chat_aborts; i++)
- free(ci->chat_abort[i]);
- free(ci);
- }
- }
- /*!
- * \brief Chat processor.
- *
- * \return 0 on success, 1 in case of invalid parameters, 2 in case
- * of an I/O error, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string plus
- * 4, if one has been received.
- */
- static int NutChatProc(int fd, char *script)
- {
- int rc = 0;
- char sendflg = 0;
- NUTCHAT *ci;
- char *arg;
- uint32_t to;
- uint32_t irto;
- uint32_t iwto;
- /*
- * Initialize the chat info structure.
- */
- if ((ci = NutChatCreate(fd)) == 0)
- return 2;
- /*
- * Save current and set default timeouts.
- */
- _ioctl(fd, UART_GETREADTIMEOUT, &irto);
- _ioctl(fd, UART_GETWRITETIMEOUT, &iwto);
- to = 45000;
- _ioctl(fd, UART_SETREADTIMEOUT, &to);
- to = 5000;
- _ioctl(fd, UART_SETWRITETIMEOUT, &to);
- /*
- * This loop splits up the chat string into arguments and
- * alternating calls NutChatSend and NutChatExpect.
- */
- while (*script && rc == 0) {
- /*
- * Skip leading spaces.
- */
- if (*script == ' ' || *script == '\t' || *script == '\r' || *script == '\n') {
- script++;
- continue;
- }
- /*
- * Collect a quoted argument.
- */
- if (*script == '"' || *script == '\'') {
- char quote = *script++;
- arg = script;
- while (*script != quote) {
- if (*script == 0) {
- rc = 1;
- break;
- }
- if (*script++ == '\\') {
- if (*script)
- ++script;
- }
- }
- }
- /*
- * Collect an argument upto the next space.
- */
- else {
- arg = script;
- while (*script && *script != ' ' && *script != '\t' && *script != '\r' && *script != '\n')
- ++script;
- }
- if (*script)
- *script++ = 0;
- /*
- * Either send or expect the collected argument.
- */
- if (rc == 0) {
- if (sendflg)
- rc = NutChatSend(ci, arg);
- else
- rc = NutChatExpect(ci, arg);
- sendflg = !sendflg;
- }
- }
- /*
- * Restore initial timeout values.
- */
- _ioctl(fd, UART_SETREADTIMEOUT, &irto);
- _ioctl(fd, UART_SETWRITETIMEOUT, &iwto);
- /*
- * Release allocated memory.
- */
- NutChatDestroy(ci);
- return rc;
- }
- /*!
- * \brief Execute a conversational exchange with a serial device.
- *
- * Its primary purpose is to establish a modem connection.
- *
- * \param fd Descriptor of a previously opened device.
- * \param script Pointer to a string containing the chat script.
- *
- * \return 0 on success, 1 in case of invalid parameters, 2 in case
- * of an I/O error, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string plus
- * 4, if one has been received.
- */
- int NutChat(int fd, const char *script)
- {
- int rc = -1;
- char *buf;
- /*
- * Work with a local copy of the chat string.
- */
- if ((buf = strdup(script)) != NULL) {
- rc = NutChatProc(fd, buf);
- free(buf);
- }
- return rc;
- }
- /*!
- * \brief Execute a conversational exchange with a serial device.
- *
- * Similar to NutChat() except that the chat string is located in
- * program memory.
- *
- * \return 0 on success, 1 in case of invalid parameters, 2 in case
- * of an I/O error, 3 in case of a timeout error while waiting
- * for an expected string, or the index of an abort string plus
- * 4, if one has been received.
- */
- #ifdef __HARVARD_ARCH__
- int NutChat_P(int fd, PGM_P script)
- {
- int rc = -1;
- char *buf;
- /*
- * Work with a local copy of the chat string.
- */
- if ((buf = malloc(strlen_P(script) + 1)) != 0) {
- strcpy_P(buf, script);
- rc = NutChatProc(fd, buf);
- free(buf);
- }
- return rc;
- }
- #endif
|