从DTS到platform_device:设备树C语言解析全链路拆解(仅限资深开发者)

第一章:设备树的 C 语言解析

在嵌入式 Linux 系统开发中,设备树(Device Tree)用于描述硬件资源与外设连接关系。C 语言作为内核开发的核心语言,广泛应用于设备树节点的解析与访问。通过标准 API 接口,开发者可以从 DTS 编译生成的 DTB 文件中提取设备信息。

设备树基本结构

设备树源文件(.dts)经编译后生成二进制格式(.dtb),由引导程序传递给内核。内核使用 `unflatten_device_tree()` 解析 DTB 并构建运行时数据结构。每个节点以 `struct device_node` 表示,包含名称、属性和子节点链表。

常用 C 语言 API 示例

内核提供一系列函数用于查找和读取设备树内容:
  • of_find_node_by_name():根据名称查找节点
  • of_property_read_u32():读取 32 位整型属性值
  • of_get_next_child():遍历子节点

// 示例:从节点获取中断号和寄存器地址
struct device_node *np;
u32 irq_num, reg_base;

np = of_find_node_by_name(NULL, "my_device");
if (np) {
    of_property_read_u32(np, "interrupts", &irq_num); // 获取中断号
    of_property_read_u32(np, "reg", &reg_base);       // 获取寄存器基址
}
上述代码首先定位名为 my_device 的设备节点,随后从中提取中断号和内存映射地址。这些信息常用于驱动初始化时配置硬件资源。

属性类型对照表

DTS 类型C 函数说明
uint32of_property_read_u32读取单个 32 位值
stringof_property_read_string读取字符串属性
phandleof_parse_phandle解析指向其他节点的引用
graph TD A[DTB 加载] --> B{调用 unflatten_device_tree} B --> C[创建 device_node 树] C --> D[驱动调用 of_* API] D --> E[获取硬件配置]

第二章:设备树基础与C语言接口原理

2.1 设备树DTS到DTB的编译流程解析

设备树源文件(DTS)是描述硬件配置的文本文件,需通过编译生成二进制格式的DTB文件,供内核在启动时解析。
DTC工具链的作用
设备树编译器(Device Tree Compiler, DTC)负责将.dts文件转换为.dtb。其核心命令如下:
dtc -I dts -O dtb -o mysystem.dtb mysystem.dts
其中,-I dts 指定输入为DTS格式,-O dtb 指定输出为DTB格式,-o 定义输出文件名。该过程会递归处理所有包含的.dtsi文件。
编译流程关键阶段
  • 预处理:展开 #include 和宏定义,合并 dtsi 共享片段
  • 语法解析:DTC 分析节点、属性与兼容性字符串
  • 二进制编码:将树形结构序列化为扁平化的内存布局(FDT)
最终生成的 DTB 可被 U-Boot 或内核直接加载,实现硬件信息与代码逻辑的解耦。

2.2 Open Firmware API与内核中的设备树映射机制

在嵌入式系统启动过程中,Open Firmware API(如IEEE 1275标准)为固件层提供了标准化的设备描述接口。这些描述以设备树(Device Tree)形式传递给内核,实现硬件拓扑与驱动模型的动态绑定。
设备树节点映射流程
内核通过unflatten_device_tree()解析FDT(Flattened Device Tree),将.dts编译生成的二进制结构转换为内核可识别的struct device_node链表:

void __init unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, NULL,
                            &of_root, of_populate_phandle_cache);
}
该函数从initial_boot_params读取FDT首地址,逐节点构建属性树,并建立compatible字段与平台设备的匹配关系。
资源匹配机制
驱动通过OF匹配表获取设备信息:
  • compatible字符串精确匹配设备节点
  • reg属性映射寄存器基址
  • interrupts描述中断号及触发方式

2.3 C语言中节点与属性的访问方法(of_*函数族详解)

在Linux内核设备树驱动开发中,`of_*`函数族是访问设备节点和属性的核心接口。这些函数定义于``头文件中,用于在运行时解析设备树信息。
常用of_*函数分类
  • of_find_node_by_*:通过名称或兼容性字符串查找节点
  • of_property_read_*:读取节点中的属性值
  • of_get_*:获取中断、地址、GPIO等资源
典型代码示例
struct device_node *np;
u32 reg_value;

