at_client.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. /*
  2. * Copyright (c) 2006-2025 RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2018-03-30 chenyong first version
  9. * 2018-04-12 chenyong add client implement
  10. * 2018-08-17 chenyong multiple client support
  11. * 2021-03-17 Meco Man fix a buf of leaking memory
  12. * 2021-07-14 Sszl fix a buf of leaking memory
  13. * 2025-01-02 dongly support SERIAL_V2
  14. */
  15. #include <at.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #define LOG_TAG "at.clnt"
  20. #include <at_log.h>
  21. #ifdef AT_USING_CLIENT
  22. #define AT_RESP_END_OK "OK"
  23. #define AT_RESP_END_ERROR "ERROR"
  24. #define AT_RESP_END_FAIL "FAIL"
  25. #define AT_END_CR_LF "\r\n"
  26. #define AT_END_CR "\r"
  27. #define AT_END_LF "\n"
  28. #define AT_END_RAW ""
  29. static struct at_client at_client_table[AT_CLIENT_NUM_MAX] = { 0 };
  30. extern rt_size_t at_utils_send(rt_device_t dev,
  31. rt_off_t pos,
  32. const void *buffer,
  33. rt_size_t size);
  34. extern rt_size_t at_vprintfln(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
  35. extern rt_size_t at_vprintf(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
  36. extern rt_size_t at_vprintfcr(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
  37. extern rt_size_t at_vprintflf(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
  38. extern void at_print_raw_cmd(const char *type, const char *cmd, rt_size_t size);
  39. /**
  40. * Create response object.
  41. *
  42. * @param buf_size the maximum response buffer size
  43. * @param line_num the number of setting response lines
  44. * = 0: the response data will auto return when received 'OK' or 'ERROR'
  45. * != 0: the response data will return when received setting lines number data
  46. * @param timeout the maximum response time
  47. *
  48. * @return != RT_NULL: response object
  49. * = RT_NULL: no memory
  50. */
  51. at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
  52. {
  53. at_response_t resp = RT_NULL;
  54. resp = (at_response_t) rt_calloc(1, sizeof(struct at_response));
  55. if (resp == RT_NULL)
  56. {
  57. LOG_E("AT create response object failed! No memory for response object!");
  58. return RT_NULL;
  59. }
  60. resp->buf = (char *) rt_calloc(1, buf_size);
  61. if (resp->buf == RT_NULL)
  62. {
  63. LOG_E("AT create response object failed! No memory for response buffer!");
  64. rt_free(resp);
  65. return RT_NULL;
  66. }
  67. resp->buf_size = buf_size;
  68. resp->line_num = line_num;
  69. resp->line_counts = 0;
  70. resp->timeout = timeout;
  71. return resp;
  72. }
  73. /**
  74. * Delete and free response object.
  75. *
  76. * @param resp response object
  77. */
  78. void at_delete_resp(at_response_t resp)
  79. {
  80. if (resp && resp->buf)
  81. {
  82. rt_free(resp->buf);
  83. }
  84. if (resp)
  85. {
  86. rt_free(resp);
  87. resp = RT_NULL;
  88. }
  89. }
  90. /**
  91. * Set response object information
  92. *
  93. * @param resp response object
  94. * @param buf_size the maximum response buffer size
  95. * @param line_num the number of setting response lines
  96. * = 0: the response data will auto return when received 'OK' or 'ERROR'
  97. * != 0: the response data will return when received setting lines number data
  98. * @param timeout the maximum response time
  99. *
  100. * @return != RT_NULL: response object
  101. * = RT_NULL: no memory
  102. */
  103. 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)
  104. {
  105. char *p_temp;
  106. RT_ASSERT(resp);
  107. if (resp->buf_size != buf_size)
  108. {
  109. resp->buf_size = buf_size;
  110. p_temp = (char *) rt_realloc(resp->buf, buf_size);
  111. if (p_temp == RT_NULL)
  112. {
  113. LOG_D("No memory for realloc response buffer size(%d).", buf_size);
  114. return RT_NULL;
  115. }
  116. else
  117. {
  118. resp->buf = p_temp;
  119. }
  120. }
  121. resp->line_num = line_num;
  122. resp->timeout = timeout;
  123. return resp;
  124. }
  125. /**
  126. * Get one line AT response buffer by line number.
  127. *
  128. * @param resp response object
  129. * @param resp_line line number, start from '1'
  130. *
  131. * @return != RT_NULL: response line buffer
  132. * = RT_NULL: input response line error
  133. */
  134. const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line)
  135. {
  136. char *resp_buf = resp->buf;
  137. rt_size_t line_num = 1;
  138. RT_ASSERT(resp);
  139. if (resp_line > resp->line_counts || resp_line <= 0)
  140. {
  141. LOG_E("AT response get line failed! Input response line(%d) error!", resp_line);
  142. return RT_NULL;
  143. }
  144. for (line_num = 1; line_num <= resp->line_counts; line_num++)
  145. {
  146. if (resp_line == line_num)
  147. {
  148. return resp_buf;
  149. }
  150. resp_buf += strlen(resp_buf) + 1;
  151. }
  152. return RT_NULL;
  153. }
  154. /**
  155. * Get one line AT response buffer by keyword
  156. *
  157. * @param resp response object
  158. * @param keyword query keyword
  159. *
  160. * @return != RT_NULL: response line buffer
  161. * = RT_NULL: no matching data
  162. */
  163. const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword)
  164. {
  165. char *resp_buf = resp->buf;
  166. rt_size_t line_num = 1;
  167. RT_ASSERT(resp);
  168. RT_ASSERT(keyword);
  169. for (line_num = 1; line_num <= resp->line_counts; line_num++)
  170. {
  171. if (strstr(resp_buf, keyword))
  172. {
  173. return resp_buf;
  174. }
  175. resp_buf += strlen(resp_buf) + 1;
  176. }
  177. return RT_NULL;
  178. }
  179. /**
  180. * Get and parse AT response buffer arguments by line number.
  181. *
  182. * @param resp response object
  183. * @param resp_line line number, start from '1'
  184. * @param resp_expr response buffer expression
  185. *
  186. * @return -1 : input response line number error or get line buffer error
  187. * 0 : parsed without match
  188. * >0 : the number of arguments successfully parsed
  189. */
  190. int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...)
  191. {
  192. va_list args;
  193. int resp_args_num = 0;
  194. const char *resp_line_buf = RT_NULL;
  195. RT_ASSERT(resp);
  196. RT_ASSERT(resp_expr);
  197. if ((resp_line_buf = at_resp_get_line(resp, resp_line)) == RT_NULL)
  198. {
  199. return -1;
  200. }
  201. va_start(args, resp_expr);
  202. resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
  203. va_end(args);
  204. return resp_args_num;
  205. }
  206. /**
  207. * Get and parse AT response buffer arguments by keyword.
  208. *
  209. * @param resp response object
  210. * @param keyword query keyword
  211. * @param resp_expr response buffer expression
  212. *
  213. * @return -1 : input keyword error or get line buffer error
  214. * 0 : parsed without match
  215. * >0 : the number of arguments successfully parsed
  216. */
  217. int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...)
  218. {
  219. va_list args;
  220. int resp_args_num = 0;
  221. const char *resp_line_buf = RT_NULL;
  222. RT_ASSERT(resp);
  223. RT_ASSERT(resp_expr);
  224. if ((resp_line_buf = at_resp_get_line_by_kw(resp, keyword)) == RT_NULL)
  225. {
  226. return -1;
  227. }
  228. va_start(args, resp_expr);
  229. resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
  230. va_end(args);
  231. return resp_args_num;
  232. }
  233. /**
  234. * Send commands to AT server and wait response.
  235. *
  236. * @param client current AT client object
  237. * @param resp AT response object, using RT_NULL when you don't care response
  238. * @param cmd_expr AT commands expression
  239. *
  240. * @return 0 : success
  241. * -1 : response status error
  242. * -2 : wait timeout
  243. * -7 : enter AT CLI mode
  244. */
  245. int at_obj_exec_cmd(at_client_t client, at_response_t resp, const char *cmd_expr, ...)
  246. {
  247. va_list args;
  248. rt_err_t result = RT_EOK;
  249. RT_ASSERT(cmd_expr);
  250. if (client == RT_NULL)
  251. {
  252. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  253. return -RT_ERROR;
  254. }
  255. /* check AT CLI mode */
  256. if (client->status == AT_STATUS_CLI && resp)
  257. {
  258. return -RT_EBUSY;
  259. }
  260. rt_mutex_take(client->lock, RT_WAITING_FOREVER);
  261. client->resp_status = AT_RESP_OK;
  262. if (resp != RT_NULL)
  263. {
  264. resp->buf_len = 0;
  265. resp->line_counts = 0;
  266. }
  267. client->resp = resp;
  268. rt_sem_control(client->resp_notice, RT_IPC_CMD_RESET, RT_NULL);
  269. va_start(args, cmd_expr);
  270. client->last_cmd_len = at_vprintfln(client->device, client->send_buf, client->send_bufsz, cmd_expr, args);
  271. if (client->last_cmd_len > 2)
  272. {
  273. client->last_cmd_len -= 2; /* "\r\n" */
  274. }
  275. va_end(args);
  276. if (resp != RT_NULL)
  277. {
  278. if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
  279. {
  280. LOG_W("execute command (%.*s) timeout (%d ticks)!", client->last_cmd_len, client->send_buf, resp->timeout);
  281. client->resp_status = AT_RESP_TIMEOUT;
  282. result = -RT_ETIMEOUT;
  283. }
  284. else if (client->resp_status != AT_RESP_OK)
  285. {
  286. LOG_E("execute command (%.*s) failed!", client->last_cmd_len, client->send_buf);
  287. result = -RT_ERROR;
  288. }
  289. }
  290. client->resp = RT_NULL;
  291. rt_mutex_release(client->lock);
  292. return result;
  293. }
  294. /**
  295. * Send commands through custom formatting to AT server and wait response.
  296. *
  297. * @param client current AT client object
  298. * @param resp AT response object, using RT_NULL when you don't care response
  299. * @param format formatting macro, it can be one of these values: AT_END_CR_LF, AT_END_RAW, AT_END_CR, AT_END_LF.
  300. * Behavior of AT_END_CR_LF is same as at_obj_exec_cmd, and it will add \r\n symnbol behind message.
  301. * AT_END_RAW means frame work won't modify anything of message. AT_END_CR will add \r for Carriage
  302. * Return. AT_END_LF means add \\n for Line Feed.
  303. * @param cmd_expr AT commands expression
  304. *
  305. * @return 0 : success
  306. * -1 : response status error
  307. * -2 : wait timeout
  308. * -7 : enter AT CLI mode
  309. */
  310. int at_obj_exec_cmd_format(at_client_t client, at_response_t resp, const char* format, const char *cmd_expr, ...)
  311. {
  312. va_list args;
  313. rt_err_t result = RT_EOK;
  314. RT_ASSERT(cmd_expr);
  315. if (client == RT_NULL)
  316. {
  317. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  318. return -RT_ERROR;
  319. }
  320. /* check AT CLI mode */
  321. if (client->status == AT_STATUS_CLI && resp)
  322. {
  323. return -RT_EBUSY;
  324. }
  325. rt_mutex_take(client->lock, RT_WAITING_FOREVER);
  326. client->resp_status = AT_RESP_OK;
  327. if (resp != RT_NULL)
  328. {
  329. resp->buf_len = 0;
  330. resp->line_counts = 0;
  331. }
  332. client->resp = resp;
  333. rt_sem_control(client->resp_notice, RT_IPC_CMD_RESET, RT_NULL);
  334. va_start(args, cmd_expr);
  335. if (strcmp(format, AT_END_CR_LF) == 0)
  336. {
  337. client->last_cmd_len = at_vprintfln(client->device, client->send_buf, client->send_bufsz, cmd_expr, args);
  338. }
  339. else if (strcmp(format, AT_END_RAW) == 0)
  340. {
  341. client->last_cmd_len = at_vprintf(client->device, client->send_buf, client->send_bufsz, cmd_expr, args);
  342. }
  343. else if (strcmp(format, AT_END_CR) == 0)
  344. {
  345. client->last_cmd_len = at_vprintfcr(client->device, client->send_buf, client->send_bufsz, cmd_expr, args);
  346. }
  347. else if (strcmp(format, AT_END_LF) == 0)
  348. {
  349. client->last_cmd_len = at_vprintflf(client->device, client->send_buf, client->send_bufsz, cmd_expr, args);
  350. }
  351. va_end(args);
  352. if (resp != RT_NULL)
  353. {
  354. if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
  355. {
  356. LOG_W("execute command (%.*s) timeout (%d ticks)!", client->last_cmd_len, client->send_buf, resp->timeout);
  357. client->resp_status = AT_RESP_TIMEOUT;
  358. result = -RT_ETIMEOUT;
  359. }
  360. else if (client->resp_status != AT_RESP_OK)
  361. {
  362. LOG_E("execute command (%.*s) failed!", client->last_cmd_len, client->send_buf);
  363. result = -RT_ERROR;
  364. }
  365. }
  366. client->resp = RT_NULL;
  367. rt_mutex_release(client->lock);
  368. return result;
  369. }
  370. /**
  371. * Waiting for connection to external devices.
  372. *
  373. * @param client current AT client object
  374. * @param timeout millisecond for timeout
  375. *
  376. * @return 0 : success
  377. * -2 : timeout
  378. * -5 : no memory
  379. */
  380. int at_client_obj_wait_connect(at_client_t client, rt_uint32_t timeout)
  381. {
  382. rt_err_t result = RT_EOK;
  383. at_response_t resp = RT_NULL;
  384. rt_tick_t start_time = 0;
  385. char *client_name = client->device->parent.name;
  386. if (client == RT_NULL)
  387. {
  388. LOG_E("input AT client object is NULL, please create or get AT Client object!");
  389. return -RT_ERROR;
  390. }
  391. resp = at_create_resp(64, 0, rt_tick_from_millisecond(300));
  392. if (resp == RT_NULL)
  393. {
  394. LOG_E("no memory for AT client(%s) response object.", client_name);
  395. return -RT_ENOMEM;
  396. }
  397. rt_mutex_take(client->lock, RT_WAITING_FOREVER);
  398. client->resp = resp;
  399. rt_sem_control(client->resp_notice, RT_IPC_CMD_RESET, RT_NULL);
  400. start_time = rt_tick_get();
  401. while (1)
  402. {
  403. /* Check whether it is timeout */
  404. if (rt_tick_get() - start_time > rt_tick_from_millisecond(timeout))
  405. {
  406. LOG_E("wait AT client(%s) connect timeout(%d tick).", client_name, timeout);
  407. result = -RT_ETIMEOUT;
  408. break;
  409. }
  410. /* Check whether it is already connected */
  411. resp->buf_len = 0;
  412. resp->line_counts = 0;
  413. at_utils_send(client->device, 0, "AT\r\n", 4);
  414. if (rt_sem_take(client->resp_notice, resp->timeout) == RT_EOK)
  415. break;
  416. }
  417. at_delete_resp(resp);
  418. client->resp = RT_NULL;
  419. rt_mutex_release(client->lock);
  420. return result;
  421. }
  422. /**
  423. * Send data to AT server, send data don't have end sign(eg: \r\n).
  424. *
  425. * @param client current AT client object
  426. * @param buf send data buffer
  427. * @param size send fixed data size
  428. *
  429. * @return >0: send data size
  430. * =0: send failed
  431. */
  432. rt_size_t at_client_obj_send(at_client_t client, const char *buf, rt_size_t size)
  433. {
  434. rt_size_t len;
  435. RT_ASSERT(buf);
  436. if (client == RT_NULL)
  437. {
  438. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  439. return 0;
  440. }
  441. #ifdef AT_PRINT_RAW_CMD
  442. at_print_raw_cmd("sendline", buf, size);
  443. #endif
  444. rt_mutex_take(client->lock, RT_WAITING_FOREVER);
  445. len = at_utils_send(client->device, 0, buf, size);
  446. rt_mutex_release(client->lock);
  447. return len;
  448. }
  449. static rt_err_t at_client_getchar(at_client_t client, char *ch, rt_int32_t timeout)
  450. {
  451. rt_err_t result = RT_EOK;
  452. while (rt_device_read(client->device, 0, ch, 1) == 0)
  453. {
  454. result = rt_sem_take(client->rx_notice, rt_tick_from_millisecond(timeout));
  455. if (result != RT_EOK)
  456. {
  457. return result;
  458. }
  459. rt_sem_control(client->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
  460. }
  461. return RT_EOK;
  462. }
  463. /**
  464. * AT client receive fixed-length data.
  465. *
  466. * @param client current AT client object
  467. * @param buf receive data buffer
  468. * @param size receive fixed data size
  469. * @param timeout receive data timeout (ms)
  470. *
  471. * @note this function can only be used in execution function of URC data
  472. *
  473. * @return >0: receive data size
  474. * =0: receive failed
  475. */
  476. rt_size_t at_client_obj_recv(at_client_t client, char *buf, rt_size_t size, rt_int32_t timeout)
  477. {
  478. rt_size_t len = 0;
  479. RT_ASSERT(buf);
  480. if (client == RT_NULL)
  481. {
  482. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  483. return 0;
  484. }
  485. while (size)
  486. {
  487. rt_size_t read_len;
  488. rt_sem_control(client->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
  489. read_len = rt_device_read(client->device, 0, buf + len, size);
  490. if (read_len > 0)
  491. {
  492. len += read_len;
  493. size -= read_len;
  494. }
  495. else
  496. {
  497. if (rt_sem_take(client->rx_notice, rt_tick_from_millisecond(timeout)) != RT_EOK)
  498. break;
  499. }
  500. }
  501. #ifdef AT_PRINT_RAW_CMD
  502. at_print_raw_cmd("urc_recv", buf, len);
  503. #endif
  504. return len;
  505. }
  506. /**
  507. * AT client set end sign.
  508. *
  509. * @param client current AT client object
  510. * @param ch the end sign, can not be used when it is '\0'
  511. */
  512. void at_obj_set_end_sign(at_client_t client, char ch)
  513. {
  514. if (client == RT_NULL)
  515. {
  516. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  517. return;
  518. }
  519. client->end_sign = ch;
  520. }
  521. /**
  522. * set URC(Unsolicited Result Code) table
  523. *
  524. * @param client current AT client object
  525. * @param table URC table
  526. * @param size table size
  527. */
  528. int at_obj_set_urc_table(at_client_t client, const struct at_urc *urc_table, rt_size_t table_sz)
  529. {
  530. rt_size_t idx;
  531. if (client == RT_NULL)
  532. {
  533. LOG_E("input AT Client object is NULL, please create or get AT Client object!");
  534. return -RT_ERROR;
  535. }
  536. for (idx = 0; idx < table_sz; idx++)
  537. {
  538. RT_ASSERT(urc_table[idx].cmd_prefix);
  539. RT_ASSERT(urc_table[idx].cmd_suffix);
  540. }
  541. if (client->urc_table_size == 0)
  542. {
  543. client->urc_table = (struct at_urc_table *) rt_calloc(1, sizeof(struct at_urc_table));
  544. if (client->urc_table == RT_NULL)
  545. {
  546. return -RT_ENOMEM;
  547. }
  548. client->urc_table[0].urc = urc_table;
  549. client->urc_table[0].urc_size = table_sz;
  550. client->urc_table_size++;
  551. }
  552. else
  553. {
  554. struct at_urc_table *new_urc_table = RT_NULL;
  555. /* realloc urc table space */
  556. new_urc_table = (struct at_urc_table *) rt_realloc(client->urc_table,client->urc_table_size * sizeof(struct at_urc_table) + sizeof(struct at_urc_table));
  557. if (new_urc_table == RT_NULL)
  558. {
  559. return -RT_ENOMEM;
  560. }
  561. client->urc_table = new_urc_table;
  562. client->urc_table[client->urc_table_size].urc = urc_table;
  563. client->urc_table[client->urc_table_size].urc_size = table_sz;
  564. client->urc_table_size++;
  565. }
  566. return RT_EOK;
  567. }
  568. /**
  569. * get AT client object by AT device name.
  570. *
  571. * @dev_name AT client device name
  572. *
  573. * @return AT client object
  574. */
  575. at_client_t at_client_get(const char *dev_name)
  576. {
  577. int idx = 0;
  578. RT_ASSERT(dev_name);
  579. for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
  580. {
  581. if (at_client_table[idx].device &&
  582. (rt_strcmp(at_client_table[idx].device->parent.name, dev_name) == 0))
  583. {
  584. return &at_client_table[idx];
  585. }
  586. }
  587. return RT_NULL;
  588. }
  589. /**
  590. * get first AT client object in the table.
  591. *
  592. * @return AT client object
  593. */
  594. at_client_t at_client_get_first(void)
  595. {
  596. if (at_client_table[0].device == RT_NULL)
  597. {
  598. return RT_NULL;
  599. }
  600. return &at_client_table[0];
  601. }
  602. static const struct at_urc *get_urc_obj(at_client_t client)
  603. {
  604. rt_size_t i, j, prefix_len, suffix_len;
  605. rt_size_t bufsz;
  606. char *buffer = RT_NULL;
  607. const struct at_urc *urc = RT_NULL;
  608. struct at_urc_table *urc_table = RT_NULL;
  609. if (client->urc_table == RT_NULL)
  610. {
  611. return RT_NULL;
  612. }
  613. buffer = client->recv_line_buf;
  614. bufsz = client->recv_line_len;
  615. for (i = 0; i < client->urc_table_size; i++)
  616. {
  617. for (j = 0; j < client->urc_table[i].urc_size; j++)
  618. {
  619. urc_table = client->urc_table + i;
  620. urc = urc_table->urc + j;
  621. prefix_len = rt_strlen(urc->cmd_prefix);
  622. suffix_len = rt_strlen(urc->cmd_suffix);
  623. if (bufsz < prefix_len + suffix_len)
  624. {
  625. continue;
  626. }
  627. if ((prefix_len ? !rt_strncmp(buffer, urc->cmd_prefix, prefix_len) : 1)
  628. && (suffix_len ? !rt_strncmp(buffer + bufsz - suffix_len, urc->cmd_suffix, suffix_len) : 1))
  629. {
  630. return urc;
  631. }
  632. }
  633. }
  634. return RT_NULL;
  635. }
  636. static int at_recv_readline(at_client_t client)
  637. {
  638. char ch = 0, last_ch = 0;
  639. rt_bool_t is_full = RT_FALSE;
  640. rt_memset(client->recv_line_buf, 0x00, client->recv_bufsz);
  641. client->recv_line_len = 0;
  642. while (1)
  643. {
  644. at_client_getchar(client, &ch, RT_WAITING_FOREVER);
  645. if (client->recv_line_len < client->recv_bufsz)
  646. {
  647. client->recv_line_buf[client->recv_line_len++] = ch;
  648. }
  649. else
  650. {
  651. is_full = RT_TRUE;
  652. }
  653. /* is newline or URC data */
  654. if ((client->urc = get_urc_obj(client)) != RT_NULL || (ch == '\n' && last_ch == '\r')
  655. || (client->end_sign != 0 && ch == client->end_sign))
  656. {
  657. if (is_full)
  658. {
  659. LOG_E("read line failed. The line data length is out of buffer size(%d)!", client->recv_bufsz);
  660. rt_memset(client->recv_line_buf, 0x00, client->recv_bufsz);
  661. client->recv_line_len = 0;
  662. return -RT_EFULL;
  663. }
  664. break;
  665. }
  666. last_ch = ch;
  667. }
  668. #ifdef AT_PRINT_RAW_CMD
  669. at_print_raw_cmd("recvline", client->recv_line_buf, client->recv_line_len);
  670. #endif
  671. return client->recv_line_len;
  672. }
  673. static void client_parser(at_client_t client)
  674. {
  675. while(1)
  676. {
  677. if (at_recv_readline(client) > 0)
  678. {
  679. if (client->urc != RT_NULL)
  680. {
  681. /* current receive is request, try to execute related operations */
  682. if (client->urc->func != RT_NULL)
  683. {
  684. client->urc->func(client, client->recv_line_buf, client->recv_line_len);
  685. }
  686. client->urc = RT_NULL;
  687. }
  688. else if (client->resp != RT_NULL)
  689. {
  690. at_response_t resp = client->resp;
  691. char end_ch = client->recv_line_buf[client->recv_line_len - 1];
  692. /* current receive is response */
  693. client->recv_line_buf[client->recv_line_len - 1] = '\0';
  694. if (resp->buf_len + client->recv_line_len < resp->buf_size)
  695. {
  696. /* copy response lines, separated by '\0' */
  697. rt_memcpy(resp->buf + resp->buf_len, client->recv_line_buf, client->recv_line_len);
  698. /* update the current response information */
  699. resp->buf_len += client->recv_line_len;
  700. resp->line_counts++;
  701. }
  702. else
  703. {
  704. client->resp_status = AT_RESP_BUFF_FULL;
  705. LOG_E("Read response buffer failed. The Response buffer size is out of buffer size(%d)!", resp->buf_size);
  706. }
  707. /* check response result */
  708. if ((client->end_sign != 0) && (end_ch == client->end_sign) && (resp->line_num == 0))
  709. {
  710. /* get the end sign, return response state END_OK.*/
  711. client->resp_status = AT_RESP_OK;
  712. }
  713. else if (rt_memcmp(client->recv_line_buf, AT_RESP_END_OK, rt_strlen(AT_RESP_END_OK)) == 0
  714. && resp->line_num == 0)
  715. {
  716. /* get the end data by response result, return response state END_OK. */
  717. client->resp_status = AT_RESP_OK;
  718. }
  719. else if (rt_strstr(client->recv_line_buf, AT_RESP_END_ERROR)
  720. || (rt_memcmp(client->recv_line_buf, AT_RESP_END_FAIL, rt_strlen(AT_RESP_END_FAIL)) == 0))
  721. {
  722. client->resp_status = AT_RESP_ERROR;
  723. }
  724. else if (resp->line_counts == resp->line_num && resp->line_num)
  725. {
  726. /* get the end data by response line, return response state END_OK.*/
  727. client->resp_status = AT_RESP_OK;
  728. }
  729. else
  730. {
  731. continue;
  732. }
  733. client->resp = RT_NULL;
  734. rt_sem_release(client->resp_notice);
  735. }
  736. else
  737. {
  738. /* log_d("unrecognized line: %.*s", client->recv_line_len, client->recv_line_buf);*/
  739. }
  740. }
  741. }
  742. }
  743. static rt_err_t at_client_rx_ind(rt_device_t dev, rt_size_t size)
  744. {
  745. int idx = 0;
  746. for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
  747. {
  748. if (at_client_table[idx].device == dev && size > 0)
  749. {
  750. rt_sem_release(at_client_table[idx].rx_notice);
  751. }
  752. }
  753. return RT_EOK;
  754. }
  755. /* initialize the client object parameters */
  756. static int at_client_para_init(at_client_t client)
  757. {
  758. #define AT_CLIENT_LOCK_NAME "at_c"
  759. #define AT_CLIENT_SEM_NAME "at_cs"
  760. #define AT_CLIENT_RESP_NAME "at_cr"
  761. #define AT_CLIENT_THREAD_NAME "at_clnt"
  762. int result = RT_EOK;
  763. static int at_client_num = 0;
  764. char name[RT_NAME_MAX];
  765. client->status = AT_STATUS_UNINITIALIZED;
  766. client->recv_line_len = 0;
  767. client->recv_line_buf = (char *) rt_calloc(1, client->recv_bufsz);
  768. if (client->recv_line_buf == RT_NULL)
  769. {
  770. LOG_E("AT client initialize failed! No memory for receive buffer.");
  771. result = -RT_ENOMEM;
  772. goto __exit;
  773. }
  774. client->last_cmd_len = 0;
  775. client->send_buf = (char *) rt_calloc(1, client->send_bufsz);
  776. if (client->send_buf == RT_NULL)
  777. {
  778. LOG_E("AT client initialize failed! No memory for send buffer.");
  779. result = -RT_ENOMEM;
  780. goto __exit;
  781. }
  782. rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_LOCK_NAME, at_client_num);
  783. client->lock = rt_mutex_create(name, RT_IPC_FLAG_PRIO);
  784. if (client->lock == RT_NULL)
  785. {
  786. LOG_E("AT client initialize failed! at_client_recv_lock create failed!");
  787. result = -RT_ENOMEM;
  788. goto __exit;
  789. }
  790. rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_SEM_NAME, at_client_num);
  791. client->rx_notice = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
  792. if (client->rx_notice == RT_NULL)
  793. {
  794. LOG_E("AT client initialize failed! at_client_notice semaphore create failed!");
  795. result = -RT_ENOMEM;
  796. goto __exit;
  797. }
  798. rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_RESP_NAME, at_client_num);
  799. client->resp_notice = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
  800. if (client->resp_notice == RT_NULL)
  801. {
  802. LOG_E("AT client initialize failed! at_client_resp semaphore create failed!");
  803. result = -RT_ENOMEM;
  804. goto __exit;
  805. }
  806. client->urc_table = RT_NULL;
  807. client->urc_table_size = 0;
  808. rt_snprintf(name, RT_NAME_MAX, "%s%d", AT_CLIENT_THREAD_NAME, at_client_num);
  809. client->parser = rt_thread_create(name,
  810. (void (*)(void *parameter))client_parser,
  811. client,
  812. 1024 + 512,
  813. RT_THREAD_PRIORITY_MAX / 3 - 1,
  814. 5);
  815. if (client->parser == RT_NULL)
  816. {
  817. result = -RT_ENOMEM;
  818. }
  819. __exit:
  820. if (result != RT_EOK)
  821. {
  822. if (client->lock)
  823. {
  824. rt_mutex_delete(client->lock);
  825. }
  826. if (client->rx_notice)
  827. {
  828. rt_sem_delete(client->rx_notice);
  829. }
  830. if (client->resp_notice)
  831. {
  832. rt_sem_delete(client->resp_notice);
  833. }
  834. if (client->recv_line_buf)
  835. {
  836. rt_free(client->recv_line_buf);
  837. }
  838. if (client->send_buf)
  839. {
  840. rt_free(client->send_buf);
  841. }
  842. rt_memset(client, 0x00, sizeof(struct at_client));
  843. }
  844. else
  845. {
  846. at_client_num++;
  847. }
  848. return result;
  849. }
  850. /**
  851. * AT client initialize.
  852. *
  853. * @param dev_name AT client device name
  854. * @param recv_bufsz the maximum number of receive buffer length
  855. * @param send_bufsz the maximum number of send command length
  856. *
  857. * @return 0 : initialize success
  858. * -1 : initialize failed
  859. * -5 : no memory
  860. */
  861. int at_client_init(const char *dev_name, rt_size_t recv_bufsz, rt_size_t send_bufsz)
  862. {
  863. int idx = 0;
  864. int result = RT_EOK;
  865. rt_err_t open_result = RT_EOK;
  866. at_client_t client = RT_NULL;
  867. RT_ASSERT(dev_name);
  868. RT_ASSERT(recv_bufsz > 0);
  869. RT_ASSERT(send_bufsz > 0);
  870. if (at_client_get(dev_name) != RT_NULL)
  871. {
  872. return result;
  873. }
  874. for (idx = 0; idx < AT_CLIENT_NUM_MAX && at_client_table[idx].device; idx++);
  875. if (idx >= AT_CLIENT_NUM_MAX)
  876. {
  877. LOG_E("AT client initialize failed! Check the maximum number(%d) of AT client.", AT_CLIENT_NUM_MAX);
  878. result = -RT_EFULL;
  879. goto __exit;
  880. }
  881. client = &at_client_table[idx];
  882. client->recv_bufsz = recv_bufsz;
  883. client->send_bufsz = send_bufsz;
  884. result = at_client_para_init(client);
  885. if (result != RT_EOK)
  886. {
  887. goto __exit;
  888. }
  889. /* find and open command device */
  890. client->device = rt_device_find(dev_name);
  891. if (client->device)
  892. {
  893. RT_ASSERT(client->device->type == RT_Device_Class_Char);
  894. rt_device_set_rx_indicate(client->device, at_client_rx_ind);
  895. #ifdef RT_USING_SERIAL_V2
  896. open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_RX_NON_BLOCKING);
  897. #else
  898. /* using DMA mode first */
  899. open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
  900. /* using interrupt mode when DMA mode not supported */
  901. if (open_result == -RT_EIO)
  902. {
  903. open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
  904. }
  905. #endif /* RT_USING_SERIAL_V2 */
  906. RT_ASSERT(open_result == RT_EOK);
  907. }
  908. else
  909. {
  910. LOG_E("AT client initialize failed! Not find the device(%s).", dev_name);
  911. result = -RT_ERROR;
  912. }
  913. __exit:
  914. if (result == RT_EOK)
  915. {
  916. client->status = AT_STATUS_INITIALIZED;
  917. rt_thread_startup(client->parser);
  918. LOG_I("AT client(V%s) on device %s initialize success.", AT_SW_VERSION, dev_name);
  919. }
  920. else
  921. {
  922. LOG_E("AT client(V%s) on device %s initialize failed(%d).", AT_SW_VERSION, dev_name, result);
  923. }
  924. return result;
  925. }
  926. #endif /* AT_USING_CLIENT */