三、两种循环方式命令的执行方式
两种循环方式执行命令的模式相同,都通过cmd_process函数进行,在/u-boot-sunxi-sunxi/common/command.c里面:
在cmd_process中调用关系如下
1、cmd_process -> find_cmd-> find_cmd_tbl,通过用户输入找到需要执行的命令代码
2、cmd_process -> cmd_call,执行指令代码
这些函数都在command.c文件内,下面分析一下这几个函数,就可以看见执行一个命令的方式了:
enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
int *repeatable)
{
enum command_ret_t rc = CMD_RET_SUCCESS;
cmd_tbl_t *cmdtp;
//参数argc,argv就是超级终端、minicom等软件通过串口输入的一行经过处理的命令
//例如:用户输入 nand write 50000000 10000 20000
//将内存0x500000000的内容写入nandflash,从nand地址0x10000开始,长度0x20000,下面都用这个例子来说明
//则经过前面的解析之后,结果如下:跟系统编程相同
//argc = 5
//argv[0] = "nand"
//argv[1] = "write"
//argv[2] = "50000000"
//argv[3] = "10000"
//argv[4] = "20000"
/* Look up command in command table */
cmdtp = find_cmd(argv[0]); //通过命令找到要执行的代码,例子中的“nand”
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) {
rc = cmd_call(cmdtp, flag, argc, argv); //运行命令,再次将用户的输入传递给命令代码
*repeatable &= cmdtp->repeatable;
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
return rc;
}
//下面这两个函数负责查找要执行的命令
cmd_tbl_t *find_cmd (const char *cmd)
{
int len = &__u_boot_cmd_end - &__u_boot_cmd_start; //命令段的长度
return find_cmd_tbl(cmd, &__u_boot_cmd_start, len); //从__u_boot_cmd_start开始往后搜索
}
到了这里,就需要了解cmd_tbl_t的定义和存储方式了
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;
extern cmd_tbl_t __u_boot_cmd_start;
extern cmd_tbl_t __u_boot_cmd_end;
还有三个重要的宏:
#define Struct_Section __attribute__((unused, section(".u_boot_cmd"), \
aligned(4)))
#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
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)
举个命令定义的例子,就用nand命令:
U_BOOT_CMD(
nand, CONFIG_SYS_MAXARGS, 1, do_nand,
"NAND sub-system",
"info - show available NAND devices\n"
"nand device [dev] - show or set current device\n"
"nand read - addr off|partition size\n"
"nand write - addr off|partition size\n"
" read/write 'size' bytes starting at offset 'off'\n"
" to/from memory address 'addr', skipping bad blocks.\n"
"nand read.raw - addr off|partition [count]\n"
"nand write.raw - addr off|partition [count]\n"
" Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
);
name -> nand,当用户输入“nand”时,就会通过这个命令,找到要执行的函数“do_nand”
maxargs -> CONFIG_SYS_MAXARGS,参数的最大个数,默认是256
rep -> 1,执行次数,一般是1次
cmd -> do_nand,执行代码,即do_nand是一个函数
usage -> "NAND sub-system",用途
help -> "......",后面一大堆就是帮助信息,提示用户操作,输入“nand help”可以看到
这些字段将会按数据结构struct cmd_tbl_s的方式存储,存储在代码段内,从下面两个宏,可以看出
#define Struct_Section __attribute__((unused, section(".u_boot_cmd"), \
aligned(4)))
#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)
可以nand命令将以标号 __u_boot_cmd_nand进行编译,编译后,__u_boot_cmd_nand最后将以一个地址存在.u_boot_cmd段内,
只要找到这个地址,通过结构体 struct cmd_tbl_s,就能把所有的信息读出来
.u_boot_cmd又是如何定义的呢??这就要看u-boot.lds这个文件了,打开\u-boot-sunxi-sunxi\arch\arm\cpu\u-boot.lds
..........
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
..........
看一下,原来__u_boot_cmd_start和 __u_boot_cmd_end直接定义在这里,
所以用extern来引用,链接器在u-boot.lds中会找到这两个指针
而且,所有通过U_BOOT_CMD定义的命令都存在.u_boot_cmd段内,就在__u_boot_cmd_start和__u_boot_cmd_end之间,
那么需要搜索一个命令理所当然就应在这个范围内寻找啦,再看下面的代码,就可以理解为什么这么做了。
看看搜索的过程:调用方式 find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
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;
//参数cmd就是argv[0],即例子的“nand”
//参数table就是命令列表的首地址,即&__u_boot_cmd_start
//参数table_len就是命令列表的长度,搜索的范围即&__u_boot_cmd_end - &__u_boot_cmd_start
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); //命令的长度,例如“nand”长度为4
for ( cmdtp = table; //从&__u_boot_cmd_start开始
cmdtp != table + table_len; //到&__u_boot_cmd_end结束
cmdtp++) { //按结构体struct cmd_tbl_t移动指针
if ( strncmp (cmd, cmdtp->name, len) == 0) { //只需要比较命令名字是否相同
if (len == strlen (cmdtp->name))
return cmdtp; /* full match */ //完全匹配的情况,找到命令,直接返回
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++; //部分匹配的情况,假设有一个命令“nand_check”并在在“nand”之前,就是这种情况,继续寻找
}
}
if (n_found == 1) { /* exactly one match */ //找到一个部分匹配的命令,也可以执行,因此,输入“nand”、“nan”、“na”,都可能执行nand命令
return cmdtp_temp;
}
return NULL; /* not found or ambiguous command */ //没找到或者找到多个部分匹配的命令,则不执行
}
//执行一个命令,直接调用这个命令内的函数指针指向的函数
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int result;
result = (cmdtp->cmd)(cmdtp, flag, argc, argv); //对于“nand”命令,就是do_nand(cmdtp, flag, argc, argv);
if (result)
debug("Command failed, result=%d", result);
return result;
}
说了一大堆,基本将主循环输入和执行一条命令的方式描述了一遍,很难说得面面俱到
|