8、1、uboot的命令体系

1、使用uboot命令

2、uboot实现命令体系的代码uboot/commond/cmd_xxx.c中,有若干个.c文件和命令体系有关。还有个main.c也是跟命令体系有关的。

3、每个命令对应一个函数

(1)uboot中的每一个命令都对应了一个函数在背后,这就是uboot来实现命令体系的一种方法。和我们在裸机中写的shell中实现的方法其实是一样的。

(2)我们要找到每一个命令背后所对应的那个函数,而且要分析每一个函数和每一个命令是怎么对应起来的。

4、命令的参数以argc&argv的方式来传递给函数

(1)有些uboot的命令还支持传参,也就是说命令背后对应的函数接受的参数列表中有argc和argv这两个参数。然后命令体系会吧我们执行命令时的命令加参数,以argc和argv的方式传递给执行命令的函数。

(2)run_command函数就是用来执行命令的函数


8、2、uboot命令解析和执行过程分析

1、从main_loop函数说起

(1)我们在uboot的第二阶段,也就是start_armboot这个函数在初始化了一些板上的硬件后,最后进入到了死循环,这个死循环不断的执行一个函数叫做main_loop,这个函数的其实就是在不断获取我们输入到uboot命令行中的命令,解析这个命令,执行这个命令。直到执行了bootm 这个命令去启动内核。


8、3、run_command函数详解

1、控制命令获取



2、解析命令

(1)paser_line函数用来解析,如将(md 0x30001000 10)把argv[0] = "md" argv[1] = "0x30001000" argv[2] = "10"


3、命令集中查找命令

(1)find_cmd(argv[0])函数就是去命令集合找有没有这个argv[0]中的那个命令。


4、执行命令

(1)最后用函数指针的形式调用了这个命令所对应的函数


总结:在获取命令和解析命令和执行命令的过程中,最关键的部分就是find_cmd这个函数是如何通过我们输入的命令找到这个在uboot命令体系中的命令的。同时,关键的还有,uboot是如何注册一个命令的,存储,管理,索引一个命令的,索引的过程就是find_cmd这个查找的过程。


8、4、uboot如何处理命令集1

1、可能的管理方式

(1)数组:比如可以用结构体数组的方式,数组中的每一个元素都是一个结构体,每一个结构体表示一个命令的所有信息

(2)链表:比如也可以用链表,链表的每一个节点的data,就是一个命令的结构体,这个结构体中包含了命令的所有信息。坏处是内存开销变大了,因为需要存放头指针和尾指针,并且代码的复杂度变大了,程序运行的效率因此会有所降低,但是灵活性相当于数组来说,却好了很多,因为数组的大小都是直接给定的,不灵活。

(3)有第三种吗?uboot实际上没有用前两种的方式来实现这个命令体系。而是用了一种新的方式来实现了这个功能。


2、命令结构体cmd_tpl_t

