订阅
纠错
加入自媒体

Linux设备驱动统一模型解析

2021-04-13 09:48
一口Linux
关注

soc节点指定了<0x0 0xe0000000 0x00100000>;此属性值指定对于1024KB范围的地址空间,在物理0x0处寻址的子节点映射到物理0xe0000000的父地址。通过这种映射,串行设备节点可以通过0xe0004600地址的加载或存储、0x4600(在注册表中指定)的偏移量以及范围中指定的0xe0000000映射寻址。

dma-ranges属性名称dma-ranges值类型<empty>或编码为任意数量的(子总线地址、父总线地址、长度)三联体描述dma-range属性用于描述存储器映射总线的直接存储器访问(dma)结构,其设备树父级可以从总线的dma操作访问。它提供了一种定义总线物理地址空间与总线父级物理地址空间之间映射或转换的方法。dma-range属性的值的格式是任意数量的(子总线地址、母线地址、长度)。指定的每个三联体描述连续DMA地址范围。1. 子总线地址是子总线地址空间内的物理地址。表示地址的单元格数取决于总线,可以通过该节点(dma-range属性出现的节点)的#address-cells地址单元格确定。2. 父总线地址是父总线地址空间中的物理地址。表示父地址的单元格数取决于总线,可以通过定义父地址空间的节点的#address-cells属性确定。3. 长度指定子地址空间中范围的大小。表示大小的单元格数量可以根据该节点(dma-range属性出现的节点)的#size-cells确定。Name(已弃用)属性名称name值类型<string>描述name属性用于记录节点名字,name属性已经被弃用 ,不推荐使用 name属性,一些老的设备树文件可能会使用此属性。device_type属性名称device_type值类型<string>描述由于DTS没有FCode,因此不建议使用该属性。只能用于在cpu节点和memory节点中,以便与IEEE 1275衍生设备兼容。1.3.4. 基本设备节点类型

所有设备树文件均要包含一个根文件,并且所有设备树文件均应在根节点下存在以下节点:

1个/cpus节点至少一个/memory节点

使用说明:R = 必需,O = 可选,OR = 可选但推荐,SD = 参见定义,所有其他的标准属性均可接受,但可选

1.3.4.1. Root node

devicetree有一个单独的根节点,所有其他设备节点都是它的后代。根节点的完整路径为/。

属性名称使用说明类型定义#address-cellsR<u32>root子节点的reg property地址格式。#size-cellsR<u32>root子节点的reg property大小格式。modelR<string>指定唯一标识。系统板型号。推荐格式为“制造商,型号”compatibleR<stringlist>指定平台体系结构列表。该平台兼容。这一建议可供操作系统在选择平台特定代码时使用。1.3.4.2. /aliases节点

设备树文件可能具有一个别名节点(/aliases),该节点定义一个或多个别名属性。别名节点应位于设备树的根节点,并且具有节点名称/别名。/aliases节点的每个属性都定义了一个别名。属性名称指定别名。属性值指定设备树中节点的完整路径。例如,属性serial0 = "/simple-bus@fe000000/serial@llc500"定义了别名serial0。别名的命名规则如下:

字符描述0-9数字a-z小写字母-破折号1.3.4.3. /memory节点

所有设备树都需要内存设备节点,并描述系统的物理内存布局。如果系统具有多个范围的内存,则可以创建多个内存节点,或者可以在单个内存节点的reg属性中指定范围。

/memory节点的属性要求如下:

属性名称使用说明类型定义device_typeRO<string>regR<prop-encoded-array>由任意数量的地址和大小对组成,它们指定内存范围的物理地址和大小initial-mapped-areaO<prop-encoded-array>指定”初始映射区域”的地址和大小,是一个由三元组组成的属性编码数组(有效地址、物理地址、大小)。有效和物理地址均应为64位(值),大小应为32位(值)

在xxx.dts中

memory {
      reg =  <0x40000000 0x10000000>;   起始地址0x40000000 长度0x10000000(32MB)
};
1.3.4.4. /chosen 节点

/chosen 节点不代表系统中的实际设备,而是描述了在运行时由系统固件选择或指定的参数。它应该是根节点的子节点。

