U-boot主循環main_loop分析

原文在我博客:http://blog.csdn.net/andy_wsj/article/details/8614905

最近在寫cortex-M3的一個裸機程序,寫的過程當中忽然想到,雖然這個程序很簡單
可是我並無關心cortex-M3的啓動代碼,不少時候,我只關心主循環在幹什麼
甚至連初始化CPU部分也不須要很細緻,這些代碼在啓動時只執行一遍,以後就再也沒有執行了,
更多時候,須要關心的是一遍又一遍執行的主循環
因而,我以爲,u-boot程序若是不關心初始化,只關注主循環會怎麼樣呢?
想到就作,因而就有了下面的主循環代碼運行分析,這個分析是基於A10的u-boot代碼,固然其餘芯片應該也適用


分析環境:WindowsXP + SourceInsight3.5
.....
主循環代碼位置:/u-boot-sunxi-sunxi/common/main.c的main_loop函數裏面的
前面的初始化和其餘部分均忽略,假設在U-boot啓動時操做鍵盤,將進入以下循環代碼:
  ......
#ifdef CONFIG_SYS_HUSH_PARSER
     parse_file_outer();  //採用「hush」方式的主循環
/* 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;  //命令無效,則不記錄該命令
    }
}

1、主循環方式一 
通常循環方式,假設未定義宏CONFIG_SYS_HUSH_PARSER
將多餘的宏彙編去掉,假設均不打開宏彙編內的功能,則簡化一下以下:
.......
for (;;) {
  len = readline (CFG_PROMPT);  //讀取一行命令輸入,從串口輸入
  flag = 0; /* assume no special flags for now */
  if (len > 0)
   strcpy (lastcommand, console_buffer);     //將命令拷貝到lastcommand
  else if (len == 0)
   flag |= CMD_FLAG_REPEAT;         //收到空命令,將會重複執行上次命令
  if (len == -1)
   puts ("<INTERRUPT>\n");
  else
   rc = run_command (lastcommand, flag); //正常輸入時,flag=0;空命令時,flag |= CMD_FLAG_REPEAT
  if (rc <= 0) {  //若調用返回值小於等於零,則清除命令記錄
   /* invalid command or not repeatable, forget it */
   lastcommand[0] = 0;
  }
}
.......
可見,主循環是很簡單的,只作兩件事情:
一、循環查詢接收的一行命令輸入
二、執行輸入命令

一、循環查詢接收的一行命令輸入
進一步閱讀代碼,只須要理解串口輸入模式和命令執行模式,便可看到u-boot在交互模式下是如何工做的了。
首先來看這句代碼:
  len = readline (CFG_PROMPT);
宏定義:
#define CFG_PROMPT    "Andy# " /* Monitor Command Prompt */
CFG_PROMPT是命令提示符,能夠本身修改字符串內容,顯示不一樣的提示符。

函數readline;代碼位置:/u-boot-sunxi-sunxi/common/main.c
int readline (const char *const prompt)
{
/*
  * If console_buffer isn't 0-length the user will be prompted to modify
  * it instead of entering it from scratch as desired.
  */
console_buffer[0] = '\0';
return   readline_into_buffer(prompt, console_buffer, 0); //將一行串口輸入讀入全局變量console_buffer
}

