Linux 和设备树
“开放固件设备树”,简称 Devicetree (DT),是一种用于描述硬件的数据结构和语言。更具体地说,它是操作系统可读取的硬件描述,因此操作系统无需对机器的详细信息进行硬编码。
从结构上看,DT 是一棵树,或具有命名节点的非循环图,节点可以具有任意数量的命名属性,封装任意数据。在自然树结构之外,还存在一种机制来创建从一个节点到另一个节点的任意链接。
从概念上讲,定义了一组通用的使用约定(称为“绑定”),用于描述数据应如何出现在树中,以描述典型的硬件特性,包括数据总线、中断线、GPIO 连接和外围设备。
尽可能使用现有绑定来描述硬件,以最大限度地利用现有支持代码,但由于属性和节点名称只是文本字符串,因此很容易扩展现有绑定或通过定义新节点和属性来创建新绑定。但是,在创建新绑定之前,请务必先了解现有绑定。目前有两种不同的、不兼容的 i2c 总线绑定,这是因为在创建新绑定时没有先研究现有系统中 i2c 设备是如何枚举的。
数据模型
高级视图
最重要的是要理解 DT 只是一种描述硬件的数据结构。它没有什么神奇之处,也不能神奇地解决所有硬件配置问题。它的作用是提供一种语言,将硬件配置与 Linux 内核(或任何其他操作系统)中的主板和设备驱动程序支持分离开来。使用它可以让主板和设备支持由数据驱动;根据传入内核的数据而不是每台机器的硬编码选择来做出设置决策。
理想情况下,数据驱动平台设置应该减少代码重复,并更容易使用单个内核映像支持各种硬件。
Linux 使用 DT 数据有三个主要目的:
- 平台识别,
- 运行时配置,以及
- 设备数量。
平台识别
首先,内核将使用 DT 中的数据来识别特定机器。在理想情况下,特定平台对内核来说并不重要,因为所有平台细节都会以一致且可靠的方式由设备树完美描述。然而,硬件并不完美,因此内核必须在早期启动期间识别机器,以便有机会运行特定于机器的修复程序。
在大多数情况下,机器身份无关紧要,内核将根据机器的核心 CPU 或 SoC 选择设置代码。例如,在 ARM 上,arch/arm/kernel/setup.c 中的 setup_arch() 将调用 arch/arm/kernel/devtree.c 中的 setup_machine_fdt(),后者搜索 machine_desc 表并选择与设备树数据最匹配的 machine_desc。它通过查看根设备树节点中的“兼容”属性并将其与 struct machine_desc 中的 dt_compat 列表进行比较来确定最佳匹配(如果您感兴趣,该列表在 arch/arm/include/asm/mach/arch.h 中定义)。
“compatible” 属性包含一个排序的字符串列表,以机器的确切名称开头,后面是与机器兼容的可选主板列表,从最兼容到最不兼容排序。例如,TI BeagleBoard 及其后继产品 BeagleBoard xM 主板的根兼容属性可能分别如下所示:
compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3";
compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
“ti,omap3-beagleboard-xm” 指定了确切的型号,它还声称它与 OMAP 3450 SoC 以及 omap3 系列 SoC 兼容。您会注意到,列表按从最具体(确切的主板)到最不具体(SoC 系列)的顺序排列。
精明的读者可能会指出,Beagle xM 也可以声称与原始 Beagle 板兼容。但是,在板级这样做时应谨慎,因为即使在同一产品线中,从一个板到另一个板通常也会有很大的变化,并且很难确定当一个板声称与另一个板兼容时到底是什么意思。对于顶层,最好谨慎行事,不要声称一个板与另一个板兼容。值得注意的例外是当一个板是另一个板的载体时,例如连接到载体板上的 CPU 模块。
关于兼容值还有一点需要注意。兼容属性中使用的任何字符串都必须记录其含义。在 Documentation/devicetree/bindings 中添加兼容字符串的文档。
同样在 ARM 上,对于每个 machine_desc,内核会查看 dt_compat 列表条目中是否有任何条目出现在兼容属性中。如果出现,则该 machine_desc 是驱动机器的候选。在搜索整个 machine_descs 表后,setup_machine_fdt() 根据每个 machine_desc 与兼容属性中的哪个条目匹配,返回“最兼容”的 machine_desc。如果未找到匹配的 machine_desc,则返回 NULL。
这种方案背后的原因是,在大多数情况下,如果所有主板都使用相同的 SoC 或相同的 SoC 系列,则单个 machine_desc 可以支持大量主板。但是,总会有一些例外,即特定主板需要特殊的设置代码,而这些代码在一般情况下是没有用的。特殊情况可以通过在一般设置代码中明确检查有问题的主板来处理,但如果情况不止几种,这样做很快就会变得丑陋和/或难以维护。
相反,兼容列表允许通用 machine_desc 通过在 dt_compat 列表中指定“兼容性较差”的值来为广泛的通用主板集提供支持。在上面的示例中,通用主板支持可以声称与“ti,omap3”或“ti,omap3450”兼容。如果在原始 beagleboard 上发现一个错误,需要在早期启动期间使用特殊的解决方法代码,则可以添加一个新的 machine_desc,它实现解决方法并且仅与“ti,omap3-beagleboard”匹配。
PowerPC 使用略有不同的方案,它从每个 machine_desc 调用 .probe() 钩子,并使用第一个返回 TRUE 的钩子。但是,这种方法没有考虑兼容列表的优先级,对于新架构支持可能应该避免使用。
运行时配置
在大多数情况下,DT 是将数据从固件传送到内核的唯一方法,因此也用于传递运行时和配置数据,如内核参数字符串和 initrd 映像的位置。
大部分数据包含在 /chosen 节点中,在启动 Linux 时它看起来会像这样:
chosen {bootargs = "console=ttyS0,115200 loglevel=8";initrd-start = <0xc8000000>;initrd-end = <0xc8200000>;
};
bootargs 属性包含内核参数,而 initrd-* 属性定义 initrd blob 的地址和大小。请注意,initrd-end 是 initrd 映像之后的第一个地址,因此这与 struct resource 的通常语义不符。所选节点还可以选择包含任意数量的其他属性,用于特定于平台的配置数据。
在早期启动期间,架构设置代码会多次使用不同的辅助回调来调用 of_scan_flat_dt(),以便在设置分页之前解析设备树数据。of_scan_flat_dt() 代码会扫描设备树并使用辅助程序提取早期启动期间所需的信息。通常,early_init_dt_scan_chosen() 辅助程序用于解析所选节点(包括内核参数)、early_init_dt_scan_root() 用于初始化 DT 地址空间模型,early_init_dt_scan_memory() 用于确定可用 RAM 的大小和位置。
在 ARM 上,函数 setup_machine_fdt() 负责在选择支持主板的正确 machine_desc 后对设备树进行早期扫描。
设备填充
识别主板并解析早期配置数据后,内核初始化即可正常进行。在此过程中的某个时刻,会调用 unflatten_device_tree() 将数据转换为更高效的运行时表示。此时还会调用特定于机器的设置钩子,例如 ARM 上的 machine_desc .init_early()、.init_irq() 和 .init_machine() 钩子。本节的其余部分使用来自 ARM 实现的示例,但所有架构在使用 DT 时都会执行几乎相同的操作。
从名称就可以看出,.init_early() 用于在启动过程早期执行任何特定于机器的设置,而 .init_irq() 用于设置中断处理。使用 DT 不会从根本上改变这两个函数的行为。如果提供了 DT,则 .init_early() 和 .init_irq() 都可以调用任何 DT 查询函数(include/linux/of*.h 中的 of_*)来获取有关平台的其他数据。
DT 上下文中最有趣的钩子是 .init_machine(),它主要负责用有关平台的数据填充 Linux 设备模型。从历史上看,这是在嵌入式平台上实现的,方法是在板级支持 .c 文件中定义一组静态时钟结构、platform_devices 和其他数据,并将其全部注册到 .init_machine() 中。使用 DT 时,无需为每个平台硬编码静态设备,而是可以通过解析 DT 并动态分配设备结构来获取设备列表。
最简单的情况是 .init_machine() 仅负责注册一个 platform_devices 块。platform_device 是 Linux 用于表示硬件无法检测到的内存或 I/O 映射设备以及“复合”或“虚拟”设备(稍后会详细介绍)的概念。虽然 DT 没有“平台设备”术语,但平台设备大致对应于树根处的设备节点和简单内存映射总线节点的子节点。
现在正是展示示例的好时机。以下是 NVIDIA Tegra 板的设备树的一部分:
/{compatible = "nvidia,harmony", "nvidia,tegra20";#address-cells = <1>;#size-cells = <1>;interrupt-parent = <&intc>;chosen { };aliases { };memory {device_type = "memory";reg = <0x00000000 0x40000000>;};soc {compatible = "nvidia,tegra20-soc", "simple-bus";#address-cells = <1>;#size-cells = <1>;ranges;intc: interrupt-controller@50041000 {compatible = "nvidia,tegra20-gic";interrupt-controller;#interrupt-cells = <1>;reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >;};serial@70006300 {compatible = "nvidia,tegra20-uart";reg = <0x70006300 0x100>;interrupts = <122>;};i2s1: i2s@70002800 {compatible = "nvidia,tegra20-i2s";reg = <0x70002800 0x100>;interrupts = <77>;codec = <&wm8903>;};i2c@7000c000 {compatible = "nvidia,tegra20-i2c";#address-cells = <1>;#size-cells = <0>;reg = <0x7000c000 0x100>;interrupts = <70>;wm8903: codec@1a {compatible = "wlf,wm8903";reg = <0x1a>;interrupts = <347>;};};};sound {compatible = "nvidia,harmony-sound";i2s-controller = <&i2s1>;i2s-codec = <&wm8903>;};
};
在 .init_machine() 时,Tegra 主板支持代码将需要查看此 DT 并决定为哪些节点创建 platform_devices。但是,查看树时,无法立即看出每个节点代表哪种设备,甚至无法确定节点是否代表设备。/chosen、/aliases 和 /memory 节点是信息节点,不描述设备(尽管可以说内存可以被视为设备)。/soc 节点的子节点是内存映射设备,但编解码器@ 1a是i2c 设备,声音节点不代表设备,而是其他设备如何连接在一起以创建音频子系统。我知道每个设备是什么,因为我熟悉主板设计,但内核如何知道如何处理每个节点?
诀窍在于,内核从树的根开始,寻找具有“兼容”属性的节点。首先,通常假设具有“兼容”属性的任何节点都代表某种设备;其次,可以假设树根处的任何节点要么直接连接到处理器总线,要么是无法以其他方式描述的杂项系统设备。对于每个节点,Linux 都会分配并注册一个 platform_device,而该设备又可能绑定到 platform_driver。
为什么对这些节点使用 platform_device 是一个安全的假设?因为,对于 Linux 建模设备的方式,几乎所有的 bus_types 都假设其设备是总线控制器的子节点。例如,每个 i2c_client 都是 i2c_master 的子节点。每个 spi_device 都是 SPI 总线的子节点。USB、PCI、MDIO 等也是如此。在 DT 中也发现了相同的层次结构,其中 I2C 设备节点只作为 I2C 总线节点的子节点出现。SPI、MDIO、USB 等也是如此。唯一不需要特定类型的父设备的设备是 platform_devices(和 amba_devices,但稍后会详细介绍),它们将愉快地存在于 Linux /sys/devices 树的底部。因此,如果 DT 节点位于树的根部,那么它实际上可能最好注册为 platform_device。
Linux 主板支持代码调用of_platform_populate(NULL, NULL, NULL, NULL) 来启动树根处的设备发现。所有参数均为 NULL,因为从树根开始时,无需提供起始节点(第一个 NULL)、父节点(最后一个 NULL),并且我们尚未使用匹配表(目前)。对于只需要注册设备的主板,.init_machine() 除了调用之外可以完全为空 。struct deviceof_platform_populate()
在 Tegra 示例中,这说明了 /soc 和 /sound 节点,但 SoC 节点的子节点呢?它们是否也应该注册为平台设备?对于 Linux DT 支持,通用行为是在驱动程序 .probe() 时由父设备驱动程序注册子设备。因此,i2c 总线设备驱动程序将为每个子节点注册一个 i2c_client,SPI 总线驱动程序将注册其 spi_device 子节点,其他 bus_types 也是如此。根据该模型,可以编写一个驱动程序,该驱动程序绑定到 SoC 节点并简单地为其每个子节点注册 platform_devices。板级支持代码将分配并注册 SoC 设备,(理论上的)SoC 设备驱动程序可以绑定到 SoC 设备,并在其 .probe() 钩子中为 /soc/interrupt-controller、/soc/serial、/soc/i2s 和 /soc/i2c 注册 platform_devices。很简单,对吧?
实际上,将某些 platform_devices 的子节点注册为更多 platform_devices 是一种常见模式,设备树支持代码反映了这一点,并使上述示例更简单。第二个参数of_platform_populate()是 of_device_id 表,与该表中的条目匹配的任何节点也会注册其子节点。在 Tegra 的情况下,代码可能看起来像这样:
static void __init harmony_init_machine(void)
{/* ... */of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
“simple-bus” 在 Devicetree 规范中定义为表示简单内存映射总线的属性,因此of_platform_populate()可以编写代码以假设始终会遍历 simple-bus 兼容节点。但是,我们将其作为参数传入,以便板级支持代码始终可以覆盖默认行为。
附录 A: AMBA 设备
ARM Primecells 是连接到 ARM AMBA 总线的一种设备,它支持硬件检测和电源管理。在 Linux 中,使用 struct amba_device 和 amba_bus_type 来表示 Primecell 设备。然而,棘手的是,AMBA 总线上并非所有设备都是 Primecells,对于 Linux 来说,amba_device 和 platform_device 实例通常是同一总线段的兄弟。
使用 DT 时,这会产生问题,of_platform_populate() 因为它必须决定是否将每个节点注册为 platform_device 或 amba_device。不幸的是,这会使设备创建模型稍微复杂一些,但解决方案并不是太具侵入性。如果节点与“arm,primecell”兼容,则将 of_platform_populate()其注册为 amba_device 而不是 platform_device。
相关文章:
Linux 和设备树
“开放固件设备树”,简称 Devicetree (DT),是一种用于描述硬件的数据结构和语言。更具体地说,它是操作系统可读取的硬件描述,因此操作系统无需对机器的详细信息进行硬编码。 从结构上看,DT 是一棵树,或具有…...
Qt仿音乐播放器:QFileDialog添加本地文件
一、套路 QFileDialog fileDialog(this);// 创建对话框,并设置父元素;fileDialog.setWindowTitle("添加本地下载的音乐");//设置窗口标题//设置文件对话框的默认打开路径 QString projectPathQDir::currentPath();//获取当前目录 QDir dir(pr…...
Odoo 引用字段 fields.Reference:动态关系的选择器
在 Odoo 模型开发中,关系型字段是构建复杂应用的基础。 然而,传统的 m2o、o2m 和 m2m 字段需要在模型定义时就明确指定关系的目标模型,这在某些场景下会显得不够灵活。 为了解决这个问题,Odoo 提供了 fields.Reference 引用字段&a…...

Android笔试面试题AI答之Android基础(6)
Android入门请看《Android应用开发项目式教程》 文章目录 1.Android Studio版本与Gradle版本有什么关联?**1. Gradle 的作用****2. Android Studio 与 Gradle 的关系****3. 版本对应关系****4. 如何查看和修改版本****查看当前版本****修改版本** **5. 版本不兼容的…...

C# 中的记录类型简介 【代码之美系列】
🎀🎀🎀代码之美系列目录🎀🎀🎀 一、C# 命名规则规范 二、C# 代码约定规范 三、C# 参数类型约束 四、浅析 B/S 应用程序体系结构原则 五、浅析 C# Async 和 Await 六、浅析 ASP.NET Core SignalR 双工通信 …...

利用Java爬虫速卖通按关键字搜索AliExpress商品
在这个信息爆炸的时代,数据的价值日益凸显。对于电商领域的从业者来说,能够快速获取商品信息成为了一项重要的技能。速卖通(AliExpress)作为全球领先的跨境电商平台,拥有海量的商品数据。本文将介绍如何使用Java语言编…...
gitlab runner 实现 微信小程序自动化部署
微信小程序多人开发的情况下,开发人员都只能在本机上发布体验版,且需要到小程序管理后台自行切换到自己发布的版本,会出现体验版本覆盖的问题。给开发测试带来问题。 miniprogram-ci 的发布,使得开发人员可以通过命令行上传小程序…...
Playwright爬虫xpath获取技巧
示例一 <button class"MuiButtonBase-root MuiButton-root MuiLoadingButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeLarge MuiButton-containedSizeLarge MuiButton-colorPrimary MuiButton-fullWidth MuiButton-root MuiLoadingButton…...

总结TCP/IP四层模型
总结TCP/IP四层模型 阅读目录(Content) 一、TCP/IP参考模型概述 1.1、TCP/IP参考模型的层次结构二、TCP/IP四层功能概述 2.1、主机到网络层 2.2、网络互连层 2.3、传输层 2.3、应用层 三、TCP/IP报文格式 3.1、IP报文格式3.2、TCP数据段格式3.3、UDP数据段格式3.4、套…...
netcat和nmap的区别
Netcat 和 Nmap 是两种广泛使用的网络工具,但它们的功能和使用场景有所不同。下面是这两种工具的对比: Netcat(nc) 用途和功能: 网络连接: Netcat 是一个功能强大的网络工具,用于创建 TCP 或 UDP 连接。可以用来进行网…...
MinIO服务器文件复制(Windows环境Linux环境)
一、下载 Windows环境:https://dl.min.io/server/minio/release/windows-amd64/minio.exe Linux环境: > curl https://dl.min.io/client/mc/release/linux-amd64/mc \ --create-dirs \ -o $HOME/minio/mc > chmod x $HOME/minio/mc > expo…...

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用
🌟 关于我 🌟 大家好呀!👋 我是一名大三在读学生,目前对人工智能领域充满了浓厚的兴趣,尤其是机器学习、深度学习和自然语言处理这些酷炫的技术!🤖💻 平时我喜欢动手做实…...
无监督学习算法
K-均值聚类(K-means clustering)是一种常用的无监督学习算法,用于将数据集划分成 K 个不同的组或簇。该算法主要通过计算数据点之间的欧几里得距离来确定数据点之间的相似性,并根据相似性将数据点分配到不同的簇中,使得…...

【Compose multiplatform教程17】【组件】BoxWithConstraints组件
查看全部组件 https://blog.csdn.net/b275518834/article/details/144751353 BoxWithConstraints 功能说明:它是 Jetpack Compose 中的关键布局组件,能够精准捕捉自身所在容器的尺寸约束信息,通过获取最大宽度和最大高度这两个关键属性&…...

银河麒麟操作系统安装达梦数据库(超详细)
目录 引言1. 前期准备1.1 安装麒麟系统1.2 下载达梦数据库安装包(DM8)1.3 上传安装包到麒麟系统1.4 挂载安装包(iso)文件1.5 配置安装用户和组1.6 创建安装路径及修改权限1.7 设置临时安装目录 2. 安装达梦数据库(DM8&…...

Spring源码_05_IOC容器启动细节
前面几章,大致讲了Spring的IOC容器的大致过程和原理,以及重要的容器和beanFactory的继承关系,为后续这些细节挖掘提供一点理解基础。掌握总体脉络是必要的,接下来的每一章都是从总体脉络中, 去研究之前没看的一些重要…...

科大讯飞在线语音合成(流式版)python版
1、进入自己的项目 复制APPID、APISecret、APIKey 2、添加好听发音人 复制vcn参数 3、需要替换代码部分: 换自己喜欢的发声人的参数 换上自己的APPID、APISecret、APIKey 4、完整代码: # -*- coding:utf-8 -*- import _thread as thread import base…...
常见搜索算法汇总
常见搜索算法总结 搜索算法是人工智能和计算机科学中用于解决问题、优化路径或发现数据模式的关键技术。本文将对常见的搜索算法进行总结,包括A*算法、D*算法、模拟退火(Simulated Annealing)、爬山法(Hill Climbing)、…...
vue 中 ref 详解
一、定义与基本用法 1. 定义 在 Vue.js 中,ref是一个用于在组件中获取 DOM 元素或者子组件实例引用的属性。它提供了一种直接访问元素或组件的方式,使得我们可以在 JavaScript 代码中对它们进行操作。 2. 基本使用 在模板中,可以通过给元…...
探索开源项目 kernel:技术的基石与无限可能
在开源的广袤世界中,有一颗璀璨的明星——kernel(https://gitee.com/openeuler/kernel),它宛如一座技术的宝藏,蕴含着无数的智慧与创新,为众多开发者所瞩目和敬仰。 一、初窥 kernel 项目 当我第一次接触…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...