本系列文章对Linux设备模型中的SPI子系统进行讲解。SPI子系统的讲解将分为4个部分。
第一部分,将对SPI子系统整体进行描述,同时给出SPI的相关数据结构,最后描述SPI总线的注册。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一)
第二部分,该文将对SPI的主控制器(master)驱动进行描述。 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)
第三部分,该文将对SPI设备驱动,也称protocol 驱动,进行讲解。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(三)
第四部分,即本篇文章,通过SPI设备驱动留给用户层的API,我们将从上到下描述数据是如何通过SPI的protocol 驱动,由bitbang 中转,最后由master驱动将
数据传输出去。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)
本文属于第四部分。
7. write,read和ioctl综述
在spi设备驱动层提供了两种数据传输方式。一种是半双工方式,write方法提供了半双工读访问,read方法提供了半双工写访问。另一种就是全双工方式,ioctl调用将同时完成数据的传送与发送。
在后面的描述中,我们将对write和ioctl方法做出详细的描述,而read方法和write极其相似,将不多做介绍。
接下来首先看看write方法是如何实现的。
8. write方法
8.1 spidev_write
在用户空间执行open打开设备文件以后,就可以执行write系统调用,该系统调用将会执行我们提供的write方法。代码如下:
下列代码位于drivers/spi/spidev.c中。
[cpp] view
plaincopy
-
-
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;
-
-
mutex_lock(&spidev->buf_lock);
-
-
missing = copy_from_user(spidev->buffer, buf, count);
-
if (missing == 0) {
-
status = spidev_sync_write(spidev, count);
-
} else
-
status = -EFAULT;
-
mutex_unlock(&spidev->buf_lock);
-
-
return status;
-
}
在这里,做的事情很少,主要就是从用户空间将需要发送的数据复制过来。然后调用spidev_sync_write。
8.2 spidev_sync_write
下列代码位于drivers/spi/spidev.c中。
[cpp] view
plaincopy
-
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_message_add_tail(&t, &m);
-
return spidev_sync(spidev, &m);
-
}
-
-
static inline void spi_message_init(struct spi_message *m)
-
{
-
memset(m, 0, sizeof *m);
-
INIT_LIST_HEAD(&m->transfers);
-
}
-
-
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
-
{
-
list_add_tail(&t->transfer_list, &m->transfers);
-
}
在这里,创建了transfer和message。spi_transfer包含了要发送数据的信息。然后初始化了message中的transfer链表头,并将spi_transfer添加到了transfer链表中。也就是以spi_message的transfers为链表头的链表中,包含了transfer,而transfer正好包含了需要发送的数据。由此可见message其实是对transfer的封装。
最后,调用了spidev_sync,并将创建的spi_message作为参数传入。
8.3 spidev_sync
下列代码位于drivers/spi/spidev.c中。
[cpp] view
plaincopy
-
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)
-
status = -ESHUTDOWN;
-
else
-
status = spi_async(spidev->spi, message);
-
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;
-
}
在这里,初始化了completion,这个东东将实现write系统调用的同步。在后面我们将会看到如何实现的。
随后调用了spi_async,从名字上可以看出该函数是异步的,也就是说该函数返回后,数据并没有被发送出去。因此使用了wait_for_completion来等待数据的发送完成,达到同步的目的。
8.4 spi_async
下列代码位于drivers/spi/spi.h中。
[cpp] view
plaincopy
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-