再看函數readline_into_buffer;代碼位置:/u-boot-sunxi-sunxi/common/main.c
將多餘的宏彙編去掉,由於那些功能都沒有使能
int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
{
    char *p = buffer;
    char * p_buf = p;
    int n = 0;    /* buffer index  */
    int plen = 0;   /* prompt length */
    int col;    /* output column cnt */
    char c;
    /* print prompt */  
    if (prompt) {
         plen = strlen (prompt);
         puts (prompt);  //打印提示符  len = readline (CFG_PROMPT) 的 CFG_PROMPT
    }
    col = plen;  //光標的位置,在提示符以後
    for (;;) {   //這裏又是一個循環,即等待一行輸入,不回車或換行將一直循環
        WATCHDOG_RESET();  /* Trigger watchdog, if needed */  //看門狗,實際是空的宏,沒使用;
  
         c = getc();   //獲取串口輸入一個字符,假設要將輸入命令移植到另外一個裸機程序內,則須要實現這個接口,
                             //我將readline函數移植到了cortex-M3的控制器STM32F103VC上,實現串口與超級中斷交互
       /*
        * Special character handling  //特別字符處理,都是ASCC-II碼,回車、換行...等等特殊字符
       */
       switch (c) {
       case '\r':    /* Enter  */  //收到回車符,認爲一行輸入完成,返回執行命令
       case '\n':                        //收到換行符
            *p = '\0';
              puts ("\r\n");
              return (p - p_buf);
       case '\0':    /* nul   */  //收到空字符,啥也不作
             continue;
       case 0x03:    /* ^C - break  */     
             p_buf[0] = '\0'; /* discard input */
             return (-1);
       case 0x15:    /* ^U - erase line */ 
             while (col > plen) {
                 puts (erase_seq);
                 --col;
             }
             p = p_buf;
            n = 0;
            continue;
       case 0x17:    /* ^W - erase word */ 
             p=delete_char(p_buf, p, &col, &n, plen);
             while ((n > 0) && (*p != ' ')) {
                  p=delete_char(p_buf, p, &col, &n, plen);
              }
              continue;
        case 0x08:    /* ^H  - backspace */   //backspace輸入
        case 0x7F:    /* DEL - backspace */   //delete鍵輸入
               p=delete_char(p_buf, p, &col, &n, plen);
               continue;
         default:    //普通字符輸入,存入全局字符串變量console_buffer
         /*
        * Must be a normal character then
         */
              if (n < CONFIG_SYS_CBSIZE-2) {
                    if (c == '\t') { /* expand TABs  */
                            puts (tab_seq+(col&07));
                            col += 8 - (col&07);
                    } else {
                            ++col;  /* echo input  */
                             putc (c);   //將輸入打印出來,這樣PC上的超級終端或minicom就能看到輸入命令了
                    }
                    *p++ = c;
                    ++n;
            } else {   /* Buffer full  */
                    putc ('\a');
            }
       }
   }
}
由上述代碼可見,與硬件相關代碼就是
      c = getc(); 
通常來講是串口,實現getc從串口讀取數據便可,
早期版本arm9的U-boot的實現方式:在/u-boot-sunxi-sunxi/common/console.c
int getc (void)
{
        if (gd->flags & GD_FLG_DEVINIT) {
                /* Get from the standard input */
                return fgetc (stdin);  //若實現了標準輸入,從標準輸入讀取數據
        }
        /* Send directly to the handler */
        return serial_getc ();  //從串口讀取數據
}
int serial_getc (void) //根據CPU實現
{
        S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);  //獲取s3c24x0串口寄存器指針
        /* wait for character to arrive */
        while (!(uart->UTRSTAT & 0x1));   //循環等待串口接收完成
         return uart->URXH & 0xff;
}
這是早期版本arm9的U-boot的實現方式,其實就是循環等待串口一個字節輸入,
在u-boot-sunxi-sunxi中原理一致,具體實現細節比這個複雜,能夠參閱代碼,說道這裏,其實輸入字符的方法已經清晰了。

二、執行輸入命令
在主循環中,經過一下函數執行一個命令:
   rc = run_command (lastcommand, flag); 
   
看看run_command函數,在/u-boot-sunxi-sunxi/common/main.c裏面:
int run_command(const char *cmd, int flag)
{
  #ifndef CONFIG_SYS_HUSH_PARSER  //未定義
            /*
             * builtin_run_command can return 0 or 1 for success, so clean up
             * its result.
             */
         if (builtin_run_command(cmd, flag) == -1)  //實際執行的代碼
                 return 1;
         return 0;
  #else
         return parse_string_outer(cmd,
                                                         FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
  #endif
}
//下面這個函數負責調用解析命令模塊和命令執行模塊
static int builtin_run_command(const char *cmd, int flag)
{
........   
      if (cmd_process(flag, argc, argv, &repeatable))      //執行命令
.......
}
#endif
該函數最後調用到cmd_process函數,執行相應的命令

2、主循環方式二 ----> 「hush」方式

定義了宏CONFIG_SYS_HUSH_PARSER,在文件/u-boot-sunxi-sunxi/include/configs/sunxi-common.h內

主循環變成以下方式:
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
.....

函數parse_file_outer,在/u-boot-sunxi-sunxi/common/hush.c內

宏__U_BOOT__沒有定義,所以調用parse_file_outer不須要傳參數

#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;
}

