uboot中命令行模式以及命令处理

AM335X中的宏定义

这里写图片描述
这里写图片描述
1、需要打开哪些宏
CONFIG_CMDLINE
表示是否支持命令行模式,定义如下:
configs/am335x_evm_defconfig :CONFIG_CMDLINE=y
CONFIG_SYS_GENERIC_BOARD
用于定义板子为通用类型的板子。打开这个宏之后,common/board_f.c和common/board_r.c才会被编译进去,否则,需要自己实现。
configs/am335x_evm_defconfig :CONFIG_SYS_GENERIC_BOARD=y
CONFIG_SYS_PROMPT
命令行模式下的提示符
configs/am335x_evm_defconfig :CONFIG_SYS_PROMPT=”=> ”
CONFIG_SYS_HUSH_PARSER
表示使用使用hush来对命令行进行解析
configs/am335x_evm_defconfig :CONFIG_SYS_HUSH_PARSER=y
对应命令需要打开对应命令的宏
以bootm命令为例,如果要支持bootm,则需要打开CONFIG_CMD_BOOTM宏,具体可以参考cmd/Makefile
这里写图片描述

API
● U_BOOT_CMD
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)
定义一个命令。
● cmd_process
enum command_ret_t cmd_process(int flag, int argc, char * const argv[], int *repeatable, ulong *ticks)
命令的处理函数,命令是作为argv[0]传入。

这个是命令的描述结构体 每个这样的结构体代表一个命令
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
};

参数说明如下:
● name:定义一个命令的名字。 其实就是执行的命令的字符串。这个要注意。
● maxargs:这个命令支持的最大参数
● repeatable:是否需要重复
● cmd:命令处理函数的地址
● usage:字符串,使用说明

● help:字符串,帮助

在uboot 2016.5版本里的bootm命令 是这样表示的
U_BOOT_CMD(
bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,
“boot application image from memory”, bootm_help_text
);
U_BOOT_CMD是一个宏指令 替换就是

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_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 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)))

ll_entry_declare(cmd_tbl_t, _name, cmd)

_type = cmd_tbl_t
_name = bootm
_cmd = do_bootm

cmd_tbl_t _u_boot_list_2_cmd_2_bootm=
{
_name=bootm,
_maxargs=CONFIG_SYS_MAXARGS,
_rep=1,
_cmd=do_bootm,
_usage=”boot application image from memory”,
_help=bootm_help_text,
_comp=NULL,
}
并且这个数据结构给存放到了 .u_boot_list_2_cmd_2_bootm段中!!!和我们上述的完全一致。

命令的处理
1、简单流程说明:
假设传进来的命令是cmd。
* 获取命令表
* 从命令表中搜索和cmd匹配的项
* 执行对应项中的命令

cmd_tbl_t *find_cmd(const char *cmd)
{
//获得指向cmd_tbl_t 的指针
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
//获得命令表的长度
const int len = ll_entry_count(cmd_tbl_t, cmd);
//以命令表的指针和命令表的长度为参数,查找和cmd匹配的表项,也就是cmd_tbl_t结构,并返回给调用者
return find_cmd_tbl(cmd, start, len);
}

cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len)
{
#ifdef CONFIG_CMDLINE
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);

// 通过指针递增的方式,查找table中的每一个cmd_tbl_t
for (cmdtp = table; cmdtp != table + table_len; cmdtp++) {
if (strncmp(cmd, cmdtp->name, len) == 0) {
if (len == strlen(cmdtp->name))
return cmdtp; /* full match */
// 如果是命令字符串和表项中的name完全匹配,包括长度一致的,则直接返回
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++;
// 如果命令字符串和表项中的name的前面部分匹配的话(我们称为部分匹配),暂时保存下来,并且记录有几个这样的表项,主要是为了支持自动补全的功能
}
}
if (n_found == 1) { /* exactly one match */
return cmdtp_temp;
// 如果部分匹配的表项是唯一的话,则可以将这个表项返回,主要是为了支持自动补全的功能
}
#endif /* CONFIG_CMDLINE */

