Linux 设备树详解:从概念到实战

关键词:设备树(Device Tree)、DTS、DTC、DTB、嵌入式Linux驱动开发

为什么需要设备树?

在旧版Linux内核中,硬件信息(如内存映射、外设地址、中断号等)直接硬编码在内核源码中。这导致:

  • 内核臃肿,需为不同硬件编译不同版本

  • 硬件变动需重新编译内核

  • 代码冗余严重(一个board-*.c文件对应一块开发板)

设备树(Device Tree)的引入彻底解决了这一问题!它通过描述硬件拓扑结构的文本文件(.dts),将硬件配置与内核代码分离。

设备树是什么?

1. 核心概念

  • 硬件描述文件:用树形结构描述CPU、内存、总线、外设等硬件信息

  • 不依赖编程语言:采用人类可读的文本格式(类似JSON)

  • 独立于操作系统:由Bootloader(如U-Boot)传递给内核

2. 设备树文件类型

扩展名

类型

说明

.dts

设备树源文件

人类可读的文本描述

.dtsi

设备树头文件

可被多个.dts包含的通用配置

.dtb

设备树二进制文件

.dts编译生成,供内核使用

设备树语法详解

1. 基础结构

// 示例:描述一个UART控制器
/ {
    model = "MyBoard";
    compatible = "myboard,v1";

    uart0: serial@10000000 {
        compatible = "ns16550a";
        reg = <0x10000000 0x1000>;  // 起始地址0x10000000, 长度4KB
        interrupts = <10>;          // 中断号10
        clock-frequency = <1843200>;// 时钟频率
    };
};

2. 关键语法元素

语法

作用

示例

/ {...}

根节点

/ { model="MyBoard"; }

node

设备节点

uart0: serial@10000000

property

节点属性(键值对)

compatible = "ns16550a"

phandle

节点引用标识符

uart0: ...(标签)

&label

引用其他节点

&uart0 { status="okay" }

3. 常用属性解析

  • compatible:驱动匹配的关键字 "厂商,设备型号"(如"ti,omap3-uart"

  • reg:设备寄存器地址和长度 <起始地址 长度>

  • interrupts:设备使用的中断号

  • status:设备状态("okay", "disabled"


设备树如何工作?

编译与使用流程

编译:
传递: U-Boot通过bootm <kernel_addr> - <dtb_addr>命令传递dtb给内核
内核解析: 内核启动时解析dtb,根据compatible匹配驱动

实战:添加一个LED设备

步骤1:修改设备树文件(.dts)

/ {
    leds {
        compatible = "gpio-leds";
        led0 {
            label = "sys_led";
            gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; // 使用GPIO0_15
            linux,default-trigger = "heartbeat";
        };
    };
};

步骤2:驱动中获取设备树信息

#include <linux/of.h>

static int led_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    int led_gpio;

    // 从设备树读取gpio号
    led_gpio = of_get_named_gpio(np, "gpios", 0);
    gpio_request(led_gpio, "sys_led");
    ...
}


static const struct of_device_id leds_ids[ ] = {

    { .compatible = "gpio-leds" }, // 匹配设备树中的compatible
    { }
};
MODULE_DEVICE_TABLE(of, leds_ids);

调试技巧

1. 查看解析后的设备树

# 内核启动后查看设备树
ls /proc/device-tree/

# 详细查看节点属性
cat /proc/device-tree/soc/i2c@4000000/clock-frequency

2. 反编译dtb(调试必备)

# 将dtb转回dts(方便检查)
dtc -I dtb -O dts -o recovered.dts myboard.dtb

常见问题解答(FAQ)

Q1:设备树修改后驱动不生效?

  • 确认.dtb文件已更新到开发板

  • 检查compatible字符串是否与驱动一致

  • 使用of_dump_status检查节点状态

Q2:如何覆盖设备树中的配置?

在板级.dts文件中重写节点:

// 原始配置在common.dtsi中
#include "common.dtsi"

&uart0 {
    status = "disabled"; // 关闭该UART
};

Q3:设备树和ACPI有何区别?

设备树 (DT)

ACPI

主要用于ARM嵌入式系统

主要用于x86桌面/服务器

静态描述硬件

支持动态硬件配置

由Bootloader传递给内核

由BIOS/UEFI提供


总结

  • ✅ 设备树 = 硬件的“身份证”:统一描述硬件配置

  • ✅ 解耦硬件与驱动:无需为每块板子重编内核

  • ✅ 开发重点:编写正确的.dts + 驱动中正确解析属性

学习建议:从修改现有开发板的设备树入手,添加简单设备(如LED、按键),逐步深入复杂外设(I2C、SPI设备)。

内容概要:本文深入探讨了Linux块设备驱动的核心概念、框架结构及其开发实践。文章首先介绍了块设备驱动在Linux系统中的重要性,它作为操作系统与硬件设备间的桥梁,负责管理块设备的读写操作和缓存管理。接着对比了块设备驱动与字符设备驱动的区别,强调了前者在高效随机访问和大量数据处理方面的优势。随后,文章详细解析了块设备驱动的工作流程,从用户请求到硬件响应的全过程,涉及虚拟文件系统、映射层、通用块层、I/O调度层等关键环节。文中还剖析了核心数据结构如gendisk、block_device_operations、request_queue和request,并讲解了API的使用,包括注册与注销、磁盘设备操作等。最后,通过一个简单的块设备驱动实例,展示了从代码编写到测试验证的完整过程。; 适合人群:对Linux内核开发、设备驱动编程感兴趣的开发者,尤其是有一定C语言编程基础和技术背景的研发人员。; 使用场景及目标:①理解块设备驱动在Linux系统存储体系中的角色和工作原理;②掌握块设备驱动的核心数据结构和API的使用方法;③通过实战演练,能够编写、编译、加载和测试简单的块设备驱动程序。; 其他说明:随着存储技术和新兴技术的发展,块设备驱动面临新的挑战和机遇。开发者应关注新技术趋势,如SSD特性的优化、云计算和大数据环境下的高性能需求等,以不断提升块设备驱动的性能和可靠性。文章鼓励读者通过实际项目和实验积累经验,提升技术水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hhhhhello啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值