装置树的出现
上一节说过装置树的出现是为了解决核心中大量的板级档案程式码,通过 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使用者态程序的内存管理
新增极客助手微信,加入技术交流群
长按,扫码,关注公众号
好看、留言、转发!





























