3. SPI控制器驱动注册与注销:核心接口与回调机制

在Linux内核中,SPI控制器驱动遵循标准的平台驱动模型。驱动通过 platform_driver 结构体与内核框架绑定,利用 probe 和 remove 回调函数完成控制器的初始化和资源释放。本章将深入剖析驱动注册/注销的完整流程,并详细讲解 spi_alloc_master 与 spi_register_master 两个核心接口的使用方法。

3.1 platform_driver 结构体:驱动与设备的桥梁

platform_driver 是 SPI 控制器驱动注册的入口。它定义了驱动与内核平台层交互的接口,包括探测、移除、电源管理等回调函数。

成员 类型 说明
probe int (*probe)(struct platform_device *) 设备匹配成功后调用,完成控制器硬件初始化、master 分配与注册
remove int (*remove)(struct platform_device *) 设备移除时调用,执行 master 注销、资源释放、硬件去初始化
shutdown void (*shutdown)(struct platform_device *) 系统关机或重启时调用,用于优雅关闭 SPI 控制器
driver struct device_driver 内嵌的通用驱动结构,包含 name、of_match_table、pm 等字段
id_table const struct platform_device_id * 传统设备 ID 匹配表,用于非设备树平台
关键点: probe 回调是 SPI 控制器驱动的“构造函数”,所有硬件资源(时钟、中断、DMA、GPIO)的获取和 master 对象的创建都在此完成。remove 则是“析构函数”,必须确保所有资源被正确释放,避免内存泄漏或内核崩溃。

3.2 probe/remove 回调函数:驱动的生命周期管理

当内核发现平台设备与驱动匹配时,自动调用 probe 函数。SPI 控制器驱动的 probe 通常遵循以下步骤:

  1. 获取硬件资源: 从 platform_device 中获取 IO 内存、中断号、时钟、DMA 通道等。
  2. 分配 master 对象: 调用 spi_alloc_master() 分配一个 spi_master 结构体。
  3. 设置 master 参数: 配置总线号、模式、传输队列、传输函数指针等。
  4. 硬件初始化: 复位控制器、配置时钟极性/相位、设置 FIFO 阈值、使能中断。
  5. 注册 master: 调用 spi_register_master() 将 master 注册到 SPI 核心层。
最佳实践: 在 probe 中建议使用 devm_* 系列资源管理函数(如 devm_ioremap_resource、devm_clk_get),这样 remove 函数可以大幅简化,甚至在某些场景下可以省略。

以下是一个典型的 probe 函数代码框架:

static int spi_controller_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct spi_master *master;
    struct spi_controller_data *cdata;
    struct resource *res;
    int ret;

    /* 1. 获取硬件资源 */
    cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL);
    if (!cdata)
        return -ENOMEM;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    cdata->base = devm_ioremap_resource(dev, res);
    if (IS_ERR(cdata->base))
        return PTR_ERR(cdata->base);

    cdata->irq = platform_get_irq(pdev, 0);
    if (cdata->irq < 0)
        return cdata->irq;

    /* 2. 分配 master 对象 */
    master = spi_alloc_master(dev, sizeof(struct spi_controller_data));
    if (!master) {
        dev_err(dev, "Failed to allocate SPI master\n");
        return -ENOMEM;
    }

    /* 3. 设置 master 参数 */
    master->bus_num = pdev->id;
    master->num_chipselect = 4;
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
    master->transfer_one = spi_controller_transfer_one;
    master->setup = spi_controller_setup;
    platform_set_drvdata(pdev, master);

    /* 4. 硬件初始化 */
    spi_controller_hw_init(cdata);

    /* 5. 注册 master */
    ret = spi_register_master(master);
    if (ret) {
        dev_err(dev, "Failed to register SPI master: %d\n", ret);
        spi_master_put(master);
        return ret;
    }

    dev_info(dev, "SPI controller registered successfully\n");
    return 0;
}
注意: spi_alloc_master 返回的 master 对象引用计数为 1。如果 spi_register_master 失败,必须调用 spi_master_put 释放引用,否则会造成内存泄漏。

3.3 spi_alloc_master 与 spi_register_master 接口详解