np = of_find_compatible_node(NULL, NULL, "vendor,device");
if (np && of_property_read_u32(np, "reg-value", &reg_value) == 0) {
    printk("Found reg-value: %u\n", reg_value);
}
上述代码首先通过兼容性字符串查找设备节点,成功后读取名为"reg-value"的32位整型属性。`of_property_read_u32`返回0表示读取成功,否则属性可能缺失或类型不匹配。
属性读取函数对照表
函数名用途
of_property_read_u32读取32位整数
of_property_read_string读取字符串
of_property_read_u8_array读取字节数组

2.4 实践:通过of_property_read_u32解析设备参数

在嵌入式Linux驱动开发中,设备树常用于描述硬件资源。`of_property_read_u32` 是内核提供的API,用于从设备树节点中读取32位无符号整型参数。
函数原型与参数说明

int of_property_read_u32(const struct device_node *np,
                         const char *propname,
                         u32 *out_value);
该函数从节点 np 中读取名为 propname 的属性值,成功时返回0,并将结果存入 out_value 指向的变量。
典型使用场景
  • 读取寄存器地址偏移量
  • 配置工作模式或超时时间
  • 获取硬件版本号等静态参数
结合设备树片段:

demo_device {
    compatible = "demo,device";
    timeout-ms = <100>;
};
驱动中可安全读取:

u32 timeout;
if (of_property_read_u32(np, "timeout-ms", &timeout)) {
    timeout = 50; // 默认值
}
此机制提升代码可维护性,实现硬件配置与驱动逻辑解耦。

2.5 节点遍历与匹配:of_each_child_of_node实战应用

在设备树解析过程中,常需对父节点的子节点进行逐一遍历与属性匹配。`of_each_child_of_node` 提供了一种安全且高效的遍历机制,避免手动管理节点指针越界或空指针问题。
遍历模式详解
该宏通过迭代器模式封装了 `for_each_child_of_node` 的底层逻辑,确保在并发访问时保持一致性。

struct device_node *parent, *child;
of_each_child_of_node(parent, child) {
    const char *name = of_get_property(child, "status", NULL);
    if (name && !strcmp(name, "okay")) {
        printk("Active node: %pOFn\n", child);
    }
}
上述代码中,`parent` 为起始父节点,`child` 自动指向每个子节点。`%pOFn` 是 OpenFirmware 特有的打印格式,用于输出节点名称。循环自动终止于最后一个子节点。
典型应用场景
  • 驱动初始化时批量探测子设备
  • 解析多通道GPIO配置节点
  • 动态加载具备相同兼容性字符串的模块

第三章:platform_device与设备树绑定机制

3.1 platform_bus_type如何基于设备树自动生成设备

在Linux内核中,`platform_bus_type`通过设备树(Device Tree)实现设备的自动发现与注册。系统启动时,内核解析设备树节点,将兼容性字符串(compatible)与驱动匹配。
设备树节点匹配机制
设备树中的每个节点若包含`compatible`属性,会被`of_platform_default_populate()`扫描并生成对应的`platform_device`。

static const struct of_device_id of_default_bus_match_table[] = {
    { .compatible = "simple-bus", },
    { .compatible = "simple-mfd", },
    { .compatible = "isa", },
    {}
};
该代码定义了可递归展开的总线类型。当节点匹配"simple-bus"时,其子节点将被逐一实例化为平台设备。
设备创建流程
  • 解析设备树DTS编译后的DTB文件
  • 遍历所有未禁用的节点
  • 根据compatible字段查找对应驱动
  • 调用platform_device_alloc()创建设备
  • 完成probe调用链

3.2 of_match_table的作用与驱动匹配流程剖析

在Linux设备树框架中,`of_match_table`用于定义驱动支持的设备节点兼容属性列表,是实现设备与驱动匹配的关键结构。
匹配表的定义方式
static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "vendor,device-a", },
    { .compatible = "vendor,device-b", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);
该代码段声明了驱动可匹配的两个设备类型。内核通过 `.compatible` 字符串与设备树中的 `compatible` 属性进行比对,实现自动绑定。
驱动匹配流程
  • 内核解析设备树节点时提取 compatible 属性值;
  • 遍历所有注册驱动的 of_match_table
  • 执行字符串匹配,成功则调用驱动的 probe 函数;
  • 完成设备资源映射与初始化。

