LV06-11-设备树插件-02-设备树插件的使用

怎么在linux系统中使用设备树插件?若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.2的64位版本
VMware® Workstation 17 Pro 17.6.0 build-24238078
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04
linux内核 linux-4.19.71(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
nxp-imx/linux-imx/releases/tag/v4.19.71 NXP linux内核仓库tags中的v4.19.71
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)
Source Code https://elixir.bootlin.com/linux/latest/source linux kernel源码
kernel/git/stable/linux.git - Linux kernel stable tree linux kernel源码(官网,tag 4.19.71)
https://elixir.bootlin.com/u-boot/latest/source uboot源码

一、移植设备树插件

1. 挂载 configfs 虚拟文件系统

1.1 内核配置

configfs 虚拟文件系统是什么?这个我们后面会学习,现在主要是学习设备树插件的使用。首先我们打开 Linux 内核源码, 输入以下命令打开 menuconfig 配置界面。

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

界面打开之后,按以下菜单找到对应的配置

1
2
3
File systems  --->
Pseudo filesystems --->
{M} Userspace-driven configuration filesystem

将这一项从 M 改为*。修改完进行保存。

1.2 编译验证

将编译之后的内核镜像烧写到开发板上, 接着可以使用 mount 命令检查 configfs 虚拟文件系统是否挂载成功:

1
mount
image-20250304194052713

没有configfs相关的内容就说明没有自动挂载上,我们可以用mount命令手动挂载:

1
mount -t configfs none /sys/kernel/config

挂载成功如下图:

image-20250304194212262

2. 配置内核支持设备树插件

我们打开 Linux 内核源码, 还是输入以下命令打开 menuconfig 配置界面。

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v6_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

2.1 Device Tree overlay

界面打开之后,按以下菜单找到对应的配置项

1
2
3
Device Drivers  --->
-*- Device Tree and Open Firmware support --->
[*] Device Tree overlays

2.2 Overlay filesystem support

按以下菜单找到对应的配置项

1
2
3
4
5
6
7
File systems  --->
<*> Overlay filesystem support
-*- Overlayfs: turn on redirect directory feature by default
[*] Overlayfs: follow redirects even if redirects are turned off (NEW)
[*] Overlayfs: turn on inodes index feature by default
[*] Overlayfs: auto enable inode number mapping
[*] Overlayfs: turn on metadata only copy up feature by default

3. 移植驱动

如果已经学完了 configfs 虚拟文件系统的数据结构。其实完全可以自己编写驱动实现一个设备树插件。 但是我们没有必要重复造轮子, github 上有大神编写好的设备树插件驱动,例如 GitHub - ikwzm/dtbocfg: Device Tree Blob Overlay Configuration File System

image-20250304190241625

这就是一个驱动模块的源码,我们修改Makefile配置好源码的路径和编译工具,直接编译就可以了,编译完毕会得到一个ko文件,这个后面会用到。

4. 总结

移植设备树插件主要包括以下几个步骤 :

(1)配置内核支持挂载 configfs 虚拟文件系统。

(2)配置内核支持设备树插件。

(3)移植设备树插件驱动。

其中(1)和(2)完成后要注意重新编译内核,再把开发板中的内核更新一下。

二、设备树插件实例

1. 设备树插件中添加节点

1.1 设备树插件

1.1.1 设备树源码

我们用 imx6ull-alpha-emmc.dts 中led灯的这个设备节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/dts-v1/;

#include "imx6ull.dtsi"
#include "imx6ull-alpha-emmc.dtsi"

/ {
model = "Freescale i.MX6 UlltraLite ALPHA EMMC Board";
compatible = "fsl,imx6ull-alpha-emmc", "fsl,imx6ull";
//把这个节点删掉,移到设备树插件中
sdev_led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "sdev_led";
status = "okay";
reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0X04 /* GPIO1_DR_BASE */
0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
};
};

&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL3_PFD2>;
assigned-clock-rates = <320000000>;
};

我们写一个设备树插件 imx6ull-alpha-emmc-overlay.dts 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/dts-v1/;
/plugin/;

&{/} {
overlay_node{
sdev_led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "sdev_led";
status = "okay";
reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0X04 /* GPIO1_DR_BASE */
0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
};
};
};

我们把主设备树中的 sdev_led 节点移动到设备树插件中去。需要注意的是,这样在设备树插件中添加节点的操作似乎并不会自动取生成platform_device,所以如果用的平台设备驱动的话可能会有问题。

1.1.2 编译设备树插件

我们使用以下命令进行编译设备树插件:

1
./dtc -I dts -O dtb imx6ull-alpha-emmc-overlay.dts -o imx6ull-alpha-emmc-overlay.dtbo

设备树的编译可以看demo中的makefile,它包含了一些其他的设备树和头文件,所以编译会复杂一点。

1.2 更新内核和设备树

我们按照前面的更新内核和设备树,然后重启设备。然后将imx6ull-alpha-emmc-overlay.dtbo和dtbocfg.ko 放到开发板中。

