(三)从解析DTS到创建device_从device_node到并入设备驱动模型(结合源码)

本文详细介绍了如何将Device Tree(DTS)中的device_node转换并注册到内核中,涉及从DTS解析到device_node的创建,以及device_node如何根据machine_desc注册到对应的总线,如platform总线和i2c总线。通过分析执行流程和代码,阐述了device_node如何最终成为platform_device的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从device_node到并入设备驱动模型

此篇博客有很多参考其他文章的内容,由于参考内容繁杂,不一一标注角标了,在末尾会贴上所有参考博客的link,如有侵权,请联系本人处理,谢谢。

深入,并且广泛
					 -沉默犀牛

上一篇文章已经详细的分析了两个问题:
1.如何根据Device Tree的信息,找到最适合的machine_desc
2.如何将DTB转换成节点是device_node的树状结构

那么为什么要做这两件事情呢?

我们现在要做的事情是把DTS中描述的节点(status = okay的)注册到kernel中,DTS的信息我们的kernel没法直接识别啊,就得通过上述的2把DTS中描述的每一个节点,转换成device_node结构体,我们可以认为DTS中写的每一个节点在这里都被解析为一个device_node。(该结构体中有parent、child、sibling成员,通过这些成员把device_node给连接成了树状结构)好,现在我们有了完全代表了DTS信息,又能被kernel识别的device_node,现在就要把这些device_node一个个的创建起来(比如有的device_node要注册到platform总线上,有的要注册到i2c总线…),这个创建的过程,就需要我们上述1中的machine_desc了!在定义machine_desc结构体的时候,会定义一个回调函数:xxxx_init(大部分情况)就是这个xxxx_init把device_node都注册起来。

再补充上面一段话的三个点
1.xxxx_init函数只是注册了platform总线上的device_node,那其他的呢?比如i2c上的device呢?在注册i2c总线时,调用qup_i2c_probe(),这个接口会添加i2c适配器,适配器添加完成后会调用of_i2c_register_devices()接口来i2c总线节点的子节点,然后调用i2c_new_device(),生成i2c设备。

2.我在定义回调函数后面写了个大部分情况,也就代表有的时候不会定义这个回调函数,那么kernel怎么注册device_node呢?事实上kernel会检测machine_desc有没有init函数,如果有,则调用;如果没有,则调用of提供的接口直接注册所有platform的device,这里就有一个问题了:这个machine_desc如果没有定义init函数,那我还要它干嘛呢?我去看(根据DTS选择最适配的machine_desc)那一段代码时发现,如果没有找到合适的machine_desc的话,有一行注释:does not return! 这似乎意味着,找不到合适的machine_desc也没啥关系,kernel还是会继续运行下去,platform上的device还是可以注册上。如果这一点的理解有误,请留言纠正,大恩不言谢!

3.DTS中的节点都转换成了device_node,但是DTS中描述的节点并不都是device啊,比如cpus node,memory node,choose node等。对于这些节点的处理,我会在下一篇文章中说明。

经过前面的介绍和解释,我们明确了这一篇文章的主题:
那些platform总线下的device_node如何注册到platform总线上的?

执行流程

进入kernel的入口后,会按照如下的调用流程start_kernel->rest_init->kernel_init->kernel_init_freeable->
do_basic_setup->do_initcallsdo_initcalls函数中,在do_initcalls函数中,kernel会依次执行各个initcall函数。

代码验证

在上述的调用流程中,会调用到调用customize_machine,也正是这个函数,检测了machine_desc是否有xxxx_init函数,代码如下:

static int __init customize_machine(void)
{
   
   

    if (machine_desc->init_machine)  //如果有init_machine()函数,则执行
        machine_desc->init_machine();
    else  //否则直接调用of提供的接口来注册device
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

    return 0;
}
arch_initcall(customize_machine); 

以高通msm8953为例,看看它有没有定义init_machine函数:

DT_MACHINE_START(MSM8953_DT,
	"Qualcomm Technologies, Inc. MSM8953 (Flattened Device Tree)")
	.init_machine		= msm8953_init,    //这里定义了init函数
	.dt_compat		= msm8953_dt_match,    //这个参数就是之前匹配machine_desc用的
						       //这个参数将于DTS中的compatible属性值来比较
MACHINE_END

再看看这个函数的内容:

