4、设备树绑定与规范:设备树绑定文档、Schema验证、YAML绑定格式、检查绑定正确性
设备树绑定,说白了就是一份「说明书」。
它告诉内核:某个硬件设备该怎么描述,哪些属性是必须的,哪些是可选的。我在早期做嵌入式开发时,就吃过没写绑定的亏——自己写的设备树节点,过了一个月再看,完全忘了那些自定义属性是干嘛用的。嗯,从那以后,我养成了写绑定文档的习惯。
4.1 设备树绑定文档
绑定文档通常放在内核源码的 Documentation/devicetree/bindings/ 目录下。每个子系统都有自己的子目录,比如 gpio/、i2c/、pinctrl/ 等。
文档里要写清楚:
- 兼容性字符串:比如
compatible = "vendor,device" - 必需属性:比如 reg、interrupts、clocks 等
- 可选属性:比如 gpio-hog、power-supply 等
- 子节点要求:比如某些总线控制器要求子节点有特定的 reg 格式
- 示例节点:一个完整的设备树节点示例
我个人习惯:写绑定文档时,先列一个表格,把属性名、类型、是否必需、描述都写清楚。这样后续维护时一目了然。
4.2 Schema验证
光有文档还不够。你想想看,如果设备树写错了,内核启动时可能直接 panic。所以我们需要一种自动化的验证方式。
Linux 内核现在使用 DT Schema 来做这件事。它基于 JSON Schema 标准,用 YAML 格式编写。每个绑定文件对应一个 schema 文件,扩展名是 .yaml。
验证工具是 dt-validate,它会检查:
- 属性名是否拼写正确
- 属性值类型是否匹配(比如字符串写成了数字)
- 必需属性是否缺失
- 子节点数量是否符合要求
避坑指南:我曾经遇到过一个问题——设备树里把 interrupts 写成了 interrupt,少了个 s。内核编译没报错,但中断就是不起作用。后来用 dt-validate 一跑,秒出错误。所以我现在每次提交代码前,都会跑一遍 schema 验证。
4.3 YAML绑定格式
YAML 绑定文件长什么样?我给你看一个典型的例子:
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/gpio-leds.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: GPIO LEDs
maintainers:
- Jacek Anaszewski <jacek.anaszewski@gmail.com>
- Pavel Machek <pavel@ucw.cz>
properties:
compatible:
const: gpio-leds
led:
type: object
additionalProperties: false
properties:
gpios:
maxItems: 1
label:
$ref: /schemas/types.yaml#/definitions/string
linux,default-trigger:
$ref: /schemas/types.yaml#/definitions/string
default-state:
enum: [on, off, keep]
required:
- gpios
required:
- compatible
additionalProperties: false
examples:
- |
leds {
compatible = "gpio-leds";
led-0 {
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
label = "power";
linux,default-trigger = "heartbeat";
};
};
这个文件里,properties 定义了节点可以有哪些属性,required 定义了哪些是必须的,examples 给出了一个完整示例。
有几个关键点要注意:
$ref用来引用其他 schema 文件,比如类型定义additionalProperties: false表示不允许出现未定义的属性enum限制属性值只能从枚举列表中选maxItems限制数组的最大长度
注意:YAML 对缩进非常敏感。我见过有人用 tab 缩进,结果 schema 解析失败。请务必使用空格,推荐 2 个空格缩进。
4.4 检查绑定正确性
写完绑定文件后,怎么检查它对不对?
内核提供了一个测试框架,叫 dt_binding_check。你可以在内核源码根目录下运行:
make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/gpio/gpio-leds.yaml
这个命令会:
- 解析你的 YAML 文件,检查语法是否正确
- 验证 schema 本身是否符合 meta-schema 的规范
- 如果有示例,还会检查示例是否合法
另外,还有 dtbs_check 命令,它会用所有绑定文件去验证实际的设备树文件:
make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/gpio/gpio-leds.yaml
这个命令会扫描所有 .dts 文件,找出那些不符合你绑定规范的节点。
我的经验:在 Yocto 项目中,我会在 local.conf 里加上 INHERIT += "devicetree",这样每次编译时都会自动验证设备树。如果验证失败,构建过程会直接报错,不会生成有问题的镜像。
4.5 知识体系结构图
下面这张图展示了设备树绑定的完整工作流:
这张图展示了从编写绑定文档到最终生成设备树的完整流程。我个人建议,在 Yocto 项目中一定要把 dt_binding_check 和 dtbs_check 集成到构建流程里。这样能提前发现很多问题,避免在硬件上调试时抓狂。
小技巧:如果你不确定某个属性该怎么写,可以去内核源码里搜一下同类型设备的绑定文件。比如你想写一个 I2C 设备的绑定,就看看 Documentation/devicetree/bindings/i2c/ 目录下的例子。模仿是最快的学习方式。
好了,关于设备树绑定与规范,核心就是这些。记住:绑定文档不是摆设,它是你和内核之间的一份「契约」。写清楚、验证好,能省下大把调试时间。