使用uboot命令
(1)uboot启动后进入命令行环境下,在·此输入命令按回车结束,uboot会收取这个命令然后解析,然后执行。
uboot命令体系实现代码在哪?
(1)uboot1命令体系的实现代码在uboot/common/cmd_xxx.c中。有若干个.c文件和命令体系有关。(还有commmand.c main.c也是和命令有关的)
每个命令对应一个函数
(1)每一个uboot的命令背后都对应一个函数,这就是uboot实现命令体系的一种思路和方法。这个东西和我们在裸机十六部分shell中实现shell命令的方法是一样的。
(2)我们要找到每一个命令背后所对应的那个函数,而且要分析这个函数和对应命令是怎样对应起来的。
命令参数以argc&argv传给函数
(1)有些uboot的命令还支持传递参数。也就是说命令背后对应的函数接收的参数列表中有argc和argv,然后命令体系会把我们执行命令时的命令+参数(md30000000 10)以argc(3)和argv(argv[0] = md,argv[1] = 30000000 ,argv[2] = 10)的方式传递给执行命令的函数。
举例:以help命令为例:help命令背后对应的函数名叫:do_help。在uboot/common/command.c的236行。
函数原型:int do_help (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
从main_loop说起
(这个函数在board.c文件中)
(1)uboot启动的第二阶段,在初始化了所有的该初始化的东西后,进入了一个死循环,死循环的循环体就是main_loop函数。
(2)main_loop函数执行一遍,就是一个获取命令,解析命令,执行命令的全过程
//实现开机倒数 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
(3)run_command函数就是用来执行命令的函数(把命令丢在这个里面就好了)
//函数原型 run_command (const char *cmd, int flag)
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)执行命令。最后用函数指针的方式调用执行了对应的函数。
//查找命令原型 if ((cmdtp = find_cmd(argv[0])) == NULL) { printf ("Unknown command '%s' - try 'help'\n", argv[0]); rc = -1; /* give up after bad command */ continue; }
//执行命令 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { rc = -1; }
思考:关键点在于find_cmd函数如何查找到这个命令是不是uboot的合法支持的命令?这取决于uboot的命令体系机制(uboot是如何完成命令的这一套设计的,命令如何去注册,存储,管理,索引。)
可能的管理方式
(1)数组,结构体数组,数组中每一个结构体成员就是一个命令的所有信息
(2)链表,链表的每个节点data段就是一个命令的结构体,所有的命令都放在一条链表上。这样就解决了数组方式的不灵活。坏处是需要额外的内存开销,然后各种算法(遍历,插入,删除)需要一定的复杂度的代码执行。
(3)有第三种嘛?uboot没有使用数组或者链表,而是使用了一种新的方式,实现这个效果。
命令结构体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 };
(1)name:命令的名字
(2)maxargs:命令最多可以接收多少个参数
(3)repeatable:指示这个命令是否可以重复执行。重复执行是一种工作机制,就是直接按回车则执行上一条执行的命令。
(4)cmd:函数指针,命令对应的函数的函数指针,将来执行这个命令的函数时,使用这个函数来调用这个函数。
(5)usage:命令的短帮助信息,命令的简单描述
(6)help:命令的长帮助信息,详细的描述信息。
(7)complete:函数指针,指向这个命令的自动补全的函数。
总结:uboot的命令体系在工作时,一个命令对应一个cmd_tbl_t结构体的一个实例,然后uboot支持多少个命令,就需要多少个结构体实例。uboot的命令体系把这些结构体实例管理起来,当用户输入一个命令时,uboot会去这些结构体实例中查找(查找方法和存储管理的方法有关)。如果找到则执行命令。如果未找到则提示命令未知。
uboot实现命令管理的思路
(1)填充1个结构体实例构成一个命令
(2)给命令结构体实例附加特定段属性(用户自定义段),链接时将带有该段属性的内容链接在一起排列(挨着的,不会夹杂其他东西。也不会丢掉一个带有这种段属性的,但是顺序是乱序的)。
(3)uboot重定位时该段整段加载到DDR中。加载到DDR中的uboot镜像中带有特定段属性的这一段其实就是命令结构体的集合,有点像一个命令结构体数组。
(4)段起始地址和结束地址(链接地址,定义在u-boot.lds中)决定了这些命令集的开始和结束地址。
找到对应的宏定义
#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} //cmd_tbl_t 结构体的类型 //__u_boot_cmd_结构体的名字 //Struct_Section 定义的宏 //这里的##name会被上面的name所替代,而且##是我们的gcc的扩展的部分 //定义的另一个宏 #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))= {#name, maxargs, rep, cmd, usage, help} //__attribute__是属性,u_boot_cmd这个是给我们的段赋予该有的段属性
总结:这个U_BOOT_CMD宏的理解,关键在于结构体变量的名字和段属性。名字使用##连字符,连接起来。附加了用户自定义段属性,以保证链接时将这些数据结构链接在一起排布!
(2)链接脚本
find_cmd函数详解
(1)find_cmd函数的任务是从当前的uboot的命令集中查找是否有某个命令。如果找到则返回这个命令的结构体指针,否则返回null
(2)函数的实现思路很简单,如果不考虑命令带点的情况(md.b md.w这种)就更简单了。查找命令的思路其实就是for循环,遍历数组,不同的是数组的起始地址和结束地址使用地址值来给定的,数组中的元素个数是结构体变量类型。
U_BOOT_SMD宏详解
(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来验证传参
新建一个.c文件,在其中添加自定义命令
(1)在uboot/common目录下新建一个命令文件,叫cmd_aston.c(对应的命令名就叫aston,对应的函数就叫do_aston函数),然后在c文件中添加命令对应的U_BOOT_CMD宏和函数。注意头文件包含不要漏掉。
(2)在uboot/common/Makefile中添加上aston.o,目的是让Make在编译时能否把cmd_aston.c编译链接进去。
COBJS -y += cmd_aston.o
(3)重新编译烧录,重新编译步骤是:make distclean;make x210_sd_config;make ;
uboot命令体系的优点
(1)uboot的命令体系本身稍微复杂,但是他写好之后就不用动了。我们后面再移植uboot时也不会去动uboot的命令体系。我们最多就是向uboot中去天骄命令,就像本次所说的一样。
(2)在uboot中添加命令非常简单。
//自己创建的新的命令aston,其中包含3个参数 #include <common.h> #include <command.h> int do_aston (cmd_tbl_t *cmdtp,int flag, int argc, char *argv[]) { int i = 0; printf("\n%s\n","cmd_aston,this is a test for add cmd to uboot."); printf("argc = %d.\n",argc); for(i=0;i<argc;i++) { printf("argv[%d] = [%s].\n",i,argv[i]); } return 0; } U_BOOT_CMD( aston, 3, 1, do_aston, "mycmd - usage of my cmd\n", "long help of mycmd\n" );