linux设备驱动程序--设备树多级子节点的转换

在上一章:设备树处理之——device_node转换成platform_device中,有提到在设备树的device_node到platform_device转换中,必须满足以下条件:

  • 一般情况下,只对设备树中根的一级子节点进行转换,也就是多级子节点(子节点的子节点)并不处理。但是存在一种特殊情况,就是当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的一级子节点将会被转换成platform_device节点。
  • 节点中必须有compatible属性。

事实上,在设备树中,通常会存在将描述设备驱动的设备树节点被放置在多级子节点的情况,比如下面这种情况:

/{
...
i2c@44e0b000 {
compatible = "ti,omap4-i2c";
...
tps@24 {
reg = <0x24>;
compatible = "ti,tps65217";
...
charger {
compatible = "ti,tps65217-charger";
...
};
pwrbutton {
compatible = "ti,tps65217-pwrbutton";
...
};
}
}
...
}

显然,i2c@44e0b000会被转换成platform_device,而tps@24、charger、pwrbutton则不会,至少在设备树初始化阶段不会被转换,仍旧以device_node的形式存在在内存中。

显而易见,这些设备并非是无意义的设备,那么它们是什么时候生成platform_device的呢?

答案是:由对应根目录的一级子节点处理。

我们以i2c@44e0b000节点为例,事实上,这个节点对应一个i2c硬件控制器,控制器的起始地址是0x44e0b000,这个节点的作用就是生成一个i2c硬件控制器的platform_device,与同样被加载到内存中的platform_driver相匹配,在内存中构建一个i2c硬件控制器的描述节点,负责对应i2c控制器的数据收发。

根据设备树的compatible属性匹配机制,在内核源代码中全局搜索,就可以找到与i2c@44e0b000设备节点对应的platform_driver部分:

在i2c-omap.c(不同平台可能文件名不一样,但是按照上面从设备树开始找的方法可以找到对应的源文件)中找到了这个compatible的定义:

static const struct of_device_id omap_i2c_of_match[] = {
{
.compatible = "ti,omap4-i2c",
.data = &omap4_pdata,
},
...
}

同时,根据platform driver驱动的规则,需要填充一个struct platform_driver结构体,然后注册到platform总线中,这样才能完成platfrom bus的匹配,因此,我们也可以在同文件下找到相应的初始化部分:

static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
.remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
.pm = OMAP_I2C_PM_OPS,
.of_match_table = of_match_ptr(omap_i2c_of_match),
},
}; static int __init omap_i2c_init_driver(void)
{
return platform_driver_register(&omap_i2c_driver);
}

既然platform总线的driver和device匹配上,就会调用相应的probe函数,根据.probe = omap_i2c_probe,我们再查看omap_i2c_probe函数:

static int omap_i2c_probe(struct platform_device *pdev)
{
... //get resource from dtb node
... //config i2c0 via set corresponding regs
i2c_add_numbered_adapter(adap);
... //deinit part
}

在probe函数中我们找到一个i2c_add_numbered_adapter()函数,再跟踪代码到i2c_add_numbered_adapter():

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
... //assert part
return __i2c_add_numbered_adapter(adap);
}

根据名称可以隐约猜到了,这个函数的作用是添加一个i2c adapter到系统中,接着看:

static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
...
return i2c_register_adapter(adap);
}

i2c_register_adapter(),根据这个名称可以看出这是根据设备树描述的硬件i2c控制器而生成的一个i2c_adapter,并注册到系统中,这个i2c_adapter负责i2c底层数据收发。

继续跟踪源码:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
...
of_i2c_register_devices(adap);
...
}

注意到一个of前缀的函数,看到of就能想到这肯定是设备树相关的函数。

void of_i2c_register_devices(struct i2c_adapter *adap)
{
...
for_each_available_child_of_node(bus, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue; client = of_i2c_register_device(adap, node);
if (IS_ERR(client)) {
dev_warn(&adap->dev,
"Failed to create I2C device for %pOF\n",
node);
of_node_clear_flag(node, OF_POPULATED);
}
}
...
}

这个函数的作用是轮询每个子节点,并调用of_i2c_register_device(),返回i2c_client结构体,值得注意的是,在i2c总线中,driver部分为struct i2c_driver,而device部分为struct i2c_client.

所以可以看出,of_i2c_register_device()这个函数的作用就是解析设备树中当前i2c中的子节点,并将其转换成相应的struct i2c_client描述结构。

不妨来验证一下我们的猜想:

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,struct device_node *node)
{
...
struct i2c_board_info info = {};
of_modalias_node(node, info.type, sizeof(info.type);
of_get_property(node, "reg", &len);
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad; if (of_property_read_bool(node, "host-notify"))
info.flags |= I2C_CLIENT_HOST_NOTIFY; if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE; result = i2c_new_device(adap, &info);
...
} struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
...
struct i2c_client *client;
client = kzalloc(sizeof *client, GFP_KERNEL);
client->adapter = adap;
client->dev.platform_data = info->platform_data; if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
client->dev.fwnode = info->fwnode; if (info->properties) {
status = device_add_properties(&client->dev, info->properties);
if (status) {
dev_err(&adap->dev,
"Failed to add properties to client %s: %d\n",
client->name, status);
goto out_err;
}
}
device_register(&client->dev);
return client;
...
}

从device_node到i2c_client的转换主要是在这两个函数中了,在of_i2c_register_device()函数中,从device_node节点中获取各种属性的值记录在info结构体中,然后将info传递给i2c_new_device(),i2c_new_device()生成一个对应的i2c_client结构并返回。

