Linux设备树简析

news2025/1/18 8:41:08

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 设备树的来源

Linux 中,每个设备驱动,管理一组设备数据,类似面向对象编程中类和其实例对象的关系。一段时间以来,这些设备数据硬编码中内核中,导致了内核代码的急剧膨胀(尤其是在ARM架构下),同时影响了维护的便利性。社区总瓢把子 Linus 对此表达了强烈的不满,要求整改。针对该问题,社区经过一系列地讨论,引入了设备树。
设备树是数据驱动逻辑思想(即数据与逻辑分离)的一个典型应用。通过将设备数据从内核代码迁移到设备树文件(.dts)中,然后经由 DTC(Device Tree Compiler) 编译器,将设备树文件 .dts 编译成 DTB(Device Tree Blob) 数据文件;内核通过对 DTB 数据文件的解析展开,最终以展开额设备树为基础,创建设备驱动的设备对象。我们用下图来描述整个过程:

     dtc       unflatten_device_tree()               of_platform_populate()
.dts ---> .dtb ----------------------> 设备树 of_root --------------------> 创建驱动设备对象

3. 设备树文件的创建

《Power_ePAPR_APPROVED_v1.1.pdf》规范文档定义的语法,根据系统中实际硬件设备的拓扑,构建设备树文件。
Linux 内核的设备树文件,按不同的硬件架构和硬件,组织定义在内核源码目录 arch/arch-XXX/boot/dts/* 目录下。
设备树文件组织成树形结构,只有1个根节点。如:
在这里插入图片描述

4. 设备树文件的编译

设备树文件 .dts ,经由内核代码目录下的编译器 scripts/dtc/dtc , 编译成 .dtb 文件:

      dtc
.dts -----> .dtb

5. 设备树的展开

BootLoader 将设备树数据文件 .dtb 在内存中的物理地址传递给内核,内核解析该数据文件,然后展开它,具体代码流程如下:

start_kernel()
	setup_arch()
		unflatten_device_tree()
			/* 将 .dtb 展开为以 of_root 为根的设备树 */
			__unflatten_device_tree(initial_boot_params, NULL, &of_root, 
									early_init_dt_alloc_memory_arch, false)
				/* 第1遍,计算设备树展开后的大小 */
				size = unflatten_dt_nodes(blob, NULL, dad, NULL);
				
				/* 为展开后的设备树分配空间: 额外4字节存储展开后设备树的魔数 */
				mem = dt_alloc(size + 4, __alignof__(struct device_node));
				
				/* 在最后4字节,存储展开后设备树的魔数 */
				*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
				
				/* 第2遍,做设备树实际的展开动作 */
				unflatten_dt_nodes(blob, mem, dad, mynodes)
					for (offset = 0;
					     offset >= 0 && depth >= initial_depth;
					     offset = fdt_next_node(blob, offset, &depth)) {
					     populate_node(blob, offset, &mem, ...)
					     	/* 分配节点空间 */
					     	struct device_node *np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, ...);
							of_node_init(np)
					     		kobject_init(&node->kobj, &of_node_ktype);
								node->fwnode.ops = &of_fwnode_ops;
							/* 展开节点属性 */
							populate_properties(blob, offset, mem, np, pathp, dryrun);
					}

通过上面的代码分析,可以将 .dtsdtc 编译器生成的 .dtb 数据文件的结构总结如下图:
在这里插入图片描述
其中,.dtb 文件头部用数据结构 struct fdt_header 描述:

struct fdt_header {
	fdt32_t magic; /* .dtb文件魔数: 0xd00dfeed */
	fdt32_t totalsize; /* .dtb文件字节数总大小 */
	fdt32_t off_dt_struct; /* .dts 节点和属性数据区间偏移 */
	fdt32_t off_dt_strings;	/* .dts 节点属性名区间偏移 */
	fdt32_t off_mem_rsvmap;	/* .dts 保留内存定义区间偏移 */
	fdt32_t version;		 /* format version */
	fdt32_t last_comp_version;	 /* last compatible version */

	/* version 2 fields below */
	fdt32_t boot_cpuid_phys;	 /* Which physical CPU id we're
					    booting on */
	/* version 3 fields below */
	fdt32_t size_dt_strings; /* .dts 节点属性名区间字节数大小 */

	/* version 17 fields below */
	fdt32_t size_dt_struct;		 /* .dts 节点和属性数据区间字节数大小 */
};

dts 设备树节点数据以 struct fdt_node_header 描述,展开后以 struct device_node 描述:

struct fdt_node_header {
	fdt32_t tag; /* 节点 tag: FDT_BEGIN_NODE */
	char name[0]; /* 节点名称,以 \0 结尾 */
};
struct device_node {
	const char *name;
	const char *type;
	phandle phandle; /* dts 节点的句柄,经常在节点间相互引用时使用。通常由 dtc 编译器隐式添加 */
	const char *full_name; /* dts 节点全路径名 */
	struct fwnode_handle fwnode;

