325 lines
6.1 KiB
C
325 lines
6.1 KiB
C
/*
|
|
* 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;
|
|
}
|