From c82f593dd9a270ece067a414e551ce1fd47442a9 Mon Sep 17 00:00:00 2001 From: zhji Date: Sun, 29 Jun 2025 17:16:15 +0800 Subject: [PATCH] [feat] add shell component and example --- component/shell/CMakeLists.txt | 21 + component/shell/shell.c | 927 +++++++++++++++++++++++ component/shell/shell.h | 167 ++++ component/shell/shell_config.h | 47 ++ component/shell/shell_freertos.c | 183 +++++ example/shell/shell_no_os/CMakeLists.txt | 11 + example/shell/shell_no_os/Makefile | 18 + example/shell/shell_no_os/main.c | 42 + example/shell/shell_no_os/proj.conf | 1 + flash.ld | 14 + 10 files changed, 1431 insertions(+) create mode 100755 component/shell/CMakeLists.txt create mode 100755 component/shell/shell.c create mode 100755 component/shell/shell.h create mode 100755 component/shell/shell_config.h create mode 100755 component/shell/shell_freertos.c create mode 100644 example/shell/shell_no_os/CMakeLists.txt create mode 100644 example/shell/shell_no_os/Makefile create mode 100644 example/shell/shell_no_os/main.c create mode 100644 example/shell/shell_no_os/proj.conf diff --git a/component/shell/CMakeLists.txt b/component/shell/CMakeLists.txt new file mode 100755 index 0000000..14322f0 --- /dev/null +++ b/component/shell/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB FILELIST +shell.c +) + +set(TARGET shell) +add_library(${TARGET} STATIC ${FILELIST}) + +target_include_directories(${TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +if(CONFIG_FREERTOS) +target_sources(${TARGET} PRIVATE shell_freertos.c) +endif() + +target_compile_definitions(${TARGET} PRIVATE -DCONFIG_SHELL) +if(CONFIG_SHELL_EXEC_THREAD) +target_compile_definitions(${TARGET} PRIVATE -DCONFIG_SHELL_EXEC_THREAD) +endif() + +if(CONFIG_SHELL_CMD_SIZE) +target_compile_definitions(${TARGET} PRIVATE -DSHELL_CMD_SIZE=${CONFIG_SHELL_CMD_SIZE}) +endif() diff --git a/component/shell/shell.c b/component/shell/shell.c new file mode 100755 index 0000000..0004f14 --- /dev/null +++ b/component/shell/shell.c @@ -0,0 +1,927 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-04-30 Bernard the first version for FinSH + * 2006-05-08 Bernard change finsh thread stack to 2048 + * 2006-06-03 Bernard add support for skyeye + * 2006-09-24 Bernard remove the code related with hardware + * 2010-01-18 Bernard fix down then up key bug. + * 2010-03-19 Bernard fix backspace issue and fix device read in shell. + * 2010-04-01 Bernard add prompt output when start and remove the empty history + * 2011-02-23 Bernard fix variable section end issue of finsh shell + * initialization when use GNU GCC compiler. + * 2016-11-26 armink add password authentication + * 2018-07-02 aozima add custom prompt support. + */ + +#include "shell.h" +#if defined(SHELL_USING_FS) +#include "ff.h" +#endif + +struct shell_syscall *_syscall_table_begin = NULL; +struct shell_syscall *_syscall_table_end = NULL; +struct shell_sysvar *_sysvar_table_begin = NULL; +struct shell_sysvar *_sysvar_table_end = NULL; + +struct shell _shell; +static struct shell *shell; +static char *shell_prompt_custom = NULL; + +extern void shell_abort_exec(int sig); +extern int shell_start_exec(cmd_function_t func, int argc, char *argv[]); +extern void shell_dup_line(char *cmd, uint32_t length); +static volatile shell_sig_func_ptr shell_sig_func; + +int shell_help(int argc, char **argv) +{ + SHELL_DGB("shell commands list:\r\n"); + { + struct shell_syscall *index; + + for (index = _syscall_table_begin; index < _syscall_table_end; index++) { + // if (strncmp(index->name, "__cmd_", 6) != 0) { + // continue; + // } + +#if defined(SHELL_USING_DESCRIPTION) + SHELL_DGB("%-16s - %s\r\n", &index->name[0], index->desc); +#else + SHELL_DGB("%s\r\n", &index->name[0]); +#endif + } + } + SHELL_DGB("\r\n"); + + return 0; +} +SHELL_CMD_EXPORT_ALIAS(shell_help, help, shell help.); + +static int shell_memtrace(int argc, char **argv) +{ + uint32_t addr, value; + int i, j; + + /* check args */ + if (argc < 2) { + SHELL_DGB("write memory: 0x42000000 0xabcd 10\r\n"); + SHELL_DGB("read memory: 0x42000000 10\r\n"); + return 0; + } + + /* get address */ + addr = strtoll(argv[1], NULL, 16); + + if (argc < 3) { + /* read word */ + SHELL_DGB("0x%08x\r\n", *(volatile uint32_t *)(uintptr_t)addr); + return 0; + } + + if (argc < 4) { + uint16_t count = atoi(argv[2]); + for (i = 0; i < count;) { + SHELL_DGB("0x%08x ", (addr + sizeof(uintptr_t) * i)); + for (j = 0; j < 4; j++) { + if (i < count) { + /* read word */ + SHELL_DGB("0x%08x ", *(volatile uint32_t *)(uintptr_t)(addr + sizeof(uintptr_t) * i)); + } else { + break; + } + i++; + } + SHELL_DGB("\r\n"); + } + + return 0; + } + + if (argc < 5) { + uint16_t count = atoi(argv[3]); + /* write value */ + value = strtoll(argv[2], NULL, 16); + + for (i = 0; i < count; i++) { + *(volatile uint32_t *)(uintptr_t)(addr + sizeof(uintptr_t) * i) = (uint32_t)value; + } + return 0; + } + return 0; +} +SHELL_CMD_EXPORT_ALIAS(shell_memtrace, memtrace, memory trace.); + +static char *shell_get_prompt(void) +{ + static char shell_prompt[SHELL_CONSOLEBUF_SIZE + 1] = { 0 }; + + if (shell_prompt_custom) { + if(strlcpy(shell_prompt, shell_prompt_custom, sizeof(shell_prompt)) >= sizeof(shell_prompt)) + printf("[OS]: strlcpy truncated \r\n"); + } else { + if(strlcpy(shell_prompt, SHELL_DEFAULT_NAME, sizeof(shell_prompt)) >= sizeof(shell_prompt)) + printf("[OS]: strlcpy truncated \r\n"); + } +#if defined(SHELL_USING_FS) + /* get current working directory */ + f_getcwd(&shell_prompt[strlen(shell_prompt)], + SHELL_CONSOLEBUF_SIZE - strlen(shell_prompt)); +#endif + + if(strlcat(shell_prompt, "/>", sizeof(shell_prompt)) >= sizeof(shell_prompt)) + printf("[OS]: strlcat truncated \r\n"); + + return shell_prompt; +} + +static int str_common(const char *str1, const char *str2) +{ + const char *str = str1; + + while ((*str != 0) && (*str2 != 0) && (*str == *str2)) { + str++; + str2++; + } + + return (str - str1); +} + +static void shell_handle_history(struct shell *shell) +{ + SHELL_PRINTF("\033[2K\r"); + SHELL_PROMPT("%s", shell_get_prompt()); + SHELL_PRINTF("%s", shell->line); +} + +static void shell_push_history(struct shell *shell) +{ + if (shell->line_position != 0) { + /* push history */ + if (shell->history_count >= SHELL_HISTORY_LINES) { + /* if current cmd is same as last cmd, don't push */ + if (memcmp(&shell->cmd_history[SHELL_HISTORY_LINES - 1], shell->line, + SHELL_CMD_SIZE)) { + /* move history */ + int index; + + for (index = 0; index < SHELL_HISTORY_LINES - 1; index++) { + memcpy(&shell->cmd_history[index][0], + &shell->cmd_history[index + 1][0], SHELL_CMD_SIZE); + } + + memset(&shell->cmd_history[index][0], 0, SHELL_CMD_SIZE); + memcpy(&shell->cmd_history[index][0], shell->line, + shell->line_position); + + /* it's the maximum history */ + shell->history_count = SHELL_HISTORY_LINES; + } + } else { + /* if current cmd is same as last cmd, don't push */ + if (shell->history_count == 0 || + memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, + SHELL_CMD_SIZE)) { + shell->current_history = shell->history_count; + memset(&shell->cmd_history[shell->history_count][0], 0, SHELL_CMD_SIZE); + memcpy(&shell->cmd_history[shell->history_count][0], shell->line, + shell->line_position); + + /* increase count and set current history position */ + shell->history_count++; + } + } + } + + shell->current_history = shell->history_count; +} + +#ifdef SHELL_USING_FS + +void shell_auto_complete_path(char *path) +{ + DIR dir; + FILINFO fno; + char *full_path, *ptr, *index; + char str_buff[256]; + + if (!path) + return; + + full_path = str_buff; + + // if (*path != '/') { + // f_getcwd(full_path, 256); + // if (full_path[strlen(full_path) - 1] != '/') + // strlcat(full_path, "/"); + // } else + *full_path = '\0'; + + index = NULL; + ptr = path; + for (;;) { + if (*ptr == '/') + index = ptr + 1; + if (!*ptr) + break; + + ptr++; + } + if (index == NULL) + index = path; + + if (index != NULL) { + char *dest = index; + + /* fill the parent path */ + ptr = full_path; + while (*ptr) + ptr++; + + for (index = path; index != dest;) + *ptr++ = *index++; + *(ptr - 1) = '\0'; + + if (f_opendir(&dir, full_path)) /* open directory failed! */ + { + return; + } + + /* restore the index position */ + index = dest; + } + + /* auto complete the file or directory name */ + if (*index == '\0') /* display all of files and directories */ + { + f_rewinddir(&dir); + for (;;) { + f_readdir(&dir, &fno); + if (fno.fname[0] == '\0') + break; + + SHELL_PRINTF("%s%s%s\r\n", fno.fname, (fno.fattrib & AM_DIR) ? "/" : "", (fno.fattrib & AM_HID) ? "(Hidden)" : ""); + } + } else { + uint32_t length, min_length = 0; + + f_rewinddir(&dir); + for (;;) { + f_readdir(&dir, &fno); + if (fno.fname[0] == '\0') + break; + + /* matched the prefix string */ + if (strncmp(index, fno.fname, strlen(index)) == 0) { + if (min_length == 0) { + min_length = strlen(fno.fname); + /* save dirent name */ + if(strlcpy(full_path, fno.fname, sizeof(str_buff)) >= sizeof(str_buff)) + printf("[OS]: strlcpy truncated \r\n"); + } + + length = str_common(fno.fname, full_path); + + if (length < min_length) { + min_length = length; + } + } + } + + if (min_length) { + if (min_length < strlen(full_path)) { + /* list the candidate */ + f_rewinddir(&dir); + for (;;) { + f_readdir(&dir, &fno); + if (fno.fname[0] == '\0') + break; + + if (strncmp(index, fno.fname, strlen(index)) == 0) + SHELL_PRINTF("%s%s%s\r\n", fno.fname, (fno.fattrib & AM_DIR) ? "/" : "", (fno.fattrib & AM_HID) ? "(Hidden)" : ""); + } + } + + length = index - path; + memcpy(index, full_path, min_length); + path[length + min_length] = '\0'; + } + } + + f_closedir(&dir); +} +#endif + +static void shell_auto_complete(char *prefix) +{ + int length, min_length; + const char *name_ptr, *cmd_name; + struct shell_syscall *index; + + min_length = 0; + name_ptr = NULL; + + SHELL_PRINTF("\r\n"); + + if (*prefix == '\0') { + shell_help(0, NULL); + return; + } + +#ifdef SHELL_USING_FS + /* check whether a spare in the command */ + { + char *ptr; + + ptr = prefix + strlen(prefix); + + while (ptr != prefix) { + if (*ptr == ' ') { + shell_auto_complete_path(ptr + 1); + break; + } + + ptr--; + } + } +#endif + + /* checks in internal command */ + { + for (index = _syscall_table_begin; index < _syscall_table_end; index++) { + /* skip finsh shell function */ + // if (strncmp(index->name, "__cmd_", 6) != 0) { + // continue; + // } + + cmd_name = (const char *)&index->name[0]; + + if (strncmp(prefix, cmd_name, strlen(prefix)) == 0) { + if (min_length == 0) { + /* set name_ptr */ + name_ptr = cmd_name; + /* set initial length */ + min_length = strlen(name_ptr); + } + + length = str_common(name_ptr, cmd_name); + + if (length < min_length) { + min_length = length; + } + + SHELL_CMD("%s\r\n", cmd_name); + } + } + } + + /* auto complete string */ + if (name_ptr != NULL) { + strlcpy(prefix, name_ptr, min_length + 1); + } + + SHELL_PROMPT("%s", shell_get_prompt()); + SHELL_PRINTF("%s", prefix); + return; +} + +static int shell_split(char *cmd, uint32_t length, char *argv[SHELL_ARG_NUM]) +{ + char *ptr; + uint32_t position; + uint32_t argc; + uint32_t i; + + ptr = cmd; + position = 0; + argc = 0; + + while (position < length) { + /* strip bank and tab */ + while ((*ptr == ' ' || *ptr == '\t') && position < length) { + *ptr = '\0'; + ptr++; + position++; + } + + if (argc >= SHELL_ARG_NUM) { + SHELL_E("Too many args ! We only Use:\r\n"); + + for (i = 0; i < argc; i++) { + SHELL_E("%s ", argv[i]); + } + + SHELL_E("\r\n"); + break; + } + + if (position >= length) { + break; + } + + /* handle string */ + if (*ptr == '"') { + ptr++; + position++; + argv[argc] = ptr; + argc++; + + /* skip this string */ + while (*ptr != '"' && position < length) { + if (*ptr == '\\') { + if (*(ptr + 1) == '"') { + ptr++; + position++; + } + } + + ptr++; + position++; + } + + if (position >= length) { + break; + } + + /* skip '"' */ + *ptr = '\0'; + ptr++; + position++; + } else { + argv[argc] = ptr; + argc++; + + while ((*ptr != ' ' && *ptr != '\t') && position < length) { + ptr++; + position++; + } + + if (position >= length) { + break; + } + } + } + + return argc; +} + +static cmd_function_t shell_get_cmd(char *cmd, int size) +{ + struct shell_syscall *index; + cmd_function_t cmd_func = NULL; + + for (index = _syscall_table_begin; index < _syscall_table_end; index++) { + // if (strncmp(index->name, "__cmd_", 6) != 0) { + // continue; + // } + + if (strncmp(&index->name[0], cmd, size) == 0 && + index->name[0 + size] == '\0') { + cmd_func = (cmd_function_t)index->func; + break; + } + } + + return cmd_func; +} + +static int shell_exec_cmd(char *cmd, uint32_t length, int *retp) +{ + int argc; + uint32_t cmd0_size = 0; + cmd_function_t cmd_func; + char *argv[SHELL_ARG_NUM]; + + // ASSERT(cmd); + // ASSERT(retp); + + /* find the size of first command */ + while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && + cmd0_size < length) { + cmd0_size++; + } + + if (cmd0_size == 0) { + return -1; + } + + cmd_func = shell_get_cmd(cmd, cmd0_size); + + if (cmd_func == NULL) { + return -1; + } + + /* split arguments */ + memset(argv, 0x00, sizeof(argv)); + argc = shell_split(cmd, length, argv); + + if (argc == 0) { + return -1; + } + + /* exec this command */ + shell_signal(SHELL_SIGINT, SHELL_SIG_DFL); + shell_dup_line(cmd, length); + *retp = shell_start_exec(cmd_func, argc, argv); + // *retp = cmd_func(argc, argv); + return 0; +} + +#if defined(SHELL_USING_LWIP) && defined(SHELL_USING_FS) +static int shell_exec_lwp(char *cmd, uint32_t length) +{ + int argc; + int cmd0_size = 0; + char *argv[SHELL_ARG_NUM]; + int fd = -1; + char *pg_name; + + extern int exec(char *, int, char **); + + /* find the size of first command */ + while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && + cmd0_size < length) { + cmd0_size++; + } + + if (cmd0_size == 0) { + return -1; + } + + /* split arguments */ + rt_memset(argv, 0x00, sizeof(argv)); + argc = msh_split(cmd, length, argv); + + if (argc == 0) { + return -1; + } + + pg_name = argv[0]; + /* try to open program */ + fd = open(pg_name, O_RDONLY, 0); + + if (fd < 0) { + return -1; + } + + /* found program */ + close(fd); + exec(pg_name, argc, argv); + + return 0; +} +#endif + +int shell_exec(char *cmd, uint32_t length) +{ + int cmd_ret; + + /* strim the beginning of command */ + while (*cmd == ' ' || *cmd == '\t') { + cmd++; + length--; + } + + if (length == 0) { + return 0; + } + + /* Exec sequence: + * 1. built-in command + * 2. module(if enabled) + */ + if (shell_exec_cmd(cmd, length, &cmd_ret) == 0) { + return cmd_ret; + } + +#ifdef SHELL_USING_FS + // extern int shell_exec_script(char *cmd, uint32_t length); + + // if (shell_exec_script(cmd, length) == 0) { + // return 0; + // } + +#endif + +#ifdef SHELL_USING_LWIP + + if (shell_exec_lwp(cmd, length) == 0) { + return 0; + } + +#endif + + /* truncate the cmd at the first space. */ + { + char *tcmd; + tcmd = cmd; + + while (*tcmd != ' ' && *tcmd != '\0') { + tcmd++; + } + + *tcmd = '\0'; + } + SHELL_E("%s: command not found.\r\n", cmd); + return -1; +} + +void shell_handler(uint8_t data) +{ + /* + * handle control key + * up key : 0x1b 0x5b 0x41 + * down key: 0x1b 0x5b 0x42 + * right key:0x1b 0x5b 0x43 + * left key: 0x1b 0x5b 0x44 + */ + + if (data == 0x03) { + /*!< ctrl + c */ + if (shell_sig_func) { + shell_sig_func(SHELL_SIGINT); + shell_sig_func = NULL; + } + SHELL_PRINTF("^C"); + data = '\r'; + } + + if (data == 0x1b) { + shell->stat = WAIT_SPEC_KEY; + return; + } else if (shell->stat == WAIT_SPEC_KEY) { + if (data == 0x5b) { + shell->stat = WAIT_FUNC_KEY; + return; + } + + shell->stat = WAIT_NORMAL; + } else if (shell->stat == WAIT_FUNC_KEY) { + shell->stat = WAIT_NORMAL; + + if (data == 0x41) /* up key */ + { + /* prev history */ + if (shell->current_history > 0) { + shell->current_history--; + } else { + shell->current_history = 0; + return; + } + + /* copy the history command */ + memcpy(shell->line, &shell->cmd_history[shell->current_history][0], + SHELL_CMD_SIZE); + shell->line_curpos = shell->line_position = strlen(shell->line); + shell_handle_history(shell); + + return; + } else if (data == 0x42) /* down key */ + { + /* next history */ + if (shell->current_history < shell->history_count - 1) { + shell->current_history++; + } else { + /* set to the end of history */ + if (shell->history_count != 0) { + shell->current_history = shell->history_count - 1; + } else { + return; + } + } + + memcpy(shell->line, &shell->cmd_history[shell->current_history][0], + SHELL_CMD_SIZE); + shell->line_curpos = shell->line_position = strlen(shell->line); + shell_handle_history(shell); + + return; + } else if (data == 0x44) /* left key */ + { + if (shell->line_curpos) { + SHELL_PRINTF("\b"); + shell->line_curpos--; + } + + return; + } else if (data == 0x43) /* right key */ + { + if (shell->line_curpos < shell->line_position) { + SHELL_PRINTF("%c", shell->line[shell->line_curpos]); + shell->line_curpos++; + } + return; + } + } + + /* received null or error */ + if ((data == '\0') || (data == 0xFF)) { + return; + } + /* handle tab key */ + else if (data == '\t') { + int i; + + /* move the cursor to the beginning of line */ + for (i = 0; i < shell->line_curpos; i++) { + SHELL_PRINTF("\b"); + } + + /* auto complete */ + shell_auto_complete(&shell->line[0]); + /* re-calculate position */ + shell->line_curpos = shell->line_position = strlen(shell->line); + + return; + } + /* handle backspace key */ + else if (data == 0x7f || data == 0x08) { + /* note that shell->line_curpos >= 0 */ + if (shell->line_curpos == 0) { + return; + } + + shell->line_position--; + shell->line_curpos--; + + if (shell->line_position > shell->line_curpos) { + int i; + + memmove(&shell->line[shell->line_curpos], + &shell->line[shell->line_curpos + 1], + shell->line_position - shell->line_curpos); + shell->line[shell->line_position] = 0; + + SHELL_PRINTF("\b%s \b", &shell->line[shell->line_curpos]); + + /* move the cursor to the origin position */ + for (i = shell->line_curpos; i <= shell->line_position; i++) { + SHELL_PRINTF("\b"); + } + } else { + SHELL_PRINTF("\b \b"); + shell->line[shell->line_position] = 0; + } + + return; + } + + /* handle end of line, break */ + if (data == '\r' || data == '\n') { + shell_push_history(shell); + + SHELL_PRINTF("\r\n"); + shell_exec(shell->line, shell->line_position); + + SHELL_PROMPT(shell_get_prompt()); + memset(shell->line, 0, sizeof(shell->line)); + shell->line_curpos = shell->line_position = 0; + return; + } + /* return not display character */ + if((data < 0x20) || (data >= 0x80)) + { + return; + } + /* it's a large line, discard it */ + if (shell->line_position >= SHELL_CMD_SIZE) { + shell->line_position = 0; + } + + /* normal character */ + if (shell->line_curpos < shell->line_position) { + int i; + + memmove(&shell->line[shell->line_curpos + 1], + &shell->line[shell->line_curpos], + shell->line_position - shell->line_curpos); + shell->line[shell->line_curpos] = data; + + SHELL_PRINTF("%s", &shell->line[shell->line_curpos]); + + /* move the cursor to new position */ + for (i = shell->line_curpos; i < shell->line_position; i++) { + SHELL_PRINTF("\b"); + } + } else { + shell->line[shell->line_position] = data; + SHELL_PRINTF("%c", data); + } + + data = 0; + shell->line_position++; + shell->line_curpos++; + + if (shell->line_position >= SHELL_CMD_SIZE) { + /* clear command line */ + shell->line_position = 0; + shell->line_curpos = 0; + } +} + +static void shell_function_init(const void *begin, const void *end) +{ + _syscall_table_begin = (struct shell_syscall *)begin; + _syscall_table_end = (struct shell_syscall *)end; +} + +static void shell_var_init(const void *begin, const void *end) +{ + _sysvar_table_begin = (struct shell_sysvar *)begin; + _sysvar_table_end = (struct shell_sysvar *)end; +} + +int shell_set_prompt(const char *prompt) +{ + if (shell_prompt_custom) { + SHELL_FREE(shell_prompt_custom); + shell_prompt_custom = NULL; + } + + /* strdup */ + if (prompt) { + shell_prompt_custom = (char *)SHELL_MALLOC(strlen(prompt) + 1); + if (shell_prompt_custom) { + if(strlcpy(shell_prompt_custom, prompt, strlen(prompt) + 1) >= strlen(prompt) + 1) + printf("[OS]: strlcpy truncated \r\n"); + } + } + + return 0; +} + +int shell_set_print(void (*shell_printf)(char *fmt, ...)) +{ + if (shell_printf) { + shell->shell_printf = shell_printf; + return 0; + } else + return -1; +} + +/* + * @ingroup shell + * + * This function will initialize shell + */ +void shell_init(void) +{ +#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM C Compiler */ + extern const int FSymTab$$Base; + extern const int FSymTab$$Limit; + extern const int VSymTab$$Base; + extern const int VSymTab$$Limit; + shell_function_init(&FSymTab$$Base, &FSymTab$$Limit); + shell_var_init(&VSymTab$$Base, &VSymTab$$Limit); +#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */ + shell_function_init(__section_begin("FSymTab"), __section_end("FSymTab")); + shell_var_init(__section_begin("VSymTab"), __section_end("VSymTab")); +#elif defined(__GNUC__) + /* GNU GCC Compiler and TI CCS */ + extern const int __fsymtab_start; + extern const int __fsymtab_end; + extern const int __vsymtab_start; + extern const int __vsymtab_end; + shell_function_init(&__fsymtab_start, &__fsymtab_end); + shell_var_init(&__vsymtab_start, &__vsymtab_end); +#endif + shell = &_shell; + shell_set_prompt(SHELL_DEFAULT_NAME); + shell_set_print((void (*)(char *fmt, ...))printf); + SHELL_PRINTF(shell_get_prompt()); +} + +shell_sig_func_ptr shell_signal(int sig, shell_sig_func_ptr func) +{ + shell_sig_func_ptr shell_sig_func_prev = shell_sig_func; + + if (sig == SHELL_SIGINT) { + if (func == SHELL_SIG_DFL) { + shell_sig_func = shell_abort_exec; + } else if (func == SHELL_SIG_IGN) { + shell_sig_func = NULL; + } else { + shell_sig_func = func; + } + return shell_sig_func_prev; + } + + return NULL; +} + +__attribute__((weak)) void shell_abort_exec(int sig) +{ + (void)sig; +} +__attribute__((weak)) int shell_start_exec(cmd_function_t func, int argc, char *argv[]) +{ + return func(argc, argv); +} +__attribute__((weak)) void shell_dup_line(char *cmd, uint32_t length) +{ + (void)cmd; + (void)length; +} diff --git a/component/shell/shell.h b/component/shell/shell.h new file mode 100755 index 0000000..f82b31b --- /dev/null +++ b/component/shell/shell.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-06-02 Bernard Add finsh_get_prompt function declaration + */ +#ifndef __SHELL_H__ +#define __SHELL_H__ + +#include +#include +#include +#include +#include "shell_config.h" + +#ifdef SHELL_USING_COLOR +/* + * The color for terminal (foreground) + * BLACK 30 + * RED 31 + * GREEN 32 + * YELLOW 33 + * BLUE 34 + * PURPLE 35 + * CYAN 36 + * WHITE 37 + */ +#define _SHELL_COLOR_HDR(n) shell->shell_printf("\033[" #n "m") +#define _SHELL_COLOR_END shell->shell_printf("\033[0m") + +#define shell_print(color_n, fmt, ...) \ + do { \ + _SHELL_COLOR_HDR(color_n); \ + shell->shell_printf(fmt, ##__VA_ARGS__); \ + _SHELL_COLOR_END; \ + } while (0) + +#define SHELL_PRINTF(fmt, ...) shell_print(0, fmt, ##__VA_ARGS__) +#define SHELL_PROMPT(fmt, ...) shell_print(36, fmt, ##__VA_ARGS__) +#define SHELL_DGB(fmt, ...) shell_print(32, fmt, ##__VA_ARGS__) +#define SHELL_CMD(fmt, ...) shell_print(33, fmt, ##__VA_ARGS__) +#define SHELL_E(fmt, ...) shell_print(31, fmt, ##__VA_ARGS__) + +#else +#define SHELL_PRINTF(fmt, ...) shell->shell_printf(fmt, ##__VA_ARGS__) +#define SHELL_PROMPT(fmt, ...) shell->shell_printf(fmt, ##__VA_ARGS__) +#define SHELL_DGB(fmt, ...) shell->shell_printf(fmt, ##__VA_ARGS__) +#define SHELL_CMD(fmt, ...) shell->shell_printf(fmt, ##__VA_ARGS__) +#define SHELL_E(fmt, ...) shell->shell_printf(fmt, ##__VA_ARGS__) +#endif + +#define SHELL_MALLOC malloc +#define SHELL_FREE free + +#define SHELL_SIGINT 1 +#define SHELL_SIG_DFL ((shell_sig_func_ptr)0) /* Default action */ +#define SHELL_SIG_IGN ((shell_sig_func_ptr)1) /* Ignore action */ + +typedef void (*shell_sig_func_ptr)(int); +typedef int (*syscall_func)(void); +typedef int (*cmd_function_t)(int argc, char **argv); + +enum input_stat { + WAIT_NORMAL, + WAIT_SPEC_KEY, + WAIT_FUNC_KEY, +}; + +struct shell { + enum input_stat stat; + + uint16_t current_history; + uint16_t history_count; + char cmd_history[SHELL_HISTORY_LINES][SHELL_CMD_SIZE]; + + char line[SHELL_CMD_SIZE]; + uint16_t line_position; + uint16_t line_curpos; + +#ifdef SHELL_USING_AUTH + char password[SHELL_PASSWORD_MAX]; +#endif + +#if defined(SHELL_USING_FS) + +#endif + void (*shell_printf)(char *fmt, ...); +}; + +/* system call table */ +struct shell_syscall { + const char *name; /* the name of system call */ +#if defined(SHELL_USING_DESCRIPTION) + const char *desc; /* description of system call */ +#endif + syscall_func func; /* the function address of system call */ +}; + +/* system variable table */ +struct shell_sysvar { + const char *name; /* the name of variable */ +#if defined(SHELL_USING_DESCRIPTION) + const char *desc; /* description of system variable */ +#endif + uint8_t type; /* the type of variable */ + void *var; /* the address of variable */ +}; + +#ifdef SHELL_USING_DESCRIPTION +#define SHELL_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] __attribute__((section(".rodata.name"))) = #cmd; \ + const char __fsym_##cmd##_desc[] __attribute__((section(".rodata.name"))) = #desc; \ + __attribute__((used)) const struct shell_syscall __fsym_##cmd __attribute__((section("FSymTab"))) = { \ + __fsym_##cmd##_name, \ + __fsym_##cmd##_desc, \ + (syscall_func)&name \ + }; + +#define SHELL_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] __attribute__((section(".rodata.name"))) = #name; \ + const char __vsym_##name##_desc[] __attribute__((section(".rodata.name"))) = #desc; \ + __attribute__((used)) const struct shell_sysvar __vsym_##name __attribute__((section("VSymTab"))) = { \ + __vsym_##name##_name, \ + __vsym_##name##_desc, \ + type, \ + (void *)&name \ + }; +#else +#define SHELL_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] = #cmd; \ + __attribute__((used)) const struct shell_syscall __fsym_##cmd __attribute__((section("FSymTab"))) = { \ + __fsym_##cmd##_name, \ + (syscall_func)&name \ + }; + +#define SHELL_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] = #name; \ + __attribute__((used)) const struct shell_sysvar __vsym_##name __attribute__((section("VSymTab"))) = { \ + __vsym_##name##_name, \ + type, \ + (void *)&name \ + }; +#endif /* end of SHELL_USING_DESCRIPTION */ + +/** + * @ingroup shell + * + * This macro exports a command to module shell. + * + * @param command the name of command. + * @param desc the description of command, which will show in help. + */ +#define SHELL_CMD_EXPORT(command, desc) \ + SHELL_FUNCTION_EXPORT_CMD(command, command, desc) +#define SHELL_CMD_EXPORT_ALIAS(command, alias, desc) \ + SHELL_FUNCTION_EXPORT_CMD(command, alias, desc) + +void shell_handler(uint8_t data); +int shell_set_prompt(const char *prompt); +int shell_set_print(void (*shell_printf)(char *fmt, ...)); +void shell_init(void); +void shell_exe_cmd(uint8_t *cmd, uint16_t len); +shell_sig_func_ptr shell_signal(int sig, shell_sig_func_ptr func); +#endif \ No newline at end of file diff --git a/component/shell/shell_config.h b/component/shell/shell_config.h new file mode 100755 index 0000000..71e652e --- /dev/null +++ b/component/shell/shell_config.h @@ -0,0 +1,47 @@ +#ifndef __SHELL_CONFIG_H__ +#define __SHELL_CONFIG_H__ + +#ifndef SHELL_DEFAULT_NAME +#define SHELL_DEFAULT_NAME "jzhgonha" +#endif + +#ifndef SHELL_CONSOLEBUF_SIZE +#define SHELL_CONSOLEBUF_SIZE 128 +#endif + +#ifndef SHELL_PROMPT_SIZE +#define SHELL_PROMPT_SIZE 20 +#endif + +#ifndef SHELL_HISTORY_LINES +#define SHELL_HISTORY_LINES 5 +#endif + +#ifndef SHELL_CMD_SIZE +#define SHELL_CMD_SIZE 120 +#endif + +#ifndef SHELL_ARG_NUM +#define SHELL_ARG_NUM 16 +#endif + +//#define SHELL_USING_FS +#define SHELL_USING_COLOR + +#ifndef SHELL_THREAD_STACK_SIZE +#define SHELL_THREAD_STACK_SIZE 1024 +#endif + +#ifndef SHELL_THREAD_PRIO +#define SHELL_THREAD_PRIO 5 +#endif + +#ifndef SHELL_EXEC_THREAD_STACK_SIZE +#define SHELL_EXEC_THREAD_STACK_SIZE 1024 +#endif + +#ifndef SHELL_EXEC_THREAD_PRIO +#define SHELL_EXEC_THREAD_PRIO 4 +#endif + +#endif diff --git a/component/shell/shell_freertos.c b/component/shell/shell_freertos.c new file mode 100755 index 0000000..a9f68b5 --- /dev/null +++ b/component/shell/shell_freertos.c @@ -0,0 +1,183 @@ +#include "shell.h" +#include +#include "semphr.h" +#include "ring_buffer.h" +#include "bflb_uart.h" +#include "mem.h" + +#if defined(CONFIG_SHELL_EXEC_THREAD) && CONFIG_SHELL_EXEC_THREAD +static int shell_exec_argc; +static char *shell_exec_argv[SHELL_ARG_NUM + 1]; +static char shell_exec_line[SHELL_CMD_SIZE]; +static ptrdiff_t shell_exec_line_diff; +TaskHandle_t shell_exec_handle; +static ATTR_NOCACHE_RAM_SECTION volatile bool shell_exec_end = true; + +static void shell_exec_task(void *pvParameters) +{ + shell_exec_end = false; + __ASM volatile("fence"); + ((cmd_function_t)(pvParameters))(shell_exec_argc, shell_exec_argv); + shell_exec_end = true; + __ASM volatile("fence"); + vTaskDelete(shell_exec_handle); + shell_exec_handle = NULL; +} + +void shell_dup_line(char *cmd, uint32_t length) +{ + memcpy(shell_exec_line, cmd, length + 1); + shell_exec_line_diff = shell_exec_line - cmd; +} + +void shell_abort_exec(int sig) +{ + (void)sig; + if (shell_exec_end == false) { + shell_exec_end = true; + __ASM volatile("fence"); + vTaskDelete(shell_exec_handle); + shell_exec_handle = NULL; + } +} + +int shell_start_exec(cmd_function_t func, int argc, char *argv[]) +{ + BaseType_t xReturned; + shell_abort_exec(SHELL_SIGINT); + shell_exec_argc = argc; + + for (uint8_t i = 0; i < argc; i++) { + shell_exec_argv[i] = argv[i] + shell_exec_line_diff; + } + shell_exec_argv[argc] = NULL; + + __ASM volatile("fence"); + xReturned = xTaskCreate(shell_exec_task, (char *)"shell_exec_task", SHELL_EXEC_THREAD_STACK_SIZE, func, SHELL_EXEC_THREAD_PRIO, &shell_exec_handle); + + if (xReturned == pdPASS) { + return 0; + } else { + shell_exec_end = true; + return -1; + } +} + +#endif + +static TaskHandle_t shell_handle; +SemaphoreHandle_t sem_shell = NULL; + +Ring_Buffer_Type shell_rb; + +uint8_t shell_buffer[512]; + +void shell_release_sem(void) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if (xPortIsInsideInterrupt()) { + int ret = xSemaphoreGiveFromISR(sem_shell, &xHigherPriorityTaskWoken); + if (ret == pdPASS) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + } else { + xSemaphoreGive(sem_shell); + } +} + +struct bflb_device_s *uart_shell = NULL; + +void uart_shell_isr(int irq, void *arg) +{ + uint32_t intstatus = bflb_uart_get_intstatus(uart_shell); + if (intstatus & UART_INTSTS_RX_FIFO) { + while (bflb_uart_rxavailable(uart_shell)) { + Ring_Buffer_Write_Byte(&shell_rb, bflb_uart_getchar(uart_shell)); + } + shell_release_sem(); + } + if (intstatus & UART_INTSTS_RTO) { + while (bflb_uart_rxavailable(uart_shell)) { + Ring_Buffer_Write_Byte(&shell_rb, bflb_uart_getchar(uart_shell)); + } + shell_release_sem(); + bflb_uart_int_clear(uart_shell, UART_INTCLR_RTO); + } +} + +static void shell_task(void *pvParameters) +{ + uint8_t data; + uint32_t len; + + bflb_uart_feature_control(uart_shell, UART_CMD_CLR_RX_FIFO, 0); + bflb_uart_rxint_mask(uart_shell, false); + bflb_irq_attach(uart_shell->irq_num, uart_shell_isr, NULL); + bflb_irq_enable(uart_shell->irq_num); + + while (1) { + if (xSemaphoreTake(sem_shell, portMAX_DELAY) == pdTRUE) { + len = Ring_Buffer_Get_Length(&shell_rb); + for (uint32_t i = 0; i < len; i++) { + Ring_Buffer_Read_Byte(&shell_rb, &data); + shell_handler(data); + } + } + } +} + +void shell_init_with_task(struct bflb_device_s *shell) +{ + vSemaphoreCreateBinary(sem_shell); + uart_shell = shell; + + Ring_Buffer_Init(&shell_rb, shell_buffer, sizeof(shell_buffer), NULL, NULL); + + shell_init(); + xTaskCreate(shell_task, (char *)"shell_task", SHELL_THREAD_STACK_SIZE, NULL, SHELL_THREAD_PRIO, &shell_handle); +} + +void shell_exe_cmd(uint8_t *cmd, uint16_t len) +{ + Ring_Buffer_Write(&shell_rb, cmd, len); + shell_release_sem(); +} + +static void ps_cmd(int argc, char **argv) +{ + char *pcWriteBuffer, *info; + const char *const pcHeader = "State Priority Stack # Base\r\n********************************************************\r\n"; + BaseType_t xSpacePadding; + + info = malloc(1536); + if (NULL == info) { + return; + } + pcWriteBuffer = info; + + /* Generate a table of task stats. */ + if (strlcpy(pcWriteBuffer, "Task", 1536) >= 1536) + printf("[OS]: strlcpy truncated \r\n"); + pcWriteBuffer += strlen(pcWriteBuffer); + + /* Minus three for the null terminator and half the number of characters in + "Task" so the column lines up with the centre of the heading. */ + for (xSpacePadding = strlen("Task"); xSpacePadding < (configMAX_TASK_NAME_LEN - 3); xSpacePadding++) { + /* Add a space to align columns after the task's name. */ + *pcWriteBuffer = ' '; + pcWriteBuffer++; + + /* Ensure always terminated. */ + *pcWriteBuffer = 0x00; + } + if (strlcpy(pcWriteBuffer, pcHeader, 1536 - (pcWriteBuffer - info)) >= + 1536 - (pcWriteBuffer - info)) + printf("[OS]: strlcpy truncated \r\n"); + vTaskList(pcWriteBuffer + strlen(pcHeader)); + printf("\r\n"); + printf(info); + + free(info); +} +SHELL_CMD_EXPORT_ALIAS(ps_cmd, ps, shell ps); diff --git a/example/shell/shell_no_os/CMakeLists.txt b/example/shell/shell_no_os/CMakeLists.txt new file mode 100644 index 0000000..010b21e --- /dev/null +++ b/example/shell/shell_no_os/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.10) + +include(proj.conf) + +project(${EXAMPLE_NAME} VERSION 0.1) +add_executable(${EXAMPLE_NAME}.elf main.c) + +include_directories(.) + +add_subdirectory(${SDK_BASE_DIR} sdk) +target_link_libraries(${EXAMPLE_NAME}.elf sdk) diff --git a/example/shell/shell_no_os/Makefile b/example/shell/shell_no_os/Makefile new file mode 100644 index 0000000..94b737f --- /dev/null +++ b/example/shell/shell_no_os/Makefile @@ -0,0 +1,18 @@ +EXAMPLE_BASE_DIR ?= $(shell realpath .) +EXAMPLE_NAME := $(notdir $(patsubst %/,%,$(CURDIR))) +SDK_BASE_DIR ?= $(shell realpath ./../../..) + +export SDK_BASE_DIR +export EXAMPLE_NAME +export EXAMPLE_BASE_DIR + +GCC_PATH := $(shell which arm-none-eabi-gcc) +CROSS_COMPILE := $(patsubst %gcc,%,$(GCC_PATH)) +ifeq ($(GCC_PATH),) +$(error arm-none-eabi-gcc not found in PATH. Please install the ARM toolchain.) +endif + +# add custom cmake definition +#cmake_definition+=-Dxxx=sss + +include $(SDK_BASE_DIR)/project.build diff --git a/example/shell/shell_no_os/main.c b/example/shell/shell_no_os/main.c new file mode 100644 index 0000000..80d3967 --- /dev/null +++ b/example/shell/shell_no_os/main.c @@ -0,0 +1,42 @@ +#include "resets.h" +#include "timer.h" +#include "gpio.h" +#include "uart.h" +#include "stdio.h" +#include "stdlib.h" +#include "mem.h" +#include "shell.h" + +#define LED_PIN (25) +#define LED_TIME (500 * 1000) /* 500ms */ + +int main(void) +{ + reset_unreset_blocks_wait(RESETS_BLOCK_IO_BANK0 | RESETS_BLOCK_UART0 | RESETS_BLOCK_TIMER); + gpio_init(0, GPIO_FUNC_UART | GPIO_PULL_UP | GPIO_DRIVE_4MA); /* UART_TX pin */ + gpio_init(1, GPIO_FUNC_UART | GPIO_PULL_UP | GPIO_SCHMITT | GPIO_PAD_IE | GPIO_PAD_OD); /* UART_RX pin */ + uart_init(uart0_hw, 6 * 1000 * 1000, UART_DATABITS_8 | UART_PARITY_NONE | UART_STOPBITS_1); + printf("shell example\r\n"); + + kmem_init((void *)0x20030000, 64 * 1024); + shell_init(); + while(1) { + int c = uart_get_char(uart0_hw); + if (c >= 0) { + shell_handler((char)c); + } + } + + return 0; +} + +int shell_test(int argc, char **argv) +{ + printf("shell test, argc=%d:\r\n", argc); + for (int i = 0; i < argc; i++) { + printf("argv[%d] = %s\r\n", i, argv[i]); + } + printf("\r\n"); + return 0; +} +SHELL_CMD_EXPORT_ALIAS(shell_test, test, shell test.); diff --git a/example/shell/shell_no_os/proj.conf b/example/shell/shell_no_os/proj.conf new file mode 100644 index 0000000..f823f8d --- /dev/null +++ b/example/shell/shell_no_os/proj.conf @@ -0,0 +1 @@ +set(CONFIG_TLSF 1) diff --git a/flash.ld b/flash.ld index 2abe0ba..d039cb1 100644 --- a/flash.ld +++ b/flash.ld @@ -15,6 +15,7 @@ PHDRS isr_reset PT_LOAD FLAGS(6); /* R + X */ text PT_LOAD FLAGS(5); /* R + X */ rodata PT_LOAD FLAGS(6); /* R + W */ + shell PT_LOAD FLAGS(6); /* R + W */ data PT_LOAD FLAGS(6); /* R + W */ bss PT_LOAD FLAGS(6); /* R + W */ } @@ -49,6 +50,19 @@ SECTIONS . = ALIGN(4); } >FLASH :rodata + .shell : + { + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + } >FLASH :shell + .data : { . = ALIGN(4);