	struct	property *properties; /* dts 属性节点 */
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent; /* dts 父节点 */
	struct	device_node *child; /* dts 子节点 */
	struct	device_node *sibling; /* dts 兄弟节点 */
	struct	kobject kobj;
	unsigned long _flags;
	void	*data;
	...
};

dts 设备树节点属性数据以 struct fdt_property 描述,展开后以 struct property 描述:

struct fdt_property {
	fdt32_t tag; /* FDT_PROP */
	fdt32_t len; /* data[] 的长度 */
	fdt32_t nameoff; /* 节点属性名称偏移  */
	/*
	 * 节点属性值, 如有 dts 定义:
	 * /{
	 * 		model = "FriendlyElec NanoPi-M1-Plus";
	 * 		......
	 * };
	 * 则 data[] 的值为 FriendlyElec NanoPi-M1-Plus\0 
	 */
	char data[0];
};
struct property {
	char	*name; /* dts 节点属性名称 */
	int	length; /* dts 节点属性数据长度: 即 @value 长度 */
	void	*value; /* dts 节点属性数据,长度为 @length */
	struct property *next; /* dts 节点的下一属性 */
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;
};

6. 创建设备树节点的设备对象

以章节 5. 设备树的展开 展开的设备树为基础,内核创建设备对象并绑定到对应的驱动,其具体流程如下:

/* 启动内核初始化线程 */
start_kernel()
	rest_init()
		kernel_thread(kernel_init, NULL, CLONE_FS)

/* 进入内核初始化线程 */
kernel_init()
	kernel_init_freeable()
		...
		of_platform_default_populate_init()
			of_platform_default_populate(NULL, NULL, NULL)
				of_platform_populate(root, of_default_bus_match_table, lookup, parent)
					for_each_child_of_node(root, child) {
						of_platform_bus_create(child, matches, lookup, parent, true)
							/* 创建 platform bus 设备,绑定设备到驱动 */
							dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
							/* 递归创建 platform bus 设备 */
							for_each_child_of_node(bus, child) {
								of_platform_bus_create(child, matches, lookup, &dev->dev, strict)
							}
							of_node_set_flag(bus, OF_POPULATED_BUS);
					}

注意到,这里并没有为所有 DTS 设备树里的节点创建设备对象。像 i2c 总线上的设备,是通过注册 i2c 总线驱动时,触发的 i2c client 设备的创建和相应驱动的绑定动作,其它的如 mmc 总线设备,是通过总线注册或定时扫描完成的从设创建和驱动绑定流程,感兴趣的读者可自行阅读相关内核代码。

7. 设备树相关工具

fdtget : 读取设备树的内容
fdtput : 写属性数据到 .dtb 文件
fdtdump: 导出设备树

8. 参考资料

《Power_ePAPR_APPROVED_v1.1.pdf》
https://manpages.debian.org/testing/device-tree-compiler/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/155622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频播放破亿,抖音近期的流量密码是什么

纵观12月抖音涨粉趋势,美食、医疗健康、生活日常等细分领域中涌现出不少优质账号,圈粉不断。从『粉丝飙升榜』TOP30来看, 12月上榜达人的更替率高达76.6%,向太陈岚单日涨粉557.26w,12月共收获751.09w粉丝,空…

做好网络舆情监测监控的重要性,TOOM网络舆情监控平台建设方案?

舆情监控在当今时代非常重要,互联网走进千家万户,各种信息在网络上传播,舆情监控旨在帮助公司了解公众对其产品、服务、品牌形象等的看法,并及时采取应对措施。接下来简单了解做好网络舆情监测监控的重要性,TOOM网络舆…

关于城市轨道交通的电力监控中心调度系统研究

摘 要 :在城市轨道交通的运行过程中,电力监控系统很好地监控了各个配电所、电力设备以及接触网等的运行情况,这对于城市轨道交通的安全稳定运行有着关键性的作用。因此,随着当今城市轨道交通事业的不断发展,城市轨道交…

MATLAB | 如何使用MATLAB绘制序列logo图

这次开发了一个生物信息学比较常用的序列logo图绘制MATLAB代码包,绘制效果如下: 数据来自基迪奥生物项目编号为seqlogojrois9l2jit的示例数据。同时本工具函数参考以下文献: Tareen A, Kinney J B. Logomaker: beautiful sequence logos in …

再见2022,你好2023:八年程序媛老兵的践行、思考与展望

再见2022,你好2023写在前面的话1.2022速记1.1 产假前,ParaView三维自动化项目1.2 产假后,EDA仿真项目1.3 从EDA行业谈谈2022年的经济寒冬2. 2023年的新年flag2.1 flag one:挑战高薪2.2 flag two:读更多的书,读更多专业书2.2.1 读过…

动态内存管理题目讲解

