U_BOOT_CMD是一个宏定义,功能是定义一个struct cmd_tbl_s的结构体变量,并将其存放到uboot没有被占用的section中。U_BOOT_CMD宏传递的参数是该结构体变量的成员变量。通过U_BOOT_CMD定义的变量会通过脚本链接到uboot指定的一个section中,然后可以通过find_cmd遍历这个section找到这个cmd。
U_BOOT_CMD的定义(Command.h (include)):
#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)
struct cmd_tbl_s(Command.h (include))结构体的定义:
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
};
bootm(Bootm.c (cmd))为例:
U_BOOT_CMD(
bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,
"boot application image from memory", bootm_help_text
);
int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
展开U_BOOT_CMD宏:
cmd_tbl_t _u_boot_list_2_do_bootm_2_bootm __aligned(4) __attribute__((unused,section(".u_boot_list_2_"#_list"_2_"#_name)))
={ bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,"boot application image from memory", bootm_help_text, NULL}
注:
这里的“#”,作用是将_name传递的值字符串化。_CMD_HELP和_CMD_COMPLETE就是取本身。
这里的"##"表示连接作用。即##_list##表示用_list变量的值替换当前位置。
type传递变量类型,_name和_list传递组成变量名称的字符串,然后将该变量放在section中,section的名称也由_name和_list命令传递。这里的__aligned(4)是指定定义的变量4字节对齐,__attribute是选择未使用的section。
如前所述,U_BOOT_CMD的作用就是定义一个结构体变量(struct cmd_tbl_s),并将其存放再uboot的没有被占用的section中。展开就是:
struct cmd_tbl_s _u_boot_list_2_cmd_2_##_name = {
#_name, _maxargs, _rep, _cmd, _usage, _help, NULL
};
注:该变量属性是4字节对齐(__aligned(4)),存放在未被使用的section中,并将该section命名为【".u_boot_list_2_"#name】(__attribute__((unused, section(".u_boot_list_2_"#name))))。
命令的执行:
main_loop-> autoboot_command->run_command_list->cli_simple_run_command_list->cli_simple_run_command->cmd_processfind_cmd
cmd_processfind_cmd:
/*
* WARNING:
*
* We must create a temporary copy of the command since the command we get
* may be the result from getenv(), which returns a pointer directly to
* the environment data, which may change magicly when the command we run
* creates or modifies environment variables (like "bootp" does).
*/
int cli_simple_run_command(const char *cmd, int flag)
{
char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CONFIG_SYS_CBSIZE];
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
int argc, inquotes;
int repeatable = 1;
int rc = 0;
debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
if (DEBUG_PARSER) {
/* use puts - string may be loooong */
puts(cmd ? cmd : "NULL");
puts("\"\n");
}
clear_ctrlc(); /* forget any previous Control C */
if (!cmd || !*cmd)
return -1; /* empty command */
if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
puts("## Command too long!\n");
return -1;
}
strcpy(cmdbuf, cmd);
/* Process separators and check for invalid
* repeatable commands
*/
debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep == '\'') &&
(*(sep - 1) != '\\'))
inquotes = !inquotes;
//通过分号将命令拆解
if (!inquotes &&
(*sep == ';') && /* separator */
(sep != str) && /* past string start */
(*(sep - 1) != '\\')) /* and NOT escaped */
break;
}
/*
* Limit the token to data between separators
*/
token = str;
if (*sep) {
str = sep + 1; /* start of command for next pass */
*sep = '\0';
} else {
str = sep; /* no more commands for next pass */
}
debug_parser("token: \"%s\"\n", token);
/* find macros in this token and replace them */
//处理宏
cli_simple_process_macros(token, finaltoken);
/* Extract arguments */
//提取参数argc[]
argc = cli_simple_parse_line(finaltoken, argv);
if (argc == 0) {
rc = -1; /* no command at all */
continue;
}
//处理命令
if (cmd_process(flag, argc, argv, &repeatable, NULL))
rc = -1;
/* Did the user stop this? */
if (had_ctrlc())
return -1; /* if stopped then not repeatable */
}
return rc ? rc : repeatable;
}
cmd_process:
enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
int *repeatable, ulong *ticks)
{
enum command_ret_t rc = CMD_RET_SUCCESS;
cmd_tbl_t *cmdtp;
/* Look up command in command table */
cmdtp = find_cmd(argv[0]);
if (cmdtp == NULL) {
printf("Unknown command '%s' - try 'help'\n", argv[0]);
return 1;
}
/* found - check max args */
if (argc > cmdtp->maxargs)
rc = CMD_RET_USAGE;
#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
else if (cmdtp->cmd == do_bootd) {
if (flag & CMD_FLAG_BOOTD) {
puts("'bootd' recursion detected\n");
rc = CMD_RET_FAILURE;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif
/* If OK so far, then do the command */
if (!rc) {
if (ticks)
*ticks = get_timer(0);
rc = cmd_call(cmdtp, flag, argc, argv);
if (ticks)
*ticks = get_timer(*ticks);
*repeatable &= cmdtp->repeatable;
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
return rc;
}
find_cmd:
cmd_tbl_t *find_cmd(const char *cmd)
{
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
const int len = ll_entry_count(cmd_tbl_t, cmd);
return find_cmd_tbl(cmd, start, len);
}
/* find command table entry for a command */
cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = table; /* Init value */
const char *p;
int len;
int n_found = 0;
if (!cmd)
return NULL;
/*
* Some commands allow length modifiers (like "cp.b");
* compare command name only until first dot.
*/
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
for (cmdtp = table; cmdtp != table + table_len; cmdtp++) {
if (strncmp(cmd, cmdtp->name, len) == 0) {
if (len == strlen(cmdtp->name))
return cmdtp; /* full match */
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++;
}
}
if (n_found == 1) { /* exactly one match */
return cmdtp_temp;
}
return NULL; /* not found or ambiguous command */
}
(1) 在uboot\cmd目录下新增hello.c文件
在hello.c中定义:
U_BOOT_CMD(
hello, 1, 1, do_bootd,
"hello world", " hello world "
);do_hello
int do_ hello (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
printf( "hello world");
}
(2) 修改uboot\cmd\Makefile加入:
obj-y += hello.o
(3)重新编译生成uboot
(4)print;hello