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. vultr的防火墙注意事项

    如下图所示,你设置让任意IP的TCP,UDP,GRE,ESP,ICMP都允许访问,并不表示开放了任意协议和端口了. 下图只是表示开放了TCP,UDP,GRE,ESP,ICMP五个协议,比如ROS路由的 ...

  2. System.getProperty("line.separator") 是什么意思?

    在java中存在一些转义字符,比如"\n"为换行符,但是也有一些JDK自带的一些操作符 比如 : System.getProperty("line.separator&q ...

  3. 7种 JVM 垃圾收集器特点、优劣势及使用场景(多图)

    7种 JVM 垃圾收集器特点.优劣势及使用场景(多图)  mp.weixin.qq.com 点击上方"IT牧场",选择"设为星标"技术干货每日送达! 一.常见垃 ...

  4. 【神经网络与深度学习】生成式对抗网络GAN研究进展(五)——Deep Convolutional Generative Adversarial Nerworks,DCGAN

    [前言]      本文首先介绍生成式模型,然后着重梳理生成式模型(Generative Models)中生成对抗网络(Generative Adversarial Network)的研究与发展.作者 ...

  5. odoo @api.constrains _sql_constrains

    实现机制: @api.constrains('fields') 服务器启动时将模型中的所有约束方法注册到对象池中: 在create.write时会根据创建或修改的fields检查是否有对应的约束方法, ...

  6. [转帖]PostgreSQL 参数调整(性能优化)

    PostgreSQL 参数调整(性能优化) https://www.cnblogs.com/VicLiu/p/11854730.html 知道一个 shared_pool 文章写的挺好的 还没仔细看 ...

  7. MongoDB里做表间关联

    MongoDB与关系型数据库的建模还是有许多不同,因为MongoDB支持内嵌对象和数组类型.MongoDB建模有两种方式,一种是内嵌(Embed),另一种是连接(Link).那么何时Embed何时Li ...

  8. lucene字典实现原理(转)

    原文:https://www.cnblogs.com/LBSer/p/4119841.html 1 lucene字典 使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找 ...

  9. Java学习:运算符的使用与注意事项

    运算符的使用与注意事项 四则运算当中的加号“+”有常见的三种用法: 对于数值来,那就是加法. 对于字符char类型来说,在计算之前,char会被提升成为int,然后再计算.char类型字符,和int类 ...

  10. Marshmallow详解

    目录 Marshmallow详解 1. Scheme 2. Serializing(序列化) 3. 过滤输出 4. Deserializing(反序列化) 5. 处理多个对象的集合 6. Valida ...