基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)
本系列文章对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驱动将数据传输出去。
本文属于第四部分。
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中。
- /* Write-only message with current device setup */
- 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;
- /* chipselect only toggles at start or end of operation */
- if (count > bufsiz) /*数据大于4096字节*/
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- /*将用户层的数据拷贝至buffer中,buffer在open方法中分配*/
- 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中。
- 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_list*/
- }
在这里,创建了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中。
- static ssize_t
- spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done); /*创建completion*/
- int status;
- message->complete = spidev_complete;/*定义complete方法*/
- message->context = &done; /*complete方法的参数*/
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL)
- status = -ESHUTDOWN;
- else
- status = spi_async(spidev->spi, message);/*异步,用complete来完成同步*/
- spin_unlock_irq(&spidev->spi_lock);
- if (status == 0) {
- wait_for_completion(&done); /*在bitbang_work中调用complete方法来唤醒*/
- 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中。
- /**
- * spi_async - asynchronous SPI transfer
- * @spi: device with which data will be exchanged
- * @message: describes the data transfers, including completion callback
- * Context: any (irqs may be blocked, etc)
- *
- * This call may be used in_irq and other contexts which can't sleep,
- * as well as from task contexts which can sleep.
- *
- * The completion callback is invoked in a context which can't sleep.
- * Before that invocation, the value of message->status is undefined.
- * When the callback is issued, message->status holds either zero (to
- * indicate complete success) or a negative error code. After that
- * callback returns, the driver which issued the transfer request may
- * deallocate the associated memory; it's no longer in use by any SPI
- * core or controller driver code.
- *
- * Note that although all messages to a spi_device are handled in
- * FIFO order, messages may go to different devices in other orders.
- * Some device might be higher priority, or have various "hard" access
- * time requirements, for example.
- *
- * On detection of any fault during the transfer, processing of
- * the entire message is aborted, and the device is deselected.
- * Until returning from the associated message completion callback,
- * no other spi_message queued to that device will be processed.
- * (This rule applies equally to all the synchronous transfer calls,
- * which are wrappers around this core asynchronous primitive.)
- */
- static inline int
- spi_async(struct spi_device *spi, struct spi_message *message)
- {
- message->spi = spi; /*指出执行transfer的SPI接口*/
- return spi->master->transfer(spi, message); /*即调用spi_bitbang_transfer*/
- }
这个函数仅仅保存了spi_device信息后,然后调用了master的transfer方法,该方法在spi_bitbang_start中定义为spi_bitbang_transfer。