一.spidev.c文件
看一个设备驱动的方法:
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
@@open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:
spidev->users++; //spidev_data使用者计数++
filp->private_data = spidev; //spidev_data放在文件的私有数据里
nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
@@read方法spidev_read
spidev = filp->private_data;=========>>status = spidev_sync_read(spidev, count);===========>>
spidev_sync(spidev, &m);==========>>status = spi_async(spidev->spi, message);===========>>
wait_for_completion(&done);========>>到了这一步是重点,在spi_async()方法中,使用以下语句将要做的事情加到workqueue中
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
此后所有的处理程序便转移到在之前初始化的work方法中看以下代码:
点击(此处)折叠或打开
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
int do_setup = -1;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
while (!list_empty(&bitbang->queue)) {
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
unsigned tmp;
unsigned cs_change;
int status;
m = container_of(bitbang->queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
list_for_each_entry (t, &m->transfers, transfer_list) {
/* override speed or wordsize? */
if (t->speed_hz || t->bits_per_word)
do_setup = 1;
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t);
if (status < 0)
break;
}
/* set up default clock polarity, and activate chip;
* this implicitly updates clock and spi modes as
* previously recorded for this device via setup().
* (and also deselects any other chip that might be
* selected ...)
*/
if (cs_change) {
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
ndelay(nsecs);
}
cs_change = t->cs_change;
if (!t->tx_buf && !t->rx_buf && t->len) {
status = -EINVAL;
break;
}
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
if (t->len) {
/* REVISIT dma API still needs a designated
* DMA_ADDR_INVALID; ~0 might be better.
*/
if (!m->is_dma_mapped)
t->rx_dma = t->tx_dma = 0;
status = bitbang->txrx_bufs(spi, t);
}
if (status > 0)
m->actual_length += status;
if (status != t->len) {
/* always report some kind of error */
if (status >= 0)
status = -EREMOTEIO;
break;
}
status = 0;
/* protocol tweaks before next transfer */
if (t->delay_usecs)
udelay(t->delay_usecs);
if (!cs_change)
continue;
if (t->transfer_list.next == &m->transfers)
break;
/* sometimes a short mid-message deselect of the chip
* may be needed to terminate a mode or command
*/
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
m->status = status;
m->complete(m->context);
/* restore speed and wordsize if it was overridden */
if (do_setup == 1)
setup_transfer(spi, NULL);
do_setup = 0;
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
*/
if (!(status == 0 && cs_change)) {
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
spin_lock_irqsave(&bitbang->lock, flags);
}
bitbang->busy = 0;
spin_unlock_irqrestore(&bitbang->lock, flags);
}
结束处理所有任务后,见上面红色底纹部分解除wait_for_completion(&done);
最后missing = copy_to_user(buf, spidev->buffer, status);将数据发送到用户空间
@@write方法spidev_write
与上面open方式基本相同
@@ioctl方法spidev_ioctl
具体的详解见下面章节(三,四)
下面是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 {
dev_t devt; //设备号
spinlock_t spi_lock; //自旋锁
struct spi_device *spi; //spi设备结构体
struct list_head device_entry;
struct mutex buf_lock; //互斥锁
unsigned users; //使用者计数
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");
static void spidev_complete(void *arg)
{
complete(arg); //调用complete
}
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; //返回实际传输长度
}
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); //同步读写
}
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); //同步读写
}
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; //返回读取成功的数据个数
}
static ssize_t spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
unsigned long missing;
if (count > bufsiz) //传输数据大于缓冲区容量
return -EMSGSIZE;
spidev = filp->private_data; //从文件私有数据指针获取spidev_data
mutex_lock(&spidev->buf_lock); //上互斥锁
missing = copy_from_user(spidev->buffer, buf, count); //用户空间复制到内核空间
if (missing == 0) { //传输失败个数为0
status = spidev_sync_write(spidev, count); //同步写,返回传输数据长度
}
else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);//解互斥锁
return status; //返回写数据的实际个数
}
static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total;
u8 *buf;
int status = -EFAULT;
spi_message_init(&msg); //初始化spi消息(初始化spi传递事务队列)
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi传输指针内存
if (k_xfers == NULL)
return -ENOMEM;
buf = spidev->buffer; //获取spidev_data的缓冲区
total = 0;
//n=xfers为spi_ioc_transfer个数,u_tmp = u_xfers为要处理的spi_ioc_transfer指针
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {
k_tmp->len = u_tmp->len; //设置传输信息的长度
total += k_tmp->len; //累加传输信息的总长度
if (total > bufsiz) { //信息量超过bufsiz缓冲区最大容量
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) { //接收缓冲区指针不为空
k_tmp->rx_buf = buf; //缓冲区指向buf
if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))
goto done;
}
if (u_tmp->tx_buf) { //发送缓冲区指针不为空
k_tmp->tx_buf = buf; //缓冲区指针指向buf
if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) //用户空间复制数据到buf
goto done;
}
buf += k_tmp->len; //缓冲区指针移动一个传输信息的长度
k_tmp->cs_change = !!u_tmp->cs_change; //设置cs_change
k_tmp->bits_per_word = u_tmp->bits_per_word; //设置bits_per_word 一个字多少位
k_tmp->delay_usecs = u_tmp->delay_usecs; //设置delay_usecs 毫秒级延时
k_tmp->speed_hz = u_tmp->speed_hz; //设置speed_hz 速率
#ifdef VERBOSE
dev_dbg(&spidev->spi->dev," xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg); //添加spr传递到该队列
}
//for循环的作用是将spi_ioc_transfer批量转换为spi传递结构体spi_transfer,然后添加进spi传递事务队列
status = spidev_sync(spidev, &msg); //同步读写
if (status < 0)
goto done;
buf = spidev->buffer; //获取spidev_data缓冲区指针
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { //批量从内核空间复制spi_ioc_transfer到用户空间
if (u_tmp->rx_buf) { //判断是否存在接收缓冲区
if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {
status = -EFAULT;
goto done;
}
}
buf += u_tmp->len; //buf指针位置调整指向下一个spi_ioc_transfer
}
status = total; //status等于实际传输的数据长度
done:
kfree(k_xfers); //释放k_xfers
return status; //返回实际传输的数据长度
}
static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) //判断控制命令的类型
return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_READ) //判断控制命令的方向是否为读read
err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判断控制命令的方向是否为写write
err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err)
return -EFAULT;
spidev = filp->private_data; //从文件私有数据中获取spidev_data
spin_lock_irq(&spidev->spi_lock); //上自旋锁
spi = spi_dev_get(spidev->spi); //获取spi设备
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
if (spi == NULL) //获取spi设备失败
return -ESHUTDOWN; //则返回错误
mutex_lock(&spidev->buf_lock); //上互斥锁
switch (cmd) {
case SPI_IOC_RD_MODE: //设置spi读模式 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST: //设置spi读最低有效位 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD: //设置spi读每个字含多个个位 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ: //设置spi读最大速率 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
case SPI_IOC_WR_MODE: //设置spi写模式
retval = __get_user(tmp, (u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u8)tmp;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST: //设置spi写最低有效位
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "%csb first\n",tmp ? 'l' : 'm');
}
break;
case SPI_IOC_WR_BITS_PER_WORD: //设置spi写每个字含多个个位
retval = __get_user(tmp, (__u8 __user *)arg); //用户空间获取数据
if (retval == 0) {
u8 save = spi->bits_per_word; //获取spi设备 每个字含多少位
spi->bits_per_word = tmp; //更新新的spi设备 每个字含多少位
retval = spi_setup(spi); //配置spi设备
if (retval < 0) //配置失败
spi->bits_per_word = save; //还原spi设备 每个字含多少位
else
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: //设置spi写最大速率
retval = __get_user(tmp, (__u32 __user *)arg); //用户空间获取数据
if (retval == 0) {
u32 save = spi->max_speed_hz; //获取spi设备最大速率
spi->max_speed_hz = tmp; //更新新的spi设备最大速率
retval = spi_setup(spi); //配置spi设备
if (retval < 0) //配置失败
spi->max_speed_hz = save; //还原spi设备最大速率
else
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
}
break;
default:
//命令必须为写方向的命令,且传输数据必须是SPI_IOC_MESSAGE()修饰的命令
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //计算传输数据大小
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { //判断是否为spi_ioc_transfer对齐
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出spi_ioc_transfer数据的个数
if (n_ioc == 0)
break;
ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指针ioc内存
if (!ioc) {
retval = -ENOMEM;
break;
}
if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //从用户空间复制到内核空间
kfree(ioc); //复制失败则释放ioc内存
retval = -EFAULT;
break;
}
retval = spidev_message(spidev, ioc, n_ioc); //spidev消息处理
kfree(ioc); //释放ioc内存
break;
}
mutex_unlock(&spidev->buf_lock); //解互斥锁
spi_dev_put(spi); //增加spi设备的引用计数
return retval;
}
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;
mutex_lock(&device_list_lock); //上互斥锁
list_for_each_entry(spidev, &device_list, device_entry) { //遍历device_list
if (spidev->devt == inode->i_rdev) { //判断设备号找到对应的设备
status = 0; //设置状态为0
break;
}
}
if (status == 0) { //找得到对应的设备
if (!spidev->buffer) { //spidev_data缓冲区为空
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); //则分配内存
if (!spidev->buffer) { //还空
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); //申请内存失败
status = -ENOMEM;
}
}
if (status == 0) { //找得到对应的设备
spidev->users++; //spidev_data使用者计数++
filp->private_data = spidev; //spidev_data放在文件的私有数据里
nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
}
}
else
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock); //接互斥锁
return status;
}
static int spidev_release(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = 0;
mutex_lock(&device_list_lock);
spidev = filp->private_data; //获取spidev_data
filp->private_data = NULL; //清除文件的私有数据指针
spidev->users--; //使用者个数--
if (!spidev->users) { //如果使用者个数为0
int dofree;
kfree(spidev->buffer); //释放spidev_data的缓冲区内存
spidev->buffer = NULL; //清除spidev_data缓冲区指针
spin_lock_irq(&spidev->spi_lock); //上自旋锁
dofree = (spidev->spi == NULL); //判断spi设备是否与spidev_data解绑了
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
if (dofree) //没有捆绑的spi设备
kfree(spidev); //则是否spidev_data内存
}
mutex_unlock(&device_list_lock);
return status;
}
static const struct file_operations spidev_fops = { //文件操作函数集
.owner = THIS_MODULE,
.write = spidev_write, //写write
.read = spidev_read, //读read
.unlocked_ioctl = spidev_ioctl, //控制ioctl
.open = spidev_open, //打开open
.release = spidev_release, //释放release
.llseek = no_llseek, //文件指针移动 no_llseek表示没有移动
};
static struct class *spidev_class;
static int __devinit spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); //分配spidev_data内存
if (!spidev)
return -ENOMEM;
spidev->spi = spi; //设置spidev_data->spi(spi设备)
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry); //初始化spidev_data入口链表
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS); //查找次设备位图分配次设备号
if (minor < N_SPI_MINORS) {
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //计算出设备号
//创建设备/dev/spidev%d.%d(spidev总线号.片选号)
dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
}
else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) { //分配设备号成功
set_bit(minor, minors); //更新次设备位图
list_add(&spidev->device_entry, &device_list); //添加进设备链表
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev); //spi->dev->p->driver_data=spidev
else
kfree(spidev);
return status;
}
static int __devexit spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi); //根据spi设备获取spidev_data
spin_lock_irq(&spidev->spi_lock); //上自旋锁
spidev->spi = NULL; //清空spidev_data->spi指针
spi_set_drvdata(spi, NULL); //spi->dev->p->driver_data=NULL
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
mutex_lock(&device_list_lock); //上互斥锁
list_del(&spidev->device_entry); //删除spidev_data入口链表
device_destroy(spidev_class, spidev->devt); //销毁/dev/spidev%d.%d
clear_bit(MINOR(spidev->devt), minors); //清除次设备位图对应位
if (spidev->users == 0) //使用者个数为0
kfree(spidev); //释放spidev_data内存
mutex_unlock(&device_list_lock); //解互斥锁
return 0;
}
static struct spi_driver spidev_spi_driver = { //spi设备驱动
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe, //spidev的probe方法(当注册了modalias域为"spidev"的spi设备或板级设备,则会调用probe方法)
.remove = __devexit_p(spidev_remove), //spidev的remove方法
};
static int __init spidev_init(void) //spidev接口初始化
{
int status;
BUILD_BUG_ON(N_SPI_MINORS > 256);
//注册字符设备,主设备号SPIDEV_MAJOR=153,捆绑的设备操作函数集为spidev_fops
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); //创建设备类spidev_class
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); //注册spi设备驱动spidev_spi_driver
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init); //声明初始化入口
static void __exit spidev_exit(void) //spidev接口销毁
{
spi_unregister_driver(&spidev_spi_driver); //注销spi设备驱动spidev_spi_driver
class_destroy(spidev_class); //注销设备类spidev_class
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); //注销字符设备
}
module_exit(spidev_exit); //声明初始化出口
MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:spidev");
二.用户空间例子(spidev_test.c)
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static void transfer(int fd)
{
int ret;
uint8_t tx[] = { //要发送的数据数组
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //接收的数据数据
struct spi_ioc_transfer tr = { //声明并初始化spi_ioc_transfer结构体
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
//SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默认操作,传输数据
if (ret < 1)
pabort("can't send spi message");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收缓冲区
if (!(ret % 6)) //6个数据为一簇打印
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
static void print_usage(const char *prog) //参数错误则打印帮助信息
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = { //参数命令表
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D': //设备名
device = optarg;
break;
case 's': //速率
speed = atoi(optarg);
break;
case 'd': //延时时间
delay = atoi(optarg);
break;
case 'b': //每字含多少位
bits = atoi(optarg);
break;
case 'l': //回送模式
mode |= SPI_LOOP;
break;
case 'H': //时钟相位
mode |= SPI_CPHA;
break;
case 'O': //时钟极性
mode |= SPI_CPOL;
break;
case 'L': //lsb 最低有效位
mode |= SPI_LSB_FIRST;
break;
case 'C': //片选高电平
mode |= SPI_CS_HIGH;
break;
case '3': //3线传输模式
mode |= SPI_3WIRE;
break;
case 'N': //没片选
mode |= SPI_NO_CS;
break;
case 'R': //从机拉低电平停止数据传输
mode |= SPI_READY;
break;
default: //错误的参数
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv); //解析传递进来的参数
fd = open(device, O_RDWR); //打开设备文件
if (fd < 0)
pabort("can't open device");
/*
* spi mode //设置spi设备模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); //写模式
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); //读模式
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word //设置每个字含多少位
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); //写 每个字含多少位
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); //读 每个字含多少位
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz //设置速率
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); //写速率
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); //读速率
if (ret == -1)
pabort("can't get max speed hz");
//打印模式,每字多少位和速率信息
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
transfer(fd); //传输测试
close(fd); //关闭设备
return ret;
}
这里整理下ioctl的命令:
SPI_IOC_RD_MODE //读 模式
SPI_IOC_RD_LSB_FIRST //读 LSB
SPI_IOC_RD_BITS_PER_WORD //读 每字多少位
SPI_IOC_RD_MAX_SPEED_HZ //读 最大速率
SPI_IOC_WR_MODE //写 模式
SPI_IOC_WR_LSB_FIRST //写 LSB
SPI_IOC_WR_BITS_PER_WORD //写 每字多少位
SPI_IOC_WR_MAX_SPEED_HZ //写 最大速率
SPI_IOC_MESSAGE(n) //传输n个数据包
看一个设备驱动的方法:
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
@@open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:
spidev->users++; //spidev_data使用者计数++
filp->private_data = spidev; //spidev_data放在文件的私有数据里
nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
@@read方法spidev_read
spidev = filp->private_data;=========>>status = spidev_sync_read(spidev, count);===========>>
spidev_sync(spidev, &m);==========>>status = spi_async(spidev->spi, message);===========>>
wait_for_completion(&done);========>>到了这一步是重点,在spi_async()方法中,使用以下语句将要做的事情加到workqueue中
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
此后所有的处理程序便转移到在之前初始化的work方法中看以下代码:
点击(此处)折叠或打开
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
int do_setup = -1;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
while (!list_empty(&bitbang->queue)) {
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
unsigned tmp;
unsigned cs_change;
int status;
m = container_of(bitbang->queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
list_for_each_entry (t, &m->transfers, transfer_list) {
/* override speed or wordsize? */
if (t->speed_hz || t->bits_per_word)
do_setup = 1;
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t);
if (status < 0)
break;
}
/* set up default clock polarity, and activate chip;
* this implicitly updates clock and spi modes as
* previously recorded for this device via setup().
* (and also deselects any other chip that might be
* selected ...)
*/
if (cs_change) {
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
ndelay(nsecs);
}
cs_change = t->cs_change;
if (!t->tx_buf && !t->rx_buf && t->len) {
status = -EINVAL;
break;
}
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
if (t->len) {
/* REVISIT dma API still needs a designated
* DMA_ADDR_INVALID; ~0 might be better.
*/
if (!m->is_dma_mapped)
t->rx_dma = t->tx_dma = 0;
status = bitbang->txrx_bufs(spi, t);
}
if (status > 0)
m->actual_length += status;
if (status != t->len) {
/* always report some kind of error */
if (status >= 0)
status = -EREMOTEIO;
break;
}
status = 0;
/* protocol tweaks before next transfer */
if (t->delay_usecs)
udelay(t->delay_usecs);
if (!cs_change)
continue;
if (t->transfer_list.next == &m->transfers)
break;
/* sometimes a short mid-message deselect of the chip
* may be needed to terminate a mode or command
*/
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
m->status = status;
m->complete(m->context);
/* restore speed and wordsize if it was overridden */
if (do_setup == 1)
setup_transfer(spi, NULL);
do_setup = 0;
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
*/
if (!(status == 0 && cs_change)) {
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
spin_lock_irqsave(&bitbang->lock, flags);
}
bitbang->busy = 0;
spin_unlock_irqrestore(&bitbang->lock, flags);
}
结束处理所有任务后,见上面红色底纹部分解除wait_for_completion(&done);
最后missing = copy_to_user(buf, spidev->buffer, status);将数据发送到用户空间
@@write方法spidev_write
与上面open方式基本相同
@@ioctl方法spidev_ioctl
具体的详解见下面章节(三,四)
下面是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 {
dev_t devt; //设备号
spinlock_t spi_lock; //自旋锁
struct spi_device *spi; //spi设备结构体
struct list_head device_entry;
struct mutex buf_lock; //互斥锁
unsigned users; //使用者计数
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");
static void spidev_complete(void *arg)
{
complete(arg); //调用complete
}
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; //返回实际传输长度
}
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); //同步读写
}
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); //同步读写
}
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; //返回读取成功的数据个数
}
static ssize_t spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
unsigned long missing;
if (count > bufsiz) //传输数据大于缓冲区容量
return -EMSGSIZE;
spidev = filp->private_data; //从文件私有数据指针获取spidev_data
mutex_lock(&spidev->buf_lock); //上互斥锁
missing = copy_from_user(spidev->buffer, buf, count); //用户空间复制到内核空间
if (missing == 0) { //传输失败个数为0
status = spidev_sync_write(spidev, count); //同步写,返回传输数据长度
}
else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);//解互斥锁
return status; //返回写数据的实际个数
}
static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total;
u8 *buf;
int status = -EFAULT;
spi_message_init(&msg); //初始化spi消息(初始化spi传递事务队列)
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi传输指针内存
if (k_xfers == NULL)
return -ENOMEM;
buf = spidev->buffer; //获取spidev_data的缓冲区
total = 0;
//n=xfers为spi_ioc_transfer个数,u_tmp = u_xfers为要处理的spi_ioc_transfer指针
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {
k_tmp->len = u_tmp->len; //设置传输信息的长度
total += k_tmp->len; //累加传输信息的总长度
if (total > bufsiz) { //信息量超过bufsiz缓冲区最大容量
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) { //接收缓冲区指针不为空
k_tmp->rx_buf = buf; //缓冲区指向buf
if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))
goto done;
}
if (u_tmp->tx_buf) { //发送缓冲区指针不为空
k_tmp->tx_buf = buf; //缓冲区指针指向buf
if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) //用户空间复制数据到buf
goto done;
}
buf += k_tmp->len; //缓冲区指针移动一个传输信息的长度
k_tmp->cs_change = !!u_tmp->cs_change; //设置cs_change
k_tmp->bits_per_word = u_tmp->bits_per_word; //设置bits_per_word 一个字多少位
k_tmp->delay_usecs = u_tmp->delay_usecs; //设置delay_usecs 毫秒级延时
k_tmp->speed_hz = u_tmp->speed_hz; //设置speed_hz 速率
#ifdef VERBOSE
dev_dbg(&spidev->spi->dev," xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg); //添加spr传递到该队列
}
//for循环的作用是将spi_ioc_transfer批量转换为spi传递结构体spi_transfer,然后添加进spi传递事务队列
status = spidev_sync(spidev, &msg); //同步读写
if (status < 0)
goto done;
buf = spidev->buffer; //获取spidev_data缓冲区指针
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { //批量从内核空间复制spi_ioc_transfer到用户空间
if (u_tmp->rx_buf) { //判断是否存在接收缓冲区
if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {
status = -EFAULT;
goto done;
}
}
buf += u_tmp->len; //buf指针位置调整指向下一个spi_ioc_transfer
}
status = total; //status等于实际传输的数据长度
done:
kfree(k_xfers); //释放k_xfers
return status; //返回实际传输的数据长度
}
static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) //判断控制命令的类型
return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_READ) //判断控制命令的方向是否为读read
err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判断控制命令的方向是否为写write
err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err)
return -EFAULT;
spidev = filp->private_data; //从文件私有数据中获取spidev_data
spin_lock_irq(&spidev->spi_lock); //上自旋锁
spi = spi_dev_get(spidev->spi); //获取spi设备
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
if (spi == NULL) //获取spi设备失败
return -ESHUTDOWN; //则返回错误
mutex_lock(&spidev->buf_lock); //上互斥锁
switch (cmd) {
case SPI_IOC_RD_MODE: //设置spi读模式 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST: //设置spi读最低有效位 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD: //设置spi读每个字含多个个位 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ: //设置spi读最大速率 (此处原作者的理解与我不同,这里应该是应用程序获取数据)
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
case SPI_IOC_WR_MODE: //设置spi写模式
retval = __get_user(tmp, (u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u8)tmp;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST: //设置spi写最低有效位
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "%csb first\n",tmp ? 'l' : 'm');
}
break;
case SPI_IOC_WR_BITS_PER_WORD: //设置spi写每个字含多个个位
retval = __get_user(tmp, (__u8 __user *)arg); //用户空间获取数据
if (retval == 0) {
u8 save = spi->bits_per_word; //获取spi设备 每个字含多少位
spi->bits_per_word = tmp; //更新新的spi设备 每个字含多少位
retval = spi_setup(spi); //配置spi设备
if (retval < 0) //配置失败
spi->bits_per_word = save; //还原spi设备 每个字含多少位
else
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: //设置spi写最大速率
retval = __get_user(tmp, (__u32 __user *)arg); //用户空间获取数据
if (retval == 0) {
u32 save = spi->max_speed_hz; //获取spi设备最大速率
spi->max_speed_hz = tmp; //更新新的spi设备最大速率
retval = spi_setup(spi); //配置spi设备
if (retval < 0) //配置失败
spi->max_speed_hz = save; //还原spi设备最大速率
else
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
}
break;
default:
//命令必须为写方向的命令,且传输数据必须是SPI_IOC_MESSAGE()修饰的命令
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //计算传输数据大小
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { //判断是否为spi_ioc_transfer对齐
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出spi_ioc_transfer数据的个数
if (n_ioc == 0)
break;
ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指针ioc内存
if (!ioc) {
retval = -ENOMEM;
break;
}
if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //从用户空间复制到内核空间
kfree(ioc); //复制失败则释放ioc内存
retval = -EFAULT;
break;
}
retval = spidev_message(spidev, ioc, n_ioc); //spidev消息处理
kfree(ioc); //释放ioc内存
break;
}
mutex_unlock(&spidev->buf_lock); //解互斥锁
spi_dev_put(spi); //增加spi设备的引用计数
return retval;
}
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;
mutex_lock(&device_list_lock); //上互斥锁
list_for_each_entry(spidev, &device_list, device_entry) { //遍历device_list
if (spidev->devt == inode->i_rdev) { //判断设备号找到对应的设备
status = 0; //设置状态为0
break;
}
}
if (status == 0) { //找得到对应的设备
if (!spidev->buffer) { //spidev_data缓冲区为空
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); //则分配内存
if (!spidev->buffer) { //还空
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); //申请内存失败
status = -ENOMEM;
}
}
if (status == 0) { //找得到对应的设备
spidev->users++; //spidev_data使用者计数++
filp->private_data = spidev; //spidev_data放在文件的私有数据里
nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
}
}
else
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock); //接互斥锁
return status;
}
static int spidev_release(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = 0;
mutex_lock(&device_list_lock);
spidev = filp->private_data; //获取spidev_data
filp->private_data = NULL; //清除文件的私有数据指针
spidev->users--; //使用者个数--
if (!spidev->users) { //如果使用者个数为0
int dofree;
kfree(spidev->buffer); //释放spidev_data的缓冲区内存
spidev->buffer = NULL; //清除spidev_data缓冲区指针
spin_lock_irq(&spidev->spi_lock); //上自旋锁
dofree = (spidev->spi == NULL); //判断spi设备是否与spidev_data解绑了
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
if (dofree) //没有捆绑的spi设备
kfree(spidev); //则是否spidev_data内存
}
mutex_unlock(&device_list_lock);
return status;
}
static const struct file_operations spidev_fops = { //文件操作函数集
.owner = THIS_MODULE,
.write = spidev_write, //写write
.read = spidev_read, //读read
.unlocked_ioctl = spidev_ioctl, //控制ioctl
.open = spidev_open, //打开open
.release = spidev_release, //释放release
.llseek = no_llseek, //文件指针移动 no_llseek表示没有移动
};
static struct class *spidev_class;
static int __devinit spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); //分配spidev_data内存
if (!spidev)
return -ENOMEM;
spidev->spi = spi; //设置spidev_data->spi(spi设备)
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry); //初始化spidev_data入口链表
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS); //查找次设备位图分配次设备号
if (minor < N_SPI_MINORS) {
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //计算出设备号
//创建设备/dev/spidev%d.%d(spidev总线号.片选号)
dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
}
else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) { //分配设备号成功
set_bit(minor, minors); //更新次设备位图
list_add(&spidev->device_entry, &device_list); //添加进设备链表
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev); //spi->dev->p->driver_data=spidev
else
kfree(spidev);
return status;
}
static int __devexit spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi); //根据spi设备获取spidev_data
spin_lock_irq(&spidev->spi_lock); //上自旋锁
spidev->spi = NULL; //清空spidev_data->spi指针
spi_set_drvdata(spi, NULL); //spi->dev->p->driver_data=NULL
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
mutex_lock(&device_list_lock); //上互斥锁
list_del(&spidev->device_entry); //删除spidev_data入口链表
device_destroy(spidev_class, spidev->devt); //销毁/dev/spidev%d.%d
clear_bit(MINOR(spidev->devt), minors); //清除次设备位图对应位
if (spidev->users == 0) //使用者个数为0
kfree(spidev); //释放spidev_data内存
mutex_unlock(&device_list_lock); //解互斥锁
return 0;
}
static struct spi_driver spidev_spi_driver = { //spi设备驱动
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe, //spidev的probe方法(当注册了modalias域为"spidev"的spi设备或板级设备,则会调用probe方法)
.remove = __devexit_p(spidev_remove), //spidev的remove方法
};
static int __init spidev_init(void) //spidev接口初始化
{
int status;
BUILD_BUG_ON(N_SPI_MINORS > 256);
//注册字符设备,主设备号SPIDEV_MAJOR=153,捆绑的设备操作函数集为spidev_fops
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); //创建设备类spidev_class
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); //注册spi设备驱动spidev_spi_driver
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init); //声明初始化入口
static void __exit spidev_exit(void) //spidev接口销毁
{
spi_unregister_driver(&spidev_spi_driver); //注销spi设备驱动spidev_spi_driver
class_destroy(spidev_class); //注销设备类spidev_class
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); //注销字符设备
}
module_exit(spidev_exit); //声明初始化出口
MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:spidev");
二.用户空间例子(spidev_test.c)
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static void transfer(int fd)
{
int ret;
uint8_t tx[] = { //要发送的数据数组
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //接收的数据数据
struct spi_ioc_transfer tr = { //声明并初始化spi_ioc_transfer结构体
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
//SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默认操作,传输数据
if (ret < 1)
pabort("can't send spi message");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收缓冲区
if (!(ret % 6)) //6个数据为一簇打印
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
static void print_usage(const char *prog) //参数错误则打印帮助信息
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = { //参数命令表
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D': //设备名
device = optarg;
break;
case 's': //速率
speed = atoi(optarg);
break;
case 'd': //延时时间
delay = atoi(optarg);
break;
case 'b': //每字含多少位
bits = atoi(optarg);
break;
case 'l': //回送模式
mode |= SPI_LOOP;
break;
case 'H': //时钟相位
mode |= SPI_CPHA;
break;
case 'O': //时钟极性
mode |= SPI_CPOL;
break;
case 'L': //lsb 最低有效位
mode |= SPI_LSB_FIRST;
break;
case 'C': //片选高电平
mode |= SPI_CS_HIGH;
break;
case '3': //3线传输模式
mode |= SPI_3WIRE;
break;
case 'N': //没片选
mode |= SPI_NO_CS;
break;
case 'R': //从机拉低电平停止数据传输
mode |= SPI_READY;
break;
default: //错误的参数
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv); //解析传递进来的参数
fd = open(device, O_RDWR); //打开设备文件
if (fd < 0)
pabort("can't open device");
/*
* spi mode //设置spi设备模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); //写模式
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); //读模式
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word //设置每个字含多少位
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); //写 每个字含多少位
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); //读 每个字含多少位
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz //设置速率
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); //写速率
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); //读速率
if (ret == -1)
pabort("can't get max speed hz");
//打印模式,每字多少位和速率信息
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
transfer(fd); //传输测试
close(fd); //关闭设备
return ret;
}
这里整理下ioctl的命令:
SPI_IOC_RD_MODE //读 模式
SPI_IOC_RD_LSB_FIRST //读 LSB
SPI_IOC_RD_BITS_PER_WORD //读 每字多少位
SPI_IOC_RD_MAX_SPEED_HZ //读 最大速率
SPI_IOC_WR_MODE //写 模式
SPI_IOC_WR_LSB_FIRST //写 LSB
SPI_IOC_WR_BITS_PER_WORD //写 每字多少位
SPI_IOC_WR_MAX_SPEED_HZ //写 最大速率
SPI_IOC_MESSAGE(n) //传输n个数据包