3.3 实践:编写支持设备树的platform_driver并验证绑定过程

在嵌入式Linux系统中,设备树(Device Tree)用于描述硬件资源。编写一个支持设备树的`platform_driver`需首先定义`of_device_id`表,匹配设备树节点。
驱动代码实现

static const struct of_device_id sample_dt_ids[] = {
    { .compatible = "acme,mydevice" },
    { } /* 空结尾 */
};
MODULE_DEVICE_TABLE(of, sample_dt_ids);

static struct platform_driver sample_driver = {
    .probe = sample_probe,
    .remove = sample_remove,
    .driver = {
        .name = "sample-driver",
        .of_match_table = sample_dt_ids,
    },
};
该代码段定义了兼容性字符串为"acme,mydevice"的设备匹配规则。当设备树中存在对应`compatible`属性的节点时,内核将触发`probe`函数。
设备树节点示例
设备树需包含如下节点:

mydevice@10000000 {
    compatible = "acme,mydevice";
    reg = <0x10000000 0x1000>;
};
此节点声明位于`0x10000000`的硬件模块,通过`reg`提供内存映射地址,驱动可在`probe`中通过`platform_get_resource()`获取。

第四章:高级解析技术与调试策略

4.1 多实例设备支持与别名(alias)的C语言处理

在嵌入式系统中,同一类设备可能在硬件层面存在多个实例,例如多个UART控制器或I2C总线。为统一接口并提升代码复用性,需通过C语言实现多实例管理与别名映射机制。
设备实例的结构体封装
每个设备实例通过结构体进行抽象,包含寄存器基地址、中断号及状态标志:

typedef struct {
    volatile uint32_t *base_addr;
    uint8_t irq_line;
    char *alias;  // 别名标识,如"debug_uart"
} device_t;
该结构允许运行时根据别名查找对应设备,提升配置灵活性。
别名注册与查找机制
使用静态数组维护设备别名映射表,并提供查找接口:
索引别名(alias)设备指针
0"i2c-display"&i2c_dev1
1"i2c-touch"&i2c_dev2
结合strcmp实现快速匹配,确保驱动可动态绑定逻辑名称与物理实例。

4.2 中断、寄存器地址的设备树提取与资源映射

在嵌入式Linux系统中,设备树(Device Tree)承担着硬件资源配置的核心职责,尤其在中断和寄存器地址的描述上至关重要。通过设备树,驱动程序可动态获取硬件资源,避免了对硬件地址的硬编码。
设备树中的资源定义
设备节点通过`reg`属性描述寄存器地址范围,`interrupts`属性定义中断号。例如:

uart@48020000 {
    compatible = "fsl,imx6ul-uart";
    reg = <0x48020000 0x1000>;
    interrupts = <26 IRQ_TYPE_LEVEL_HIGH>;
};
其中,`reg`表示起始地址为0x48020000,长度为4KB;`interrupts`中的26为GIC中断号。
驱动中的资源映射流程
内核使用`of_iomap()`从设备树获取并映射寄存器地址,`platform_get_irq()`提取中断号:

void __iomem *base = of_iomap(np, 0);
int irq = platform_get_irq(pdev, 0);
`of_iomap()`将物理地址映射为虚拟地址,便于CPU访问;`platform_get_irq()`解析设备树中断属性,返回内核中断号,供`request_irq()`注册使用。
函数功能
of_iomap()物理地址到虚拟地址映射
platform_get_irq()从设备树获取中断号

4.3 实践:从设备树获取GPIO并实现动态控制

在嵌入式Linux系统中,通过设备树(Device Tree)描述硬件配置是标准做法。要实现对GPIO的动态控制,首先需在设备树节点中正确定义GPIO引脚。
设备树配置示例

leds {
    compatible = "gpio-leds";
    red-led {
        gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
        label = "red";
    };
};
上述代码将GPIO1的第18号引脚分配给红色LED,并设置为高电平有效。内核解析该节点后,会自动注册对应的GPIO资源。
用户空间动态控制
通过sysfs接口可实现运行时控制:
  1. 导出GPIO:echo 42 > /sys/class/gpio/export
  2. 设置方向:echo out > /sys/class/gpio/gpio42/direction
  3. 控制电平:echo 1 > /sys/class/gpio/gpio42/value
