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回调的标准流程

  1. 获取设备树资源:调用platform_get_resource()、platform_get_irq()等API从platform_device中提取IO内存基址、中断号等。
  2. 解析设备树属性:使用of_property_read_*系列函数读取自定义属性(时钟频率、GPIO编号、DMA通道等)。
  3. 硬件初始化:映射IO内存(devm_ioremap_resource)、注册中断(devm_request_irq)、初始化硬件寄存器。
  4. 注册子系统:将设备注册到对应的内核框架中(如I2C、SPI、ALSA、Input、RTC等)。
  5. 初始化完成:返回0表示成功,返回负错误码表示失败。

remove回调的标准流程

  1. 注销子系统:从内核框架中移除设备(如i2c_del_adapter、misc_deregister)。
  2. 释放中断:通常由devm_系列API自动管理,无需手动释放。
  3. 取消IO内存映射:同样由devm_机制自动处理。
  4. 清理私有数据:释放驱动动态分配的内存、关闭时钟、撤销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为例)

  1. 模块加载:当insmod或内核自动加载驱动模块时,module_init宏指定的入口函数被调用。
  2. 调用platform_driver_register:将platform_driver挂接到Platform总线驱动链表上。
  3. 总线匹配:内核遍历已注册的platform_device,对每个设备调用platform_match函数进行匹配。
  4. 匹配成功:调用platform_driver的probe回调,驱动开始初始化设备。
  5. 匹配失败:设备保持未绑定状态,等待后续驱动加载或设备热插拔事件。

注销流程

  1. 模块卸载:当rmmod或系统关闭时,module_exit宏指定的出口函数被调用。
  2. 调用platform_driver_unregister:从总线驱动链表中移除该驱动。
  3. 遍历已绑定设备:对每个与该驱动绑定的设备,调用驱动的remove回调。
  4. 设备解绑:设备状态变为“未绑定”,但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和注销的完整生命周期。

Platform驱动生命周期与匹配流程 驱动模块加载 module_init → platform_driver_register Platform总线匹配 platform_match() 设备树匹配 of_match_table → compatible 匹配? 设备保持未绑定状态 等待后续驱动或热插拔 probe() 回调执行 获取资源 → 解析属性 → 硬件初始化 注册子系统 → 返回0 设备正常运行 驱动提供文件操作/中断处理/数据收发 remove触发条件 rmmod / 设备移除 / 系统关机 remove() 回调执行 注销子系统 → 释放资源 取消IO映射 → 清理私有数据 platform_driver_unregister() 匹配方式优先级 1. of_match_table (设备树) 2. id_table (传统ID表) 3. driver.name (名称匹配)

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中提取资源,无需关心设备的具体物理位置。