前言: 上一期我们讲述了有关动态内存管理的知识点,这期我们通过几个经典的笔试题来进行深入的了解以及对知识点的巩固 目录第一题第二题第三题第四套第一题 试题如下: void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) …

1.H3CNE-计算机网络概述

计算机网络概述计算机网络定义一组自治计算机互联的集合计算机网络基本功能资源共享综合信息服务分布式处理与负载均衡计算机网络的类型局域网LAN(Local Area Network) 由用户自行建设,使用私有地址组建的网络城域网MAN(Metropolitan Area Network)由运营…

为什么职场第一开发语言会是SQL?看完这些你就瞬间明白了

看到一个有趣的比喻,用来说明SQL与Excel的差别是什么。 如果把SQL比作火车,把Excel更比作卡车。 卡车灵活自由,高速或乡村小道想去哪就去哪,但即便每天不停歇卡车的运载量也不大,而且容易出交通事故。 火车运载量大…

windows下软件安装:miniconda下安装R4.1.3并将其添加到 Jupyter notebook 中

0. 说明: 本来是想在windows中用conda安装R,然后再下载安装RStudio并对其进行配置使之可以用conda环境中的R,但是经过尝试以及网络上查找相关文档发现,原版RStudio不支持使用conda环境中的R(可能Anaconda中自带的RStu…

Visual studio C++桌面应用程序添加外部文件引用

C桌面应用程序添加外部文件引用 前言 之前对C的开发接触很少,本章节记录一下Visual studio开发C桌面应用程序是如何引入外部文件 ★提高阅读体验★ 👉 ♠一级标题 👈 👉 ♥二级标题 👈 👉 ♥ 三级标…

Apache Spark 机器学习 基本统计 1

1 基本概念 相关性,是指两个变量或者两个系列变量的关联程度,也就是,其中一方变量的变化会影响另外一方变量的变化。 相关性分为三种关系,正相关、负相关以及不相关。 正相关,从单调递增的角度看,其中一…

Netty基础入门——文件编程、网络编程【2】

Netty基础入门——文件编程、网络编程【2】 基础入门【1】 1 文件编程 1.1 channel 两个channel传输数据 transferTo方法一次性最多传输2G大小的文件,如果超出会丢弃 public static void main(String[] args) {try (FileChannel from new FileInputStream(&quo…

APM系统是什么?有什么用处?

自SpringCloud问世以来,微服务以席卷之势风靡全球,企业架构都在从传统SOA向微服务转型。然而微服务这把双刃剑在带来各种优势的同时,也给运维、性能监控、错误的排查带来的极大的困难。在大型项目中,服务架构会包含数十乃至上百个…

分布式助力光伏太阳能规模化发展解决方案

行业背景 光伏太阳能作为一种清洁环保的能源,得到各种开发利用,光伏太阳能电池板是其中的重点研究对象,其质量是影响太阳能电池发电效率的主要因素,所以对电池板表面质量的检测是生产中一个重要环节。随着工业的发展,太阳能电池板…

198:vue+openlayers 解决drawend后不能获取当前feature的方法

第198个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayers项目中绘制矩形,drawend触发事件,要获取到当前绘制的feature的信息。drawend触发的时刻,add feature to the source or collection 这个变化还没有发生,所以用source.getFeatures()是获取不到最新数据的。可…

OpenStack 认证Api

在调用OpenStack的Api或者其它组建的Api时都需要进行 OpenStack 认证,在这里记录一下如何调用OpenStack 认证接口或者token 和给其它接口增加token的方式一. 调用OpenStack auth接口接口地址:http://ip:5000/v3/auth/tokens参数:{"auth&…

特色风情小镇行业发展动态及市场需求前景分析

2023-2029年中国特色风情小镇行业发展动态及市场需求前景报告报告编号:1691653免费目录下载:http://www.cninfo360.com/yjbg/qthy/qt/20230110/1691653.html本报告著作权归博研咨询所有,未经书面许可,任何组织和个人不得以任何形式…

结构体内存对齐与结构体位段:学习笔记8

目录 一.结构体基础知识 1. 结构体的特殊声明 2. 结构的自引用 3.结构体变量的定义和初始化 二.结构体内存对齐 1.关键概念: 2.计算示例 3.嵌套结构体的内存计算 4.结构体内存对齐的意义 5.定义结构体时的注意事项 6.修改默认对齐数 附:关…

【PWA学习】5. 使用 Notification API 来进行消息提醒

引言 在上一节, 介绍了如何使用 Push API 进行服务端消息推送。提到 Push 就不得不说与其联系紧密的另一个 API——Notification API。它让我们可以在“网站外”显示消息提示: 消息推送示例即使当你切换到其他 Tab,也可以通过提醒交互来快速让用户回到你…

webviz安装,docker安装可正常使用与Foxglove Studio

Foxglove Studio Foxglove Studio与webviz使用起来非常类似 去可以直接使用web也可以下载安装包 Foxglove Studio不提供源码 安装包下载地