内核驱动也可使用 `gpiod_get()` 获取GPIO描述符,实现更安全的资源管理。这种方式避免了直接操作寄存器的风险,提升代码可维护性。

4.4 利用debugfs和 printk跟踪设备树解析全过程

在Linux内核开发中,深入理解设备树(Device Tree)的解析过程对调试硬件初始化至关重要。通过结合`printk`日志输出与`debugfs`虚拟文件系统,开发者能够实时观测内核对设备树节点的处理流程。
启用printk追踪关键路径
可在设备树相关函数中插入`printk`语句,例如在`unflatten_device_tree()`中添加:

printk(KERN_INFO "DT: Parsing node %s\n", np->full_name);
该语句会在每次解析节点时输出节点名称,帮助定位解析顺序与异常节点。
利用debugfs暴露解析状态
通过在`/sys/kernel/debug`下创建自定义接口,可动态查看解析进度:
  • 挂载debugfs:mount -t debugfs none /sys/kernel/debug
  • 创建调试文件:使用debugfs_create_file()导出内部状态
结合两者,可构建完整的设备树解析可视化路径,极大提升嵌入式平台调试效率。

第五章:总结与展望

技术演进的持续驱动
现代软件架构正加速向云原生和边缘计算融合。以 Kubernetes 为核心的调度平台已成标配,但服务网格(如 Istio)与 eBPF 技术的结合正在重构网络层可观测性。某金融客户通过部署 Cilium 替代 kube-proxy,将网络延迟降低 38%,并实现基于身份的安全策略。
  • 采用 GitOps 模式管理集群配置,确保环境一致性
  • 利用 OpenTelemetry 统一指标、日志与追踪数据采集
  • 在 CI/CD 流程中集成混沌工程实验,提升系统韧性
