300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 设备树之DTS与DTB格式

设备树之DTS与DTB格式

时间:2019-09-24 12:55:20

相关推荐

设备树之DTS与DTB格式

目录

一、设备树

二、DTS格式

2.1 属性

2.2 节点

2.3 引用其他节点

2.4 小总结

三、DTB格式

3.1 结构

3.2 分析

一、设备树

对于点灯字符设备驱动程序可以有三种写法,首先是传统方法,这种方式直接在程序中写死,其次是利用总线设备驱动模型,利用分离的思想,将程序规划为两部分,对于驱动部分一般是稳定的,在设备部分更改资源,最后是利用设备树方法,可以使用设备树直接指明引脚,而驱动写法的核心不变,差别就在于如何指定硬件资源,这三种方法有各自的优缺点

对于设备树,通过dts文件来指定资源,内核会根据.dts文件分配设置注册platform_device(平台设备),当更改单板时,只需要重新定义dts文件就可以了,dts文件最终会编译为dtb文件,启动单板的时候,既要启动内核,也要传入dtb文件,因此更改单板时,不需要重新编译驱动程序,只需要提供一个不一样的dtb文件

二、DTS格式

写设备树我们是写出dts文件,文件写出来之后根据编译器dtc编译为二进制dtb文件,DTS文件布局(layout)如下,第一行表示版本,第二行表示保留的的内存区域,比如板子有64M内存,里面想留下4M给自己使用,不想让内核使用,就可以定义这个选项,如果想让内核使用全部内存就可以省略这个选项

/dts-v1/;[memory reservations] /* 格式为:/memreserve/ <address> <length>; *//{ /* /表示根,设备树的起点 */[property definitions] /* 首先有属性来描述硬件 */[child nodes] /* 子节点,在子节点中还可以有子节点 */};

2.1 属性

对于属性"[property definitions]"的格式有两种,即无值和有值两种方式,属性后需要分号

Property格式1:[label:] property-name = value;Property格式2(没有值):[label:] property-name = value;

如下led节点,可以知道属性中的value值的形式有多种

led {compatible = "jz2440_led";pin = <S3C2410_GPF>; //S3C2410_GPF为一个宏};

对于value的值有三种表示:arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示)、string(字符串)、bytestring(1个或多个字节),这些值什么含义,完全取决于驱动程序

示例:

interrupts = <17 0xc>;(里面每一个数据都是32位数据)

clock-frequency = <0x00000001 0x00000000>; (64bit数据使用2个cell来表示)

compatible = "simple-bus"; (A null-terminated string有结束符的字符串)

A bytestring(字节序列) : local-mac-address = [00 00 12 34 56 78]; 或者local-mac-address = [000012345678]; (每个byte使用2个16进制数来表示 )

可以是各种值的组合, 用逗号隔开: compatible = "ns16550", "ns8250";example = <0xf00f0000 19>, "a strange property format";

特殊、默认的属性:

在根节点中

#address-cells 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)#size-cells在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)

如下,若CPU为64位的,则需要两个32位来表示reg,因此可以写为#address-cells = <1>; 对于reg中还可以写多个内存,如reg = <0x30000000 0x40000000 0 4096>; 第一个是地址单元 第二个表示大小,可以写多个address size

compatible:定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备,即这个板子兼容哪些平台, uImage是用来选择单板的,uImage支持很多单板每个单板都会有一个machine_desc结构体 ,如下设备树优先去寻找 “samsung,smdk2440”的machine_desc,如果找不到的话再来找 "samsung,s3c24xx"

chosen节点可以设置内核command line参数, 跟u-boot中设置的bootargs作用一样

cpus节点代表有多个CPU,对于个人的单板只有一个CPU,就可以只写一个子节点,在子节点中需要写device_type = "cpu";上面的例子没有引用cpus节点,cpu子节点中用reg属性用来标明自己是哪一个cpu, 所以 /cpus 中有以下2个属性: #address-cells:在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address),#size-cells:在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size),必须设置为0

