3. Linux SPI 子系统架构
Linux SPI 子系统采用分层架构设计,将 SPI 控制器硬件差异、核心协议逻辑与具体设备功能解耦。理解这一架构是开发稳定、可移植 SPI 驱动的基石。本章深入剖析 SPI 核心层、控制器驱动层、设备驱动层的职责与交互,并详细解读核心数据结构之间的关系。
3.1 三层架构总览
SPI 子系统在 Linux 内核中位于 drivers/spi/ 目录,主要分为三个层次:
- SPI 核心层 (spi.c):提供总线注册、设备枚举、数据传输抽象、驱动模型管理等基础设施。
- SPI 控制器驱动层:直接操作 SPI 控制器硬件寄存器,实现 FIFO 控制、DMA 传输、中断处理等底层操作。
- SPI 设备驱动层:面向具体 SPI 外设(如 ADC、传感器、显示屏),通过核心层 API 与控制器通信,实现设备特定功能。
架构优势: 设备驱动无需关心硬件寄存器细节,控制器驱动无需理解设备协议,核心层负责两者之间的标准化通信。这种分层极大提升了代码复用性和可移植性。
3.2 SPI 核心层 (spi.c)
核心层是 SPI 子系统的中枢,主要功能包括:
- 总线管理:注册 SPI 总线类型 (
spi_bus_type),管理总线上设备和驱动的匹配。 - 数据传输抽象:提供
spi_sync()、spi_async()、spi_write_then_read()等标准 API。 - 设备枚举:解析设备树 (Device Tree) 或平台数据,创建
spi_device实例。 - 消息队列管理:将
spi_message组织成队列,调度给控制器驱动处理。
关键文件:
drivers/spi/spi.c 是核心实现,include/linux/spi/spi.h 是核心头文件,定义了所有关键数据结构和 API 原型。
3.3 SPI 控制器驱动层
控制器驱动负责与硬件 SPI 控制器交互,必须实现 spi_master 结构体中的关键回调:
| 回调函数 | 功能描述 | 必须实现 |
|---|---|---|
transfer_one() |
执行单次 SPI 传输(发送+接收) | 是 |
setup() |
配置 SPI 控制器参数(时钟、极性、相位) | 是 |
prepare_message() |
在消息传输前进行准备(如 DMA 设置) | 否 |
unprepare_message() |
消息传输完成后的清理工作 | 否 |
transfer_one_message() |
处理整个消息队列(默认实现调用 transfer_one) | 否 |
注意事项: 控制器驱动通常需要处理中断、DMA、FIFO 溢出等硬件细节。在实现
transfer_one() 时,必须考虑 SPI 模式 (CPOL/CPHA)、位序 (MSB/LSB) 和时钟频率的配置。
3.4 SPI 设备驱动层
设备驱动是开发者最常编写的部分,通过 spi_driver 结构体注册到核心层:
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my_spi_device",
.of_match_table = my_spi_of_match,
},
.probe = my_spi_probe,
.remove = my_spi_remove,
.id_table = my_spi_id_table,
};
module_spi_driver(my_spi_driver);
设备驱动的主要职责:
- 在
probe()中初始化设备、分配缓冲区、注册字符设备或 IIO 框架。 - 使用
spi_write()、spi_read()、spi_write_then_read()等 API 与设备通信。 - 在
remove()中释放资源、注销设备。
3.5 核心数据结构关系图
SPI 子系统的三个核心数据结构——spi_master、spi_device、spi_driver——通过内核驱动模型紧密关联:
3.6 数据结构交互流程
当系统启动或设备插入时,SPI 子系统的数据流如下:
- 控制器注册:控制器驱动调用
spi_register_master(),核心层创建spi_master实例并注册到总线。 - 设备创建:核心层解析设备树或平台数据,为每个 SPI 从设备创建
spi_device,并关联到对应的spi_master。 - 驱动匹配:核心层遍历已注册的
spi_driver,通过of_match_table或id_table与spi_device匹配。 - 设备绑定:匹配成功后,核心层调用
spi_driver->probe(),传入spi_device指针,驱动开始初始化。 - 数据传输:设备驱动通过
spi_sync()提交spi_message,核心层将其传递给spi_master的transfer_one()执行。
关键理解:
spi_device 是连接控制器和设备的桥梁。它既持有 spi_master 的指针用于传输,又作为 struct device 参与驱动模型匹配。这种设计使得设备驱动只需操作 spi_device,无需直接接触硬件寄存器。
3.7 数据传输路径
一次典型的 SPI 数据传输路径如下:
| 步骤 | 调用链 | 所在层 |
|---|---|---|
| 1 | spi_write() / spi_read() |
设备驱动 |
| 2 | spi_sync() |
核心层 |
| 3 | __spi_validate() 检查消息合法性 |
核心层 |
| 4 | spi_queued_transfer() 加入队列 |
核心层 |
| 5 | spi_master->transfer_one_message() |
控制器驱动 |
| 6 | spi_master->transfer_one() 硬件操作 |
控制器驱动 |
| 7 | 中断/DMA 完成回调 | 控制器驱动 |
| 8 | spi_finalize_current_message() |
核心层 |
性能提示: 对于批量小数据,使用
spi_write_then_read() 可以避免多次消息队列调度。对于大数据块,建议使用 DMA 模式,并在控制器驱动的 prepare_message() 中配置 DMA 描述符。