FunctionFS 的工作原理

概述

從核心的角度來看,FunctionFS 只是一個具有獨特行為的複合功能。它只能在使用者空間驅動程式透過寫入描述符和字串進行註冊後才能新增到 USB 配置中(使用者空間程式必須提供與核心級別複合功能新增到配置時所提供的資訊相同的資訊)。

這尤其意味著複合初始化函式可能不在 init 段中(即,可能無法使用 `__init` 標籤)。

從使用者空間的角度來看,它是一個檔案系統,掛載後提供一個“ep0”檔案。使用者空間驅動程式需要將描述符和字串寫入該檔案。它無需擔心端點、介面或字串編號,只需提供描述符,就像該功能是唯一一個一樣(端點和字串編號從一開始,介面編號從零開始)。FunctionFS 會根據需要更改它們,並處理不同配置中編號不同的情況。

有關 FunctionFS 描述符的更多資訊,請參閱 FunctionFS 描述符

當描述符和字串寫入後,“ep#”檔案就會出現(每個宣告的端點對應一個),它們處理單個端點上的通訊。FunctionFS 再次負責實際編號和配置的更改(這意味著“ep1”檔案可能實際對映到(例如)端點 3(當配置更改時對映到(例如)端點 2))。“ep0”用於接收事件和處理設定請求。

當所有檔案關閉時,該功能會自行停用。

我還想提到的是,FunctionFS 的設計方式允許其多次掛載,因此最終一個 gadget 可以使用多個 FunctionFS 功能。其理念是,每個 FunctionFS 例項都由掛載時使用的裝置名稱標識。

可以想象一個 gadget 具有乙太網、MTP 和 HID 介面,其中後兩者是透過 FunctionFS 實現的。在使用者空間層面,它看起來會像這樣:

$ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
$ ( cd /dev/ffs-hid && hid-daemon ) &

在核心層面,gadget 會檢查 `ffs_data->dev_name` 來識別其 FunctionFS 是為 MTP(“mtp”)還是 HID(“hid”)設計的。

如果沒有提供“functions”模組引數,驅動程式只接受一個具有任意名稱的功能。

當提供了“functions”模組引數時,只接受列出的名稱的功能。特別是,如果“functions”引數的值只是一個單元素列表,那麼行為與完全沒有“functions”時類似;但是,只接受具有指定名稱的功能。

只有在所有宣告的功能檔案系統都被掛載並且所有功能的 USB 描述符都已寫入其 ep0 後,該 gadget 才會註冊。

相反,在第一個 USB 功能關閉其端點後,該 gadget 就會登出。

DMABUF 介面

FunctionFS 額外支援基於 DMABUF 的介面,使用者空間可以將 DMABUF 物件(外部建立的)附加到端點,並隨後使用它們進行資料傳輸。

使用者空間應用程式隨後可以使用此介面在多個介面之間共享 DMABUF 物件,從而允許以零複製方式傳輸資料,例如在 IIO 和 USB 堆疊之間。

作為此介面的一部分,新增了三個 IOCTL。這三個 IOCTL 必須在資料端點上執行(即,非 ep0)。它們是:

FUNCTIONFS_DMABUF_ATTACH(int)

將由檔案描述符標識的 DMABUF 物件附加到資料端點。成功時返回零,錯誤時返回負的 errno 值。

FUNCTIONFS_DMABUF_DETACH(int)

將由檔案描述符標識的給定 DMABUF 物件從資料端點分離。成功時返回零,錯誤時返回負的 errno 值。請注意,關閉端點的檔案描述符將自動分離所有已附加的 DMABUF。

FUNCTIONFS_DMABUF_TRANSFER(struct usb_ffs_dmabuf_transfer_req *)

將先前附加的 DMABUF 加入傳輸佇列。該引數是一個結構體,其中包含 DMABUF 的檔案描述符、要傳輸的位元組大小(通常應與 DMABUF 的大小對應)以及一個目前未使用的“flags”欄位。成功時返回零,錯誤時返回負的 errno 值。