一、平台总线的核心定位与架构
1.1 为什么需要平台总线?
Linux 内核中的总线(如 PCIe、USB)主要用于 “可热插拔的外部设备”,而片上外设(如 SOC 内部的 GPIO、定时器)通常与硬件紧密绑定,无法热插拔。平台总线正是为解决这类 “非热插拔片上设备” 的驱动适配问题而生,其核心价值:
- 解耦设备与驱动:设备信息(如寄存器地址、中断号)单独描述,驱动逻辑通用化,同一驱动可适配不同硬件(只需修改设备信息);
- 统一管理资源:内核通过平台总线统一分配 / 释放设备资源(如 IO 地址、中断),避免资源冲突;
- 简化驱动开发:开发者无需关注总线底层细节,只需按规范实现设备和驱动,内核自动完成匹配。

1.2 平台总线的三层架构
平台总线采用 “总线(Bus)→ 设备(Device)→ 驱动(Driver) ” 三层结构,三者关系如下:
- 总线(Platform Bus):内核预定义的虚拟总线(无物理硬件),负责管理所有平台设备和驱动,提供 “设备 - 驱动匹配” 的核心逻辑;
- 设备(Platform Device):描述硬件设备的信息(如寄存器基地址、中断号、GPIO 引脚),通常由设备树(Device Tree)或板级文件(Board File)定义;
- 驱动(Platform Driver):实现设备的控制逻辑(如初始化硬件、数据读写),驱动代码与硬件信息分离,可复用。
核心流程:平台总线启动后,会遍历所有已注册的 platform_device 和 platform_driver,当二者匹配成功时,自动调用驱动的初始化函数(probe),完成硬件与驱动的绑定。
二、核心组件与数据结构
平台总线的实现依赖两个核心结构体:platform_device(设备)和 platform_driver(驱动),以及配套的注册 / 注销函数。
2.1 平台设备(Platform Device)
用于描述硬件设备的 “资源信息”,核心是 “告诉内核设备在哪里、用哪些资源”。
2.1.1 核心结构体:struct platform_device
#include <linux/platform_device.h>
struct platform_device {
const char *name; // 设备名(用于与驱动匹配,需与驱动的name一致)
int id; // 设备编号(多个同类型设备时区分,通常设为 -1 表示单设备)
struct device dev; // 内嵌的通用设备结构体(内核设备模型的基础)
u32 num_resources; // 设备资源数量(如寄存器、中断、GPIO 等)
struct resource *resource; // 资源数组(存储设备的具体资源信息)
const struct platform_device_id *id_entry; // 设备 ID 表(用于多设备匹配)
};
2.1.2 设备资源:struct resource
用于定义设备的硬件资源(如 IO 地址、中断、内存),常见类型如下:
struct resource {
resource_size_t start; // 资源起始地址(如寄存器基地址、中断号)
resource_size_t end; // 资源结束地址(如寄存器末地址,中断号与start相同)
const char *name; // 资源名称(可选,用于调试)
unsigned long flags; // 资源类型(核心字段,如 IORESOURCE_MEM 表示内存资源)
struct resource *parent, *sibling, *child; // 资源链表(内核内部管理)
};
常见资源类型(flags 取值):
IORESOURCE_MEM:内存映射的寄存器资源(如 SOC 内部的 GPIO 控制器寄存器);IORESOURCE_IRQ:中断资源(如设备的中断号);IORESOURCE_IO:IO 端口资源(x86 架构常用,ARM 架构较少用);IORESOURCE_GPIO:GPIO 资源(部分内核版本支持,或通过devm_gpio_request单独申请)。
2.1.3 设备注册与注销函数
-
注册设备:
int platform_device_register(struct platform_device *pdev)功能:将平台设备注册到平台总线上,内核会触发总线的匹配逻辑。返回值:0 表示成功,非 0 表示失败(如设备名重复、资源冲突)。 -
注销设备:
void platform_device_unregister(struct platform_device *pdev)功能:从平台总线上注销设备,释放资源,并解绑对应的驱动。
2.2 平台驱动(Platform Driver)
用于实现设备的控制逻辑(如初始化硬件、处理中断、数据收发),核心是 “如何操作设备”,与具体硬件资源解耦。
2.2.1 核心结构体:struct platform_driver
struct platform_driver {
int (*probe)(struct platform_device *pdev); // 设备-驱动匹配成功后调用(初始化硬件)
int (*remove)(struct platform_device *pdev); // 设备注销或驱动卸载时调用(释放资源)
void (*shutdown)(struct platform_device *pdev); // 设备关机时调用(可选)
int (*suspend)(struct platform_device *pdev, pm_message_t state); // 设备休眠时调用(可选)
int (*resume)(struct platform_device *pdev); // 设备唤醒时调用(可选)
struct device_driver driver; // 内嵌的通用驱动结构体(核心字段:.name 用于匹配设备)
const struct platform_device_id *id_table; // 驱动支持的设备 ID 表(多设备匹配)
bool prevent_deferred_probe; // 禁止延迟探测(可选,默认 false)
};
关键函数说明:
probe:核心初始化函数,匹配成功后自动执行,通常完成:资源申请(如 IO 地址映射、中断注册)、硬件初始化(如配置寄存器)、创建设备节点等;remove:资源释放函数,通常完成:中断注销、IO 地址解映射、GPIO 释放等,避免资源泄漏。
2.2.2 驱动注册与注销函数
-
注册驱动:
int platform_driver_register(struct platform_driver *pdrv)功能:将平台驱动注册到平台总线上,内核会遍历已注册的平台设备,触发匹配逻辑。返回值:0 表示成功,非 0 表示失败(如驱动名无效)。 -
注销驱动:
void platform_driver_unregister(struct platform_driver *pdrv)功能:从平台总线上注销驱动,自动调用已匹配设备的remove函数,释放资源。
三、设备与驱动的匹配机制
平台总线的核心能力是 “自动匹配设备与驱动”,匹配逻辑由总线实现,开发者只需保证 “设备与驱动的标识一致”。常见匹配方式有两种:
3.1 名称匹配(最常用)
- 原理:通过
platform_device.name与platform_driver.driver.name进行字符串比较,若完全一致,则匹配成功; - 适用场景:单类型设备(如一个驱动仅适配一种设备);
- 示例:设备侧:
pdev->name = "my_gpio_led";驱动侧:pdrv->driver.name = "my_gpio_led";此时二者名称一致,总线会触发驱动的probe函数。
3.2 ID 表匹配(多设备适配)
- 原理:当一个驱动需要适配多种设备时,通过
platform_device_id表定义支持的设备列表,总线遍历 ID 表,若设备名在表中存在,则匹配成功; - 适用场景:多类型设备(如一个驱动适配不同型号的同功能芯片);
- 实现步骤:
- 定义设备 ID 表:
// 驱动侧:定义支持的设备 ID 表 static const struct platform_device_id led_driver_id_table[] = { {"my_gpio_led_v1", 0}, // 设备1:名称+私有数据(私有数据可选) {"my_gpio_led_v2", 1}, // 设备2:名称+私有数据 { }, // 表的结束标志(必须添加) }; MODULE_DEVICE_TABLE(platform, led_driver_id_table); // 告知内核该 ID 表 - 驱动绑定 ID 表:
pdrv->id_table = led_driver_id_table; // 驱动关联 ID 表 - 设备侧只需设置
pdev->name为 ID 表中的任一名称(如 "my_gpio_led_v2"),即可匹配成功。
四、实战代码示例(GPIO 控制 LED)
以 “平台总线驱动控制 GPIO LED” 为例,完整展示设备定义、驱动实现、资源操作的流程(基于设备树传递资源,现代 Linux 开发常用)。
4.1 设备侧:设备树描述平台设备(替代传统板级文件)
现代 Linux 中,平台设备的资源通常通过设备树(.dts)定义,无需手动编写 platform_device 代码,内核会自动解析设备树生成平台设备。
设备树节点定义(添加到 SOC 对应的设备树文件中)
// 平台设备节点:描述 GPIO LED 的资源(GPIO 引脚、设备名)
my_gpio_led {
compatible = "my,gpio-led"; // 兼容属性(用于驱动匹配,替代 name 匹配)
led-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; // GPIO 资源:gpio1 组的第 2 引脚,高电平点亮
status = "okay"; // 设备使能("disabled" 表示禁用)
};
- 说明:
compatible属性用于驱动匹配(替代传统的名称匹配),驱动侧需通过of_match_table声明支持的compatible值。
4.2 驱动侧:平台驱动实现(控制 LED)
驱动通过平台总线匹配设备树节点,申请 GPIO 资源,实现 LED 的点亮 / 熄灭控制。
完整驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
// 驱动私有数据:存储设备相关资源(如 GPIO 编号)
struct led_driver_data {
int led_gpio; // LED 对应的 GPIO 编号
};
// 1. probe 函数:匹配成功后初始化硬件
static int led_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct led_driver_data *drv_data;
int ret;
// (1)分配私有数据内存(devm_ 前缀:内核自动管理内存,设备注销时自动释放)
drv_data = devm_kzalloc(dev, sizeof(struct led_driver_data), GFP_KERNEL);
if (!drv_data) {
return -ENOMEM; // 内存分配失败
}
// (2)从设备树获取 GPIO 资源(解析 led-gpios 属性)
drv_data->led_gpio = of_get_named_gpio_flags(dev->of_node, "led-gpios", 0, NULL);
if (drv_data->led_gpio < 0) {
dev_err(dev, "failed to get led gpio\n");
return drv_data->led_gpio; // 获取 GPIO 失败
}
// (3)申请 GPIO 资源(避免资源冲突)
ret = devm_gpio_request(dev, drv_data->led_gpio, "led_gpio");
if (ret < 0) {
dev_err(dev, "failed to request led gpio\n");
return ret;
}
// (4)配置 GPIO 为输出模式,并初始化熄灭 LED
gpio_direction_output(drv_data->led_gpio, 0);
// 点亮 LED(测试)
gpio_set_value(drv_data->led_gpio, 1);
dev_info(dev, "led probe success: gpio %d is on\n", drv_data->led_gpio);
// (5)将私有数据绑定到设备结构体,供后续函数(如 remove)使用
platform_set_drvdata(pdev, drv_data);
return 0;
}
// 2. remove 函数:设备注销时释放资源
static int led_platform_remove(struct platform_device *pdev)
{
struct led_driver_data *drv_data = platform_get_drvdata(pdev);
// 熄灭 LED
gpio_set_value(drv_data->led_gpio, 0);
dev_info(&pdev->dev, "led remove success: gpio %d is off\n", drv_data->led_gpio);
// 无需手动释放 devm_ 申请的资源(GPIO、内存),内核自动处理
return 0;
}
// 3. 设备树匹配表:驱动支持的 compatible 属性列表
static const struct of_device_id led_of_match_table[] = {
{ .compatible = "my,gpio-led" }, // 与设备树节点的 compatible 一致
{ /* Sentinel */ } // 表的结束标志
};
MODULE_DEVICE_TABLE(of, led_of_match_table); // 告知内核该匹配表
// 4. 定义平台驱动结构体
static struct platform_driver led_platform_driver = {
.probe = led_platform_probe, // 匹配成功后执行
.remove = led_platform_remove, // 设备注销时执行
.driver = {
.name = "my_gpio_led_driver", // 驱动名(可选,用于名称匹配)
.of_match_table = led_of_match_table, // 设备树匹配表(优先于名称匹配)
.owner = THIS_MODULE,
},
};
// 5. 驱动初始化与注销(模块入口/出口)
static int __init led_driver_init(void)
{
// 注册平台驱动
return platform_driver_register(&led_platform_driver);
}
static void __exit led_driver_exit(void)
{
// 注销平台驱动
platform_driver_unregister(&led_platform_driver);
}
module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Platform Bus Driver for GPIO LED");
MODULE_AUTHOR("Your Name");
4.3 代码说明
- 私有数据(
led_driver_data):存储设备的 GPIO 编号,避免全局变量,支持多设备适配; - 设备树解析:通过
of_get_named_gpio_flags从设备树获取 GPIO 资源,实现 “硬件信息与驱动解耦”; - 资源管理:使用
devm_前缀的函数(devm_kzalloc、devm_gpio_request)申请资源,内核自动释放,避免资源泄漏; - 匹配优先级:设备树匹配(
of_match_table)优先级高于名称匹配,是现代 Linux 驱动的标准方式。

3413

被折叠的 条评论
为什么被折叠?



