Linux 驱动开辟基础知识——内核对设备树的处理惩罚与使用(十)

[复制链接]
发表于 2026-10-3 13:48:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

设备树写好了,内核到底怎么用

上一章讲完设备树的语法和怎么编译,这一章接上:内核拿到 .dtb 之后做了什么、哪些节点会变成 platform_device、driver 和 device 怎么配对、以及操作设备树要调哪些 API。

先把流程记住:设备树是给内核和驱动用的中介。板级信息原本写在 C 代码里,现在搬到 .dts 里描述,内核启动时解析成内存中的结构体,再按规则生成平台设备,最后由总线模型完成匹配。

一、内核对设备树的处理流程

1.1 每个节点都会有一个 device_node

内核启动时(setup_machine_fdt → of_flat_dt_match 这条链路上)会把 dtb 里每一个节点解析出来,逐一申请 struct device_node 并挂进全局的 tree。节点之间的父子关系、compatible、reg、status 这些属性都存在这个结构体的属性链表里。

也就是说,在内核视角里,设备树首先是一棵对象树,还不是设备。要变成真正的 device 还得过第二关。

1.2 哪些节点会变成 platform_device

不是所有节点都会生成 platform_device,判断规则很清楚:

  • 没有 compatible 属性的根节点会生成一个 platform_device。
  • 有 compatible 属性、且不是以下几种特殊节点的节点才会转换。

被排除的特殊节点包括:

  • Chosen、aliases、labels 这类不描述设备的节点;
  • 挂在某些特殊总线(如 i2c、spi)下的子节点——它们走自己的总线模型,生成的是 i2c_client / spi_device,不走 platform 总线。

1.3 转换时机与位置

转换发生在内核初始化阶段,具体在 platform_bus_init() / of_platform_populate() 里遍历整棵树,按上面规则为每个 eligible 节点填一个 struct platform_device:节点的 reg 变成 resource,name 取 compatible[0],然后 platform_device_register() 挂到 platform 总线上。

1.4 platform_device 怎么和 platform_driver 配对

内核里一个驱动想被匹配上,依次过四关——按顺序比较,命中即配对:

  1. 先比较是否强制选择某个 driver:设备树里若写了 force_driver(或驱动用 .id_table 指定),优先按指定 driver 匹配。
  2. 再比较设备树信息:取 device 的 of_device_id 表里的 .compatible 字符串,和节点的 compatible 属性比对。驱动里要定义 static const struct of_device_id xxx_of_match[] 并用 of_match_table 指过去。
  3. 接下比较 platform_device_id:如果节点带 platform_data 或者驱动提供 id_table,用 id 字段匹配。
  4. 最后比较:都用上后如果还是对不上,再比 name(老式匹配路径,现在的 DTS 驱动基本不走这条)。

匹配成功的 callback 就是驱动里的 .probe,此时 device 的 resource 已经在手,可以直接 platform_get_resource() 拿寄存器地址然后 ioremap。

二、操作设备树的常用内核 API

2.1 头文件在哪

  • 处理 dtb / 顶层:include/linux/of.h、include/linux/of_fdt.h(of_get_root_node()、of_get_next_child())
  • 处理 device_node:include/linux/of.h(of_find_node_by_name、of_property_read_u32、of_get_next_child)
  • 处理 platform_device:include/linux/of_platform.h、include/linux/platform_device.h

2.2 platform_device 相关函数

struct platform_device *pdev;
struct resource *res;

/* 按 device_node 反查 platform_device */
pdev = of_find_device_by_node(np);

/* 取资源:IORESOURCE_MEM / IORESOURCE_IRQ 等 */
res  = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ioremap(res->start, resource_size(res));

常见读写属性的函数:

of_property_read_u32(np, "reg", &val);        // 读 u32
of_property_read_string(np, "status", &s);   // 读字符串
of_get_next_child(np, &child);               // 遍历子节点
of_find_node_by_name(NULL, "xxx");           // 按名字找节点

2.3 有些节点不会生成 platform_device,怎么访问

比如 i2c 下的子节点生成的是 i2c_client、spi 下生成的是 spi_device。这时驱动里用对应总线的 API 拿节点,而不是 platform 那套:

struct i2c_client *client;
client = of_resolve_aliases... 
client->dev.of_node        // 从 client 拿回 device_node
i2c_get_client_data(client);

一句话:先找到对象(device_node),再按它属于哪条总线选择访问方式。这是驱动调试里最容易卡住的点——你在 DTS 里明明写了节点,驱动 probe 却不进,八成是节点类型走错了总线。

三、调试与排错

  • 看内核实际解析出什么:启动参数加 openfdt 相关调试,或在驱动里 pr_info("node name: %s\n", np->name) 打印。
  • probe 没被调用:先用 of_machine_is_compatible() 或 cat /sys/firmware/devicetree/base/ 确认节点在 sysfs 里存在,再查 driver 的 of_match_table 字符串有没有拼错(大小写、模块名前缀都要一致)。
  • 同一份 DTS 编译报错:先用 dtc -I dts -O dtb -o x.dtb x.dts 离线编译一遍,语法错误在编译期就暴露,别到板子上再看内核日志日志。
  • 资源地址不对:检查 reg 的地址单位(某些平台是字节、某些按 #address-cells 分格),以及有没有和 iomem 重叠。

小结

整条链路四步:dtb 解析成 device_node 树 → 按规则(有 compatible、非特殊节点)生成 platform_device → platform 总线上按 of_match_table / id_table 顺序配对 → probe 里用 of_* 系列 API 取属性取资源。记住「不是所有节点都会变成 platform_device」和「总线决定访问方式」这两条,能解决驱动里绝大多数「节点写了却不生效」的问题。

qidao123.com企服评测-应用市场 编辑提示

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表