从零开始的UBOOT的学习8--命令体系

从零开始的UBOOT的学习8--命令体系

参考朱有鹏UBOOT全集的一部分

1、从UBOOT的启动阶段的第二流程到命令体系

(1)在UBOOT中使用一个死循环,实现了命令体系:

在这个main_loop()函数里面实现了命令的注册,存储,解析,处理的过程
而且这个是一个死循环,也就是说会不断的执行这个函数。

    for (;;) 
    {
        main_loop ();
    }

2、从零开始分析这个函数main_loop()

void main_loop (void)
{
    static char lastcommand[CFG_CBSIZE] = { 0, };
//定义的最近的使用的命令,是用来使用回车键执行的命令
    int len;
    int rc = 1;
    int flag;

    char *s;   //定义一个字符数组
    int bootdelay;  //定义一个开机倒计时的功能

//如果定义了UBOOT的版本说明
#ifdef CONFIG_VERSION_VARIABLE
    {
        extern char version_string[];

        setenv ("ver", version_string);  /* set version variable */
    }
#endif /* CONFIG_VERSION_VARIABLE */

#ifdef CFG_HUSH_PARSER
    u_boot_hush_start ();
#endif

//使用fastboot来烧写程序
#ifdef CONFIG_FASTBOOT
    if (fastboot_preboot())
        run_command("fastboot", 0);
#endif

#ifdef CONFIG_PREBOOT
    if ((p = getenv ("preboot")) != NULL) {
    #ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
    #endif

    #ifndef CFG_HUSH_PARSER
        run_command (p, 0);
    #else
        parse_string_outer(p, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
    #endif

    #ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
    #endif
    }
#endif /* CONFIG_PREBOOT */

//这里通过得到环境变量的值来判断程序
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

# ifdef CONFIG_BOOT_RETRY_TIME
    init_cmd_timeout ();
# endif    /* CONFIG_BOOT_RETRY_TIME */

#ifdef CONFIG_POST
    if (gd->flags & GD_FLG_POSTFAIL) {
        s = getenv("failbootcmd");
    }
    else
#endif /* CONFIG_POST */

#ifdef CONFIG_BOOTCOUNT_LIMIT
    if (bootlimit && (bootcount > bootlimit)) {
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
                (unsigned)bootlimit);
        s = getenv ("altbootcmd");
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
#ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
#endif

#ifndef CFG_HUSH_PARSER
        run_command (s, 0);
#else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
#endif

#ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

#ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = getenv("menucmd");
        if (s) {
#ifndef CFG_HUSH_PARSER
        run_command (s, 0);
#else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
#endif
        }
    }
#endif /* CONFIG_MENUKEY */
#endif    /* CONFIG_BOOTDELAY */

    /*
     * Main Loop for Monitor Command Processing
     */
#ifdef CFG_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;);
#else
    for (;;) {
    #ifdef CONFIG_BOOT_RETRY_TIME
        if (rc >= 0) {
            /* Saw enough of a valid command to
             * restart the timeout.
             */
            reset_cmd_timeout();
        }
    #endif
        len = readline (CFG_PROMPT);

        flag = 0;    /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
        else if (len == -2) {
            /* -2 means timed out, retry autoboot
             */
            puts ("\nTimed out waiting for command\n");

    #ifdef CONFIG_RESET_TO_RETRY
            /* Reinit board to run initialization code again */
            do_reset (NULL, 0, 0, NULL);
    #else
            return;        /* retry autoboot */
    #endif
        }
#endif

        if (len == -1)
            puts ("<INTERRUPT>\n");
        else
            rc = run_command (lastcommand, flag);

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

这是执行运行命令的函数

int run_command (const char *cmd, int flag)
{
    cmd_tbl_t *cmdtp;
    char cmdbuf[CFG_CBSIZE]; //定义了一个字符数组,用来存储命令
    char *token;            /* start of token in cmdbuf    */
    char *sep;            /* end of token (separator) in cmdbuf */
    char finaltoken[CFG_CBSIZE];
    char *str = cmdbuf;
    char *argv[CFG_MAXARGS + 1];    /* NULL terminated    */
    int argc, inquotes;
    int repeatable = 1;
    int rc = 0;

    clear_ctrlc(); //Ctrl C来打断程序的运行

    if (!cmd || !*cmd) {
        return -1;    /* empty command */
    }

//如果命令的长度,比512个字节来大的话,打印执行的命令太长
    if (strlen(cmd) >= CFG_CBSIZE) {
        puts ("## Command too long!\n");
        return -1;
    }

    strcpy (cmdbuf, cmd);//把cmd的命令存储到cmdbuf中去

//此指针是字符数组
    while (*str) {
        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 */
#ifdef DEBUG_PARSER
        printf ("token: \"%s\"\n", token);
#endif

        /* Extract arguments */
        if ((argc = parse_line (finaltoken, argv)) == 0) {
            rc = -1;    /* no command at all */
            continue;
        }

//通过查找命令不断的从命令池中轮询命令,没有的话,打印不确定的命令
        if ((cmdtp = find_cmd(argv[0])) == NULL) {
            printf ("Unknown command '%s' - try 'help'\n", argv[0]);
            rc = -1;    /* give up after bad command */
            continue;
        }

//判断传递过来的参数是否超过这个命令超过的参数
        if (argc > cmdtp->maxargs) {
            printf ("Usage:\n%s\n", cmdtp->usage);
            rc = -1;
            continue;
        }

#if defined(CONFIG_CMD_BOOTD)
        /* avoid "bootd" recursion */
        if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
            printf ("[%s]\n", finaltoken);
#endif
            if (flag & CMD_FLAG_BOOTD) {
                puts ("'bootd' recursion detected\n");
                rc = -1;
                continue;
            } else {
                flag |= CMD_FLAG_BOOTD;
            }
        }
#endif

//直接使用函数指针的方式,来调用函数
        if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
            rc = -1;
        }

        repeatable &= cmdtp->repeatable;

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

    return rc ? rc : repeatable;
}

这个是找命令的函数,传递给这个函数的参数是命令的第一个参数

cmd_tbl_t *find_cmd (const char *cmd)
{
    cmd_tbl_t *cmdtp;
    cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;//定义命令的起始地址
    const char *p;
    int len;
    int n_found = 0;

    len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);

//不断的遍历这个段,然后通过指针的移动,不断的遍历命令,一个命令相当于一个结构体,一个结构体之间的占用内存的空间是一样的
    for (cmdtp = &__u_boot_cmd_start;
         cmdtp != &__u_boot_cmd_end;
         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 */
}

3、命令结构体cmd_tbl_t

typedef struct cmd_tbl_s cmd_tbl_t;
(1)name:命令名称,字符串格式。
(2)maxargs:命令最多可以接收多少个参数
(3)repeatable:指示这个命令是否可重复执行。重复执行是uboot命令行的一种工作机制,就是直接按回车则执行上一条执行的命令。
(4)cmd:函数指针,命令对应的函数的函数指针,将来执行这个命令的函数时使用这个函数指针来调用。
(5)usage:命令的短帮助信息。对命令的简单描述。
(6)help:命令的长帮助信息。细节的帮助信息。
(7)complete:函数指针,指向这个命令的自动补全的函数。

总结:UBOOT的命令体系在工作的时候,一个命令对应一个cmd_tbl_t结构体的一个实例,然后UBOOT支持多少个命令。

UBOOT实现命令管理的思路:
(1)填充一个结构体实例构成一个命令
(2)给命令结构体实例附加特定段属性(用户自定义段),链接时将带有该段属性的内容链接在一起排列。
(3)UBOOT重定位时将该段整体加载到DDR中,加载到DDR中的UBOOT镜像中带有特定段属性的这一段其实就是命令结构体的集合,有点像一个命令结构体数组。
(4)段起始地址和段结束地址(链接地址、定义在uboot.lds)决定了这些命令集的开始和结束地址。

struct cmd_tbl_s
{
    char *name;    //命令的名字
    int  maxargs;  //命令允许的最多的参数
    int  repeatable;  //是否允许ENTER键重复
    int  (*cmd)(struct cmd_tbl_s *, int, char *[]); //函数指针,相当于方法
    char *usage;   //短帮助信息
    char *help;    //长帮助信息
};

4、简单的实例分析:

VERSION命令集的实现

(1)U_BOOT_CMD:其实可以认为它定义了一个命令结构体的实例。
也就是Linux内核经常说的很牛逼的注册

(2)使用一个宏定义来进行注册命令,这个宏定义其实就是定义一个结构体实例,并且初始化它。

(3)do_version函数其实就是C++常说的面向对象的思考方法
我们需要对一些重复的东西进行抽象,这样就是面向对象的思想。

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}


int
do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    extern char version_string[];
    printf ("\n%s\n", version_string);
    return 0;
}

U_BOOT_CMD(
    version,    1,        1,    do_version,
    "version - print monitor version\n",
    NULL
);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值