4. SPI设备驱动注册:spi_driver结构体、spi_register_driver接口、probe/remove回调函数实现

SPI设备驱动的核心注册机制围绕 spi_driver 结构体展开,通过 spi_register_driver 接口将驱动与内核SPI核心层绑定。本章深入剖析驱动注册的完整流程,包括结构体定义、接口调用规范以及 probe/remove 回调函数的实现要点。

4.1 spi_driver结构体深度解析

spi_driver 是SPI设备驱动在Linux内核中的标准描述符,定义在 include/linux/spi/spi.h 中。它封装了驱动与设备匹配、初始化和销毁的全部回调接口。

成员 类型 说明
probe int (*)(struct spi_device *) 设备匹配成功后调用,完成硬件初始化、资源分配
remove int (*)(struct spi_device *) 设备移除或驱动卸载时调用,释放资源
shutdown void (*)(struct spi_device *) 系统关机或重启时调用,执行优雅关闭
id_table const struct spi_device_id * 设备ID匹配表,用于传统匹配方式
driver struct device_driver 通用设备驱动结构体,包含名称、of_match_table等
关键点: spi_driver 不直接包含SPI传输参数(如模式、频率、字长),这些参数通过 spi_device 结构体中的 mode、max_speed_hz、bits_per_word 字段传递,在 probe 回调中由驱动读取并配置。
// spi_driver 结构体定义(内核源码精简版)
struct spi_driver {
    const struct spi_device_id *id_table;
    int (*probe)(struct spi_device *spi);
    int (*remove)(struct spi_device *spi);
    void (*shutdown)(struct spi_device *spi);
    struct device_driver driver;
};

4.2 spi_register_driver接口与注册流程

spi_register_driver 是驱动注册的入口函数,它将 spi_driver 实例注册到内核SPI子系统。该接口内部调用 driver_register 通用驱动注册函数,并建立与SPI总线类型的关联。

最佳实践: 推荐使用 module_spi_driver() 宏来简化驱动注册和卸载代码,该宏自动展开为 module_init 和 module_exit 的标准模板。
// 标准注册方式
static int __init my_spi_driver_init(void)
{
    return spi_register_driver(&my_spi_driver);
}

static void __exit my_spi_driver_exit(void)
{
    spi_unregister_driver(&my_spi_driver);
}

module_init(my_spi_driver_init);
module_exit(my_spi_driver_exit);

// 简化宏方式(推荐)
module_spi_driver(my_spi_driver);
避坑指南: 使用 module_spi_driver() 时,必须确保 spi_driver 实例的 driver.name 字段已正确初始化,否则驱动注册会因名称缺失而失败。

4.3 probe回调函数实现规范

probe 回调是驱动初始化的核心,它在设备与驱动匹配成功后由内核自动调用。实现质量直接影响设备的稳定性和性能。

probe回调标准实现步骤:

  1. 获取设备参数: 从 spi_device 中读取 mode、max_speed_hz、bits_per_word 等配置。
  2. 设置SPI参数: 调用 spi_setup() 将参数同步到控制器硬件。
  3. 分配私有数据: 使用 devm_kzalloc() 分配驱动私有数据结构体。
  4. 初始化硬件: 执行设备复位、寄存器初始化等硬件操作。
  5. 注册子系统接口: 如字符设备、输入设备、IIO设备等。
  6. 保存设备指针: 将 spi_device 指针存入私有数据供后续使用。
static int my_spi_probe(struct spi_device *spi)
{
    struct my_spi_dev *dev;
    int ret;

    // 1. 读取并验证设备参数
    dev_info(&spi->dev, "SPI mode 0x%x, speed %d Hz, bits %d\n",
             spi->mode, spi->max_speed_hz, spi->bits_per_word);

    // 2. 设置SPI参数(必须调用)
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 10000000;  // 10 MHz
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret) {
        dev_err(&spi->dev, "spi_setup failed: %d\n", ret);
        return ret;
    }

    // 3. 分配私有数据
    dev = devm_kzalloc(&spi->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    dev->spi = spi;
    spi_set_drvdata(spi, dev);

    // 4. 硬件初始化(示例:复位设备)
    ret = my_spi_hw_reset(dev);
    if (ret)
        return ret;

    // 5. 注册字符设备或其它子系统
    ret = my_spi_register_device(dev);
    if (ret)
        return ret;

    dev_info(&spi->dev, "probe success\n");
    return 0;
}

4.4 remove回调函数实现规范

remove 回调负责在设备拔出或驱动卸载时执行清理操作,确保资源完全释放,避免内存泄漏或内核崩溃。

remove回调标准实现步骤:

  • 取消注册子系统接口(如字符设备、中断处理函数)。
  • 释放硬件资源(如GPIO、DMA通道、中断)。
  • 执行设备断电或进入低功耗模式。
  • 清理工作队列、定时器等内核对象。
