1 /**
2 *********************************************************************************************************
3 * Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved.
4 **********************************************************************************************************
5 * @file user_cmd_parse.c
6 * @brief Parse user command from lower Data UART data.
7 * @details Parse user commands and execute right commands.
8 * @author
9 * @date 2016-02-18
10 * @version v0.1
11 *********************************************************************************************************
12 */
13 #include <string.h>
14 #include <data_uart.h>
15 #include <user_cmd_parse.h>
16
17 /**
18 * @brief Check if a character is a white space.
19 *
20 * @param c Char data to check.
21 * @return Check result.
22 * @retval 1 true.
23 * @retval 0 false.
24 */
user_cmd_is_white_space(char c)25 static bool user_cmd_is_white_space(char c)
26 {
27 return (((c >= 9) && (c <= 13)) || (c == 32));
28 }
29
30 /**
31 * @brief Skip white spaces in buffer.
32 *
33 * @param buffer Address of the buffer.
34 * @return pointer to skipped white spaces' new buffer.
35 */
user_cmd_skip_spaces(char * buffer)36 static char *user_cmd_skip_spaces(char *buffer)
37 {
38 char *p = buffer;
39
40 while (user_cmd_is_white_space(*p)) /* white space */
41 {
42 p++;
43 }
44 return p;
45 }
46
47 /**
48 * @brief Find end of a word.
49 *
50 * @param buffer Address of the buffer.
51 * @return
52 */
user_cmd_find_end_of_word(char * buffer)53 static char *user_cmd_find_end_of_word(char *buffer)
54 {
55 char *p = buffer;
56
57 while (!user_cmd_is_white_space(*p) && (*p != '\0'))
58 {
59 p++;
60 }
61 return p;
62 }
63
64 /**
65 * @brief Read ASCII string and convert to uint32_t.
66 *
67 * @param p String address.
68 * @return
69 */
user_cmd_str_to_uint32(char * p)70 static uint32_t user_cmd_str_to_uint32(char *p)
71 {
72 uint32_t result = 0;
73 bool hex = false;
74
75 /* check if value is dec */
76 if (p[0] == 'x')
77 {
78 hex = true;
79 p = &p[1];
80 }
81 else if ((p[0] == '0') && (p[1] == 'x'))
82 {
83 hex = true;
84 p = &p[2];
85 }
86
87 for (;;)
88 {
89 char ch;
90 ch = *(p++) | 0x20; /* convert to lower case */
91
92 if (hex) /* dec value */
93 {
94 /* hex value */
95 if ((ch >= 'a') && (ch <= 'f'))
96 {
97 ch -= ('a' - 10);
98 }
99 else if ((ch >= '0') && (ch <= '9'))
100 {
101 ch -= '0';
102 }
103 else
104 {
105 break;
106 }
107 result = (result << 4);
108 result += (ch & 0x0f);
109 }
110 else
111 {
112 if (ch < '0' || ch > '9')
113 {
114 break; /* end of string reached */
115 }
116 result = 10 * result + ch - '0';
117 }
118 }
119 return (result);
120 }
121
122 /**
123 * @brief Send result, display in UART Assitant.
124 *
125 * @param result Command parse result.
126 * @return none
127 */
cmd_send_result(T_USER_CMD_PARSE_RESULT result)128 static void cmd_send_result(T_USER_CMD_PARSE_RESULT result)
129 {
130 switch (result)
131 {
132 case RESULT_ERR:
133 data_uart_print("%s\r\n", "CMD:Error");
134 break;
135 case RESULT_GAP_CAUSE_ALREADY_IN_REQ:
136 data_uart_print("%s\r\n", "GAP_CAUSE_ALREADY_IN_REQ");
137 break;
138 case RESULT_GAP_CAUSE_INVALID_STATE:
139 data_uart_print("%s\r\n", "GAP_CAUSE_INVALID_STATE");
140 break;
141 case RESULT_GAP_CAUSE_INVALID_PARAM:
142 data_uart_print("%s\r\n", "GAP_CAUSE_INVALID_PARAM");
143 break;
144 case RESULT_GAP_CAUSE_NON_CONN:
145 data_uart_print("%s\r\n", "GAP_CAUSE_NON_CONN");
146 break;
147 case RESULT_GAP_CAUSE_NOT_FIND_IRK:
148 data_uart_print("%s\r\n", "GAP_CAUSE_NOT_FIND_IRK");
149 break;
150 case RESULT_GAP_CAUSE_ERROR_CREDITS:
151 data_uart_print("%s\r\n", "GAP_CAUSE_ERROR_CREDITS");
152 break;
153 case RESULT_GAP_CAUSE_SEND_REQ_FAILED:
154 data_uart_print("%s\r\n", "GAP_CAUSE_SEND_REQ_FAILED");
155 break;
156 case RESULT_GAP_CAUSE_NO_RESOURCE:
157 data_uart_print("%s\r\n", "GAP_CAUSE_NO_RESOURCE");
158 break;
159 case RESULT_GAP_CAUSE_INVALID_PDU_SIZE:
160 data_uart_print("%s\r\n", "GAP_CAUSE_INVALID_PDU_SIZE");
161 break;
162 case RESULT_GAP_CAUSE_NOT_FIND:
163 data_uart_print("%s\r\n", "GAP_CAUSE_NOT_FIND");
164 break;
165 case RESULT_GAP_CAUSE_CONN_LIMIT:
166 data_uart_print("%s\r\n", "GAP_CAUSE_CONN_LIMIT");
167 break;
168 case RESULT_CMD_NOT_FOUND:
169 data_uart_print("%s\r\n", "CMD:Command not found");
170 break;
171 case RESULT_CMD_ERR_PARAM_NUM:
172 data_uart_print("%s\r\n", "CMD:Wrong number of parameters");
173 break;
174 case RESULT_CMD_ERR_PARAM:
175 data_uart_print("%s\r\n", "CMD:Wrong parameter");
176 break;
177 case RESULT_CMD_OUT_OF_RANGE:
178 data_uart_print("%s\r\n", "CMD:Value out of range");
179 break;
180 case RESULT_CMD_NOT_SUPPORT:
181 data_uart_print("%s\r\n", "CMD:Not support");
182 break;
183 case RESULT_GAP_CAUSE_ERROR_UNKNOWN:
184 data_uart_print("%s\r\n", "GAP_CAUSE_ERROR_UNKNOWN");
185 break;
186 default:
187 break;
188 }
189 }
190
191 /**
192 * @brief List cmd.
193 *
194 * @param p_cmd_table Command table, include user self-definition command function.
195 * @return Command execute result.
196 */
user_cmd_list(const T_USER_CMD_TABLE_ENTRY * p_cmd_table)197 static T_USER_CMD_PARSE_RESULT user_cmd_list(const T_USER_CMD_TABLE_ENTRY *p_cmd_table)
198 {
199 int32_t i = 0;
200 T_USER_CMD_PARSE_RESULT result = RESULT_CMD_NOT_FOUND;
201
202 /* find command in table */
203 while ((p_cmd_table + i)->p_cmd != NULL)
204 {
205 data_uart_print("%s", (p_cmd_table + i)->p_option);
206 data_uart_print("%s", " *");
207 data_uart_print("%s", (p_cmd_table + i)->p_help);
208 result = RESULT_SUCESS;
209 i++;
210 };
211
212 data_uart_print(",.\r\n *up down\r\n");
213 data_uart_print("[]\r\n *left right\r\n");
214 data_uart_print("/\\\r\n *home end\r\n");
215 data_uart_print("backspace\r\n *delete\r\n");
216
217 return result;
218 }
219
220 /**
221 * @brief Execute command.
222 *
223 * @param p_parse_value Command parse value.
224 * @param p_cmd_table Command table, include user self-definition command function.
225 * @return Command execute result.
226 */
user_cmd_execute(T_USER_CMD_PARSED_VALUE * p_parse_value,const T_USER_CMD_TABLE_ENTRY * p_cmd_table)227 static T_USER_CMD_PARSE_RESULT user_cmd_execute(T_USER_CMD_PARSED_VALUE *p_parse_value,
228 const T_USER_CMD_TABLE_ENTRY *p_cmd_table)
229 {
230 int32_t i = 0;
231 T_USER_CMD_PARSE_RESULT result = RESULT_CMD_NOT_FOUND;
232
233 if (strcmp((const char *)p_parse_value->p_cmd, (const char *)"?") == 0)
234 {
235 user_cmd_list(p_cmd_table);
236 return RESULT_SUCESS;
237 }
238
239 /* find command in table */
240 while ((p_cmd_table + i)->p_cmd != NULL)
241 {
242 if (strcmp((const char *)(p_cmd_table + i)->p_cmd, (const char *)p_parse_value->p_cmd) == 0)
243 {
244 /* check if user wants help */
245 if (p_parse_value->param_count && *p_parse_value->p_param[0] == '?')
246 {
247 data_uart_print("%s", (p_cmd_table + i)->p_option);
248 data_uart_print("%s", " *");
249 data_uart_print("%s", (p_cmd_table + i)->p_help);
250 result = RESULT_SUCESS;
251 }
252 else
253 {
254 /* execute command function */
255 result = (p_cmd_table + i)->func(p_parse_value);
256 }
257 /* exit while */
258 break;
259 }
260 i++;
261 };
262
263 return result;
264 }
265
266 /**
267 * @brief Parse a command line and return the found command and parameters in "p_parse_value"
268 *
269 * @param p_user_cmd_if Command parsed.
270 * @param p_parse_value Command parse value.
271 * @return Command parse result.
272 */
user_cmd_parse(T_USER_CMD_IF * p_user_cmd_if,T_USER_CMD_PARSED_VALUE * p_parse_value)273 static T_USER_CMD_PARSE_RESULT user_cmd_parse(T_USER_CMD_IF *p_user_cmd_if,
274 T_USER_CMD_PARSED_VALUE *p_parse_value)
275 {
276 int32_t i;
277 char *p, *q;
278
279 /* clear all results */
280 p_parse_value->p_cmd = NULL;
281 p_parse_value->param_count = 0;
282 for (i = 0 ; i < USER_CMD_MAX_PARAMETERS; i++)
283 {
284 p_parse_value->p_param[i] = NULL;
285 p_parse_value->dw_param[i] = 0;
286 }
287
288 /* Parse line */
289 p = p_user_cmd_if->cmdline_buf;
290
291 /*ignore leading spaces */
292 p = user_cmd_skip_spaces(p);
293 if (*p == '\0') /* empty command line ? */
294 {
295 return RESULT_CMD_EMPTY_LINE;
296 }
297
298 /* find end of word */
299 q = user_cmd_find_end_of_word(p);
300 if (p == q) /* empty command line ? */
301 {
302 return RESULT_CMD_EMPTY_LINE;
303 }
304
305 p_parse_value->p_cmd = p;
306 *q = '\0'; /* mark end of command */
307 p = q + 1;
308
309 /* parse parameters */
310 if (*p != '\0') /* end of line ? */
311 {
312 int32_t j;
313
314 j = 0;
315 do
316 {
317 uint32_t d;
318 /* ignore leading spaces */
319 p = user_cmd_skip_spaces(p);
320 d = user_cmd_str_to_uint32(p);
321
322 p_parse_value->p_param[j] = p;
323 p_parse_value->dw_param[j++] = d;
324
325 if (j >= USER_CMD_MAX_PARAMETERS)
326 {
327 break;
328 }
329
330 /* find next parameter */
331 p = user_cmd_find_end_of_word(p);
332 *p++ = '\0'; /* mark end of parameter */
333 }
334 while (*p != '\0');
335
336 p_parse_value->param_count = j;
337 }
338
339 return RESULT_SUCESS;
340 }
341
342 /**
343 * @brief Clear command line buffer.
344 *
345 * @param p_user_cmd_if Command parsed.
346 * @return none.
347 */
cmd_clear(T_USER_CMD_IF * p_user_cmd_if)348 static void cmd_clear(T_USER_CMD_IF *p_user_cmd_if)
349 {
350 p_user_cmd_if->accum_cmd_len = 0;
351 p_user_cmd_if->cmd_cur = 0;
352 memset(p_user_cmd_if->cmdline_buf, 0, sizeof(p_user_cmd_if->cmdline_buf));
353 }
354
cmd_move_back(T_USER_CMD_IF * p_user_cmd_if)355 static void cmd_move_back(T_USER_CMD_IF *p_user_cmd_if)
356 {
357 for (uint8_t loop = 0; loop < p_user_cmd_if->accum_cmd_len - p_user_cmd_if->cmd_cur; loop ++)
358 {
359 p_user_cmd_if->cmdline_buf[p_user_cmd_if->accum_cmd_len - loop] =
360 p_user_cmd_if->cmdline_buf[p_user_cmd_if->accum_cmd_len - loop - 1];
361 }
362 }
363
cmd_move_forward(T_USER_CMD_IF * p_user_cmd_if)364 static void cmd_move_forward(T_USER_CMD_IF *p_user_cmd_if)
365 {
366 for (uint8_t loop = 0; loop < p_user_cmd_if->accum_cmd_len - p_user_cmd_if->cmd_cur; loop ++)
367 {
368 p_user_cmd_if->cmdline_buf[p_user_cmd_if->cmd_cur + loop - 1] =
369 p_user_cmd_if->cmdline_buf[p_user_cmd_if->cmd_cur + loop];
370 }
371 }
cmd_clear_screen(T_USER_CMD_IF * p_user_cmd_if)372 static void cmd_clear_screen(T_USER_CMD_IF *p_user_cmd_if)
373 {
374 if (p_user_cmd_if->cmd_cur < p_user_cmd_if->accum_cmd_len)
375 {
376 data_uart_print("%s", p_user_cmd_if->cmdline_buf + p_user_cmd_if->cmd_cur);
377 }
378
379 while (p_user_cmd_if->accum_cmd_len != 0)
380 {
381 p_user_cmd_if->accum_cmd_len--;
382 p_user_cmd_if->cmdline_buf[p_user_cmd_if->accum_cmd_len] = '\0';
383 data_uart_print("\b \b");
384 }
385 p_user_cmd_if->cmd_cur = 0;
386 }
387
388 /**
389 * @brief Collect command characters.
390 *
391 * @param[in] p_user_cmd_if Store parsed commands.
392 * @param[in] p_data Data to be parsed.
393 * @param[in] len Length of data to be command parsed.
394 * @param[in] p_cmd_table Command table to execute function.
395 * @return Command collect result.
396 * @retval 1 true.
397 * @retval 0 false.
398 *
399 * <b>Example usage</b>
400 * \code{.c}
401 void app_handle_io_msg(T_IO_MSG io_msg)
402 {
403 uint16_t msg_type = io_msg.type;
404 uint8_t rx_char;
405
406 switch (msg_type)
407 {
408 case IO_MSG_TYPE_UART:
409 // We handle user command informations from Data UART in this branch.
410 rx_char = (uint8_t)io_msg.subtype;
411 user_cmd_collect(&user_cmd_if, &rx_char, sizeof(rx_char), user_cmd_table);
412 break;
413 default:
414 break;
415 }
416 }
417 * \endcode
418 */
user_cmd_collect(T_USER_CMD_IF * p_user_cmd_if,uint8_t * p_data,int32_t len,const T_USER_CMD_TABLE_ENTRY * p_cmd_table)419 bool user_cmd_collect(T_USER_CMD_IF *p_user_cmd_if, uint8_t *p_data, int32_t len,
420 const T_USER_CMD_TABLE_ENTRY *p_cmd_table)
421 {
422 T_USER_CMD_PARSED_VALUE parse_result;
423
424 while (len--)
425 {
426 char c = *p_data++;
427
428 if (c != 0x0) /* not ESC character received */
429 {
430 switch (c) /* Normal handling */
431 {
432 case '\n':
433 case '\r': /* end of line */
434 data_uart_print("\r\n");
435 p_user_cmd_if->history_cur = USER_CMD_MAX_HISTORY_LINE;
436
437 if (p_user_cmd_if->accum_cmd_len > 0) /* at least one character in command line ? */
438 {
439 T_USER_CMD_PARSE_RESULT result;
440
441 // save cmd first
442 if (p_user_cmd_if->history_head == USER_CMD_MAX_HISTORY_LINE)
443 {
444 p_user_cmd_if->history_head = 0;
445 p_user_cmd_if->history_tail = 0;
446 }
447 else
448 {
449 p_user_cmd_if->history_tail = (p_user_cmd_if->history_tail + 1) % USER_CMD_MAX_HISTORY_LINE;
450 if (p_user_cmd_if->history_tail == p_user_cmd_if->history_head)
451 {
452 p_user_cmd_if->history_head = (p_user_cmd_if->history_head + 1) % USER_CMD_MAX_HISTORY_LINE;
453 }
454 }
455 p_user_cmd_if->cmd_history_len[p_user_cmd_if->history_tail] = p_user_cmd_if->accum_cmd_len;
456 memcpy(p_user_cmd_if->cmd_history[p_user_cmd_if->history_tail], p_user_cmd_if->cmdline_buf,
457 p_user_cmd_if->accum_cmd_len);
458
459 p_user_cmd_if->cmdline_buf[p_user_cmd_if->accum_cmd_len] = '\0';
460 result = user_cmd_parse(p_user_cmd_if, &parse_result);
461 if (result == RESULT_SUCESS)
462 {
463 result = user_cmd_execute(&parse_result, p_cmd_table);
464 }
465
466 if (result != RESULT_SUCESS)
467 {
468 cmd_send_result(result);
469 }
470 }
471
472 cmd_clear(p_user_cmd_if);
473 break;
474
475 case '\b': /* backspace */
476 if (p_user_cmd_if->accum_cmd_len > 0 && p_user_cmd_if->cmd_cur > 0)
477 {
478 uint8_t loop;
479
480 cmd_move_forward(p_user_cmd_if);
481 p_user_cmd_if->accum_cmd_len--;
482 p_user_cmd_if->cmd_cur--;
483 p_user_cmd_if->cmdline_buf[p_user_cmd_if->accum_cmd_len] = '\0';
484 data_uart_print("\b%s", p_user_cmd_if->cmdline_buf + p_user_cmd_if->cmd_cur);
485 data_uart_print(" \b");
486 for (loop = 0; loop < p_user_cmd_if->accum_cmd_len - p_user_cmd_if->cmd_cur; loop++)
487 {
488 data_uart_print("\b");
489 }
490 }
491 break;
492
493 case 44: /* up: < */
494 if (p_user_cmd_if->history_head != USER_CMD_MAX_HISTORY_LINE)
495 {
496 cmd_clear_screen(p_user_cmd_if);
497 if (p_user_cmd_if->history_cur == USER_CMD_MAX_HISTORY_LINE)
498 {
499 p_user_cmd_if->history_cur = p_user_cmd_if->history_tail;
500 }
501 else
502 {
503 if (p_user_cmd_if->history_cur != p_user_cmd_if->history_head)
504 {
505 p_user_cmd_if->history_cur = (p_user_cmd_if->history_cur + USER_CMD_MAX_HISTORY_LINE - 1) %
506 USER_CMD_MAX_HISTORY_LINE;
507 }
508 else
509 {
510 p_user_cmd_if->history_cur = USER_CMD_MAX_HISTORY_LINE;
511 break;
512 }
513 }
514 p_user_cmd_if->accum_cmd_len = p_user_cmd_if->cmd_history_len[p_user_cmd_if->history_cur];
515 p_user_cmd_if->cmd_cur = p_user_cmd_if->accum_cmd_len;
516 memcpy(p_user_cmd_if->cmdline_buf, p_user_cmd_if->cmd_history[p_user_cmd_if->history_cur],
517 p_user_cmd_if->accum_cmd_len);
518 data_uart_print("%s", p_user_cmd_if->cmdline_buf);
519 }
520 break;
521
522 case 46: /* down: > */
523 if (p_user_cmd_if->history_head != USER_CMD_MAX_HISTORY_LINE)
524 {
525 cmd_clear_screen(p_user_cmd_if);
526 if (p_user_cmd_if->history_cur == USER_CMD_MAX_HISTORY_LINE)
527 {
528 p_user_cmd_if->history_cur = p_user_cmd_if->history_head;
529 }
530 else
531 {
532 if (p_user_cmd_if->history_cur != p_user_cmd_if->history_tail)
533 {
534 p_user_cmd_if->history_cur = (p_user_cmd_if->history_cur + 1) % USER_CMD_MAX_HISTORY_LINE;
535 }
536 else
537 {
538 p_user_cmd_if->history_cur = USER_CMD_MAX_HISTORY_LINE;
539 break;
540 }
541 }
542 p_user_cmd_if->accum_cmd_len = p_user_cmd_if->cmd_history_len[p_user_cmd_if->history_cur];
543 p_user_cmd_if->cmd_cur = p_user_cmd_if->accum_cmd_len;
544 memcpy(p_user_cmd_if->cmdline_buf, p_user_cmd_if->cmd_history[p_user_cmd_if->history_cur],
545 p_user_cmd_if->accum_cmd_len);
546 data_uart_print("%s", p_user_cmd_if->cmdline_buf);
547 }
548 break;
549
550 case 91: /* left: { */
551 if (p_user_cmd_if->cmd_cur > 0)
552 {
553 data_uart_print("\b");
554 p_user_cmd_if->cmd_cur--;
555 }
556 break;
557
558 case 93: /* right: } */
559 if (p_user_cmd_if->cmd_cur < p_user_cmd_if->accum_cmd_len)
560 {
561 data_uart_print("%c", p_user_cmd_if->cmdline_buf[p_user_cmd_if->cmd_cur]);
562 p_user_cmd_if->cmd_cur++;
563 }
564 break;
565
566 case 92: /* end: \ */
567 if (p_user_cmd_if->cmd_cur < p_user_cmd_if->accum_cmd_len)
568 {
569 data_uart_print("%s", p_user_cmd_if->cmdline_buf + p_user_cmd_if->cmd_cur);
570 p_user_cmd_if->cmd_cur = p_user_cmd_if->accum_cmd_len;
571 }
572 break;
573
574 case 47: /* begin: / */
575 while (p_user_cmd_if->cmd_cur > 0)
576 {
577 data_uart_print("\b");
578 p_user_cmd_if->cmd_cur--;
579 }
580 break;
581
582 default:
583 /* Put character in command buffer */
584 if (p_user_cmd_if->accum_cmd_len < USER_CMD_MAX_COMMAND_LINE)
585 {
586 uint8_t loop;
587
588 cmd_move_back(p_user_cmd_if);
589 p_user_cmd_if->cmdline_buf[p_user_cmd_if->cmd_cur] = c;
590 data_uart_print("%s", p_user_cmd_if->cmdline_buf + p_user_cmd_if->cmd_cur);
591 p_user_cmd_if->accum_cmd_len++;
592 p_user_cmd_if->cmd_cur++;
593 for (loop = 0; loop < p_user_cmd_if->accum_cmd_len - p_user_cmd_if->cmd_cur; loop++)
594 {
595 data_uart_print("\b");
596 }
597 }
598 break;
599 }
600 }
601 }
602
603 return true;
604 }
605
606 /**
607 * @brief Initiate command interface structure
608 * @param[in] p_user_cmd_if Store parsed commands.
609 * @param[in] project_name Initiate project name.
610 * @return void
611 *
612 * <b>Example usage</b>
613 * \code{.c}
614 void app_main_task(void *p_param)
615 {
616 char event;
617
618 os_msg_queue_create(&io_queue_handle, MAX_NUMBER_OF_IO_MESSAGE, sizeof(T_IO_MSG));
619 os_msg_queue_create(&evt_queue_handle, MAX_NUMBER_OF_EVENT_MESSAGE, sizeof(unsigned char));
620
621 gap_start_bt_stack(evt_queue_handle, io_queue_handle, MAX_NUMBER_OF_GAP_MESSAGE);
622
623 data_uart_init(evt_queue_handle, io_queue_handle);
624 user_cmd_init(&user_cmd_if, "central");
625 ......
626 }
627 * \endcode
628 */
user_cmd_init(T_USER_CMD_IF * p_user_cmd_if,char * project_name)629 void user_cmd_init(T_USER_CMD_IF *p_user_cmd_if, char *project_name)
630 {
631 memset(p_user_cmd_if, 0, sizeof(T_USER_CMD_IF));
632 p_user_cmd_if->history_head = USER_CMD_MAX_HISTORY_LINE;
633 p_user_cmd_if->history_tail = USER_CMD_MAX_HISTORY_LINE;
634 p_user_cmd_if->history_cur = USER_CMD_MAX_HISTORY_LINE;
635 data_uart_print(">> Command Parse Init (%s) <<\r\n", project_name);
636 }
637
638