最近看了一款STM32设计的模块,其通过串口实现的命令解释器十分灵活好用,因此也想借鉴移植一个命令解释器到自己的设备中,进而想到了UBOOT中的命令实现,准备跟踪U-BOOT里的命令解释器,并将其移植到STM单片机中,作为一个通用模板来使用。别的不说了,RTFSC (Read the fucking source code )!
uboot中的命令解释函数在command.c 和command.h中,他的声明
倒着跟踪U_BOOT中的命令声明与注册。
先来看一下cmd_help.c 文件中,关于help命令的注册过程,需要声明一个执行函数,do_help,一般均为do_XXX命名。其函数符合cmd_tbl_t 中的成员函数的声明。
static int do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
const int len = ll_entry_count(cmd_tbl_t, cmd);
return _do_help(start, len, cmdtp, flag, argc, argv);
}
U_BOOT_CMD(
help, CONFIG_SYS_MAXARGS, 1, do_help,
"print command description/usage",
"\n"
" - print brief description of all commands\n"
"help command ...\n"
" - print detailed usage of 'command'"
);
struct cmd_tbl_s的定义,在command.h中,定义如下:其中包括了函数名,函数最大参数个数,函数是否支持自动重复调用(再次发送ENTER键时自动执行),函数的实际执行函数,用途说明及帮助。如果定义了自动执行,还包括自动执行的函数。
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
U_BOOT_CMD的定义如下
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp) \
{ #_name, _maxargs, _rep, _cmd, _usage, \
_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
#define U_BOOT_CMD_MKENT(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, NULL)
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
ll_entry_declare(cmd_tbl_t, _name, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp);
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
U_BOOT_CMD是 U_BOOT_CMD_COMPLETE的特殊调用,只是他的自动执行函数为NULL。
U_BOOT_COMPLETE相当于一个声明函数
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
ll_entry_declare(cmd_tbl_t, _name, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp);
ll_entry_declare的定义为:
#define ll_entry_declare(_type, _name, _list) \
_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_"#_list"_2_"#_name)))
U_BOOT_CMD_MKENT_COMPLETE宏的定义为:
#define U_BOOT_CMD_MKENT(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, NULL)
在其中又使用了U_BOOT_CMD_MKENT_COMPLETE宏,这时候是不是有点想骂人XXXX,为啥搞这么多宏????再来看看U_BOOT_CMD_MKENT_COMPLETE的定义
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp) \
{ #_name, _maxargs, _rep, _cmd, _usage, \
_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
这回终于快到头了,再来看看_CMD_HELP 和_CMD_COMPLETE。
#ifdef CONFIG_AUTO_COMPLETE
# define _CMD_COMPLETE(x) x,
#else
# define _CMD_COMPLETE(x)
#endif
#ifdef CONFIG_SYS_LONGHELP
# define _CMD_HELP(x) x,
#else
# define _CMD_HELP(x)
如果定义了CONFIG_AUTO_COMPLETE 和CONFIG_SYS_LONGHELP,只是多一个,或少一个参数的问题,正好对应到cmt_tbl_t结构体的定义。这里我们按照不定义这两个宏来跟踪代码。整体展开这个UBOOT_CMD
ll_entry_declare(cmd_tbl_t, help, do_help)=
{"help", CONFIG_SYS_MAXARGS, 1, do_help,
"print command description/usage",
"\n"
" - print brief description of all commands\n"
"help command ...\n"
" - print detailed usage of 'command'"
};
cmd_tbl_t _u_boot_list_2_do_help_2_help _aligned(4) __attribute__((unused, section(".u_boot_list_2_"help))) =
{"help", CONFIG_SYS_MAXARGS, 1, do_help,
"print command description/usage",
"\n"
" - print brief description of all commands\n"
"help command ...\n"
" - print detailed usage of 'command'"
};
可是这里没有注册函数,也没有调用的地方,直接就完成了命令的注册,这里一定又有什么奇技淫巧,一定就在cmd_tbl_t 声明后面的一票XXXXXXXX身上出的问题。
这里用到了一个函数,也就是section()函数,其中_aligned(4)代表使用4字节对齐存储,__attribute__(unused,section(".u_boot_list_2_help)表使用未使用的section,另外再来一个函数来解释__attribute__