1> main_loop common/main.cide
/****************************************************************************/ 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的功能是根據給出的前綴字符串,找出全部前綴相同的命令。oop
每一個命令在內存中用一個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.cthis
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> 引導命令spa
/**************************************************************************** * 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; }