1.3 环境准备

1.3.1 configfs文件系统挂载

在内核根文件系统中,有一个文件 /proc/filesystems ,它表示当前内核支持的文件系统,通常是编入内核的文件系统类型,但也可以通过模块加入新的类型。可以通过命令 cat /proc/filesystems 检查 configfs 是否支持。 支持的话就会有如下信息 :

image-20250304194430956

不支持的话可以看前面的笔记对内核进行配置。支持之后,我们可以用mount命令看一下有没有挂载上,若是没挂载的话,后面加载驱动是不会生成对应的目录的。可以使用以下命令挂载:

1
mount -t configfs none /sys/kernel/config

1.3.2 加载dtbocfg.ko

我们通过以下命令加载:

1
insmod dtbocfg.ko

会有如下打印信息:

image-20250316110056630

加载成功应该会生成 /sys/kernel/config/device-tree/overlays 目录:

image-20250316110142985

要是没有的话,就注意用mount看一下是不是成功挂载了configfs。

1.4 设备树插件测试

  • (1)查看未添加设备树插件的时候的设备树情况
1
ls /proc/device-tree/
image-20250316110402857
  • (2)我们进入 /sys/kernel/config/device-tree/overlays 目录
1
cd /sys/kernel/config/device-tree/overlays
  • (3)新建一个dtbo_demo目录
1
mkdir dtbo_demo

image-20250316110241369

然后进入这个目录就会发现这个目录下自动生成了两个文件:dtbo和status。

  • (4)将设备树插件的内容写入到dtbo中
1
2
cd /sys/kernel/config/device-tree/overlays/dtbo_demo
cat /drivers_demo/imx6ull-alpha-emmc-overlay.dtbo > dtbo
  • (5)使能 dtbo
1
2
cd /sys/kernel/config/device-tree/overlays/dtbo_demo
echo 1 > status
  • (6)查看设备树插件加载的节点
1
ls /proc/device-tree/overlay_node/
image-20250316110717161
  • (7)删除使用 dtbo 修改的节点
1
2
3
4
5
cd /sys/kernel/config/device-tree/overlays
rmdir dtbo_demo
# 或者
cd /sys/kernel/config/device-tree/overlays/dtbo_demo
echo 0 > status

然后我们再查看一下 /proc/device-tree,就会发现没有 overlay_node 节点了:

image-20250316110936568

2. 修改后设备树节点属性

2.1 设备树插件

我们用 imx6ull-alpha-emmc.dts 中led灯的这个设备节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/dts-v1/;

#include "imx6ull.dtsi"
#include "imx6ull-alpha-emmc.dtsi"

/ {
model = "Freescale i.MX6 UlltraLite ALPHA EMMC Board";
compatible = "fsl,imx6ull-alpha-emmc", "fsl,imx6ull";

sdev_led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "sdev_led";
//status = "disable";
reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0X04 /* GPIO1_DR_BASE */
0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
};
};

&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL3_PFD2>;
assigned-clock-rates = <320000000>;
};

我们写一个设备树插件 imx6ull-alpha-emmc-overlay.dts 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/dts-v1/;
/plugin/;

//修改设备树节点属性的demo
/{
fragment@0 {
target-path = "/alpha/sdev_led";
__overlay__ {
overlay_node{
status = "okay";
};
};
};
};

我们把主设备树中的 sdev_led 节点移动到设备树插件中去。需要注意的是,这样在设备树插件中添加节点的操作似乎并不会自动取生成platform_device,所以如果用的平台设备驱动的话可能会有问题。

2.2 开发板测试

  • (1)加载设备树插件之前节点的情况
image-20250316114743941
  • (2)加载设备树插件之前加载驱动
image-20250316114835680

看到会报一个状态读取失败,然后我们运行测试demo,发现可以正常操作,这个应该是因为我内部判断了,但是没有返回错误的原因,不过这里不影响测试。测完卸载驱动。

  • (3)加载设备树插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 挂载configfs文件系统
mount -t configfs none /sys/kernel/config

# 加载设备树插件驱动
insmod dtbocfg.ko

# 创建目录
cd /sys/kernel/config/device-tree/overlays
mkdir dtbo_demo

# 将设备树插件的内容写入到dtbo中
cd /sys/kernel/config/device-tree/overlays/dtbo_demo
cat /drivers_demo/imx6ull-alpha-emmc-overlay.dtbo > dtbo

# 使能 dtbo
cd /sys/kernel/config/device-tree/overlays/dtbo_demo
echo 1 > status
image-20250316115228837

可以看到这里生成了一个 overlay_node 目录,里面包含了name和status属性。

  • 重新加载设备的驱动
1
insmod sdriver_demo.ko
image-20250316115355677

发现还是报错的,这是因为status属性并不是在/alpha/sdev_led/status,而是alpha/sdev_led/overlay_node/status所以获取的时候出现了问题,这个大概了解就可以了,后续使用设备树插件的话需要注意这个事情。