/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory {device_type = "memory"; //这是大家约定的,必须写这个reg = <0x30000000 0x4000000>; //reg用来表示内存的起始地址和大小};/*cpus {cpu {compatible = "arm,arm926ej-s";};};*/chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};};

2.2 节点

对于节点"[child nodes]"的格式如下,可以看出节点可以有自己的子节点,节点结束后需要分号

[label:] node-name[@unit-address] { //label可以写也可以不写,写的时候别人引用该节点就比较方便,而node-name比如上面写的led[properties definitions][child nodes]};

对于node-name后面可以加上地址,组合起来就是节点的名字,假设有两个memory(内存)节点在同一级别就可以来用区别,用@加上内存的首地址来表示名字的不同,在memory的子目录下可以有同名的memory目录,当然没有这么古怪的节点,这里只是举例

memory@30000000{device_type = "memory";reg = <0x30000000 0x4000000>; memory{device_type = "memory";reg = <0 4096>; };};memory@0{device_type = "memory";reg = <0 4096>; };

2.3 引用其他节点

利用节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)

pic@10000000 {phandle = <1>;interrupt-controller;};another-device-node {interrupt-parent = <1>; // 使用phandle值为1来引用上述节点};

还有另外一种方式,实质也是利用了phandle属性,使用label来引用节点

PIC: pic@10000000 { //编译器dtc会在父节点插入一个 phandle = <xxx>; 然后会把下面的引用<&PIC>替换为<xxx>interrupt-controller; };another-device-node {interrupt-parent = <&PIC>; // 使用label来引用上述节点, // 使用lable时实际上也是使用phandle来引用, // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性};

举例,在dts中会把共同的地方抽出来然后写为dtsi文件

dtsi文件

// SPDX-License-Identifier: GPL-2.0/** SAMSUNG SMDK2440 board device tree source** Copyright (c) weidongshan@* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*/#define S3C2410_GPA(_nr)((0<<16) + (_nr))#define S3C2410_GPB(_nr)((1<<16) + (_nr))#define S3C2410_GPC(_nr)((2<<16) + (_nr))#define S3C2410_GPD(_nr)((3<<16) + (_nr))#define S3C2410_GPE(_nr)((4<<16) + (_nr))#define S3C2410_GPF(_nr)((5<<16) + (_nr))#define S3C2410_GPG(_nr)((6<<16) + (_nr))#define S3C2410_GPH(_nr)((7<<16) + (_nr))#define S3C2410_GPJ(_nr)((8<<16) + (_nr))#define S3C2410_GPK(_nr)((9<<16) + (_nr))#define S3C2410_GPL(_nr)((10<<16) + (_nr))#define S3C2410_GPM(_nr)((11<<16) + (_nr))/dts-v1/;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory { /* /memory */device_type = "memory";reg = <0x30000000 0x4000000 0 4096>;};/*cpus {cpu {compatible = "arm,arm926ej-s";};};*/chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};};

dts文件

// SPDX-License-Identifier: GPL-2.0/** SAMSUNG SMDK2440 board device tree source** Copyright (c) weidongshan@* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*//dts-v1/;#include "jz2440.dtsi"/ {led {pin = <S3C2410_GPF(6)>;};};

这样后面写的属性会覆盖前面的属性,反汇编"./scripts/dtc/dtc -I dtb -O dts -o tmp.dts arch/arm/boot/dts/jz2440.dtb ",查看tmp.dts可以看到led节点中的pin引脚就被覆盖了

/dts-v1/;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <0x1>;#size-cells = <0x1>;memory {device_type = "memory";reg = <0x30000000 0x4000000 0x0 0x1000>;};chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <0x50006>;};};

上面这种方式比较麻烦,若不想重头写led节点可以修改dtsi中的led节点加上label

LED: led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};

修改dts中的led节点,反汇编之后一样会看到节点的变化

/dts-v1/;#include "jz2440.dtsi"&LED {pin = <S3C2410_GPF(7)>;};

2.4 小总结

在内核文档中Documentation\devicetree\usage-model.txt,其中对设备树进行了三部分的总结,首先是平台识别信息,其次是运行时的配置,最后是设备枚举

Linux uses DT data for three major purposes:1) platform identification,2) runtime configuration, and3) device population.

举例

平台信息

model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;

