设备驱动是在core之上的模块,向上给应用程序提供file_operations接口,应用程序可以通过设备节点访问驱动程序,向下通过core向控制器模块发送数据,控制器模块将数据发送到物理总线上。
spidev.c是一个典型的设备驱动程序,前面提到在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,目的是最大程度保证代码的可移植性,我们以应用程序调用为主线,详细分析spi驱动的数据流流向。
SPI主控制器驱动,也就是SPI总线驱动,既然总线有了,根据Linux设备驱动模型,还得有SPI设备和SPI设备驱动。SPI设备是在板级文件中注册,SPI设备驱动需要用户自己实现,好在内核为我们提供了一个通用的SPI设备驱动spidev.c。
看一个设备驱动的方法:
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations—>spidev_fops
spidev.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <asm/uaccess.h>
#define SPIDEV_MAJOR 153 //spidev主设备号
#define N_SPI_MINORS 32 /* ... up to 256 */
static DECLARE_BITMAP(minors, N_SPI_MINORS); //声明次设备位图
#define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)
struct spidev_data
struct spidev_data {
dev_t devt; //设备号
spinlock_t spi_lock; //自旋锁
struct spi_device *spi; //spi设备结构体
struct list_head device_entry; //设备链表,所有采用此驱动的设备将连成一个链表
struct mutex buf_lock; //互斥锁
unsigned users; //使用者计数 也即是此设备被open的次数
u8 *buffer; //缓冲区
};
static LIST_HEAD(device_list); //声明spi设备链表
static DEFINE_MUTEX(device_list_lock); //定义互斥锁
static unsigned bufsiz = 4096; //最大传输缓冲区大小
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
spidev_complete
static void spidev_complete(void *arg)
{
complete(arg); //调用complete
}
spidev_sync
static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete; //设置spi消息的complete方法 回调函数
message->context = &done;
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL) //判断是否有指定对应的spi设备
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message); //spi异步同步
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done); //等待传输完成
status = message->status; //获取spi消息传输事务状态
if (status == 0)
status = message->actual_length; //status等于传输的实际长度
}
return status; //返回实际传输长度
}
spidev_sync_write
static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.tx_buf = spidev->buffer, //发送缓冲区
.len = len, //发送数据长度
};
struct spi_message m;
spi_message_init(&m); //初始化spi消息(初始化spi传递事务队列)
spi_message_add_tail(&t, &m); //添加spr传递到该队列
return spidev_sync(spidev, &m); //同步读写
}
spidev_sync_read
static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->buffer, //接收缓冲区
.len = len, //接收数据长度
};
struct spi_message m;
spi_message_init(&m); //初始化spi消息(初始化spi传递事务队列)
spi_message_add_tail(&t, &m); //添加spr传递到该队列
return spidev_sync(spidev, &m); //同步读写
}
spidev_read
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
if (count > bufsiz) //传输数据大于缓冲区容量
return -EMSGSIZE;
spidev = filp->private_data; //从文件私有数据指针获取spidev_data
mutex_lock(&spidev->buf_lock); //上互斥锁
status = spidev_sync_read(spidev, count); //同步读,返回传输数据长度
if (status > 0) {
unsigned long missing; //丢失的数据个数
missing = copy_to_user(buf, spidev->buffer, status); //内核空间复制到用户空间
if (missing == status) //丢失的数据个数等于要传输的数据个数
status = -EFAULT;
else
status = status - missing; //传输成功的数据个数
}
mutex_unlock(&spidev->buf_lock);//解互斥锁
return status; //返回读取成功的数据个数
}
spidev_sync_read函数
static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m); //初始化spi_message
spi_message_add_tail(&t, &m); //将spi_transfer添加到spi_message的transfers
return spidev_sync(spidev, &m); //见下面分析
}
spidev_sync函数
static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete; //消息完成调用函数
message->context = &done; //消息完成调用函数参数
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL) //没有对应的spi_device则出错返回,最终依赖于spi_device进行数据收发
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message); //异步传输函数,位于spi.c中,见下面分析
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done); //等待消息完成
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
spi_async函数
int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
if ((master->flags & SPI_MASTER_HALF_DUPLEX) //半双工?
|| (spi->mode & SPI_3WIRE)) {
struct spi_transfer *xfer;
unsigned flags = master->flags;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
if (xfer->rx_buf && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
return -EINVAL;
}
}
message->spi = spi;
message->status = -EINPROGRESS;
return master->transfer(spi, message); //最终调用spi_master的transfer函数
}
spi_bitbang_transfer
通过前面控制器驱动中spi_bitbang_start函数分析,我们知道spi_master的transfer函数其实是spi_bitbang_transfer,该函数不执行数据的具体发送,仅仅是将message消息添加到spi_bitbang的队列中:
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
struct spi_bitbang *bitbang;
unsigned long flags;
int status = 0;
m->actual_length = 0;
m->status = -EINPROGRESS;
//得到sp