SPI 使用者空間 API

SPI 裝置的使用者空間 API 功能有限,支援對 SPI 從裝置進行基本的半雙工 read() 和 write() 訪問。透過 ioctl() 請求,也可以進行全雙工傳輸和裝置 I/O 配置。

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

您可能希望使用此程式設計介面的一些原因包括:

  • 在不易崩潰的環境中進行原型開發;使用者空間中的野指標通常不會導致任何 Linux 系統崩潰。

  • 開發用於與充當 SPI 從裝置功能的微控制器通訊的簡單協議,這些協議可能需要經常更改。

當然,有些驅動程式無法在使用者空間中編寫,因為它們需要訪問使用者空間無法訪問的核心介面(例如中斷請求處理程式或驅動程式堆疊的其他層)。

裝置建立,驅動繫結

spidev 驅動程式包含支援不同硬體拓撲表示的 SPI 裝置列表。

以下是 spidev 驅動程式支援的 SPI 裝置表:

  • struct spi_device_id spidev_spi_ids[]:當使用帶 .modalias 欄位(與表中某個條目匹配)的 struct spi_board_info 定義裝置時,可以繫結的裝置列表。

  • struct of_device_id spidev_dt_ids[]:當使用相容字串與表中某個條目匹配的裝置樹節點定義裝置時,可以繫結的裝置列表。

  • struct acpi_device_id spidev_acpi_ids[]:當使用 _HID 與表中某個條目匹配的 ACPI 裝置物件定義裝置時,可以繫結的裝置列表。

如果相關表中尚未包含您的 SPI 裝置名稱條目,我們鼓勵您新增一個。為此,請向 linux-spi@vger.kernel.org 郵件列表提交一個針對 spidev 的補丁。

過去曾支援使用“spidev”名稱定義 SPI 裝置。例如,作為 .modalias = “spidev” 或 compatible = “spidev”。但 Linux 核心不再支援此功能,而是必須使用表中列出的真實 SPI 裝置名稱。

如果沒有真實的 SPI 裝置名稱,將會列印錯誤並且 spidev 驅動程式將無法探測。

Sysfs 還支援使用者空間驅動的繫結/解綁驅動程式到那些不透過上述任何表格自動繫結的裝置。要使 spidev 驅動程式繫結到此類裝置,請使用以下方法:

echo spidev > /sys/bus/spi/devices/spiB.C/driver_override
echo spiB.C > /sys/bus/spi/drivers/spidev/bind

當 spidev 驅動程式繫結到 SPI 裝置時,該裝置的 sysfs 節點將包含一個子裝置節點,該節點帶有一個“dev”屬性,此屬性將被 udev 或 mdev(BusyBox 中的 udev 替代品;功能較少,但通常足夠用)理解。

對於匯流排 B 上帶有片選 C 的 SPI 裝置,您應該會看到:

/dev/spidevB.C ...

字元特殊裝置,主裝置號為 153,次裝置號動態選擇。這是使用者空間程式將開啟的節點,由“udev”或“mdev”建立。

/sys/devices/.../spiB.C ...

和往常一樣,SPI 裝置節點將是其 SPI 主控制器的一個子節點。

/sys/class/spidev/spidevB.C ...

當“spidev”驅動程式繫結到該裝置時建立。(目錄或符號連結,取決於您是否啟用了“deprecated sysfs files” Kconfig 選項。)

不要嘗試手動管理 /dev 字元裝置特殊檔案節點。這樣做容易出錯,並且您需要密切關注系統安全問題;udev/mdev 應該已經配置安全了。

如果您從該裝置解綁“spidev”驅動程式,這兩個“spidev”節點(在 sysfs 和 /dev 中)應自動刪除(分別由核心和 udev/mdev 刪除)。您可以透過刪除“spidev”驅動模組來解綁,這將影響所有使用此驅動程式的裝置。您還可以透過讓核心程式碼刪除 SPI 裝置來解綁,這通常是透過刪除其 SPI 控制器的驅動程式(從而使其 spi_master 消失)來實現。