这两个接口是 SPI 控制器驱动与 SPI 核心层交互的核心 API。理解它们的工作原理是编写正确驱动的关键。

3.3.1 spi_alloc_master:分配并初始化 master 对象

spi_alloc_master 在 SPI 核心层中分配一个 spi_master 结构体,并为其关联的设备初始化部分字段。

函数原型 参数说明 返回值
struct spi_master *spi_alloc_master(struct device *dev, unsigned int size) dev: 控制器设备指针;size: 附加的私有数据大小 成功返回 master 指针,失败返回 NULL

该函数执行以下操作:

  • 通过 kzalloc 分配 spi_master 结构体 + 附加私有数据的内存块。
  • 初始化 master 内部的设备对象、传输队列、锁、引用计数。
  • 将附加私有数据的起始地址赋值给 master->dev.driver_data。
  • 设置默认的传输模式、时钟速率等参数。

3.3.2 spi_register_master:将 master 注册到 SPI 核心

spi_register_master 将分配好的 master 对象正式注册到内核 SPI 子系统,使其对 SPI 设备驱动可见。

函数原型 参数说明 返回值
int spi_register_master(struct spi_master *master) master: 已分配并配置好的 master 指针 成功返回 0,失败返回负的错误码

注册过程的核心步骤:

  1. 检查 master 的 transfer_one 或 transfer_one_message 回调是否已设置。
  2. 为 master 分配唯一的总线编号(如果 bus_num 为负值则动态分配)。
  3. 创建 SPI 总线设备,并注册到设备模型。
  4. 扫描设备树或 ACPI 表,为每个子节点创建 SPI 设备。
  5. 将 master 添加到全局的 SPI master 链表。
核心关系: spi_alloc_master 负责“生”,spi_register_master 负责“立”。两者必须成对使用,且顺序不可颠倒。

3.4 驱动注册与注销完整流程

下图展示了 SPI 控制器驱动从加载到卸载的完整生命周期,以及各阶段调用的关键函数:

SPI 控制器驱动注册与注销完整流程 阶段一:驱动加载与 probe 调用 module_init() platform_driver_register() probe() 回调 OK 阶段二:probe 内部执行序列 获取硬件资源 spi_alloc_master() 配置 master 参数 spi_register_master() 阶段三:驱动卸载与 remove 调用 module_exit() platform_driver_unregister() remove() 回调 OK 说明:probe 中必须完成 spi_alloc_master → spi_register_master 序列;remove 中调用 spi_unregister_master 并释放资源

3.5 remove 回调与资源释放

remove 回调的执行顺序与 probe 相反,确保资源按依赖关系正确释放:

static int spi_controller_remove(struct platform_device *pdev)
{
    struct spi_master *master = platform_get_drvdata(pdev);
    struct spi_controller_data *cdata = spi_master_get_devdata(master);

    /* 1. 注销 SPI master,断开与所有 SPI 设备的连接 */
    spi_unregister_master(master);

    /* 2. 硬件去初始化:禁用控制器、关闭时钟、释放中断 */
    spi_controller_hw_deinit(cdata);

    /* 3. 释放 master 引用(如果使用 devm 资源管理,此步可省略) */
    spi_master_put(master);

    return 0;
}
简化技巧: 如果 probe 中全部使用 devm_* 函数(如 devm_ioremap_resource、devm_clk_get、devm_request_irq),则 remove 中只需调用 spi_unregister_master,其余资源由内核自动释放。

3.6 常见错误与避坑指南

错误场景 后果 解决方案
spi_alloc_master 后未检查返回值 空指针解引用导致内核崩溃 始终检查返回值,失败时返回 -ENOMEM
spi_register_master 失败后未调用 spi_master_put master 对象内存泄漏 在错误路径中调用 spi_master_put
remove 中未调用 spi_unregister_master SPI 设备无法正常断开,可能导致系统挂起 确保 remove 中调用 spi_unregister_master
在 probe 中修改 master->dev.driver_data 覆盖核心层内部数据,引发不可预知行为 使用 spi_master_get_devdata 访问私有数据
未设置 transfer_one 或 transfer_one_message 回调 spi_register_master 返回 -EINVAL 注册前必须至少设置一个传输回调

3.7 本章小结