[feat] add shell component and example

This commit is contained in:
zhji 2025-06-29 17:16:15 +08:00
parent 3e9132d402
commit c82f593dd9
10 changed files with 1431 additions and 0 deletions

21
component/shell/CMakeLists.txt Executable file
View File

@ -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()

927
component/shell/shell.c Executable file
View File

@ -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;
}

167
component/shell/shell.h Executable file
View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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

47
component/shell/shell_config.h Executable file
View File

@ -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

183
component/shell/shell_freertos.c Executable file
View File

@ -0,0 +1,183 @@
#include "shell.h"
#include <FreeRTOS.h>
#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);

View File

@ -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)

View File

@ -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

View File

@ -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.);

View File

@ -0,0 +1 @@
set(CONFIG_TLSF 1)

View File

@ -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);