Skip to content

LV220-设备树插件简介

设备树插件是什么?若笔记中有错误或者不合适的地方,欢迎批评指正 😃。

一、设备树插件?

1. 什么是设备树插件

Linux4.4 以后引入了动态设备树(Dynamic DeviceTree) 。 设备树插件(Device Tree Overlay)是一种用于设备树(Device Tree) 的扩展机制。 设备树是一种用于描述硬件设备的数据结构,广泛应用于嵌入式系统中, 特别是基于 Linux 内核的系统中。

设备树插件 允许在运行时动态修改设备树的内容, 以便添加, 修改或删除设备节点和属性。它提供了一种灵活的方式来配置和管理硬件设备, 而无需重新编译整个设备树。 通过使用设备树插件, 开发人员可以在不重新启动系统的情况下对硬件进行配置更改。

设备树插件(Dynamic DeviceTree) 通常以一种文本格式定义, 称为设备树源文件(Device Tree Source, DTS) 。 DTS 文件描述了设备树的结构和属性, 包括设备节点, 寄存器地址, 中断信息等。 设备树插件可以通过加载和解析设备树文件, 并将其合并到现有的设备树中, 从而实现对设备树的动态修改。

设备树插件可以理解为主设备树的“补丁”它动态的加载到系统中,并被内核识别。 例如我们要在系统中增加 RGB 驱动,那么我们可以针对 RGB 这个硬件设备写一个设备树插件, 然后编译、加载到系统即可,无需从新编译整个设备树。

2. 应用场景

使用设备树插件, 可以实现一些常见的配置变化, 比如添加外部设备, 禁用不需要的设备,修改设备属性等。 这对于嵌入式系统的开发和调试非常有用, 特别是面对多种硬件配置或需要频繁更改硬件配置的情况下。

二、基本语法

设备树插件是在设备树基础上增加的内容,我们之前讲解的设备树语法完全适用, 甚至我们可以直接将之前编写的设备树节点复制到设备树插件里。但是也会有自己的一些语法,下面就来看一下。

1. 头部声明

设备树插件有 插件头部声明, 它指定了插件的名称和版本等信息, 并指定了要修改的设备树的路径, 如下所示:

c
/dts-v1/;
/plugin/;
  • 第 1 行: 用于指定 dts 的版本。
  • 第 2 行: 表示允许使用未定义的引用并记录它们,设备树插件中可以引用主设备树中的节点,而这些“引用的节点”对于设备树插件来说就是未定义的,所以设备树插件应该加上“/plugin/”。

2. 节点

插件节点名称用于定义要添加, 修改或删除的设备节点及其属性。 它使用与设备树源文件相同的语法, 但在节点名称前面使用特定的修饰符来指示插件的操作。

比如设备树中 rs485 节点为如下所示, rs485 节点位于根节点下:

c
/dts-v1/;
/ {
    model = "This is my devicetree!";
    #address-cells = <1>;
    #size-cells = <1>;
    chosen {
        bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
    };
    //......
    rk_485_ctl: rk-485-ctl {
        compatible = "topeet, rs485_ctl";
        gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&rk_485_gpio>;
    };
};

那么我们如果在设备树插件中要为这个节点添加 overlay_node 节点, 可以有四种表达方式:

2.1 方式一:从根节点开始的绝对路径

c
/dts-v1/;
/plugin/;

&{/rk-485-ctl} {
    overlay_node{
        status = "okay";
    };
};

2.2 方式二:使用节点的别名

c
/dts-v1/;
/plugin/;

&rk_485_ctl {
    overlay_node{
        status = "okay";
    };
};

2.3 方式三

固定写法,如果有多个节点,通过子节点 fragment@x 的序号区分,路径也是绝对路径:

c
/dts-v1/;
/plugin/;

/{
    fragment@0 {
        target-path = "/rk-485-ctl";
        __overlay__ {
            overlay_node{
            	status = "okay";
            };
        };
    };
};
  • 第 6 行: 指定设备树插件的加载位置,默认我们加载到根节点下,既“target-path =“/”。
  • 第 7-8 行: 我们要插入的设备及节点或者要引用(追加)的设备树节点放在__overlay__ {…};内。

方式一编译为 dtbo 后,再反编译就会得到这种格式的。

2.4 方式四

固定写法,如果有多个节点,通过子节点 fragment@x 的序号区分,路径也可以用别名:

c
/dts-v1/;
/plugin/;

/{
    fragment@0 {
        target = <&rk_485_ctl>;
        __overlay__ {
            overlay_node{
            	status = "okay";
            };
        };
    };
};

方式二编译为 dtbo 后,再反编译就会得到这种格式的。

3. 总结

设备树插件拥有相对固定的格式,甚至可以认为它只是把设备节点加了一个“壳”编译后内核能够动态加载它。

3.1 基本格式 1

c
/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__ {…}内,你可以增加、修改或者覆盖主设备树的节点。

3.2 基本格式 2

c
/dts-v1/;
/plugin/;

&{/} {
    /*此处在根节点 "/" 下, 添加要插入的节点或者属性*/
};

&XXXXX {
    /*此处在节点 "XXXXX" 下, 添加要插入的节点或者属性*/
};

三、设备树插件编译

1. DTC 编译

使用 dtc 工具:

shell
./dtc -I dts -O dtb overlay.dts -o overlay.dtbo

2. DTC 反编译

使用 dtc 工具:

shell
./dtc -I dtb -O dts overlay.dtbo -o overlay_dtb.dts