static void __init msm8953_init(void)
{
   
   
	board_dt_populate(NULL);
}
void __init 
<think>好的,我现在需要帮助用户理解Linux内核中device_add函数创建设备节点的工作原理。首先,用户的问题涉及Linux设备模型,这是一个比较复杂的主题,我需要先回顾相关的知识,并结合提供的引用资料来组织回答。 根据用户提供的引用,特别是引用[3],其中提到设备模型中的device、driver和总线的关系,以及device_add的作用。我需要先理清device_add在整个设备模型中的位置。引用[3]说明,当device_add被调用时,设备会被添加到对应的总线设备链表,并尝试匹配驱动。同时,引用[1]提到device_create()用于创建设备节点,而device_del()则会删除设备节点和相关目录。这可能与device_add有关联,但需要区分两者的不同。 接下来,引用[4]提到DTS节点转换为device_node结构体,然后通过machine_desc的初始化函数注册到总线上。这可能涉及到device_add的调用时机,即当设备节点被解析并需要注册到系统时,会调用device_add函数。 用户的问题集中在device_add如何创建设备节点。需要明确的是,device_add主要负责将设备注册到内核的设备模型中,而设备节点的创建(即在/dev下的文件)可能由其他机制如devtmpfs处理。引用[2]提到devtmpfs虚拟文件系统,这可能与设备节点的创建有关。引用[1]中的device_create()可能内部调用了device_add,但具体流程需要进一步分析。 我需要逐步拆解device_add的工作流程: 1. 设备注册到总线:将设备添加到总线的设备链表,触发驱动匹配。 2. sysfs目录的创建:设备在sysfs中的表示,生成属性文件。 3. 用户空间设备节点的创建:通过devtmpfs在/dev下生成设备文件。 根据引用[3],device_add负责前两部分,而第部分可能由devtmpfs自动处理。引用[1]中的device_create()可能封装了device_add和devtmpfs的交互,但用户的问题是关于device_add本身,所以需要明确区分。 可能存在的混淆点在于device_add和device_create的区别。根据引用[1],device_create负责创建设备节点,而device_add是更底层的函数,用于将设备添加到内核的设备模型。因此,device_add本身不直接创建设备节点,而是触发后续的uevent事件,由用户空间的守护进程(如udev)或devtmpfs来创建设备节点。但根据引用[1]中的描述,device_del会删除/dev下的设备文件,这可能意味着device_create在调用device_add之后,通过某种机制(如devtmpfs)创建设备节点。 需要进一步确认devtmpfs的工作流程。引用[2]中提到关于devtmpfs的分析,可能说明当设备被添加到内核后,devtmpfs会自动在/dev下创建对应的节点。这可能发生在device_add之后,通过内核的uevent机制通知用户空间。 因此,device_add的主要步骤可能包括: - 初始化设备结构体,设置父设备、总线类型等。 - 将设备添加到总线的设备链表。 - 在sysfs中创建对应的目录和属性文件。 - 触发uevent事件,通知用户空间创建设备节点。 而设备节点的实际创建由devtmpfs或udev根据uevent事件处理。这可能涉及到引用[3]中提到的设备与驱动匹配成功后,才会生成相应的节点。 总结来说,device_add是内核中设备注册的核心函数,负责将设备添加到设备模型,触发驱动匹配,并在sysfs中创建条目。设备节点的创建(在/dev下)则由其他机制处理,如devtmpfs,在device_add之后通过uevent事件触发。 现在需要将这些信息组织成结构化的回答,确保符合用户的要求:使用中文,行内公式用$...$,独立公式用$$...$$,正确引用标注,并生成相关问题。</think>### device_add函数创建设备节点的工作原理 在Linux设备模型中,`device_add`函数是设备注册的核心接口,其工作原理可分为以下几个关键步骤: 1. **设备初始化与总线注册** - 当调用`device_add(dev)`时,内核首先验证设备结构体`struct device`的有效性,包括所属总线类型(`dev->bus`)和父设备指针(`dev->parent`)[^3]。 - 设备被添加到对应总线的设备链表(`bus->p->devices`),触发总线匹配机制:遍历该总线的驱动链表,寻找匹配的`struct driver`(通过`bus->match`回调函数)。匹配成功后,调用`driver->probe()`激活设备。 2. **sysfs节点创建** - 内核在`/sys/devices/virtual/`目录下创建设备属性文件,例如设备名、电源状态等。例如,输入设备会在`/sys/class/input/`下生成子目录[^1]。 - 该过程通过`kobject_add()`实现,将设备对象的属性映射为sysfs文件,文件权限由`dev->kobj.kset->ktype->sysfs_ops`定义[^2]。 3. **用户空间设备节点生成** - `device_add`触发内核发送`uevent`事件(类型为`KOBJ_ADD`),通知用户空间守护进程(如`udev`)或`devtmpfs`。 - `devtmpfs`虚拟文件系统响应事件,自动在`/dev`目录下创建设备文件。例如,字符设备会生成`/dev/mydevice`,主次设备号由`dev->devt`决定[^1][^2]。 4. **设备树(DTS)集成** - 对于嵌入式设备,DTS节点通过`of_platform_populate()`转换为`device_node`结构体,最终调用`device_add`注册设备。例如,`status = "okay"`的节点会被激活[^4]。 #### 关键代码流程 ```c // 伪代码示例 int device_add(struct device *dev) { // 1. 检查设备总线类型和父设备 if (!dev->bus || !dev->parent) return -EINVAL; // 2. 将设备加入总线设备链表 list_add(&dev->bus_list, &dev->bus->p->devices); // 3. 创建sysfs条目 kobject_add(&dev->kobj); // 4. 触发uevent事件 kobject_uevent(&dev->kobj, KOBJ_ADD); // 5. 尝试匹配驱动 bus_probe_device(dev); return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值