再調用parse_stream_outer函數,也在/u-boot-sunxi-sunxi/common/hush.c內
函數parse_stream_outer是這種方式的循環主體,就是其中的do-while循環體。

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, '\n');  //讀取一行輸入命令,並解析之
#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.\n");
                          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>\n");
                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__ */
}

循環方式二也是兩個過程:
一、讀取一行輸入
二、解析並執行輸入命令


一、讀取數據
parse_file_outer函數內,輸入初始化:
         struct in_str input;
         setup_file_in_str(&input);

看看setup_file_in_str函數幹了啥:
#ifndef __U_BOOT__
static void setup_file_in_str(struct in_str *i, FILE *f)
#else
static void setup_file_in_str(struct in_str *i)
#endif
{
      i->peek = file_peek;  //輸入方式peek
      i->get = file_get;    //輸入方式get
      i->__promptme=1;
      i->promptmode=1;
#ifndef __U_BOOT__
      i->file = f;
#endif
      i->p = NULL;
}
看看數據結構struct in_str
struct in_str {
     const char *p;
#ifndef __U_BOOT__
     char peek_buf[2];
#endif
     int __promptme;
     int promptmode;
#ifndef __U_BOOT__
     FILE *file;
#endif
     int (*get) (struct in_str *);
     int (*peek) (struct in_str *);
};

能夠看出,函數指針get和peek就指向輸入方式,在初始化時:
i->peek = file_peek;
i->get = file_get;

函數file_peek和file_get就是實現輸入的函數
file_peek調用了fgetc,實現輸入,這個須要查看控制檯文件console.c
file_get調用get_user_input函數,get_user_input調用readline,又回到了方式一的輸入啦
具體內容請參看這幾個函數代碼
file_peek,file_get,get_user_input都在文件/u-boot-sunxi-sunxi/common/hush.c裏面



雖然初始化時將 i->get = file_get ,可是在哪使用呢???

初始化以後,parse_file_outer函數內調用主循環函數:
                 rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
將初始化以後的input傳給了主循環,主循環繼續調用
                 rcode = parse_stream(&temp, &ctx, inp, '\n'); 
將inp(即input)傳遞給parse_stream