到这里,不难猜测为什么在内核初始化时只将一级子目录节点(compatible属性中含有"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"的向下递归一级)转换成platform_device,因为在linux中,将一级子节点视为bus,而多级子节点则由具体的bus去处理。

同时,对于bus而言,有不同的总线处理方式和不同的driver、device的命名,自然不能将所有节点全部转换成platform_device.

本文仅仅以i2c为例讲解设备树中多级子节点的转换,朋友们也可以按照这种从设备树出发的代码跟踪方式查看其它bus子节点的转换。

好了,关于linux设备树多级子节点的转换的讨论就到此为止啦,如果朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言

原创博客,转载请注明出处!

祝各位早日实现项目丛中过,bug不沾身.

linux设备驱动程序-设备树(3)-设备树多级子节点的转换的更多相关文章

  1. 嵌入式Linux设备驱动程序:编写内核设备驱动程序

    嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...

  2. 【转】linux设备驱动程序之简单字符设备驱动

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/03/2272869.html 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用 ...

  3. linux设备驱动程序-i2c(0)-i2c设备驱动源码实现

    (基于4.14内核版本) 为了梳理清楚linux内核中的i2c实现框架,从本文开始,博主将分几个章节分别解析i2c总线在linux内核中的形成过程.匹配过程.以及设备驱动程序源码实现. 在介绍linu ...

  4. 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

    字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...

  5. linux设备驱动程序-i2c(2)-adapter和设备树的解析

    linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 在本系列linux内核i2c框架的前两篇,分别讲 ...

  6. linux设备驱动程序-设备树(2)-device_node转换成platform_device

    设备树处理之--device_node转换成platform_device 以下讨论基于linux4.14,arm平台 platform device 设备树的产生就是为了替代driver中过多的pl ...

  7. 【linux】驱动-8-一文解决设备树

    目录 前言 8. Linux设备树 8.1 设备树简介 8.2 设备树框架 8.2.1 设备树格式 8.2.1.1 DTS 文件布局 8.2.1.2 node 格式 8.2.1.3 propertie ...

  8. linux设备驱动程序-i2c(1):i2c总线的添加与实现

    linux设备驱动程序-i2c(1):i2c总线的添加与实现 (基于4.14内核版本) 在上一章节linux设备驱动程序-i2c(0)-i2c设备驱动源码实现中,我们演示了i2c设备驱动程序的源码实现 ...

  9. Linux 简单字符设备驱动程序 (自顶向下)

    第零章:扯扯淡 特此总结一下写的一个简单字符设备驱动程序的过程,我要强调一下“自顶向下”这个介绍方法,因为我觉得这样更容易让没有接触过设备驱动程序的童鞋更容易理解,“自顶向下”最初从<计算机网络 ...

随机推荐

  1. Spring Boot 2.2.1 发布,一个有点坑的版本!

    上一篇:Spring Boot 2.2.0 正式发布,支持 JDK 13! Spring Boot 2.2.0 没发布多久,Spring Boot 2.2.1 又发布了,这是一个很有意思,又有点 &q ...

  2. Executor多线程框架

    啥都别说了,上代码: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; impor ...

  3. linux quota磁盘限额,引发的rename系统调用 errno:18 - Invalid cross-device link

    起因: log4j日志滚动失败,debug发现jvm调用native方法rename失败,也就是系统调用rename失败. 自己写c程序系统调用rename,证实确实是这个问题. 日志打在容器里,日志 ...

  4. Docker 版rocketmq部署

    rocketmq 部署启动指南-Docker 版   最近学习使用 rocketmq,需要搭建 rocketmq 服务端,本文主要记录 rocketmq 搭建过程以及这个过程踩到的一些坑. 准备工作# ...

  5. SWIG 3 中文手册——2. 引言

    目录 2 引言 2.1 SWIG 是什么? 2.2 为什么使用 SWIG? 2.3 一个 SWIG 示例 2.3.1 SWIG 接口文件 2.3.2 swig 命令 2.3.3 构建 Perl5 模块 ...

  6. phpcms新建模板页教程

    phpcms新建模板页教程1 直接去template文件夹里的复制的模板页 比方说list1.html2 去后台 界面模板风格 default 默认模板 点击详情列表 找到list1.htm 设置中文 ...

  7. centos下导出docx为html

    yum -y install libreoffice.x86_64 libreoffice --invisible --convert-to html --outdir /root/demo_html ...

  8. TeamViewer 一款远程控制软件

    TeamViewer 一款远程控制软件,可以在任何防火圈和Nat代理的后台用于远程控制的应用程序. 主要功能:桌面共享和文件传输. 使用前提:两台计算机上同时运行TeamViewer, 使用方法:如果 ...

  9. keil 编译器V6 定义函数在ram中运行-和在指定地址定义常量

    之前一直是用v5编译,编译速度慢,换成V6编译速度差不多快50% ,而且arm后期只维护v5编译器不在更新v5编译器.切换到V6编译器大势所趋,把之前v5且换到v6需要如下更改: 1. CMSIS包需 ...

  10. 配置 Nginx 反向代理 WebSocket

    用Nginx给网站做反向代理和负载均衡是广泛使用的一种Web服务器部署技术.不仅能够保证后端服务器的隐蔽性,还可以提高网站部署灵活性. 今天我们来讲一下,如何用Nginx给WebSocket服务器实现 ...