0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

描述u-boot驱动模型的数据结构

电子工程师 来源:嵌入式小生 作者:iriczhao 2022-08-08 14:52 次阅读


一、描述u-boot驱动模型的数据结构

u-boot有一个功能强大的驱动模型,这一点与linux内核一致。驱动模型对设备驱动相关操作做了一个抽象:使用uclass来描述设备类,使用driver来描述驱动,使用udevice来描述设备。

(1-1)uclass

uclass表示以相同特征方式运行的一组device。uclass提供一种以相同接口方式访问组内单个设备的方式。例如:GPIO类提供了get/set值的操作。一个I2C类可能有10个I2C端口,其中4个用于一个驱动程序,6个用于另一个驱动程序。

该结构由struct uclass表示(/include/dm/uclass.h):

structuclass{
void*priv;//这个类的私有数据
structuclass_driver*uc_drv;//类本身的驱动程序,不要与struct driver混淆。
structlist_headdev_head;//该类中的设备列表(当设备的绑定方法被调用时,它们会被附加到它们的类上)。
structlist_headsibling_node;//类链表中的下一个类。
};

(1-2)driver

用于提供与外设交互的高级接口。本文将分析这一点。

(1-3)udevice

与特定端口或外围设备绑定的驱动程序实例

二、声明驱动

通过分析u-boot的/drivers目录下的文件可以得出u-boot驱动程序具有共同的特征,驱动程序声明一般具有如下类似的结构(参见drivers/demo/demo-shape.c):

staticconststructdemo_opsshape_ops={
.hello=shape_hello,
.status=shape_status,
};

U_BOOT_DRIVER(demo_shape_drv)={
.name="demo_shape_drv",
.id=UCLASS_DEMO,
.ops=&shape_ops,
.priv_data_size=sizeof(structshape_data),
};

例如上述代码所示,首先会创建一个xxx_ops结构,该结构与具体的设备驱动相关。然后使用U_BOOT_DRIVER宏将其声明为u-boot驱动。

在U_BOOT_DRIVER中,还可以指定用于绑定和解绑定的方法,这些方法会在适当的时候被调用。对于大多数驱动程序来说,一般只会使用到“probe”和“remove”方法。

设备驱动可以提供的方法记录在device.h头文件中:

structdriver{
char*name;//设备名称。
enumuclass_idid;//指示该驱动属于哪个uclass。
conststructudevice_id*of_match;//要匹配的兼容字符串列表,以及每个字符串的标识数据。
int(*bind)(structudevice*dev);//调用该函数将设备绑定到其驱动程序。
int(*probe)(structudevice*dev);//用于探测设备,即激活设备。
int(*remove)(structudevice*dev);//调用该函数来移除一个设备。
int(*unbind)(structudevice*dev);//调用该函数来解除设备与驱动程序的绑定。
int(*ofdata_to_platdata)(structudevice*dev);//在probe之前调用该函数以解码设备树数据。
int(*child_post_bind)(structudevice*dev);//在一个新子设备被绑后调用。
int(*child_pre_probe)(structudevice*dev);//在probe子设备之前调用。设备已经分配了内存,但还没有被probe到。
int(*child_post_remove)(structudevice*dev);//在子设备被移除后调用。设备已经分配了内存,但是它的device remove()方法已经被调用。
intpriv_auto_alloc_size;//如果非零,这是在设备的->priv指针中分配的私有数据的大小。如果为零,则驱动负责分配所需的数据。

//如果非零,这是在设备的->platdata中分配的平台数据的大小。这通常只对设备树驱动有用(那些有of_match的驱动),因为使用平台数据的驱动会在U_BOOT_DEVICE()实例化中提供数据。
intplatdata_auto_alloc_size;

//每个设备都可以保存其父设备拥有的私有数据。如果这个值非零,这个将被自动分配。
intper_child_auto_alloc_size;

//bus存储关于它的子设备的信息。如果非零,该数值则是数据的大小,将分配在子进程的parent_platdata指针指向的区域中。
intper_child_platdata_auto_alloc_size;

//驱动特殊操作。这通常是一个由驱动定义的函数指针列表,用于实现类(uclass)所需要的驱动函数。
constvoid*ops;

//驱动标志。
uint32_tflags;
};

在u-boot中,让一个设备工作的顺序是:

