zoukankan      html  css  js  c++  java
  • uboot学习之BL3的流程

    BL2的最后通过汇编调用了board_init_r函数,此时进入BL3的阶段,此时的主要工作:

    这一阶段涉及的文件及任务如下

    arch/arm/lib/board.c
               1. board_init_r()是进入定制板目录的入口
    common/main.c
               2. main_loop()中关闭中断,执行命令以及加载引导内核

    下面分析一下board_init_r函数:

    /*
     ************************************************************************
     *
     * This is the next part if the initialization sequence: we are now
     * running from RAM and have a "normal" C environment, i. e. global
     * data can be written, BSS has been cleared, the stack size in not
     * that critical any more, etc.
     *
     ************************************************************************
     */
    void board_init_r(gd_t *id, ulong dest_addr)
    {
        ...
        bd_t *bd;
        ...
        gd = id;
        bd = gd->bd;
    
        gd->flags |= GD_FLG_RELOC;    /* tell others: relocation done */
    
        monitor_flash_len = _end_ofs;
    
        ...
        debug("monitor flash len: %08lX
    ", monitor_flash_len);
        board_init();    /* Setup chipselects */    

    上述代码的作用是对gd和bd进行赋值,其中monitor_flash_len为整个U-Boot的长度。

     malloc_start = dest_addr - TOTAL_MALLOC_LEN - sizeof(struct spare_boot_head_t);
    ...
    /* The Malloc area is immediately below the monitor copy in DRAM */
        mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);

    对SDRAM中的malloc空间进行清零初始化。

    #if !defined(CONFIG_SYS_NO_FLASH)
        puts("Flash: ");
    
        flash_size = flash_init();
        if (flash_size > 0) {
    # ifdef CONFIG_SYS_FLASH_CHECKSUM
            print_size(flash_size, "");
            /*
             * Compute and print flash CRC if flashchecksum is set to 'y'
             *
             * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
             */
            s = getenv("flashchecksum");
            if (s && (*s == 'y')) {
                printf("  CRC: %08X", crc32(0,
                    (const unsigned char *) CONFIG_SYS_FLASH_BASE,
                    flash_size));
            }
            putc('
    ');
    # else    /* !CONFIG_SYS_FLASH_CHECKSUM */
            print_size(flash_size, "
    ");
    # endif /* CONFIG_SYS_FLASH_CHECKSUM */
        } else {
            puts(failed);
            hang();
        }
    #endif

    上述代码的作用是计算FLASH的大小,并把它通过串口显示在控制台上。由于没有定义CONFIG_SYS_FLASH_CHECKSUM,所以没有执行CRC的校验和。其中flash_init函数是在drivers/mtd目录下的cfi_flash.c文件内(因为include/configs/smdk2410.h中定义了CONFIG_FLASH_CFI_DRIVER)。

        /* set up exceptions */
        interrupt_init();
        /* enable exceptions */
        enable_interrupts();

    interrupt_init函数是建立IRQ中断堆栈,enable_interrupts函数是使能IRQ中断,它们都是在arch/arm/lib目录下的interrupts.c文件中定义的。

    ...
    /* initialize environment */
        env_relocate();

    初始化环境变量,由于gd->env_valid等于0,所以在这里设置的是缺省环境变量。env_relocate函数是在common目录下的env_common.c文件中定义的。

    #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
        arm_pci_init();
    #endif

    初始化PCI。

        /* IP Address */
        gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");
        stdio_init();    /* get the devices list going. */
        jumptable_init();
    #if defined(CONFIG_API)
        /* Initialize API */
        api_init();
    #endif
    
        console_init_r();    /* fully init console as a device */

    上面代码的作用分别对应:

    设置IP地址。

    初始化各类外设,如IIC、LCD、键盘、USB等,当然只有在定义了这些外设的前提下,才对这些外设进行初始化。该函数是在common目录下的stdio.c文件中定义的。

    初始化跳转表gd->jt,该跳转表是一个函数指针数组,它定义了U-Boot中基本的常用函数库。该函数是在common目录下的exports.c文件中定义的。

    初始化API。

    初始化控制台,即标准输入、标准输出和标准错误,在这里都是串口。该函数是在common目录下的console.c文件中定义的。

        /* Initialize from environment */
        s = getenv("loadaddr");
        if (s != NULL)
            load_addr = simple_strtoul(s, NULL, 16);

    从环境变量中获取loadaddr参数,得到需要加载的地址。

    #if defined(CONFIG_CMD_NET)
        s = getenv("bootfile");
        if (s != NULL)
            copy_filename(BootFile, s, sizeof(BootFile));
    #endif

    从环境变量中获取bootfile参数,得到通过TFTP加载的镜像文件名。

    #if defined(CONFIG_CMD_NET)
    #if defined(CONFIG_NET_MULTI)
        puts("Net:   ");
    #endif
        eth_initialize(gd->bd);
    #if defined(CONFIG_RESET_PHY_R)
        debug("Reset Ethernet PHY
    ");
        reset_phy();
    #endif
    #endif

    初始化以太网,其中eth_initialize函数是在net目录下的eth.c文件中定义的。

        ...
       /* main_loop() can return to retry autoboot, if so just run it again. */
        for (;;)
        {
            main_loop();
        }
        hang();
        /* NOTREACHED - no way out of command loop except booting */
    }

    board_init_r函数的最后就是执行一个死循环,调用main_loop函数。该函数是在common目录下的main.c文件内定义的。

    总结:

    3.接下来分析main_loop函数:

    void main_loop (void)
    {
    #ifndef CONFIG_SYS_HUSH_PARSER
        static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
        int len;
        int rc = 1;
        int flag;
    #endif

    声明一些hush参数。关于hush后面会讲到。

    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
        char *s;
        int bootdelay;
    #endif

    声明启动延时需要的参数。

    #ifdef CONFIG_SYS_HUSH_PARSER
        u_boot_hush_start ();
    #endif

    初始化hush功能。稍后再说。

     1 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
     2     s = getenv ("bootdelay");
     3     bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
     4 
     5     debug ("### main_loop entered: bootdelay=%d
    
    ", bootdelay);
     6 
     7 # ifdef CONFIG_BOOT_RETRY_TIME
     8     init_cmd_timeout ();
     9 # endif    /* CONFIG_BOOT_RETRY_TIME */
    10 
    11 #ifdef CONFIG_POST
    12     if (gd->flags & GD_FLG_POSTFAIL) {
    13         s = getenv("failbootcmd");
    14     }
    15     else
    16 #endif /* CONFIG_POST */
    17 #ifdef CONFIG_BOOTCOUNT_LIMIT
    18     if (bootlimit && (bootcount > bootlimit)) {
    19         printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.
    ",
    20                 (unsigned)bootlimit);
    21         s = getenv ("altbootcmd");
    22     }
    23     else
    24 #endif /* CONFIG_BOOTCOUNT_LIMIT */
    25         s = getenv ("bootcmd");
    26 
    27     debug ("### main_loop: bootcmd="%s"
    ", s ? s : "<UNDEFINED>");
    28 
    29     if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
    30 # ifdef CONFIG_AUTOBOOT_KEYED
    31         int prev = disable_ctrlc(1);    /* disable Control C checking */
    32 # endif
    33 
    34 # ifndef CONFIG_SYS_HUSH_PARSER
    35         run_command (s, 0);
    36 # else
    37         parse_string_outer(s, FLAG_PARSE_SEMICOLON |
    38                     FLAG_EXIT_FROM_LOOP);
    39 # endif
    40 
    41 # ifdef CONFIG_AUTOBOOT_KEYED
    42         disable_ctrlc(prev);    /* restore Control C checking */
    43 # endif
    44     }
    45 
    46 # ifdef CONFIG_MENUKEY
    47     if (menukey == CONFIG_MENUKEY) {
    48         s = getenv("menucmd");
    49         if (s) {
    50 # ifndef CONFIG_SYS_HUSH_PARSER
    51             run_command(s, 0);
    52 # else
    53             parse_string_outer(s, FLAG_PARSE_SEMICOLON |
    54                         FLAG_EXIT_FROM_LOOP);
    55 # endif
    56         }
    57     }
    58 #endif /* CONFIG_MENUKEY */
    59 #endif /* CONFIG_BOOTDELAY */

    第2行和第3行的含义是从环境变量中获取bootdelay参数,得到自动启动缺省镜像文件的延时(单位是秒)。

    其中bootdelay的作用是:uboot正常启动后,会调用main_loop(void)函数,进入main_loop()之后,如果在规定的时间(CONFIG_BOOTDELAY)内,没有检查到任何按键事件的发生,就会去加载OS,并启动系统。

    第8行的含义是初始化命令行超时机制。

    第25行的含义是从环境变量中获取bootcmd参数,得到在启动延时过程中自动执行的命令。当我们得到了bootcmd参数,bootdelay参数也是大于等于0,并且在启动延时过程中没有按下任意键时,执行第37行的parse_string_outer函数,该函数的作用是解释bootcmd参数

    并执行,它是在common目录下的hush.c文件内定义的。

     1     /*
     2      * Main Loop for Monitor Command Processing
     3      */
     4 #ifdef CONFIG_SYS_HUSH_PARSER
     5     parse_file_outer();
     6     /* This point is never reached */
     7     for (;;);
     8 #else
     9     for (;;) {
    10 #ifdef CONFIG_BOOT_RETRY_TIME
    11         if (rc >= 0) {
    12             /* Saw enough of a valid command to
    13              * restart the timeout.
    14              */
    15             reset_cmd_timeout();
    16         }
    17 #endif
    18         len = readline (CONFIG_SYS_PROMPT);
    19 
    20         flag = 0;    /* assume no special flags for now */
    21         if (len > 0)
    22             strcpy (lastcommand, console_buffer);
    23         else if (len == 0)
    24             flag |= CMD_FLAG_REPEAT;
    25 #ifdef CONFIG_BOOT_RETRY_TIME
    26         else if (len == -2) {
    27             /* -2 means timed out, retry autoboot
    28              */
    29             puts ("
    Timed out waiting for command
    ");
    30 # ifdef CONFIG_RESET_TO_RETRY
    31             /* Reinit board to run initialization code again */
    32             do_reset (NULL, 0, 0, NULL);
    33 # else
    34             return;        /* retry autoboot */
    35 # endif
    36         }
    37 #endif
    38 
    39         if (len == -1)
    40             puts ("<INTERRUPT>
    ");
    41         else
    42             rc = run_command (lastcommand, flag);
    43 
    44         if (rc <= 0) {
    45             /* invalid command or not repeatable, forget it */
    46             lastcommand[0] = 0;
    47         }
    48     }
    49 #endif /*CONFIG_SYS_HUSH_PARSER*/
    50 }

    由于在include/configs/smdk2410.h文件中定义了CONFIG_SYS_HUSH_PARSER,所以上面的代码仅仅执行的是第5行至第7行的内容。第5行的parse_file_outer函数是在common目录下的hush.c文件中定义的,它的含义是依次读取命令序列中的命令并执行之,其中在该

    函数还调用了parse_stream_outer函数,这个函数体内有一个do-while循环,只有发生语法错误的时候才会跳出该循环,因此一般情况下永远也不会执行上面代码中的第7行内容,而是始终在那个do-while循环体内。

    上面说到过如果在CONFIG_BOOTDELAY时间内,用户按下键盘上的任意一个按键,uboot就会进入与用户交互的状态。如果用户在配置文件中定义了CONFIG_SYS_HUSH_PARSER,就会通过parse_file_outer(),去接收并解析用户命令,否则进入一个for(;;)循环,

    通过 readline (CONFIG_SYS_PROMPT)接收用户命令,然后调用run_command(cmd,flag)去解析并执行命令。

    当在配置文件中定义了CONFIG_SYS_HUSH_PARSER,main_loop会调用parse_file_outer(),进入hush中。其中parse_file_outer()在u-bootcommonhush.c下。

    #ifndef __U_BOOT__
    static int parse_file_outer(FILE *f)
    #else
    int parse_file_outer(void)
    #endif
    {
        int rcode;
        struct in_str input;
    #ifndef __U_BOOT__
        setup_file_in_str(&input, f);
    #else
        setup_file_in_str(&input);
    #endif
        rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
        return rcode;
    }
    /* most recursion does not come through here, the exeception is
     * from builtin_source() */
    int parse_stream_outer(struct in_str *inp, int flag)
    {
    
        struct p_context ctx;
        o_string temp=NULL_O_STRING;
        int rcode;
    #ifdef __U_BOOT__
        int code = 0;
    #endif
        do {
            ctx.type = flag;
            initialize_context(&ctx);
            update_ifs_map();
            if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
            inp->promptmode=1;
            rcode = parse_stream(&temp, &ctx, inp, '
    ');
    #ifdef __U_BOOT__
            if (rcode == 1) flag_repeat = 0;
    #endif
            if (rcode != 1 && ctx.old_flag != 0) {
                syntax();
    #ifdef __U_BOOT__
                flag_repeat = 0;
    #endif
            }
            if (rcode != 1 && ctx.old_flag == 0) {
                done_word(&temp, &ctx);
                done_pipe(&ctx,PIPE_SEQ);
    #ifndef __U_BOOT__
                run_list(ctx.list_head);
    #else
                code = run_list(ctx.list_head);
                if (code == -2) {    /* exit */
                    b_free(&temp);
                    code = 0;
                    /* XXX hackish way to not allow exit from main loop */
                    if (inp->peek == file_peek) {
                        printf("exit not allowed from main input shell.
    ");
                        continue;
                    }
                    break;
                }
                if (code == -1)
                    flag_repeat = 0;
    #endif
            } else {
                if (ctx.old_flag != 0) {
                    free(ctx.stack);
                    b_reset(&temp);
                }
    #ifdef __U_BOOT__
                if (inp->__promptme == 0) printf("<INTERRUPT>
    ");
                inp->__promptme = 1;
    #endif
                temp.nonnull = 0;
                temp.quote = 0;
                inp->p = NULL;
                free_pipe_list(ctx.list_head,0);
            }
            b_free(&temp);
        } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP));   /* loop on syntax errors, return on EOF */
    #ifndef __U_BOOT__
        return 0;
    #else
        return (code != 0) ? 1 : 0;
    #endif /* __U_BOOT__ */
    }

    hush是uboot中命令接收和解析的工具,与uboot原始的命令解析方法相比,该工具更加智能。

    这里会在run_list中调用到hush中的run_pipe_real(struct pipe *pi),在该函数中经过一些列解析,最终会调用到对应的命令执行函数。

    /* run_pipe_real() starts all the jobs, but doesn't wait for anything
     * to finish.  See checkjobs().
     *
     * return code is normally -1, when the caller has to wait for children
     * to finish to determine the exit status of the pipe.  If the pipe
     * is a simple builtin command, however, the action is done by the
     * time run_pipe_real returns, and the exit code is provided as the
     * return value.
     *
     * The input of the pipe is always stdin, the output is always
     * stdout.  The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
     * because it tries to avoid running the command substitution in
     * subshell, when that is in fact necessary.  The subshell process
     * now has its stdout directed to the input of the appropriate pipe,
     * so this routine is noticeably simpler.
     */
    static int run_pipe_real(struct pipe *pi)
    {
        int i;
    #ifndef __U_BOOT__
        int nextin, nextout;
        int pipefds[2];                /* pipefds[0] is for reading */
        struct child_prog *child;
        struct built_in_command *x;
        char *p;
    # if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &i;
        (void) &nextin;
        (void) &nextout;
        (void) &child;
    # endif
    #else
        int nextin;
        int flag = do_repeat ? CMD_FLAG_REPEAT : 0;
        struct child_prog *child;
        cmd_tbl_t *cmdtp;
        char *p;
    # if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &i;
        (void) &nextin;
        (void) &child;
    # endif
    #endif    /* __U_BOOT__ */
    
        nextin = 0;
    #ifndef __U_BOOT__
        pi->pgrp = -1;
    #endif
    
        /* Check if this is a simple builtin (not part of a pipe).
         * Builtins within pipes have to fork anyway, and are handled in
         * pseudo_exec.  "echo foo | read bar" doesn't work on bash, either.
         */
        if (pi->num_progs == 1) child = & (pi->progs[0]);
    #ifndef __U_BOOT__
        if (pi->num_progs == 1 && child->group && child->subshell == 0) {
            int squirrel[] = {-1, -1, -1};
            int rcode;
            debug_printf("non-subshell grouping
    ");
            setup_redirects(child, squirrel);
            /* XXX could we merge code with following builtin case,
             * by creating a pseudo builtin that calls run_list_real? */
            rcode = run_list_real(child->group);
            restore_redirects(squirrel);
    #else
            if (pi->num_progs == 1 && child->group) {
            int rcode;
            debug_printf("non-subshell grouping
    ");
            rcode = run_list_real(child->group);
    #endif
            return rcode;
        } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
            for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
            if (i!=0 && child->argv[i]==NULL) {
                /* assignments, but no command: set the local environment */
                for (i=0; child->argv[i]!=NULL; i++) {
    
                    /* Ok, this case is tricky.  We have to decide if this is a
                     * local variable, or an already exported variable.  If it is
                     * already exported, we have to export the new value.  If it is
                     * not exported, we need only set this as a local variable.
                     * This junk is all to decide whether or not to export this
                     * variable. */
                    int export_me=0;
                    char *name, *value;
                    name = xstrdup(child->argv[i]);
                    debug_printf("Local environment set: %s
    ", name);
                    value = strchr(name, '=');
                    if (value)
                        *value=0;
    #ifndef __U_BOOT__
                    if ( get_local_var(name)) {
                        export_me=1;
                    }
    #endif
                    free(name);
                    p = insert_var_value(child->argv[i]);
                    set_local_var(p, export_me);
                    if (p != child->argv[i]) free(p);
                }
                return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */
            }
            for (i = 0; is_assignment(child->argv[i]); i++) {
                p = insert_var_value(child->argv[i]);
    #ifndef __U_BOOT__
                putenv(strdup(p));
    #else
                set_local_var(p, 0);
    #endif
                if (p != child->argv[i]) {
                    child->sp--;
                    free(p);
                }
            }
            if (child->sp) {
                char * str = NULL;
    
                str = make_string((child->argv + i));
                parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
                free(str);
                return last_return_code;
            }
    #ifndef __U_BOOT__
            for (x = bltins; x->cmd; x++) {
                if (strcmp(child->argv[i], x->cmd) == 0 ) {
                    int squirrel[] = {-1, -1, -1};
                    int rcode;
                    if (x->function == builtin_exec && child->argv[i+1]==NULL) {
                        debug_printf("magic exec
    ");
                        setup_redirects(child,NULL);
                        return EXIT_SUCCESS;
                    }
                    debug_printf("builtin inline %s
    ", child->argv[0]);
                    /* XXX setup_redirects acts on file descriptors, not FILEs.
                     * This is perfect for work that comes after exec().
                     * Is it really safe for inline use?  Experimentally,
                     * things seem to work with glibc. */
                    setup_redirects(child, squirrel);
    #else
                /* check ";", because ,example , argv consist from
                 * "help;flinfo" must not execute
                 */
                if (strchr(child->argv[i], ';')) {
                    printf ("Unknown command '%s' - try 'help' or use 'run' command
    ",
                        child->argv[i]);
                    return -1;
                }
                /* Look up command in command table */
    
    
                if ((cmdtp = find_cmd(child->argv[i])) == NULL) {
                    printf ("Unknown command '%s' - try 'help'
    ", child->argv[i]);
                    return -1;    /* give up after bad command */
                } else {
                    int rcode;
    #if defined(CONFIG_CMD_BOOTD)
                    /* avoid "bootd" recursion */
                    if (cmdtp->cmd == do_bootd) {
                        if (flag & CMD_FLAG_BOOTD) {
                            printf ("'bootd' recursion detected
    ");
                            return -1;
                        }
                    else
                        flag |= CMD_FLAG_BOOTD;
                    }
    #endif
                    /* found - check max args */
                    if ((child->argc - i) > cmdtp->maxargs)
                        return cmd_usage(cmdtp);
    #endif
                    child->argv+=i;  /* XXX horrible hack */
    #ifndef __U_BOOT__
                    rcode = x->function(child);
    #else
                    /* OK - call function to do the command */
    
                    rcode = (cmdtp->cmd)
    (cmdtp, flag,child->argc-i,&child->argv[i]);
                    if ( !cmdtp->repeatable )
                        flag_repeat = 0;
    
    
    #endif
                    child->argv-=i;  /* XXX restore hack so free() can work right */
    #ifndef __U_BOOT__
    
                    restore_redirects(squirrel);
    #endif
    
                    return rcode;
                }
            }
    #ifndef __U_BOOT__
        }
    
        for (i = 0; i < pi->num_progs; i++) {
            child = & (pi->progs[i]);
    
            /* pipes are inserted between pairs of commands */
            if ((i + 1) < pi->num_progs) {
                if (pipe(pipefds)<0) perror_msg_and_die("pipe");
                nextout = pipefds[1];
            } else {
                nextout=1;
                pipefds[0] = -1;
            }
    
            /* XXX test for failed fork()? */
            if (!(child->pid = fork())) {
                /* Set the handling for job control signals back to the default.  */
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                signal(SIGTERM, SIG_DFL);
                signal(SIGTSTP, SIG_DFL);
                signal(SIGTTIN, SIG_DFL);
                signal(SIGTTOU, SIG_DFL);
                signal(SIGCHLD, SIG_DFL);
    
                close_all();
    
                if (nextin != 0) {
                    dup2(nextin, 0);
                    close(nextin);
                }
                if (nextout != 1) {
                    dup2(nextout, 1);
                    close(nextout);
                }
                if (pipefds[0]!=-1) {
                    close(pipefds[0]);  /* opposite end of our output pipe */
                }
    
                /* Like bash, explicit redirects override pipes,
                 * and the pipe fd is available for dup'ing. */
                setup_redirects(child,NULL);
    
                if (interactive && pi->followup!=PIPE_BG) {
                    /* If we (the child) win the race, put ourselves in the process
                     * group whose leader is the first process in this pipe. */
                    if (pi->pgrp < 0) {
                        pi->pgrp = getpid();
                    }
                    if (setpgid(0, pi->pgrp) == 0) {
                        tcsetpgrp(2, pi->pgrp);
                    }
                }
    
                pseudo_exec(child);
            }
    
    
            /* put our child in the process group whose leader is the
               first process in this pipe */
            if (pi->pgrp < 0) {
                pi->pgrp = child->pid;
            }
            /* Don't check for errors.  The child may be dead already,
             * in which case setpgid returns error code EACCES. */
            setpgid(child->pid, pi->pgrp);
    
            if (nextin != 0)
                close(nextin);
            if (nextout != 1)
                close(nextout);
    
            /* If there isn't another process, nextin is garbage
               but it doesn't matter */
            nextin = pipefds[0];
        }
    #endif
        return -1;
    }
    View Code

    上面关键是这段:

                    /* OK - call function to do the command */
    
                    rcode = (cmdtp->cmd)
    (cmdtp, flag,child->argc-i,&child->argv[i]);
                    if ( !cmdtp->repeatable )
                        flag_repeat = 0;

    通过这段代码调用对应的命令执行函数。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;

    对于uboot支持的每一个命令,是通过U_BOOT_CMD宏定义的,他定义了该命令对应的名称name,支持的最大参数rep,重复次数,实现函数cmd,以及输入help命令时,显示的帮助信息usage。

    在执行函数cmd中,第一个参数对应该命令结构本身的指针,第二个参数对应flag标记,第三个参数对应参数数目,第四个参数是指针数组,里面存储的是对应参数的指针。

  • 相关阅读:
    bodybuilding
    DBLINK的session无法关闭,报异常!
    失控
    eclipse
    Linux下查看用户列表
    org.apache.commons.httpclient
    java map 遍历
    java String split
    胸上肌到底要怎么练啊!
    POI操作Excel常用方法总结
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/7372849.html
Copyright © 2011-2022 走看看