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

    文章

    116

    浏览量

    38044
  • 数据结构
    +关注

    关注

    3

    文章

    564

    浏览量

    39899
  • 驱动模型
    +关注

    关注

    0

    文章

    5

    浏览量

    7389
  • 宏定义
    +关注

    关注

    0

    文章

    48

    浏览量

    8927

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

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

收藏 人收藏

    评论

    相关推荐

    DM6467的U-BOOT烧录

    重金悬赏DM6467底层驱动编译和U-BOOT烧录高手,帮忙解决DM6467烧录不进U-BOOT的问题。如有熟悉DM6467的U-BOOT烧录者,请电联:***,如有时间可面谈。
    发表于 08-05 11:48

    u-boot

    最近在移植u-boot,移植到dm9000网卡的时候出问题了。u-boot能识别dm9000,并且在板子上通过u-boot能ping通服务器,但是在tftp下载时候一直处于等待状态。。。,搞了两天了,还没解决。有没有遇到这个问题
    发表于 04-14 16:48

    u-boot

    最近在移植u-boot,移植到dm9000网卡的时候出问题了。u-boot能识别dm9000,并且在板子上通过u-boot能ping通服务器,但是在tftp下载时候一直处于等待状态。。。,搞了两天了,还没解决。有没有遇到这个问题
    发表于 04-14 16:53

    关于U-boot的问题?

    U-Boot是不是下载好的文件然后在ubuntu里去编译?U-Boot的编译和移植的区别和关系是什么?U-Boot的移植=烧写?
    发表于 05-12 08:22

    u-boot学习指南,非常好的u-boot学习资料!

    非常好的u-boot学习资料!u-boot学习指南u-boot学习指南u-boot学习指南u-boot学习指南
    发表于 05-19 15:50

    【OK210试用体验】u-boot篇 -- u-boot初体验

    /。u-boot文件夹结构 u-boot的源码文件夹数量太多,但是分类明确,在2008年8月的版本前后的文件夹排放也有区别,2008年8月及以前版号形式为:u-boot-1.3.4.
    发表于 08-21 19:57

    U-boot 下 DM 驱动模型的相关笔记

    U-boot 下 DM 驱动模型的相关笔记要注意的关键两点:1. DM 驱动模型的一般流程 bind->ofdata_to_platdata
    发表于 03-28 14:40

    u-boot详解

    U-Boot主要目录结构 - board 目标板相关文件,主要包含SDRAM、FLASH驱动; - common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测; - cp
    发表于 07-04 04:56

    U-Boot NAND FLASH移植

    名,可以推断出主要是完成开发板上的NAND FLASH的初始化。 关于board_nand_init()函数以及u-boot中NAND FLASH的数据结构,函数调用关系,后续会有详细的分析文章,这里先
    发表于 07-03 05:45

    浅析U-Boot NAND FLASH驱动

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

    U-BOOT的启动流程分享

    Bootloader移植(下)U-BOOT 启动流程u-boot启动三个2启动步骤(重点)U-boot 启动源码分析U-BOOT 启动流程u-boo
    发表于 01-18 10:17

    imx28是否支持U-Boot中的网络?

    使用(浪费)四天时间尝试在我的 i.MX28EVK 板上使用 U-Boot 启用网络(即使用 DHCP),然后我在 github 上找到了这个更新 v2022.04mx28evk:转换为驱动模型
    发表于 04-17 07:23

    u-boot的Makefile分析

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

    U-Boot结构功能介绍

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

    U-boot的DPU驱动移植方法

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