基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)

基于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中。    

[cpp]  view plain copy
  1. /* Write-only message with current device setup */  
  2. static ssize_t  
  3. spidev_write(struct file *filp, const char __user *buf,  
  4.         size_t count, loff_t *f_pos)  
  5. {  
  6.     struct spidev_data  *spidev;  
  7.     ssize_t         status = 0;  
  8.     unsigned long       missing;  
  9.   
  10.     /* chipselect only toggles at start or end of operation */  
  11.     if (count > bufsiz)  /*数据大于4096字节*/  
  12.         return -EMSGSIZE;  
  13.   
  14.     spidev = filp->private_data;  
  15.   
  16.     mutex_lock(&spidev->buf_lock);  
  17.     /*将用户层的数据拷贝至buffer中,buffer在open方法中分配*/  
  18.     missing = copy_from_user(spidev->buffer, buf, count);   
  19.     if (missing == 0) {  
  20.         status = spidev_sync_write(spidev, count);  
  21.     } else  
  22.         status = -EFAULT;  
  23.     mutex_unlock(&spidev->buf_lock);  
  24.   
  25.     return status;  
  26. }  
在这里,做的事情很少,主要就是从用户空间将需要发送的数据复制过来。然后调用spidev_sync_write。

8.2 spidev_sync_write

下列代码位于drivers/spi/spidev.c中。  

[cpp]  view plain copy
  1. static inline ssize_t  
  2. spidev_sync_write(struct spidev_data *spidev, size_t len)  
  3. {  
  4.     struct spi_transfer t = {  
  5.             .tx_buf     = spidev->buffer,  
  6.             .len        = len,  
  7.         };  
  8.     struct spi_message  m;  
  9.   
  10.     spi_message_init(&m);  
  11.     spi_message_add_tail(&t, &m);  
  12.     return spidev_sync(spidev, &m);  
  13. }  
  14.   
  15. static inline void spi_message_init(struct spi_message *m)  
  16. {  
  17.     memset(m, 0, sizeof *m);  
  18.     INIT_LIST_HEAD(&m->transfers);    /*初始化链表头*/  
  19. }  
  20.   
  21. spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)  
  22. {  
  23.     list_add_tail(&t->transfer_list, &m->transfers);/*添加transfer_list*/  
  24. }  
在这里,创建了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 plain copy
  1. static ssize_t  
  2. spidev_sync(struct spidev_data *spidev, struct spi_message *message)  
  3. {  
  4.     DECLARE_COMPLETION_ONSTACK(done);   /*创建completion*/  
  5.     int status;  
  6.   
  7.     message->complete = spidev_complete;/*定义complete方法*/  
  8.     message->context = &done;            /*complete方法的参数*/  
  9.   
  10.     spin_lock_irq(&spidev->spi_lock);  
  11.     if (spidev->spi == NULL)  
  12.         status = -ESHUTDOWN;  
  13.     else  
  14.         status = spi_async(spidev->spi, message);/*异步,用complete来完成同步*/  
  15.     spin_unlock_irq(&spidev->spi_lock);  
  16.   
  17.     if (status == 0) {  
  18.         wait_for_completion(&done); /*在bitbang_work中调用complete方法来唤醒*/  
  19.         status = message->status;  
  20.         if (status == 0)  
  21.             status = message->actual_length; /*返回发送的字节数*/  
  22.     }  
  23.     return status;  
  24. }  
在这里,初始化了completion,这个东东将实现write系统调用的同步。在后面我们将会看到如何实现的。

随后调用了spi_async,从名字上可以看出该函数是异步的,也就是说该函数返回后,数据并没有被发送出去。因此使用了wait_for_completion来等待数据的发送完成,达到同步的目的。

8.4 spi_async

下列代码位于drivers/spi/spi.h中。 

