编写SPI设备驱动程序
把大象放进冰箱,总共分三步:
编写设备树
- 查看原理图,确定你的SPI设备挂在哪个SPI总线下面,去构造一个dts设备树,比如添加一个子节点。
- 以下面tlc5615为例,你需要清楚SCK/MOSI/MISO/CS的引脚。也需要查看数据手册,这款芯片SPI时钟信号支持的最大时钟频率。
下面是设备树的信息。
注册 spi_driver
- 子节点会被转换成一个 spi_device
- 对于相应的spi_device,需要编写一个spi_driver,并注册这个spi_driver。
- spi_driver中的of_match_table表示支持的设备,内容是为了去和dts中的compatible变量去匹配,同时其中的probe函数可以去注册字符设备程序,调用register_chrdev。
- 字符设备程序中,核心是file_operation结构体,结构体中会有open/write/read/ioctl功能函数定义。
static const struct of_device_id dac_of_match[] = {
{.compatible = "fairchild,74hc595"},
{}
};
static struct spi_driver dac_driver = {
.driver = {
.name = "dac",
.of_match_table = dac_of_match,
},
.probe = dac_probe,
.remove = dac_remove,
//.id_table = dac_spi_ids,
};
发起SPI传输
对于SPI传输,功能无非就是写和读,在Linux内核的spi头文件中,对读写的接口函数进行了定义和说明
- 同步写
/**
* spi_write - SPI synchronous write
* @spi: device to which data will be written
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function writes the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)//spi:写哪个设备
{
struct spi_transfer t = {
.tx_buf = buf,//需要写的数据内容
.len = len,//写数据的长度
};
return spi_sync_transfer(spi, &t, 1);//成功返回0,失败返回err code
}
函数最终返回时,调用了 spi_sync_transfer 函数。
所以大家关注的最终核心的函数是spi_sync,这是一个同步、阻塞的SPI传输函数,返回要么成功,要么失败。此外还有spi_async函数,对应的是异步的SPI传输函数,简单地说就是这个函数即刻返回,它返回后SPI传输不一定已经完成。
- spi_sync
无法获得锁时,mutex会选择挂起当前线程,进入阻塞状态。所以,mutex是无法在中断上下文使用。 - spi_async
和spi_sync相比,spi_async这个函数可以在_irq和其他不能休眠的上下文中使用,也可以在可以休眠的任务上下文中使用。此外- 完成SPI传输后,回调函数被调用,它是在"无法休眠的上下文"中被调用的,所以回调函数里不能有休眠操作。
- 在回调函数被调用前message->statuss是未定义的值,没有意义。
- 当回调函数被调用时,就可以根据message->status判断结果: 0-成功,负数表示失败码
- 当回调函数执行完后,驱动程序要认为message等结构体已经被释放,不能再使用它们。
- 在传输过程中一旦发生错误,整个message传输都会中止,对spi设备的片选被取消。
核心的数据结构
上面将函数读写的上下层调用关系简单说明,其中,有个重要的数据结构,就是 struct spi_message *message。spi_message中存放的transfer链表,是由多个spi_transfer结构体组成,链表通过上图中的spi_message_add_tail函数,将传输的buff放入链表尾部。
spi_message_add_tail函数:
所以spi_transfer是我们传输数据的最基本单位。
那么,以同步写函数为例,主要的流程包括4步:
- 构造spi_transfer结构体和内容
- init spi_message
- 将spi_transfer加入到spi_message尾部
- 调用spidev_sync发送
还有其他的函数实现,在这里简单罗列,读者可以根据内核源码自行查找,还包括:
- 同步读
/**
* spi_read - SPI synchronous read
* @spi: device from which data will be read
* @buf: data buffer
* @len: data buffer size
* Context: can sleep
*
* This function reads the buffer @buf.
* Callable only from contexts that can sleep.
*
* Return: zero on success, else a negative error code.
*/
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)//spi:读哪个设备
{
struct spi_transfer t = {
.rx_buf = buf,//读到的数据
.len = len,//数据长度
};
return spi_sync_transfer(spi, &t, 1);//成功返回0,失败返回err code
}
- 先写再读
- 这个函数是一个半双工操作,先发送txbuf中的数据,在读数据,将读到的数据放到rxbuf中
- 这个函数用来传输少量数据(建议不要操作32字节), 它的效率不高
- 返回值: 0-成功, 负数-失败码
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,//读哪个设备
const void *txbuf, unsigned n_tx,//txbuf:发送buffer,n_tx:发送多少字节
void *rxbuf, unsigned n_rx);//rxbuf:接收buffer,n_tx:接收多少字节
- 写8位读8位
/**
* spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read
* @spi: device with which data will be exchanged
* @cmd: command to be written before data is read back
* Context: can sleep
*
* Callable only from contexts that can sleep.
*
* Return: the (unsigned) eight bit number returned by the
* device, or else a negative error code.
*/
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
{
ssize_t status;
u8 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 1);
/* return negative errno or unsigned value */
return (status < 0) ? status : result; //返回值: 成功的话返回一个8位数据(unsigned), 负数表示失败码
}
- 写8位读16位
/**
* spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read
* @spi: device with which data will be exchanged
* @cmd: command to be written before data is read back
* Context: can sleep
*
* The number is returned in wire-order, which is at least sometimes
* big-endian.
*
* Callable only from contexts that can sleep.
*
* Return: the (unsigned) sixteen bit number returned by the
* device, or else a negative error code.
*/
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
{
ssize_t status;
u16 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 2);
/* return negative errno or unsigned value */
return (status < 0) ? status : result;//返回值: 成功的话返回一个16位数据(unsigned, 被转换为本地字节序), 负数表示失败码
}