include/linker_lists.h
#define ll_entry_start(_type, _list) \
({ \
static char start[0] aligned(4) __attribute((unused, \
section(“.u_boot_list_2_”#_list”_1”))); \
(_type *)&start; \
})
// 因为传进来的_list是cmd,所以这里就是获取命令表的起始地址,也就是.u_boot_list_2_cmd_1的地址,

#define ll_entry_end(_type, _list) \
({ \
static char end[0] aligned(4) __attribute((unused, \
section(“.u_boot_list_2_”#_list”_3”))); \
(_type *)&end; \
})
// 因为传进来的_list是cmd,所以这里就是获取命令表的结束地址,也就是.u_boot_list_2_cmd_3的地址,

#define ll_entry_count(_type, _list) \
({ \
_type *start = ll_entry_start(_type, _list); \
_type *end = ll_entry_end(_type, _list); \
unsigned int _ll_result = end - start; \
_ll_result; \
})
// 因为传进来的_list是cmd,所以这里就是计算命令表的长度

执行对应表项中的命令——cmd_call
通过调用cmd_call可以执行命令表项cmd_tbl_t 中的命令
common/command.c
static int cmd_call(cmd_tbl_t cmdtp, int flag, int argc, char const argv[])
{
int result;

result = (cmdtp->cmd)(cmdtp, flag, argc, argv);

// 直接执行命令表项cmd_tbl_t 中的cmd命令处理函数
if (result)
debug(“Command failed, result=%d\n”, result);
// 命令返回非0值时,报错
return result;
}
命令处理函数——cmd_process
代码如下:
common/command.c
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]);
    // 第一个参数argv[0]表示命令,调用find_cmd获取命令对应的表项cmd_tbl_t。
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 OK so far, then do the command */
if (!rc) {
    if (ticks)
        *ticks = get_timer(0);
    rc = cmd_call(cmdtp, flag, argc, argv);
            // 调用cmd_call执行命令表项中的命令,成功的话需要返回0值
    if (ticks)
        *ticks = get_timer(*ticks);
            // 判断命令执行的时间
    *repeatable &= cmdtp->repeatable;
            // 这个命令执行的重复次数存放在repeatable中的
}
if (rc == CMD_RET_USAGE)
    rc = cmd_usage(cmdtp);
    // 命令格式有问题,打印帮助信息
return rc;

}
返回0表示执行成功,返回非0值表示执行失败。
后续需要执行一个命令的时候,直接调用cmd_process即可

命令行模式的流程
命令行模式有两种简单的方式。正常模式是简单地获取串口数据、解析和处理命令。
hush模式则是指命令的接收和解析使用busybox的hush工具,对应代码是hush.c。
关于hush模式的作用和使用自己还不是很清楚,还要再研究一下。这里简单的写一点流程。
1、入口
通过《[uboot] (第五章)uboot流程——uboot启动流程》,我们知道了uboot在执行完所有初始化程序之后,调用run_main_loop进入主循环。
通过主循环进入了命令行模式。
common/board_r.c
static int run_main_loop(void)
{
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
// 这里进入了主循环,而autoboot也是在主循环里面实现
return 0;
}
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;

bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifndef CONFIG_SYS_GENERIC_BOARD
puts(“Warning: Your board does not use generic board. Please read\n”);
puts(“doc/README.generic-board and take action. Boards not\n”);
puts(“upgraded by the late 2014 may break or be removed.\n”);
#endif

#ifdef CONFIG_VERSION_VARIABLE
setenv(“ver”, version_string); /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */

cli_init();

run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */

s = bootdelay_process();
if (cli_process_fdt(&s))
    cli_secure_boot_cmd(s);

autoboot_command(s);

cli_loop();
panic("No CLI available");

}

通过调用cli_loop进入了命令行模式,并且不允许返回。
common/cli.c
void cli_loop(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
// 这里进入hush命令模式
/* This point is never reached */
for (;;);
#elif defined(CONFIG_CMDLINE)
// 这里进入通用命令行模式
cli_simple_loop();
#else
// 说明没有打开CONFIG_CMDLINE宏,无法进入命令行模式,直接打印一个提示
printf(“## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n”);
#endif /CONFIG_SYS_HUSH_PARSER/
}

2、通用命令行模式
代码如下:
void cli_simple_loop(void)
{
static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };

int len;
int flag;
int rc = 1;

for (;;) {

    len = cli_readline(CONFIG_SYS_PROMPT);

// 打印CONFIG_SYS_PROMPT,然后从串口读取一行作为命令,存储在console_buffer中
// 在tiny210中定义如下:./configs/tiny210_defconfig:203:CONFIG_SYS_PROMPT=”TINY210 => “

    flag = 0;   /* assume no special flags for now */
    if (len > 0)
        strlcpy(lastcommand, console_buffer,
            CONFIG_SYS_CBSIZE + 1);

// 如果获得了一个新行时,命令会存储在console_buffer,将命令复制到lastcommand中
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
// 只是得到一个单纯的换行符时,设置重复标识,后续重复执行上一次命令

    if (len == -1)
        puts("<INTERRUPT>\n");

// 获得非数据值时,直接打印中断
else
rc = run_command_repeatable(lastcommand, flag);
// 否则,执行lastcommand中的命令

    if (rc <= 0) {
        /* invalid command or not repeatable, forget it */
        lastcommand[0] = 0;

// 命令执行出错时,清空lastcommand,防止下一次重复执行这个命令
}
}
}

/* run_command_repeatable实现如下 */
int run_command_repeatable(const char *cmd, int flag)
{
return cli_simple_run_command(cmd, flag);
}

/* cli_simple_run_command实现如下 */
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 macros in this token and replace them */
cli_simple_process_macros(token, finaltoken);

    /* Extract arguments */
    argc = cli_simple_parse_line(finaltoken, argv);

// 对命令进行加工处理,转化成argv和argc格式。
if (argc == 0) {
rc = -1; /* no command at all */
continue;
}

    if (cmd_process(flag, argc, argv, &repeatable, NULL))
        rc = -1;

// 调用cmd_process对命令进行处理
// 关于这个的实现我们在上述三、4中说明过了

    /* Did the user stop this? */
    if (had_ctrlc())
        return -1;  /* if stopped then not repeatable */
}

return rc ? rc : repeatable;

}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值