Linux SPI核心数据结构分析

1. spi_controller

之前我们讲解了整个SPI框架的构成和数据传输流程。现在我们深入源码分析每一部分。

首先来看一下 spi_controller,它表示一个 SoC 内部的 SPI 控制器硬件,对应物理模型的 Master。
比如一片 SoC 里面有 SPI0-3 控制器,那么对应的就是 spi_controller0-3

源码位置:include/linux/spi/spi.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
struct spi_controller {
struct device dev; // 内嵌的设备模型,用于挂载到 Linux 设备树

struct list_head list; // 将所有 spi_controller 链接到全局链表

s16 bus_num; // 控制器编号,如 SoC 中 SPI0 对应 bus_num = 0
u16 num_chipselect; // 该控制器最多支持多少个片选信号(CS)

/* 控制器能力的位掩码 */
u32 mode_bits; // 支持的 SPI 模式位(CPOL/CPHA/CS_HIGH 等)
u32 bits_per_word_mask; // 支持的字长掩码,如 8bit、16bit

/* 时钟频率范围 */
u32 min_speed_hz; // 支持的最小 SCK 频率
u32 max_speed_hz; // 支持的最大 SCK 频率

/* 控制器硬件特性标志 */
u16 flags;
#define SPI_CONTROLLER_HALF_DUPLEX BIT(0) // 只支持半双工
#define SPI_CONTROLLER_NO_RX BIT(1) // 不支持接收
#define SPI_CONTROLLER_NO_TX BIT(2) // 不支持发送
#define SPI_CONTROLLER_MUST_RX BIT(3) // 必须同时接收
#define SPI_CONTROLLER_MUST_TX BIT(4) // 必须同时发送

bool slave; // 是否为从机控制器(极少数场景)

/* 传输大小限制(可选) */
size_t (*max_transfer_size)(struct spi_device *spi);
size_t (*max_message_size)(struct spi_device *spi);

/* ---- 锁与同步 ---- */
struct mutex io_mutex; // I/O 互斥锁,保证同一时刻只有一个传输在进行
struct mutex add_lock; // 防止重复添加相同 CS 的锁

/* ---- 回调函数集 ---- */
int (*setup)(struct spi_device *spi); // 配置从设备的模式和时钟
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg); // 旧式批量传输接口(已不推荐)
void (*cleanup)(struct spi_device *spi); // 释放设备资源

/* ---- 新版排队传输机制 ----
* 若设置 queued = true,则使用 kthread_worker 将 message 排队处理,
* 不再直接调用 transfer()。
*/
bool queued; // 是否启用排队传输
struct kthread_worker *kworker; // 内核线程,负责处理消息队列
struct kthread_work pump_messages; // 消息泵工作项
spinlock_t queue_lock; // 队列自旋锁
struct list_head queue; // 待处理的消息链表
struct spi_message *cur_msg; // 当前正在处理的消息
bool busy; // 控制器是否繁忙
bool running; // 控制器是否处于运行状态
struct completion xfer_completion; // 传输完成通知量

/* 新版回调(控制器驱动实现这些) */
int (*prepare_transfer_hardware)(struct spi_controller *ctlr); // 准备硬件(如使能时钟)
int (*unprepare_transfer_hardware)(struct spi_controller *ctlr); // 释放硬件
int (*prepare_message)(struct spi_controller *ctlr,
struct spi_message *message); // 消息预处理
int (*unprepare_message)(struct spi_controller *ctlr,
struct spi_message *message); // 消息后处理
int (*transfer_one_message)(struct spi_controller *ctlr,
struct spi_message *mesg); // 处理一条消息
int (*transfer_one)(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *transfer); // 处理单个 transfer(最常用)
void (*set_cs)(struct spi_device *spi, bool enable); // 控制片选电平
void (*handle_err)(struct spi_controller *ctlr,
struct spi_message *message); // 传输错误处理

/* ---- 片选 GPIO ---- */
int *cs_gpios; // 片选 GPIO 编号数组
struct gpio_desc **cs_gpiods; // 片选 GPIO 描述符数组
bool use_gpio_descriptors; // 是否使用 GPIO 描述符管理 CS

/* ---- DMA 支持 ---- */
bool (*can_dma)(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer); // 判断当前传输是否可用 DMA
size_t max_dma_len; // 单次 DMA 最大传输长度
struct dma_chan *dma_tx; // DMA 发送通道
struct dma_chan *dma_rx; // DMA 接收通道
void *dummy_rx; // 全双工时无数据要收时的占位缓冲区
void *dummy_tx; // 全双工时无数据要发时的占位缓冲区
};

