2. 设备树语法

好,咱们今天来啃设备树语法这块硬骨头。说实话,很多工程师一看到设备树就头大,觉得它像个黑盒子。其实没那么玄乎,它就是个描述硬件信息的文本文件。我刚开始接触时也犯过迷糊,后来发现只要把几个核心概念搞明白,剩下的就是熟练工了。

2.1 节点——设备树的基本单元

节点是设备树里最基本的组成单位。你可以把它想象成一个硬件设备的“身份证”。每个节点都描述了一个具体的硬件设备或总线。

节点长什么样?看个例子:

/ {
    cpu {
        compatible = "arm,cortex-a7";
        reg = <0x00>;
    };
    
    memory {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;
    };
};

看到没?每个节点都用花括号 {} 包起来,里面可以嵌套子节点。根节点是 /,所有设备都挂在它下面。

我的经验:节点命名最好用全小写,单词之间用短横线连接。比如 i2c-controller 而不是 I2C_Controller。这是内核社区的约定,咱们跟着走准没错。

2.2 属性——描述节点的“形容词”

属性就是节点的特征描述。常用的属性就那么几种:

属性名 作用 示例
compatible 指定驱动匹配名称 "ti,am335x-uart"
reg 描述地址和大小 <0x44e09000 0x2000>
interrupts 指定中断号 <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>
status 设备状态 "okay" 或 "disabled"

属性值可以是字符串、32位整数、64位整数、字节数组等。我最常用的是 compatible 属性,它决定了内核能不能找到对应的驱动。

避坑指南:我曾经在一个项目里把 compatible 写成了 "my-uart",结果驱动死活加载不上。后来才发现,规范写法应该是 "厂商,设备名" 格式,比如 "ti,am335x-uart"。少了厂商前缀,内核匹配机制就认不出来了。

2.3 标签——给节点起个“外号”

标签就是节点的别名。为什么要用标签?因为节点路径太长了,比如 /soc/serial@44e09000,每次引用都得写全路径,多麻烦。

标签的用法很简单:

uart0: serial@44e09000 {
    compatible = "ti,am335x-uart";
    reg = <0x44e09000 0x2000>;
};

// 在其他地方引用
&uart0 {
    status = "okay";
};

看到 uart0: 了吗?这就是标签。后面用 &uart0 就能直接引用这个节点。我个人习惯给每个重要外设都加个标签,方便后续修改。

2.4 包含文件——代码复用的利器

设备树支持 #include 指令,跟C语言的头文件类似。这样可以把通用的定义抽出来,避免重复劳动。

#include "soc-common.dtsi"
#include "board-specific.dtsi"

/ {
    model = "My Custom Board";
    compatible = "my,board-v1";
};

.dtsi 文件是设备树包含文件,通常放一些芯片级或板级通用的定义。比如某个SoC的UART、I2C控制器定义,都可以放在 .dtsi 里。

我的建议:把芯片相关的定义放在 .dtsi 里,板级特有的配置放在 .dts 里。这样换板子时只需要改 .dts 文件,省事多了。

2.5 宏定义——让设备树更灵活

设备树里也能用宏定义!不过它用的是C预处理器,所以你得在 .dts 文件开头加上 #define。

#define UART_BASE 0x44e09000
#define UART_SIZE 0x2000

uart0: serial@44e09000 {
    reg = <UART_BASE UART_SIZE>;
};

宏定义的好处是:如果地址变了,只需要改一个地方。我在做多板兼容时特别喜欢用这招。

内核还提供了一些预定义的宏,比如中断类型:

#include <dt-bindings/interrupt-controller/irq.h>

interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;

这样写比直接写数字可读性强多了。你想想看,IRQ_TYPE_LEVEL_HIGH 一眼就知道是高电平触发,要是写个 4,鬼知道什么意思。

2.6 知识体系总览

说了这么多,咱们用一张图把设备树语法的核心逻辑串起来:

设备树语法核心结构 根节点 / 子节点(如 cpu、memory、uart) 节点属性 compatible = "厂商,设备名" reg = <地址 大小> interrupts = <中断号 类型> status = "okay" / "disabled" 标签 + 包含文件 + 宏定义

这张图把设备树语法的核心要素都串起来了。从根节点出发,下面挂子节点,子节点里填属性。标签、包含文件、宏定义这些辅助工具,让整个结构更灵活、更易维护。

核心要点:设备树语法其实就三件事——节点描述硬件、属性描述特征、辅助工具提升复用性。把这三点吃透了,写设备树就跟写配置文件一样简单。

嗯,语法部分就讲这么多。记住,多写多练,遇到问题别怕。我刚开始写设备树时也经常编译不过,后来慢慢就摸出门道了。你要是卡住了,回头看看这张图,思路就清晰了。