在parse_stream函數內循環讀取輸入:
                while ((ch=b_getch(input))!=EOF) {
b_getch是一個宏: #define b_getch(input) ((input)->get(input))

實際就是 while ( ( ch = input->get(input) ) != EOF) {
如此以來就調用到了readline,而後調用關係:readline-->readline_into_buffer-->getc
getc再調用serial_getc,實現與串口輸入關聯,與主循環方式一相同。

二、執行命令

主循環經過代碼:
           run_list(ctx.list_head);
執行用戶輸入的命令。

run_list函數,在/u-boot-sunxi-sunxi/common/hush.c裏面:
static int run_list(struct pipe *pi)
{
      int rcode=0;
#ifndef __U_BOOT__
      if (fake_mode==0) {
#endif
          rcode = run_list_real(pi);
#ifndef __U_BOOT__
      }
#endif
/* free_pipe_list has the side effect of clearing memory
  * In the long run that function can be merged with run_list_real,
  * but doing that now would hobble the debugging effort. */
     free_pipe_list(pi,0);
     return rcode;
}

再調用run_list_real函數,在/u-boot-sunxi-sunxi/common/hush.c裏面:
static int run_list_real(struct pipe *pi)
{
    ......
      rcode = run_pipe_real(pi);
    .......
}

再調用run_pipe_real函數,在/u-boot-sunxi-sunxi/common/hush.c裏面:
static int run_pipe_real(struct pipe *pi)
{
      ......
     /* Process the command */
     return cmd_process(flag, child->argc, child->argv,
                                           &flag_repeat);
    ......
}
以上代碼粗一看,彷佛在實現管道,具體的不在本文分析,大體意思就是將收到的指令經過一系列處理加入一個執行列表,而後執行這個列表
還有一大堆的控制、出錯處理.......

最後,循環方式二也調用到cmd_process函數,這又與循環方式一相同。

3、兩種循環方式命令的執行方式

兩種循環方式執行命令的模式相同,都經過cmd_process函數進行,在/u-boot-sunxi-sunxi/common/command.c裏面:
在cmd_process中調用關係以下
一、cmd_process -> find_cmd-> find_cmd_tbl,經過用戶輸入找到須要執行的命令代碼
二、cmd_process -> cmd_call,執行指令代碼
這些函數都在command.c文件內,下面分析一下這幾個函數,就能夠看見執行一個命令的方式了:

enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
          int *repeatable)
{
     enum command_ret_t rc = CMD_RET_SUCCESS;
     cmd_tbl_t *cmdtp;

//參數argc,argv就是超級終端、minicom等軟件經過串口輸入的一行通過處理的命令
//例如:用戶輸入 nand write 50000000 10000 20000      
//將內存0x500000000的內容寫入nandflash,從nand地址0x10000開始,長度0x20000,下面都用這個例子來講明
//則通過前面的解析以後,結果以下:跟系統編程相同
//argc    = 5
//argv[0] = "nand"
//argv[1] = "write"
//argv[2] = "50000000"
//argv[3] = "10000"
//argv[4] = "20000"
  
     /* Look up command in command table */
     cmdtp = find_cmd(argv[0]);     //經過命令找到要執行的代碼,例子中的「nand」
     if (cmdtp == NULL) {           //沒有這個命令,退出,若隨便亂敲鍵盤,看到的就是這句話
           printf("Unknown command '%s' - try 'help'\n", argv[0]);
           return 1;
     }

     /* found - check max args */
     if (argc > cmdtp->maxargs)     //用戶輸入參數個數不能超過過命令的最大參數個數
           rc = CMD_RET_USAGE;

#if defined(CONFIG_CMD_BOOTD)
      /* avoid "bootd" recursion */
      else if (cmdtp->cmd == do_bootd) {
             if (flag & CMD_FLAG_BOOTD) {
                     puts("'bootd' recursion detected\n");
                     rc = CMD_RET_FAILURE;
              } else {
                      flag |= CMD_FLAG_BOOTD;
              }
      }
#endif

/* If OK so far, then do the command */
      if (!rc) {
               rc = cmd_call(cmdtp, flag, argc, argv);  //運行命令,再次將用戶的輸入傳遞給命令代碼
               *repeatable &= cmdtp->repeatable;
       }
       if (rc == CMD_RET_USAGE)
                rc = cmd_usage(cmdtp);
       return rc;
}


//下面這兩個函數負責查找要執行的命令
cmd_tbl_t *find_cmd (const char *cmd)
{
     int len = &__u_boot_cmd_end - &__u_boot_cmd_start;    //命令段的長度
     return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);   //從__u_boot_cmd_start開始日後搜索
}


到了這裏,就須要瞭解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;

extern    cmd_tbl_t    __u_boot_cmd_start;
extern    cmd_tbl_t    __u_boot_cmd_end;

還有三個重要的宏:
#define Struct_Section     __attribute__((unused, section(".u_boot_cmd"), \
                                              aligned(4)))

#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
       U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)
  
  
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
       U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,NULL)


舉個命令定義的例子,就用nand命令:

U_BOOT_CMD(
      nand, CONFIG_SYS_MAXARGS, 1, do_nand,
      "NAND sub-system",
      "info - show available NAND devices\n"
      "nand device [dev] - show or set current device\n"
      "nand read - addr off|partition size\n"
      "nand write - addr off|partition size\n"
      "    read/write 'size' bytes starting at offset 'off'\n"
      "    to/from memory address 'addr', skipping bad blocks.\n"
      "nand read.raw - addr off|partition [count]\n"
      "nand write.raw - addr off|partition [count]\n"
      "    Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
);
name       -> nand,當用戶輸入「nand」時,就會經過這個命令,找到要執行的函數「do_nand」
maxargs  -> CONFIG_SYS_MAXARGS,參數的最大個數,默認是256 
rep            -> 1,執行次數,通常是1次
cmd          -> do_nand,執行代碼,即do_nand是一個函數
usage       -> "NAND sub-system",用途
help           -> "......",後面一大堆就是幫助信息,提示用戶操做,輸入「nand help」能夠看到

這些字段將會按數據結構struct cmd_tbl_s的方式存儲,存儲在代碼段內,從下面兩個宏,能夠看出
#define Struct_Section     __attribute__((unused, section(".u_boot_cmd"), \
                                              aligned(4)))
#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
         U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)
能夠nand命令將以標號 __u_boot_cmd_nand進行編譯,編譯後,__u_boot_cmd_nand最後將以一個地址存在.u_boot_cmd段內,
只要找到這個地址,經過結構體 struct cmd_tbl_s,就能把全部的信息讀出來

