接着看看spidev.c
我们知道spidev.c里面,我们注册了SPI接口设备驱动,并且添加了字符设备,为用户提供接口。
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.open = spidev_open,
.release = spidev_release,
};
我们一个个的看看这些函数,看看怎么实现数据传输的吧。
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;
lock_kernel();
mutex_lock(&device_list_lock);
list_for_each_entry(spidev, &device_list, device_entry) {//从接口设备链表中获取接口设备
if (spidev->devt == inode->i_rdev) {
status = 0;
break;
}
}
if (status == 0) {
if (!spidev->buffer) { //如果没申请缓冲区就去申请缓冲区
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spidev->buffer) {
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
status = -ENOMEM;
}
}
if (status == 0) { //如果初次使用就把使用次数加一
spidev->users++;
filp->private_data = spidev; //把spidev作为文件的私有数据
nonseekable_open(inode, filp);
}
} else
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock);
unlock_kernel();
return status;
}
这个open函数很简单,主要就是从接口设备链表中获取spidev设备,并对其初始化。好了,我们重点看看ioctl函数
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) //查看这个命令的幻数字段是否为'k'
return -ENOTTY;
//如果方向是用户空间从内核读,即内核向用户空间写,检查用户空间的地址是否有效
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd));
//如果方向是用户空间向内核写,即内核读用户空间,则检查用户空间的地址是否有效
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
spidev = filp->private_data; //从文件的私有数据中获取spidev
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi); //从spidev中获取spi_device
spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
return -ESHUTDOWN;
mutex_lock(&spidev->buf_lock);
switch (cmd) {
case SPI_IOC_RD_MODE:
//因为已经进行了地址是否有效的检查
//所以这里使用__put_user,__get_user,__copy_from_user可以节省几个时钟周期
retval = __put_user(spi->mode & SPI_MODE_MASK,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST:
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD:
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ:
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:
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode;
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(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: //写入几比特代表一个字
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->bits_per_word;
spi->bits_per_word = tmp;
retval = spi_setup(spi);
if (retval < 0)
spi->bits_per_word = save;
else
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: //写传输速度
retval = __get_user(tmp, (__u32 __user *)arg);
if (retval == 0) {
u32 save = spi->max_speed_hz;
spi->max_speed_hz = tmp;
retval = spi_setup(spi);
if (retval < 0)
spi->max_speed_hz = save;
else
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
}
break;
default:
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //得到用户空间数据的大小
//如果这些数据不能分成spi_ioc_transfer的整数倍,则不能进行传输
//spi_io_transfer是对spi_transfer的映射
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出能分多少个spi_ioc_transfer
if (n_ioc == 0)
break;
ioc = kmalloc(tmp, GFP_KERNEL); //在内核中分配装载这些数据的内存空间
if (!ioc) {
retval = -ENOMEM;
break;
}
if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //把用户空间的数据拷贝过来
kfree(ioc);
retval = -EFAULT;
break;
}
retval = spidev_message(spidev, ioc, n_ioc); //进行数据传输
kfree(ioc);
break;
}
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;
}
在这个ioctl函数中,我们可以设置工作模式,可以设置bit/word,可以设置波特率,当然当其他命令进来会执行数据传输函数spidev_message,那么我们就跟踪看看这个函数如何实现数据传输的吧