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-14 chenyong modify parse arguments
10 * 2025-01-02 dongly support SERIAL_V2
11 * 2025-04-18 RyanCw support New SERIAL_V2
12 */
13
14 #include <at.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18
19 #include <rthw.h>
20
21 #define LOG_TAG "at.svr"
22 #include <at_log.h>
23
24 #ifdef AT_USING_SERVER
25
26 #define AT_CMD_CHAR_0 '0'
27 #define AT_CMD_CHAR_9 '9'
28 #define AT_CMD_QUESTION_MARK '?'
29 #define AT_CMD_EQUAL_MARK '='
30 #define AT_CMD_L_SQ_BRACKET '['
31 #define AT_CMD_R_SQ_BRACKET ']'
32 #define AT_CMD_L_ANGLE_BRACKET '<'
33 #define AT_CMD_R_ANGLE_BRACKET '>'
34 #define AT_CMD_COMMA_MARK ','
35 #define AT_CMD_SEMICOLON ';'
36 #define AT_CMD_CR '\r'
37 #define AT_CMD_LF '\n'
38 #define AT_CMD_NULL '\0'
39
40 static at_server_t at_server_local = RT_NULL;
41 static at_cmd_t cmd_table = RT_NULL;
42 static rt_size_t cmd_num;
43
44 extern rt_size_t at_utils_send(rt_device_t dev,
45 rt_off_t pos,
46 const void *buffer,
47 rt_size_t size);
48 extern void at_vprintf(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
49 extern void at_vprintfln(rt_device_t device, char *send_buf, rt_size_t buf_size, const char *format, va_list args);
50
51 /**
52 * AT server send data to AT device
53 *
54 * @param format the input format
55 */
at_server_printf(const char * format,...)56 void at_server_printf(const char *format, ...)
57 {
58 va_list args;
59
60 va_start(args, format);
61
62 at_vprintf(at_server_local->device, at_server_local->send_buffer, sizeof(at_server_local->send_buffer), format, args);
63
64 va_end(args);
65 }
66
67 /**
68 * AT server send data and newline to AT device
69 *
70 * @param format the input format
71 */
at_server_printfln(const char * format,...)72 void at_server_printfln(const char *format, ...)
73 {
74 va_list args;
75
76 va_start(args, format);
77
78 at_vprintfln(at_server_local->device, at_server_local->send_buffer, sizeof(at_server_local->send_buffer), format, args);
79
80 va_end(args);
81 }
82
83
84 /**
85 * AT server request arguments parse arguments
86 *
87 * @param req_args request arguments
88 * @param req_expr request expression
89 *
90 * @return -1 : parse arguments failed
91 * 0 : parse without match
92 * >0 : The number of arguments successfully parsed
93 */
at_req_parse_args(const char * req_args,const char * req_expr,...)94 int at_req_parse_args(const char *req_args, const char *req_expr, ...)
95 {
96 va_list args;
97 int req_args_num = 0;
98
99 RT_ASSERT(req_args);
100 RT_ASSERT(req_expr);
101
102 va_start(args, req_expr);
103
104 req_args_num = vsscanf(req_args, req_expr, args);
105
106 va_end(args);
107
108 return req_args_num;
109 }
110
111 /**
112 * AT server send command execute result to AT device
113 *
114 * @param result AT command execute result
115 */
at_server_print_result(at_result_t result)116 void at_server_print_result(at_result_t result)
117 {
118 switch (result)
119 {
120 case AT_RESULT_OK:
121 at_server_printfln("");
122 at_server_printfln("OK");
123 break;
124
125 case AT_RESULT_FAILE:
126 at_server_printfln("");
127 at_server_printfln("ERROR");
128 break;
129
130 case AT_RESULT_NULL:
131 break;
132
133 case AT_RESULT_CMD_ERR:
134 at_server_printfln("ERR CMD MATCH FAILED!");
135 at_server_print_result(AT_RESULT_FAILE);
136 break;
137
138 case AT_RESULT_CHECK_FAILE:
139 at_server_printfln("ERR CHECK ARGS FORMAT FAILED!");
140 at_server_print_result(AT_RESULT_FAILE);
141 break;
142
143 case AT_RESULT_PARSE_FAILE:
144 at_server_printfln("ERR PARSE ARGS FAILED!");
145 at_server_print_result(AT_RESULT_FAILE);
146 break;
147
148 default:
149 break;
150 }
151 }
152
153 /**
154 * AT server print all commands to AT device
155 */
rt_at_server_print_all_cmd(void)156 void rt_at_server_print_all_cmd(void)
157 {
158 rt_size_t i = 0;
159
160 at_server_printfln("Commands list : ");
161
162 for (i = 0; i < cmd_num; i++)
163 {
164 at_server_printf("%s", cmd_table[i].name);
165
166 if (cmd_table[i].args_expr)
167 {
168 at_server_printfln("%s", cmd_table[i].args_expr);
169 }
170 else
171 {
172 at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
173 }
174 }
175 }
176
177 /**
178 * Send data to AT Client by uart device.
179 *
180 * @param server current AT server object
181 * @param buf send data buffer
182 * @param size send fixed data size
183 *
184 * @return >0: send data size
185 * =0: send failed
186 */
at_server_send(at_server_t server,const char * buf,rt_size_t size)187 rt_size_t at_server_send(at_server_t server, const char *buf, rt_size_t size)
188 {
189 RT_ASSERT(buf);
190
191 if (server == RT_NULL)
192 {
193 LOG_E("input AT Server object is NULL, please create or get AT Server object!");
194 return 0;
195 }
196
197 return at_utils_send(server->device, 0, buf, size);
198 }
199
200 /**
201 * AT Server receive fixed-length data.
202 *
203 * @param client current AT Server object
204 * @param buf receive data buffer
205 * @param size receive fixed data size
206 * @param timeout receive data timeout (ms)
207 *
208 * @note this function can only be used in execution function of AT commands
209 *
210 * @return >0: receive data size
211 * =0: receive failed
212 */
at_server_recv(at_server_t server,char * buf,rt_size_t size,rt_int32_t timeout)213 rt_size_t at_server_recv(at_server_t server, char *buf, rt_size_t size, rt_int32_t timeout)
214 {
215 rt_size_t read_idx = 0;
216 rt_err_t result = RT_EOK;
217 char ch = 0;
218
219 RT_ASSERT(buf);
220
221 if (server == RT_NULL)
222 {
223 LOG_E("input AT Server object is NULL, please create or get AT Server object!");
224 return 0;
225 }
226
227 #if (!defined(RT_USING_SERIAL_V2))
228 while (1)
229 {
230 if (read_idx < size)
231 {
232 /* check get data value */
233 result = server->get_char(server, &ch, timeout);
234 if (result != RT_EOK)
235 {
236 LOG_E("AT Server receive failed, uart device get data error.");
237 return 0;
238 }
239
240 buf[read_idx++] = ch;
241 }
242 else
243 {
244 break;
245 }
246 }
247 #else
248 rt_int32_t rx_timout = rt_tick_from_millisecond(timeout);
249 rt_device_control(server->device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void*)&rx_timout);
250 read_idx = rt_device_read(server->device, 0, buf, size);
251 rx_timeout = RT_WAITING_FOREVER;
252 rt_device_control(server->device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void*)&rx_timeout);
253 #endif
254
255 return read_idx;
256 }
257
at_get_server(void)258 at_server_t at_get_server(void)
259 {
260 RT_ASSERT(at_server_local);
261 RT_ASSERT(at_server_local->status != AT_STATUS_UNINITIALIZED);
262
263 return at_server_local;
264 }
265
at_check_args(const char * args,const char * args_format)266 static rt_err_t at_check_args(const char *args, const char *args_format)
267 {
268 rt_size_t left_sq_bracket_num = 0, right_sq_bracket_num = 0;
269 rt_size_t left_angle_bracket_num = 0, right_angle_bracket_num = 0;
270 rt_size_t comma_mark_num = 0;
271 rt_size_t i = 0;
272
273 RT_ASSERT(args);
274 RT_ASSERT(args_format);
275
276 for (i = 0; i < strlen(args_format); i++)
277 {
278 switch (args_format[i])
279 {
280 case AT_CMD_L_SQ_BRACKET:
281 left_sq_bracket_num++;
282 break;
283
284 case AT_CMD_R_SQ_BRACKET:
285 right_sq_bracket_num++;
286 break;
287
288 case AT_CMD_L_ANGLE_BRACKET:
289 left_angle_bracket_num++;
290 break;
291
292 case AT_CMD_R_ANGLE_BRACKET:
293 right_angle_bracket_num++;
294 break;
295
296 default:
297 break;
298 }
299 }
300
301 if (left_sq_bracket_num != right_sq_bracket_num || left_angle_bracket_num != right_angle_bracket_num
302 || left_sq_bracket_num > left_angle_bracket_num)
303 {
304 return -RT_ERROR;
305 }
306
307 for (i = 0; i < strlen(args); i++)
308 {
309 if (args[i] == AT_CMD_COMMA_MARK)
310 {
311 comma_mark_num++;
312 }
313 }
314
315 if ((comma_mark_num + 1 < left_angle_bracket_num - left_sq_bracket_num)
316 || comma_mark_num + 1 > left_angle_bracket_num)
317 {
318 return -RT_ERROR;
319 }
320
321 return RT_EOK;
322 }
323
at_cmd_process(at_cmd_t cmd,const char * cmd_args)324 static at_result_t at_cmd_process(at_cmd_t cmd, const char *cmd_args)
325 {
326 RT_ASSERT(cmd);
327 RT_ASSERT(cmd_args);
328
329 /* AT+TEST=? */
330 if (cmd_args[0] == AT_CMD_EQUAL_MARK && cmd_args[1] == AT_CMD_QUESTION_MARK)
331 {
332 if (cmd->test == RT_NULL)
333 {
334 return AT_RESULT_CMD_ERR;
335 }
336
337 return cmd->test();
338 }
339 /* AT+TEST? */
340 else if (cmd_args[0] == AT_CMD_QUESTION_MARK)
341 {
342 if (cmd->query == RT_NULL)
343 {
344 return AT_RESULT_CMD_ERR;
345 }
346
347 return cmd->query();
348 }
349 /* AT+TEST=1 or ATE1 */
350 else if (cmd_args[0] == AT_CMD_EQUAL_MARK
351 || (cmd_args[0] >= AT_CMD_CHAR_0 && cmd_args[0] <= AT_CMD_CHAR_9 && (cmd_args[1] == AT_CMD_CR || cmd_args[1] == AT_CMD_LF)))
352 {
353 if (cmd->setup == RT_NULL)
354 {
355 return AT_RESULT_CMD_ERR;
356 }
357
358 if(at_check_args(cmd_args, cmd->args_expr) < 0)
359 {
360 return AT_RESULT_CHECK_FAILE;
361 }
362
363 return cmd->setup(cmd_args);
364 }
365 /* AT+TEST */
366 else if (cmd_args[0] == AT_CMD_CR || cmd_args[0] == AT_CMD_LF)
367 {
368 if (cmd->exec == RT_NULL)
369 {
370 return AT_RESULT_CMD_ERR;
371 }
372
373 return cmd->exec();
374 }
375
376 return AT_RESULT_FAILE;
377 }
378
at_find_cmd(const char * cmd)379 static at_cmd_t at_find_cmd(const char *cmd)
380 {
381 rt_size_t i = 0;
382
383 RT_ASSERT(cmd_table);
384
385 for (i = 0; i < cmd_num; i++)
386 {
387 if (!strcasecmp(cmd, cmd_table[i].name))
388 {
389 return &cmd_table[i];
390 }
391 }
392 return RT_NULL;
393 }
394
at_cmd_get_name(const char * cmd_buffer,char * cmd_name)395 static rt_err_t at_cmd_get_name(const char *cmd_buffer, char *cmd_name)
396 {
397 rt_size_t cmd_name_len = 0, i = 0;
398
399 RT_ASSERT(cmd_name);
400 RT_ASSERT(cmd_buffer);
401
402 for (i = 0; i < strlen(cmd_buffer) && i < AT_CMD_NAME_LEN; i++)
403 {
404 if (*(cmd_buffer + i) == AT_CMD_QUESTION_MARK || *(cmd_buffer + i) == AT_CMD_EQUAL_MARK
405 || *(cmd_buffer + i) == AT_CMD_CR || *(cmd_buffer + i) == AT_CMD_LF
406 || (*(cmd_buffer + i) >= AT_CMD_CHAR_0 && *(cmd_buffer + i) <= AT_CMD_CHAR_9))
407 {
408 cmd_name_len = i;
409 rt_memcpy(cmd_name, cmd_buffer, cmd_name_len);
410 *(cmd_name + cmd_name_len) = '\0';
411
412 return RT_EOK;
413 }
414 }
415
416 return -RT_ERROR;
417 }
418
at_server_getchar(at_server_t server,char * ch,rt_int32_t timeout)419 static rt_err_t at_server_getchar(at_server_t server, char *ch, rt_int32_t timeout)
420 {
421 rt_err_t result = RT_EOK;
422
423 #if (!defined(RT_USING_SERIAL_V2))
424 while (rt_device_read(at_server_local->device, 0, ch, 1) == 0)
425 {
426 result = rt_sem_take(at_server_local->rx_notice, rt_tick_from_millisecond(timeout));
427 if (result != RT_EOK)
428 {
429 return result;
430 }
431 rt_sem_control(at_server_local->rx_notice, RT_IPC_CMD_RESET, RT_NULL);
432 }
433 #else
434 rt_int32_t rx_timout = rt_tick_from_millisecond(timeout);
435 rt_device_control(server->device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void*)&rx_timout);
436 result = rt_device_read(server->device, 0, ch, 1);
437 if(result <= 0)
438 {
439 result = -RT_ERROR;
440 }
441 rx_timeout = RT_WAITING_FOREVER;
442 rt_device_control(server->device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void*)&rx_timeout);
443 #endif
444
445 return result;
446 }
447
server_parser(at_server_t server)448 static void server_parser(at_server_t server)
449 {
450 #define ESC_KEY 0x1B
451 #define BACKSPACE_KEY 0x08
452 #define DELECT_KEY 0x7F
453
454 char cur_cmd_name[AT_CMD_NAME_LEN] = { 0 };
455 at_cmd_t cur_cmd = RT_NULL;
456 char *cur_cmd_args = RT_NULL, ch, last_ch;
457 at_result_t result;
458
459 RT_ASSERT(server);
460 RT_ASSERT(server->status != AT_STATUS_UNINITIALIZED);
461
462 while (1)
463 {
464 server->get_char(server, &ch, RT_WAITING_FOREVER);
465 if (ESC_KEY == ch)
466 {
467 break;
468 }
469
470 if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
471 {
472 if (server->cur_recv_len)
473 server->cur_recv_len--;
474 if (server->echo_mode)
475 at_server_printf("\b \b");
476 }
477 else if (ch == AT_CMD_LF && last_ch == AT_CMD_CR)
478 {
479 /* skip '\n' if we get "\r\n" */
480 }
481 else
482 {
483 if (server->cur_recv_len < sizeof(server->recv_buffer) - 1)
484 {
485 server->recv_buffer[server->cur_recv_len++] = ch;
486 if (ch == AT_CMD_CR || ch == AT_CMD_LF)
487 {
488 if (server->echo_mode)
489 at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
490 server->recv_buffer[server->cur_recv_len] = '\0';
491 result = AT_RESULT_FAILE;
492 if (at_cmd_get_name(server->recv_buffer, cur_cmd_name) == RT_EOK)
493 {
494 cur_cmd = at_find_cmd(cur_cmd_name);
495 if (cur_cmd)
496 {
497 cur_cmd_args = server->recv_buffer + strlen(cur_cmd_name);
498 result = at_cmd_process(cur_cmd, cur_cmd_args);
499 }
500 }
501
502 at_server_print_result(result);
503 server->cur_recv_len = 0;
504 }
505 else
506 {
507 if (server->echo_mode)
508 at_server_printf("%c", ch);
509 }
510 last_ch = ch;
511 }
512 else
513 {
514 /* server receive buffer overflow!!! */
515 server->cur_recv_len = 0;
516 }
517 }
518 }
519 }
520
521 #if (!defined(RT_USING_SERIAL_V2))
at_rx_ind(rt_device_t dev,rt_size_t size)522 static rt_err_t at_rx_ind(rt_device_t dev, rt_size_t size)
523 {
524 if (size > 0)
525 {
526 rt_sem_release(at_server_local->rx_notice);
527 }
528
529 return RT_EOK;
530 }
531 #endif
532
533 #if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
534 #pragma section="RtAtCmdTab"
535 #endif
536
at_server_init(void)537 int at_server_init(void)
538 {
539 rt_err_t result = RT_EOK;
540 rt_err_t open_result = RT_EOK;
541
542 if (at_server_local)
543 {
544 return result;
545 }
546
547 /* initialize the AT commands table.*/
548 #if defined(__ARMCC_VERSION) /* ARM C Compiler */
549 extern const int RtAtCmdTab$$Base;
550 extern const int RtAtCmdTab$$Limit;
551 cmd_table = (at_cmd_t)&RtAtCmdTab$$Base;
552 cmd_num = (at_cmd_t)&RtAtCmdTab$$Limit - cmd_table;
553 #elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
554 cmd_table = (at_cmd_t)__section_begin("RtAtCmdTab");
555 cmd_num = (at_cmd_t)__section_end("RtAtCmdTab") - cmd_table;
556 #elif defined (__GNUC__) /* for GCC Compiler */
557 extern const int __rtatcmdtab_start;
558 extern const int __rtatcmdtab_end;
559 cmd_table = (at_cmd_t)&__rtatcmdtab_start;
560 cmd_num = (at_cmd_t) &__rtatcmdtab_end - cmd_table;
561 #endif /* defined(__CC_ARM) */
562
563 at_server_local = (at_server_t) rt_calloc(1, sizeof(struct at_server));
564 if (!at_server_local)
565 {
566 result = -RT_ENOMEM;
567 LOG_E("AT server session initialize failed! No memory for at_server structure !");
568 goto __exit;
569 }
570
571 at_server_local->echo_mode = 1;
572 at_server_local->status = AT_STATUS_UNINITIALIZED;
573
574 rt_memset(at_server_local->recv_buffer, 0x00, AT_SERVER_RECV_BUFF_LEN);
575 at_server_local->cur_recv_len = 0;
576
577 #if (!defined(RT_USING_SERIAL_V2))
578 at_server_local->rx_notice = rt_sem_create("at_svr", 0, RT_IPC_FLAG_FIFO);
579 if (!at_server_local->rx_notice)
580 {
581 LOG_E("AT server session initialize failed! at_rx_notice semaphore create failed!");
582 result = -RT_ENOMEM;
583 goto __exit;
584 }
585 #endif
586
587 /* Find and open command device */
588 at_server_local->device = rt_device_find(AT_SERVER_DEVICE);
589 if (at_server_local->device)
590 {
591 RT_ASSERT(at_server_local->device->type == RT_Device_Class_Char);
592 #if (!defined(RT_USING_SERIAL_V2))
593 rt_device_set_rx_indicate(at_server_local->device, at_rx_ind);
594 /* using DMA mode first */
595 open_result = rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
596 /* using interrupt mode when DMA mode not supported */
597 if (open_result == -RT_EIO)
598 {
599 open_result = rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
600 }
601 RT_ASSERT(open_result == RT_EOK);
602 #else
603 open_result = rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);
604 RT_ASSERT(open_result == RT_EOK);
605 #endif
606 }
607 else
608 {
609 LOG_E("AT device initialize failed! Not find the device : %s.", AT_SERVER_DEVICE);
610 result = -RT_ERROR;
611 goto __exit;
612 }
613
614 at_server_local->get_char = at_server_getchar;
615
616 at_server_local->parser_entry = server_parser;
617 at_server_local->parser = rt_thread_create("at_svr",
618 (void (*)(void *parameter))server_parser,
619 at_server_local,
620 2 * 1024,
621 RT_THREAD_PRIORITY_MAX / 3 - 1,
622 5);
623 if (at_server_local->parser == RT_NULL)
624 {
625 result = -RT_ENOMEM;
626 }
627
628 __exit:
629 if (!result)
630 {
631 at_server_local->status = AT_STATUS_INITIALIZED;
632
633 rt_thread_startup(at_server_local->parser);
634
635 LOG_I("RT-Thread AT server (V%s) initialize success.", AT_SW_VERSION);
636 }
637 else
638 {
639 if (at_server_local)
640 {
641
642 #if (!defined(RT_USING_SERIAL_V2))
643 if (at_server_local->rx_notice)
644 {
645 rt_sem_delete(at_server_local->rx_notice);
646 }
647 #endif
648
649 if (at_server_local->device)
650 {
651 rt_device_close(at_server_local->device);
652 }
653 rt_free(at_server_local);
654 at_server_local = RT_NULL;
655 }
656
657 LOG_E("RT-Thread AT server (V%s) initialize failed(%d).", AT_SW_VERSION, result);
658 }
659
660 return result;
661 }
662 INIT_COMPONENT_EXPORT(at_server_init);
663
at_port_reset(void)664 rt_weak void at_port_reset(void)
665 {
666 LOG_E("The reset for AT server is not implement.");
667 }
668
at_port_factory_reset(void)669 rt_weak void at_port_factory_reset(void)
670 {
671 LOG_E("The factory reset for AT server is not implement.");
672 }
673
674 #endif /* AT_USING_SERVER */
675