Linux driver spi API
1. SPI
1.1 SPI 相关结构体
1.1.1 struct spi_device spi 设备结构体
#include <linux/spi.h>
struct spi_device {
struct device dev; /* 继承的 device 结构体 */
/* 当前 spi 设备挂载的 spi 控制器 */
struct spi_controller *controller;
/* 指定 spi 设备挂载的 spi 总线 */
struct spi_controller *master; /* compatibility layer */
u32 max_speed_hz; /* spi 的最大传输速率 */
u8 chip_select; /* spi 总线用于区分不同 spi 设备的标号 */
u8 bits_per_word; /* spi 通讯时一个字节多少个位 */
u16 mode; /* spi 工作模式 */
/*
下列的宏定义是用来设置 mode 成员的
mode 的每个位都代表不同的模式功能
如 mode 的第 0 位为 1 时,SPI_CPHA 使能,为 0 时 SPI_CPHA 失能
*/
/* 工作模式如以上代码中的宏定义。包括时钟极性、位宽等等,这些宏定义可以使用或运算 | 进行组合 */
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire)␣
*/
/* SPI_CPOL 和 SPI_CPHA的值同时也指定了全双工模式/半双工等工作模式 */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active, high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-, wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no,chipselect */
#define SPI_READY 0x80 /* slave pulls low to,pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2, wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4, wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires, */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires,*/
int irq; /* 如果使用了中断,用于指定中断号 */
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* 片选引脚 */
/* 设备树中设置了片选引脚,驱动和设别树节点匹配成功后自动获取片选引脚,我们也可以在驱动总通过设置该参数自定义片选引脚 */
/* the statistics */
struct spi_statistics statistics; /* 记录 spi 的名字,用于和 spi_driver 进行匹配 */
};
1.1.2 spi_controller spi 总线控制器结构体
#include <linux/spi/spi.h>
struct spi_controller {
struct device dev;
...
struct list_head list; /* 链表节点,IC 可能有多个 spi 控制器 */
s16 bus_num; /* spi 控制器的编号 */
u16 num_chipselect; /* spi 片选信号的个数 */
...
struct spi_message *cur_msg; /* spi_message 结构体,发送消息时都会把信息封装在这个结构体中 */
...
int (*setup)(struct spi_device *spi);
/* 用于将数据加入 spi 控制器的消息队列中 */
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* 当 spi_message 被释放时,执行清理工作 */
void (*cleanup)(struct spi_device *spi);
/* 内核线程工人,spi 可以使用异步传输的方式发送数据 */
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages; /* 具体传输工作 */
struct list_head queue; /* 所有等待传输消息队列挂载的头部链表节点 */
struct spi_message *cur_msg;
...
int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,struct spi_transfer *transfer);
int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
/* 产生 spi 时序的底层接口 */
int (*transfer_one_message)(struct spi_controller *ctlr,struct spi_message *mesg);
void (*set_cs)(struct spi_device *spi, bool enable);
...
int *cs_gpios; /* 记录 spi 上具体的片选信号 */
}
1.1.3 struct spi_transfer 数据传输结构体
#include <linux/spi/spi.h>
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf; /* 发送缓冲区 */
void *rx_buf; /* 接收缓冲区 */
unsigned len; /* 传输长度,根据 spi 的特性发送和接收的长度相等 */
dma_addr_t tx_dma; /* 如果使用了 DMA,用于指定 DMA 传输的发送地址 */
dma_addr_t rx_dma; /* 如果使用了 DMA,用于指定 DMA 传输的接收地址 */
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word; /* 传输单个字节的位数 */
u16 delay_usecs;
u32 speed_hz; /* spi 的发送频率 */
struct list_head transfer_list;
};
1.1.4 struct spi_message spi 消息结构体
· 总体来说 spi 设备驱动中的数据是以消息队列的形式发送的
· 发送和接收数据都是封装在 spi_message 消息结构体中的
#inlude <linux/spi/spi.h>
struct spi_message {
struct list_head transfers; /* spi 数据传输结构体链表头部节点 */
struct spi_device *spi; /* 对应的 spi 设备结构体对象 */
unsigned is_dma_mapped:1;
/* REVISIT: we might want a flag affecting the behavior of the
* last transfer ... allowing things like "read 16 bit length L"
* immediately followed by "read L bytes". Basically imposing
* a specific message scheduling algorithm.
*
* Some controller drivers (message-at-a-time queue processing)
* could provide that as their default scheduling algorithm. But
* others (with multi-message pipelines) could need a flag to
* tell them about such special cases.
*/
/* 下面和是异步传输回调函数相关的成员 */
/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue;
void *state;
};
1.1.5 struct spi_driver spi 驱动结构体
#include <linux/spi/spi.h>
struct spi_driver {
const char *name; // 驱动程序名称
const char *modalias; // 驱动程序的别名,可选
unsigned int bus_type; // 总线类型,如 SPI_BUS_TYPE_SPI
int max_speed_hz; // 最大传输速率,单位为 Hz
int max_queue_len; // 最大队列长度
struct device_driver *drv; // 继承的驱动程序实例
struct bus_type *bus; // SPI 总线实例
struct device_node *of_node; // 设备树节点
const struct fwnode_handle *fwnode; // Firmware 节点
const char *modalias_of_node; // 设备树节点的驱动程序别名
void (*probe)(struct spi_device *); // 设备探测回调函数
void (*remove)(struct spi_device *); // 设备删除回调函数
void (*shutdown)(struct spi_device *); // 设备关闭回调函数
void (*suspend)(struct spi_device *); // 设备挂起回调函数
void (*resume)(struct spi_device *); // 设备恢复回调函数
void (*init)(struct spi_device *); // 设备初始化回调函数
struct list_head entry; // 设备列表
unsigned int num_devices; // 设备数量
};
1.1.n struct spi_ioc_transfer 应用层 spi 通讯结构体
#include <linux/spi/spidev.h>
struct spi_ioc_transfer {
__u64 tx_buf; //发送数据缓存
__u64 rx_buf; //接收数据缓存
__u32 len; //数据长度(以字节为单位的)
__u32 speed_hz; //通讯速率
__u16 delay_usecs; //两个spi_ioc_transfer之间的延时,微秒
__u8 bits_per_word; //数据长度
__u8 cs_change; //取消选中片选
__u8 tx_nbits; //单次数据宽度(多数据线模式)
__u8 rx_nbits; //单次数据宽度(多数据线模式)
__u16 pad;
};
1.2 spi_register_driver 注册 SPI 驱动
#include <linux/spi.h>
int spi_register_driver(sturct spi_driver *sdrv);
· int :执行成功返回 0,执行失败返回负数
· sturct spi_driver *sdrv :申请注册的 SPI 驱动结构体对象
1.3 spi_unregister_driver 注销 SPI 驱动
#include <linux/spi.h>
static inline void spi_unregister_driver(sturct spi_driver *sdrv);
· sturct spi_driver *sdrv :申请注册的 SPI 驱动结构体对象
1.4 spi_setup 设置 spi
· spi_setup 函数用于设置 spi 的传输速率,带宽和片选信号等
#include <linux/spi.h>
int spi_setup(struct spi_device *spi)
· int : 执行成功返回 0,执行失败返回负值
· struct spi_device *spi : spi spi_device spi设备结构体
1.5 spi_message_init 消息结构体初始化
#include <linux/spi.h>
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m); /* 清空内存操作 */
spi_message_init_no_memset(m); /* 初始化结构体的接口 */
}
· struct spi_message *m : 要初始化的消息结构体指针对象
1.6 spi_message_add_tail 添加传输结构体到消息结构体对象中
· 将 spi_transfer 结构体添加到 spi_message 队列的末尾。
#include <linux/spi.h>
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
list_add_tail(&t->transfer_list, &m->transfers);
}
· struct spi_transfer *t : 要添加的数据传输结构体的指针对象
· struct spi_message *m : 操作的消息结构体指针指针对象
1.7 spi_sync spi 同步传输
· 阻塞当前进程,直至将 message 中的数据传输完成才结束阻塞
#include <linux/spi.h>
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
int ret;
mutex_lock(&spi->controller->bus_lock_mutex);
ret = __spi_sync(spi, message);
mutex_unlock(&spi->controller->bus_lock_mutex);
return ret;
}
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
int status;
struct spi_controller *ctlr = spi->controller;
unsigned long flags;
status = __spi_validate(spi, message);
if (status != 0)
return status;
message->complete = spi_complete;
message->context = &done;
message->spi = spi;
...
if (ctlr->transfer == spi_queued_transfer) {
spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);
trace_spi_message_submit(message);
status = __spi_queued_transfer(spi, message, false);
spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
} else {
status = spi_async_locked(spi, message);
}
if (status == 0) {
...
wait_for_completion(&done);
status = message->status;
}
message->context = NULL;
return status;
}
· struct spi_message *m : 操作的消息结构体指针指针对象
· struct spi_device *spi : spi spi_device spi设备结构体
1.8 spi_async spi 异步传输
· 异步传输不会阻塞当前进程,但内核态又要怎么在 spi 异步通讯完成后去通知用户空间呢?
· struct spi_message 在 spi 的消息结构体中你可以看到下面这俩个成员
· void (*complete)(void *context);
· void *context;
· 当 spi 异步传输完成后,内核会执行 complete 这个回调函数,而 context 是 complete 回调函数的参数
· 所以只需要自定义 complete 类型的回调函数供内核调用,在回调函数中编写通知用户态的内容即可
#include <linux/spi.h>
int spi_async(struct spi_device *spi, struct spi_message *message)
{
...
ret = __spi_async(spi, message);
...
}
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_controller *ctlr = spi->controller;
...
return ctlr->transfer(spi, message);
}
· struct spi_message *m : 操作的消息结构体指针指针对象
· struct spi_device *spi : spi spi_device spi设备结构体