一.设备树的引进
1.1字符设备驱动的三种写法
1.1.1怎么写设备驱动?
- 看原理图 1. 确定引脚: 2. 看芯片手册:确定如何操作引脚 -写驱动程序(起封装作用,不用涉及硬件)
- 分配一个结构体:file_operation
- 设置结构体成员:
open = led_open:把led引脚设置为输出引脚
write = led_write :根据App传入的值,设置引脚状态 - 注册(告诉内核,加入驱动链表):利用注册函数 regist_chrdev(主设备号,结构体,name)
- 入口:
- 出口
1.1.2 驱动中指定引脚的三种方法
- 传统方法:在驱动代码中写死
- 总线设备驱动模型
led_drv:注册驱动,入口,出口等等
led_dev:指定引脚 - 使用设备树指明引脚
led_drv:注册驱动,入口,出口等等
dts:设备树指定引脚
- 驱动写法,核心不变,差别在于如何指定硬件资源
1.1.3三种方法指定引脚优缺点
1.1.3.1传统方法:
优点:简单
缺点:不易拓展,需要重新编译
1.2.3.2总线设备驱动模型:
- led_dev.c:负责配置platform_device结构体,指定引脚资源,不同的设备指定不同的资源,我们只需要修改对应的资源,每一个设备就是一个dev.c文件
platform_device包含:
.resource:指明引脚资源
.name:设备名字
- led_drv.c:负责配置platform_driver结构体
platform_driver包含:
.id_table: 第一个用来匹配dev和drv的成员属性
.driver:name等等,找到对应的dev.c
.probe:如果匹配成功,分配/设置/注册file_operation结构体,根据APP传入的值,确定对应的引脚,来自平台设备
.remove:移除设备
- 如何匹配dev和drv?
- bus_type 结构体中的platform_match用来比对dev和drv,优先比较drv中id_table和dev中的名字,如果找不到,再比较drv中driver和dev中的名字。
- 稍微复杂,易拓展。但是与很多冗余代码,每次更改都需要重新更改
1.1.3.3 设备树:是对平台设备驱动的一种改进
- 相对于总线设备驱动,drv不变,dev变成dts文件,即指定资源的方式变了。
- dts中构造节点,节点含有资源,将dts编译成dtb二进制文件,传递给内核,内核会解析dtb文件,得到设备节点device_node,进一步得到platform_device(资源)
- 对于dts生成的platform_device,含有of_node,of_node里面含有属性,比如compatible,pin等
1. led_drv.c:负责分配/设置/注册:platform_device(同总线方法一样)
2. dts:指定资源,更换单板时,只需修改dts文件,最后会生成dtb文件
3. 无冗余代码,不要重新编译内核或驱动,只需提供不一样的设备树文件
1.2 dts
1.2.1 dts格式
DTS文件布局(layout);
/dts-v1/; 表示dts的版本
[memory reservations] //格式为 /memreserve/ <adress><length>;表示保留的内存区域
/{ 根,设备树的起点
[property definitions]属性
[child nodes]子节点,同一级别的节点名字互不相同
};
Property 格式1:
[lable:] property-name = value;
value的表达方式:
1.<1 0x1 0x123>:表示3个32位数据array of cell
2."字符串"
3.[00 11 22]:byte string,16进制表示的一个或多个byte,必须用俩位
16进制数表示,空格可以省略
4.可组合多种类型的值,中间用逗号分开
Property 格式2:(没有值)
[lable:]property-name;
Devicetree node 格式:
[lable:] node-name[@unit-address]{
[properties definatons]
[]child nodes
};
1.2.2 dts特殊的,默认属性
- /根节点
1. #address-cells //在他的子节点reg属性中,使用多少个u32整数来描述地址
2. #size-cells //在他的子节点reg属性中,使用多少个u32整数来描述大小
3. compatible // 定义一系列的字符串,用来指定内核中哪个machhine_desc可以支持本设备,即这个板子兼容哪些平台,uImage
4. model //这个板子是什么,比如两个板子的配置基本一致,他们的compatible是一样的,那么就通过,model来分辨 - /memory
device_type = “memory”;
reg //用来指定内存的地址和大小 - /chosen
bootarges //内核command line参数,跟u-boot中设置的bootarges的作用是一样的 - /cpus
device_type = “cpu”
reg //表明自己是哪个cpu
1.2.3引用其他节点
- phandle://节点中的phandle属性,他的取值必须是唯一的(不要跟其他节点的phandle值是一样的)
例:
pic@10000000{
phandle = <1>;
interrupt-controller;//中断控制器
};
another-device-node{
interrupt-parent = <1>;//引用phandle值为1的节点
}
- lable:
PIC:pic@10000000{
interrupt-controller;//中断控制器
};
another-device-node{
interrupt-parent = <&PIC>;//使用lable来引用上述节点,使用lable实际上也是使用phandle来引用,
//在编译dits文件为dtb时,编译器dtc会在dtb中插入phandle属性
}
1.3 dtb
1.3.1 dtb格式
- struct _ftd_header:头部·用来表明各个分部的偏移地址
- memory reservation block:保留的内存空间信息
- structure block:主体结构
- string block:属性的名字,以00结尾
1.3.2 struct _ftd_header
1.3.3 memory reservation block
1.3.4 structure block
- FDT_BEGIN_NODE(0X0000 0001):节点的开头,+节点名字(根节点除外)
- FDT_END_NODE(0X0000 0002):节点的尾部
- FDT_PROP(0X0000 0003):表示一个属性的开始,+ 一个结构体 ,+ value
len表示value的长度,nameoff表示属性的名字在string block的偏移量
4.FDT_END(0X0000 0009):整个structure结束