春节期间调一块新板子串口能打满logrootfs也挂上了偏偏板载的一个LED怎么点都不亮。查原理图GPIO挂在SPI控制器下面设备树里配了节点驱动也编译进去了——问题出在哪最后发现是compatible string没对齐。一个细节折腾一下午。设备树到底在解决什么问题嵌入式Linux的发展史上设备树Device Tree的出现是为了终结一个噩梦arch/arm/目录下那堆越来越臃肿的board-*.c文件。每出一个新板子就要改内核代码重新编译内核——这不是做产品的方式。设备树把硬件描述从内核源码中抽离出来变成一种独立的数据结构。内核编译一次不同板子用不同的.dtb文件即可。思路很直接硬件有哪些外设、地址在哪、中断号多少、时钟怎么配——全写在dts里内核启动时解析出来驱动按描述去匹配。一个dts节点长什么样spi1 { status okay; pinctrl-names default; pinctrl-0 pinctrl_spi1; led_controller: gpio-expander0 { compatible microchip,mcp23s17; reg 0; spi-max-frequency 10000000; gpio-controller; #gpio-cells 2; microchip,spi-present-mask 0x01; }; };MCP23S17是一款通过SPI扩展GPIO的芯片。注意compatible字段——驱动就是靠它来找设备的。microchip,mcp23s17这个字符串会出现在驱动的of_match_table里两边配对成功probe才被调用。从文本到二进制dtc做了什么dts写的是给人看的。内核编译时dtcDevice Tree Compiler把dts编译成二进制dtb。dtb是扁平化的逐层flattened device tree结构头部包含了magic number、totalsize、结构块和字符串块的偏移。可以反编译看看实际内容dtc -I dtb -O dts -o dump.dts board.dtb这个命令把二进制的dtb还原成可读的dts。我经常用这个操作来确认自己写的设备树有没有被正确编译。有一次写错了一个属性名dts编译没报错dtc对不认识属性名默认忽略但驱动没有找到它预期的资源。反编译一看属性名硬生生少了一个字母。驱动那边怎么匹配的以mcp23s17为例驱动代码里这么写static const struct of_device_id mcp23s17_dt_ids[] { { .compatible microchip,mcp23s17 }, { .compatible microchip,mcp23s18 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mcp23s17_dt_ids); static struct spi_driver mcp23s17_driver { .driver { .name mcp23s17, .of_match_table mcp23s17_dt_ids, }, .probe mcp23s17_probe, .remove mcp23s17_remove, };内核在启动过程中SPI核心注册了spi总线上的设备。当它遍历dtb中挂在spi1总线下的节点时会提取每个节点的compatible属性拿它与所有注册到SPI总线的驱动去比对。比对逻辑在drivers/base/platform.c和drivers/of/base.c里。内核先把驱动的of_device_id表和节点compatible逐个比对匹配上了就调用驱动的probe。匹配规则是compatible字符串可以有多组按优先级排列节点与驱动配对一个即可。常见误区一个容易忽视的地方compatible的命名规则是厂商,型号逗号前小写不能乱写。有些产商的手册里只写mcp23s17但内核驱动要求的带厂商前缀。写错了节点of_device_id比对失败probe函数永远不会被调用——这就是我那天的教训。另一个误区是status属性的处理。dtsi里通常会把大部分外设节点state disabled板级dts里按需打开。但有时候在dtsi中加了一个新节点却忘了改status节点有了地址有了资源也描述了但内核直接跳过。status支持的取值有okay、disabled、reserved、fail、fail-sss一般情况下就用前两个。再往深一点看设备树除了描述设备还能携带平台数据——通过properties和child nodes。比如gpio-controller和#gpio-cells定义了这是一个GPIO控制器以及address cells的数量。一个有意思的设计是你可以在dts里把自定义属性传给驱动// 在驱动probe里获取自定义属性 of_property_read_u32(node, microchip,spi-present-mask, mask);所以设备树不仅仅是个硬件清单它还是驱动和板级配置之间的数据通道。有些驱动完全靠dts里的属性来决策运行模式代码里不写死任何配置——可移植性就是这么来的。这是今天想分享的内容。你的板子上有没有遇到过类似的问题回头看了dtb反编译结果自然就有答案了。