概述
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 <VID>和product id <PID>:
$ 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>
<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>對應於一個允許的功能名稱,<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
其中<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中有項目和組,它們都表示為目錄。項和組之間的區別在於,組可以包含其他組。下圖中只顯示了一個項目。項和組都可以具有屬性,這些屬性表示為文件。用戶可以創建和刪除目錄,但不能刪除文件,文件可以是只讀的或讀寫的,這取決於它們所代表的內容。
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如下:
1. 一個小工具有它的配置組,它有一些屬性(idVendor, idProduct等)和默認子組(configs, functions, strings)。寫入屬性將導致信息存儲在適當的位置。在配置、函數和字符串子組中,用戶可以創建它們的子組來表示給定語言中的配置、函數和字符串組。
2. 用戶創建配置和函數,在配置中創建到函數的符號鏈接。當將gadget的UDC屬性寫入時使用此信息,這意味着將gadget綁定到UDC。驅動程序/usb/gadget/configfs.c中的代碼遍歷所有配置,並且在每個配置中遍歷所有函數並綁定它們。這樣整個gadget就被綁定了。
3. 文件驅動程序/usb/gadget/configfs.c包含以下代碼:
- gadget's config_group
- gadget's default groups (configs, functions, strings)
- associating functions with configurations (symlinks)
4. 個USB函數自然都有自己想要配置的視圖,所以特定函數的config_groups定義在函數實現文件drivers/ USB /gadget/f_*.c中。
5. 函數的代碼是以它所使用的方式編寫的。
Usb_get_function_instance(),它反過來調用request_module。因此,只要modprobe工作正常,特定函數的模塊就會自動加載。請注意,相反的情況是不正確的: 在 gadget 被禁用和卸載后,模塊仍然是加載的。