儘管 spidev 驅動程式只是向用戶空間公開了一個低階 API,但它是一個標準的 Linux 裝置驅動程式,可以同時與任意數量的裝置關聯。只需為每個此類 SPI 裝置提供一個 spi_board_info 記錄,您就會為每個裝置獲得一個 /dev 裝置節點。

基本字元裝置 API

對 /dev/spidevB.D 檔案執行正常的 open() 和 close() 操作,結果與您預期的一樣。

標準的 read() 和 write() 操作顯然是半雙工的,並且在這些操作之間片選會被停用。全雙工訪問以及無需片選停用即可進行的複合操作,可透過 SPI_IOC_MESSAGE(N) 請求實現。

幾個 ioctl() 請求允許您的驅動程式讀取或覆蓋裝置的當前資料傳輸引數設定:

SPI_IOC_RD_MODE, SPI_IOC_WR_MODE ...

傳入一個指向位元組的指標,該位元組將返回(RD)或分配(WR)SPI 傳輸模式。使用常量 SPI_MODE_0..SPI_MODE_3;或者,如果您願意,可以組合 SPI_CPOL(時鐘極性,如果設定則空閒高電平)或 SPI_CPHA(時鐘相位,如果設定則在尾沿取樣)標誌。請注意,此請求僅限於適合單個位元組的 SPI 模式標誌。

SPI_IOC_RD_MODE32, SPI_IOC_WR_MODE32 ...

傳入一個指向 uin32_t 的指標,該指標將返回(RD)或分配(WR)完整的 SPI 傳輸模式,不受限於單個位元組中的位。

SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ...

傳入一個指向位元組的指標,該位元組將返回(RD)或分配(WR)用於傳輸 SPI 字的位對齊方式。零表示 MSB-first(最高有效位優先);其他值表示較不常見的 LSB-first(最低有效位優先)編碼。在兩種情況下,指定的值都在每個字中右對齊,因此未使用的(TX)或未定義的(RX)位位於 MSB 中。

SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD ...

傳入一個指向位元組的指標,該位元組將返回(RD)或分配(WR)每個 SPI 傳輸字中的位數。值零表示八位。

SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ ...

傳入一個指向 u32 的指標,該指標將返回(RD)或分配(WR)最大 SPI 傳輸速度,單位為赫茲(Hz)。控制器不一定能分配該特定的時鐘速度。

注意事項

  • 目前沒有非同步 I/O 支援;所有操作都是純同步的。

  • 目前沒有辦法報告用於將資料移入/移出給定裝置的實際位元率。

  • 從使用者空間,您目前無法更改片選極性;這可能會損壞與共享 SPI 匯流排的其他裝置之間的傳輸。每個 SPI 裝置在不活動使用時都會被取消選擇,從而允許其他驅動程式與其他裝置通訊。

  • 每個 I/O 請求可以傳輸到 SPI 裝置的位元組數是有限制的。預設值為一頁,但可以透過模組引數更改。

  • 因為 SPI 沒有低階傳輸確認機制,所以當與不存在的裝置通訊時,您通常不會看到任何 I/O 錯誤。

全雙工字元裝置 API

請參閱 spidev_fdx.c 示例程式,瞭解使用全雙工程式設計介面的一個示例。(儘管它並未執行全雙工傳輸。)該模型與核心 spi_sync() 請求中使用的模型相同;單獨的傳輸提供與核心驅動程式相同的功能(除了不是非同步的)。

該示例展示了一個半雙工 RPC 風格的請求和響應訊息。這些請求通常要求在請求和響應之間不取消選擇晶片。可以將多個此類請求連結到一個單獨的核心請求中,甚至允許在每個響應後取消選擇晶片。(其他協議選項包括為每個傳輸段更改字長和位元率。)

要發出全雙工請求,請為同一傳輸提供 rx_buf 和 tx_buf。即使它們是同一個緩衝區也可以。