歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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

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

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

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

  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。

Copyright © Linux教程網 All Rights Reserved