快速链接:
.
👉👉👉 Linux内核驱动面试-百问百答-[目录] 👈👈👈
- 付费专栏-付费课程 【购买须知】
- 个人博客笔记导读目录(全部)
在Linux内核中,设备树(Device Tree)是一种描述硬件的方式,尤其在嵌入式系统和ARM架构中被广泛使用。设备树使用一种名为设备树源(Device Tree Source, DTS)的语法来描述系统的硬件布局,包括设备和它们的属性。内核使用设备树来了解系统中存在的硬件,并相应地加载和配置驱动程序。以下是设备树中设备与驱动匹配的机制及其详细过程:
1. 设备树的基本结构
设备树是一个树状结构,根节点是系统的根,每个节点表示一个设备或总线。每个节点包含一组属性,描述设备的特性和配置。
一个简单的设备树节点示例如下:
uart0: serial@101f1000 {
compatible = "ns16550a";
reg = <0x101f1000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <5>;
};
2. 设备树编译
设备树源文件(.dts)需要编译成二进制设备树(Device Tree Blob, DTB)文件。编译过程使用设备树编译器(DTC)。
dtc -I dts -O dtb -o device_tree.dtb device_tree.dts
编译后的DTB文件会被加载到内核启动过程中,以便内核读取和解析。
3. 内核解析设备树
内核在启动时会加载DTB文件,并根据设备树中的信息初始化硬件。内核会将设备树的每个节点与相应的驱动进行匹配。
4. 匹配机制
设备树节点与驱动匹配主要通过compatible
属性进行。驱动程序通过指定支持的compatible
字符串,告诉内核哪些设备节点与其匹配。
设备树节点中的compatible
属性
每个设备节点都有一个compatible
属性,用于描述该设备的类型及其兼容的驱动。
uart0: serial@101f1000 {
compatible = "ns16550a";
...
};
驱动程序中的of_device_id
结构
驱动程序中使用of_device_id
结构来定义匹配的设备树节点。
static const struct of_device_id uart_of_match[] = {
{ .compatible = "ns16550a" },
{},
};
MODULE_DEVICE_TABLE(of, uart_of_match);
5. 注册和匹配驱动
驱动程序会在初始化时注册一个平台驱动(platform driver)或总线驱动,并将of_device_id
匹配表传递给内核。
驱动初始化和注册
static int uart_probe(struct platform_device *pdev) {
// 驱动初始化代码
return 0;
}
static int uart_remove(struct platform_device *pdev) {
// 驱动卸载代码
return 0;
}
static struct platform_driver uart_driver = {
.driver = {
.name = "uart_driver",
.of_match_table = uart_of_match,
},
.probe = uart_probe,
.remove = uart_remove,
};
module_platform_driver(uart_driver);
6. 设备绑定和驱动加载
当内核解析设备树时,会遍历每个节点,检查compatible
属性,并与所有已注册驱动的of_match_table
进行匹配。如果匹配成功,内核会调用驱动的probe
函数,绑定设备和驱动,并进行设备初始化。
设备绑定过程
- 内核启动时加载DTB:内核加载设备树二进制文件(DTB),并开始解析。
- 遍历设备树节点:内核遍历设备树的每个节点,读取每个节点的
compatible
属性。 - 匹配驱动:内核将节点的
compatible
属性与所有已注册驱动的of_match_table
进行匹配。 - 调用
probe
函数:如果找到匹配的驱动,内核会调用该驱动的probe
函数,将设备信息传递给驱动进行初始化。 - 设备初始化:驱动在
probe
函数中初始化设备,设置设备寄存器,分配资源,创建设备节点等。
示例:UART设备驱动匹配
设备树节点
uart0: serial@101f1000 {
compatible = "ns16550a";
reg = <0x101f1000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <5>;
};
驱动代码
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
static const struct of_device_id uart_of_match[] = {
{ .compatible = "ns16550a" },
{},
};
MODULE_DEVICE_TABLE(of, uart_of_match);
static int uart_probe(struct platform_device *pdev) {
// 驱动初始化代码
printk(KERN_INFO "UART device matched and probed\n");
return 0;
}
static int uart_remove(struct platform_device *pdev) {
// 驱动卸载代码
printk(KERN_INFO "UART device removed\n");
return 0;
}
static struct platform_driver uart_driver = {
.driver = {
.name = "uart_driver",
.of_match_table = uart_of_match,
},
.probe = uart_probe,
.remove = uart_remove,
};
module_platform_driver(uart_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple UART Driver");
MODULE_VERSION("1.0");
通过以上步骤,设备树中的设备节点可以与相应的驱动程序匹配,并在内核启动过程中自动加载和初始化。设备树和驱动匹配机制使得硬件描述与驱动代码分离,简化了硬件平台的适配和管理。