linux 设备树详解
2 Linux 设备树
2.1 什么是设备树?
设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息。
设备树的机制其实也是总线型的 BUS/Dev/Drv 模型,只是编写 Dev 的方式变了。即编写 设备树文件 .dts。dst 文件会被编译成 dtb 文件。dtb文件会传给内核, 内核会解析dtb文件, 构造出一系列的 device_node 结构体,
device_node 结构体会转换为 platform_device 结构体。
不了解 总线型驱动可看另外一篇:
https://blog.csdn.net/weixin_46640184/article/details/124229608
对应的实例可以看这篇
https://blog.csdn.net/weixin_46640184/article/details/124291470
所以: 我们可以在 dts 文件中指定资源, 不再需要在 .c 文件中设置 platform_device 结构体
“来自dts的platform_device结构体” 与 “我们写的platform_driver” 的匹配过程:
- "来自 dts 的 platform_device 结构体"里面有成员 “.dev.of_node”, 它里面含有各种属性, 比如 compatible, reg, pin
- “我们写的 platform_driver” 里面有成员 “.driver.of_match_table”, 它表示能支持哪些来自于 dts 的platform_device
如果 “of_node 中的 compatible” 跟 “of_match_table 中的 compatible” 一致, 就表示匹配成功, 则调用 platform_driver中的probe函数; 在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zKh4329-1650347058800)(assets/image-20220419103127867.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cDajUKjD-1650347058801)(assets/image-20220419103411940.png)]
2.2 设备树的规范
2.2.1 DTS 格式
//语法:
//Devicetree node格式:
[label:] node-name[@unit-address] {[properties definitions][child nodes]
};//Property格式1:
[label:] property-name = value;//Property格式2(没有值):
[label:] property-name;/* Property取值只有3种: * arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示), * string(字符串), * bytestring(1个或多个字节)
*///示例:
//a. Arrays of cells : cell就是一个32位的数据
interrupts = <17 0xc>;//b. 64bit数据使用2个cell来表示:
clock-frequency = <0x00000001 0x00000000>;//c. A null-terminated string (有结束符的字符串):
compatible = "simple-bus";//d. A bytestring(字节序列) :
local-mac-address = [00 00 12 34 56 78]; // 每个byte使用2个16进制数来表示, 必须两位
local-mac-address = [000012345678]; // 每个byte使用2个16进制数来表示// 可以是各种值的组合, 用逗号隔开:
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";
2.2.2 DTS 文件布局
/dts-v1/; //版本
[memory reservations] // 格式为: /memreserve/ <address> <length>;
/ { // '/'表示根节点[property definitions][child nodes]
};
[memory reservations] 作用:
留出该空间给自己使用。
2.2.3 特殊的、默认的属性
根节点
#address-cells
在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells
在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible
定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
即这个板子兼容哪些平台
uImage : smdk2410 smdk2440 mini2440 ==> machine_descmodel
咱这个板子是什么
比如有2款板子配置基本一致, 它们的 compatible 是一样的
那么就通过model来分辨这2款板子
/memory
device_type = "memory"; reg = < start_addr memory_size >; // 用来指定内存的地址、大小 //e.g.: memory{device_type = "memory";reg = <0x80000000 0x20000000>; }
/chosen
bootargs // 内核command line参数, 跟u-boot中设置的bootargs作用一样 //e.g.: chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};
/cpus
/cpus 节点下有 1 个或多个 cpu 子节点, cpu 子节点中用 reg 属性用来标明自己是哪一个 cpu
所以 /cpus 中有以下2个属性:#address-cells
在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells
在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
必须设置为0
cpus {cpu {compatible = "arm,arm926ej-s";};};
2.2.4 引用其他节点:
phandle
节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)
pic@10000000 {phandle = <1>;interrupt-controller;
};another-device-node {interrupt-parent = <1>; // 使用phandle值为1来引用上述节点
};
label
PIC: pic@10000000 {interrupt-controller;
};another-device-node {interrupt-parent = <&PIC>; // 使用label来引用上述节点, // 使用lable时实际上也是使用phandle来引用, // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};
2.2.5 DTB 文件布局
2.3 内核对设备树的处理
2.3.1 前提
Linux uses DT data for three major purposes:
- platform identification,
- runtime configuration, and
- device population.
bootloader启动内核时,会设置r0,r1,r2三个寄存器,
- r0一般设置为0;
- r1一般设置为machine id (在使用设备树时该参数没有被使用);
- r2一般设置ATAGS或DTB的开始地址
2.3.2 设备树中平台信息的处理
设备树根节点的compatible属性列出了一系列的字符串,
表示它兼容的单板名,
从"最兼容"到次之
内核中有多个machine_desc, 其中有dt_compat成员, 它指向一个字符串数组, 里面表示该 machine_desc支持哪些单板,
使用 compatile 属性的值, 跟每一个 machine_desc.dt_compat 比较,成绩为"吻合的 compatile 属性值的位置", 成绩越低越匹配, 对应的 machine_desc 即被选中,选中后就采用该 machine_desc 的初始化函数初始化。
2.3.3 函数调用过程
start_kernel // init/main.csetup_arch(&command_line); // arch/arm/kernel/setup.cmdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.cearly_init_dt_verify(phys_to_virt(dt_phys) // 判断是否有效的dtb, drivers/of/ftd.cinitial_boot_params = params;mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); // 找到最匹配的machine_desc, drivers/of/ftd.cwhile ((data = get_next_compat(&compat))) {score = of_flat_dt_match(dt_root, compat);if (score > 0 && score < best_score) {best_data = data;best_score = score;}}machine_desc = mdesc;
2.4 对设备树中运行时配置信息的处理
2.4.1 函数调用过程
start_kernel // init/main.csetup_arch(&command_line); // arch/arm/kernel/setup.cmdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.cearly_init_dt_scan_nodes(); // drivers/of/ftd.c/* Retrieve various information from the /chosen node */of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);/* Initialize {size,address}-cells info */of_scan_flat_dt(early_init_dt_scan_root, NULL);/* Setup memory, calling early_init_dt_add_memory_arch */of_scan_flat_dt(early_init_dt_scan_memory, NULL);
- /chosen节点中bootargs属性的值, 存入全局变量: boot_command_line
- 确定根节点的这2个属性的值: #address-cells, #size-cells
存入全局变量: dt_root_addr_cells, dt_root_size_cells - 解析/memory中的reg属性, 提取出"base, size", 最终调用memblock_add(base, size);
2. 5 dtb 转换为device_node(unflatten)
2.5.1 函数调用过程
start_kernel // init/main.csetup_arch(&command_line); // arch/arm/kernel/setup.carm_memblock_init(mdesc); // arch/arm/kernel/setup.cearly_init_fdt_reserve_self();/* Reserve the dtb region */// 把DTB所占区域保留下来, 即调用: memblock_reserveearly_init_dt_reserve_memory_arch(__pa(initial_boot_params),fdt_totalsize(initial_boot_params),0); early_init_fdt_scan_reserved_mem(); // 根据dtb中的memreserve信息, 调用memblock_reserveunflatten_device_tree(); // arch/arm/kernel/setup.c__unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false); // drivers/of/fdt.c/* First pass, scan for size */size = unflatten_dt_nodes(blob, NULL, dad, NULL);/* Allocate memory for the expanded device tree */mem = dt_alloc(size + 4, __alignof__(struct device_node));/* Second pass, do actual unflattening */unflatten_dt_nodes(blob, mem, dad, mynodes);populate_nodenp = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));np->full_name = fn = ((char *)np) + sizeof(*np);populate_propertiespp = unflatten_dt_alloc(mem, sizeof(struct property),__alignof__(struct property));pp->name = (char *)pname;pp->length = sz;pp->value = (__be32 *)val;
2.5.2 规则
在DTB文件中,
每一个节点都以TAG(FDT_BEGIN_NODE, 0x00000001)开始, 节点内部可以嵌套其他节点,
每一个属性都以TAG(FDT_PROP, 0x00000003)开始每一个节点都转换为一个device_node结构体:
利用该结构体构造树。
struct device_node {const char *name; // 来自节点中的name属性, 如果没有该属性, 则设为"NULL"const char *type; // 来自节点中的device_type属性, 如果没有该属性, 则设为"NULL"phandle phandle;const char *full_name; // 节点的名字, node-name[@unit-address]struct fwnode_handle fwnode;struct property *properties; // 节点的属性struct property *deadprops; /* removed properties */struct device_node *parent; // 节点的父亲struct device_node *child; // 节点的孩子(子节点)struct device_node *sibling; // 节点的兄弟(同级节点)#if defined(CONFIG_OF_KOBJ)struct kobject kobj;#endifunsigned long _flags;void *data;#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;#endif};
device_node结构体中有properties, 用来表示该节点的属性
//每一个属性对应一个property结构体: struct property {char *name; // 属性名字, 指向dtb文件中的字符串int length; // 属性值的长度void *value; // 属性值, 指向dtb文件中value所在位置, 数据仍以big endian存储struct property *next;#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)unsigned long _flags;#endif#if defined(CONFIG_OF_PROMTREE)unsigned int unique_id;#endif#if defined(CONFIG_OF_KOBJ)struct bin_attribute attr;#endif};
这些device_node构成一棵树, 根节点为: of_root
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQvHDi0U-1650347058808)(assets/image-20220419130539279.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsDnxQWl-1650347058809)(assets/image-20220419130112484.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJFJEhde-1650347058809)(assets/image-20220419130333904.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q21U6JYf-1650347058809)(assets/image-20220419130424248.png)]
2.6 device_node转换为platform_device
dts -> dtb -> device_node -> platform_device
两个问题:
哪些device_node可以转换为platform_device?
- 根节点下含有compatile属性的子节点
- 如果一个结点的compatile属性含有这些特殊的值(“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”)之一, 那么它的子结点(需含compatile属性)也可以转换为platform_device
- i2c, spi等总线节点下的子节点, 应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device
怎么转换?
- platform_device中含有resource数组, 它来自device_node的reg, interrupts属性;
- platform_device.dev.of_node指向device_node, 可以通过它获得其他属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6mBk2JB-1650347058810)(assets/image-20220419131303274.png)]
2.7 Dev 和 Drv 比配过程
平台总线上有个
其中的 platform_match 用来判断 platform_device 与 platform_drive 是否匹配
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jzKXZzp4-1650347058811)(assets/image-20220419132505934.png)]
e.g.:
- 先比较
platform_device.driver_override
和platform_driver.driver.name
- 再逐个比较
platform_device.dev.comatible
和platform_driver.driver.of_match_table
- 然后比较
platform_device.name
和platform_driver.idtable
- 最后比较
paltform_device.name
和platform_driver.driver.name
按这些顺序比较,有一个成功就采用。
2.8 总结
内核函数of_platform_default_populate_init, 遍历device_node树, 生成platform_device
并非所有的device_node都会转换为platform_device,只有以下的device_node会转换:
该节点必须含有compatible属性
根节点的子节点(节点必须含有compatible属性)
含有特殊compatible属性的节点的 子节点(子节点必须含有compatible属性):
这些特殊的compatilbe属性为: “simple-bus”, “simple-mfd”, “isa”, “arm,amba-bus”
//e.g.: /* /mytest会被转换为platform_device, 因为它兼容"simple-bus", 它的子节点/mytest/mytest@0 也会被转换为platform_device/i2c节点一般表示i2c控制器, 它会被转换为platform_device, 在内核中有对应的platform_driver; /i2c/at24c02节点不会被转换为platform_device, 它被如何处理完全由父节点的platform_driver决定, 一般是被创建为一个i2c_client。 */ / {mytest {compatile = "mytest", "simple-bus";mytest@0 {compatile = "mytest_0";};};i2c {compatile = "samsung,i2c";at24c02 {compatile = "at24c02"; };};};
2.9 内核中设备树的操作函数
include/linux/ 目录下有很多 of 开头的头文件:
处理 DTB
of_fdt.h
dtb文件的相关操作函数, 我们一般用不到, 因为dtb文件在内核中已经被转换为device_node树(它更易于使用)
处理device_node
of.h // 提供设备树的一般处理函数, //比如 of_property_read_u32(读取某个属性的u32值), of_get_child_count(获取某个device_node的子节点数)
of_address.h // 地址相关的函数, 比如 of_get_address(获得reg属性中的addr, size值)
of_match_device(从matches数组中取出与当前设备最匹配的一项)
of_dma.h // 设备树中DMA相关属性的函数
of_gpio.h // GPIO相关的函数
of_graph.h // GPU相关驱动中用到的函数, 从设备树中获得GPU信息
of_iommu.h // 很少用到
of_irq.h // 中断相关的函数
of_mdio.h // MDIO (Ethernet PHY) API
of_net.h // OF helpers for network devices.
of_pci.h // PCI相关函数
of_pdt.h // 很少用到
of_reserved_mem.h // reserved_mem的相关函数
处理 platform_device
of_platform.h // 把device_node转换为platform_device时用到的函数, // 比如of_device_alloc(根据device_node分配设置platform_device), // of_find_device_by_node (根据device_node查找到platform_device),// of_platform_bus_probe (处理device_node及它的子节点)
of_device.h // 设备相关的函数, 比如 of_match_device
2.10 在根文件系统中查看设备树
/sys/firmware/fdt
原始dtb文件
hexdump -C /sys/firmware/fdt
/sys/firmware/devicetree
以目录结构程现的dtb文件, 根节点对应base目录, 每一个节点对应一个目录, 每一个属性对应一个文件
/sys/devices/platform
系统中所有的platform_device, 有来自设备树的, 也有来有.c文件中注册的
对于来自设备树的platform_device, 可以进入 /sys/devices/platform/<设备名>/of_node 查看它的设备树属性
/proc/device-tree
是链接文件, 指向
/sys/firmware/devicetree/base
linux 设备树详解相关推荐
- Linux设备树详解
Linux设备树详解 设备树小故事 设备树文件 使用设备树 修改设备树文件 编译设备树 异常处理 编写驱动文件 参考资料 设备树小故事 设备树(Device Tree),将这个词分开就是"设 ...
- 正点原子----Linux设备树详解
linux设备树 1.什么是设备树 2.DTS.DTB.DTC的关系 3.如何编译设备树 4.DTS基本结构 4.1.语法 4.2.设备树在系统中的体现 4.3.尝试自己添加节点 4.4.尝试对根节点 ...
- 【Linux驱动开发】Linux设备树详解
目录 一.设备树基础 1.概念 2.文件格式 3.编译工具 二.DTS语法 1..dtsi 头文件 2. 设备节点 3.标准属性 4.compatible 属性详解 5.修改设备树文件,增加或修改节点 ...
- 奇小葩讲设备树(5/5)-- Linux设备树详解(五)设备树的使用
对于任何的知识来说,了解了理论的知识,知道了设备树怎么解析用以代替传统的范式之后,我们需要知道怎么使用设备树.对于使用我们分两部分,一部分是它有哪些接口,能做些什么,至于怎么编写dts文件本章不讨论. ...
- 奇小葩讲设备树(4/5)-- Linux设备树详解(四)kernel的解析
uboot将一些参数,设备树文件传给内核,那么内核如何处理这些设备树文件呢?本章就kernel解析设备树的过程和原理,本章的主要内容以Device Tree相关的数据流分析为索引,对ARM linux ...
- 奇小葩讲设备树(2/5)-- Linux设备树详解(二)文件构成
设备树就是描述单板资源以及设备的一种文本文件.至于出现的原因,基本的语法和使用方法,上一章节做了基本的介绍.本篇文章主要是更深层次的探讨设备文件的构成. 1. devie tree的编译 Device ...
- 奇小葩讲设备树(1/5)-- Linux设备树详解(一) 基础知识
关于设备树,之前就经过详细的系统培训,但是本着会用就行的原则,对各个知识点都没有进行系统的总结.都是用到哪里学哪里,时间长了,基本也忘记了.所以对于后期知识各个知识点进行总结,本章主要讨论一下内容,能 ...
- linux 视频教程 韦山东,韦东山 linux 设备树详解
简 介 设备树视频录制完毕,29节,现在只要69元.学员对此课程的评价:这是最翔实最实惠最精益求精的设备树教程,感兴趣的了解一下, 以下是课程详情~ [设备树是什么?] 设备树是一种机制,用文本的方式 ...
- 奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递
前面两节介绍了设备的基本概念.编译.结构的组成,本章讨论的主要内容为 dtb如何通过Bootloader引导程序加载到内核 bootloader如何解析dbt bootloader支持哪些dtb的操作 ...
最新文章
- 以太坊开启区块链2.0时代
- 内网穿透从搭建到溯源
- CTFshow 文件上传 web155
- 【Hibernate】Hibernate查询语言HQL详解
- virtual多态 你不知道的事情
- 如何模拟Spring bean(版本2)
- 重新查看Play Framework发布的值
- Java(五)异常处理,异常参数,自定义异常,嵌套异常
- node yarn_使用Yarn Plug'n'Play摆脱node_modules
- 常用API2 正则表达式
- 博文目录(最新更新:2018.6.6)
- 单片机ADC采样算法----限幅平均滤波法
- python中的dict是什么数据类型_Python基本数据类型之dict
- AD9371开发总结(一)
- java程序员必须安装的软件
- html调查问卷页面,html+js 问卷调查页面的展示以及form提交
- 用canvas画太极图(一步步详解附带源代码)
- 【dgl框架】dgl.metapath_reachable_graph函数解析
- 一个微信公众号sdk(封装的比较全面)
- Python报错处理libpng warning: iCCP: cHRM chunk does not match sRGB
热门文章
- Vue h5 调用微信扫码接口
- 树莓派无法识别摄像头+树莓派中opencv调用视频流人脸检测
- Activity销毁不调用Ondestroy情况以及处理
- 频谱仪的更改ip_【正点原子FPGA连载】第五十一章 基于FFT IP核的音频频谱仪-摘自【正点原子】开拓者 FPGA 开发指南 (amobbs.com 阿莫电子论坛)...
- 为了让5G更省电,这家设备商秀出黑科技
- linux:shell命令之软硬链接
- 从USB数据采集板看技术造诣
- 如何将PDF删除水印?PDF怎么删除水印
- 如何用电脑画平面坐标图_如何在WORD或者EXERL上画坐标图?
- 微信小程序之图片压缩