linux 设备树简析

news2025/1/20 2:02:45

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/155219.html

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

相关文章

VMware-【Linux】centos7 /boot磁盘扩容

我的/boot当时安装的时候只分了不到200MB现在不够了所以扩容下这是我学习的文章,细节说明了很多地方还有一些坑:https://blog.51cto.com/u_15801765/5697187开始操作前,可以先用命令 df -hl 记录一下你现在/boot 剩余磁盘空间。到文章最后面我…

设计模式_创建型模式 -《工厂模式》

设计模式_创建型模式 -《工厂模式》 笔记整理自 黑马程序员Java设计模式详解, 23种Java设计模式(图解框架源码分析实战) 概述 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其…

MySQL学习之一条SQL查询语句的执行

文章目录前言一、MySQL基础架构二、连接器三、查询缓存四、分析器五、优化器六、执行器前言 今天我们通过一条SQL查询语句的执行过程,来剖析MySQL的基础架构。让我们从宏观上先有一个对MySQL的认识与了解 一、MySQL基础架构 首先,我们要对MySQL的整体架…

深入分析Linux PCI驱动框架分析(二)

说明: Kernel版本:4.14ARM64处理器使用工具:Source Insight 3.5, Visio 1. 概述 本文将分析Linux PCI子系统的框架,主要围绕Linux PCI子系统的初始化以及枚举过程分析;如果对具体的硬件缺乏了解&#xff…

详解c++---vector介绍

这里写目录标题什么是vectorvector的定义reservevector数据插入push_backinsertresizeassignvector数据的删除pop_backeraseclearvector性质查看sizecapacityemptymax_sizevector元素修改operator[ ]atfrontbackvector其他函数operatorswap什么是vector vector是表示可变大小数…

深入浅出Cookie、Session、Token:背后的技术原理

目录 简介 . 网站交互体验升级 1.1 无状态的 HTTP 协议 1.2 解决之道 2. Cookie方案 2.1 Cookie 定义和作用 2.2 服务端创建 Cookie 2.4 存在的问题 3. Session 方案 3.1 Session 机制的概念 3.2 简单的交互流程 3.3 Session 的实现方式 3.4 存在的问题 4. Token…

【微服务】Nacos 认证机制

目录 一、背景 二、需求 三、方案 1、安全架构选型 2、会话管理 2.1、会话选型 2.2、Session 登录流程 2.3、Token 登录流程 2.4、jwt 框架选型 2.5、会话超时 3、SSO 支持 4、UI设计 5、接口设计 6、数据库表设计 6.1、user表 6.2、roles表 7、Filter 拦截请求…

C++ —— 模板的基本概念和使用

目录 1.函数模板是什么 1.1函数模板的基本概念 1.2函数模板的基本使用 1.3函数模板的特化 1.4非类型模板参数 2.类模板是什么 2.1类模板的基本使用 2.2非类型模板参数 2.3类模板的特化 2.4模板特化后的优先级 3.函数模板不要分离编译 1.函数模板是什么 模板是一种…

python和MySQL的基础使用和数据的插入导出

一.基础使用第三方库pymysql除了使用图形化工具以外,我们也可以使用编程语言来执行SQL从而操作数据库。在Python中,使用第三方库:pymysql来完成对MySQL数据库的操作。安装创建到MySQL的数据库链接具体代码如下from pymysql import Connect #获取到MySQL数…

LVS+Keepalived+Nginx宏观整体结构与关键问答

视频链接:4-2 为什么要使用 LVS Nginx?_哔哩哔哩_bilibili ———————————————————————————————————————————————————————— 1. 问题: 为什么要使用LVS Nginx?&#xf…

C语言刮刮乐(掩码图的范例)

程序简介 这个程序模拟了刮刮乐的刮卡操作,按下鼠标左键并移动可以刮开刮卡层。 刮卡操作是通过掩码图实现的,一张隐藏的待刮开背景图,一张掩码图。 刮卡的时候,是在黑色的掩码图上画线,显示的时候,通过…

官方正品 | Ultralytics YOLOv8算法来啦(尖端SOTA模型)

🚀🚀🚀卷王之王 | Ultralytics YOLOv8 算法来啦!!✨✨✨ 一、前言简介 🎄🎈 📚 代码地址:卷王之王 | YOLOv8代码下载地址 📚 详细文档:https://…

代码随想录算法训练营第十四天字符串 java :二叉树理论基础 144前序遍历 145后续遍历94 中序遍历

系列文章目录 第十一天笔记 文章目录系列文章目录前言1、二叉树理论基础1.1二叉树的种类1.1 如何区分二叉树的遍历方式1.2 如何定义二叉树节点2 递归遍历2.1**前序遍历 AC代码**2.2**后序遍历 AC代码**2.3 **中序遍历 AC代码**3 迭代法4 层次遍历总结**什么是List<List <…

组态王软件与S7-1200无线MODBUS通信方案详解

本方案是组态软件与西门子 S7-1200进行无线 MODBUS 通信的实现方法。此方案可以作为西门子 S7-1200与组态软件的无线 MODBUS 通信实例。在本方案中采用了西门子PLC专用无线通讯终端DTD434MC&#xff0c;作为实现无线通讯的硬件设备。 一、方案概述 组态王配置为标准 MODBUS 主…

基础面试问题

在Java中获取当前的工作目录System.getProperty("user.dir")public class Test {public static void main(final String[] args) {final String dir System.getProperty("user.dir");System.out.println("current dir " dir);} }获取一定范围…

Redis01之Windows版本的Redis安装配置

目录 0. 学习网址 https://www.w3cschool.cn/redis/https://www.w3cschool.cn/redis/ 1. Redis简介 2. 下载 3. 安装和配置 3.1 window(略...) 3.2 linux(CentOS) 4. Redis支持五种数据类型 5.通过命令操作redis 0. 学习网址 https://www.w3cschool.cn/redis/http…

一文搞懂CPU如何控制I/O设备

1 接口和设备&#xff1a;经典适配器模式 输入输出设备不只是一个设备。大部分输入输出设备&#xff0c;都有&#xff1a; 它的接口&#xff08;Interface&#xff09;实际的I/O设备&#xff08;Actual I/O Device&#xff09; 硬件设备并非直接接入到总线上和CPU通信&#…

UOS 录制电脑播放的音频 / 内录音频

Windows 里面有一个“立体声混音”&#xff0c;可以内录电脑播放的音频&#xff0c;而不受到外界噪音的干扰。前段时间接到反馈说 UOS 的设置里面的音频输入里面没有可以选择的设备&#xff0c;这里就稍微探索了一下&#xff0c;发现 UOS 也是可以配置内录的。这里参考了一下这…

网络基础(一)

网络基础&#xff08;一&#xff09;计算机网络背景网络发展独立模式: &#xff08;计算机之间相互独立&#xff09;网络互联: ( 多台计算机连接在一起, 完成数据共享)局域网LAN: (计算机数量更多了, 通过交换机和路由器连接在一起);广域网WAN: &#xff08;将远隔千里的计算机…

vue入门到精通(一)

一、vue简介 Vue是一款用于构建用户界面的 JavaScript 框架。 它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。 无论是简单还是复杂的界面&#xff0c;Vue 都可以胜任。 二、vue3选项式…