平台 | U-Boot 版本 | Linux SDK 版本 |
---|---|---|
RK356x | 2017.09 | v1.2.3 |
U-Boot 中自带一个 demo 命令,这个命令定义在
cmd/demo.c
文件,透过它,可以很容易理解 U-Boot 命令是如何定义以及如何使用!
一、demo命令的定义
打开cmd/demo.c
文件,直接拖到最底下就能看到demo
命令的定义:
可以看到demo
命令直接引用U_BOOT_CMD
宏函数定义,这个宏函数定义在include/command.h
头文件:
从宏函数定义的参数,可以知道 demo
命令的定义参数意义如下:
形参 | 实参 | 意义 |
---|---|---|
_name | demo | 名字 |
_maxargs | 4 | 最大参数个数 |
_rep | 1 | 允许自动重复?(输入该命令并执行后,再次回车可重复执行,参数为0或1) |
_cmd | do_demo | 回调函数 |
_usage | “Driver model (dm) demo operations” | 描述 |
_help | “list List available demo devices\n” “demo hello <num> [<char>] Say hello\n” “demo light [<num>] Set or get the lights\n” “demo status <num> Get demo device status\n” “demo list List available demo devices” | 使用说明 |
U_BOOT_CMD
继续调用U_BOOT_CMD_COMPLETE
宏函数,来看看U_BOOT_CMD_COMPLETE
的定义:
这里的COMPLETE
应该翻译成填充,而不是完成!
U_BOOT_CMD_COMPLETE
相比U_BOOT_CMD
宏函数,只是传多一个_comp
参数,这个参数的实参是NULL
,这里可以看出U_BOOT_CMD
其实是U_BOOT_CMD_COMPLETE
的简化版。
U_BOOT_CMD_COMPLETE
使用两个宏去定义变量并初始化:
这个ll_entry_declare
宏函数定义在include/linker_lists.h
头文件,这个函数的作用是Declare linker-generated array entry
,翻译过来就是声明链接器生成的数组条目!
U_BOOT_CMD_MKENT_COMPLETE
宏函数的定义如下:
_CMD_HELP
与_CMD_COMPLETE
宏函数的定义如下:
由于宏是预处理阶段会进行展开替换,因此demo
的实际展开过程如下:
U_BOOT_CMD(
demo, 4, 1, do_demo,
"Driver model (dm) demo operations",
"list List available demo devices\n"
"demo hello <num> [<char>] Say hello\n"
"demo light [<num>] Set or get the lights\n"
"demo status <num> Get demo device status\n"
"demo list List available demo devices"
);
U_BOOT_CMD
展开后:
U_BOOT_CMD_COMPLETE(
demo, 4, 1, do_demo,
"Driver model (dm) demo operations",
"list List available demo devices\n"
"demo hello <num> [<char>] Say hello\n"
"demo light [<num>] Set or get the lights\n"
"demo status <num> Get demo device status\n"
"demo list List available demo devices",
NULL
);
U_BOOT_CMD_COMPLETE
展开后:
ll_entry_declare(cmd_tbl_t, demo, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(demo, 4, 1, do_demo, \
"Driver model (dm) demo operations", \
"demo hello <num> [<char>] Say hello\n" \
"demo light [<num>] Set or get the lights\n" \
"demo status <num> Get demo device status\n" \
"demo list List available demo devices", \
NULL);
ll_entry_declare
、U_BOOT_CMD_MKENT_COMPLETE
、_CMD_HELP
与_CMD_COMPLETE
展开后:
cmd_tbl_t _u_boot_list_2_cmd_2_demo __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_cmd_2_demo))) = \
{ \
"demo", 4, 1, do_demo, "Driver model (dm) demo operations", \
"Driver model (dm) demo operations", \
"demo hello <num> [<char>] Say hello\n" \
"demo light [<num>] Set or get the lights\n" \
"demo status <num> Get demo device status\n" \
"demo list List available demo devices", \
NULL, \
};
二、cmd_tbl_t的数据结构
cmd_tbl_t
的数据结构定义在include/command.h
头文件:
cmd_tbl_t
其实是struct cmd_tbl_s
,数据结构的意义与前面分析的一致!complete
函数指针传进入的是NULL
,这个不用理会!
三、do_demo函数的定义
do_demo
函数的定义如下:
除了函数名可以自定义,其他都是固定写法,参考cmd_tbl_t
的cmd
函数指针定义,函数名一般是do_<cmd>
这样的形式命名!
do_demo
的函数体如下:
首先是判断argc
是否小于2
,这里其实是判断命令参数的个数,要求至少2
个参数以上(包括2
个),若失败则直接返回CMD_RET_USAGE
宏,该宏调用cmd_usage()
函数,它在功能上会打印出前面定义的帮助信息:
当然,这样类似的宏定义共有三个(其余两个是成功与失败):
接着往下走,调用find_cmd_tbl
函数:
这个函数的声明如下:
从参数不难理解,应该是通过命令行字符串获取对应的cmd
的处理函数,来看看demo_commands
是如何定义的:
看来是通过argv[1]
参数找到对应的回调函数进行返回,例如argv[1]
的字符串是list
,那么就返回do_demo_list
!
来看看U_BOOT_CMD_MKENT
宏函数的定义:
再看看U_BOOT_CMD_MKENT_COMPLETE
宏函数的定义:
这个宏函数的作用与之前U_BOOT_CMD_COMPLETE
有点不一样(缺少ll_entry_declare
对变量的定义):
因此在此处是单纯的全局变量定义(定义一个cmd_tbl_t
类型的数组),并把作用域限制在当前文件:
static cmd_tbl_t demo_commands[] = {
{ "list", 0, 1, do_demo_list, "", "", NULL },
{ "hello", 2, 1, do_demo_hello, "", "", NULL },
{ "light", 2, 1, do_demo_light, "", "", NULL },
{ "status", 1, 1, do_demo_status, "", "", NULL },
};
接下来是判断错误逻辑:
传递的参数不对之时就会返回命令使用说明。
然后通过argc
的个数为0
,则认为传入的命令为demo list
,否则认为是其余命令:
其中simple_strtoul(argv[0], NULL, 10)
的作用是把字符串转化为10
进制数;uclass_get_device(UCLASS_DEMO, devnum, &demo_dev)
通过UCLASS_DEMO
宏以及设备号获取demo
设备,这属于U-Boot DM(Driver Model);cmd_process_error
的声明如下:
这个cmd_process_error
用于报告命令执行情况,返回数是以下三者之一:
最后是执行命令的回调函数:
以下是各个回调函数的定义:
demo list
的回调函数:
demo hello <num> [<char>]
的回调函数:
demo status <num>
的回调函数:
demo light [<num>]
的回调函数:
从这些回调函数中不难发现,函数最终是要调用到底层设备驱动中去,底层部分提供API
给命令进行调用,体现了分层的概念!