基于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驱动将
数据传输出去。基于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中。
- /* 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;
- }
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*/
- }
最后,调用了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;
- }
随后调用了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*/
- }
8.5 spi_bitbang_transfer
下列代码位于drivers/spi/spi_bitbang.c中。
- /**
- * spi_bitbang_transfer - default submit to transfer queue
- */
- 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;
- bitbang = spi_master_get_devdata(spi->master);
- spin_lock_irqsave(&bitbang->lock, flags);
- if (!spi->max_speed_hz)
- status = -ENETDOWN;
- else {
- /*下面的工作队列和queue在spi_bitbang_start函数中初始化*/
- list_add_tail(&m->queue, &bitbang->queue); /*将message添加到bitbang的queue链表中*/
- queue_work(bitbang->workqueue, &bitbang->work); /*提交工作到工作队列*/
- }
- spin_unlock_irqrestore(&bitbang->lock, flags);
- return status;
- }
这里将message添加到了bitbang的queue链表中。然后提交了一个工作到工作队列,随后函数返回到spi_async,又返回到spidev_sync中。为方便将spidev_sync的部分代码列出:
- 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;
8.6 bitbang_work
在上一节最后添加了工作bitbang->work到工作队列中,在过一段时候后,内核将以进程执行该work。而work即为在spi_bitbang_start中定义的bitbang_work函数。我们来看下这个函数。
下列代码位于drivers/spi/spi_bitbang.c中。
- /*
- * SECOND PART ... simple transfer queue runner.
- *
- * This costs a task context per controller, running the queue by
- * performing each transfer in sequence. Smarter hardware can queue
- * several DMA transfers at once, and process several controller queues
- * in parallel; this driver doesn't match such hardware very well.
- *
- * Drivers can provide word-at-a-time i/o primitives, or provide
- * transfer-at-a-time ones to leverage dma or fifo hardware.
- */
- static void bitbang_work(struct work_struct *work)
- {
- struct spi_bitbang *bitbang =
- container_of(work, struct spi_bitbang, work); /*获取spi_bitbang*/
- unsigned long flags;
- spin_lock_irqsave(&bitbang->lock, flags); /*自旋锁加锁*/
- bitbang->busy = 1; /*bitbang忙碌*/
- while (!list_empty(&bitbang->queue)) { /*有spi_message*/
- struct spi_message *m;
- struct spi_device *spi;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned tmp;
- unsigned cs_change;
- int status;
- int (*setup_transfer)(struct spi_device *,
- struct spi_transfer *);
- m = container_of(bitbang->queue.next, struct spi_message,/*获取spi_message*/
- queue);
- list_del_init(&m->queue); /*以获取spi_message,删除该spi_message*/
- 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;
- setup_transfer = NULL;
- /*遍历,获取所有的spi_transfer*/
- list_for_each_entry (t, &m->transfers, transfer_list) {
- /* override or restore speed and wordsize */
- if (t->speed_hz || t->bits_per_word) { /*如果这两个参数有任何一个已经设置了,本例中没有定义*/
- setup_transfer = bitbang->setup_transfer;
- if (!setup_transfer) {
- status = -ENOPROTOOPT;
- break;
- }
- }
- if (setup_transfer) { /*本例中为NULL*/
- 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) { /*初值为1*/
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);/*即调用s3c24xx_spi_chipsel,激活CS信号,写寄存器,设置SPI模式*/
- ndelay(nsecs); /*延迟100纳秒*/
- }
- cs_change = t->cs_change; /*保存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; /*不使用DMA*/
- status = bitbang->txrx_bufs(spi, t); /*即调用s3c24xx_spi_txrx,开始发送数据,status为已发送数据的大小*/
- }
- 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)/*判断是否需要禁止CS,为1表示要求在两次数据传输之间禁止CS*/
- continue;
- if (t->transfer_list.next == &m->transfers) /*没有transfer*/
- 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); /*禁止CS*/
- ndelay(nsecs);
- } /*遍历spi_transfer结束*/
- m->status = status;
- m->complete(m->context); /*调用complete,一个message处理完毕*/
- /* restore speed and wordsize */
- if (setup_transfer)
- setup_transfer(spi, NULL);
- /* 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); /*禁止CS*/
- ndelay(nsecs);
- }
- spin_lock_irqsave(&bitbang->lock, flags);
- }
- bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
- }
8.7 s3c24xx_spi_txrx 和s3c24xx_spi_irq
下列代码位于deivers/spi/s3c24xx.c。
- static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
- {
- return hw->tx ? hw->tx[count] : 0; /*发送缓冲区指针是否为空,空则发送0*/
- }
- static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)/*bitbang.txrx_bufs方法*/
- {
- struct s3c24xx_spi *hw = to_hw(spi);
- dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
- t->tx_buf, t->rx_buf, t->len);
- /*保存transfer相关数据到s3c24xx_sp结构中*/
- hw->tx = t->tx_buf;
- hw->rx = t->rx_buf;
- hw->len = t->len;
- hw->count = 0;
- init_completion(&hw->done); /*初始化completion*/
- /* send the first byte */ /*发送第一个数据,tx[0]*/
- writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
- wait_for_completion(&hw->done);/*等待completion*/
- return hw->count; /*返回发送的字节数*/
- }
- static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
- {
- struct s3c24xx_spi *hw = dev;
- unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);/*获取状态寄存器*/
- unsigned int count = hw->count;
- if (spsta & S3C2410_SPSTA_DCOL) { /*发生错误*/
- dev_dbg(hw->dev, "data-collision\n");
- complete(&hw->done); /*唤醒等待complete的进程*/
- goto irq_done;
- }
- if (!(spsta & S3C2410_SPSTA_READY)) {/*未就绪*/
- dev_dbg(hw->dev, "spi not ready for tx?\n");
- complete(&hw->done); /*唤醒等待complete的进程*/
- goto irq_done;
- }
- hw->count++;/*增加计数*/
- if (hw->rx)
- hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);/*读取数据*/
- count++; /*增加计数*/
- if (count < hw->len) /*未发送完毕,则继续发送*/
- writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
- else
- complete(&hw->done); /*发送完毕,唤醒等待complete的进程*/
- irq_done:
- return IRQ_HANDLED;
- }
NOTE:这里的completion是master驱动层的,spi设备驱动也有一个completion,用于IO同步,不要混淆。
当第一个数据发送完成以后,SPI中断产生,开始执行中断服务程序。在中断服务程序中,将判断是否需要读取数据,如果是则从寄存器中读取数据。
NOTE:如果是使用read系统调用,那么在此发送的数据将是0。
随后发送下一个数据,直到数据发送完成。发送完成后调用complete,使在s3c24xx_spi_txrx的wait_for_completion得以返回。接着,s3c24xx_spi_txrx就将返回已发送的字节数。
NOTE:其实该中断服务子程序实现了全双工数据的传输,只不过特定于具体的系统调用,从而分为了半双工读和写。
8.8 complete方法
在8.6节的bitbang_work中,当一个message的所有数据发送完成以后,将会调用complete函数。该函数如下:
- /*
- * We can't use the standard synchronous wrappers for file I/O; we
- * need to protect against async removal of the underlying spi_device.
- */
- static void spidev_complete(void *arg)
- {
- complete(arg);
- }
至此,整个write系统调用的流程均以讲解完毕,在这其中也对在master和protocol中未曾给出的函数做出了一一讲解,最后,对第8章进行小结。
8.9 小结
从示意图中,我们可以很清除看到函数的调用过程:先调用spi设备驱动层,随后调用bitbang中间层,最后调用了master驱动层来完成数据的传输。
9. read方法
read方法和write方法基本差不多,关键的区别在于其发送的数据为0,而在s3c24xx_spi_txrx中断服务程序中将读取数据寄存器。下面仅仅给出函数调用示意图。
在这里给出spidev_read和spidev_sync_read,方便读者进行对比。