[cpp]  view plain copy
  1. /** 
  2.  * spi_async - asynchronous SPI transfer 
  3.  * @spi: device with which data will be exchanged 
  4.  * @message: describes the data transfers, including completion callback 
  5.  * Context: any (irqs may be blocked, etc) 
  6.  * 
  7.  * This call may be used in_irq and other contexts which can't sleep, 
  8.  * as well as from task contexts which can sleep. 
  9.  * 
  10.  * The completion callback is invoked in a context which can't sleep. 
  11.  * Before that invocation, the value of message->status is undefined. 
  12.  * When the callback is issued, message->status holds either zero (to 
  13.  * indicate complete success) or a negative error code.  After that 
  14.  * callback returns, the driver which issued the transfer request may 
  15.  * deallocate the associated memory; it's no longer in use by any SPI 
  16.  * core or controller driver code. 
  17.  * 
  18.  * Note that although all messages to a spi_device are handled in 
  19.  * FIFO order, messages may go to different devices in other orders. 
  20.  * Some device might be higher priority, or have various "hard" access 
  21.  * time requirements, for example. 
  22.  * 
  23.  * On detection of any fault during the transfer, processing of 
  24.  * the entire message is aborted, and the device is deselected. 
  25.  * Until returning from the associated message completion callback, 
  26.  * no other spi_message queued to that device will be processed. 
  27.  * (This rule applies equally to all the synchronous transfer calls, 
  28.  * which are wrappers around this core asynchronous primitive.) 
  29.  */  
  30. static inline int  
  31. spi_async(struct spi_device *spi, struct spi_message *message)  
  32. {  
  33.     message->spi = spi;       /*指出执行transfer的SPI接口*/  
  34.     return spi->master->transfer(spi, message);    /*即调用spi_bitbang_transfer*/  
  35. }  
这个函数仅仅保存了spi_device信息后,然后调用了master的transfer方法,该方法在spi_bitbang_start中定义为spi_bitbang_transfer。

8.5 spi_bitbang_transfer

下列代码位于drivers/spi/spi_bitbang.c中。 

[cpp]  view plain copy
  1. /** 
  2.  * spi_bitbang_transfer - default submit to transfer queue 
  3.  */  
  4. int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)  
  5. {  
  6.     struct spi_bitbang  *bitbang;  
  7.     unsigned long       flags;  
  8.     int         status = 0;  
  9.   
  10.     m->actual_length = 0;  
  11.     m->status = -EINPROGRESS;  
  12.   
  13.     bitbang = spi_master_get_devdata(spi->master);  
  14.   
  15.     spin_lock_irqsave(&bitbang->lock, flags);  
  16.     if (!spi->max_speed_hz)  
  17.         status = -ENETDOWN;  
  18.     else {  
  19.         /*下面的工作队列和queue在spi_bitbang_start函数中初始化*/  
  20.         list_add_tail(&m->queue, &bitbang->queue);    /*将message添加到bitbang的queue链表中*/  
  21.         queue_work(bitbang->workqueue, &bitbang->work);    /*提交工作到工作队列*/  
  22.     }  
  23.     spin_unlock_irqrestore(&bitbang->lock, flags);  
  24.   
  25.     return status;  
  26. }  

这里将message添加到了bitbang的queue链表中。然后提交了一个工作到工作队列,随后函数返回到spi_async,又返回到spidev_sync中。为方便将spidev_sync的部分代码列出:

[cpp]  view plain copy
  1. status = spi_async(spidev->spi, message);/*异步,用complete来完成同步*/  
  2.     spin_unlock_irq(&spidev->spi_lock);  
  3.   
  4.     if (status == 0) {  
  5.         wait_for_completion(&done); /*在bitbang_work中调用complete方法来唤醒*/  
  6.         status = message->status;  
  7.         if (status == 0)  
  8.             status = message->actual_length; /*返回发送的字节数*/  
  9.     }  
  10.     return status;  
 当spi_async函数返回后,需要发送的数据已经通过工作的形式添加到了工作队列,在稍后的工作执行时,将完成数据的发送。随后调用了wait_for_completion等待数据的发送完成。到此,可以看出completion的使用是用来完成同步I/O的

