首页 > 系统相关 >嵌入式linux系统中设备树基础知识

嵌入式linux系统中设备树基础知识

时间:2023-10-30 11:06:32浏览次数:32  
标签:node 函数 find 基础知识 linux device 嵌入式 节点 属性


笔记整理自百问网+正点原子

前言

之前分享的笔记:【Linux笔记】总线设备驱动模型中在platform_device部分有简单说明描述设备有两种方法:一种是使用platform_device结构体来指定;另一种是使用设备树来描述。

本篇笔记我们就来简单地学习一下设备树的一些知识。

什么是设备树

嵌入式linux系统中设备树基础知识_属性值

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

嵌入式linux系统中设备树基础知识_设备树_02

这些代码被保存在.dts/dtsi后缀文件中,也即设备树源文件 DTS(DeviceTree Source)

这些源文件同我们的C代码一样,并不能直接使用的,而是得经过一个编译过程生成机器可运行的二进制文件,如:

嵌入式linux系统中设备树基础知识_属性值_03

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

嵌入式linux系统中设备树基础知识_设备树_04

Linux为什么会引入设备树?

在上一个实验:【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语言一样,也要遵循一些语法规则。下面简单看一下设备树的源码结构及语法。

先看一个设备树示例:

嵌入式linux系统中设备树基础知识_字符串_05

1、节点格式

label: node-name@unit-address

其中:

label:标号

node-name:节点名字

unit-address:单元地址

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

嵌入式linux系统中设备树基础知识_设备树_06

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

嵌入式linux系统中设备树基础知识_设备树_07

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 属性值也是字符串,字符串是设备的状态信息,可选的状态如下所示:

嵌入式linux系统中设备树基础知识_属性值_08

(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属性的值,是一系列的“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 中,定义如下:

嵌入式linux系统中设备树基础知识_linux_09

与查找节点有关的 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系统中设备树基础知识_linux_10

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 类型的数组数据

嵌入式linux系统中设备树基础知识_linux_11

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

嵌入式linux系统中设备树基础知识_设备树_12

(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)

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

标签:node,函数,find,基础知识,linux,device,嵌入式,节点,属性
From: https://blog.51cto.com/u_11947739/8086436

相关文章

  • Linux 虚拟内存参数配置
    一、问题出发点Jun110:30:21audit1kernel:swapper:pageallocationfailure.order:1,mode:0x20Jun110:30:21audit1kernel:Pid:0,comm:swapperTainted:G---------------T2.6.32-431.20.3.el6.x86_64#1Jun110:30:21audit1kernel:CallTrace:Jun11......
  • 嵌入式linux系统设备树实例分析
    前言我们可以从LED程序中榨取很多知识:基本的驱动框架、驱动的简单分层、驱动的分层+分离思想、总线设备驱动模型、设备树等。这大多都是结合韦老师的教程学的。这篇笔记结合第6个demo(基于设备树)来学习、分析:框图下面是LED程序的几个层次结构图:注意:层与层之间的箭头指向是相对的,从哪......
  • 【Python】在linux环境下, Django 3.0+版本遇到的一些问题
    1.Django3.2版本升级后的一些报错,导致项目运行失败1.1site-packages/rest_framework/serializers.py fromdjango.db.models.fieldsimportFieldDoesNotExistImportError:cannotimportname'FieldDoesNotExist'解决办法:改变FieldDoesNotExist的导入方式fromdjango......
  • 嵌入式硬件中常见的100种硬件选型方式
    1请列举您知道的电阻、电容、电感品牌(最好包括国内、国外品牌)。电阻:美国:AVX、VISHAY威世日本:KOA兴亚、Kyocera京瓷、muRata村田、Panasonic松下、ROHM罗姆、susumu、TDK台湾:LIZ丽智、PHYCOM飞元、RALEC旺诠、ROYALOHM厚生、SUPEROHM美隆、TA-I大毅、TMTEC泰铭、TOK......
  • 嵌入式硬件库的基本操作方式与分析
    本次要介绍的开源软件是c-periphery:https://github.com/vsergeev/c-periphery一个用C语言编写的硬件外设访问库。我们可以用它来读写Serial、SPI、I2C等,非常适合在嵌入式产品上使用。我们可以基于它优秀的代码框架,不断地扩展出更多的功能模块,最终形成自己产品适用的Linux硬......
  • 实战攻防演练-Linux写入ssh密钥,利用密钥登录
    前言密钥形式登录的原理是利用密钥生成器制作一对密钥,一只公钥和一只私钥。将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录。这样一来,没有私钥,任何人都无法通过SSH暴力破解你的密码来远程登录到系统。此外,如果将公钥复制到其他账户甚至主机,利用私钥也......
  • linux基本文件命令复习笔记
    1,放大缩小终端窗口字体  放大 ctrlshift+=   缩小  ctrl-2,6个常见终端命令 (1)ls  查看当前文件夹下的内容 (2)pwd 查看当前所在文件夹  (3)cd目录名 切换文件夹 (4)touch文件名 如果文件不存在,新建文件。和mkdir不同的是,mkdir创......
  • python进阶14大模块200页知识体系md笔记,第3篇:linux命令进阶
    本完整笔记从14大模块展示了python高级用的应用。分别有Linux命令,多任务编程、网络编程、Http协议和静态Web编程、html+css、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。完整版笔记直接地址:请移步这里......
  • 第九周 Linux课后技术总结
    5.1初始进程什么是进程?进程是已启动的可执行程序的运行实例。进程有以下组成部分。已分配内存的地址空间。安全属性,包括所有权凭据和特权。程序代码的一个或多个的执行线程。进程状态。每个进程都有唯一的进程标识PID,一个PID只能标识一个进程。(使用pidof命令查看sshd......
  • Linux用户管理
    1用户/组概览Linux系统是多用户、多任务的分时操作系统,系统上每一个进程都有一个特定的文件,每个文件都被一个特定的用户所拥有。如果需要使用系统资源,首先必须向系统超级用户申请成为普通用户,然后以普通用户的身份进入系统。超级用户可以对普通用户进行跟踪,并设置他们的访问权限,......