4、SPI核心数据结构:struct spi_master、struct spi_device、struct spi_message、struct spi_transfer
在Linux内核SPI子系统中,驱动开发的核心是理解和操作四个关键数据结构。它们构成了SPI通信的骨架:控制器(谁控制总线)、设备(谁挂在总线上)、消息(一次完整的通信请求)和传输段(消息中的最小数据单元)。本章将逐一拆解它们的定义、字段含义和协作关系。
核心要点:这四个数据结构从硬件抽象到软件请求,层层封装。驱动开发者通常直接操作
spi_message 和 spi_transfer,而 spi_master 和 spi_device 更多由内核框架和板级代码管理。
4.1 struct spi_master —— SPI控制器抽象
struct spi_master 是SPI控制器的内核抽象,代表一个硬件SPI主机(如SoC内部的SPI IP核)。它管理总线时钟、片选信号、模式配置以及数据传输的底层硬件操作。
| 字段 | 类型 | 说明 |
|---|---|---|
bus_num |
u16 |
总线编号,用于标识该SPI控制器 |
num_chipselect |
u16 |
支持的片选信号数量 |
mode_bits |
u16 |
支持的SPI模式位掩码(CPOL/CPHA/CS_HIGH等) |
min_speed_hz |
u32 |
支持的最低SCK频率 |
max_speed_hz |
u32 |
支持的最高SCK频率 |
transfer_one_message |
函数指针 | 控制器驱动必须实现的底层传输函数 |
setup |
函数指针 | 配置SPI设备参数(时钟、模式、位序) |
dev |
struct device |
内核设备模型中的设备对象 |
驱动开发提示:编写SPI控制器驱动时,核心工作是实现
transfer_one_message 回调。该函数负责解析 spi_message 中的传输段链表,并操作硬件寄存器完成数据收发。
4.2 struct spi_device —— SPI设备抽象
struct spi_device 代表一个挂在SPI总线上的从设备。它由板级代码或设备树在系统初始化时创建,并绑定到对应的 spi_master。驱动开发者通过该结构体访问设备配置和进行通信。
struct spi_device {
struct device dev; /* 内核设备模型 */
struct spi_master *master; /* 所属的 SPI 控制器 */
u32 max_speed_hz; /* 设备最大时钟频率 */
u8 chip_select; /* 片选信号编号 */
u8 mode; /* SPI 模式 (CPOL/CPHA/CS_HIGH/3WIRE) */
u8 bits_per_word; /* 每字位数,通常为 8 */
int irq; /* 设备中断号,可选 */
void *controller_state; /* 控制器私有数据 */
struct spi_device *dev; /* 指向自身设备结构 */
...
};
| 字段 | 说明 |
|---|---|
master |
指向控制该设备的 spi_master 实例 |
max_speed_hz |
该设备支持的最大SCK频率,不能超过master的限制 |
chip_select |
片选线索引,决定使用哪根CS线 |
mode |
包含 SPI_MODE_0/1/2/3、SPI_CS_HIGH、SPI_3WIRE 等标志 |
bits_per_word |
每次传输的数据位宽,常见为8或16 |
常见错误:在设备驱动中直接修改
spi_device 的 mode 或 max_speed_hz 而不调用 spi_setup(),会导致配置不生效。所有参数变更后必须调用 spi_setup() 同步到控制器。
4.3 struct spi_message —— SPI消息封装
struct spi_message 是一次SPI通信请求的完整封装。它包含一个或多个 spi_transfer 传输段,形成一个原子操作:要么全部传输完成,要么全部不执行。消息是驱动与控制器交互的基本单位。
struct spi_message {
struct list_head transfers; /* 传输段链表 */
struct spi_device *spi; /* 目标设备 */
unsigned is_dma_mapped:1;/* 是否已做DMA映射 */
int status; /* 传输完成后的状态码 */
void (*complete)(void *context); /* 完成回调 */
void *context; /* 回调上下文 */
unsigned frame_length; /* 总帧长度(字节) */
...
};
关键机制:
spi_message 通过 spi_async() 提交时是异步的,通过 spi_sync() 提交时是同步的(内部使用等待队列)。消息的 complete 回调在传输完成后被调用,用于通知驱动层。
4.4 struct spi_transfer —— 最小传输单元
struct spi_transfer 描述一段连续的数据传输。它定义了发送缓冲区、接收缓冲区、传输长度、时钟频率和延迟等参数。多个 spi_transfer 通过链表串联在 spi_message 中,实现复杂的通信时序。
struct spi_transfer {
const void *tx_buf; /* 发送缓冲区,NULL 表示只接收 */
void *rx_buf; /* 接收缓冲区,NULL 表示只发送 */
unsigned len; /* 传输字节数 */
u32 speed_hz; /* 本次传输的时钟频率,0 表示使用设备默认值 */
u16 delay_usecs; /* 传输完成后的延迟(微秒) */
u8 bits_per_word; /* 本次传输的位宽,0 表示使用设备默认值 */
u16 cs_change; /* 传输完成后是否切换片选 */
...
};
| 字段 | 典型用法 |
|---|---|
tx_buf / rx_buf |
全双工时同时设置;半双工时设置其中一个为 NULL |
len |
决定本次传输的SCK周期数,必须与缓冲区大小匹配 |
speed_hz |
用于在单消息内动态切换速率,如慢速配置+快速数据 |
cs_change |
设置为1时,传输结束后释放片选,常用于多消息序列 |
4.5 数据结构协作流程
在实际驱动开发中,这四个数据结构的协作遵循固定流程:
- 设备注册阶段:板级代码或设备树创建
spi_device,绑定到spi_master。 - 驱动探测阶段:设备驱动获取
spi_device,调用spi_setup()配置参数。 - 通信发起阶段:驱动初始化
spi_message,添加一个或多个spi_transfer。 - 提交执行阶段:调用
spi_sync()或spi_async()提交消息。 - 硬件处理阶段:
spi_master的transfer_one_message遍历传输段链表,操作硬件寄存器。 - 完成通知阶段:传输完成后调用
complete回调,驱动检查status字段。
性能优化建议:对于高频小数据量通信,尽量将多个
spi_transfer 合并到同一个 spi_message 中,减少消息提交的开销。对于大数据块传输,使用DMA映射(设置 is_dma_mapped 标志)可以显著提升吞吐量。
4.6 数据结构对比总结
| 数据结构 | 层次 | 生命周期 | 驱动开发者操作频率 |
|---|---|---|---|
spi_master |
控制器层 | 系统运行期间常驻 | 低(编写控制器驱动时) |
spi_device |
设备层 | 设备绑定期间存在 | 中(probe/remove时配置) |
spi_message |
请求层 | 单次通信周期 | 高(每次通信都需创建) |
spi_transfer |
传输段层 | 消息生命周期内 | 高(定义具体数据收发) |
理解这四个数据结构的职责和协作关系,是掌握SPI驱动开发的基础。下一层我们将深入 spi_message 和 spi_transfer 的初始化与提交API,学习如何在实际驱动中构建通信请求。