4. Platform驱动框架:核心数据结构与生命周期
Platform驱动框架是Linux内核中用于管理“非可枚举、非热插拔”设备(通常集成在SoC内部或板级总线上)的标准模型。其核心围绕两个数据结构——platform_driver和platform_device——以及它们之间的匹配、绑定与解绑流程展开。理解这套框架是进行设备树驱动移植的基础。
4.1 platform_driver结构体
platform_driver是驱动侧的抽象,它封装了驱动与内核交互的所有回调函数和标识信息。定义在linux/platform_device.h中。
| 成员 | 类型 | 说明 |
|---|---|---|
probe |
int (*)(struct platform_device *) |
设备匹配成功时调用,驱动在此完成硬件初始化、资源获取、注册子系统 |
remove |
int (*)(struct platform_device *) |
设备移除或驱动卸载时调用,执行资源释放、注销子系统等清理工作 |
shutdown |
void (*)(struct platform_device *) |
系统关机或重启时调用,用于优雅关闭硬件 |
suspend |
int (*)(struct platform_device *, pm_message_t) |
系统进入低功耗模式时调用,保存硬件状态 |
resume |
int (*)(struct platform_device *) |
系统从低功耗模式唤醒时调用,恢复硬件状态 |
driver |
struct device_driver |
内嵌的通用驱动结构,包含name、of_match_table等关键字段 |
id_table |
const struct platform_device_id * |
传统非设备树匹配时的ID表,设备树环境下通常设为NULL |
关键点:在设备树环境下,
driver.of_match_table必须指向一个of_device_id数组,内核通过该表将设备树节点的compatible属性与驱动进行匹配。
/* 典型的 platform_driver 定义示例 */
static const struct of_device_id my_i2c_of_match[] = {
{ .compatible = "vendor,my-i2c-controller", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_i2c_of_match);
static struct platform_driver my_i2c_driver = {
.probe = my_i2c_probe,
.remove = my_i2c_remove,
.driver = {
.name = "my_i2c",
.of_match_table = my_i2c_of_match,
},
};
4.2 platform_device结构体
platform_device是设备侧的抽象,代表一个挂接在Platform总线上的物理或虚拟设备。在设备树系统中,内核在启动阶段解析DTB,为每个匹配的节点动态创建platform_device实例。
| 成员 | 类型 | 说明 |
|---|---|---|
name |
const char * |
设备名称,用于与platform_driver.driver.name匹配(非设备树路径) |
id |
int |
设备实例编号,-1表示只有一个实例 |
dev |
struct device |
内嵌的通用设备结构,包含of_node指针(指向设备树节点) |
num_resources |
u32 |
资源数组的元素个数 |
resource |
struct resource * |
指向资源数组,描述IO内存、中断、DMA等硬件资源 |
设备树视角:当内核调用
of_platform_populate()遍历设备树时,每个带有compatible属性的节点都会生成一个platform_device。该设备的dev.of_node指向对应的设备树节点,resource数组则由内核根据reg、interrupts等属性自动填充。
4.3 probe与remove回调函数
probe和remove是驱动生命周期中最重要的两个回调,分别对应设备的“发现与初始化”和“移除与清理”。
probe回调的标准流程
- 获取设备树资源:调用
platform_get_resource()、platform_get_irq()等API从platform_device中提取IO内存基址、中断号等。 - 解析设备树属性:使用
of_property_read_*系列函数读取自定义属性(时钟频率、GPIO编号、DMA通道等)。 - 硬件初始化:映射IO内存(
devm_ioremap_resource)、注册中断(devm_request_irq)、初始化硬件寄存器。 - 注册子系统:将设备注册到对应的内核框架中(如I2C、SPI、ALSA、Input、RTC等)。
- 初始化完成:返回0表示成功,返回负错误码表示失败。
remove回调的标准流程
- 注销子系统:从内核框架中移除设备(如
i2c_del_adapter、misc_deregister)。 - 释放中断:通常由
devm_系列API自动管理,无需手动释放。 - 取消IO内存映射:同样由
devm_机制自动处理。 - 清理私有数据:释放驱动动态分配的内存、关闭时钟、撤销GPIO等。
/* probe 与 remove 典型实现骨架 */
static int my_i2c_probe(struct platform_device *pdev)
{
struct resource *res;
int irq, ret;
/* 1. 获取IO内存资源 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
/* 2. 获取中断资源 */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
/* 3. 映射IO内存 */
void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
/* 4. 注册I2C适配器 */
ret = my_i2c_adapter_register(pdev, base, irq);
if (ret)
return ret;
dev_info(&pdev->dev, "probe success\n");
return 0;
}
static int my_i2c_remove(struct platform_device *pdev)
{
/* 注销I2C适配器 */
my_i2c_adapter_unregister(pdev);
dev_info(&pdev->dev, "remove done\n");
return 0;
}
常见错误:在probe中直接使用
ioremap而非devm_ioremap_resource,会导致资源泄漏;忘记检查platform_get_resource和platform_get_irq的返回值,在资源缺失时引发空指针异常。
4.4 驱动注册与注销流程
驱动通过module_platform_driver()宏或手动调用platform_driver_register()/platform_driver_unregister()完成在内核中的注册与注销。
注册流程(以module_platform_driver为例)
- 模块加载:当
insmod或内核自动加载驱动模块时,module_init宏指定的入口函数被调用。 - 调用platform_driver_register:将
platform_driver挂接到Platform总线驱动链表上。 - 总线匹配:内核遍历已注册的
platform_device,对每个设备调用platform_match函数进行匹配。 - 匹配成功:调用
platform_driver的probe回调,驱动开始初始化设备。 - 匹配失败:设备保持未绑定状态,等待后续驱动加载或设备热插拔事件。
注销流程
- 模块卸载:当
rmmod或系统关闭时,module_exit宏指定的出口函数被调用。 - 调用platform_driver_unregister:从总线驱动链表中移除该驱动。
- 遍历已绑定设备:对每个与该驱动绑定的设备,调用驱动的
remove回调。 - 设备解绑:设备状态变为“未绑定”,但
platform_device实例仍然存在。
/* 使用 module_platform_driver 宏简化注册注销 */
module_platform_driver(my_i2c_driver);
/* 等价于手动展开的代码:
static int __init my_i2c_init(void)
{
return platform_driver_register(&my_i2c_driver);
}
module_init(my_i2c_init);
static void __exit my_i2c_exit(void)
{
platform_driver_unregister(&my_i2c_driver);
}
module_exit(my_i2c_exit);
*/
4.5 Platform驱动匹配与生命周期SVG图
下图展示了从驱动注册到设备绑定、probe执行、再到remove和注销的完整生命周期。
4.6 关键数据结构关系总结
| 概念 | 数据结构 | 创建时机 | 销毁时机 | 设备树关联 |
|---|---|---|---|---|
| 驱动 | platform_driver |
模块加载时静态定义或动态注册 | 模块卸载时注销 | 通过of_match_table匹配节点 |
| 设备 | platform_device |
设备树解析时由of_platform_populate创建 |
设备树节点移除或系统关机时销毁 | dev.of_node指向对应DT节点 |
| 资源 | struct resource |
设备创建时由内核从reg/interrupts属性生成 |
随platform_device销毁 |
自动解析reg、interrupts、dmas等 |
| 私有数据 | 驱动自定义结构体 | probe中通过devm_kzalloc分配 |
remove中释放或由devm自动管理 |
通过device_get_match_data获取平台数据 |
核心要点:Platform驱动框架的本质是“驱动-设备-总线”三者的协作。驱动提供操作函数,设备提供硬件描述,总线负责匹配和生命周期管理。在设备树环境下,设备描述从板级代码转移到DTB文件,驱动只需关注如何从
platform_device中提取资源,无需关心设备的具体物理位置。