10. 设备树插件(Device Tree Overlays)¶
Linux4.4以后引入了动态设备树(Dynamic DeviceTree),我们这里翻译为“设备树插件”。 设备树插件可以理解为主设备树的“补丁”它动态的加载到系统中,并被内核识别。 例如我们要在系统中增加RGB驱动,那么我们可以针对RGB这个硬件设备写一个设备树插件, 然后编译、加载到系统即可,无需从新编译整个设备树。
设备树插件是在设备树基础上增加的内容,我们之前讲解的设备树语法完全适用, 甚至我们可以直接将之前编写的设备树节点复制到设备树插件里。具体使用方法介绍如下。
10.1. 设备树插件格式¶
设备树插件拥有相对固定的格式,甚至可以认为它只是把设备节点加了一个“壳”编译后内核能够动态加载它。 格式如下,具体节点省略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
/*在此添加要插入的节点*/
.......
};
};
fragment@1 {
target = <&XXXXX>;
__overlay__ {
/*在此添加要插入的节点*/
.......
};
};
.......
};
|
第1行: 用于指定dts的版本。
第2行: 表示允许使用未定义的引用并记录它们,设备树插件中可以引用主设备树中的节点,而这些“引用的节点”对于设备树插件来说就是未定义的,所以设备树插件应该加上“/plugin/”。
第6行: 指定设备树插件的加载位置,默认我们加载到根节点下,既“target-path =“/”,或者使用target = <&XXXXX>,增加节点或者属性到某个节点下。
第7-8行: 我们要插入的设备及节点或者要引用(追加)的设备树节点放在__overlay__ {…}内,你可以增加、修改或者覆盖主设备树的节点。
另外一种设备树插件格式:
1 2 3 4 5 6 7 8 9 10 | /dts-v1/;
/plugin/;
&{/} {
/*此处在根节点"/"下,添加要插入的节点或者属性*/
};
&XXXXX {
/*此处在节点"XXXXX"下,添加要插入的节点或者属性*/
};
|
以上两种格式的设备树插件都可以使用,本章的实验以第二种格式为例。编译都是在内核源码下,具体参考 驱动环境搭建章节,如果单独编译需要注意一些库的使用和DTC版本。
10.2. 设备树插件加载¶
以lubancat2为例(ubuntu20.04镜像),该设备树插件的加载是通过uboot,流程如下:
编写设备树插件源文件,通过DTC工具编译生成.dtbo文件,存储在boot分区;
加载boot分区的设备树插件到内存;
在uboot中,合并设备树插件dtbo和设备树dtb文件为一个设备树,并得到内存指定地址;
启动内核,传递设备树在内存中的地址。
10.3. 设备树插件实验一¶
10.3.1. 硬件介绍¶
本节实验使用到Lubancat_RK系列板卡
10.3.2. 设备树插件编写和加载¶
本章的示例代码目录为:linux_driver/dynamic_device_tree
为避免冲突,需要删除上一章节在主设备树上添加的led_test节点,改为设备树插件的形式, 在内核源码/arch/arm64/boot/dts/rockchip/overlays目录下添加名为lubancat-led-overlay.dts的文件,内容参考如下:
以lubancat2为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
/*添加led_test节点,*/
led_test{
#address-cells = <1>;
#size-cells = <1>;
compatible = "fire,led_test";
ranges;
//例程是控制lubancat2的系统灯 GPIO0_C7
led@0xfdd60004{
reg = <0xfdd60004 0x00000004 0xfdd6000C 0x00000004>; //数据寄存器和数据方向寄存器(高16位)
status = "okay";
};
};
};
|
以lubancat4为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
/*添加led_test节点,*/
led_test{
#address-cells = <2>;
#size-cells = <1>;
compatible = "fire,led_test";
ranges;
led@0xfec50000{
reg = <0xfec50000 0x00000004 0xfec50008 0x00000004>;
status = "okay";
};
};
};
};
};
|
以上内容和上一章节区别不大,只是根据设备树插件的编写格式进行修改。
第6行: 指定设备树插件的加载位置,加载到根节点下。
第8-21行: 我们要插入的设备及节点或者要引用(追加)的设备树节点放在__overlay__ {…}内,将上一章节主设备树的test_led节点添加于此。
修改内核目录/arch/arm64/boot/dts/rockchip/overlays下的Makefile文件, 添加我们编辑好的设备树插件。并把设备树插件文件放在和Makefile文件同级目录下。 以进行设备树插件的编译。
然后在内核源码顶层目录执行以下命令编译设备树插件:
rk356x系列板卡执行以下命令:
#加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
rk3588系列板卡执行以下命令:
#加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3588_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
编译出来的设备树插件在 内核源码/arch/arm64/boot/dts/rockchip/overlay/lubancat-led-overlay.dtbo
,
将设备树插件传到板卡的 /boot/dtb/overlay/
目录下,并在 /boot/uEnv/uEnv.txt
按照格式添加我们的设备树插件,然后重启开发板,那么系统就会加载我们编译的设备树插件。
10.3.3. 驱动代码¶
驱动部分和上一章节完全一样,此处不做过多说明,区别只是上一章节使用设备树,本章节使用设备树插件,原理是一样的。
10.3.4. 测试LED¶
在本节实验中,鲁班猫系列板卡,系统设备树中均默认使能了 LED
的设备功能,需要关闭设备树的leds节点,可以修改leds节点的 status = "okay";
为 status = "disabled";
,然后编译设备树进行替换,也可以在板卡中直接使用以下命令关闭系统leds驱动对LED的控制:
sudo sh -c 'echo 0 > /sys/class/leds/sys_status_led/brightness'
将led的亮度调为0,与此同时led的触发条件自动变为none,从而取消leds驱动对LED的控制。
将设备树、驱动程序和应用程序通过NFS或SCP等方式拷贝到开发板中。
重启后在目录/proc/device-tree/下,可以找到led_test,图中控制的是GPIO0_C7引脚,如下所示:
执行如下命令加载驱动:
sudo insmod led_test.ko
驱动加载成功后直接运行应用程序如下所示。
命令:./test_app <命令>
命令是一个“unsigned char”型数据,输入1表示灭,0表示亮。
执行结果如下:
与此同时,观察板卡心跳灯可以看到LED亮或者灭。