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