我们看一下源码,只看重要部分,先看最重要的。

1.1 transfer_one

1
2
3
int (*transfer_one)(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *transfer);

表示让每个硬件完成一次传输。它不是一个固定函数,而是提供给 SPI Core 的回调接口。

那它是怎么被调用的呢?前面我们说过了,会在 SPI Core 层通过下面函数调用:

1
controller->transfer_one()

SPI Core 不知道具体 SPI 控制器的寄存器结构,而是提供了上面的统一接口。具体控制器驱动会在初始化时赋值。

我们以 RK 系列为例,提供了 spi-rockchip.c,具体链接我会提供在末尾。

1
2
3
4
static int rockchip_spi_transfer_one(
struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)

其中定义的属于这款 SoC 独特的 transfer_one

在 probe 函数中会被调用:

1
2
3
4
5
6
static int rockchip_spi_probe(struct platform_device *pdev) {
struct spi_controller *ctlr;

ctlr->transfer_one = rockchip_spi_transfer_one;

}

以后 SPI Core 调用:

1
ctlr->transfer_one(ctlr, spi, transfer);

实际上调用的是:

1
rockchip_spi_transfer_one(ctlr, spi, transfer);

整个调用关系如下:

这就是整个 Linux 驱动框架里面典型的抽象层 + 回调机制,读者可以慢慢体会一下。

1.2 num_chipselect

1
u16 num_chipselect;             // 该控制器最多支持多少个片选信号(CS)

注释上写了,我就不啰嗦了。

1.3 bus_num

1
s16 bus_num;                    // 控制器编号,如 SoC 中 SPI0 对应 bus_num = 0

对应的控制器编号。


2. spi_device

设备端,就是具体的从机设备,挂在某个 SPI 控制器上的一个外设。
源码位置:include/linux/spi/spi.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct spi_device {
struct device dev; // 内嵌的设备模型,用于挂载到 Linux 设备树
struct spi_controller *controller; // 指向所属的 SPI 主控制器
struct spi_controller *master; // 旧名称,兼容层,等同于 controller
u32 max_speed_hz; // 该从设备支持的最大 SPI 时钟频率 (Hz)
u8 chip_select; // 片选信号索引(对应控制器第几路 CS)
u8 bits_per_word; // 每个字的位数(如 8、16)
u32 mode; // SPI 工作模式(CPOL/CPHA/CS_HIGH 等组合)
int irq; // 该设备使用的中断号
void *controller_state; // 控制器驱动私有数据(控制器驱动读写)
void *controller_data; // 板级额外配置数据(设备树/平台数据传入)
char modalias[SPI_NAME_SIZE]; // 设备名,用于与 spi_driver 匹配
const char *driver_override; // 强制绑定的驱动名(用于 sysfs 手动匹配)
int cs_gpio; // 旧版片选 GPIO 编号(已不推荐)
struct gpio_desc *cs_gpiod; // 片选 GPIO 描述符(新版方式)
struct spi_delay word_delay; // 字间延迟(两个 word 之间的等待时间)
struct spi_delay cs_setup; // CS 拉低后到第一个 CLK 之间的延时
struct spi_delay cs_hold; // 最后一个 CLK 后到 CS 拉高之间的延时
struct spi_delay cs_inactive; // CS 拉高后再次拉低的最小间隔
};

2.1 chip_select

对应片选 CS,比如设备树:

1
2
3
4
5
imu@0 {

reg = <0>;

};

chip_select = 0

2.2 mode

SPI 的模式,对应 CPOL + CPHA 的组合。


3. spi_driver

设备对应的驱动代码:

1
2
3
4
5
6
7
struct spi_driver {
const struct spi_device_id *id_table; // 支持的设备 ID 表,用于与 spi_device 的 modalias 匹配
int (*probe)(struct spi_device *spi); // 设备匹配成功后调用,初始化硬件并注册
int (*remove)(struct spi_device *spi); // 设备移除时调用,释放资源
void (*shutdown)(struct spi_device *spi); // 系统关机/重启时调用
struct device_driver driver; // 内嵌的设备驱动模型,包含 name、of_match_table 等
};

最重要的就是 probe 函数,当 device 和 driver 匹配后会调用。

probe 函数通常完成:

1
2
3
4
5
6
static int probe(struct spi_device *spi)
{
spi_setup(spi);
spi_read();
register_device();
}

4. spi_transfer

