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