APP下载

Linux 装置树(DTS)的深入理解

消息来源:baojiabao.com 作者: 发布时间:2026-05-22

报价宝综合消息Linux 装置树(DTS)的深入理解

装置树的出现

上一节说过装置树的出现是为了解决核心中大量的板级档案程式码,通过 DTS 可以像应用程序里的 XML 语言一样很方便的对硬件资讯进行配置。关于装置树的出现其实在 2005 年时候就已经在 PowerPC Linux 里出现了,由于 DTS 的方便性,慢慢地被广泛应用到 ARM、MIPS、X86 等架构上。为了理解装置树的出现的好处,先来看下在使用装置树之前是采用什么方式的。

关于硬件的描述资讯之前一般放在一个个类似 arch/xxx/mach-xxx/board-xxx.c 的档案中,如:

static struct resource gitchat_resource = {

{

.start = 0x20100000 ,

.end = 0x20100000 +1,

.flags = IORESOURCE_MEM

.start = IRQ_PF IRQ_PF 15 ,

.end = IRQ_PF IRQ_PF 15 ,

.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE

}

};

static struct platform_device gitchat_device = {

.name name ="gitchat",

.id = 0,

.num_resources num_resources = ARRAY_SIZE(gitchat_resource),

.resource = gitchat_resource,

};

static struct platform_device *ip0x_devices __initdata ={

&gitchat_device,

};

static int __init ip0x_init(void)

{

platform_add_devices(ip0x_devices, ARRAY_SIZE(ip0x_devices));

}

一个很少的地址获取,我们就要写大量的类似程式码,当年 Linus 看到核心里有大量的类似程式码,很是生气并且在 Linux 邮件列表里发了份邮件,才有了现在的装置树概念,至于装置树的出现到底带来了哪些好处,先看一下装置树的档案:

eth:[email protected] 4,c00000 {

compatible ="csdn, gitchat";

reg =4 0x00c00000 0x2

4 0x00c00002 0x2

>;

interrupt-parent =;

interrupts=;

};从程式码中可看到对于 GITCHAT 这个网络卡驱动、一些暂存器、中断号和上一层 gpio 节点都很清晰的被描述。比上一图的程式码优化了很多,也容易维护了很多。这样就形成了装置在指令码,驱动在 c 档案里的关系图:

从图中可以看出 A、B、C 三个板子里都含有 GITCHAT 装置树档案,这样对于 GITCHAT 驱动写一份就可以在 A、B、C 三个板子里共用。从上幅图里不难看出,其实装置树的出现在软件模型上相对于之前并没有太大的改变,装置树的出现主要在装置维护上有了更上一层楼的提高,此外在核心编译上使核心更精简,映象更小。

装置树的档案结构和剖析

装置树和装置树之间到底是什么关系,有着哪些依赖和联络,先看下装置树之间的关系图:

除了装置树(DTS)外,还存有 dtsi 档案,就像程式码里的标头档案一样,是不同装置树共有的装置档案,这不难理解,但是值得注意的是如果 dts 和 dtsi 里都对某个属性进行定义的话,底层覆盖上层的属性定义。这样的好处是什么呢?假如你要做一块电路板,电路板里有很多模组是已经存在的,这样就可以直接像包含标头档案一样把共性的 dtsi 档案包含进来,大大减少工作量,后期也可以对类似模组再次利用。

装置树档案的格式是 dts,包含的标头档案格式是 dtsi,dts 档案是一种程序员可以看懂的格式,但是 Uboot 和 Linux 只能识别二进位制档案,不能直接识别。所以就需要把 dts 档案编译成 dtb 档案。把 dts 编译成 dtb 档案的工具是 dtc,位于核心目录下 scripts/dtc,也可以手动安装:sudo apt-get install device-tree-compiler 工具。具体 dts 是如何转换成机器码并在内存里供 kernel 识别的,请看下图:

装置树的应用

有了理论,在具体的工程里如何做装置树呢?这里介绍三大法宝:文件、指令码、程式码。文件是对各种 node 的描述,位于核心 documentation/devicetree/bingdings/arm/ 下,指令码就是装置树 dts,程式码就是你要写的装置程式码,一般位于 arch/arm/ 下,以后在写装置的时候可以用这种方法,绝对的事半功倍。很多上层应用开发者没有做过核心开发的经验,对核心一直觉得很神秘,其实可以换一种思路来看核心,相信上层应用开发者最熟悉的就是各种 API,工作中可以说就是和 API 打交道,对于核心也可以想象是各种 API,只不过是核心态的 API。这里装置档案就是根据各种核心态的 API 来呼叫装置树里的板级资讯:

struct device_node *of_find_node_by_phandle(phandle handle);

struct device_node *of_get_parent(const struct device_node_ *node);

of_get_child_count

of_property_read_u32_array

of_property_read_u64

of_property_read_string

of_property_read_string_array

of_property_read_bool

具体的用法这里不做进一步的解释,大家可以查询资料或者看官网解释。

这里对装置树做个总结,装置树可以总结为三大作用:一是平台标识,所谓平台标识就是板级识别,让核心知道当前使用的是哪个开发板,这里识别的方式是根据 root 节点下的 compatible 字段来匹配。二是执行时配置,就是在核心启动的时候 ramdisk 的配置,比如 bootargs 的配置,ramdisk 的起始和结束地址。三是装置资讯集合,这也是最重要的资讯,集合了各种装置控制器,接下来的实践课会对这一作用重点应用。这里有张图对大家理解装置树作用有一定的帮助:

【部分内容整理于宋宝华老师课程】

【推荐阅读】

CPU是如何访问内存的?

实体地址和虚拟地址的分布

Linux核心内存管理算法Buddy和Slab

Linux使用者态程序的内存管理

新增极客助手微信,加入技术交流群

长按,扫码,关注公众号

好看、留言、转发!

2019-12-20 00:05:00

相关文章