DTS实验
qemu的dumpdtb参数可以解析出qemu virt设备平台使用的默认dtb配置。
qemu-system-riscv64 -M virt,dumpdtb=qemu.dtb
成功解析出目标dtb文件,但此文件无法直接进行修改,必须将其修改为dts文件。
dtc -I dtb -O dts qemu.dtb -o qemu.dts
(dtc为设备树的编译工具,可以做dts文件与dtb文件的相互转化。该工具可以在发行版下直接安装device-tree-compiler,或者在Linux源码scripts\dtc下:
编译过内核源码,此目录下会有dtc的二进制文件)
解析出的dts文件内容(部分):
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "riscv-virtio";
model = "riscv-virtio,qemu";
fw-cfg@10100000 {
dma-coherent;
reg = <0x00 0x10100000 0x00 0x18>;
compatible = "qemu,fw-cfg-mmio";
};
flash@20000000 {
bank-width = <0x04>;
reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>;
compatible = "cfi-flash";
};
chosen {
rng-seed = <0xc7e0aad9 0x9eef51cb 0xbdae5ad1 0x2e4ef955 0x6fb40bee 0xb0a597a1 0x60790b61 0x5ac3b84b>;
stdout-path = "/soc/serial@10000000";
};
poweroff {
value = <0x5555>;
offset = <0x00>;
regmap = <0x04>;
compatible = "syscon-poweroff";
};
dts文件用于描述设备的属性(其中自然包括了设备的起始地址以及使用地址的大小),涉及到地址的部分使用u32进行描述。
而#address-cells和#size-cells分别表示设备的起始地址以及使用地址的大小用几个u32进行表示。
每一个{}包括的地方都是一个所谓的设备节点,其中用=来表示设备节点的各个属性以及属性值(相当于map的key和value)。
使用fdtdump工具可以看到fdt文件的内容:
fdtdump -sd qemu.dtb > qemu.txt
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x10de (4318)
// off_dt_struct: 0x38
// off_dt_strings: 0xf2c
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x1b2
// size_dt_struct: 0xef4
// 0038: tag: 0x00000001 (FDT_BEGIN_NODE)
/ {
// 0040: tag: 0x00000003 (FDT_PROP)
// 0f49: string: #address-cells
// 004c: value
#address-cells = <0x00000002>;
// 0050: tag: 0x00000003 (FDT_PROP)
// 0f3d: string: #size-cells
// 005c: value
#size-cells = <0x00000002>;
// 0060: tag: 0x00000003 (FDT_PROP)
// 0f32: string: compatible
// 006c: value
compatible = "riscv-virtio";
// 007c: tag: 0x00000003 (FDT_PROP)
// 0f2c: string: model
// 0088: value
model = "riscv-virtio,qemu";
// 009c: tag: 0x00000001 (FDT_BEGIN_NODE)
fw-cfg@10100000 {
// 00b0: tag: 0x00000003 (FDT_PROP)
// 1050: string: dma-coherent
// 00bc: value
dma-coherent;
// 00bc: tag: 0x00000003 (FDT_PROP)
// 0fb8: string: reg
// 00c8: value
reg = <0x00000000 0x10100000 0x00000000 0x00000018>;
// 00d8: tag: 0x00000003 (FDT_PROP)
// 0f32: string: compatible
// 00e4: value
compatible = "qemu,fw-cfg-mmio";
// 00f8: tag: 0x00000002 (FDT_END_NODE)
};
dtb文件的最前面是一些文件本身的信息。
在每个设备节点之前会有一个tag:FDT_BEGIN_NODE,而在设备节点之后会有一个tag:FDT_END_NODE。
而设备节点的每个属性之前,会有一个tag:FDT_PROP,之后依次是string和value(即map的key和value)。
观察string的值,可以发现,string的值在开头标明的off_dt_strings之后,这是dtc编译时,将所有的string放置在一块区域,而对应string的key只用表明其在文件的所属位置即可。
接下来在dts文件中添加自己的设备节点。
在linux源码的arch/riscv/boot/dts目录下新建一个dts_demo目录,将qemu.dts复制到此为qemu.dtsi文件(dtsi文件相当于C语言中的.h文件。)
之后,新建一个dts_demo.dts:
#include "qemu.dtsi"
/ {
dts-demo {
compatible = "dts-demo, zyz test";
status = "okay";
a-string-property = "xxxx xxxx";
sub-dts-demo {
a-string-list-property = "yyyy yyyy";
};
};
};
这里新建了一个dts-demo设备节点,设置了它的一些属性,并在其中包含另一个子设备节点,也设置了它的属性。
之后,在当前目录下设置其Makefile:
# SPDX-License-Identifier: GPL-2.0
dtb-$(CONFIG_SOC_DTS_DEMO) += dts_demo.dtb
obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .o, $(dtb-y))
在上一级目录(/arch/riscv/boot/dts)修改Makefile为:
# SPDX-License-Identifier: GPL-2.0
subdir-y += sifive
subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
subdir-y += microchip
subdir-y += dts_demo
obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
修改arch/riscv目录下的Kconfig.socs文件,增加如下内容:
config SOC_DTS_DEMO
bool "Dts Demo"
help
This demo for increase dts file in arch
之后make ARCH=riscv CROSS_COMPILE=riscv64-linux- menuconfig时,可以看到以下编译选项:
直接选定后编译。
在arch/riscv/boot/dts/dts_demo下生成了对应的dtb文件:
将dtb文件拷贝到工作区,在qemu的启动参数中增加-dtb选项dtb dts_demo.dtb。
启动虚拟机,在/proc目录下可以看到device-tree目录:
进入该目录下可以通过文件读取到设备节点的信息。
那么,如何在驱动代码中使驱动与设备对应呢?这里可以使用platform的框架。
dts_test.c:
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#define DTS_DEMO_DRIVER "dts_demo_driver"
static int dts_demo_probe(struct platform_device *pdev)
{
printk("enter %s \n", __func__);
printk("node name: %s, node full name: %s\n", pdev->dev.of_node->name, pdev->dev.of_node->full_name);
dump_stack();
return 0;
}
static int dts_demo_remove(struct platform_device *pdev)
{
printk("exit %s \n", __func__);
dump_stack();
return 0;
}
static const struct of_device_id dts_demo_of_match_table[] = {
{ .compatible = "dts-demo, zyz test", },
{ },
};
MODULE_DEVICE_TABLE(of, dts_demo_of_match_table);
static struct platform_driver dts_demo_driver = {
.probe = dts_demo_probe,
.remove = dts_demo_remove,
.driver = {
.name = DTS_DEMO_DRIVER,
.owner = THIS_MODULE,
.of_match_table = dts_demo_of_match_table,
},
};
module_platform_driver(dts_demo_driver);
MODULE_LICENSE("GPL v2");
这里主要是静态化定义了一个struct platform_driver结构体,其中关键的是of_match_table定义了匹配的字符串。
对应交叉编译的Makefile:
obj-m+=dts_test.o
CORSS_COMPILE:=riscv64-linux-
ARCH:=riscv
PWD:=$(pwd)
KDIR:=/home/zyz/Code/riscv64-linux/linux
all:
make ARCH=$(ARCH) CORSS_COMPILE=$(CORSS_COMPILE) -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
编译时出错:
不熟悉riscv的交叉编译这里的出错原因是什么,哥们儿直接放进内核里编译。
在drivers下新建dts_test目录,拷贝dts_test.c至该目录下,并增加Makefile:
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DTS_TEST) += dts_test.o
以及Kconfig:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig DTS_TEST
tristate "Test DTS Function"
depends on OF
help
This test is a LKM for test dts function
修改drivers目录中的Makefile,在最后增加一句:
obj-$(CONFIG_DTS_TEST) += dts_test/
以及在Kconfig中最后增加一句:
source "drivers/dts_test/Kconfig"
至此,重新make ARCH=riscv CROSS_COMPILE=riscv64-linux- menuconfig,选择编译选项:
这里勾选为按模块编译。
编译后,drivers/dts_test目录下生成dts_test.ko文件。
将该dts_test.ko文件拷贝到虚拟机和主机的共享区域,之后insmod该内核模块。
这里打印调用栈,是因为我在dts_demo_probe函数中增加了dump_stack函数。
标签:string,dtb,dts,test,实验,demo,DTS,qemu From: https://www.cnblogs.com/codetrap/p/17746770.html