在Linux驱动开发中,设备总线(bus)是驱动框架的核心概念之一。总线的抽象帮助内核管理各种硬件设备的发现、注册与驱动匹配。Platform总线作为嵌入式Linux常用的总线类型,承担着管理SoC(系统芯片)上各种非即插即用设备的重要职责。
本文将从理论到实践,系统详解Linux Platform总线的设计理念、核心机制以及驱动开发流程。希望对嵌入式开发工程师和Linux驱动学习者有实质帮助。
Linux设备模型(device model)通过总线(bus)、设备(device)、驱动(driver)三大核心结构体,构建了统一的设备管理框架。
设备模型的核心职责包括:
这些模型的实现使内核能够对不同类型设备统一管理,方便驱动复用和系统扩展。
Platform总线是Linux内核中专门针对SoC平台上内嵌设备的总线类型,主要负责管理与CPU紧密耦合的设备,如片上外设、片上存储控制器、GPIO、UART等。
总线类型在Linux内核中由 struct bus_type
表示,Platform总线对应的是 platform_bus_type
。
struct platform_device
表示一个platform设备,主要包含:
struct platform_driver
表示一个platform设备驱动,包含:
struct device_driver
平台设备通常通过以下方式注册:
platform_device_register()
驱动通过调用 platform_driver_register()
注册到内核platform总线上。
内核会遍历所有platform设备和platform驱动进行匹配,匹配规则为:
name
)设备树(Device Tree,简称DT)是描述硬件的设备信息数据结构。在嵌入式Linux中,平台设备几乎都是通过设备树描述硬件资源的。
设备树中的每个设备节点会被转换成一个 platform_device
结构体。
platform驱动中常用 of_match_table
定义设备树匹配表:
static const struct of_device_id my_device_of_match[] = {
{ .compatible = "vendor,mydevice" },
{ }
};
MODULE_DEVICE_TABLE(of, my_device_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "mydevice",
.of_match_table = my_device_of_match,
},
};
设备树compatible属性和驱动的of_match_table匹配成功即绑定驱动。
Platform总线在内核中由 drivers/base/platform.c
中的 platform_bus_type
定义:
struct bus_type platform_bus_type = {
.name = "platform",
.match = platform_match,
.probe = platform_probe,
.remove = platform_remove,
};
平台设备注册主要通过 platform_device_register() 实现:
int platform_device_register(struct platform_device *pdev)
{
return device_register(&pdev->dev);
}
该函数会将设备添加到设备模型中,进而触发驱动匹配流程。
平台驱动注册使用 platform_driver_register():
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
return driver_register(&drv->driver);
}
设置驱动所属总线为 platform,并注册到内核驱动框架。
匹配函数为 platform_match():
static int platform_match(struct device *dev, struct device_driver *drv)
{
// 1. 设备树 compatible 匹配
// 2. name 匹配
// 3. id 匹配(可选)
}
匹配优先顺序为:
1、设备树 of_match_table 匹配;
2、name 匹配(pdev->name 与 drv->driver.name);
3、如果设置了 id_table,也可按 ID 进一步精细匹配。
以一个简单的 GPIO 灯设备为例,演示 platform 驱动编写。
led_gpio: led@0 {
compatible = "mycompany,my-led";
gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
status = "okay";
};
#include
#include
#include
#include
static int my_led_probe(struct platform_device *pdev)
{
struct gpio_desc *led_gpio;
led_gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(led_gpio)) {
dev_err(&pdev->dev, "Failed to get GPIO\n");
return PTR_ERR(led_gpio);
}
gpiod_set_value(led_gpio, 1); // 点亮 LED
dev_info(&pdev->dev, "LED turned on\n");
return 0;
}
static int my_led_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "LED driver removed\n");
return 0;
}
static const struct of_device_id my_led_of_match[] = {
{ .compatible = "mycompany,my-led" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_led_of_match);
static struct platform_driver my_led_driver = {
.driver = {
.name = "my_led",
.of_match_table = my_led_of_match,
},
.probe = my_led_probe,
.remove = my_led_remove,
};
module_platform_driver(my_led_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Platform LED Driver");
检查设备树中 compatible 与驱动匹配表是否一致。
查看驱动是否注册成功(dmesg)。
使用 ls /sys/bus/platform/devices/ 查看设备是否存在。
确认模块已成功加载。
检查 devm_ 函数是否成功。
确保设备树中资源定义正确。
查看 dmesg 是否有错误输出。
Platform 总线作为 Linux 内核中 SoC 设备的重要总线机制,是驱动开发的基础设施之一。理解它的结构、匹配流程与设备树关系,对于嵌入式平台驱动编写至关重要。
platform_device 与 platform_driver 分别描述设备与驱动;
匹配依赖于 name 或设备树中的 compatible;
设备树是 platform 总线最常用的设备定义方式;
使用 devm_ 系列函数有助于提高资源管理效率。
Linux 内核源码 drivers/base/platform.c
《Linux设备驱动开发详解》
Linux 官方文档 Documentation/driver-api/platform
《深入理解Linux内核》