百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

设备树基础知识分享_设备树详解

haoteby 2025-02-16 20:55 31 浏览

本文部分内容整理自:百文网及正点原子

前言

之前分享的笔记:Linux总线设备驱动模型 中在platform_device部分有简单说明描述设备有两种方法:一种是使用platform_device结构体来指定;另一种是使用设备树来描述。本篇笔记我们就来简单地学习一下设备树的一些知识。

什么是设备树


设备树简单理解就是描述设备信息(资源)的一棵树。设备树(Device Tree)用代码体现如下:

这些代码被保存在.dts/dtsi后缀文件中,也即设备树源文件 DTS(DeviceTree Source)。这些源文件同我们的C代码一样,并不能直接使用的,而是得经过一个编译过程生成机器可运行的二进制文件,如:

dts文件使用dtc工具编译生成dtb文件,这个dtb文件就是内核可以使用的文件。例如我们的板子跑起来之后,我们系统使用的设备树文件就存在目录/boot下:

Linux为什么会引入设备树?

在上一个实验:基于总线设备驱动模型的LED驱动实践 中我们使用了platform_device结构体来描述led设备(硬件资源)。既然已经有了描述设备的方法了,为什么还要引入设备树呢?因为Linux内核中有很多BSP(板级支持包),不同的BSP会包含着不同的描述设备的代码(.c或.h文件)。随着芯片的发展,Linux内核中就包含着越来越多这些描述设备的代码,导致Linux内核代码会很臃肿。

这导致Linux之父Linus 大发雷霆: "this whole ARM thing is a f*cking pain in the ass"。 因此引入了设备树文件,从而可精简一些臃肿的C代码。除此之外,.dts编译生成.dtb文件的过程要比.c编译生成驱动模块、加载驱动模块的过程要简单很多,也更方面我们进行开发。

设备树的语法

设备树源文件也是需要根据一定规则来编写的,同C语言一样,也要遵循一些语法规则。下面简单看一下设备树的源码结构及语法。

先看一个设备树示例:


1、节点格式

label: node-name@unit-address

其中:

label:标号

node-name:节点名字

unit-address:单元地址

label 是标号,可以省略。 label 的作用是为了方便地引用 node。比如:

可以使用下面 2 种方法来修改 uart@fe001000 这个 node:

2、属性格式

简单地说, properties 就是“name=value”, value 有多种取值方式。 示例:

  • 一个32位的数据,用尖括号包围起来,如
interrupts = <17 0xc>; ? 
  • 一个64位数据(使用2个32位数据表示),用尖括号包围起来,如:
clock-frequency = <0x00000001 0x00000000>; ? 
  • 有结束符的字符串,用双引号包围起来,如:
compatible = "simple-bus"; ? 
  • 字节序列,用中括号包围起来,如:
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"; ? 

3、一些标准属性

(1) compatible 属性

“compatible”表示“兼容”,对于某个LED,内核中可能有A、B、C三个驱动都支持它,那可以这样写:

led { ? 
    compatible = “A”, “B”, “C”; ? 
}; ?

内核启动时,就会为这个LED按这样的优先顺序为它找到驱动程序:A、B、C。

(2)model 属性

model属性与compatible属性有些类似,但是有差别。compatible属性是一个字符串列表,表示可以你的硬件兼容A、B、C等驱动;model用来准确地定义这个硬件是什么。比如根节点中可以这样写:

/ { ? 
 ? ?compatible = "samsung,smdk2440", "samsung,mini2440"; ? 
 ? ?model = "jz2440_v3"; ? 
}; ?

它表示这个单板,可以兼容内核中的“smdk2440”,也兼容“mini2440”。从compatible属性中可以知道它兼容哪些板,但是它到底是什么板?用model属性来明确。

(3)status 属性

status 属性看名字就知道是和设备状态有关的, status 属性值也是字符串,字符串是设备的状态信息,可选的状态如下所示:

(4)#address-cells 和#size-cells 属性

格式:

address-cells:address要用多少个32位数来表示; ? 
size-cells:size要用多少个32位数来表示。 