.u_boot_cmd又是如何定義的呢??這就要看u-boot.lds這個文件了,打開\u-boot-sunxi-sunxi\arch\arm\cpu\u-boot.lds 
   ..........
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

. = ALIGN(4);
  ..........
  
看一下,原來__u_boot_cmd_start和 __u_boot_cmd_end直接定義在這裏,
因此用extern來引用,連接器在u-boot.lds中會找到這兩個指針
並且,全部經過U_BOOT_CMD定義的命令都存在.u_boot_cmd段內,就在__u_boot_cmd_start和__u_boot_cmd_end之間,
那麼須要搜索一個命令理所固然就應在這個範圍內尋找啦,再看下面的代碼,就能夠理解爲何這麼作了。

看看搜索的過程:調用方式  find_cmd_tbl(cmd, &__u_boot_cmd_start, len);

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;

  //參數cmd就是argv[0],即例子的「nand」
  //參數table就是命令列表的首地址,即&__u_boot_cmd_start
  //參數table_len就是命令列表的長度,搜索的範圍即&__u_boot_cmd_end - &__u_boot_cmd_start

     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);  //命令的長度,例如「nand」長度爲4

     for ( cmdtp = table;                            //從&__u_boot_cmd_start開始
             cmdtp != table + table_len;     //到&__u_boot_cmd_end結束
             cmdtp++) {                                 //按結構體struct cmd_tbl_t移動指針
                   if ( strncmp (cmd, cmdtp->name, len) == 0) {          //只須要比較命令名字是否相同
                          if (len == strlen (cmdtp->name))
                                   return cmdtp; /* full match */                               //徹底匹配的狀況,找到命令,直接返回

                          cmdtp_temp = cmdtp; /* abbreviated command ? */
                          n_found++;                                 //部分匹配的狀況,假設有一個命令「nand_check」並在在「nand」以前,就是這種狀況,繼續尋找
                    }
      }
      if (n_found == 1) {   /* exactly one match */   //找到一個部分匹配的命令,也能夠執行,所以,輸入「nand」、「nan」、「na」,均可能執行nand命令
              return cmdtp_temp;
      }

      return NULL; /* not found or ambiguous command */ //沒找到或者找到多個部分匹配的命令,則不執行
}

//執行一個命令,直接調用這個命令內的函數指針指向的函數
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
      int   result;

     result = (cmdtp->cmd)(cmdtp, flag, argc, argv);  //對於「nand」命令,就是do_nand(cmdtp, flag, argc, argv);
     if (result)
          debug("Command failed, result=%d", result);
     return result;
}

說了一大堆,基本將主循環輸入和執行一條命令的方式描述了一遍,很難說得面面俱到

下面舉例說說如何在U-boot內加入本身的命令

一、在\u-boot-sunxi-sunxi\common下創建一個文件,就叫cmd_my_command.c
二、修改\u-boot-sunxi-sunxi\common\Makefile,在command那一段加一句:
       COBJS-$(CONFIG_CMD_MY_COMMAND) += cmd_my_command.o
   
三、在\u-boot-sunxi-sunxi\include\config_cmd_all.h末尾加一句,使編譯生效
       #define CONFIG_CMD_MY_COMMAND     

四、文件cmd_my_command.c內容:
   
#include <common.h>     //頭文件必須包含
#include <command.h>    //頭文件必須包含

//命令函數,按照u-boot慣例,將函數名寫成do_xxxxx
int do_test(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
      int i;
  
      printf("this is a test command\n");
      printf("argc = %d\n", argc);
      for(i = 0; i < argc; i++){
           printf( "argv[%d] = %s\n", i, argv);
      }
      return 0;
}
//定義命令  
U_BOOT_CMD(
     test, CONFIG_SYS_MAXARGS, 1, do_test,
     "test my test command",
    "test [anything]\n"
);  

五、編譯u-boot,寫入SD啓動卡,上電啓動之

輸入
test 1 a b

輸出:

argc = 4
argv[0] = test
argv[1] = 1
argv[2] = a
argv[3] = b


原文做者:andy
原文連接:http://forum.cubietech.com/forum.php?mod=viewthread&tid=153&extra=page%3D1php

相關文章
相關標籤/搜索