本系列文章對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。