未来挑战与应对路径
挑战领域典型问题推荐方案
多云治理策略不一致、成本失控使用 Crossplane 实现统一控制平面
AI 工作负载调度GPU 资源碎片化部署 Kueue 进行批处理队列管理
// 示例:使用 Kueue 定义资源队列
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: gpu-node
usage: "nvidia.com/gpu"
图示: 边缘节点通过轻量级控制面(K3s)连接中心集群, 数据经由 MQTT 协议汇聚至时序数据库(TDengine),触发基于 Prometheus 的智能告警规则。
某智能制造项目中,该架构支撑了 2000+ 设备实时监控,日均处理消息达 1.2 亿条。系统通过动态扩缩容策略,在生产高峰期间自动增加边缘推理实例,保障 SLA 达到 99.95%。
基于TROPOMI高光谱遥感仪器获取的大气成分观测资料,本研究聚焦于大气污染物一氧化氮(NO₂)的空间分布与浓度定量反演问题。NO₂作为影响空气质量的关键指标,其精确监测对环境保护与大气科学研究具有显著价值。当前,利用卫星遥感数据结合先进算法实现NO₂浓度的高精度反演已成为该领域的重要研究方向。 本研究构建了一套以深度学习为核心的技术框架,整合了来自TROPOMI仪器的光谱辐射信息、观测几何参数以及辅助气象数据,形成多维度特征数据集。该数据集充分融合了不同来源的观测信息,为深入解析大气中NO₂的时空变化规律提供了数据基础,有助于提升反演模型的准确性与环境预测的可靠性。 在模型架构方面,项目设计了一种多分支神经网络,用于分别处理光谱特征与气象特征等多模态数据。各分支通过独立学习提取代表性特征,并在深层网络中进行特征融合,从而综合利用不同数据的互补信息,显著提高了NO₂浓度反演的整体精度。这种多源信息融合策略有效增强了模型对复杂大气环境的表征能力。 研究过程涵盖了系统的数据处理流程。前期预处理包括辐射定标、噪声抑制及数据标准化等步骤,以保障输入特征的质量与一致性;后期处理则涉及模型输出的物理量转换与结果验证,确保反演结果符合实际大气浓度范围,提升数据的实用价值。 此外,本研究进一步对不同功能区域(如城市建成区、工业带、郊区及自然背景区)的NO₂浓度分布进行了对比分析,揭示了人类活动与污染物空间格局的关联性。相关结论可为区域环境规划、污染管控政策的制定提供科学依据,助力大气环境治理与公共健康保护。 综上所述,本研究通过融合TROPOMI高光谱数据与多模态特征深度学习技术,发展了一套高效、准确的大气NO₂浓度遥感反演方法,不仅提升了卫星大气监测的技术水平,也为环境管理与决策支持提供了重要的技术工具。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
在机器人技术领域,机器人操作系统(ROS)的演进为各类应用提供了关键支撑。计算机视觉与增强现实的结合,进一步拓展了机器人的感知与交互能力。OpenCV作为广泛使用的视觉处理库,集成了多种图像分析与模式识别算法。其中,Aruco标记系统作为一种基于二维码的视觉标识,因其识别稳定、计算高效的特点,被广泛应用于空间定位、姿态估计及增强现实场景的虚实融合。 Aruco标记通过预定义的编码图案,可在复杂环境中实现快速检测与高精度位姿解算。这一特性使其在自主导航、三维重建、目标跟踪等任务中具有重要价值。例如,在移动机器人定位中,可通过布设标记点辅助实现厘米级的位置修正;在增强现实应用中,则能依据标记的空间姿态准确叠加虚拟信息。 针对ROS2框架,现已开发出集成OpenCV的Aruco标记检测与位姿估计工具包。该工具能够实时处理图像流,识别标记的独特编码,并解算其相对于相机坐标系的三维位置与旋转姿态。结果可通过ROS2的话题或服务接口发布,为其他功能模块提供实时视觉反馈。工具包兼容多种标准标记字典,用户可根据实际场景的复杂度与识别范围需求,灵活选择不同尺寸与编码数量的标记集合。 将Aruco检测模块嵌入ROS2系统,可充分利用其分布式通信机制与模块化架构。开发者能够便捷地将视觉定位数据与运动规划、控制决策等模块相融合,进而构建更为综合的机器人应用系统。例如,结合点云处理技术可实现动态环境的三维建模,或与机械臂控制器联动完成基于视觉引导的精准抓取操作。 该开源工具的推出,降低了在ROS2中部署视觉定位功能的技术门槛。通过提供稳定、可配置的标记识别与姿态解算方案,它不仅促进了机器人视觉应用的快速原型开发,也为后续在工业自动化、服务机器人、混合现实等领域的深入应用奠定了技术基础。随着感知算法与硬件性能的持续提升,此类融合视觉、增强现实与机器人中间件的工具包,将在智能化系统的构建中发挥日益重要的作用。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
<think>我们讨论的是Linux内核中设备树的处理流程,特别是`of_platform_default_populate_init`函数如何将设备树节点(device_node)转换为platform_device并挂载到总线上。根据引用内容,我们可以分步骤解释: 1. **函数调用时机**:`of_platform_default_populate_init`是在内核初始化过程中通过`arch_initcall`(优先级3)注册的初始化函数(引用[2])。它在内核启动的`do_initcalls`阶段被执行。 2. **函数作用**:该函数负责遍历设备树Device Tree)中的节点,并将符合条件的节点转换为`platform_device`(引用[4])。转换的条件包括: - 节点必须有`compatible`属性。 - 根节点的子节点(必须含有`compatible`属性)。 - 某些特定compatible属性(如`"simple-bus"`、`"simple-mfd"`、`"isa"`、`"arm,amba-bus"`)的节点的子节点(也必须含有`compatible`属性)。 3. **转换过程**(引用[5]): - 调用`of_platform_default_populate()`,它内部调用`of_platform_populate()`。 - `of_platform_populate()`遍历根节点的子节点(或者指定的父节点),对每个子节点调用`of_platform_bus_create()`。 - 在`of_platform_bus_create()`中: - 首先检查节点是否匹配(通过`of_default_bus_match_table`,其中包含上述特殊compatible字符串)。 - 如果匹配,则递归处理其子节点(因为这是一个总线节点,其子节点可能代表挂载在该总线上的设备)。 - 然后,无论是否匹配,都会调用`of_platform_device_create_pdata()`来创建该节点对应的设备。 - `of_platform_device_create_pdata()`会调用`of_device_alloc()`分配一个`platform_device`结构,并设置其资源(如内存、中断等)来自设备树节点的属性。 4. **挂载到总线**: - 创建`platform_device`后,通过`platform_device_add()`(在`of_platform_device_create_pdata()`内部调用)将其添加到内核中。 - `platform_device_add()`会将该设备注册到`platform_bus_type`总线上(引用[1])。注册到总线后,内核会将该设备与已注册的驱动程序进行匹配(通过比较驱动程序的`of_match_table`或`id_table`与设备的`compatible`属性)。 5. **特殊总线(如I2C)的处理**: - 对于I2C总线节点,设备树中通常表示为: ```dts i2c1: i2c@400a0000 { compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; // ... 其他属性 clock-frequency = <100000>; status = "okay"; eeprom@50 { compatible = "at24,24c02"; reg = <0x50>; }; }; ``` - 在`of_platform_default_populate_init`中,当处理到I2C总线节点(即上面的`i2c1`节点)时,由于它的compatible属性匹配了`of_default_bus_match_table`中的`"simple-bus"`等(实际上,I2C总线节点的compatible通常是特定的,但可能不会匹配`simple-bus`,所以这里需要更精确的解释),因此会递归处理其子节点(即`eeprom@50`)。但是,对于I2C子节点,它们不会被转换为`platform_device`,而是由I2C总线驱动处理(引用[2])。 - 具体来说,I2C总线驱动在初始化时(通常使用`subsys_initcall`)会注册一个`i2c_adapter`,然后调用`of_i2c_register_devices()`来注册该总线下的I2C设备(即生成`i2c_client`,而不是`platform_device`)。因此,设备树中I2C总线节点的子节点不会由`of_platform_default_populate_init`转换为`platform_device`。 6. **总结流程**: - 内核启动到`arch_initcall`阶段,执行`of_platform_default_populate_init`。 - 该函数遍历设备树根节点的子节点,对每个节点: - 如果节点是总线类型(匹配`of_default_bus_match_table`),则递归处理其子节点。 - 然后,为该节点创建`platform_device`(如果它满足条件),并注册到`platform_bus_type`总线上。 - 对于非platform bus的其他总线(如I2C、SPI等),它们的设备节点不会被转换为`platform_device`,而是由各自的总线驱动处理。 注意:设备树中有些节点不会转换为platform_device,比如: - 没有compatible属性的节点。 - 根节点本身。 - 处理为I2C、SPI等总线的设备节点(它们由各自的总线驱动处理)。 因此,`of_platform_default_populate_init`主要处理的是直接挂在平台总线(platform bus)上的设备,以及一些简单总线(如simple-bus)上的设备(这些总线上的设备也会被转换为platform_device)。 ### 挂载到总线的关键代码 在`of_platform_bus_create()`中,创建设备的函数调用链: ```c of_platform_bus_create() if (of_platform_bus_create(child, ...)) // 递归创建子总线设备 ... of_platform_device_create_pdata() struct platform_device *dev; dev = of_device_alloc(np, bus_id, parent); // 分配并设置platform_device ... of_device_add(dev); // 内部调用device_add,将设备添加到总线 ``` 其中,`of_device_alloc`会从设备树节点中提取资源(如内存区域、中断等)并填充到`platform_device`的`resource`数组中。然后,`of_device_add`最终调用`device_add`,该函数会将该设备(`dev->dev`)添加到其总线上(这里是`platform_bus_type`)。总线会进行驱动匹配,如果找到匹配的驱动,则调用驱动的`probe`函数。 ### 示例:设备树节点转换 假设设备树中有如下节点: ```dts / { ... mydevice { compatible = "myvendor,mydevice"; reg = <0x10000000 0x1000>; interrupts = <0 10 4>; }; }; ``` 这个节点会被转换为一个`platform_device`,其`name`为`"myvendor,mydevice"`(或者根据compatible生成),资源包括一块内存区域(起始0x10000000,长度0x1000)和一个中断号(10)。该设备会被注册到`platform_bus_type`总线上。 ### 相关问题 §§相关问题§§ 1. 设备树中哪些节点不会被转换为platform_device? 2. 平台总线和设备树的关系是什么? 3. 如何编写一个平台设备驱动来匹配设备树生成的platform_device? 4. I2C设备在设备树中是如何表示并被内核处理的?</think>### `of_platform_default_populate_init` 如何将设备树节点转换为 `platform_device` 并挂载到总线 `of_platform_default_populate_init` 是 Linux 内核初始化过程中的关键函数,负责将设备树Device Tree)中的节点转换为 `platform_device` 并注册到对应的总线(主要是平台总线 `platform_bus_type`)。其工作流程如下: --- #### **1. 函数调用时机** - **执行阶段**:在 `arch_initcall` 优先级(优先级 3)被调用,位于内核启动流程的 `do_initcalls()` 阶段[^2]。 - **调用链**: ```mermaid graph TD start_kernel --> rest_init rest_init --> kernel_init kernel_init --> kernel_init_freeable kernel_init_freeable --> do_basic_setup do_basic_setup --> do_initcalls do_initcalls --> of_platform_default_populate_init ``` --- #### **2. 设备树节点转换条件** 并非所有设备树节点都会被转换,需满足以下条件[^4]: 1. **必须包含 `compatible` 属性**:标识设备驱动匹配的关键。 2. **特定节点类型**: - 根节点的直接子节点(需含 `compatible`)。 - 特殊总线节点的子节点(如 `simple-bus`, `simple-mfd`, `isa`, `arm,amba-bus` 等)。 --- #### **3. 转换与注册流程** ```mermaid graph LR A[of_platform_default_populate_init] --> B[of_platform_populate] B --> C{遍历根节点子节点} C --> D[of_platform_bus_create] D --> E[检查节点是否匹配总线类型] E -->|是| F[递归处理子节点] E -->|否| G[of_platform_device_create_pdata] G --> H[of_device_alloc] H --> I[填充 platform_device 资源] I --> J[platform_device_register] J --> K[挂载到 platform_bus_type] ``` ##### 关键步骤详解: 1. **节点遍历**: 通过 `of_platform_populate()` 遍历设备树根节点的子节点[^5]。 2. **总线类型检查**: 使用 `of_default_bus_match_table` 匹配特殊总线类型(如 `simple-bus`)。若匹配,则递归处理其子节点[^4][^5]。 3. **创建 `platform_device`**: - **资源分配**:`of_device_alloc()` 根据节点属性(`reg`, `interrupts` 等)填充 `platform_device` 的 `resource` 结构[^5]。 - **设备初始化**:设置 `dev.of_node` 指向原始设备树节点,保留 `compatible` 等关键属性[^1]。 4. **注册到总线**: 通过 `platform_device_register()` 将设备添加到 `platform_bus_type` 总线。此时触发总线匹配机制: - 总线遍历已注册的 `platform_driver`。 - 匹配驱动与设备的 `compatible` 字符串。 - 若匹配成功,调用驱动的 `probe()` 函数[^1]。 --- #### **4. 总线挂载的底层实现** - **平台总线注册**: `platform_bus_type` 在内核启动早期(`core_initcall` 优先级)已注册: ```c struct bus_type platform_bus_type = { .name = "platform", .match = platform_match, // 匹配函数 }; core_initcall(platform_bus_init); // 优先级 1 ``` 其中 `platform_match()` 通过比较设备和驱动的 `compatible` 实现匹配[^1]。 - **设备注册触发匹配**: 当 `platform_device_register()` 执行时,内核调用: ```c bus_add_device(dev); // 将设备加入总线 bus_probe_device(dev); // 触发驱动匹配 ``` --- #### **5. 特殊总线节点的处理** 对于非平台总线(如 I2C、SPI): 1. **I2C 总线节点**: 设备树中的 I2C 控制器节点会被转换为 `platform_device`(因其在 `compatible` 中声明为平台设备),但挂载的 I2C 设备(如 `eeprom@50`)由 I2C 子系统通过 `of_i2c_register_devices()` 处理,生成 `i2c_client` 而非 `platform_device`[^2]。 2. **递归处理**: 若节点是 `simple-bus` 类型,`of_platform_bus_create()` 会递归处理其子节点,确保层级结构完整转换[^4][^5]。 --- ### 总结 - ✅ **转换前提**:仅含 `compatible` 的节点被转换,根节点、纯硬件描述节点(如 `clock`)除外[^3][^4]。 - ✅ **总线挂载**:通过 `platform_device_register()` 注册到 `platform_bus_type`,触发驱动匹配。 - ⚠️ **递归处理**:特殊总线节点(如 `simple-bus`)的子节点会被递归转换,形成设备层级[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值