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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 550 RT_WEAK void at_port_reset(void) 551 { 552 LOG_E("The reset for AT server is not implement."); 553 } 554 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