static int my_spi_remove(struct spi_device *spi)
{
    struct my_spi_dev *dev = spi_get_drvdata(spi);

    // 1. 注销子系统接口
    my_spi_unregister_device(dev);

    // 2. 释放中断(如果使用 devm 则自动释放)
    free_irq(dev->irq, dev);

    // 3. 硬件关闭
    my_spi_hw_powerdown(dev);

    // 4. 清理工作队列
    cancel_work_sync(&dev->work);

    dev_info(&spi->dev, "remove complete\n");
    return 0;
}
注意: 如果使用 devm_ 系列API(如 devm_kzalloc、devm_request_irq),内核会在 remove 后自动释放资源,但显式清理硬件状态和取消异步操作仍是必要的。

4.5 驱动注册与回调完整流程图

SPI设备驱动注册与probe/remove回调流程图 SPI设备驱动注册与回调流程 驱动加载 (module_init) spi_register_driver() 设备匹配 (id_table / DT) probe() 回调执行 读取参数 → spi_setup → 分配资源 → 硬件初始化 设备正常运行 remove() 回调执行 注销接口 → 释放中断 → 硬件关闭 → 清理工作 驱动卸载 (module_exit) 说明:probe/remove回调由内核SPI核心自动调用,驱动开发者只需实现业务逻辑

4.6 完整驱动注册示例

以下是一个完整的SPI设备驱动注册示例,整合了 spi_driver 定义、设备树匹配表、probe/remove回调以及模块入口/出口。

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>

/* 驱动私有数据结构 */
struct my_spi_dev {
    struct spi_device *spi;
    struct mutex lock;
    /* 其他硬件相关字段 */
};

/* probe 回调 */
static int my_spi_probe(struct spi_device *spi)
{
    struct my_spi_dev *dev;
    int ret;

    dev = devm_kzalloc(&spi->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    dev->spi = spi;
    mutex_init(&dev->lock);
    spi_set_drvdata(spi, dev);

    /* 配置SPI参数 */
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 5000000;  /* 5 MHz */
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret) {
        dev_err(&spi->dev, "spi_setup failed: %d\n", ret);
        return ret;
    }

    dev_info(&spi->dev, "device probed at %d Hz\n", spi->max_speed_hz);
    return 0;
}

/* remove 回调 */
static int my_spi_remove(struct spi_device *spi)
{
    struct my_spi_dev *dev = spi_get_drvdata(spi);

    /* 执行硬件关闭 */
    dev_info(&spi->dev, "device removed\n");
    return 0;
}

/* 设备树匹配表 */
static const struct of_device_id my_spi_of_match[] = {
    { .compatible = "myvendor,my-spi-device" },
    { }
};
MODULE_DEVICE_TABLE(of, my_spi_of_match);

/* SPI设备ID表(传统匹配) */
static const struct spi_device_id my_spi_id[] = {
    { "my-spi-device", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, my_spi_id);

/* spi_driver 实例 */
static struct spi_driver my_spi_driver = {
    .driver = {
        .name = "my_spi_driver",
        .of_match_table = my_spi_of_match,
    },
    .id_table = my_spi_id,
    .probe = my_spi_probe,
    .remove = my_spi_remove,
};

/* 使用简化宏注册 */
module_spi_driver(my_spi_driver);

MODULE_AUTHOR("Linux SPI Expert");
MODULE_DESCRIPTION("SPI device driver example");
MODULE_LICENSE("GPL v2");

4.7 常见错误与调试技巧

错误场景 现象 解决方案
probe未调用 驱动加载成功但probe不执行 检查设备树compatible属性与of_match_table是否匹配;检查spi_device_id表
spi_setup失败 probe返回-EINVAL 确认mode、max_speed_hz、bits_per_word在控制器支持范围内
资源泄漏 多次加载/卸载后内存不足 优先使用devm_系列API;remove中显式取消工作队列和定时器
中断未释放 卸载后系统崩溃 使用devm_request_irq或remove中调用free_irq
调试技巧: 在probe和remove函数中添加 dev_dbg() 或 dev_info() 日志,通过 dmesg 观察驱动加载/卸载流程。使用 ls /sys/bus/spi/drivers/ 查看已注册的SPI驱动列表。

4.8 设备树与驱动匹配关系图

设备树与spi_driver匹配关系 设备树节点与spi_driver匹配机制 设备树 (Device Tree) spi@ff110000 { mydevice@0 { compatible = "myvendor,my-spi-device" reg = <0>; spi-max-frequency = <5000000> }; spi_driver 驱动 static struct spi_driver my_driver .of_match_table = my_of_match .compatible = "myvendor,my-spi-device" .probe = my_spi_probe .remove = my_spi_remove 匹配
核心要点: 设备树中的 compatible 属性与驱动中 of_match_table 的 .compatible 字符串必须完全一致(包括大小写和厂商前缀),否则probe回调不会被触发。这是SPI驱动开发中最常见的匹配失败原因。