ATCmdParser.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /* Copyright (c) 2017 ARM Limited
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. *
  15. * @section DESCRIPTION
  16. *
  17. * Parser for the AT command syntax
  18. *
  19. */
  20. #include "ATCmdParser.h"
  21. #include "mbed_poll.h"
  22. #include "mbed_debug.h"
  23. #ifdef LF
  24. #undef LF
  25. #define LF 10
  26. #else
  27. #define LF 10
  28. #endif
  29. #ifdef CR
  30. #undef CR
  31. #define CR 13
  32. #else
  33. #define CR 13
  34. #endif
  35. // getc/putc handling with timeouts
  36. int ATCmdParser::putc(char c)
  37. {
  38. pollfh fhs;
  39. fhs.fh = _fh;
  40. fhs.events = POLLOUT;
  41. int count = poll(&fhs, 1, _timeout);
  42. if (count > 0 && (fhs.revents & POLLOUT)) {
  43. return _fh->write(&c, 1) == 1 ? 0 : -1;
  44. } else {
  45. return -1;
  46. }
  47. }
  48. int ATCmdParser::getc()
  49. {
  50. pollfh fhs;
  51. fhs.fh = _fh;
  52. fhs.events = POLLIN;
  53. int count = poll(&fhs, 1, _timeout);
  54. if (count > 0 && (fhs.revents & POLLIN)) {
  55. unsigned char ch;
  56. return _fh->read(&ch, 1) == 1 ? ch : -1;
  57. } else {
  58. return -1;
  59. }
  60. }
  61. void ATCmdParser::flush()
  62. {
  63. while (_fh->readable()) {
  64. unsigned char ch;
  65. _fh->read(&ch, 1);
  66. }
  67. }
  68. // read/write handling with timeouts
  69. int ATCmdParser::write(const char *data, int size)
  70. {
  71. int i = 0;
  72. for (; i < size; i++) {
  73. if (putc(data[i]) < 0) {
  74. return -1;
  75. }
  76. }
  77. return i;
  78. }
  79. int ATCmdParser::read(char *data, int size)
  80. {
  81. int i = 0;
  82. for (; i < size; i++) {
  83. int c = getc();
  84. if (c < 0) {
  85. return -1;
  86. }
  87. data[i] = c;
  88. }
  89. return i;
  90. }
  91. // printf/scanf handling
  92. int ATCmdParser::vprintf(const char *format, va_list args)
  93. {
  94. if (vsprintf(_buffer, format, args) < 0) {
  95. return false;
  96. }
  97. int i = 0;
  98. for (; _buffer[i]; i++) {
  99. if (putc(_buffer[i]) < 0) {
  100. return -1;
  101. }
  102. }
  103. return i;
  104. }
  105. int ATCmdParser::vscanf(const char *format, va_list args)
  106. {
  107. // Since format is const, we need to copy it into our buffer to
  108. // add the line's null terminator and clobber value-matches with asterisks.
  109. //
  110. // We just use the beginning of the buffer to avoid unnecessary allocations.
  111. int i = 0;
  112. int offset = 0;
  113. while (format[i]) {
  114. if (format[i] == '%' && format[i + 1] != '%' && format[i + 1] != '*') {
  115. _buffer[offset++] = '%';
  116. _buffer[offset++] = '*';
  117. i++;
  118. } else {
  119. _buffer[offset++] = format[i++];
  120. }
  121. }
  122. // Scanf has very poor support for catching errors
  123. // fortunately, we can abuse the %n specifier to determine
  124. // if the entire string was matched.
  125. _buffer[offset++] = '%';
  126. _buffer[offset++] = 'n';
  127. _buffer[offset++] = 0;
  128. // To workaround scanf's lack of error reporting, we actually
  129. // make two passes. One checks the validity with the modified
  130. // format string that only stores the matched characters (%n).
  131. // The other reads in the actual matched values.
  132. //
  133. // We keep trying the match until we succeed or some other error
  134. // derails us.
  135. int j = 0;
  136. while (true) {
  137. // Ran out of space
  138. if (j + 1 >= _buffer_size - offset) {
  139. return false;
  140. }
  141. // Receive next character
  142. int c = getc();
  143. if (c < 0) {
  144. return -1;
  145. }
  146. _buffer[offset + j++] = c;
  147. _buffer[offset + j] = 0;
  148. // Check for match
  149. int count = -1;
  150. sscanf(_buffer + offset, _buffer, &count);
  151. // We only succeed if all characters in the response are matched
  152. if (count == j) {
  153. // Store the found results
  154. vsscanf(_buffer + offset, format, args);
  155. return j;
  156. }
  157. }
  158. }
  159. // Command parsing with line handling
  160. bool ATCmdParser::vsend(const char *command, va_list args)
  161. {
  162. // Create and send command
  163. if (vsprintf(_buffer, command, args) < 0) {
  164. return false;
  165. }
  166. for (int i = 0; _buffer[i]; i++) {
  167. if (putc(_buffer[i]) < 0) {
  168. return false;
  169. }
  170. }
  171. // Finish with newline
  172. for (size_t i = 0; _output_delimiter[i]; i++) {
  173. if (putc(_output_delimiter[i]) < 0) {
  174. return false;
  175. }
  176. }
  177. debug_if(_dbg_on, "AT> %s\n", _buffer);
  178. return true;
  179. }
  180. bool ATCmdParser::vrecv(const char *response, va_list args)
  181. {
  182. restart:
  183. _aborted = false;
  184. // Iterate through each line in the expected response
  185. while (response[0]) {
  186. // Since response is const, we need to copy it into our buffer to
  187. // add the line's null terminator and clobber value-matches with asterisks.
  188. //
  189. // We just use the beginning of the buffer to avoid unnecessary allocations.
  190. int i = 0;
  191. int offset = 0;
  192. bool whole_line_wanted = false;
  193. while (response[i]) {
  194. if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') {
  195. _buffer[offset++] = '%';
  196. _buffer[offset++] = '*';
  197. i++;
  198. } else {
  199. _buffer[offset++] = response[i++];
  200. // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
  201. if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) {
  202. whole_line_wanted = true;
  203. break;
  204. }
  205. }
  206. }
  207. // Scanf has very poor support for catching errors
  208. // fortunately, we can abuse the %n specifier to determine
  209. // if the entire string was matched.
  210. _buffer[offset++] = '%';
  211. _buffer[offset++] = 'n';
  212. _buffer[offset++] = 0;
  213. debug_if(_dbg_on, "AT? %s\n", _buffer);
  214. // To workaround scanf's lack of error reporting, we actually
  215. // make two passes. One checks the validity with the modified
  216. // format string that only stores the matched characters (%n).
  217. // The other reads in the actual matched values.
  218. //
  219. // We keep trying the match until we succeed or some other error
  220. // derails us.
  221. int j = 0;
  222. while (true) {
  223. // Receive next character
  224. int c = getc();
  225. if (c < 0) {
  226. debug_if(_dbg_on, "AT(Timeout)\n");
  227. return false;
  228. }
  229. // Simplify newlines (borrowed from retarget.cpp)
  230. if ((c == CR && _in_prev != LF) ||
  231. (c == LF && _in_prev != CR)) {
  232. _in_prev = c;
  233. c = '\n';
  234. } else if ((c == CR && _in_prev == LF) ||
  235. (c == LF && _in_prev == CR)) {
  236. _in_prev = c;
  237. // onto next character
  238. continue;
  239. } else {
  240. _in_prev = c;
  241. }
  242. _buffer[offset + j++] = c;
  243. _buffer[offset + j] = 0;
  244. // Check for oob data
  245. for (struct oob *oob = _oobs; oob; oob = oob->next) {
  246. if ((unsigned)j == oob->len && memcmp(
  247. oob->prefix, _buffer + offset, oob->len) == 0) {
  248. debug_if(_dbg_on, "AT! %s\n", oob->prefix);
  249. oob->cb();
  250. if (_aborted) {
  251. debug_if(_dbg_on, "AT(Aborted)\n");
  252. return false;
  253. }
  254. // oob may have corrupted non-reentrant buffer,
  255. // so we need to set it up again
  256. goto restart;
  257. }
  258. }
  259. // Check for match
  260. int count = -1;
  261. if (whole_line_wanted && c != '\n') {
  262. // Don't attempt scanning until we get delimiter if they included it in format
  263. // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
  264. // (scanf does not itself match whitespace in its format string, so \n is not significant to it)
  265. } else {
  266. sscanf(_buffer + offset, _buffer, &count);
  267. }
  268. // We only succeed if all characters in the response are matched
  269. if (count == j) {
  270. debug_if(_dbg_on, "AT= %s\n", _buffer + offset);
  271. // Reuse the front end of the buffer
  272. memcpy(_buffer, response, i);
  273. _buffer[i] = 0;
  274. // Store the found results
  275. vsscanf(_buffer + offset, _buffer, args);
  276. // Jump to next line and continue parsing
  277. response += i;
  278. break;
  279. }
  280. // Clear the buffer when we hit a newline or ran out of space
  281. // running out of space usually means we ran into binary data
  282. if (c == '\n' || j + 1 >= _buffer_size - offset) {
  283. debug_if(_dbg_on, "AT< %s", _buffer + offset);
  284. j = 0;
  285. }
  286. }
  287. }
  288. return true;
  289. }
  290. // Mapping to vararg functions
  291. int ATCmdParser::printf(const char *format, ...)
  292. {
  293. va_list args;
  294. va_start(args, format);
  295. int res = vprintf(format, args);
  296. va_end(args);
  297. return res;
  298. }
  299. int ATCmdParser::scanf(const char *format, ...)
  300. {
  301. va_list args;
  302. va_start(args, format);
  303. int res = vscanf(format, args);
  304. va_end(args);
  305. return res;
  306. }
  307. bool ATCmdParser::send(const char *command, ...)
  308. {
  309. va_list args;
  310. va_start(args, command);
  311. bool res = vsend(command, args);
  312. va_end(args);
  313. return res;
  314. }
  315. bool ATCmdParser::recv(const char *response, ...)
  316. {
  317. va_list args;
  318. va_start(args, response);
  319. bool res = vrecv(response, args);
  320. va_end(args);
  321. return res;
  322. }
  323. // oob registration
  324. void ATCmdParser::oob(const char *prefix, Callback<void()> cb)
  325. {
  326. struct oob *oob = new struct oob;
  327. oob->len = strlen(prefix);
  328. oob->prefix = prefix;
  329. oob->cb = cb;
  330. oob->next = _oobs;
  331. _oobs = oob;
  332. }
  333. void ATCmdParser::abort()
  334. {
  335. _aborted = true;
  336. }
  337. bool ATCmdParser::process_oob()
  338. {
  339. if (!_fh->readable()) {
  340. return false;
  341. }
  342. int i = 0;
  343. while (true) {
  344. // Receive next character
  345. int c = getc();
  346. if (c < 0) {
  347. return false;
  348. }
  349. // Simplify newlines (borrowed from retarget.cpp)
  350. if ((c == CR && _in_prev != LF) ||
  351. (c == LF && _in_prev != CR)) {
  352. _in_prev = c;
  353. c = '\n';
  354. } else if ((c == CR && _in_prev == LF) ||
  355. (c == LF && _in_prev == CR)) {
  356. _in_prev = c;
  357. // onto next character
  358. continue;
  359. } else {
  360. _in_prev = c;
  361. }
  362. _buffer[i++] = c;
  363. _buffer[i] = 0;
  364. // Check for oob data
  365. struct oob *oob = _oobs;
  366. while (oob) {
  367. if (i == (int)oob->len && memcmp(
  368. oob->prefix, _buffer, oob->len) == 0) {
  369. debug_if(_dbg_on, "AT! %s\r\n", oob->prefix);
  370. oob->cb();
  371. return true;
  372. }
  373. oob = oob->next;
  374. }
  375. // Clear the buffer when we hit a newline or ran out of space
  376. // running out of space usually means we ran into binary data
  377. if (((i + 1) >= _buffer_size) || (c == '\n')) {
  378. debug_if(_dbg_on, "AT< %s", _buffer);
  379. i = 0;
  380. }
  381. }
  382. }