at_client.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /*
  2. * File : at_client.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Change Logs:
  21. * Date Author Notes
  22. * 2018-03-30 chenyong first version
  23. * 2018-04-12 chenyong add client implement
  24. */
  25. #include <at.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #define AT_RESP_END_OK "OK"
  30. #define AT_RESP_END_ERROR "ERROR"
  31. #define AT_RESP_END_FAIL "FAIL"
  32. #define AT_END_CR_LF "\r\n"
  33. static at_client_t at_client_local = RT_NULL;
  34. static char cust_end_sign = 0;
  35. extern rt_size_t at_vprintfln(rt_device_t device, const char *format, va_list args);
  36. extern void at_print_raw_cmd(const char *type, const char *cmd, rt_size_t size);
  37. extern const char *at_get_last_cmd(rt_size_t *cmd_size);
  38. /**
  39. * Create response structure.
  40. *
  41. * @param buf_size the maximum response buffer size
  42. * @param line_num the number of setting response lines
  43. * = 0: the response data will auto return when received 'OK' or 'ERROR'
  44. * != 0: the response data will return when received setting lines number data
  45. * @param timeout the maximum response time
  46. *
  47. * @return != RT_NULL: response structure
  48. * = RT_NULL: no memory
  49. */
  50. at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
  51. {
  52. at_response_t resp = RT_NULL;
  53. resp = (at_response_t) rt_calloc(1, sizeof(struct at_response));
  54. if (!resp)
  55. {
  56. LOG_E("AT create response structure failed! No memory for response structure!");
  57. return RT_NULL;
  58. }
  59. resp->buf = (char *) rt_calloc(1, buf_size);
  60. if (!resp->buf)
  61. {
  62. LOG_E("AT create response structure failed! No memory for response buf structure!");
  63. rt_free(resp);
  64. return RT_NULL;
  65. }
  66. resp->buf_size = buf_size;
  67. resp->line_num = line_num;
  68. resp->line_counts = 0;
  69. resp->timeout = timeout;
  70. return resp;
  71. }
  72. /**
  73. * Delete and free response structure.
  74. *
  75. * @param resp response structure
  76. */
  77. void at_delete_resp(at_response_t resp)
  78. {
  79. if (resp && resp->buf)
  80. {
  81. rt_free(resp->buf);
  82. }
  83. if (resp)
  84. {
  85. rt_free(resp);
  86. resp = RT_NULL;
  87. }
  88. }
  89. /**
  90. * Set response structure information
  91. *
  92. * @param resp response structure
  93. * @param buf_size the maximum response buffer size
  94. * @param line_num the number of setting response lines
  95. * = 0: the response data will auto return when received 'OK' or 'ERROR'
  96. * != 0: the response data will return when received setting lines number data
  97. * @param timeout the maximum response time
  98. *
  99. * @return != RT_NULL: response structure
  100. * = RT_NULL: no memory
  101. */
  102. at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
  103. {
  104. RT_ASSERT(resp);
  105. if(resp->buf_size != buf_size)
  106. {
  107. resp->buf_size = buf_size;
  108. resp->buf = rt_realloc(resp->buf, buf_size);
  109. if(!resp->buf)
  110. {
  111. LOG_D("No memory for realloc response buffer size(%d).", buf_size);
  112. return RT_NULL;
  113. }
  114. }
  115. resp->line_num = line_num;
  116. resp->timeout = timeout;
  117. return resp;
  118. }
  119. /**
  120. * Get one line AT response buffer by line number.
  121. *
  122. * @param resp response structure
  123. * @param resp_line line number, start from '1'
  124. *
  125. * @return != RT_NULL: response line buffer
  126. * = RT_NULL: input response line error
  127. */
  128. const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line)
  129. {
  130. char *resp_buf = resp->buf;
  131. char *resp_line_buf = RT_NULL;
  132. rt_size_t line_num = 1;
  133. RT_ASSERT(resp);
  134. if (resp_line > resp->line_counts || resp_line <= 0)
  135. {
  136. LOG_E("AT response get line failed! Input response line(%d) error!", resp_line);
  137. return RT_NULL;
  138. }
  139. for (line_num = 1; line_num <= resp->line_counts; line_num++)
  140. {
  141. if (resp_line == line_num)
  142. {
  143. resp_line_buf = resp_buf;
  144. return resp_line_buf;
  145. }
  146. resp_buf += strlen(resp_buf) + 1;
  147. }
  148. return RT_NULL;
  149. }
  150. /**
  151. * Get one line AT response buffer by keyword
  152. *
  153. * @param resp response structure
  154. * @param keyword query keyword
  155. *
  156. * @return != RT_NULL: response line buffer
  157. * = RT_NULL: no matching data
  158. */
  159. const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword)
  160. {
  161. char *resp_buf = resp->buf;
  162. char *resp_line_buf = RT_NULL;
  163. rt_size_t line_num = 1;
  164. RT_ASSERT(resp);
  165. RT_ASSERT(keyword);
  166. for (line_num = 1; line_num <= resp->line_counts; line_num++)
  167. {
  168. if(strstr(resp_buf, keyword))
  169. {
  170. resp_line_buf = resp_buf;
  171. return resp_line_buf;
  172. }
  173. resp_buf += strlen(resp_buf) + 1;
  174. }
  175. return RT_NULL;
  176. }
  177. /**
  178. * Get and parse AT response buffer arguments by line number.
  179. *
  180. * @param resp response structure
  181. * @param resp_line line number, start from '1'
  182. * @param resp_expr response buffer expression
  183. *
  184. * @return -1 : input response line number error or get line buffer error
  185. * 0 : parsed without match
  186. * >0 : the number of arguments successfully parsed
  187. */
  188. int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...)
  189. {
  190. va_list args;
  191. int resp_args_num = 0;
  192. const char *resp_line_buf = RT_NULL;
  193. RT_ASSERT(resp);
  194. RT_ASSERT(resp_expr);
  195. if ((resp_line_buf = at_resp_get_line(resp, resp_line)) == RT_NULL)
  196. {
  197. return -1;
  198. }
  199. va_start(args, resp_expr);
  200. resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
  201. va_end(args);
  202. return resp_args_num;
  203. }
  204. /**
  205. * Get and parse AT response buffer arguments by keyword.
  206. *
  207. * @param resp response structure
  208. * @param keyword query keyword
  209. * @param resp_expr response buffer expression
  210. *
  211. * @return -1 : input keyword error or get line buffer error
  212. * 0 : parsed without match
  213. * >0 : the number of arguments successfully parsed
  214. */
  215. int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...)
  216. {
  217. va_list args;
  218. int resp_args_num = 0;
  219. const char *resp_line_buf = RT_NULL;
  220. RT_ASSERT(resp);
  221. RT_ASSERT(resp_expr);
  222. if ((resp_line_buf = at_resp_get_line_by_kw(resp, keyword)) == RT_NULL)
  223. {
  224. return -1;
  225. }
  226. va_start(args, resp_expr);
  227. resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
  228. va_end(args);
  229. return resp_args_num;
  230. }
  231. /**
  232. * Send commands to AT server and wait response.
  233. *
  234. * @param resp AT response structure, using RT_NULL when you don't care response
  235. * @param cmd_expr AT commands expression
  236. *
  237. * @return 0 : success
  238. * -1 : response status error
  239. * -2 : wait timeout
  240. */
  241. int at_exec_cmd(at_response_t resp, const char *cmd_expr, ...)
  242. {
  243. at_client_t client = at_client_local;
  244. va_list args;
  245. rt_size_t cmd_size = 0;
  246. rt_err_t result = RT_EOK;
  247. const char *cmd = RT_NULL;
  248. RT_ASSERT(cmd_expr);
  249. rt_mutex_take(client->lock, RT_WAITING_FOREVER);
  250. client->resp_status = AT_RESP_OK;
  251. client->resp = resp;
  252. va_start(args, cmd_expr);
  253. at_vprintfln(client->device, cmd_expr, args);
  254. va_end(args);
  255. if (resp)
  256. {
  257. resp->line_counts = 0;
  258. if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
  259. {
  260. cmd = at_get_last_cmd(&cmd_size);
  261. LOG_E("execute command (%.*s) timeout (%d ticks)!", cmd_size, cmd, resp->timeout);
  262. client->resp_status = AT_RESP_TIMEOUT;
  263. result = -RT_ETIMEOUT;
  264. goto __exit;
  265. }
  266. if (client->resp_status != AT_RESP_OK)
  267. {
  268. cmd = at_get_last_cmd(&cmd_size);
  269. LOG_E("execute command (%.*s) failed!", cmd_size, cmd);
  270. result = -RT_ERROR;
  271. goto __exit;
  272. }
  273. }
  274. __exit:
  275. client->resp = RT_NULL;
  276. rt_mutex_release(client->lock);
  277. return result;
  278. }
  279. /**
  280. * Send data to AT server, send data don't have end sign(eg: \r\n).
  281. *
  282. * @param buf send data buffer
  283. * @param size send fixed data size
  284. *
  285. * @return send data size
  286. */
  287. rt_size_t at_client_send(const char *buf, rt_size_t size)
  288. {
  289. at_client_t client = at_client_local;
  290. RT_ASSERT(buf);
  291. #ifdef AT_PRINT_RAW_CMD
  292. at_print_raw_cmd("send", buf, size);
  293. #endif
  294. return rt_device_write(client->device, 0, buf, size);
  295. }
  296. static char at_client_getchar(void)
  297. {
  298. char ch;
  299. while (rt_device_read(at_client_local->device, 0, &ch, 1) == 0)
  300. {
  301. rt_sem_control(at_client_local->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
  302. rt_sem_take(at_client_local->rx_notice, RT_WAITING_FOREVER);
  303. }
  304. return ch;
  305. }
  306. /**
  307. * AT client receive fixed-length data.
  308. *
  309. * @param buf receive data buffer
  310. * @param size receive fixed data size
  311. *
  312. * @note this function can only be used in execution function of URC data
  313. *
  314. * @return success receive data size
  315. */
  316. rt_size_t at_client_recv(char *buf, rt_size_t size)
  317. {
  318. rt_size_t read_idx = 0;
  319. char ch;
  320. RT_ASSERT(buf);
  321. while (1)
  322. {
  323. if (read_idx < size)
  324. {
  325. ch = at_client_getchar();
  326. buf[read_idx++] = ch;
  327. }
  328. else
  329. {
  330. break;
  331. }
  332. }
  333. #ifdef AT_PRINT_RAW_CMD
  334. at_print_raw_cmd("urc_recv", buf, size);
  335. #endif
  336. return read_idx;
  337. }
  338. /**
  339. * get AT client structure pointer.
  340. *
  341. * @return AT client structure pointer
  342. */
  343. at_client_t rt_at_get_client(void)
  344. {
  345. RT_ASSERT(at_client_local);
  346. RT_ASSERT(at_client_local->status != AT_STATUS_UNINITIALIZED);
  347. return at_client_local;
  348. }
  349. /**
  350. * AT client set end sign.
  351. *
  352. * @param ch the end sign, can not be used when it is '\0'
  353. *
  354. * @return 0: set success
  355. */
  356. int at_set_end_sign(char ch)
  357. {
  358. cust_end_sign = ch;
  359. return 0;
  360. }
  361. static const struct at_urc *get_urc_obj(char *data, rt_size_t size)
  362. {
  363. rt_size_t i, prefix_len, suffix_len;
  364. at_client_t client = at_client_local;
  365. if (client->urc_table == RT_NULL)
  366. {
  367. return RT_NULL;
  368. }
  369. for (i = 0; i < client->urc_table_size; i++)
  370. {
  371. prefix_len = strlen(client->urc_table[i].cmd_prefix);
  372. suffix_len = strlen(client->urc_table[i].cmd_suffix);
  373. if (size < prefix_len + suffix_len)
  374. {
  375. continue;
  376. }
  377. if ((prefix_len ? !strncmp(data, client->urc_table[i].cmd_prefix, prefix_len) : 1)
  378. && (suffix_len ? !strncmp(data + size - suffix_len, client->urc_table[i].cmd_suffix, suffix_len) : 1))
  379. {
  380. return &client->urc_table[i];
  381. }
  382. }
  383. return RT_NULL;
  384. }
  385. static int at_recv_readline(void)
  386. {
  387. rt_size_t read_len = 0;
  388. char ch = 0, last_ch = 0;
  389. rt_bool_t is_full = RT_FALSE;
  390. at_client_t client = at_client_local;
  391. memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
  392. client->cur_recv_len = 0;
  393. while (1)
  394. {
  395. ch = at_client_getchar();
  396. if (read_len < AT_CLIENT_RECV_BUFF_LEN)
  397. {
  398. client->recv_buffer[read_len++] = ch;
  399. }
  400. else
  401. {
  402. is_full = RT_TRUE;
  403. }
  404. /* is newline or URC data */
  405. if ((ch == '\n' && last_ch == '\r') || (cust_end_sign != 0 && ch == cust_end_sign)
  406. || get_urc_obj(client->recv_buffer, read_len))
  407. {
  408. if (is_full)
  409. {
  410. LOG_E("read line failed. The line data length is out of buffer size(%d)!", AT_CLIENT_RECV_BUFF_LEN);
  411. memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
  412. client->cur_recv_len = 0;
  413. return -RT_EFULL;
  414. }
  415. client->cur_recv_len = read_len;
  416. break;
  417. }
  418. last_ch = ch;
  419. }
  420. #ifdef AT_PRINT_RAW_CMD
  421. at_print_raw_cmd("recvline", client->recv_buffer, read_len);
  422. #endif
  423. return read_len;
  424. }
  425. static void client_parser(at_client_t client)
  426. {
  427. int resp_buf_len = 0;
  428. const struct at_urc *urc;
  429. rt_size_t line_counts = 0;
  430. while(1)
  431. {
  432. if (at_recv_readline() > 0)
  433. {
  434. if ((urc = get_urc_obj(client->recv_buffer, client->cur_recv_len)) != RT_NULL)
  435. {
  436. /* current receive is request, try to execute related operations */
  437. if (urc->func != RT_NULL)
  438. {
  439. urc->func(client->recv_buffer, client->cur_recv_len);
  440. }
  441. }
  442. else if (client->resp != RT_NULL)
  443. {
  444. /* current receive is response */
  445. client->recv_buffer[client->cur_recv_len - 1] = '\0';
  446. if (resp_buf_len + client->cur_recv_len < client->resp->buf_size)
  447. {
  448. /* copy response lines, separated by '\0' */
  449. memcpy(client->resp->buf + resp_buf_len, client->recv_buffer, client->cur_recv_len);
  450. resp_buf_len += client->cur_recv_len;
  451. line_counts++;
  452. }
  453. else
  454. {
  455. client->resp_status = AT_RESP_BUFF_FULL;
  456. LOG_E("Read response buffer failed. The Response buffer size is out of buffer size(%d)!", client->resp->buf_size);
  457. }
  458. /* check response result */
  459. if (memcmp(client->recv_buffer, AT_RESP_END_OK, strlen(AT_RESP_END_OK)) == 0
  460. && client->resp->line_num == 0)
  461. {
  462. /* get the end data by response result, return response state END_OK. */
  463. client->resp_status = AT_RESP_OK;
  464. }
  465. else if (strstr(client->recv_buffer, AT_RESP_END_ERROR)
  466. || (memcmp(client->recv_buffer, AT_RESP_END_FAIL, strlen(AT_RESP_END_FAIL)) == 0))
  467. {
  468. client->resp_status = AT_RESP_ERROR;
  469. }
  470. else if (line_counts == client->resp->line_num && client->resp->line_num)
  471. {
  472. /* get the end data by response line, return response state END_OK.*/
  473. client->resp_status = AT_RESP_OK;
  474. }
  475. else
  476. {
  477. continue;
  478. }
  479. client->resp->line_counts = line_counts;
  480. client->resp = RT_NULL;
  481. rt_sem_release(client->resp_notice);
  482. resp_buf_len = 0, line_counts = 0;
  483. }
  484. else
  485. {
  486. // log_d("unrecognized line: %.*s", client->cur_recv_len, client->recv_buffer);
  487. }
  488. }
  489. }
  490. }
  491. static rt_err_t at_client_rx_ind(rt_device_t dev, rt_size_t size)
  492. {
  493. if (size > 0)
  494. {
  495. rt_sem_release(at_client_local->rx_notice);
  496. }
  497. return RT_EOK;
  498. }
  499. /**
  500. * Set URC(Unsolicited Result Code) table
  501. *
  502. * @param table URC table
  503. * @param size table size
  504. */
  505. void at_set_urc_table(const struct at_urc *table, rt_size_t size)
  506. {
  507. rt_size_t idx;
  508. for(idx = 0; idx < size; idx++)
  509. {
  510. RT_ASSERT(table[idx].cmd_prefix);
  511. RT_ASSERT(table[idx].cmd_suffix);
  512. }
  513. at_client_local->urc_table = table;
  514. at_client_local->urc_table_size = size;
  515. }
  516. int at_client_init(void)
  517. {
  518. int result = RT_EOK;
  519. rt_err_t open_result = RT_EOK;
  520. if (at_client_local)
  521. {
  522. return result;
  523. }
  524. at_client_local = (at_client_t) rt_calloc(1, sizeof(struct at_client));
  525. if (!at_client_local)
  526. {
  527. result = -RT_ERROR;
  528. LOG_E("AT client session initialize failed! No memory for at_client structure !");
  529. goto __exit;
  530. }
  531. at_client_local->status = AT_STATUS_UNINITIALIZED;
  532. at_client_local->lock = rt_mutex_create("at_lock", RT_IPC_FLAG_FIFO);
  533. if(!at_client_local->lock)
  534. {
  535. LOG_E("AT client session initialize failed! at_client_recv_lock create failed!");
  536. result = -RT_ENOMEM;
  537. goto __exit;
  538. }
  539. at_client_local->cur_recv_len = 0;
  540. at_client_local->rx_notice = rt_sem_create("at_client_notice", 0, RT_IPC_FLAG_FIFO);
  541. if (!at_client_local->rx_notice)
  542. {
  543. LOG_E("AT client session initialize failed! at_client_notice semaphore create failed!");
  544. result = -RT_ENOMEM;
  545. goto __exit;
  546. }
  547. at_client_local->resp_notice = rt_sem_create("at_client_resp", 0, RT_IPC_FLAG_FIFO);
  548. if (!at_client_local->resp_notice)
  549. {
  550. LOG_E("AT client session initialize failed! at_client_resp semaphore create failed!");
  551. result = -RT_ENOMEM;
  552. goto __exit;
  553. }
  554. /* Find and open command device */
  555. at_client_local->device = rt_device_find(AT_CLIENT_DEVICE);
  556. if (at_client_local->device)
  557. {
  558. RT_ASSERT(at_client_local->device->type == RT_Device_Class_Char);
  559. /* using DMA mode first */
  560. open_result = rt_device_open(at_client_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
  561. /* using interrupt mode when DMA mode not supported */
  562. if (open_result == -RT_EIO)
  563. {
  564. open_result = rt_device_open(at_client_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
  565. }
  566. RT_ASSERT(open_result == RT_EOK);
  567. rt_device_set_rx_indicate(at_client_local->device, at_client_rx_ind);
  568. }
  569. else
  570. {
  571. LOG_E("AT client device initialize failed! Not find the device : %s.", AT_CLIENT_DEVICE);
  572. result = -RT_ERROR;
  573. goto __exit;
  574. }
  575. at_client_local->urc_table = RT_NULL;
  576. at_client_local->urc_table_size = 0;
  577. at_client_local->parser = rt_thread_create("at_client",
  578. (void (*)(void *parameter))client_parser,
  579. at_client_local,
  580. 1024 + 512,
  581. RT_THREAD_PRIORITY_MAX / 3 - 1,
  582. 5);
  583. if (at_client_local->parser == RT_NULL)
  584. {
  585. result = -RT_ENOMEM;
  586. goto __exit;
  587. }
  588. if ((result = at_client_port_init()) != RT_EOK)
  589. {
  590. LOG_E("AT client port initialize failed(%d).", result);
  591. }
  592. __exit:
  593. if (!result)
  594. {
  595. at_client_local->status = AT_STATUS_INITIALIZED;
  596. rt_thread_startup(at_client_local->parser);
  597. LOG_I("RT-Thread AT client (V%s) initialize success.", AT_SW_VERSION);
  598. }
  599. else
  600. {
  601. if (at_client_local)
  602. {
  603. rt_free(at_client_local);
  604. }
  605. LOG_E("RT-Thread AT client (V%s) initialize failed(%d).", AT_SW_VERSION, result);
  606. }
  607. return result;
  608. }
  609. INIT_COMPONENT_EXPORT(at_client_init);
  610. RT_WEAK int at_client_port_init(void)
  611. {
  612. at_client_local->urc_table = RT_NULL;
  613. at_client_local->urc_table_size = 0;
  614. LOG_E("The client porting initialize for AT client is not implement.");
  615. return 0;
  616. }