一次连续 SPI 数据交换,是 SPI 传输的最小单位,表示一次不可分割的、连续的字节流传输。一个 spi_message 可以包含多个 spi_transfer,靠链表串联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct spi_transfer {
const void *tx_buf; // 发送缓冲区指针(要发给从设备的数据)
void *rx_buf; // 接收缓冲区指针(存放从设备返回的数据)
unsigned len; // 本次传输的字节数

/* DMA 相关 */
dma_addr_t tx_dma; // 发送缓冲区的 DMA 地址
dma_addr_t rx_dma; // 接收缓冲区的 DMA 地址

/* 传输模式(单线/双线/四线) */
unsigned tx_nbits:3; // 发送数据线数:1bit / 2bit / 4bit
unsigned rx_nbits:3; // 接收数据线数:1bit / 2bit / 4bit
#define SPI_NBITS_SINGLE 0x01 // 1 线传输(标准 SPI)
#define SPI_NBITS_DUAL 0x02 // 2 线传输(Dual SPI)
#define SPI_NBITS_QUAD 0x04 // 4 线传输(Quad SPI)

/* 控制标志 */
unsigned dummy_data:1; // 是否为占位数据(全双工时一方无实际数据时用)
unsigned cs_change:1; // 本次 transfer 结束后是否翻转片选

u8 bits_per_word; // 每个字的位数(如 8、16),可覆盖 spi_device 的默认值

/* 时序延迟 */
struct spi_delay delay; // 本次 transfer 结束后的延时
struct spi_delay cs_change_delay; // cs_change 生效后的等待时间
struct spi_delay word_delay; // 两个数据字之间的间隔

u32 speed_hz; // 本次传输用的时钟频率(可覆盖默认值)

struct list_head transfer_list; // 链表节点,挂载到 spi_message.transfers
};

4.1 tx_buf / rx_buf / len

三件套,描述一次传输数据。

比如发送:

1
tx_buf0x11 0x22

接收:

1
rx_buf0x11 0x22

那么长度 len = 2

4.2 transfer_list

链表节点,用来将多个 spi_transfer 串在一起,挂到 spi_message.transfers 上。SPI Core 在 spi_transfer_one_message() 中通过 list_for_each_entry 遍历这个链表,逐个交给 controller->transfer_one()


5. spi_message

表示一次完整的 SPI 事件。比如先发送地址再读取数据,这两个动作组成一个 message。
源码位置:include/linux/spi/spi.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct spi_message {
struct list_head transfers; // 链表头,串联多个 spi_transfer
struct spi_device *spi; // 指向本次通信的目标从设备
unsigned is_dma_mapped:1; // 缓冲区是否已经做过 DMA 映射

/* 异步完成回调 */
void (*complete)(void *context); // 传输完成时调用的回调函数
void *context; // 传给 complete 回调的参数

/* 传输结果 */
unsigned frame_length; // 预计传输的总字节数
unsigned actual_length; // 实际传输的字节数
int status; // 传输结果状态(0 成功,负值表示错误码)

/* 内部使用 */
struct list_head queue; // 消息在控制器等待队列中的节点
void *state; // 控制器驱动私有状态(可选)
struct list_head resources; // spi_res 资源链表,传输完成后自动释放
};

1. transfers

将若干个 spi_transfer 通过链表串起来,控制器驱动按顺序逐个发送。

spi_messagespi_transfer 的关系如下:

spi_transfer 是数据传输的最小段,spi_message 将多个段组合成一次片选持续的完整事务,设备驱动通过构造 spi_message 并发起 spi_sync() / spi_async() 来完成与芯片的交互。


6. 一次 SPI 传输的数据流

整个流程我们在上一章讲解过了,这里就不多说了,回顾一下:


7. 总结

Linux SPI Framework 通过多个核心数据结构完成硬件抽象。
其中:

  • spi_controller:描述 SoC 中的 SPI 控制器,负责操作具体硬件
  • spi_device:描述挂载在 SPI 总线上的外设
  • spi_driver:描述设备驱动,通过 probe 函数完成设备初始化
  • spi_message:描述一次完整 SPI 事务
  • spi_transfer:描述一次具体的数据交换

整个 SPI 框架通过这些结构体,将设备驱动和底层硬件控制解耦,使同一个设备驱动可以运行在不同 SoC 平台上。


8. 互动

  1. spi_controller 是什么?
  2. spi_device 是什么?
  3. spi_driver 是什么?
  4. spi_messagespi_transfer 为什么存在?
  5. 一次 SPI 传输在这些结构体之间如何流转?

Linux SPI核心数据结构分析
https://cj0510.github.io/2026/08/01/Linux Kernel/SPI/Linux SPI核心数据结构分析/
作者
CJ1018
发布于
2026年8月1日
许可协议
CC BY-NC-SA 4.0