比如一段内存,怎么描述它的起始地址和大小?下例中,address-cells为1,所以reg中用1个数来表示地址,即用0x80000000来表示地址;size-cells为1,所以reg中用1个数来表示大小,即用0x20000000表示大小:

/ {   
    # address-cells = <1>;   
    # size-cells = <1>;   
    memory {   
    	reg = <0x80000000 0x20000000>;   
    };   
};   

(5)reg 属性

reg的本意是register,用来表示寄存器地址。但是在设备树里,它可以用来描述一段空间。反正对于ARM系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。

reg属性的值,是一系列的“address size”,用多少个32位的数来表示address和size,由其父节点的# address-cells、#size-cells决定。示例:

/dts-v1/;   
/ {   
    # address-cells = <1>;   
    # size-cells = <1>;   
    memory {   
    	reg = <0x80000000 0x20000000>;   
    };   
};   

(7)name 属性

过时了,建议不用。它的值是字符串,用来表示节点的名字。在跟platform_driver匹配时,优先级最低。compatible属性在匹配过程中,优先级最高。

(8)device_type 属性

过时了,建议不用。它的值是字符串,用来表示节点的类型。在跟platform_driver匹配时,优先级为中。compatible属性在匹配过程中,优先级最高。

3、常用的节点

(1)根节点

用 / 标识根节点,如:

/dts-v1/;   
/ {   
    model = "SMDK24440";   
    compatible = "samsung,smdk2440";   

    # address-cells = <1>;   
    # size-cells = <1>;   
};   

(2)CPU节点

一般不需要我们设置,在 dtsi 文件中都定义好了,如:

cpus {   
    # address-cells = <1>;   
    # size-cells = <0>;   

    cpu0: cpu@0 {   
    	.......   
    }   
};   

(3)memory 节点

芯片厂家不可能事先确定你的板子使用多大的内存,所以 memory 节点需要板厂设置,比如:

memory {   
	reg = <0x80000000 0x20000000>;   
}; 

(4)chosen 节点

我们可以通过设备树文件给内核传入一些参数,这要在chosen节点中设置bootargs属性:

chosen {   
	bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";   
};   

操作设备树的函数

Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”(“open firmware”即开放固件。 ),所以在很多资料里面也被叫做 OF 函数。

1、节点相关操作函数

Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h 中,定义如下:

与查找节点有关的 OF 函数有 5 个:

(1) of_find_node_by_name 函数

of_find_node_by_name 函数通过节点名字查找指定的节点,函数原型如下:

struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);

(2) of_find_node_by_type 函数

of_find_node_by_type 函数通过 device_type 属性查找指定的节点,函数原型如下:

struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

(3) of_find_compatible_node 函数

of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点,函数原型如下:

struct device_node *of_find_compatible_node(struct device_node *from,const char *type,
const char *compatible);

(4)of_find_matching_node_and_match 函数


of_find_matching_node_and_match 函数通过 of_device_id 匹配表来查找指定的节点,函数原型如下:

struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match);

(5)of_find_node_by_path 函数

of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:

inline struct device_node *of_find_node_by_path(const char *path);

2、提取属性值的 OF 函数

Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,内容如下:

Linux 内核也提供了提取属性值的 OF 函数 :

(1) of_find_property 函数

of_find_property 函数用于查找指定的属性,函数原型如下:

property *of_find_property(const struct device_node *np,const char *name,int *lenp);

(2)of_property_count_elems_of_size 函数


of_property_count_elems_of_size 函数用于获取属性中元素的数量,比如 reg 属性值是一个数组,那么使用此函数可以获取到这个数组的大小,此函数原型如下:

int of_property_count_elems_of_size(const struct device_node *np,const char *propname,int elem_size);

(3)读取 u8、 u16、 u32 和 u64 类型的数组数据

(4)读取 u8、 u16、 u32 和 u64 类型属性值

(5)of_property_read_string 函数

of_property_read_string 函数用于读取属性中字符串值,函数原型如下:

int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)

以上就是关于设备树的一些基础知识的整理学习,下一篇笔记我们再来一起学一下设备树的一些具体实验。

1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。私信回复1024,即可免费获取!