运行时的配置,设置bootargs传给内核,还有/memreserve/ 0x33000000 0x10000;

chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};

设备枚举

LED: led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};

三、DTB格式

用dtc编译器把dts文件转换为dtb文件,首先会将宏展开,其次还有dtsi和dts编译为一个dtb文件,可以根据可读性强的代码进行帮我们找到错误,对于DTB格式,可以阅读官方文档:/specifications/ ,也可以阅读内核文档:Documentation/devicetree/booting-without-of.txt

3.1 结构

dtb文件分为四部分,首先是头部信息,其次是内存保留区、结构区和字符串区,对于保留区:例/memreserve/ 0x33f00000 0x100000;放在了memory reserve map,其中都是64位表示的,在dtb文件中数据存放的格式是以大端保存的,对于小端高位存放在高地址,对于大端低位存放在低地址

在官方文件中头部header的结构体,在文档描述了magic必须是D00DFEED,totalsize为文件大小,查看文件属性占用空间是4096而实际的大小是465个字节,465的16进制是1D1,因此是"01 D1 00 00",而off_dt_struct是structure block在文件中的偏移地址,off_dt_strings、off_mem_rsvmap同理,在strings block存放属性的名称,每一个名字有0字符结尾,属性的名字例如compatible用了多次为了节约空间,把属性的名字单独作为字符串保留在strings block里面

struct fdt__header {uint32_t magic;uint32_t totalsize;uint32_t off_dt_struct ;uint32_t off_dt_strings ;uint32_t off_mem_rsvmap;uint32_t version;uint32_t last_comp_version;uint32_t boot_cpuid_phys;uint32_t size_dt_strings;uint32_t size_dt_struct;};

对于内存保留区在文档中也有个结构体用来表示 ,并且都是64位的表示地址和大小,在下面例子中"/memreserve/ 0x33f00000 0x100000;"在dts文件中在第四行和五行标红的地方

struct fdt_reserve_entry {uint64_t address;uint64_t size;};

3.2 分析

dtb文件

// SPDX-License-Identifier: GPL-2.0/** SAMSUNG SMDK2440 board device tree source** Copyright (c) weidongshan@* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*/#define S3C2410_GPA(_nr)((0<<16) + (_nr))#define S3C2410_GPB(_nr)((1<<16) + (_nr))#define S3C2410_GPC(_nr)((2<<16) + (_nr))#define S3C2410_GPD(_nr)((3<<16) + (_nr))#define S3C2410_GPE(_nr)((4<<16) + (_nr))#define S3C2410_GPF(_nr)((5<<16) + (_nr))#define S3C2410_GPG(_nr)((6<<16) + (_nr))#define S3C2410_GPH(_nr)((7<<16) + (_nr))#define S3C2410_GPJ(_nr)((8<<16) + (_nr))#define S3C2410_GPK(_nr)((9<<16) + (_nr))#define S3C2410_GPL(_nr)((10<<16) + (_nr))#define S3C2410_GPM(_nr)((11<<16) + (_nr))/dts-v1/;/memreserve/ 0x33f00000 0x100000;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory { /* /memory */device_type = "memory";reg = <0x30000000 0x4000000 0 4096>;};/*cpus {cpu {compatible = "arm,arm926ej-s";};};*/chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};};

dts文件

分析device-tree structure区域,是最复杂的区,用0x00000001表示根节点和子节点的开始,从0x00000002结束,整个根节点结束 0x00000009

而节点的名字并不是放在strings block里面 而是紧跟着0x00000001后面加上节点名字,根节点没有名字,因此后面都0,直到0x00000003表示属性开始,那怎么表示名字和值呢,在属性后面有一个结构体,然后再加上val 如果val不是向4取整的话就会自动向4取整,对于第二个属性也一致

struct{uint32_t len; //val长度 uint32_t nameoff; //表示属性名字在strings blosk的偏移}

对于字符串的偏移值为188h, 第一个属性为model,大小为0x0A,在字符串区的偏移值为0,值为"53 4D 44 4B 32 34 34 34 30 00 00 00",自动向4取整,紧接着从第二属性开始即"00 00 00 03"

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。