8.6 bitbang_work

  在上一节最后添加了工作bitbang->work到工作队列中,在过一段时候后,内核将以进程执行该work。而work即为在spi_bitbang_start中定义的bitbang_work函数。我们来看下这个函数。

  下列代码位于drivers/spi/spi_bitbang.c中。 

[cpp]  view plain copy
  1. /* 
  2.  * SECOND PART ... simple transfer queue runner. 
  3.  * 
  4.  * This costs a task context per controller, running the queue by 
  5.  * performing each transfer in sequence.  Smarter hardware can queue 
  6.  * several DMA transfers at once, and process several controller queues 
  7.  * in parallel; this driver doesn't match such hardware very well. 
  8.  * 
  9.  * Drivers can provide word-at-a-time i/o primitives, or provide 
  10.  * transfer-at-a-time ones to leverage dma or fifo hardware. 
  11.  */  
  12. static void bitbang_work(struct work_struct *work)  
  13. {  
  14.     struct spi_bitbang    *bitbang =  
  15.         container_of(work, struct spi_bitbang, work);    /*获取spi_bitbang*/  
  16.     unsigned long        flags;  
  17.   
  18.     spin_lock_irqsave(&bitbang->lock, flags);    /*自旋锁加锁*/  
  19.     bitbang->busy = 1;        /*bitbang忙碌*/  
  20.     while (!list_empty(&bitbang->queue)) {    /*有spi_message*/  
  21.         struct spi_message    *m;  
  22.         struct spi_device    *spi;  
  23.         unsigned        nsecs;  
  24.         struct spi_transfer    *t = NULL;  
  25.         unsigned        tmp;  
  26.         unsigned        cs_change;  
  27.         int            status;  
  28.         int            (*setup_transfer)(struct spi_device *,  
  29.                         struct spi_transfer *);  
  30.   
  31.         m = container_of(bitbang->queue.next, struct spi_message,/*获取spi_message*/  
  32.                 queue);          
  33.         list_del_init(&m->queue);        /*以获取spi_message,删除该spi_message*/  
  34.         spin_unlock_irqrestore(&bitbang->lock, flags);/*释放自旋锁*/  
  35.   
  36.         /* FIXME this is made-up ... the correct value is known to 
  37.          * word-at-a-time bitbang code, and presumably chipselect() 
  38.          * should enforce these requirements too? 
  39.          */  
  40.         nsecs = 100;  
  41.   
  42.         spi = m->spi;  
  43.         tmp = 0;  
  44.         cs_change = 1;  
  45.         status = 0;  
  46.         setup_transfer = NULL;  
  47.   
  48.         /*遍历,获取所有的spi_transfer*/  
  49.         list_for_each_entry (t, &m->transfers, transfer_list) {      
  50.   
  51.             /* override or restore speed and wordsize */  
  52.             if (t->speed_hz || t->bits_per_word) { /*如果这两个参数有任何一个已经设置了,本例中没有定义*/  
  53.                 setup_transfer = bitbang->setup_transfer;  
  54.                 if (!setup_transfer) {  
  55.                     status = -ENOPROTOOPT;  
  56.                     break;  
  57.                 }  
  58.             }  
  59.             if (setup_transfer) {        /*本例中为NULL*/  
  60.                 status = setup_transfer(spi, t);  
  61.                 if (status < 0)  
  62.                     break;  
  63.             }  
  64.   
  65.             /* set up default clock polarity, and activate chip; 
  66.              * this implicitly updates clock and spi modes as 
  67.              * previously recorded for this device via setup(). 
  68.              * (and also deselects any other chip that might be 
  69.              * selected ...) 
  70.              */  
  71.             if (cs_change) {                                /*初值为1*/  
  72.                 bitbang->chipselect(spi, BITBANG_CS_ACTIVE);/*即调用s3c24xx_spi_chipsel,激活CS信号,写寄存器,设置SPI模式*/  
  73.                 ndelay(nsecs);                                /*延迟100纳秒*/  
  74.             }  
  75.             cs_change = t->cs_change;                        /*保存cs_change*/                  
  76.             if (!t->tx_buf && !t->rx_buf && t->len) {        /*检查参数*/  
  77.                 status = -EINVAL;  
  78.                 break;  
  79.             }  
  80.   
  81.             /* transfer data.  the lower level code handles any 
  82.              * new dma mappings it needs. our caller always gave 
  83.              * us dma-safe buffers. 
  84.              */  
  85.             if (t->len) {  
  86.                 /* REVISIT dma API still needs a designated 
  87.                  * DMA_ADDR_INVALID; ~0 might be better. 
  88.                  */  
  89.                 if (!m->is_dma_mapped)  
  90.                     t->rx_dma = t->tx_dma = 0;            /*不使用DMA*/  
  91.                 status = bitbang->txrx_bufs(spi, t);    /*即调用s3c24xx_spi_txrx,开始发送数据,status为已发送数据的大小*/  
  92.             }  
  93.             if (status > 0)  
  94.                 m->actual_length += status;    /*保存已发送字节*/  
  95.             if (status != t->len) {    /*要求发送和已发送的大小不同*/  
  96.                 /* always report some kind of error */  
  97.                 if (status >= 0)  
  98.                     status = -EREMOTEIO;  
  99.                 break;  
  100.             }  
  101.             status = 0;  
  102.   
  103.             /* protocol tweaks before next transfer */  
  104.             if (t->delay_usecs)  
  105.                 udelay(t->delay_usecs);    /*延迟*/  
  106.   
  107.             if (!cs_change)/*判断是否需要禁止CS,为1表示要求在两次数据传输之间禁止CS*/  
  108.                 continue;  
  109.             if (t->transfer_list.next == &m->transfers)    /*没有transfer*/  
  110.                 break;  
  111.   
  112.             /* sometimes a short mid-message deselect of the chip 
  113.              * may be needed to terminate a mode or command 
  114.              */  
  115.             ndelay(nsecs);    /*延迟*/  
  116.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);    /*禁止CS*/  
  117.             ndelay(nsecs);  
  118.         }    /*遍历spi_transfer结束*/  
  119.   
  120.         m->status = status;  
  121.         m->complete(m->context); /*调用complete,一个message处理完毕*/  
  122.   
  123.         /* restore speed and wordsize */  
  124.         if (setup_transfer)  
  125.             setup_transfer(spi, NULL);  
  126.   
  127.         /* normally deactivate chipselect ... unless no error and 
  128.          * cs_change has hinted that the next message will probably 
  129.          * be for this chip too. 
  130.          */  
  131.         if (!(status == 0 && cs_change)) {  
  132.             ndelay(nsecs);  
  133.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);    /*禁止CS*/  
  134.             ndelay(nsecs);  
  135.         }  
  136.   
  137.         spin_lock_irqsave(&bitbang->lock, flags);  
  138.     }  
  139.     bitbang->busy = 0;      
  140.     spin_unlock_irqrestore(&bitbang->lock, flags);  
  141. }  
