良许Linux教程网 干货合集 linux 驱动程序的数据封装

linux 驱动程序的数据封装

0.引言

基于 ARM 内核的 SoC 在引入设备树技术之后,通过设备树文件来描述不同的设备并匹配不同的驱动代码,使得一个 kernel 镜像文件可以支持多种设备。这种代码可重用的思想不仅体现在设备树文件中,在驱动代码中同样也有所体现。其中之一就是驱动代码中设备描述表-of_device_id。同一个 IP 集成到不同 SoC 或者根据应用场景激活不同功能,可以通过 of_device_id 这个数据结构来实现。

对于同一个 IP 集成到不同 SoC 的应用场景而言,其寄存器基地址以及时钟等参数可能不同,但是 IP 功能基本一样。那么可以通过 of_device_id 里的不同 data 条目获取对应的参数信息。例如 exynos 的 dsi IP,在不同版本的 SoC 中基地址不同,定义了 5 种 SoC 类型。在 dsi probe 时获取其在 SoC 中的基地址。

下面驱动代码表示该模块需要支持多种不同时钟频率的初始化,可以定义一个 of_device_id 表,根据匹配到的设备信息为每一种时钟提供独立的初始化函数。由 of_device_id_match_data 获取到不同的 init_fn,按照不同的 dev.of_node,执行 return init_fn(np);

以上应用场景核心的数据结构是 of_device_id,关键的处理函数是 of_device_get_match_data(),当然,关于 of_device_id 的应用场景不仅仅限于上面说的这两种。

1.数据结构 of_device_id

of_device_id 数据结构如下,定义在 mod_devicetable.h 中,组成也并不复杂。

1struct of_device_id {
2    char    name[32];
3    char    type[32];
4    char    compatible[128];
5    const void *data;
6};

mod_devicetable.h 这个文件最初并没有 of_device_id 这个数据结构,该文件的历史暂时也只能查到 2005 年的 Linux-2.6.12-rc2

它的功能从最初的文件中也可以看到,主要是为 PCI 以及 USB 设备使用的,将设备的 vendor ID、subsystem ID、class 等信息提供给 scripts/table2alias.c,当系统新插入一个 PCI 或 USB 设备时,用户空间程序根据对应的 vendor ID 等信息来加载对应的驱动程序。

2005 年 7 月 Linux-2.6.13-rc2 中提交了 of_match_id 这个数据结构的代码。

2.of_device_get_match_data()

函数原型位于 drivers/of/device.c

 1const void *of_device_get_match_data(
 2    const struct device *dev)
 3{
 4    const struct of_device_id *match;
 5
 6    match = of_match_device(xxx);
 7    if (!match)
 8        return NULL;
 9
10    return match->data;
11}
12EXPORT_SYMBOL(of_device_get_match_data);

这个函数的返回值类型可强制转换成任何类型,取决于驱动程序中例化数据结构 of_device_id data。当然,由于 of_device_get_match_data 的函数返回值类型决定了不做强制类型转换,也不会有问题。

代码中增加下面的内容,来追踪 of_device_get_match_data 执行流程。

#定义 of_device_id 并完成例化

#在 probe 函数中增加获取数据的代码

执行结果显示正确的获取到了 of_device_id 各个成员例化的 value 值

#of_device_get_match_data() 代码流程

有几种情况是无法获取到数据的

  • 解析 dtb 之后未创建设备结点
  • 驱动代码未实现 of_device_id 设备表
  • of_device_id 成员 compatible、name、type 的值和设备树中定义的同

基于模块加载的并且可以热插拔的驱动程序,可以在系统启动后查看设备表信息。以定位出未获取到设备表信息的故障原因。

3.查看设备表信息

能够查看到设备表信息的一个前置条件是在定义 of_device_id 的时候,要将该设备表通过 MODULE_DEVICE_TABLE 来进行声明注册,否则在用户空间是看不到的。其定义在/include/linux/module.h 中。type 可以是 of、usb、pci 等,name 为设备表的名字。

内核中 scripts/mod/file2alias.c,用于将设备表导出到用户空间 modules.alias 中,所以可以直接查看 modules.alias 文件。

也可以通过 modinfo 来查看 ko 文件符号信息!

设备表的定义如下,代码定义了 name、type,那么设备树里同样也要定义:

删除 MODULE_DEVICE_TABLE,modules.alias 里是没有设备表信息的。

对于 of_device_id 而言,name、type、compatible 添加的方法:

#USB 设备表

 1struct usb_device_id {
 2    /* which fields to match against? */
 3    __u16       match_flags;
 4
 5    /* Used for product specific matches; range is inclusive */
 6    __u16       idVendor;
 7    __u16       idProduct;
 8    __u16       bcdDevice_lo;
 9    __u16       bcdDevice_hi;
10
11    /* Used for device class matches */
12    __u8        bDeviceClass;
13    __u8        bDeviceSubClass;
14    __u8        bDeviceProtocol;
15
16    /* Used for interface class matches */
17    __u8        bInterfaceClass;
18    __u8        bInterfaceSubClass;
19    __u8        bInterfaceProtocol;
20
21    /* Used for vendor-specific interface matches */
22    __u8        bInterfaceNumber;
23
24    /* not matched against */
25    kernel_ulong_t  driver_info
26        __attribute__((aligned(sizeof(kernel_ulong_t))));
27};

#PCI 设备表

1struct pci_device_id {
2    __u32 vendor, device;       /* Vendor and device ID or PCI_ANY_ID*/
3    __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
4    __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */
5    kernel_ulong_t driver_data; /* Data private to the driver */
6};

对于这两种类型的设备,导出的符号信息和普通设备也不一样。

PCI 设备导出到用户空间的设备信息:

导出 PCI 设备信息的代码

USB 设备导出到用户空间的设备信息:

导出 USB 设备信息的代码

除了上面三种设备描述 table 之外,kernel 还提供了很多种其他的设备描述表,定义在 include/linux/mod_devicetable.h

mod_devicetable.h 的 commit log:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/include/linux/mod_devicetable.h

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部