| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * @brief Console handler implementation of shell.h API |
| */ |
| |
| |
| #include <zephyr.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <console/uart_console.h> |
| #include <misc/printk.h> |
| #include <misc/util.h> |
| |
| #include <misc/shell.h> |
| |
| /* maximum number of command parameters */ |
| #define ARGC_MAX 10 |
| |
| static const struct shell_cmd *commands; |
| |
| static const char *prompt; |
| |
| #define STACKSIZE CONFIG_CONSOLE_HANDLER_SHELL_STACKSIZE |
| static char __stack stack[STACKSIZE]; |
| |
| #define MAX_CMD_QUEUED 3 |
| static struct uart_console_input buf[MAX_CMD_QUEUED]; |
| |
| static struct nano_fifo avail_queue; |
| static struct nano_fifo cmds_queue; |
| |
| static shell_cmd_function_t app_cmd_handler; |
| static shell_prompt_function_t app_prompt_handler; |
| |
| static const char *get_prompt(void) |
| { |
| if (app_prompt_handler) { |
| const char *str; |
| |
| str = app_prompt_handler(); |
| if (str) { |
| return str; |
| } |
| } |
| |
| return prompt; |
| } |
| |
| static void line_queue_init(void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_CMD_QUEUED; i++) { |
| nano_fifo_put(&avail_queue, &buf[i]); |
| } |
| } |
| |
| static size_t line2argv(char *str, char *argv[], size_t size) |
| { |
| size_t argc = 0; |
| |
| if (!strlen(str)) { |
| return 0; |
| } |
| |
| while (*str && *str == ' ') { |
| str++; |
| } |
| |
| if (!*str) { |
| return 0; |
| } |
| |
| argv[argc++] = str; |
| |
| while ((str = strchr(str, ' '))) { |
| *str++ = '\0'; |
| |
| while (*str && *str == ' ') { |
| str++; |
| } |
| |
| if (!*str) { |
| break; |
| } |
| |
| argv[argc++] = str; |
| |
| if (argc == size) { |
| printk("Too many parameters (max %u)\n", size - 1); |
| return 0; |
| } |
| } |
| |
| /* keep it POSIX style where argv[argc] is required to be NULL */ |
| argv[argc] = NULL; |
| |
| return argc; |
| } |
| |
| static int show_cmd_help(int argc, char *argv[]) |
| { |
| int i; |
| |
| if (!argv[0] || argv[0][0] == '\0') { |
| goto done; |
| } |
| |
| for (i = 0; commands[i].cmd_name; i++) { |
| if (!strcmp(argv[0], commands[i].cmd_name)) { |
| printk("%s %s\n", commands[i].cmd_name, |
| commands[i].help ? commands[i].help : ""); |
| return 0; |
| } |
| } |
| |
| done: |
| printk("Unrecognized command: %s\n", argv[0]); |
| return 0; |
| } |
| |
| static int show_help(int argc, char *argv[]) |
| { |
| int i; |
| |
| if (argc > 1) { |
| return show_cmd_help(--argc, &argv[1]); |
| } |
| |
| printk("Available commands:\n"); |
| printk("help\n"); |
| |
| for (i = 0; commands[i].cmd_name; i++) { |
| printk("%s\n", commands[i].cmd_name); |
| } |
| |
| return 0; |
| } |
| |
| static shell_cmd_function_t get_cb(const char *string) |
| { |
| int i; |
| |
| if (!string || string[0] == '\0') { |
| return NULL; |
| } |
| |
| if (!strcmp(string, "help")) { |
| return show_help; |
| } |
| |
| for (i = 0; commands[i].cmd_name; i++) { |
| if (!strcmp(string, commands[i].cmd_name)) { |
| return commands[i].cb; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void shell(int arg1, int arg2) |
| { |
| char *argv[ARGC_MAX + 1]; |
| size_t argc; |
| |
| while (1) { |
| struct uart_console_input *cmd; |
| shell_cmd_function_t cb; |
| |
| printk("%s", get_prompt()); |
| |
| cmd = nano_fiber_fifo_get(&cmds_queue, TICKS_UNLIMITED); |
| |
| argc = line2argv(cmd->line, argv, ARRAY_SIZE(argv)); |
| if (!argc) { |
| nano_fiber_fifo_put(&avail_queue, cmd); |
| continue; |
| } |
| |
| cb = get_cb(argv[0]); |
| if (!cb) { |
| if (app_cmd_handler != NULL) { |
| cb = app_cmd_handler; |
| } else { |
| printk("Unrecognized command: %s\n", argv[0]); |
| printk("Type 'help' for list of available commands\n"); |
| nano_fiber_fifo_put(&avail_queue, cmd); |
| continue; |
| } |
| } |
| |
| /* Execute callback with arguments */ |
| if (cb(argc, argv) < 0) { |
| show_cmd_help(argc, argv); |
| } |
| |
| nano_fiber_fifo_put(&avail_queue, cmd); |
| } |
| } |
| |
| static uint8_t completion(char *line, uint8_t len) |
| { |
| char cmds[MAX_LINE_LEN]; |
| int common_chars = -1; |
| size_t cmds_len = 0; |
| int i; |
| |
| for (i = 0; commands[i].cmd_name; i++) { |
| int name_len, j; |
| |
| if (strncmp(line, commands[i].cmd_name, len)) { |
| continue; |
| } |
| |
| name_len = strlen(commands[i].cmd_name); |
| if (name_len > (MAX_LINE_LEN - (cmds_len + 1))) { |
| break; |
| } |
| |
| memcpy(cmds + cmds_len, commands[i].cmd_name, name_len); |
| cmds_len += name_len; |
| cmds[cmds_len++] = '\n'; |
| |
| /* first match */ |
| if (common_chars < 0) { |
| common_chars = name_len; |
| continue; |
| } |
| |
| /* cut common part of matching names */ |
| for (j = 0; j < common_chars; j++) { |
| if (cmds[j] != commands[i].cmd_name[j]) { |
| break; |
| } |
| } |
| |
| common_chars = j; |
| } |
| |
| /* no match */ |
| if (common_chars < 0) { |
| return 0; |
| } |
| |
| /* alter line with common part of commands */ |
| memcpy(line + len, cmds + len, common_chars - len); |
| |
| if (common_chars < cmds_len - 1) { |
| /* |
| * more than one match, print matching commands, restore prompt |
| * and print common part of matched commands |
| */ |
| cmds[cmds_len] = '\0'; |
| printk("\n%s", cmds); |
| |
| /* restore prompt */ |
| printk("%s", get_prompt()); |
| |
| /* print common part after prompt */ |
| for (i = 0; i < common_chars; i++) { |
| printk("%c", line[i]); |
| } |
| } else { |
| /* only one match, complete command name */ |
| for (i = len; i < common_chars; i++) { |
| printk("%c", line[i]); |
| } |
| |
| /* for convenience add space after command */ |
| printk(" "); |
| line[common_chars] = ' '; |
| common_chars++; |
| } |
| |
| return common_chars - len; |
| } |
| |
| void shell_init(const char *str, const struct shell_cmd *cmds) |
| { |
| nano_fifo_init(&cmds_queue); |
| nano_fifo_init(&avail_queue); |
| |
| commands = cmds; |
| |
| line_queue_init(); |
| |
| prompt = str ? str : ""; |
| |
| task_fiber_start(stack, STACKSIZE, shell, 0, 0, 7, 0); |
| |
| /* Register serial console handler */ |
| uart_register_input(&avail_queue, &cmds_queue, completion); |
| } |
| |
| /** @brief Optionally register an app default cmd handler. |
| * |
| * @param handler To be called if no cmd found in cmds registered with shell_init. |
| */ |
| void shell_register_app_cmd_handler(shell_cmd_function_t handler) |
| { |
| app_cmd_handler = handler; |
| } |
| |
| void shell_register_prompt_handler(shell_prompt_function_t handler) |
| { |
| app_prompt_handler = handler; |
| } |