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 中的传输段链表,并操作硬件寄存器完成数据收发。
struct spi_master 核心字段与回调 spi_master bus_num / num_chipselect mode_bits / min/max_speed_hz transfer_one_message() setup() 控制器驱动职责 • 注册 spi_master 到内核 SPI 框架 • 实现 transfer_one_message 完成硬件数据收发 • 通过 setup 回调配置设备参数

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时,传输结束后释放片选,常用于多消息序列
spi_message 与 spi_transfer 的层次关系 struct spi_message transfers 链表头 complete 回调 spi_transfer 链表(struct list_head transfers) spi_transfer #1 tx_buf / rx_buf len=4, speed_hz=1MHz spi_transfer #2 tx_buf / rx_buf len=8, speed_hz=5MHz spi_transfer #3 tx_buf / rx_buf len=2, cs_change=1 原子性:一个 spi_message 中的所有 transfer 要么全部执行,要么全部不执行 片选在 message 开始前拉低,在最后一个 transfer 结束后拉高(除非 cs_change 改变)

4.5 数据结构协作流程

在实际驱动开发中,这四个数据结构的协作遵循固定流程:

  1. 设备注册阶段:板级代码或设备树创建 spi_device,绑定到 spi_master。
  2. 驱动探测阶段:设备驱动获取 spi_device,调用 spi_setup() 配置参数。
  3. 通信发起阶段:驱动初始化 spi_message,添加一个或多个 spi_transfer。
  4. 提交执行阶段:调用 spi_sync() 或 spi_async() 提交消息。
  5. 硬件处理阶段:spi_master 的 transfer_one_message 遍历传输段链表,操作硬件寄存器。
  6. 完成通知阶段:传输完成后调用 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,学习如何在实际驱动中构建通信请求。