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