属性名称使用说明类型定义bootargsO<string>指定程序的启动参数。如果不需要引导参数,则该值可能为空字符串stdout-pathO<string>指定到表示用于引导控制台输出的设备的节点的完整路径。如果包含’:’,则它将终止路径。该值可以是别名。stdin-pathO<string>指定到表示用于引导控制台输入的设备的节点的完整路径。如果包含’:’,则它将终止路径。该值可以是别名。

示例:

chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
1.3.4.5. /cpus节点属性

所有设备树均需要/cpus/cpu节点。它并不代表系统中的真实设备,而是作为代表系统cpu的子cpu节点的容器。

属性名称使用说明类型定义device_typeR<string>值应为“cpu”regR<prop-encoded-array>它为CPU节点表示的CPU/线程定义了唯一的CPU/线程ID。如果CPU支持多线程,则reg是一个数组,每个线程具有一个元素。clock-frequencyR<prop-encoded-array>以Hz为单位指定CPU的当前时钟速度,格式可以是,或timebase-frequencyR<prop-encoded-array>指定更新时基的当前频率statusSD<string>此属性应存在于对称多进程(SMP)CPU的节点中 配置。”okay”:CPU正在运行;”disable”:CPU处于静止状态。1.3.5. 中断映射

在设备树中,存在逻辑中断树,该逻辑中断树表示平台硬件中断的层次结构和路由。在设备树中,使用interrupt-parent属性表示中断源与中断控制器的物理连线。代表产生中断的设备节点包含一个中断父属性,该属性具有一个虚拟值,指向给设备的中断所路由到的设备(通常是中断控制器)。

如果产生中断的设备不具有中断父属性,则假定其中断父节点为其设备父节点。每个中断产生设备都包含一个中断属性,该属性的值描述该设备的一个或多个中断源。每个源都用称为中断描述符表示。中断描述符的格式和含义是特定于中断域的,即,取决于中断域根节点上节点的属性。中断域的根使用#interrupt-cells属性定义对中断描述符进行编码所需的值数量。

中断域是解释中断描述符的上下文。中断域的根可以是中断控制器(interrupt controller)或中断连接器(interrupt nexus):

中断控制器是物理设备,需要一个驱动程序来处理通过它路由的中断。它还可能级联到另一个中断域。中断控制器由设备树中该节点上的interrupt-controller指定。中断连接器定义了一个中断域和另一个中断域之间的转换。翻译基于特定领域和总线的信息。使用interrupt-map属性在域之间进行转换。例如,PCI控制器设备节点可以是一个中断连接器,定义从PCI中断命名空间(INTA、INTB等)到具有中断请求(IRQ)编号的中断控制器的转换。

1.3.5.1. Interrupts属性名称interrupts值类型<prop-encoded-array>编码为任意数量的中断描述符描述设备节点的中断属性定义设备生成的中断。interrupts属性的值由任意数量的中断描述符组成。中断描述符的格式由中断域根定义。

示例:

interrupts = <GIC_SPI INT_DMA IRQ_TYPE_LEVEL_HIGH>;

1.3.5.2. interrupt-parent属性名称interrupt-parent值类型<phandle>描述由于中断树中节点的层次结构可能与device tree不匹配,因此interrupt-parent属性可用于明确中断父级的定义。该值是中断父级的phandle。如果设备缺少此属性,则假定其中断父级为其设备树父级。

示例:

interrupt-parent = <&gpe>;

1.3.5.3. interrupts-extended属性名称interrupts-extended值类型<phandle>描述扩展的中断属性列出了设备产生的中断。当设备连接到多个中断控制器时,应该使用interrupts-extended代替interrupts,因为它会在每一个中断描述符编码一个父代phandle

示例:

interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;

1.3.5.4. #interrupt-cells属性名称#interrupt-cells值类型<u32>描述#interrupt-cells属性定义对中断域的中断描述符进行编码所需的单元数量1.3.5.5. interrupt-controller属性名称interrupt-controller值类型<empty>描述中断控制器属性的存在将节点定义为中断控制器节点。1.4.Device Tree binary格式

Devicetree Blob (DTB)格式是Devicetree数据的平面二进制编码。它用来在软件程序之间交换设备数据。例如,在引导操作系统时,固件将向操作系统内核传递一个DTB。

DTB格式将devicetree数据编码为一个单一的、线性的、无指针的数据结构。它由一个小标题组成,接下来是三个大小可变的部分:内存保留块、结构块和字符串块这些应该按照这个顺序出现在扁平的devicetree中。

