| /* |
| * 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) |
| { |
| const char *first_match = NULL; |
| int common_chars = -1; |
| int i; |
| |
| for (i = 0; commands[i].cmd_name; i++) { |
| int j; |
| |
| if (strncmp(line, commands[i].cmd_name, len)) { |
| continue; |
| } |
| |
| if (!first_match) { |
| first_match = commands[i].cmd_name; |
| continue; |
| } |
| |
| /* more commands match, print first match */ |
| if (first_match && (common_chars < 0)) { |
| printk("\n%s\n", first_match); |
| common_chars = strlen(first_match); |
| } |
| |
| /* cut common part of matching names */ |
| for (j = 0; j < common_chars; j++) { |
| if (first_match[j] != commands[i].cmd_name[j]) { |
| break; |
| } |
| } |
| |
| common_chars = j; |
| |
| printk("%s\n", commands[i].cmd_name); |
| } |
| |
| /* no match, do nothing */ |
| if (!first_match) { |
| return 0; |
| } |
| |
| if (common_chars >= 0) { |
| /* multiple match, restore prompt */ |
| printk("%s", get_prompt()); |
| |
| /* add common part after prompt */ |
| for (i = 0; i < common_chars; i++) { |
| printk("%c", first_match[i]); |
| line[i] = first_match[i]; |
| } |
| } else { |
| common_chars = strlen(first_match); |
| |
| /* full command name with trailing spaces, do nothing */ |
| if (len > common_chars) { |
| return 0; |
| } |
| |
| /* complete matched command name */ |
| for (i = len; i < common_chars; i++) { |
| printk("%c", first_match[i]); |
| line[i] = first_match[i]; |
| } |
| |
| /* for convenience add space after command */ |
| printk(" "); |
| line[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; |
| } |