本函数中,调用了两个方法bibang->chipselect和bitbang->txrx_bufs,这两个方法实际调用了s3c24xx_spi_chipsel和s3c24xx_spi_txrx函数,这两个函数都是master驱动层提供的函数。 s3c24xx_spi_chipsel已经在4.2.2节中给出,该函 数设置控制寄存器并激活CS信号。s3c24xx_spi_txrx函数的实参t,即为spi_transfer,函数完成该spi_transfer中数据的发送,并返回已发送的字节数。然后,判断是否需要禁止CS。接着遍历到下一个spi_transfer,再次发送数据。当所 有spi_transfer发送完成以后,将调用complete方法,从而让在spidev_sync函数中等待completion的函数返回。下面,先来来看下数据是怎么发送出去的,也就是s3c24xx_spi_txrx函数。最后,看看complete方法。

8.7 s3c24xx_spi_txrx 和s3c24xx_spi_irq

下列代码位于deivers/spi/s3c24xx.c。

[cpp]  view plain copy
  1. static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)  
  2. {  
  3.     return hw->tx ? hw->tx[count] : 0;    /*发送缓冲区指针是否为空,空则发送0*/  
  4. }  
  5.   
  6. static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)/*bitbang.txrx_bufs方法*/  
  7. {  
  8.     struct s3c24xx_spi *hw = to_hw(spi);  
  9.   
  10.     dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",  
  11.         t->tx_buf, t->rx_buf, t->len);  
  12.     /*保存transfer相关数据到s3c24xx_sp结构中*/  
  13.     hw->tx = t->tx_buf;  
  14.     hw->rx = t->rx_buf;  
  15.     hw->len = t->len;  
  16.     hw->count = 0;  
  17.   
  18.     init_completion(&hw->done);    /*初始化completion*/  
  19.   
  20.     /* send the first byte */    /*发送第一个数据,tx[0]*/  
  21.     writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);  
  22.   
  23.     wait_for_completion(&hw->done);/*等待completion*/  
  24.   
  25.     return hw->count;  /*返回发送的字节数*/  
  26. }  
  27.   
  28. static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)  
  29. {  
  30.     struct s3c24xx_spi *hw = dev;  
  31.     unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);/*获取状态寄存器*/  
  32.     unsigned int count = hw->count;  
  33.   
  34.     if (spsta & S3C2410_SPSTA_DCOL) {    /*发生错误*/  
  35.         dev_dbg(hw->dev, "data-collision\n");  
  36.         complete(&hw->done);    /*唤醒等待complete的进程*/  
  37.         goto irq_done;  
  38.     }  
  39.   
  40.     if (!(spsta & S3C2410_SPSTA_READY)) {/*未就绪*/  
  41.         dev_dbg(hw->dev, "spi not ready for tx?\n");  
  42.         complete(&hw->done);    /*唤醒等待complete的进程*/  
  43.         goto irq_done;  
  44.     }  
  45.   
  46.     hw->count++;/*增加计数*/  
  47.   
  48.     if (hw->rx)  
  49.         hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);/*读取数据*/  
  50.   
  51.     count++;    /*增加计数*/  
  52.   
  53.     if (count < hw->len)         /*未发送完毕,则继续发送*/  
  54.         writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);  
  55.     else  
  56.         complete(&hw->done);    /*发送完毕,唤醒等待complete的进程*/  
  57.   
  58.  irq_done:  
  59.     return IRQ_HANDLED;  
  60. }   
在s3c24xx_spi_txrx函数中,首先发送了待发送数据中的第一个字节,随后就调用wait_for_completion来等待剩余的数据发送完成。

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函数。该函数如下:

[cpp]  view plain copy
  1. /* 
  2.  * We can't use the standard synchronous wrappers for file I/O; we 
  3.  * need to protect against async removal of the underlying spi_device. 
  4.  */  
  5. static void spidev_complete(void *arg)  
  6. {  
  7.     complete(arg);  
  8. }  
该函数将使在spidev_sync函数中的wait_for_completion得以返回,从而完成了同步IO。

至此,整个write系统调用的流程均以讲解完毕,在这其中也对在master和protocol中未曾给出的函数做出了一一讲解,最后,对第8章进行小结。

8.9 小结


   从示意图中,我们可以很清除看到函数的调用过程:先调用spi设备驱动层,随后调用bitbang中间层,最后调用了master驱动层来完成数据的传输。

9. read方法

read方法和write方法基本差不多,关键的区别在于其发送的数据为0,而在s3c24xx_spi_txrx中断服务程序中将读取数据寄存器。下面仅仅给出函数调用示意图。



在这里给出spidev_read和spidev_sync_read,方便读者进行对比。

[cpp]  view plain copy
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值