rtthread学习笔记系列(3) -- FINSH模块
文章目录
- 3. FINSH模块
- 3.1MSH
- 3.1.1初始化
- 3.1.1.1FSymtab段
- 3.1.1.2 宏
- 3.1.2遍历FINSH命令
- 3.1.3TAB补全实现
- 3.1.3.1 `msh_auto_complete`
- 3.1.3.2 `msh_opt_auto_complete`
- 3.1.4 TAB 子选项自动补全
- 3.2 SHELL
- 3.2.1 `finsh_system_init`分配finsh结构体使用内存
- 3.2.2 `finsh_thread_entry`
- 3.2.3 历史命令显示
- 3.2.4 MSH命令执行
- 3.3 cmd&msh_parse&&msh_file
3. FINSH模块
https://github.com/wdfk-prog/RT-Thread-Study
3.1MSH
3.1.1初始化
- 根据链接脚本指明FINSH使用内存空间
_syscall_table_begin
_syscall_table_end
的地址
3.1.1.1FSymtab段
. = ALIGN(4);
__fsymtab_start = .;
KEEP(*(FSymTab))
__fsymtab_end = .;
__fsymtab_start
和__fsymtab_end
用于指明finsh使用内存- FSymTab用于存放所有注册命令的结构体
struct finsh_syscall
,包括命令名称,命令选项,命令描述,命令函数执行地址信息
3.1.1.2 宏
/**
* @ingroup msh
*
* This macro exports a command to module shell.
*
* @param command is the name of the command.
* @param desc is the description of the command, which will show in help list.
* @param opt This is an option, enter any content to enable option completion
*/
/* MSH_CMD_EXPORT(command, desc) or MSH_CMD_EXPORT(command, desc, opt) */
#define MSH_CMD_EXPORT(...) \
__MSH_GET_MACRO(__VA_ARGS__, _MSH_FUNCTION_CMD2_OPT, \
_MSH_FUNCTION_CMD2)(__VA_ARGS__)
#define __MSH_GET_MACRO(_1, _2, _3, _FUN, ...) _FUN
#define _MSH_FUNCTION_CMD2(a0, a1) \
MSH_FUNCTION_EXPORT_CMD(a0, a0, a1, 0)
#define _MSH_FUNCTION_CMD2_OPT(a0, a1, a2) \
MSH_FUNCTION_EXPORT_CMD(a0, a0, a1, a0##_msh_options)
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
const char __fsym_##cmd##_name[] rt_section(".rodata.name") = #cmd; \
const char __fsym_##cmd##_desc[] rt_section(".rodata.name") = #desc; \
rt_used const struct finsh_syscall __fsym_##cmd rt_section("FSymTab")= \
{ \
__fsym_##cmd##_name, \
__fsym_##cmd##_desc, \
(syscall_func)&name \
};
- 宏过载语法,选择2个参数使用
MSH_FUNCTION_CMD2
,_MSH_FUNCTION_CMD2
的desc为0;3个参数使用MSH_FUNCTION_CMD2_OPT
- 定义字符串成员,地址定义为.rodata.name;内容为#cmd;
const char __fsym_##cmd##_name[] rt_section(".rodata.name") = #cmd; \
const char __fsym_##cmd##_desc[] rt_section(".rodata.name") = #desc; \
__fsym_##cmd
存储名称,细节,挂钩的函数指针地址
3.1.2遍历FINSH命令
for (index = _syscall_table_begin;
index < _syscall_table_end;
FINSH_NEXT_SYSCALL(index))
{
}
3.1.3TAB补全实现
- 判断输入为
/t
- 将光标移动到行首;使用
/b
退格,一个个退回删除之前的显示字符 - 将命令首地址传入
shell_auto_complete
- 计算偏移地址
shell_auto_complete
3.1.3.1 msh_auto_complete
- 首地址为
/0
,即无输入任何字符串,直接TAB/t
,则调用msh_cmd
输出所有支持的命令 - 匹配命令名字,并输出命令
for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index))
{
/* skip finsh shell function */
cmd_name = (const char *) index->name;
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;
rt_kprintf("%s\n", cmd_name);
}
}
- 输出提示字符
msh />
与命令输入的字符串
rt_kprintf("%s%s", FINSH_PROMPT, prefix);
- finsh_get_prompt输出提示
#define FINSH_PROMPT finsh_get_prompt()
finsh_prompt_custom 用于自定义替换默认提示
finsh_set_prompt("artpi@root");
3.1.3.2 msh_opt_auto_complete
argc = msh_get_argc(prefix, &opt_str);
if (argc)
{
opt = msh_get_cmd_opt(prefix);
}
else if (!msh_get_cmd(prefix, strlen(prefix)) && (' ' == prefix[strlen(prefix) - 1]))
{
opt = msh_get_cmd_opt(prefix);
}
msh_get_argc
获取命令空格之后参数- 没获取到参数时,判断
prefix
不是一个已知的命令,并且命令行字符串的最后一个字符是空格 msh_get_cmd_opt
获取syscall_table
中是否有匹配的命令argc
为0,输出msh_opt_help
所有的命令?msh_opt_complete
与msh_auto_complete
作用相同
3.1.4 TAB 子选项自动补全
- msh_opt_auto_complete
- 使用完成命令注册
#define CMD_OPTIONS_STATEMENT(command) static struct msh_cmd_opt command##_msh_options[];
#define CMD_OPTIONS_NODE_START(command) static struct msh_cmd_opt command##_msh_options[] = {
#define CMD_OPTIONS_NODE(_id, _name, _des) {.id = _id, .name = #_name, .des = #_des},
#define CMD_OPTIONS_NODE_END {0},};
CMD_OPTIONS_NODE_START(vref_temp_get)
CMD_OPTIONS_NODE(1, write, tx data)
CMD_OPTIONS_NODE(2, irq, irq list)
CMD_OPTIONS_NODE(3, speed-test, eth physical speed test)
CMD_OPTIONS_NODE_END
3.2 SHELL
3.2.1 finsh_system_init
分配finsh结构体使用内存
- 创建
finsh_thread_entry
线程 - 创建信号量,用于阻塞接字符串;信号量由控制台设备对象解除
- 设置提示模式
- INIT_APP_EXPORT中调用
3.2.2 finsh_thread_entry
-
获取控制台设备对象
-
获取控制台密码用于密码确认
finsh_wait_auth
- 阻塞等待获取密码;密码输入显示
*
- 敲入回车认为密码输入完成
- 进入密码验证判断;失败线程阻塞等待2S再次获取输入字符
- 阻塞等待获取密码;密码输入显示
-
进入控制台线程
finsh_getchar
rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
等待信号量释放finsh_rx_ind
函数中释放信号量
- handle control key判断
/* * handle control key * up key : 0x1b 0x5b 0x41 * down key: 0x1b 0x5b 0x42 * right key:0x1b 0x5b 0x43 * left key: 0x1b 0x5b 0x44 */
- 上下键:历史命令显示
- 左右键:移动当前光标
- tab键 补全命令
- 删除键:删除
- 回车键:执行msh命令
-
设置控制台设备模式
/**
* @ingroup finsh
*
* This function sets the input device of finsh shell.
*
* @param device_name the name of new input device.
*/
void finsh_set_device(const char *device_name)
{
rt_device_t dev = RT_NULL;
RT_ASSERT(shell != RT_NULL);
dev = rt_device_find(device_name);
if (dev == RT_NULL)
{
rt_kprintf("finsh: can not find device: %s\n", device_name);
return;
}
/* check whether it's a same device */
if (dev == shell->device) return;
/* open this device and set the new device in finsh shell */
if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \
RT_DEVICE_FLAG_STREAM) == RT_EOK)
{
if (shell->device != RT_NULL)
{
/* close old finsh device */
rt_device_close(shell->device);
rt_device_set_rx_indicate(shell->device, RT_NULL);
}
/* clear line buffer before switch to new device */
rt_memset(shell->line, 0, sizeof(shell->line));
shell->line_curpos = shell->line_position = 0;
shell->device = dev;
rt_device_set_rx_indicate(dev, finsh_rx_ind);
}
}
3.2.3 历史命令显示
- 回车存入历史命令
shell_push_history
- 上下键显示历史命令
shell_handle_history
3.2.4 MSH命令执行
3.3 cmd&msh_parse&&msh_file
- 一些系统命令的输出
- 一些共用的解析
- 文件系统的命令支持