a1fd7346-16bf-11ed-ba43-dac502259ad0.png

U_BOOT_DRIVER宏创建了一个可从C访问的数据结构,因此驱动模型可以找到可用的驱动程序。下文将分析该宏的具体实现。

三、U_BOOT_DRIVER宏分析

U_BOOT_DRIVER宏定义在/include/dm/device.h文件中:

/*DeclareanewU-Bootdriver*/
#defineU_BOOT_DRIVER(__name)
ll_entry_declare(structdriver,__name,driver)

ll_entry_declare同样是一个宏定义,用于声明链接器生成的数组项,定义在/include/linker_lists.h中:

#definell_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)))

  • _type:条目的数据类型。

  • _name:条目的名称。

  • _list:列表名称。只包含在C变量中允许的字符。

ll_entry_declare宏声明了一个变量,该变量被放置在链接器生成的数组中。使用此宏声明的变量必须在编译时初始化。

此处以/drivers/led目录下的led_gpio.c驱动为例,在该文件的末尾使用U_BOOT_DRIVER进行了驱动声明:a217bb2a-16bf-11ed-ba43-dac502259ad0.png

那么将98行宏定义展开则是:

structdriver_u_boot_list_2_driver_2_led_gpio__aligned(4)
__attribute__((unused,
section(".u_boot_list_2_driver_2_led_gpio")))={
.name="gpio_led",
.id=UCLASS_LED,
.of_match=led_gpio_ids,
.ops=&gpio_led_ops,
.priv_auto_alloc_size=sizeof(structled_gpio_priv),
.bind=led_gpio_bind,
.probe=led_gpio_probe,
.remove=led_gpio_remove,
}

从上述代码片段可知,宏定义展开后本质则是定义一个struct driver的驱动结构变量,并初始化结构变量中的元素。然后将其放到.u_boot_list_2_driver_2_led_gpio节段中。在u-boot源码/drivers目录下存在大量使用U_BOOT_DRIVER声明的驱动,这些驱动会形成一张表(本质为数组)。

至此,分析完U_BOOT_DRIVER这个宏定义,则有一个疑问产生了?u-boot是如何使用这张表的呢?

我们继续查看/include/linker_lists.h文件,该文件中以宏定义的方式提供了访问这张表的首元素和尾元素和数组总数的接口:

(1)指向连接器生成数组的第一个条目:

