2. Linux SPI 子系统架构:分层设计与数据结构关系
Linux SPI 子系统采用经典的分层架构设计,从上到下依次为:SPI 设备驱动层、SPI 核心层和SPI 控制器驱动层。这种分层结构实现了硬件控制逻辑与设备业务逻辑的解耦,使得 SPI 控制器驱动可以复用,而 SPI 设备驱动只需关注协议解析与数据处理。
2.1 三层架构总览
下图展示了 SPI 子系统的三层架构及其核心数据结构之间的关联关系:
2.2 核心数据结构关系
SPI 子系统的三层架构通过一组核心数据结构紧密关联。下表列出了各层的关键数据结构及其职责:
| 数据结构 | 所属层次 | 核心字段 / 作用 |
|---|---|---|
| spi_controller | 控制器驱动层 | 管理 SPI 总线控制器硬件,包含 bus_num、num_chipselect、min/max_speed_hz、transfer_one_message() 回调 |
| spi_device | 设备驱动层 | 代表一个 SPI 从设备,包含 chip_select、max_speed_hz、mode、bits_per_word、controller 指针 |
| spi_driver | 设备驱动层 | SPI 设备驱动实例,包含 probe()、remove()、id_table,通过 spi_register_driver() 注册 |
| spi_message | 核心层 | 一次 SPI 传输的完整请求,包含多个 spi_transfer,通过链表组织 |
| spi_transfer | 核心层 | 单个 SPI 传输片段,包含 tx_buf、rx_buf、len、speed_hz、cs_change、delay_usecs |
| spi_board_info | 板级描述层 | 板级初始化时描述 SPI 设备信息,包含 modalias、chip_select、max_speed_hz、platform_data |
2.3 设备枚举与绑定流程
SPI 设备的枚举与驱动绑定遵循 Linux 设备模型的标准流程,具体步骤如下:
- 控制器注册:SPI 控制器驱动调用
spi_register_controller(),将spi_controller注册到核心层,核心层为其分配 bus_num 并创建 /sys/bus/spi/devices/spiX 目录。 - 设备创建:通过板级代码的
spi_board_info数组或设备树中的 SPI 子节点,核心层调用spi_new_device()创建spi_device实例,并挂载到对应控制器的设备链表。 - 驱动注册:SPI 设备驱动调用
spi_register_driver(),将spi_driver注册到spi_bus_type总线。 - 匹配绑定:核心层通过
spi_bus_type->match()函数,将spi_driver->id_table中的名称与spi_device->modalias比对,匹配成功后调用spi_driver->probe()。 - 设备初始化:在 probe() 中,驱动调用
spi_setup()配置 SPI 通信参数(模式、速率、位宽),并准备数据传输所需的资源。
关键点:设备树(Device Tree)是当前主流 SPI 设备描述方式。在设备树中,SPI 从设备作为控制器节点的子节点出现,核心层在解析设备树时自动调用 of_register_spi_devices() 完成设备创建,无需板级代码。
2.4 数据传输路径
SPI 数据传输从设备驱动发起,经过核心层调度,最终由控制器驱动完成硬件操作。下图展示了典型的数据传输路径:
2.5 关键设计要点
性能设计考量:
- 消息队列:控制器驱动维护一个消息队列,核心层通过
spi_controller->queue管理待处理消息,支持异步流水线处理。 - DMA 支持:控制器驱动可在
prepare_message()中为spi_transfer的 tx_buf/rx_buf 建立 DMA 映射,减少 CPU 拷贝开销。 - CS 控制优化:通过
cs_change字段控制片选信号在多个 transfer 之间是否拉高,减少 GPIO 操作次数。 - 速率自适应:每个
spi_transfer可独立指定speed_hz,控制器驱动在transfer_one()中动态调整时钟分频。
常见陷阱:
- 在
spi_message中添加 transfer 时,必须使用spi_message_add_tail(),不能直接操作链表。 - 异步传输
spi_async()的完成回调在原子上下文中执行,不能调用可能导致睡眠的函数。 - 设备树中 SPI 从设备的
reg属性值必须与控制器驱动的chip_select编号一致,否则设备无法正确寻址。
2.6 数据结构关系总结
下表总结了各数据结构之间的引用关系,帮助理解代码中的指针传递:
| 源结构 | 目标结构 | 指针字段 | 关系说明 |
|---|---|---|---|
| spi_device | spi_controller | controller | 设备所属的控制器 |
| spi_controller | spi_device | devices 链表 | 控制器下挂载的所有设备 |
| spi_message | spi_device | spi | 消息目标设备 |
| spi_message | spi_transfer | transfers 链表 | 消息包含的传输片段 |
| spi_driver | spi_device | id_table / match | 驱动与设备的匹配关系 |
| spi_board_info | spi_device | modalias / chip_select | 板级描述到设备实例的映射 |