第一次接触设备树插件这个概念,是在调试一块定制化开发板时。当时硬件同事临时增加了几个外设接口,按照传统方式需要重新编译整个设备树文件并烧写系统。正当我准备加班时,老司机拍了拍我肩膀:"试试overlay?像贴便利贴一样动态加载设备节点就行"。那一刻我才意识到,原来Linux设备树还能这么玩。
设备树插件(Device Tree Overlays)本质是对基础设备树(Base Device Tree)的增量修改。它允许我们在不重新编译整个DTB的情况下,动态添加、修改或删除设备树节点。这个机制在嵌入式开发中特别实用,比如:
设备树插件的运作可以类比为Photoshop的图层系统。基础设备树是背景层,而overlay则是上面的透明图层。当系统加载时,这些图层会按照特定规则合并:
/delete-node/指令移除现有节点内核中负责这个合并过程的正是OF(Open Firmware)子系统。具体流程如下:
bash复制原始DTB → 解析为内核数据结构 → 应用Overlay → 生成最终设备树
一个标准的overlay文件通常包含这些要素:
dts复制/dts-v1/;
/plugin/;
/* 目标板卡兼容性声明 */
/ {
fragment@0 {
target = <&amba>;
__overlay__ {
/* 新增的I2C控制器 */
i2c@f000 {
compatible = "vendor,custom-i2c";
reg = <0xf000 0x100>;
#address-cells = <1>;
#size-cells = <0>;
};
};
};
};
特别注意几个语法要点:
/plugin/ 声明这是可叠加的片段fragment@X 定义修改片段target 指定要修改的父节点路径__overlay__ 包含实际的修改内容推荐使用以下工具链配置:
bash复制# 安装基础工具
sudo apt install device-tree-compiler flex bison
# 检查内核配置选项
CONFIG_OF_OVERLAY=y
CONFIG_OF_CONFIGFS=y
CONFIG_OF_RESOLVE=y
重要提示:内核版本建议≥4.4,早期版本对overlay支持不完善
以添加一个自定义GPIO设备为例:
dts复制/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&gpio>;
__overlay__ {
custom_pins: custom_pins {
brcm,pins = <12 13>;
brcm,function = <1>; /* 输出模式 */
};
};
};
};
bash复制dtc -@ -I dts -O dtb -o custom_gpio.dtbo custom_gpio.dts
bash复制# 配置configfs
mount -t configfs none /configfs
# 加载overlay
mkdir /configfs/device-tree/overlays/custom_gpio
cat custom_gpio.dtbo > /configfs/device-tree/overlays/custom_gpio/dtbo
遇到加载失败时,按这个顺序排查:
bash复制dmesg | grep -i overlay
bash复制fdtdump custom_gpio.dtbo
bash复制ls /proc/device-tree/
在产线测试中,我们可以通过overlay快速切换测试模式:
bash复制#!/bin/bash
for test_case in eth_test hdmi_test gpio_test; do
load_overlay $test_case.dtbo
run_test_script $test_case
unload_overlay $test_case
done
同一款SoC的不同硬件版本可以这样管理:
code复制base.dtb
├── variantA.dtbo (添加摄像头)
├── variantB.dtbo (替换LCD参数)
└── variantC.dtbo (禁用USB接口)
dts复制/* 错误示例:地址未考虑已有设备 */
reg = <0x1000 0x100>;
/* 正确做法:检查iomem分配 */
cat /proc/iomem
dts复制/* 需要先定义phandle */
__overlay__ {
custom_phandle: node@0 {
/* ... */
};
other_node {
ref = <&custom_phandle>;
};
};
/include/指令模块化设计以树莓派添加DHT11温湿度传感器为例:
硬件连接:
overlay配置 (dht11-overlay.dts):
dts复制/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
fragment@0 {
target = <&gpio>;
__overlay__ {
dht11_pin: dht11_pin {
brcm,pins = <4>;
brcm,function = <0>; /* 输入模式 */
};
};
};
fragment@1 {
target-path = "/";
__overlay__ {
dht11 {
compatible = "dht11";
gpios = <&gpio 4 0>;
status = "okay";
};
};
};
};
bash复制# 加载后检查设备节点
ls /sys/bus/platform/devices/dht11
# 读取传感器数据
cat /sys/class/hwmon/hwmon0/temp1_input
这个案例展示了如何通过overlay快速为现有硬件添加传感器支持,而无需修改系统镜像。在实际项目中,我遇到过GPIO驱动强度不够导致数据读取失败的情况,最终通过添加brcm,drive = <2>;属性解决了问题。这种细节正是overlay灵活性的最佳体现。