透過 configfs 配置 Linux USB gadget

2013年4月25日

概述

Linux USB Gadget 是一個具有 UDC(USB 裝置控制器)的裝置,可以連線到 USB 主機,以擴充套件其額外的功能,例如序列埠或大容量儲存功能。

主機將 gadget 視為一組配置,每個配置包含多個介面,從 gadget 的角度來看,這些介面被稱為功能(functions),每個功能代表著例如一個序列連線或一個 SCSI 磁碟。

Linux 為 gadget 提供了許多可用的功能。

建立 gadget 意味著決定將有哪些配置以及每個配置將提供哪些功能。

Configfs(請參閱 Configfs - 使用者空間驅動的核心物件配置)非常適合用於將上述決定告知核心。本文件介紹瞭如何操作。

本文件還描述了 configfs 如何整合到 gadget 中的設計。

要求

為了使其正常工作,configfs 必須可用,因此 .config 中的 CONFIGFS_FS 必須為 'y' 或 'm'。截至本文撰寫時,USB_LIBCOMPOSITE 會選擇 CONFIGFS_FS。

用法

(描述透過 configfs 提供的第一個功能的原始帖子可以在此處檢視: http://www.spinics.net/lists/linux-usb/msg76388.html

$ modprobe libcomposite
$ mount none $CONFIGFS_HOME -t configfs

其中 CONFIGFS_HOME 是 configfs 的掛載點

1. 建立 gadget

對於要建立的每個 gadget,必須建立其對應的目錄

$ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>

例如:

$ mkdir $CONFIGFS_HOME/usb_gadget/g1

...
...
...

$ cd $CONFIGFS_HOME/usb_gadget/g1

每個 gadget 都需要指定其供應商 ID <VID> 和產品 ID <PID>

$ echo <VID> > idVendor
$ echo <PID> > idProduct

gadget 還需要其序列號、製造商和產品字串。為了有地方儲存這些字串,必須為每種語言建立一個 strings 子目錄,例如:

$ mkdir strings/0x409

然後可以指定這些字串

$ echo <serial number> > strings/0x409/serialnumber
$ echo <manufacturer> > strings/0x409/manufacturer
$ echo <product> > strings/0x409/product

可以作為目錄在語言目錄下建立進一步的自定義字串描述符,字串文字寫入到該字串目錄內的“s”屬性中

$ mkdir strings/0x409/xu.0 $ echo <string text> > strings/0x409/xu.0/s

如果功能驅動程式支援,功能可能允許符號連結到這些自定義字串描述符,以將這些字串與類描述符關聯起來。

2. 建立配置

每個 gadget 將由多個配置組成,必須建立其對應的目錄

$ mkdir configs/<name>.<number>

其中 <name> 可以是檔案系統中合法的任何字串,而 <number> 是配置的編號,例如:

$ mkdir configs/c.1

...
...
...

每個配置也需要其字串,因此必須為每種語言建立一個子目錄,例如:

$ mkdir configs/c.1/strings/0x409

然後可以指定配置字串

$ echo <configuration> > configs/c.1/strings/0x409/configuration

還可以為配置設定一些屬性,例如:

$ echo 120 > configs/c.1/MaxPower

3. 建立功能

gadget 將提供一些功能,對於每個功能,必須建立其對應的目錄

$ mkdir functions/<name>.<instance name>

其中 <name> 對應於允許的功能名稱之一,而例項名稱是檔案系統中允許的任意字串,例如:

$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()

...
...
...

每個功能都提供其特定的屬性集,具有隻讀或讀寫訪問許可權。在適用情況下,需要適當地寫入它們。請參閱 ABI 檔案測試/configfs-usb-gadget 獲取更多資訊。

4. 將功能與其配置關聯

此時已建立了多個 gadget,每個 gadget 都指定了多個配置並提供了多個功能。接下來需要指定哪個功能在哪個配置中可用(同一個功能可以在多個配置中使用)。這透過建立符號連結實現

$ ln -s functions/<name>.<instance name> configs/<name>.<number>

例如:

$ ln -s functions/ncm.usb0 configs/c.1

...
...
...

5. 啟用 gadget

以上所有步驟都旨在將配置和功能組合成 gadget。

一個示例目錄結構可能如下所示

.
./strings
./strings/0x409
./strings/0x409/serialnumber
./strings/0x409/product
./strings/0x409/manufacturer
./configs
./configs/c.1
./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
./configs/c.1/strings
./configs/c.1/strings/0x409
./configs/c.1/strings/0x409/configuration
./configs/c.1/bmAttributes
./configs/c.1/MaxPower
./functions
./functions/ncm.usb0
./functions/ncm.usb0/ifname
./functions/ncm.usb0/qmult
./functions/ncm.usb0/host_addr
./functions/ncm.usb0/dev_addr
./UDC
./bcdUSB
./bcdDevice
./idProduct
./idVendor
./bMaxPacketSize0
./bDeviceProtocol
./bDeviceSubClass
./bDeviceClass

這樣的 gadget 必須最終被啟用,以便 USB 主機能夠列舉它。

為了啟用 gadget,它必須繫結到一個 UDC(USB 裝置控制器)

$ echo <udc name> > UDC

其中 <udc name> 是在 /sys/class/udc/* 中找到的名稱之一,例如:

$ echo s3c-hsotg > UDC

6. 停用 gadget

$ echo "" > UDC

7. 清理

從配置中移除功能

$ rm configs/<config name>.<number>/<function>

其中 <config name>.<number> 指定配置,而 <function> 是一個指向要從配置中移除的功能的符號連結,例如:

$ rm configs/c.1/ncm.usb0

...
...
...

移除配置中的字串目錄

$ rmdir configs/<config name>.<number>/strings/<lang>

例如:

$ rmdir configs/c.1/strings/0x409

...
...
...

並移除配置

$ rmdir configs/<config name>.<number>

例如:

rmdir configs/c.1

...
...
...

移除功能(但功能模組不會被解除安裝)

$ rmdir functions/<name>.<instance name>

例如:

$ rmdir functions/ncm.usb0

...
...
...

移除 gadget 中的字串目錄

$ rmdir strings/<lang>

例如:

$ rmdir strings/0x409

最後移除 gadget

$ cd ..
$ rmdir <gadget name>

例如:

$ rmdir g1

實現設計

下面介紹了 configfs 的工作原理。在 configfs 中有專案(items)和組(groups),兩者都表示為目錄。專案和組之間的區別在於組可以包含其他組。下圖只顯示了一個專案。專案和組都可以擁有屬性,這些屬性表示為檔案。使用者可以建立和刪除目錄,但不能刪除檔案,這些檔案可以是隻讀或讀寫的,取決於它們所代表的內容。

檔案系統檢視將是這樣的

每當使用者讀寫“sa”檔案時,就會呼叫一個接受 struct config_item 和 struct configfs_attribute 的函式。在該函式中,使用眾所周知的 container_of 技術檢索“cs”和“sa”,並呼叫適當的 sa 函式(show 或 store),並傳入“cs”和一個字元緩衝區。“show”用於顯示檔案內容(將資料從 cs 複製到緩衝區),而“store”用於修改檔案內容(將資料從緩衝區複製到 cs),但這兩個函式的實際作用由實現者決定。

./
./cs        (directory)
   |
   +--sa    (file)
   |
   .
   .
   .

檔名由 config item/group 設計者決定,而目錄通常可以隨意命名。一個組可以自動建立一些預設子組。

typedef struct configured_structure cs;
typedef struct specific_attribute sa;

                                       sa
                       +----------------------------------+
        cs             |  (*show)(cs *, buffer);          |
+-----------------+    |  (*store)(cs *, buffer, length); |
|                 |    |                                  |
| +-------------+ |    |       +------------------+       |
| | struct      |-|----|------>|struct            |       |
| | config_item | |    |       |configfs_attribute|       |
| +-------------+ |    |       +------------------+       |
|                 |    +----------------------------------+
| data to be set  |                .
|                 |                .
+-----------------+                .

有關 configfs 的更多資訊,請參閱 Configfs - 使用者空間驅動的核心物件配置

上述概念轉換為 USB gadget 如下

1. gadget 擁有其配置組(config group),其中包含一些屬性(如 idVendor、idProduct 等)和預設子組(configs、functions、strings)。寫入這些屬性會導致資訊儲存在適當的位置。在 configs、functions 和 strings 子組中,使用者可以建立自己的子組來表示給定語言的配置、功能和字串組。

2. 使用者建立配置和功能,並在配置中建立指向功能的符號連結。當 gadget 的 UDC 屬性被寫入時,此資訊會被使用,這意味著將 gadget 繫結到 UDC。drivers/usb/gadget/configfs.c 中的程式碼會遍歷所有配置,並在每個配置中遍歷所有功能並繫結它們。透過這種方式,整個 gadget 都被綁定了。

檔案 drivers/usb/gadget/configfs.c 包含以下程式碼:

  1. gadget 的 config_group

    • gadget 的預設組(configs、functions、strings)

    • 將功能與配置關聯(符號連結)

    • 4. 每個 USB 功能自然都有其自己的配置檢視,因此特定功能的 config_groups 定義在功能實現檔案 drivers/usb/gadget/f_*.c 中。

功能程式碼的編寫方式使其使用

  1. usb_get_function_instance(),它又會呼叫 request_module。因此,只要 modprobe 正常工作,特定功能的模組就會自動載入。請注意,反之則不然:在 gadget 被停用並拆除後,模組仍然保持載入狀態。

© 核心開發社群。 | 由 Sphinx 5.3.0Alabaster 0.7.16 提供技術支援 | 頁面源