设备树解决了原来内核源码中有大量硬件信息硬编码在.h头文件中造成的内核污染这一问题
在驱动源码中,往往包括两大部分:策略(操作函数)和数据(硬件信息)
没有设备树的时候数据和策略都是在内核中保存的,数据量越来越大,内核也越来越大
所以引入了设备树:
设备树文件(.dts),是一个单独的文件,用于保存硬件信息,还有.dtsi文件是.dts文件所包含的头文件,因为可能存在一个soc被用到多种电路板上,大部分的公共部分信息被存在dtsi文件中,特殊的一些信息保存在dts文件中。
dtc,是内核配置好的设备树编译\反编译\调试的工具
dtb,内核使用dtc把dts文件编译成二进制的镜像就是dtb
可以看出,使用设备树之后的硬件信息是独立在内核之外的,这就解决了上述问题
设备树的工作流程:
1.驱动开发人员先写好自己需要的硬件dts文件,编写或者改写
2.make uImage时,kernel用内部的dtc把dts编译成dtb
3.kernel启动初期就调用内部的函数解析dtb,把硬件信息解析出来组成数据结构,供操作函数使用
此时驱动中的信息已经是完备的了,dts和dtb文件完成了任务
以下是一个设备树的demo,尽管设备树一般不需要驱动开发人员来从头写,但是也很有必要深入理解它
/dts-v1/;/*版本号,一定要写*/
/ {
compatible = "demo,board";
model = "Demo Board";
cpus {
#address-cells = <1>;//值1代表占32位,值2代表64位
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0>;
};
};
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 512MB RAM at address 0x80000000
};
aliases {
serial0 = &uart0;
};
uart0: uart@101f1000 {
compatible = "arm,pl011";
reg = <0x101f1000 0x1000>;
interrupts = <1 0>;
};
led@0 {
compatible = "demo,led";
reg = <0x40000000 0x1000>;
gpio = <&gpio0 0 0>;
};
gpio0: gpio@50000000 {
compatible = "demo,gpio";
reg = <0x50000000 0x1000>;
};
};
其中描述了这几个硬件信息
compatible: 描述了设备或板子的类型和型号,操作系统可以根据这个属性加载合适的驱动。
cpus 节点: 描述了CPU的信息,如类型和地址。
memory 节点: 描述了内存的起始地址和大小。
aliases 节点: 提供别名,以简化设备节点的引用。
uart0 节点: 描述了UART设备的信息,如地址、大小和中断号。
led 节点: 描述了LED设备的信息,包括寄存器地址和关联的GPIO。
gpio0 节点: 描述了GPIO控制器的信息。
要注意,设备树是树结构,按照缩进来划分的,"/"代表根节点
了解完了什么是设备树,看一下在具体的一个驱动程序里,是怎么用的
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/io.h>
struct demo_led {
void __iomem *base;
};
static int demo_led_probe(struct platform_device *pdev)//初始化led设备
{
struct resource *res;
struct demo_led *led;
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
if (!led)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
led->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(led->base))
return PTR_ERR(led->base);
// Initialize the LED hardware here
platform_set_drvdata(pdev, led);
dev_info(&pdev->dev, "Demo LED driver probed\n");
return 0;
}
static int demo_led_remove(struct platform_device *pdev)//清理资源
{
// Cleanup the LED hardware here
dev_info(&pdev->dev, "Demo LED driver removed\n");
return 0;
}
static const struct of_device_id demo_led_of_match[] = {//定义了设备树中的compatible字符串与驱动的匹配关系
{ .compatible = "demo,led", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, demo_led_of_match);
static struct platform_driver demo_led_driver = {//定义了平台驱动结构,包括probe和remove函数,以及驱动的名称和设备树匹配表
.probe = demo_led_probe,
.remove = demo_led_remove,
.driver = {
.name = "demo-led",
.of_match_table = demo_led_of_match,
},
};
module_platform_driver(demo_led_driver);//注册了这个平台驱动,使其在模块加载时自动注册,并在模块卸载时自动注销
MODULE_AUTHOR("Author Name");
MODULE_DESCRIPTION("Demo LED Driver");
MODULE_LICENSE("GPL");
以上驱动程序中,定义了一个设备树匹配表demo_led_match,
设备树中定义的节点如果有 compatible = "demo,led" 属性,这个驱动程序就会与之匹配