首先U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)解析
一堆的宏定义最终的目标就是定义一个结构体并且定义一个section(段),

引入lds链接脚本这个东西:
我是用的是xilinx的板子,所以链接脚本的目录在: bootloader源码/arch/arm/mach-zynq/u-boot.lds
先看下这个文件内容:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  //输出格式 
OUTPUT_ARCH(arm)		//架构声明
ENTRY(_start)			//指明程序入口	
SECTIONS				//声明段的关键字
{
	. = 0x00000000;   	// . 表示当前地址  也就说明了单板是从0x0地址启动执行的

	. = ALIGN(4);	  	// 四字节对其
	.text :				//声明代码段
	{
		*(.__image_copy_start)		//代码端内部的排列顺序 首先是__image_copy_start的代码端部分
		*(.vectors)					//向量表的代码端部分
		CPUDIR/start.o (.text*)		//start.o的代码端部分
		*(.text*)					//*通配符 其他所有文件的代码端部分
	}

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //read only data 只读数据段

	. = ALIGN(4);
	.data : {													//所有文件数据段
		*(.data*)
	}
	还有很多省略了
}	

可以看到SECTIONS{  ...... }中间定义了很多个段, 代码段数据段 只读段等等。接下来看下U_BOOT_CMD的定义和解析

path : bootloader源码/include/command.h

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\
	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

typedef struct cmd_tbl_s	cmd_tbl_t;

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
	ll_entry_declare(cmd_tbl_t, _name, cmd) =			\
		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
						_usage, _help, _comp);

//声明段和组装结构体名
#define ll_entry_declare(_type, _name, _list)				\
	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))

//组装结构体成员
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
				_usage, _help, _comp)			\
		{ #_name, _maxargs, _rep, _cmd, _usage,			\
			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }

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
};
//以上是定义==================================================================================

/* 
    把他们放到一个文件里慢慢展开就会发现定义了一个结构 这个结构作为一个段
    段的名称由 cmd 和name组成
    通过__attribute__ 指定属性声明一个段
    section(".u_boot_list_2_"#cmd"_2_"#name)
 */

struct cmd_tbl_s _u_boot_list_2_cmd_2_name __aligned(4)
	__attribute__((unused, section(".u_boot_list_2_"#cmd"_2_"#name))
{
	.name 		= _name,        //cmd的命令 像reset 等
	.maxargs 	= _maxargs,     //最大参数个数
	.repeatable = _rep,         //回车重复   
	.cmd 		= _cmd,        //这是个函数指针,也是cmd命令的功能实现
	.uage		= _usage,        //短的提示信息
	.help 		= _help,        //长的详细的帮助信息
	.complete	= _complete,    //传入的是NULL U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

}

所以U_BOOT_CMD就是定义了一个段而已。

ok如何使用这个U_BOOT_CMD定义一个命令: 

在/cmd/目录下有个makefile

添加自己的文件 到makefile 就是编译的时候带上;

也可以使用宏 配置 obj -$(CONFIG_CM_HELLO) += hellocmd.o 这里的宏一定要用$和括号修饰语法如此;CONFIG_CM_HELLO可以定义在uboot的配置文件里;

//创建新文件实现命令: /cmd/hello_cmd.c



#include <common.h>
#include <bootm.h>
#include <command.h>
#include <environment.h>
#include <errno.h>
#include <image.h>
#include <malloc.h>
#include <nand.h>
#include <asm/byteorder.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <u-boot/zlib.h>

#include <dm.h>
#include <i2c.h>
#include <dm/lists.h>
#include <dm/root.h>


static int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int ret = 0;
	printf(" enter do_hello \n");
	return ret;
}

//最大参数 1  回车不生效
U_BOOT_CMD(
	hello, 1, 0,	do_hello,
	"Perform hello ",
	"\n"
	" - print brief description of all commands\n"
	"help command ...\n"
	" - print detailed usage of 'command'"
);

编译后可以查看自己的段有没有生成: 如下

效果如下: 

table: 可以联想出我的命令

敲击后执行我定义的函数

 

 第二部分如何在uboot中写一个驱动并调用:

驱动也是一样定义的一个段而已,通过两个宏定义(UCLASS_DRIVERU_BOOT_DRIVER);

path:        include\dm\device.h    include\linker_lists.h  

                include\dm\uclass.h   include\linker_lists.h

//path      include\dm\device.h    include\linker_lists.h
/* Declare a new U-Boot driver */
#define U_BOOT_DRIVER(__name)						\
	ll_entry_declare(struct driver, __name, driver)

#define ll_entry_declare(_type, _name, _list)				\
	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))

struct driver {
	char *name;
	enum uclass_id id;
	const struct udevice_id *of_match;
	int (*bind)(struct udevice *dev);
	int (*probe)(struct udevice *dev);
	int (*remove)(struct udevice *dev);
	int (*unbind)(struct udevice *dev);
	int (*ofdata_to_platdata)(struct udevice *dev);
	int (*child_post_bind)(struct udevice *dev);
	int (*child_pre_probe)(struct udevice *dev);
	int (*child_post_remove)(struct udevice *dev);
	int priv_auto_alloc_size;
	int platdata_auto_alloc_size;
	int per_child_auto_alloc_size;
	int per_child_platdata_auto_alloc_size;
	const void *ops;	/* driver-specific operations */
	uint32_t flags;
};

