文章目录
- 设备树驱动模型
- 如何使用设备树写驱动程序
- 设备树节点要与 platform_driver 能匹配
- 设备树节点指定资源,platform_driver 获得资源
- LED 模板驱动程序的改造:设备树驱动模型
- 修改设备树,添加 led 设备节点
- 修改 platform_driver 的源码
- 编译测试
- /sys目录
- 设备树的信息
- platform_device 的信息
阅读STM32MP157驱动开发——LED驱动(总线设备架构)可知,利用设备总线写的驱动代码,虽然利用platform_device结构体去定义设备资源可以避免创建太多的结构体,但是由于这些资源文件写在linux内核中,导致linux冗余,所以利用设备树管理这些资源,通过配置文件的形式在启动内核时会把设备树在内存中的地址告诉内核。
设备树相关的基础知识可以阅读博文:STM32MP157驱动开发——设备树知识
设备树驱动模型
对比总线设备架构:
设备树模型:
- 通过配置文件──设备树来定义“资源”。
- 无冗余代码,
修改引脚时只需要修改 dts 文件并编译得到 dtb 文件
,把它传给内核。 - 无需重新编译内核/驱动。
核心永远是 file_operations 结构体【 在probe 函数里分配/设置/ 注册 file_operations 结构体】
。上述方法,只是指定“硬件资源”的方式不一样。platform_device/platform_driver 只是编程的技巧,不涉及驱动的核心。
如何使用设备树写驱动程序
主要有两个步骤:
-
设备树节点要与 platform_driver 能匹配
-
设备树节点指定资源,platform_driver 获得资源
设备树节点要与 platform_driver 能匹配
① 设备树要有 compatible 属性,它的值是一个字符串
② platform_driver 中要有 of_match_table,其中一项的.compatible 成员设置为一个字符串
③ 上述 2 个字符串要一致。
实例:
设备树节点指定资源,platform_driver 获得资源
- 如果在设备树节点里使用reg 属性 , 那么内核生成对应的platform_device 时会用 reg 属性来设置 IORESOURCE_MEM 类型的资源。
- 如果在设备树节点里使用 interrupts 属性,那么内核生成对应的platform_device 时会用 reg 属性来设置 IORESOURCE_IRQ 类型的资源。对于 interrupts 属性,内核会检查它的有效性,所以不建议在设备树里使用该属性来表示其他资源。
- 如果驱动程序中根据 pin 属性来确定引脚,那么我们就在设备树节点中添加 pin 属性。
实例:
#define GROUP_PIN(g,p) ((g<<16) | (p))
100ask_led0 {
compatible = “100ask,led”;
pin = <GROUP_PIN(5, 3)>;
};
驱动程序中,可以从 platform_device 中得到 device_node,再用of_property_read_u32
得到属性的值:
struct device_node* np = pdev->dev.of_node;
int led_pin;
int err = of_property_read_u32(np, “pin”, &led_pin);
LED 模板驱动程序的改造:设备树驱动模型
修改设备树,添加 led 设备节点
进入ubuntu系统中存放的Linux内核文件,找到单板所用的设备树文件,在它的根节点下添加如下内容:
define GROUP_PIN(g,p) ((g<<16) | (p))
/ {
my_led@0 {
compatible = "myled,leddrv";
pin = <GROUP_PIN(3, 1)>;
};
my_led@1 {
compatible = "myled,leddrv";
pin = <GROUP_PIN(5, 8)>;
};
};
修改 platform_driver 的源码
找到 chip_demo_gpio.c,在结构体变量chip_demo_gpio_driver前插入如下内容,并修改chip_demo_gpio_driver变量内容:
static const struct of_device_id my_leds[] = {
{ .compatible = "myled,leddrv" },
{ },
};
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe,
.remove = chip_demo_gpio_remove,
.driver = {
.name = "my_led",
.of_match_table = my_leds,
},
};
指定了 of_match_table,它是用来跟设备树节点匹配的,如果设备树节点中有 compatile 属性,并且其值等于“myled,leddrv”,
就会导致 probe 函数被调用
其他代码和STM32MP157驱动开发——LED驱动(总线设备架构)内一样
编译测试
首先要设置 ARCH、CROSS_COMPILE、PATH 这三个环境变量后,进入 ubuntu 上板子内核源码的目录,在Linux内核源码根目录下,执行如下命令即可编译 dtb 文件:
make dtbs V=1
编译好的文件在路径由DTC指定,在 arch/arm64/boot/dts 目录下,移植到开发板的共享文件夹中,先保存源文件,然后覆盖源文件,重启后会挂载新的设备树,进入该目录查看是否有新添加的设备节点
cd /sys/firmware/devicetree/base
ls 节点名称
查看/sys/devices/platform 目录下有无对应的 platform_device
加载驱动、测试驱动:
//按顺序加载模块
insmod leddrv.ko
insmod chip_demo_gpio.ko
echo "7 4 1 7" > /proc/sys/kernel/printk //打开内核输出信息
ls /dev/my_led* //显示注册了的节点
./ledtest /dev/my_led0 on //打开
./ledtest /dev/my_led0 off //熄灭
/sys目录
/sys 目录下有很多内核、驱动的信息。
设备树的信息
以下目录对应设备树的根节点,可以从此进去找到自己定义的节点。
cd /sys/firmware/devicetree/base/
节点是目录,属性是文件。
- 属性值是字符串时,用 cat 命令可以打印出来;
- 属性值是数值时,用hexdump 命令可以打印出来。
platform_device 的信息
以下目录含有注册进内核的所有 platform_device:
/sys/devices/platform
一个设备对应一个目录
进入某个目录后,如果它有“driver”子目录,就表示这个 platform_device 跟某个 platform_driver 配对了。一个平台设备只能配对一个平台驱动,一个平台驱动可以配对多个平台设备。