struct cmd_tbl_s {

char *name; /* Command Name */

int maxargs; /* maximum number of arguments */

int repeatable; /* autorepeat allowed? 1:OK 0:NO *///命令输完后回车一遍后,在回车可以继续上一次的命令

/* 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:命令是否能够重复执行,就是直接按回车能否执行上一次输入的命令

(4)cmd:函数指针,命令所对应的那个函数的函数指针。

(5)usage:使用信息,简单的使用信息说明

(6)help:使用信息,详细的使用信息说明

(7)complete:函数指针,指向这个命令的自动补全函数

总结:uboot的命令体系在工作的时候,一个命令对应一个这个cmd_tbl_t结构体的实例(实例化),uboot需要多少个命令就需要多少个cmd_tbl_t这个结构体的实例。uboot的命令体系把这些结构体实例管理起来,当用户输入一个命令的时候,uboot会去这些结构体实例中查找(查找方法和管理方法有关)。如果找到则执行这个实例当中的cmd函数指针去执行命令。如果没有找到则提示命令未知。


3、uboot实现命令管理的思路

(1)填充一个结构体实例构成一个命令

(2)给命令结构体附加一个特定的段属性(用户自定义段),链接时链接器就会将有该段属性的内容链接在一起排列(挨着的,不会夹杂着其他东西,也不会丢掉任何一个带有这种段属性的,但是顺序是乱序的)。(链接完成后,最后都会在u-boot.bin这个镜像中)。

(3)uboot在重定位的时候,将该段属性整片加载到DDR中。(其实分析开来,加载到DDR中的uboot镜像的带有特定段属性的这一段是一个命令结构体的集合,有点像一个结构体数组,不一样的是,我们在写完这些结构体实例后,写了多少个结构体实例,链接器就会链接多少个,最后形成镜像后,我们直接重定位到DDR中,这样就弥补了数组的不灵活性)

(4)段起始地址和结束地址(链接地址,定义在u-boot.lds中)决定了这些命令的开始和结束地址。



8、5、uboot如何处理命令集2


1、1、uboot命令定义具体实现分析

1、1、1、U_BOOT_CMD宏基本分析

1、这个宏在uboot/common/commond.h中的93行到99行定义


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


(1)这个宏接接收六个参数。宏体cmd_tbl_t是命令的结构体类型,用这个类型定义了一个变量叫__u_boot_cmd_##name

其中##name,是gcc支持的,##name将来会被宏传递进去的name这个参数替代,也就是##name就是name这个参数。

(2)Struct_Section 是由这个宏定义的

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

(2(1))_attribute__ ((unused,section (".u_boot_cmd")))是gcc中的一种扩展语法,给赋予属性,目的就是给前面的变量赋予一个段属性(.u_boot_cmd)就是把##name这个变量放在了这个.u_boot_cmd这个段上。

(3) = {#name, maxargs, rep, cmd, usage, help} 就是将这个被放在用户自定义段(.u_boot_cmd)的结构体变量##name进行初始化,值就是宏传参过来的六个参数。

其中的参数有一个#name,这个参数前面有个#号的意思就是,将传进来的东西转换成字符串,因为结构体成员的第一个是一个字符串。

(4)链接脚本:链接脚本中,有这个段的起始地址和结束地址。在u-boot.lds中


总结:这个宏的最终实现的就是填充一个命令,并且将这个命令附加了一个段属性。将来属于这同一属性的东西,链接器在链接的时候,就会链接在一起进行排列。


1、1、2、find_cmd函数详解

(1)find_cmd函数的任务就是从uboot的命令集中,查找是否有这个命令。如果找到,就返回这个命令结构体的指针,如果没找到,返回NULL。

(2)函数中的,__u_boot_cmd_start,是在commond.h中声明的,是在u-boot.lds中定义的。

cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;

意思就是将命令集在DDR中的链接处的首地址给cmdtp_temp指针,让这个指针指向这个地址。

(3)这个函数实现查找命令的思路就是,使用了一个for循环的方式,知道命令集的链接时的首地址,命令集链接时的结束地址是这个for循环的终止条件,这个命令结构体++,来查询这一段命令集的所有命令,看哪一个名字和长度是相同的。,找到后返回这个命令的结构体指针。




8、6、uboot中自定义命令

1、在已有的c文件中直接添加命令

(1)在uboot/common/command.c中添加

在已有的.c文件中添加命令比较简单,直接使用U_BOOT_CMD宏添加就行,相应的我们也要为我们添加的命令,添加一个对应的命令所以执行的函数do_xxx。

(2)添加完了要重新make distclean,和配置编译工程,在烧录u-boot.bin,运行就可以了查找这个命令了

(3)也可以用argc和argv来验证函数的传参,你也可以根据这些传参做一些应该做的事情



2、uboot中自建c文件添加一个命令

(1)在uboot/common目录下新建一个.c文件,名字以cmd_为开头,如果你的文件的名字叫做cmd_why.c,那么将来对应的命令名就叫做why,对应的函数就叫do_why函数(这是一种命名规范,别人添加命令的时候,是这样的,所以我们也要按照这样的方式去添加),然后在c文件中添加U_BOOT_CMD宏和对应的函数。注意:头文件要包含。

(2)还要在uboot/common中的makefile中添加这个.o文件,目的是就是让我们写的文件能够在makefile中编译,能链接进去

(3)重新make distclean , make x210_sd_config , make