歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(四)

基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(四)

日期:2017/3/1 10:12:24   编辑:Linux編程

本系列文章對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. /* chipselect only toggles at start or end of operation */
  10. if (count > bufsiz) /*數據大於4096字節*/
  11. return -EMSGSIZE;
  12. spidev = filp->private_data;
  13. mutex_lock(&spidev->buf_lock);
  14. /*將用戶層的數據拷貝至buffer中,buffer在open方法中分配*/
  15. missing = copy_from_user(spidev->buffer, buf, count);
  16. if (missing == 0) {
  17. status = spidev_sync_write(spidev, count);
  18. } else
  19. status = -EFAULT;
  20. mutex_unlock(&spidev->buf_lock);
  21. return status;
  22. }

在這裡,做的事情很少,主要就是從用戶空間將需要發送的數據復制過來。然後調用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. spi_message_init(&m);
  10. spi_message_add_tail(&t, &m);
  11. return spidev_sync(spidev, &m);
  12. }
  13. static inline void spi_message_init(struct spi_message *m)
  14. {
  15. memset(m, 0, sizeof *m);
  16. INIT_LIST_HEAD(&m->transfers); /*初始化鏈表頭*/
  17. }
  18. spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
  19. {
  20. list_add_tail(&t->transfer_list, &m->transfers);/*添加transfer_list*/
  21. }

在這裡,創建了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. message->complete = spidev_complete;/*定義complete方法*/
  7. message->context = &done; /*complete方法的參數*/
  8. spin_lock_irq(&spidev->spi_lock);
  9. if (spidev->spi == NULL)
  10. status = -ESHUTDOWN;
  11. else
  12. status = spi_async(spidev->spi, message);/*異步,用complete來完成同步*/
  13. spin_unlock_irq(&spidev->spi_lock);
  14. if (status == 0) {
  15. wait_for_completion(&done); /*在bitbang_work中調用complete方法來喚醒*/
  16. status = message->status;
  17. if (status == 0)
  18. status = message->actual_length; /*返回發送的字節數*/
  19. }
  20. return status;
  21. }

在這裡,初始化了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