因此。当按地址加载到内存中时,设备树结构作为一个整体。将类似于图中的图表。

1.4.1. dt_header

设备树的头部是由以下C结构体定义的。所有字段都是32位整数,以big-endian格式存储。

struct fdt_header {
此字段应包含值0xd00dfeed(big-endian)
uint32_t magic;     magic word FDT_MAGIC
此字段应包含设备数据结构的总大小(字节)。该大小应包含结构的所有部分:报头、内存预留块、结构块和字符串块,以及块之间或最终块之后的自由空间间隙。
uint32_t totalsize;    total size of DT block
此字段应包含结构块从标题开始的字节偏移
uint32_t off_dt_struct;    offset to structure
此字段应包含从标题开始的字符串块的字节偏移量
uint32_t off_dt_strings;   offset to strings
此字段应包含从标题开始的内存保留块的字节偏移量
uint32_t off_mem_rsvmap;   offset to memory reserve map
此字段应包含设备数据结构的版本
uint32_t version;    format version
此字段应包含设备所用版本向后兼容的最低版本数据结构
uint32_t last_comp_version;   last compatible version
 version 2 fields below
此字段应包含系统引导CPU的物理ID。它应与设备树中CPU节点的reg属性中给定的物理ID相同
uint32_t boot_cpuid_phys;   Which physical CPU id we're booting on
 version 3 fields below
此字段应包含字符串块部分的字节长度
uint32_t size_dt_strings;   size of the strings block
 version 17 fields below
此字段应包含结构块部分的字节长度
uint32_t size_dt_struct;   size of the structure block
};
1.4.2. memory reservation block

内存保留块向客户端程序提供物理内存中被保留的区域的列表,这些内存不用于一般的内存分配,目的是保护重要的数据结构不被客户端程序覆盖。这个区域包括了若干的reserve memory描述符。每个reserve memory描述符是由address和size组成。其中address和size都是用U64来描述:

struct fdt_reserve_entry {
uint64_t address;
uint64_t size;
};
1.4.3. Structure block

结构块描述了设备树本身的结构和内容。它由若干的分片组成,每个分片开始位置都是保存了令牌(token),以此来描述该分片的属性和内容。

