驱动与设备的关系,可以类比为函数与变量的关系,函数负责具体的执行动作,变量负责描述自身属性,两者之间
是互相依存,缺一不可,通常情况下,都是一对一的关系,即一个驱动对应一个设备,但更多的时候是一对多的关
系,即一个驱动对应好几个设备。比如,一颗主控芯片,集成了四个串行端口,这个时候,你不可能写四份驱动然
后分别注册设备,这样显然很冗余。通常都是只写一份驱动,然后用变量描述之间的差异,分别执行四次,达到注
册四个设备的目的。实际操作中,直接用变量来描述设备的属性,也显得灵活性不够。每添加一个设备,不仅仅面
临重新编译的问题,而且代码可读性还比较差。在这个时候,设备树应运而生,我们只需将所有的设备用文本的方
式描述好,然后直接解析这个文件,依据文件的内容直接生成相应的设备。
设备树本质就是一个配置文件,描述了所有设备的属性。JSON作为一种轻量级的数据交换格式,简洁的层次结构
使得其成为理想的配置文件,易于人们编写和阅读,同时也易于机器生成和解析。选择JSON作为设备树的存储格
式再合适不过了。
JSON
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为
,纪录
(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者
关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
object
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于
这些结构的编程语言之间交换成为可能。
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一
个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)
分隔。
设
备
树
技
术
源
码
解
析
引
言
语
法
两
种
结
构
对
象
(
)
五
种
形
式
值(value)可以是双引号括起来的字符串(string)、数值(number)、 true 、 false 、 null 、对象(object)
或者数组(array)。这些结构可以嵌套。
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一
个单独的字符串(character string)。
字符串(string)与C或者Java的字符串非常相似。
数值(number)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。
dtnode
XBOOT规定了设备树中描述的每一个设备节点都是一个json对象,对象里面可以包含各种形式的键值对。每个设备
节点包含如下关键信息:
设备节点名字
设备自动分配起始索引或者设备物理地址
具体的json对象
struct dtnode_t {
};
const char * name;
physical_addr_t addr;
json_value * value;
为了快速获取设备节点信息,这里提供了一组快捷操作函数,直接返回设备节点结构体相关信息。下面列举了两个
设备节点描述,一个LED灯,没有设备物理地址,仅有设备分配起始索引,另一个PL011串行端口控制器,提供了
设备物理地址。设备名称与索引或物理地址之间以 @ 分隔,并表现为 object 对象,如下所示:
设
备
节
点
结
构
体
定
义
获
取
设
备
节
点
信
息
"led‐gpio@0": {
"gpio": 0,
"active‐low": true,
"default‐brightness": 0
"uart‐pl011@0x10009000": {
"clock‐name": "uclk",
"txd‐gpio": ‐1,
"txd‐gpio‐config": ‐1,
"rxd‐gpio": ‐1,
"rxd‐gpio‐config": ‐1,
"baud‐rates": 115200,
"data‐bits": 8,
"parity‐bits": 0,
"stop‐bits": 1
},
}
即获取 @ 左侧部分,这个节点名称,就是所对应的驱动名称,在依据设备树添加添备时,会自动匹配同名驱动。写
在设备树前面的的设备节点先匹配,写在后面的设备节点后匹配,这里提供了一套优先级机制,解决设备间互相依
赖的问题。如果一个设备依赖于另外一个设备,那么此设备的设备节点,必须写在依赖设备的后面,如果顺序颠
倒,在注册设备时,会因找不到依赖设备,而出现注册失败的现象。一般的,写在最前面的,都是比较底层的驱动
设备,比如 clk 、 irq 、 gpio 等,写在后面的,都是比较高等级的设备,比如 framebuffer 等。
const char * dt_read_name(struct dtnode_t * n)
{
}
return n ? n‐>name : NULL;
即获取 @ 右侧部分,数值类型。起始索引ID,主要用于同一个驱动在注册多个设备时,可以手动指定设备尾缀,
以 .0 、 .1 、 .2 等形式存在,在注册设备时,如果该设备尾缀已经被占用,则自动加一,直到找到空闲尾缀为
止。如果设备节点没有提供 @ 右侧部分,自动从 .0 开始。
int dt_read_id(struct dtnode_t * n)
{
}
return n ? (int)n‐>addr : 0;
获
取
设
备
节
点
名
称
获
取
设
备
自
动
分
配
起
始
索
引
获
取
设
备
物
理
地
址
这个函数具体的实现跟上面的获取自动分配起始索引几乎一模一样,唯一的差异,就是返回值额类型。设备节点存
在两种形态,一种带有设备物理地址的,还有一种没有的,比如上面的 PL011 与 LED 。这两种形态在描述设备时
不加以区分,仅在注册设备时,驱动才显式的调用对应的方法,以获取设备信息。
physical_addr_t dt_read_address(struct dtnode_t * n)
{
}
return n ? n‐>addr : 0;
设备节点对象包含各种形式的键值对,包括布尔逻辑、整形、浮点、字符串、对象,数组。每个具体的实现函数,
都提供了默认值参数,如果找不到该键值对,就返回传递的默认值参数。
json_value * v;
int i;
return v‐>u.boolean ? 1 : 0;
int dt_read_bool(struct dtnode_t * n, const char * name, int def)
{
if(n && n‐>value && (n‐>value‐>type == json_object))
{
for(i = 0; i < n‐>value‐>u.object.length; i++)
{
if(strcmp(n‐>value‐>u.object.values[i].name, name) == 0)
{
}
}
}
return def;
}
v = n‐>value‐>u.object.values[i].value;
if(v && (v‐>type == json_boolean))
访
问
设
备
节
点
对
象
获
取
布
尔
逻
辑
获
取
整
型
数
据
json_value * v;
int i;
int dt_read_int(struct dtnode_t * n, const char * name, int def)
{
if(n && n‐>value && (n‐>value‐>type == json_object))
{
for(i = 0; i < n‐>value‐>u.object.length; i++)
{
if(strcmp(n‐>value‐>u.object.values[i].name, name) == 0)
{
}
}
}
return def;
}
v = n‐>value‐>u.object.values[i].value;
if(v && (v‐>type == json_integer))
return (int)v‐>u.integer;
json_value * v;
int i;
long long dt_read_long(struct dtnode_t * n, const char * name, long long def)
{
if(n && n‐>value && (n‐>value‐>type == json_object))
{
for(i = 0; i < n‐>value‐>u.object.length; i++)
{
if(strcmp(n‐>value‐>u.object.values[i].name, name) == 0)
{
}
}
}
return def;
}
v = n‐>value‐>u.object.values[i].value;
if(v && (v‐>type == json_integer))
return (long long)v‐>u.integer;
获
取
长
整
型
数
据
json_value * v;
int i;
return (double)v‐>u.dbl;
double dt_read_double(struct dtnode_t * n, const char * name, double def)
{
if(n && n‐>value && (n‐>value‐>type == json_object))
{
for(i = 0; i < n‐>value‐>u.object.length; i++)
{
if(strcmp(n‐>value‐>u.object.values[i].name, name) == 0)
{
}
}
}
return def;
}
v = n‐>value‐>u.object.values[i].value;
if(v && (v‐>type == json_double))
获
取
浮
点
型
数
据
获
取
字
符
串
json_value * v;
int i;
return (char *)v‐>u.string.ptr;
char * dt_read_string(struct dtnode_t * n, const char * name, char * def)
{
if(n && n‐>value && (n‐>value‐>type == json_object))
{
for(i = 0; i < n‐>value‐>u.object.length; i++)
{
if(strcmp(n‐>value‐>u.object.values[i].name, name) == 0)
{
}
}
}
return def;
}
v = n‐>value‐>u.object.values[i].value;
if(v && (v‐>type == json_string))
json
获
取
对
象