设备树自定义属性:写入、读取、验证
依据:2026-10-13《设备树自定义属性》录音,以及学习系统"驱动初级 → 平台设备 → 设备树自定义属性"的 DTS、驱动笔记。录音转写的函数名以笔记中的代码为准。这是课程示例整理,未在你的开发板上重新测试。
一句话看懂
DTS 在设备节点里写属性,驱动匹配到这个节点后,在 probe() 中按"属性名 + 数据类型"读取。 compatible 决定设备与驱动能否匹配;yh_addr/yh_age/yh_infor 是匹配后由驱动自己解析的示例数据。(录音约 00:07---02:14)
text
修改 DTS → 编译并让板子加载新 DTB → insmod 模块
→ compatible 匹配 → 内核调用 probe(pdev)
→ pdev->dev.of_node 找到节点
→ of_property_read_* 读取属性 → printk → dmesg 查看
1. DTS 写了什么
老师沿用上一节的 yhai-led-gpiod 节点,再加三个自定义属性:
dts
yhai-led-gpiod {
compatible = "yhai,led_gpiod";
status = "okay";
gpios = <&gpio TEGRA_GPIO(J, 7) GPIO_ACTIVE_HIGH>;
yh_addr = "chenddu";
yh_age = <32>;
yh_infor = <12 34 0x66>;
};
| 属性 | DTS 类型和本例值 | 驱动用什么读取 |
|---|---|---|
yh_addr |
字符串 "chenddu";只是演示字符串,并非 GPIO 物理地址。 |
of_property_read_string() |
yh_age |
一个 32 位整数 32。 |
of_property_read_u32() |
yh_infor |
三个 32 位整数:12、34、0x66。 |
of_property_read_u32_array(..., 3) |
属性名由示例驱动约定;内核不会自动理解 yh_age 的业务含义。实际项目中的设备专用属性应有清楚的 binding 文档,并按约定使用厂商前缀;这里的 yh_* 是课堂练习名。Linux 设备树 binding 建议
2. 驱动如何找到并读出这三个值
平台总线先用 compatible = "yhai,led_gpiod" 匹配驱动的 of_device_id,成功才调用 led_gpiod_probe(struct platform_device *pdev)。进入 probe() 后:
c
struct device_node *np = pdev->dev.of_node;
const char *name;
u32 age;
u32 info[3];
if (!of_property_read_string(np, "yh_addr", &name))
printk("yh_addr=%s\n", name);
if (!of_property_read_u32(np, "yh_age", &age))
printk("yh_age=%u\n", age);
if (!of_property_read_u32_array(np, "yh_infor", info, 3))
printk("yh_infor=%u %u 0x%x\n", info[0], info[1], info[2]);
| 写法 | 为什么这样写 |
|---|---|
pdev->dev.of_node |
pdev 是指针,用 -> 取其成员;dev 是内嵌结构体,用 . 取 of_node。得到的 np 指向本次匹配的设备树节点。 |
&name |
字符串读取函数要写回一个字符串指针 ,所以传指针变量的地址。成功后 name 指向设备树中的字符串;不是新分配给你的一块内存。 |
&age |
把一个 32 位整数写入 age,需要传变量地址。 |
info, 3 |
info 指向数组首元素;3 表示本次读三个 u32 元素。 |
!of_property_read_*() |
这些函数成功返回 0,所以 !0 为真才打印;失败返回负错误码。 |
%u 按十进制打印无符号整数,%x 按十六进制打印,所以最后一个数会显示为 0x66(若按示例 0x%x 格式打印)。读取 API 会按类型解析设备树数据,驱动不用自己拆 DTB 的原始字节。Linux OF 属性读取 API(录音约 02:14---07:36)
3. 验证时看什么
- **先更新 DTB:**保存 DTS,编译
dtbs,把生成的 DTB 放到板子实际启动所用的位置并重新启动。仅重新编译.ko不会让旧 DTB 出现新属性。 - **再加载驱动:**用匹配板上运行内核的构建目录编译模块,在板上
sudo insmod ./led.ko,然后sudo dmesg | tail -n 30。 - **看结果:**日志中应分别出现
yh_addr对应chenddu、yh_age对应32、yh_infor的三项值12/34/0x66,以及包含led_gpiod_probe的信息。老师原代码逐项打印数组,空格和0x前缀取决于具体printk格式。printk()不一定直接出现在命令行。(录音约 07:00---07:36)
**不要只看最后的 probe ok 2:**老师代码把三个属性都写在独立的 if 里。即使某个属性不存在或类型不对,那个 if 只会跳过打印,probe() 仍可能返回 0。如果实际驱动必须依赖该属性,就检查返回值并在失败时返回错误;如果属性可选,当前写法适合演示。
这节笔记里的 led_gpiod_probe() 只读取并打印属性,没有调用上一节的 gpiod_get() 点灯 。DTS 中保留 gpios 并不意味着这一版示例驱动已经控制 LED。找不到 probe 日志时,先核对当前加载的 DTB、节点 status 与两处 compatible;能进 probe 但缺少属性日志时,再核对属性名、类型及数组长度。
一分钟复述
设备树节点可以写驱动约定的属性。平台驱动通过
compatible匹配后,内核把pdev传给probe();pdev->dev.of_node指向该节点。我按属性类型分别调用of_property_read_string/u32/u32_array,返回 0 才说明读成功。改了 DTS 要让板子加载新 DTB,查看dmesg才能确认读到了值。
上一节:GPIO 通用化。