uboot中添加自定义命令和驱动
首先U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)解析一堆的宏定义最终的目标就是定义一个结构体并且定义一个section(段),引入lds链接脚本这个东西:我是用的是xilinx的板子,所以链接脚本的目录在: bootloader源码/arch/arm/mach-zynq/u-boot.lds先看下这个文件内容:OUTPUT_FORMA
首先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_DRIVER, U_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
这就完成了对驱动的调用;
更多推荐
所有评论(0)