在 Linux SPI 子系统中,有几个核心数据结构贯穿于驱动开发的始终。理解这些结构体对于编写和调试 SPI 驱动至关重要。
1. struct spi_master (spi_controller)
struct spi_master(在新版内核中已重命名或别名为 struct spi_controller)代表一个物理的 SPI 控制器。
c
struct spi_controller {
struct device dev;
struct list_head list;
// 总线号,例如 spi0 对应 bus_num=0
s16 bus_num;
// 支持的片选数量
u16 num_chipselect;
// 模式支持掩码 (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH ...)
u16 mode_bits;
// 传输相关的配置
u32 max_speed_hz;
u32 min_speed_hz;
// 核心传输方法,由控制器驱动实现
int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,
struct spi_transfer *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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- 作用:描述控制器的硬件特性(如支持的模式、频率范围)和传输方法。
- 开发者关注:通常由 SoC 厂商(如 Rockchip)的 BSP 工程师维护。设备驱动开发者主要通过 API 间接使用它。
2. struct spi_device
struct spi_device 代表挂载在 SPI 总线上的一个具体从设备。
c
struct spi_device {
struct device dev;
struct spi_controller *controller;
struct spi_controller *master; // 兼容旧名称
u32 max_speed_hz; // 该设备的最大通信速率
u8 chip_select; // 片选号 (CS0, CS1...)
u8 bits_per_word;// 字长 (通常为 8)
u16 mode; // 工作模式 (CPOL, CPHA, CS_HIGH 等)
int irq; // 中断号
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE]; // 驱动匹配名称
// ...
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 作用:描述具体外设的配置。
- 获取方式:在设备驱动的
probe函数中作为参数传入。 - 关键成员:
mode:设置 CPOL, CPHA 等。max_speed_hz:限制与该设备通信的最大时钟频率。chip_select:硬件连接的片选线索引。
3. struct spi_transfer
struct spi_transfer 描述一次原子的数据传输操作(读/写缓冲区)。
c
struct spi_transfer {
const void *tx_buf; // 发送缓冲区指针 (虚拟地址)
void *rx_buf; // 接收缓冲区指针 (虚拟地址)
unsigned len; // 传输长度 (字节)
dma_addr_t tx_dma; // 发送 DMA 地址
dma_addr_t rx_dma; // 接收 DMA 地址
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1; // 传输结束后是否改变片选状态
unsigned tx_nbits:3; // 发送位宽 (1: Standard SPI, 2: Dual, 4: Quad)
unsigned rx_nbits:3; // 接收位宽
u8 bits_per_word; // 本次传输的字长 (覆盖 spi_device 设置)
u16 delay_usecs; // 传输结束后的延时 (微秒)
u32 speed_hz; // 本次传输的速度 (覆盖 spi_device 设置)
struct list_head transfer_list;
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 作用:构建传输的最小单元。一个
spi_message可以包含多个spi_transfer。 - 使用:
tx_buf和rx_buf可以同时设置(全双工)。- 如果只发不收,
rx_buf设为 NULL。 - 如果只收不发,
tx_buf设为 NULL。
4. struct spi_message
struct spi_message 是 SPI 子系统处理传输请求的逻辑单元,它是一系列 spi_transfer 的集合。
c
struct spi_message {
struct list_head transfers; // 挂载 spi_transfer 的链表头
struct spi_device *spi; // 指向目标 SPI 设备
unsigned is_dma_mapped:1; // 是否已经映射了 DMA
// 完成回调
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length; // 实际传输的字节数
int status; // 传输状态 (0: 成功, <0: 错误码)
struct list_head queue;
void *state;
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 作用:原子地执行一系列传输。在整个 message 传输过程中,片选通常保持有效(除非 transfer 中指定了
cs_change)。 - 常用操作:
spi_message_init(struct spi_message *m):初始化。spi_message_add_tail(struct spi_transfer *t, struct spi_message *m):添加 transfer 到 message。spi_sync(struct spi_device *spi, struct spi_message *m):同步传输。spi_async(struct spi_device *spi, struct spi_message *m):异步传输。
5. 关系图解
plaintext
+-------------------+
| spi_device | <-- 代表一个外设
+-------------------+
|
v
+-------------------+
| spi_message | <-- 一次完整的交互任务
+-------------------+
| 包含
|
+----+----+
| |
v v
+----------+ +----------+
| transfer | | transfer | ... <-- 具体的读写动作
+----------+ +----------+1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
理解这些结构体是编写 SPI 驱动的基础。大多数 SPI 驱动的工作就是配置 spi_device,然后构建 spi_transfer 和 spi_message 并提交给核心层。