相关推荐

Chrome OS 41 用 Freon 取代 X11_chrome os atom

在刚发布的ChromeOS41里,除了常规的Wi-Fi稳定性提升(几乎所有系统的changelog里都会包含这一项)、访客模式壁纸等之外,还存在底层改变。这一更新中Google移除...

苹果iPad Pro再曝光 有望今年六月发布

自进入2015年以后,有关大屏iPad的消息便一直不绝于耳,之前就有不少媒体猜想这款全新的平板电脑将会在三月发布,不过可惜的是我么只在那次发布会上看到了MacBookPro。近日@Ubuntu团队便...

雷卯针对香橙派Orange Pi 5 Max开发板防雷防静电方案

一、应用场景高端平板、边缘计算、人工智能、云计算、AR/VR、智能安防、智能家居、Linux桌面计算机、Linux网络服务器、Android平板、Android游戏机...

Ubuntu Server无法更新问题解决_ubuntu server not found

上周老家的一台运行UbuntuServer的盒子无法连接上了,中秋这两天回来打开,顺手更新一下发现更新报错。提示`E:Releasefileforhttps://mirrors.aliyun...

虚幻引擎5正式版发布:古墓丽影&amp;巫师新作采用、新一代实时渲染

机器之心报道编辑:杜伟、陈萍虚幻引擎5的目标是「助力各种规模的团队在视觉领域和互动领域挑战极限,施展无限潜能」。...

AMD Milan-X双路霄龙7773X平台基准测试曝光 CPU缓存总量超1.5GB

OpenBenchmarking基准测试数据库刚刚曝光了AMDMilan-X双路霄龙7773X平台的跑分成绩,虽然很快就被撤下,但我们还是知晓了高达1.6GB的总CPU缓存。早些时...

ROS机器人建模_ros机器人硬件搭建

...

全网最新的Dify(1.7.2)私有化离线部署教程(ARM架构)

Hello,大家好!近期工作中有涉及到Dify私有化离线部署,特别是针对于一些国产设备。因此特别整理了该教程,实测有效!有需要的小伙伴可以参考下!本文主要针对Dify1.7.2最新版本+国产操作系...

在ubuntu下新建asp.net core项目_创建ubuntu

本文一步步讲述在ubuntu下用visualstudiocode创建asp.netcore项目的过程。step1:环境操作系统:virtualbox下安装的lubuntu。请不要开启“硬件...

在晶晨A311D2处理器上进行Linux硬件视频编码
在晶晨A311D2处理器上进行Linux硬件视频编码

在KhadasVIM4AmogicA311D2SBC上,我更多的时间是在使用Ubuntu22.04。它的总体性能还不错,只不过缺少3D图形加速和硬件视...

2025-08-26 17:22 haoteby

Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!

之前和大家分享过JDK17的多版本管理及详细安装过程,然后在项目升级完jdk17后又发现之前的注册和配置中心nacos又用不了,原因是之前的nacos1.3版本的,版本太老了,已经无法适配当前新的JD...

电影质量级渲染来了!虚幻引擎5.3正式发布:已开放下载

快科技9月8日消息,日前,Unrealengine正式发布了虚幻引擎5.3,带来了大量全方位的改进。...

2025如何选购办公电脑?极摩客mini主机英特尔系列选购指南

当下,迷你主机的性能越来越强,品类也越来越多。但是CPU是不变的,基本都是AMD和英特尔的。有一个小伙伴在评论区提问,我应该如何在众多机器中选购一台符合自己的迷你主机呢?那今天我们优先把我们的系列,分...

ubuntu 20.04+RTX4060 Ti+CUDA 11.7+cudnn

ububtu添加国内源sudocp/etc/apt/sources.list/etc/apt/sources.list.backupsudovim/etc/apt/sources.lis...

Linux Mint 18将重新基于Ubuntu 16.04 带来更好硬件支持

项目负责人ClementLefebvre在本月6日披露了关于LinuxMint18“Sarah”操作系统的大量信息,包括带来全新扁平化体验的Mint-Y主题。而现在,这款将于年底之前上线的操作...