1> main_loop common/main.c
/****************************************************************************/ void main_loop (void) { #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif #ifdef CONFIG_PREBOOT char *p; #endif #ifdef CONFIG_BOOTCOUNT_LIMIT unsigned long bootcount = 0; unsigned long bootlimit = 0; char *bcs; char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) ulong bmp = 0; /* default bitmap */ extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT if (do_mdm_init) bmp = 1; /* alternate bitmap */ #endif trab_vfd (bmp); #endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store (bootcount); sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ #ifdef CONFIG_VERSION_VARIABLE { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif #ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); //安装自动补全的函数,分析如下 #endif #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_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_UPDATE_TFTP) update_tftp (); #endif /* CONFIG_UPDATE_TFTP */ #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)) { //如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。abortboot函数的分析见下面。 # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0);//运行引导内核的命令。这个命令是在配置头文件中定义的。run_command的分析在下面。 # 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 CONFIG_SYS_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 */ #ifdef CONFIG_AMIGAONEG3SE { extern void video_banner(void); video_banner(); } #endif /* * Main Loop for Monitor Command Processing */ #ifdef CONFIG_SYS_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 (CONFIG_SYS_PROMPT); //CONFIG_SYS_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 /*CONFIG_SYS_HUSH_PARSER*/ }
2> 自动补全
int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[]) { static char tmp_buf[512]; int space; space = last_char == ‘\0‘ || last_char == ‘ ‘ || last_char == ‘\t‘; if (space && argc == 1) return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); if (!space && argc == 2) return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); return 0; } static void install_auto_complete_handler(const char *cmd, int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[])) { cmd_tbl_t *cmdtp; cmdtp = find_cmd(cmd); if (cmdtp == NULL) return; cmdtp->complete = complete; //命令结构体的complete指针指向传入的函数。 } void install_auto_complete(void) { #if defined(CONFIG_CMD_EDITENV) install_auto_complete_handler("editenv", var_complete); #endif install_auto_complete_handler("printenv", var_complete); install_auto_complete_handler("setenv", var_complete); #if defined(CONFIG_CMD_RUN) install_auto_complete_handler("run", var_complete); #endif }
可以看到将editenv、printenv、setenv和run的自动补全函数安装为 var_complete。var_complete的功能是根据给出的前缀字符串,找出所有前缀相同的命令。
每个命令在内存中用一个cmd_tbl_t 表示。include/command.h
struct cmd_tbl_s { char *name; /* 命令名,输入的就是它 */ int maxargs; /* 最大参数个数 */ int repeatable; /* 允许自动重发,也就是在按下空格键之后执行最后一条命令。 */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 实现命令的参数 */ char *usage; /* 短的提示信息 */ #ifdef CONFIG_SYS_LONGHELP char *help; /* 详细的帮助信息。 */ #endif #ifdef CONFIG__AUTO_COMPLETE /* do auto completion on the arguments */ int (*complete)(int argc, char *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 U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) / cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} #define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) / {#name, maxargs, rep, cmd, usage, help} /*uboot中的命令使用U_BOOT_CMD这个宏声明来注册进系统,链接脚本会把所有的cmd_tbl_t结构体放在相邻的地方。 链接脚本中的一些内容如下: */ . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; /*可见,__u_boot_cmd_start 和__u_boot_cmd_end 分别对应命令结构体在内存中开始和结束的地址。 */
3> abortboot函数的分析 abortboot是uboot在引导期间的延时函数。期间可以按键进入uboot的命令行。common/main.c
static __inline__ int abortboot(int bootdelay) { int abort = 0; #ifdef CONFIG_MENUPROMPT printf(CONFIG_MENUPROMPT); #else printf("Hit any key to stop autoboot: %2d ", bootdelay); #endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK //如果定义了这个宏,即使定义延时为0,也会检查一次是否有按键按下。 //只要在这里执行之前按键,还是能进入uboot的命令行。 /* * Check if key already pressed * Don‘t check if bootdelay < 0 */ if (bootdelay >= 0) { if (tstc()) { // 测试是否有按键按下 (void) getc(); //接收按键值 puts ("\b\b\b 0"); abort = 1; //修改标记,停止自动引导 } } #endif while ((bootdelay > 0) && (!abort)) { //如果延时大于零并且停止标记没有赋值则进入延时循环,直到延时完或者接收到了按 键 int i; --bootdelay; /* delay 100 * 10ms */ //每秒中测试按键100次,之后延时10ms。 for (i=0; !abort && i<100; ++i) { if (tstc()) { /* we got a key press */ abort = 1; //修改标记,停止自动引导 bootdelay = 0; //延时归零 # ifdef CONFIG_MENUKEY menukey = getc(); # else (void) getc(); /* 获取按键*/ # endif break; } udelay(10000); //延时10000us,也就是10ms } printf("\b\b\b%2d ", bootdelay);//打印当前剩余时间 //可以看到uboot延时的单位是秒,如果想提高延时的精度, //比如想进行10ms级的延时,将udelay(10000)改为udelay(100)就可以了 。 } putc(‘\n‘); #ifdef CONFIG_SILENT_CONSOLE if (abort) gd->flags &= ~GD_FLG_SILENT; #endif return abort;//返回结果:1-停止引导,进入命令行; 0-引导内核。 } # endif /* CONFIG_AUTOBOOT_KEYED */ #endif /* CONFIG_BOOTDELAY >= 0 */
4> 引导命令
/**************************************************************************** * returns: * 1 - command executed, repeatable * 0 - command executed but not repeatable, interrupted commands are * always considered not repeatable * -1 - not executed (unrecognized, bootd recursion or too many args) * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is * considered unrecognized) * * WARNING: * * We must create a temporary copy of the command since the command we get * may be the result from getenv(), which returns a pointer directly to * the environment data, which may change magicly when the command we run * creates or modifies environment variables (like "bootp" does). */ int run_command (const char *cmd, int flag) { cmd_tbl_t *cmdtp; 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; #ifdef DEBUG_PARSER printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ puts ("\"\n"); #endif clear_ctrlc(); /* forget any previous Control C */ if (!cmd || !*cmd) { return -1; /* 空命令 */ } if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { //命令太长 puts ("## Command too long!\n"); return -1; } strcpy (cmdbuf, cmd);//将命令拷贝到临时命令缓冲cmdbuf /* Process separators and check for invalid * repeatable commands */ #ifdef DEBUG_PARSER printf ("[PROCESS_SEPARATORS] %s\n", cmd); #endif while (*str) { /* * Find separator, or string end * Allow simple escape of ‘;‘ by writing "\;" */ 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; //token指向命令的开头 if (*sep) { //如果是分隔符的话,将分隔符替换为空字符 str = sep + 1; /* str指向下一句的开头*/ *sep = ‘\0‘; } else str = sep; /* 如果没有其它命令了,str指向命令尾部 */ #ifdef DEBUG_PARSER printf ("token: \"%s\"\n", token); #endif /* find macros in this token and replace them */ process_macros (token, finaltoken); //将token指向的命令中的宏替换掉,如把$(kernelsize)替换成内核的大小 /* Extract arguments */ if ((argc = parse_line (finaltoken, argv)) == 0) { //将每一个参数用‘/0’隔开,argv中的每一个指针指向一个参数的起始地址。 //返回值为参数的个数 rc = -1; /* no command at all */ continue; } /* Look up command in command table */ if ((cmdtp = find_cmd(argv[0])) == NULL) { //第一个参数就是要运行的命令,首先在命令表中找到它的命令结构体的指针 printf ("Unknown command ‘%s‘ - try ‘help‘\n", argv[0]); rc = -1; /* give up after bad command */ continue; } /* found - check max args */ if (argc > cmdtp->maxargs) {//检查参数个数是否过多 cmd_usage(cmdtp); 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 /* OK - call function to do the command */ if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {//调用命令执行函数。这是最重要的一步。 rc = -1; } repeatable &= cmdtp->repeatable;;//设置命令自动重复执行的标志。也就是只按下enter键是否可以执行最近执行的命令 . /* Did the user stop this? */ if (had_ctrlc ()) //检查是否有ctrl+c按键按下,如果有,结束当前命令。 //本函数并没有从中断接收字符,接收ctrl+c的是一些执行命令的函数。 return -1; /* if stopped then not repeatable */ } return rc ? rc : repeatable; }