FDT_BEGIN_NODE (0x00000001):该token描述了一个node的开始位置,紧挨着该token的就是node name(包括unit address)FDT_END_NODE (0x00000002):该token描述了一个node的结束位置FDT_PROP (0x00000003):该token描述了一个property的开始位置,该token之后是两个u32的数据。它们之后就是长度为len的具体的属性值数据。struct {
uint32_t len; 表示该property value data的size。
uint32_t nameoff; 表示该属性字符串在device tree strings block的偏移值

FDT_NOP (0x00000004):被解析设备树的程序忽略,可用于覆盖其他属性,以删除它FDT_END (0x00000009):标记结构块的结束所以,一个DTB的结构块可能如下:(optionally) any number of FDT_NOP tokens
FDT_BEGIN_NODE token:
--node’s name
--paddings
For each property of the node:
              --FDT_NOP(optionally)
              --FDT_PROP token
                  --property    
all child nodes in this format
(optionally) any number of FDT_NOP tokens
FDT_END_NODE token
1.4.4. Strings Block

定义了各个node中使用的属性的字符串表。由于很多属性会出现在多个node中,因此,所有的属性字符串组成了一个string block。这样可以压缩DTB的size。

1.5.Linux解析设备树

设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动时需要去获取这些信息。Linux内核提供一系列以of_开头的函数来获取设备树信息,这些函数的原型都定义在include/linux/of.h中。设备以节点的形式挂在设备树上,Linux内核使用device_node结构体来描述一个节点,其定义在include/linux/of.h中:

struct device_node {
const char *name;     device node name
const char *type;     对应device_type的属性
phandle phandle;      对应该节点的phandle属性
const char *full_name;  从“/”开始的,表示该node的full path
Struct  property *properties;      该节点的属性列表
如果需要删除某些属性,kernel并非真的删除,而是挂入到deadprops的列表
struct  property *deadprops;  removed properties
parent、child以及sibling将所有的device node连接起来
Struct  device_node *parent;  
Struct  device_node *child;
Struct  device_node *sibling;
通过该指针可以获取相同类型的下一个node
Struct  device_node *next;  next device of same type
通过该指针可以获取node global list下一个node
struct  device_node *allnext;  next in list of all nodes
struct  kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
1.5.1. 查找节点的 OF函数1.5.1.1. of_find_node_by_name

功能 :Find a node by its "name" property函数

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

参数 :

@from:开始查找的节点,如果为NULL表示从根节点开始查找整个设备树。
@name::要查找的节点名字。

返回值: 找到的节点,如果为NULL表示查找失败。

1.5.1.2. of_find_node_by_path

功能 :Find a node matching a full OF path函数 :

struct device_node *of_find_node_by_path(const char *path)

参数 :@path: 完整的匹配路径返回值 :找到的节点,如果为NULL表示查找失败。

1.5.1.3. of_find_node_by_type

功能 Find a node by its "device_type" property函数

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

参数

@from:开始查找的节点,如果为NULL表示从根节点开始查找整个设备树
@type: 要查找的节点类型

返回值 找到的节点,如果为NULL表示查找失败。

1.5.1.4. of_find_compatible_node

功能 通过device_type和compatible查找指定节点函数

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

参数

@from:开始查找的节点,如果为NULL表示从根节点开始查找整个设备树
@type: 要查找的节点device_type属性
@compatible:节点的compatible属性列表

返回值 找到的节点,如果为NULL表示查找失败。

1.5.1.5. of_find_node_with_property

功能 通过属性名查找指定节点函数

struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)

参数

@from:开始查找的节点,如果为NULL表示从根节点开始查找整个设备树
@type: 要查找的节点属性名称

返回值 找到的节点,如果为NULL表示查找失败。

1.5.2. 查找父 /子节点的 OF函数1.5.2.1. of_get_parent

功能 函数用于获取指定节点的父节点(如果有父节点的话 )函数

struct device_node *of_get_parent(const struct device_node *node)

参数

@node:要查找父节点的节点

返回值 找到的父节点

1.5.2.2. of_get_next_available_child

功能 获取子节点,并跳过status = "disabled"的节点函数

struct device_node *of_get_next_available_child(const struct device_node *node,struct device_node *prev)

参数

@node: 父节点
@prev:当前父节点的上一个子节点, 如果为空,则获取第一个子节点

返回值 找到的子节点

1.5.3. 提取属性值的 OF函数

Linux内核使用struct property来保存节点的属性,其定义在/include/linux/of.h中:

struct property {
char  *name;      属性的名称
int  length;      属性的长度
void  *value;     属性的值
struct property *next;   下一个属性
unsigned long _flags;    
unsigned int unique_id;
struct bin_attribute attr;
};
1.5.3.1. of_find_property

功能 寻找指定的属性函数

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

参数

@np: 设备节点
@name:属性名称
@lenp:属性的字节数

返回值 找到的属性

1.5.3.2. 读取属性中u8、u16、u32和u64类型的数组数据

当设置sz为1时,就是读取一个数据,Linux内核也是这么封装的。

int of_property_read_u8_array(const struct device_node *np,
       const char *propname, u8 *out_values, size_t sz)
int of_property_read_u16_array(const struct device_node *np,
       const char *propname, u16 *out_values, size_t sz)
int of_property_read_u32_array(const struct device_node *np,
       const char *propname, u32 *out_values,size_t sz)
int of_property_read_u64(const struct device_node *np, const char *propname,
       u64 *out_value)
1.5.3.3. of_property_read_string

功能 找到并读取属性字符串函数

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

参数

@np: 设备节点
@propname:属性名称
@out_string:读取的字符串

返回值

0:读取成功
-EINVAL:属性不存在
-ENODATA:属性没有这个值
-EILSEQ:字符串不是以空字符’’结尾

<上一页  1  2  3  下一页>  
声明: 本文由入驻维科号的作者撰写,观点仅代表作者本人,不代表OFweek立场。如有侵权或其他问题,请联系举报。

发表评论

0条评论,0人参与

请输入评论内容...

请输入评论/评论长度6~500个字

您提交的评论过于频繁,请输入验证码继续

暂无评论

暂无评论

    人工智能 猎头职位 更多
    扫码关注公众号
    OFweek人工智能网
    获取更多精彩内容
    文章纠错
    x
    *文字标题:
    *纠错内容:
    联系邮箱:
    *验 证 码:

    粤公网安备 44030502002758号