1、代碼位置
(1)uboot命令體系的實現代碼在uboot/common/cmd_xxx.c中。有若干個.c文件和命令體系有關。(還有command.c main.c也是和命令有關的)
2、傳參方式
命令參數以argc&argv傳給函數
(1)有些uboot的命令還支持傳遞參數。也就是說命令背后對應的函數接收的參數列表中有argc和argv,然后命令體系會把我們執行命令時的命令+參數(md 30000000 10)以argc(3)和argv(argv[0]=md, argv[1]=30000000 argv[2]=10)的方式傳遞給執行命令的函數。
3、uboot命令解析和執行過程分析
(1)uboot啟動的第二階段,在初始化了所有該初始化的東西后,進入了一個死循環,死循環的循環體就是main_loop。
(2)main_loop函數執行一遍,就是一個獲取命令、解析命令、執行命令的過程。
(3)run_command函數就是用來執行命令的函數。
關鍵點分析
(1)控制台命令獲取
(2)命令解析。parse_line函數把"md 30000000 10"解析成argv[0]=md, argv[1]=30000000 argv[2]=10;
(3)命令集中查找命令。find_cmd(argv[0])函數去uboot的命令集合當中搜索有沒有argv[0]這個命令,
(4)執行命令。最后用函數指針的方式調用執行了對應函數。
3、存儲方式
uboot沒有使用數組或者鏈表,而是使用了一種新的方式來實現這個功能。指定起始地址與結束地址,將命令依次排布(在鏈接腳本中實現。)
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
命令結構體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 *[]); char *usage; /* Usage message (short) */ #ifdef CFG_LONGHELP char *help; /* Help message (long) */ #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;
(1)name:命令名稱,字符串格式。
(2)maxargs:命令最多可以接收多少個參數
(3)repeatable:指示這個命令是否可重復執行。重復執行是uboot命令行的一種工作機制,就是直接按回車則執行上一條執行的命令。
(4)cmd:函數指針,命令對應的函數的函數指針,將來執行這個命令的函數時使用這個函數指針來調用。
(5)usage:命令的短幫助信息。對命令的簡單描述。
(6)help:命令的長幫助信息。細節的幫助信息。
(7)complete:函數指針,指向這個命令的自動補全的函數。
總結:uboot的命令體系在工作時,一個命令對應一個cmd_tbl_t結構體的一個實例,然后uboot支持多少個命令,就需要多少個結構體實例。uboot的命令體系把這些結構體實例管理起來,當用戶輸入了一個命令時,uboot會去這些結構體實例中查找(查找方法和存儲管理的方法有關)。如果找到則執行命令,如果未找到則提示命令未知。
4、uboot實現命令管理的思路
(1)填充1個結構體實例構成一個命令
(2)給命令結構體實例附加特定段屬性(用戶自定義段),鏈接時將帶有該段屬性的內容鏈接在一起排列(挨着的,不會夾雜其他東西,也不會丟掉一個帶有這種段屬性的,但是順序是亂序的)。
(3)uboot重定位時將該段整體加載到DDR中。加載到DDR中的uboot鏡像中帶有特定段屬性的這一段其實就是命令結構體的集合,有點像一個命令結構體數組。
(4)段起始地址和結束地址(鏈接地址、定義在u-boot.lds中)決定了這些命令集的開始和結束地址。
uboot命令定義具體實現分析
(1)U_BOOT_CMD宏基本分析
這個宏定義在uboot/common/command.h中。
U_BOOT_CMD( version, 1, 1, do_version, "version - print monitor version\n", NULL ); 這個宏替換后變成: cmd_tbl_t __u_boot_cmd_version __attribute__ ((unused,section (".u_boot_cmd"))) = {#name, maxargs, rep, cmd, usage, help}
總結:這個U_BOOT_CMD宏的理解,關鍵在於結構體變量的名字和段屬性。名字使用##作為連字符,附加了用戶自定義段屬性,以保證鏈接時將這些數據結構鏈接在一起排布。
5、.uboot中增加自定義命令
1、在已有的c文件中直接添加命令
(1)在uboot/common/command.c中添加一個命令,叫:mycmd
(2)在已有的.c文件中添加命令比較簡單,直接使用U_BOOT_CMD宏即可添加命令,給命令提供一個do_xxx的對應的函數這個命令就齊活了。
(3)添加完成后要重新編譯工程(make distclean; make x210_sd_config; make),然后燒錄新的uboot去運行即可體驗新命令。
(4)還可以在函數中使用argc和argv來驗證傳參。
2、自建一個c文件並添加命令
(1)在uboot/common目錄下新建一個命令文件,叫cmd_aston.c(對應的命令名就叫aston,對應的函數就叫do_aston函數),然后在c文件中添加命令對應的U_BOOT_CMD宏和函數。注意頭文件包含不要漏掉。
(2)在uboot/common/Makefile中添加上aston.o,目的是讓Make在編譯時能否把cmd_aston.c編譯鏈接進去。
(3)重新編譯燒錄。重新編譯步驟是:make distclean; make x210_sd_config; make
3、體會:uboot命令體系的優點
(1)uboot的命令體系本身稍微復雜,但是他寫好之后就不用動了。我們后面在移植uboot時也不會去動uboot的命令體系。我們最多就是向uboot中去添加命令,就像本節課所做的這樣。
(2)向uboot中添加命令非常簡單。
#include <common.h> #include <command.h> int do_mycmd (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( mycmd, 1, 1, do_mycmd, "version - print monitor version\n", NULL );