chat.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. /*
  2. * Copyright (C) 2001-2004 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. /*
  33. * $Id: chat.c 4610 2012-09-17 10:48:26Z haraldkipp $
  34. */
  35. #include <cfg/os.h>
  36. #include <sys/timer.h>
  37. #include <dev/uart.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <io.h>
  41. #include <memdebug.h>
  42. #include <dev/chat.h>
  43. uint8_t *chat_report;
  44. #ifdef NUTDEBUG_CHAT
  45. #include <stdio.h>
  46. static FILE *__chat_trs; /*!< \brief Chat trace output stream. */
  47. static uint8_t __chat_trf; /*!< \brief Chat trace flags. */
  48. /*!
  49. * \brief Control chat tracing.
  50. *
  51. * \param stream Pointer to a previously opened stream or null to
  52. * disable trace output.
  53. * \param flags Flags to enable specific traces.
  54. */
  55. void NutTraceChat(FILE * stream, uint8_t flags)
  56. {
  57. if (stream)
  58. __chat_trs = stream;
  59. if (__chat_trs) {
  60. __chat_trf = flags;
  61. fprintf(__chat_trs, "Chat trace flags=0x%02x\n", flags);
  62. } else
  63. __chat_trf = 0;
  64. }
  65. static INLINE void NutTracePrintf(const char *fmt, ...)
  66. {
  67. if (__chat_trf) {
  68. va_list ap;
  69. va_start(ap, fmt);
  70. vfprintf(__chat_trs, fmt, ap);
  71. va_end(ap);
  72. }
  73. }
  74. static INLINE void NutTracePutChar(int ch)
  75. {
  76. if (__chat_trf) {
  77. if (ch > 31 && ch < 127) {
  78. fputc(ch, __chat_trs);
  79. } else {
  80. fprintf(__chat_trs, "\\x%02x", ch);
  81. }
  82. }
  83. }
  84. #else
  85. #ifdef __GNUC__
  86. #define NutTracePrintf(fmt,...)
  87. #else
  88. static INLINE void NutTracePrintf(const char *fmt, ...) {}
  89. #endif
  90. #define NutTracePutChar(ch)
  91. #endif
  92. /*
  93. * Special version of strchr, handling escaped characters.
  94. */
  95. static char *strechr(const char *str, int c)
  96. {
  97. while (*str) {
  98. if (*str == '\\') {
  99. if (*++str)
  100. str++;
  101. } else if (*str == c)
  102. return (char *) str;
  103. else
  104. str++;
  105. }
  106. return 0;
  107. }
  108. /*!
  109. * \brief Wait for a specific string to appear.
  110. *
  111. * \param ci Pointer to a NUTCHAT structure, which must have been
  112. * created by NutChatCreate().
  113. * \param str Expected string. May be empty if nothing is expected.
  114. *
  115. * \return 0 on success, 3 in case of a timeout error while waiting
  116. * for an expected string, or the index of an abort string
  117. * plus 4, if one has been received.
  118. */
  119. int NutChatExpectString(NUTCHAT * ci, char *str)
  120. {
  121. char ch;
  122. uint8_t m;
  123. uint8_t i;
  124. char *cp = str;
  125. NutTracePrintf("Expect '%s', got '", str);
  126. while (*cp) {
  127. /*
  128. * Read the next character. Return on timeout.
  129. */
  130. if (_read(ci->chat_fd, &ch, 1) != 1) {
  131. NutTracePrintf("' TIMEOUT\n");
  132. return 3;
  133. }
  134. NutTracePutChar(ch);
  135. /*
  136. * If the character doesn't match the next expected one,
  137. * then restart from the beginning of the expected string.
  138. */
  139. if (ch != *cp) {
  140. cp = str;
  141. }
  142. /*
  143. * If the character matched, advance the pointer into
  144. * the expected string.
  145. */
  146. if (ch == *cp) {
  147. cp++;
  148. }
  149. /*
  150. * Check for abort strings.
  151. */
  152. for (i = 0; i < ci->chat_aborts; i++) {
  153. m = ci->chat_abomat[i];
  154. if (ch == ci->chat_abort[i][m]) {
  155. if (ci->chat_abort[i][++m] == 0) {
  156. NutTracePrintf("' ABORT\n");
  157. return i + 4;
  158. }
  159. } else
  160. m = (ch == ci->chat_abort[i][0]);
  161. ci->chat_abomat[i] = m;
  162. }
  163. /*
  164. * Check for report strings.
  165. */
  166. if (ci->chat_report_state > 0) {
  167. m = ci->chat_repmat;
  168. if (ci->chat_report_state == 2) {
  169. chat_report[m++] = ch;
  170. } else if (ch == ci->chat_report_search[m]) {
  171. chat_report[m++] = ch;
  172. if (ci->chat_report_search[m] == 0) {
  173. ci->chat_report_state = 2;
  174. }
  175. } else {
  176. m = (ch == ci->chat_report_search[0]);
  177. }
  178. ci->chat_repmat = m;
  179. }
  180. }
  181. /*
  182. * Read the remainder of the string before NutChatSendString clears it
  183. */
  184. if (ci->chat_report_state == 2) {
  185. m = ci->chat_repmat; /* not needed... (but not nice to remove it) */
  186. while (m < CHAT_MAX_REPORT_SIZE) {
  187. if (_read(ci->chat_fd, &ch, 1) != 1 || ch < ' ') {
  188. break;
  189. }
  190. chat_report[m++] = ch;
  191. NutTracePutChar(ch);
  192. }
  193. ci->chat_report_state = 0; /* Only find first occurence */
  194. chat_report[m] = 0;
  195. }
  196. NutTracePrintf("'\n");
  197. return 0;
  198. }
  199. /*
  200. * \return 0 on success or 2 in case of an I/O error.
  201. */
  202. static int NutChatSendString(int fd, char *str)
  203. {
  204. int rc = 0;
  205. uint8_t eol = 1;
  206. uint8_t skip;
  207. char ch;
  208. NutTracePrintf("Send '%s'\n", str);
  209. /* Flush input buffer. */
  210. _read(fd, 0, 0);
  211. while (*str && eol && rc == 0) {
  212. ch = *str++;
  213. skip = 0;
  214. if (ch == '^') {
  215. ch = *str++;
  216. ch &= 0x1f;
  217. } else if (ch == '\\') {
  218. ch = *str++;
  219. switch (ch) {
  220. case 'b':
  221. ch = '\b';
  222. break;
  223. case 'c':
  224. eol = 0;
  225. skip = 1;
  226. break;
  227. case 'd':
  228. NutSleep(1000);
  229. skip = 1;
  230. break;
  231. case 'n':
  232. ch = '\n';
  233. break;
  234. case 'N':
  235. ch = 0;
  236. break;
  237. case 'p':
  238. NutDelay(100);
  239. skip = 1;
  240. break;
  241. case 'r':
  242. ch = '\r';
  243. break;
  244. case 's':
  245. ch = ' ';
  246. break;
  247. case 't':
  248. ch = '\t';
  249. break;
  250. default:
  251. if (ch >= '0' && ch <= '7') {
  252. ch &= 0x07;
  253. if (*str >= '0' && *str <= '7') {
  254. ch <<= 3;
  255. ch |= *str++ & 0x07;
  256. if (*str >= '0' && *str <= '7') {
  257. ch <<= 3;
  258. ch |= *str++ & 0x07;
  259. }
  260. }
  261. }
  262. break;
  263. }
  264. }
  265. if (skip)
  266. skip = 0;
  267. else {
  268. NutDelay(10);
  269. if (_write(fd, &ch, 1) != 1)
  270. rc = 2;
  271. else
  272. _write(fd, 0, 0);
  273. }
  274. }
  275. if (eol && rc == 0 && _write(fd, "\r", 1) != 1)
  276. rc = 2;
  277. else
  278. _write(fd, 0, 0);
  279. return rc;
  280. }
  281. /*
  282. * \param ci Pointer to a NUTCHAT structure, which must have been
  283. * created by NutChatCreate().
  284. *
  285. * \return 0 on success, 1 in case of invalid parameters, 2 in case
  286. * of an I/O error, 3 in case of a timeout error while waiting
  287. * for an expected string, or the index of an abort string plus
  288. * 4, if one has been received.
  289. */
  290. int NutChatExpect(NUTCHAT * ci, char *str)
  291. {
  292. int rc = 0;
  293. char *reply;
  294. char *subexpect;
  295. /*
  296. * Process special keywords.
  297. */
  298. if (strcmp(str, "ABORT") == 0) {
  299. ci->chat_arg = CHAT_ARG_ABORT;
  300. return 0;
  301. }
  302. if (strcmp(str, "TIMEOUT") == 0) {
  303. ci->chat_arg = CHAT_ARG_TIMEOUT;
  304. return 0;
  305. }
  306. if (strcmp(str, "REPORT") == 0) {
  307. ci->chat_repmat = 0; /* not needed ??? */
  308. ci->chat_report_state = 1;
  309. ci->chat_arg = CHAT_ARG_REPORT;
  310. return 0;
  311. }
  312. /*
  313. * Process expected string.
  314. */
  315. while (str) {
  316. if ((reply = strechr(str, '-')) != 0) {
  317. *reply++ = 0;
  318. if ((subexpect = strechr(reply, '-')) != 0)
  319. *subexpect++ = 0;
  320. } else
  321. subexpect = 0;
  322. if ((rc = NutChatExpectString(ci, str)) != 3 || reply == 0)
  323. break;
  324. if ((rc = NutChatSendString(ci->chat_fd, reply)) != 0)
  325. break;
  326. str = subexpect;
  327. }
  328. return rc;
  329. }
  330. /*!
  331. * \brief Process a chat send argument.
  332. *
  333. * \param ci Pointer to a NUTCHAT structure, which must have been
  334. * created by NutChatCreate().
  335. * \param str String containing the chat send argument.
  336. *
  337. * \return 0 on success, 1 in case of invalid parameters, 2 in case
  338. * of an I/O error, 3 in case of a timeout error while waiting
  339. * for an expected string, or the index of an abort string plus
  340. * 4, if one has been received.
  341. */
  342. int NutChatSend(NUTCHAT * ci, char *str)
  343. {
  344. char *cp;
  345. char ch;
  346. long lv;
  347. /*
  348. * Add a chat abort string.
  349. */
  350. if (ci->chat_arg == CHAT_ARG_ABORT) {
  351. ci->chat_arg = CHAT_ARG_SEND;
  352. if (ci->chat_aborts >= CHAT_MAX_ABORTS)
  353. return 1;
  354. cp = malloc(strlen(str) + 1);
  355. ci->chat_abort[ci->chat_aborts++] = cp;
  356. while (*str) {
  357. ch = *str++;
  358. if (ch == '^')
  359. *cp = *str++ & 0x1f;
  360. else if (ch == '\\') {
  361. ch = *str++;
  362. switch (ch) {
  363. case 'b':
  364. *cp++ = '\b';
  365. break;
  366. case 'n':
  367. *cp++ = '\n';
  368. break;
  369. case 'r':
  370. *cp++ = '\r';
  371. break;
  372. case 's':
  373. *cp++ = ' ';
  374. break;
  375. case 't':
  376. *cp++ = '\t';
  377. break;
  378. default:
  379. if (ch >= '0' && ch <= '7') {
  380. ch &= 0x07;
  381. if (*str >= '0' && *str <= '7') {
  382. ch <<= 3;
  383. ch |= *str++ & 0x07;
  384. if (*str >= '0' && *str <= '7') {
  385. ch <<= 3;
  386. ch |= *str++ & 0x07;
  387. }
  388. }
  389. }
  390. if (ch)
  391. *cp++ = ch;
  392. break;
  393. }
  394. } else
  395. *cp++ = ch;
  396. }
  397. *cp = 0;
  398. return 0;
  399. }
  400. /*
  401. * Set chat timeout.
  402. */
  403. if (ci->chat_arg == CHAT_ARG_TIMEOUT) {
  404. ci->chat_arg = CHAT_ARG_SEND;
  405. lv = atol(str) * 1000L;
  406. if (lv <= 0)
  407. lv = CHAT_DEFAULT_TIMEOUT * 1000L;
  408. _ioctl(ci->chat_fd, UART_SETREADTIMEOUT, &lv);
  409. return 0;
  410. }
  411. /*
  412. * Set report string
  413. */
  414. if (ci->chat_arg == CHAT_ARG_REPORT) {
  415. ci->chat_arg = CHAT_ARG_SEND;
  416. chat_report = malloc(CHAT_MAX_REPORT_SIZE + 1);
  417. cp = malloc(strlen(str) + 1);
  418. ci->chat_report_search = cp;
  419. while (*str)
  420. *cp++ = *str++; /* Do it the easy way, not as smart and thorough as the abort string... */
  421. *cp = 0;
  422. return 0;
  423. }
  424. /*
  425. * Send the argument string.
  426. */
  427. return NutChatSendString(ci->chat_fd, str);
  428. }
  429. /*!
  430. * \brief Create a NUTCHAT structure.
  431. *
  432. * \return Pointer to a new NUTCHAT structure.
  433. */
  434. NUTCHAT *NutChatCreate(int fd)
  435. {
  436. NUTCHAT *ci;
  437. if ((ci = malloc(sizeof(NUTCHAT))) != 0) {
  438. memset(ci, 0, sizeof(NUTCHAT));
  439. ci->chat_fd = fd;
  440. }
  441. return ci;
  442. }
  443. /*!
  444. * \brief Destroy a previously created NUTCHAT structure.
  445. *
  446. * \param ci Pointer to a NUTCHAT structure, which must have been
  447. * created by NutChatCreate().
  448. */
  449. void NutChatDestroy(NUTCHAT * ci)
  450. {
  451. uint8_t i;
  452. if (ci) {
  453. for (i = 0; i < ci->chat_aborts; i++)
  454. free(ci->chat_abort[i]);
  455. free(ci);
  456. }
  457. }
  458. /*!
  459. * \brief Chat processor.
  460. *
  461. * \return 0 on success, 1 in case of invalid parameters, 2 in case
  462. * of an I/O error, 3 in case of a timeout error while waiting
  463. * for an expected string, or the index of an abort string plus
  464. * 4, if one has been received.
  465. */
  466. static int NutChatProc(int fd, char *script)
  467. {
  468. int rc = 0;
  469. char sendflg = 0;
  470. NUTCHAT *ci;
  471. char *arg;
  472. uint32_t to;
  473. uint32_t irto;
  474. uint32_t iwto;
  475. /*
  476. * Initialize the chat info structure.
  477. */
  478. if ((ci = NutChatCreate(fd)) == 0)
  479. return 2;
  480. /*
  481. * Save current and set default timeouts.
  482. */
  483. _ioctl(fd, UART_GETREADTIMEOUT, &irto);
  484. _ioctl(fd, UART_GETWRITETIMEOUT, &iwto);
  485. to = 45000;
  486. _ioctl(fd, UART_SETREADTIMEOUT, &to);
  487. to = 5000;
  488. _ioctl(fd, UART_SETWRITETIMEOUT, &to);
  489. /*
  490. * This loop splits up the chat string into arguments and
  491. * alternating calls NutChatSend and NutChatExpect.
  492. */
  493. while (*script && rc == 0) {
  494. /*
  495. * Skip leading spaces.
  496. */
  497. if (*script == ' ' || *script == '\t' || *script == '\r' || *script == '\n') {
  498. script++;
  499. continue;
  500. }
  501. /*
  502. * Collect a quoted argument.
  503. */
  504. if (*script == '"' || *script == '\'') {
  505. char quote = *script++;
  506. arg = script;
  507. while (*script != quote) {
  508. if (*script == 0) {
  509. rc = 1;
  510. break;
  511. }
  512. if (*script++ == '\\') {
  513. if (*script)
  514. ++script;
  515. }
  516. }
  517. }
  518. /*
  519. * Collect an argument upto the next space.
  520. */
  521. else {
  522. arg = script;
  523. while (*script && *script != ' ' && *script != '\t' && *script != '\r' && *script != '\n')
  524. ++script;
  525. }
  526. if (*script)
  527. *script++ = 0;
  528. /*
  529. * Either send or expect the collected argument.
  530. */
  531. if (rc == 0) {
  532. if (sendflg)
  533. rc = NutChatSend(ci, arg);
  534. else
  535. rc = NutChatExpect(ci, arg);
  536. sendflg = !sendflg;
  537. }
  538. }
  539. /*
  540. * Restore initial timeout values.
  541. */
  542. _ioctl(fd, UART_SETREADTIMEOUT, &irto);
  543. _ioctl(fd, UART_SETWRITETIMEOUT, &iwto);
  544. /*
  545. * Release allocated memory.
  546. */
  547. NutChatDestroy(ci);
  548. return rc;
  549. }
  550. /*!
  551. * \brief Execute a conversational exchange with a serial device.
  552. *
  553. * Its primary purpose is to establish a modem connection.
  554. *
  555. * \param fd Descriptor of a previously opened device.
  556. * \param script Pointer to a string containing the chat script.
  557. *
  558. * \return 0 on success, 1 in case of invalid parameters, 2 in case
  559. * of an I/O error, 3 in case of a timeout error while waiting
  560. * for an expected string, or the index of an abort string plus
  561. * 4, if one has been received.
  562. */
  563. int NutChat(int fd, const char *script)
  564. {
  565. int rc = -1;
  566. char *buf;
  567. /*
  568. * Work with a local copy of the chat string.
  569. */
  570. if ((buf = strdup(script)) != NULL) {
  571. rc = NutChatProc(fd, buf);
  572. free(buf);
  573. }
  574. return rc;
  575. }
  576. /*!
  577. * \brief Execute a conversational exchange with a serial device.
  578. *
  579. * Similar to NutChat() except that the chat string is located in
  580. * program memory.
  581. *
  582. * \return 0 on success, 1 in case of invalid parameters, 2 in case
  583. * of an I/O error, 3 in case of a timeout error while waiting
  584. * for an expected string, or the index of an abort string plus
  585. * 4, if one has been received.
  586. */
  587. #ifdef __HARVARD_ARCH__
  588. int NutChat_P(int fd, PGM_P script)
  589. {
  590. int rc = -1;
  591. char *buf;
  592. /*
  593. * Work with a local copy of the chat string.
  594. */
  595. if ((buf = malloc(strlen_P(script) + 1)) != 0) {
  596. strcpy_P(buf, script);
  597. rc = NutChatProc(fd, buf);
  598. free(buf);
  599. }
  600. return rc;
  601. }
  602. #endif