Linux设备树简析

news/2024/11/8 9:42:02/

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'rebooting 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.ppmy.cn/news/11413.html

相关文章

OMNet++安装pydev

菜单栏->Help->Install New SoftWare->Add 输入 Pydev http://pydev.sourceforge.net/pydev_update_site/5.2.0链接为下载5.2.0版本&#xff0c;若jdk<1.8安装5.2.0之前的版本。\color{blue}{链接为下载5.2.0版本&#xff0c;若jdk<1.8安装5.2.0之前的版本。}链…

电力系统机组组合优化调度(IEEE14节点、IEEE30节点、IEEE118节点)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f4dd;目前更新&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;电力系统相关知识&#xff0c;期刊论文&…

4.R语言【rehsape2包】介绍、melt( )、cast( )函数、其他使用技巧

b站课程视频链接&#xff1a; https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99&#x1f622;&#x1f622;元买了&#xff0c;感觉讲的没问题&#xff0c;就是知识点结构有点乱&#xff0c;有点废话&#xff09;&…

caoz的梦呓:谈谈信息不对称与To B业务的难点【推荐】

一、谈谈信息不对称 一直以来&#xff0c;大部分商业行为获利的一个重要基础是信息不对称。互联网时代一个巨大的谎言是&#xff0c;信息是公开透明的&#xff0c;但是很遗憾&#xff0c;这不是真的。 第一&#xff0c;一些非常具有价值的信息资源&#xff0c;并不在互联网流…

由数据范围反推算法复杂度以及算法内容

文章目录一、由数据范围反推算法复杂度以及算法内容二、数据范围三、其他知识点1. long 和 int 的大小跟系统位数有关2. memset 常用赋值一、由数据范围反推算法复杂度以及算法内容 一般 ACM 或者笔试题的时间限制是 1s 或 2s。在这种情况下&#xff0c;C 代码中的操作次数控制…

MacOS上效率神器 Alfred的基本用法

文章目录前言软件安装基础功能配置和使用基础配置基础功能搜索功能Web 搜索计算器词典查询和通讯录查询系统功能其它扩展Bookmarks 的应用设计 Workflows最后参考文章前言 工欲善其事必先利其器&#xff0c;Alfred 在 Mac 上所迸发的效率是前所未有的&#xff01; 作为 Mac 上…

进入内存,透彻理解数据类型存在的意义,整形在内存中存储,大小端字节序,浮点型在内存中存储

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习C进阶、数据结构、算法、JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01;&#x1f4dc;所属专栏&#xff1a;C语言✈往期博文回顾&#xff1a;【Java基础篇…

【uniapp】记录地址管理页面

uniapp中的地址管理页面 <template><view class"container"><view class"oldaddress" v-for"(item,index) in cardInfo" :key"index"><view class"topview"><view class"name">{{i…