#definell_entry_start(_type,_list)
({
staticcharstart[0]__aligned(4)__attribute__((unused,
section(".u_boot_list_2_"#_list"_1")));
(_type*)&start;
})

(2)指向在连接器生成的数组的最后一个条目的后面:

#definell_entry_end(_type,_list)
({
staticcharend[0]__aligned(4)__attribute__((unused,
section(".u_boot_list_2_"#_list"_3")));
(_type*)&end;
})

(3)返回链接器生成数组中条目的数量:

#definell_entry_count(_type,_list)
({
_type*start=ll_entry_start(_type,_list);
_type*end=ll_entry_end(_type,_list);
unsignedint_ll_result=end-start;
_ll_result;
})

综上,终于拨开迷雾,实则这张表开始的表项则是:u_boot_list_2_drivers_1,表尾项则是:u_boot_list_2_drivers_3,大量的驱动则在这两者之间。三者关系如下图所示:

a245b6e2-16bf-11ed-ba43-dac502259ad0.png

在u-boot源码中,当需要操作这张表的时候,则会使用到这三个宏定义来完成这张表的循环遍历操作。

四、总结

本文描述了u-boot驱动模型的大致组成,重点描述在驱动程序中U_BOOT_DRIVER宏定义的使用以及背后的实现机制。

下回会接着分析u-boot驱动模型是如何起来的,如何“雄霸一方”。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • u-boot
    +关注

    关注

    0

    文章

    120

    浏览量

    38198
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    40090
  • 驱动模型
    +关注

    关注

    0

    文章

    5

    浏览量

    7397
  • 宏定义
    +关注

    关注

    0

    文章

    50

    浏览量

    9001

原文标题:扒一扒u-boot的驱动模型(第一回)

文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    U-boot的基本介绍

    从本文开始,将陆续推送“手把手教你移植U-boot”系列文章,目标是由浅入深地讲解U-boot的工作流程、原理、配置方法和移植方法,手把手教你完成U-boot的移植工作,默认硬件开发平台为ARM,操作系统为Linux。
    发表于 07-14 16:52 2765次阅读
    <b class='flag-5'>U-boot</b>的基本介绍

    U-boot的执行流程

    本文主要讲述了U-boot的执行流程。
    发表于 07-14 16:58 671次阅读
    <b class='flag-5'>U-boot</b>的执行流程

    浅析U-Boot NAND FLASH驱动

    。mtd_info结构体中的*priv指向设备对应的nand_chip结构体。如图3.1所示。图3.1 U-Boot中NAND FLASH数据结构控制NAND FLASH时,通过
    发表于 07-08 03:56

    Porting U-Boot to the Control

    In this paper, the way of porting U-Boot to Control Computer Based MPC8349 will beintroduced
    发表于 01-25 15:45 13次下载

    u-boot的Makefile分析

    u-boot的Makefile分析 U-BOOT是一个LINUX下的工程,在编译之前必须已经安装对应体系结构的交叉编译环境,这里只针对ARM,编译器系列软件为arm-linux-*。 U-
    发表于 05-17 09:16 2057次阅读

    U-Boot结构功能介绍

      U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot
    发表于 07-30 09:17 1168次阅读
    <b class='flag-5'>U-Boot</b><b class='flag-5'>结构</b>功能介绍

    u-boot简介

    演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是根据相应的Linux内核源程序进行简化而形成的,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点
    发表于 10-14 11:17 3539次阅读

    你了解u-boot与linux内核间的参数传递过程?

    U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;
    发表于 05-13 10:00 1712次阅读
    你了解<b class='flag-5'>u-boot</b>与linux内核间的参数传递过程?

    深度解析U-Boot网络实现

    对于U-Boot而言,并没有完整的实现上述模型u-boot需要控制固件的尺寸,所以根据需要做了一些简化,其拓扑框架如下图所示:
    发表于 02-07 11:53 2次下载
    深度解析<b class='flag-5'>U-Boot</b>网络实现

    U-Boot架构浅析

    导读:嵌入式Linux系统搭建,bootloader是必不可少的一环,而U-Boot已成嵌入式Linux事实标准。所以较为深入的分析U-Boot的设计,对于更...
    发表于 02-07 11:56 7次下载
    <b class='flag-5'>U-Boot</b>架构浅析

    Linux U-Boot开发指南

    介绍 U-Boot 的编译打包、基本配置、常用命令的使用、基本调试方法等, 为 U-BOOT 的移植及应用开发提供了基础。
    的头像 发表于 03-06 10:28 1260次阅读
    Linux <b class='flag-5'>U-Boot</b>开发指南

    U-boot的QSPI驱动移植方法及验证方法

    本文主要讲述了U-boot的QSPI驱动移植方法及验证方法。在产品调试阶段,U-boot的driver子系统包含了丰富的外设驱动,方便外设功能验证与调试。
    的头像 发表于 04-14 10:21 2854次阅读
    <b class='flag-5'>U-boot</b>的QSPI<b class='flag-5'>驱动</b>移植方法及验证方法

    U-boot的MMC DM框架驱动的移植方法

    本文主要讲述了U-boot的MMC DM框架驱动的移植方法。DM是Driver Model的简称,是U-boot的基本驱动框架。常见的MMC设备包括SD卡、eMMC存储器等。本文讨论遵
    的头像 发表于 04-14 10:22 2853次阅读
    <b class='flag-5'>U-boot</b>的MMC DM框架<b class='flag-5'>驱动</b>的移植方法

    U-boot的DPU驱动移植方法

    本文以ARM Mali系列显示处理器驱动为例,讲述了U-boot的DPU驱动移植方法。
    的头像 发表于 04-14 10:25 1319次阅读
    <b class='flag-5'>U-boot</b>的DPU<b class='flag-5'>驱动</b>移植方法

    U-boot驱动SPLASH_SCREEN驱动移植方法

    U-boot集成了SPLASH_SCREEN驱动源码,当使能和配置SPLASH_SCREEN驱动后,可以将启动画面使用的图片文件转换为位图数据数组
    的头像 发表于 06-09 14:39 1211次阅读
    <b class='flag-5'>U-boot</b><b class='flag-5'>驱动</b>SPLASH_SCREEN<b class='flag-5'>驱动</b>移植方法