u-boot CMD摘记

2150阅读 0评论2013-10-21 shaohui973
分类:嵌入式

本文主要介绍u-boot的命令实现。
文中以命令tftpboot为例,来说明这个过程。
当我们在命令行中输入tftpboot命令时,u-boot会调用相应的命令处理函数do_tftpb(),如下:
U_BOOT_CMD(
    tftpboot,    3,    1,    do_tftpb,        /* do_tftpb为实际的命令处理函数*/
    "boot image via network using TFTP protocol",
    "[loadAddress] [[hostIPaddr:]bootfilename]"
);

那么系统是如何找到这个函数的呢?
当u-boot从串口上读取到用户的输入命令后,进入命令处理函数cmd_process()
cmd_process()首先通过find_cmd()查找用户命令想对应的数据结构,找到之后就调用命令处理函数cmd_call()来处理。
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    int result;
 
    result = (cmdtp->cmd)(cmdtp, flag, argc, argv); /*调用命令处理函数*/
    if (result)
        debug("Command failed, result=%d", result);
    return result;
}

由此我们看出,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);
}
 
#define ll_entry_start(_type, _section_u)                \
    ({                                \
        extern _type _u_boot_list_##_section_u##__start;    \
        _type *_ll_result = &_u_boot_list_##_section_u##__start;\
        _ll_result;                        \
    })

find_cmd()是从&_u_boot_list_cmd__start这个位置开始查找的,通过比较命令名字name.
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 */
}

到这里我们只知道命令去哪找,但是它是怎么生成的还没有答案。
然后,我们去u-boot.lds查看如下脚本段:
  PROVIDE (edata = .);
  . = .;
  . = ALIGN(4);
  .u_boot_list : {
_u_boot_list__start = .;      /* 定义符号_u_boot_list_cmd__start,并且安排在当前定位计数器指定的位置,
                                         *  也就是.u_boot_list节的0地址
                                         */
_u_boot_list_cmd__start = .;
*(SORT(.u_boot_list.cmd.*));
_u_boot_list_cmd__end = .;
_u_boot_list_env_clbk__start = .;
*(SORT(.u_boot_list.env_clbk.*));
_u_boot_list_env_clbk__end = .;
*(SORT(.u_boot_list.*));
_u_boot_list__end = .;
  }
这个脚本中,描述了一个.u_boot.list节,_u_boot_list_cmd__start就是在这定义的。

然后,我们回到命令定义处cmd_net.c
U_BOOT_CMD(
    tftpboot,    3,    1,    do_tftpb,
    "boot image via network using TFTP protocol",
    "[loadAddress] [[hostIPaddr:]bootfilename]"
);
这个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, cmd) =            \
        U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,    \
                        _usage, _help, _comp);

#define ll_entry_declare(_type, _name, _section_u, _section_d)        \
    _type _u_boot_list_##_section_u##_##_name __attribute__((    \
            unused,    aligned(4),                \
            section(".u_boot_list."#_section_d"."#_name)))

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,        \
                _usage, _help, _comp)            \
        { #_name, _maxargs, _rep, _cmd, _usage,            \
            _CMD_HELP(_help) _CMD_COMPLETE(_comp) }

通过上述的定义,我们对命令tftpboot的的实现就展现为如下的形式:
    cmd_tbl_t _u_boot_list_cmd_tftpboot __attribute__((    \
            unused,    aligned(4),                \                         /* 4字节对齐 */
            section(".u_boot_list."#_section_d"."#_name)))   /* 指定存放在.u_boot.list节 */
            =
            {
                "tftpboot",       /* name */
                3,                /* 最大参数数目 */
                1,                /* 自动重复开关 */
                do_tftpb,         /* 命令函数 */
                "",               /* 命令用法usage,字符串 */
                "",               /* 帮助help,字符串 */
                NULL
            }
这个变量_u_boot_list_cmd_tftpboot的定义时指示了属性,连接器会将其链接到 .u_boot_list节中,这样,我们就通过_u_boot_list_cmd__start 比较name,来得到具体命令的结构了。

上一篇:UATR与RS232以及COM端口的关系
下一篇:GDB调试多线程