//include\dm\uclass.h   include\linker_lists.h

/* Declare a new uclass_driver */
#define UCLASS_DRIVER(__name)						\
	ll_entry_declare(struct uclass_driver, __name, uclass)

#define ll_entry_declare(_type, _name, _list)				\
	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))

struct uclass_driver {
	const char *name;
	enum uclass_id id;
	int (*post_bind)(struct udevice *dev);
	int (*pre_unbind)(struct udevice *dev);
	int (*pre_probe)(struct udevice *dev);
	int (*post_probe)(struct udevice *dev);
	int (*pre_remove)(struct udevice *dev);
	int (*child_post_bind)(struct udevice *dev);
	int (*child_pre_probe)(struct udevice *dev);
	int (*init)(struct uclass *class);
	int (*destroy)(struct uclass *class);
	int priv_auto_alloc_size;
	int per_device_auto_alloc_size;
	int per_device_platdata_auto_alloc_size;
	int per_child_auto_alloc_size;
	int per_child_platdata_auto_alloc_size;
	const void *ops;
	uint32_t flags;
};

定义一个新文件:   drivers\misc\hello_cj.c

修改Makefile: drivers\misc\Makefile (添加 obj-y = hello_cj.o)吧我们定义的文件编译上

UCLASS_DRIVER 主要作用是供给调用的

U_BOOT_DRIVER 主要作用是供给驱动功能实现的

下面是个使用设备树匹配的一个例子(当然也可以自己定义设备):

#include <common.h>
#include <linux/err.h>
#include <dm.h>
#include <asm/gpio.h>
#include <hello_cj.h>

static int simple_panel_enable_backlight(struct udevice *dev)
{
	printf("enter hello start simple_panel_enable_backlight \n");
    //驱动实现部分
	return 0;
}

static struct hello_cj_ops simple_panel_ops = {
	.hello_start	= simple_panel_enable_backlight,
};

static const struct udevice_id hello_cj_std_dts[] = {
	{ .compatible = "hello_dts"},
	{ }
};

U_BOOT_DRIVER(hello_drv) = {
	.id			= UCLASS_HELLO_CJ,
	.name		= "hello_drv",
	.of_match	= hello_cj_std_dts,
	.ops 		= &simple_panel_ops
};
	
UCLASS_DRIVER(hello_cj_class) = {
	.id 	= UCLASS_HELLO_CJ,
	.name		= "hello_cj_class",
};

在根节点下添加如下子节点: 使用 "hello_dts"匹配

	hello_dts_name {
		compatible = "hello_dts";
		reg = <0x31>;
	};

之后我们开始编译u-boot,查看我们添加的段是否都存在 

存在之后我们可以在cmd中调用我们定义的驱动:

需要将上面的hello_cmd.c 重新添加新功能不仅仅是打印信息了:

这个文件里定义了调用驱动的接口: include\dm\uclass.h

可以看到整个是通过 class_id  和 驱动结构调用
int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv,
				struct udevice **devp);

直接通过class——id 调用  第一个设备(查找到第一个匹配的设备就返回)
int uclass_first_device(enum uclass_id id, struct udevice **devp);

通过id 和 名字来查找
int uclass_get_device_by_name(enum uclass_id id, const char *name,
			      struct udevice **devp);

我是使用的第一个方式:


#include <common.h>
#include <bootm.h>
#include <command.h>
#include <environment.h>
#include <errno.h>
#include <image.h>
#include <malloc.h>
#include <nand.h>
#include <asm/byteorder.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <u-boot/zlib.h>

#include <dm.h>
#include <i2c.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <hello_cj.h>

static int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int ret = 0;
	printf("enter do_hello \n");

	struct udevice *dev;
	const struct hello_cj_ops *ops;
	ret = uclass_get_device_by_driver(UCLASS_HELLO_CJ,
					  DM_GET_DRIVER(hello_drv), &dev);
	if (ret) {
		  printf("%s: no misc-device found\n", __func__);
		  return 0;
	}
	
	printf("uclass_get_device_by_driver name:%s \n", dev->name);
	printf("driver name:%s id:%d\n", dev->driver->name, dev->driver->id);
	ops = dev->driver->ops;
	if(!ops->hello_start)
	{
		printf("ops->hello_start is NULL \n");
		return 0;
	}

	ops->hello_start(dev);
	printf(" do_hello end \n");
	return ret;
}

U_BOOT_CMD(
	hello, 1, 0,	do_hello,
	"Perform hello ",
	"\n"
	" - print brief description of all commands\n"
	"help command ...\n"
	" - print detailed usage of 'command'"
);

这个宏是获取对应段的地址:

\include\linker_lists.h

/* Get a pointer to a given driver */
#define DM_GET_DRIVER(__name)                        \
    ll_entry_get(struct driver, __name, driver)

#define ll_entry_get(_type, _name, _list)                \
    ({                                \
        extern _type _u_boot_list_2_##_list##_2_##_name;    \
        _type *_ll_result =                    \
            &_u_boot_list_2_##_list##_2_##_name;        \
        _ll_result;                        \
    })

之后编译调用hello

这就完成了对驱动的调用; 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