概述
USB Linux Gadget是一種具有UDC (USB裝置控制器)的裝置,可以連線到USB主機,以擴充套件其附加功能,如串列埠或大容量儲存能力。
一個gadget被它的主機視為一組配置,每個配置都包含一些介面,從gadget的角度來看,這些介面被稱為功能,每個功能代表一個序列連線或一個SCSI磁碟。
Linux提供了許多gadget可以使用的功能。
建立一個gadget意味著決定將有哪些配置以及每個配置將提供哪些功能。
Configfs(請參閱Configfs—使用者空間驅動的核心物件配置)非常適合告訴核心上述決定。本文件是關於如何實現這一點的。它還描述瞭如何將configfs整合到gadget中。
要求
為了使其工作,配置檔案必須可用,因此CONFIGFS_FS必須為 'y' 或 'm' 在.config中。在撰寫本文時,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需要指定其vendor id
$ echo <VID> > idVendor
$ echo <PID> > idProduct
gadget還需要它的序列號、製造商和產品字串。為了有一個地方儲存它們,必須為每種語言建立一個字串子目錄,例如:
$ mkdir strings/0x409
然後可以指定字串:
$ echo <serial number> > strings/0x409/serialnumber
$ echo <manufacturer> > strings/0x409/manufacturer
$ echo <product> > strings/0x409/product
2. 建立配置
每個 gadget 將由許多配置組成,必須建立相應的目錄:
$ mkdir configs/<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>
其中
$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
每個函式都提供其特定的屬性集,具有隻讀或讀寫訪問許可權。如適用,需要酌情寫入。更多資訊請參考Documentation/ABI/testing/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
其中
$ echo s3c-hsotg > UDC
6. 禁用gadget
$ echo "" > UDC
7. 清理
從配置中刪除功能:
$ rm configs/<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中有專案和組,它們都表示為目錄。項和組之間的區別在於,組可以包含其他組。下圖中只顯示了一個專案。項和組都可以具有屬性,這些屬性表示為檔案。使用者可以建立和刪除目錄,但不能刪除檔案,檔案可以是隻讀的或讀寫的,這取決於它們所代表的內容。
configfs的檔案系統部分操作config_items/groups和configfs_attributes,它們是通用的,對所有配置的元素具有相同的型別。但是,它們被嵌入到特定於使用的更大的結構中。下面的圖片中有一個“cs”,它包含一個config_item和一個“sa”,它包含一個configfs_attribute。
檔案系統檢視是這樣的:
./
./cs (directory)
|
+--sa (file)
|
.
.
.
每當使用者讀取/寫入“sa”檔案時,都會呼叫一個函式,該函式接受一個struct config_item和一個struct configfs_attribute。在上述函式中,使用眾所周知的container_of技術檢索“cs”和“sa”,並呼叫適當的sa函式(show或store)並傳遞“cs”和字元緩衝區。“show”用於顯示檔案的內容(將資料從cs複製到緩衝區),而“store”用於修改檔案的內容(將資料從緩衝區複製到cs),但這取決於兩個函式的實現者來決定它們的操作。
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的更多資訊,請參見Documentation/filesystems/configfs.rst
。
上面描述的概念轉化為USB gadget如下:
- 一個小工具有它的配置組,它有一些屬性(idVendor, idProduct等)和預設子組(configs, functions, strings)。寫入屬性將導致資訊儲存在適當的位置。在配置、函式和字串子組中,使用者可以建立它們的子組來表示給定語言中的配置、函式和字串組。
- 使用者建立配置和函式,在配置中建立到函式的符號連結。當將gadget的UDC屬性寫入時使用此資訊,這意味著將gadget繫結到UDC。驅動程式/usb/gadget/configfs.c中的程式碼遍歷所有配置,並且在每個配置中遍歷所有函式並繫結它們。這樣整個gadget就被繫結了。
- 檔案驅動程式/usb/gadget/configfs.c包含以下程式碼:
- gadget's config_group
- gadget's default groups (configs, functions, strings)
- associating functions with configurations (symlinks)
- 個USB函式自然都有自己想要配置的檢視,所以特定函式的config_groups定義在函式實現檔案drivers/ USB /gadget/f_*.c中。
- 函式的程式碼是以它所使用的方式編寫的。
Usb_get_function_instance(),它反過來呼叫request_module。因此,只要modprobe工作正常,特定函式的模組就會自動載入。請注意,相反的情況是不正確的: 在 gadget 被禁用和解除安裝後,模組仍然是載入的。