spidev.c文件

本文深入解析Linux设备驱动中的spidev.c,介绍了spidev_data结构体、同步读写函数以及中断处理流程。spidev作为通用SPI设备驱动,通过file_operations接口使应用程序能通过设备节点访问驱动,数据流从应用程序经SPI核心到控制器模块。文章详细阐述了spidev的初始化、匹配、传输和中断处理过程。
摘要由CSDN通过智能技术生成

设备驱动是在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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值