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

2019-07-13 04:21发布


   本系列文章对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
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 proce