一、USB驅動框架分析
? ? ? ? USB控制器作為device的驅動框架分為:gadget Function驅動、gadget Function API、Composite以及UDC驅動。
- gadget Function 驅動:
- 解釋:是針對 USB 設備特定功能的驅動程序。
- 功能:負責實現 USB 設備對外提供的具體功能,比如一個 USB 存儲設備的 gadget Function 驅動,就負責處理數據的讀寫操作等與存儲功能相關的任務 。
- gadget Function API:
- 解釋:為 gadget Function 驅動提供的一組應用程序接口。
- 功能:方便開發者在編寫 gadget Function 驅動時調用,使驅動能夠更好地與上層應用以及下層的其他組件交互,實現不同功能模塊之間的通信和數據傳遞。
- Composite:
- 解釋:用于組合多個 gadget Function,使一個 USB 設備可以同時提供多種功能。
- 功能:比如一個多功能的 USB 設備,既可以作為存儲設備,又可以作為音頻設備,Composite 就負責將這兩種功能整合起來,讓設備以一個整體的形式被主機識別和使用。
- UDC 驅動:
- 解釋:USB Device Controller 驅動,即 USB 設備控制器驅動。
- 功能:負責管理 USB 設備控制器硬件,處理與硬件相關的底層操作,比如數據的傳輸控制、設備狀態的監測和管理等,是連接硬件和上層軟件的關鍵橋梁 。
1.USB控制器作為Host的時候(USB主機控制器),使用USB主機控制器驅動。USB控制器作為Device時(USB設備控制器)。使用UDC(usb device controller)驅動。
USB控制器作為Device時,驅動架構為:
上層 gadget function 驅動(代表具體設備驅動),比如存儲設備:移動硬盤、U 盤設備;通訊設備:USB 串口、USB 虛擬網卡等;UAC 驅動(USB 聲卡、USB 音頻設備)。
gadget function API(抽象層),向上和向下提供統一的標準接口 API。
composite 中間層(支持多種功能的設備與 USB 復合設備驅動的開發)。
USB控制器是計算機系統中用于管理USB通信的硬件組件。它可以作為Host(主機)或Device(設備)。
- 作為Host(主機控制器)?:當USB控制器作為Host時,它負責管理和控制連接到它的所有USB設備。例如,電腦的USB端口就是一個Host控制器,它可以連接鍵盤、鼠標、U盤等設備。
- 作為Device(設備控制器)?:當USB控制器作為Device時,它是一個被連接到Host的設備。例如,U盤、鼠標、鍵盤等都是Device。
2.gadget function驅動使用usb_function_driver數據結構進行描述:
usb_function_driver
?結構體這個結構體定義了一個 USB 功能驅動程序的基本信息和操作方法。
重要成員:
const char *name
:
- 功能: 用于標識該功能驅動程序的名稱。
- 使用: 在注冊功能驅動程序時,需要提供一個唯一的名稱,以便在系統中識別和引用。
struct module *mod
:
- 功能: 指向該功能驅動程序所屬的模塊。
- 使用: 用于模塊管理,確保在模塊卸載時能夠正確地清理資源。
struct list_head list
:
- 功能: 用于將該功能驅動程序鏈接到一個全局的鏈表中。
- 使用: 內核通過這個鏈表來管理和查找所有已注冊的功能驅動程序。
struct usb_function_instance *(*alloc_inst)(void)
:
- 功能: 用于創建并初始化一個?
usb_function_instance
?實例。- 使用: 當需要使用該功能時,調用這個函數來創建一個實例,并進行必要的初始化操作。
struct usb_function *(*alloc_func)(struct usb_function_instance *inst)
:
- 功能: 用于創建并初始化一個?
usb_function
?實例。- 使用: 在創建?
usb_function_instance
?實例之后,調用這個函數來創建一個具體的?usb_function
?實例,并進行進一步的初始化。
usb_function結構體:
?結構體描述了一個具體的 USB 功能模塊,它包含了功能模塊的名稱、描述符、配置信息等。一個 USB 設備可以包含多個功能模塊,每個功能模塊都對應一個?
usb_functionusb_function
?實例。重要成員及其功能:
const char *name
:
- 功能: 用于標識該功能模塊的名稱。
- 使用: 在注冊功能模塊時,需要提供一個唯一的名稱,以便在系統中識別和引用。
struct usb_gadget_strings**strings
:
- 功能: 用于存儲該功能模塊的字符串描述符。
- 使用: 當 USB 主機請求字符串描述符時,通過這個成員來提供相應的字符串信息。
struct usb_descriptor_header**fs_descriptors
:
- 功能: 用于存儲該功能模塊的全速描述符。
- 使用: 當 USB 設備工作在全速模式時,通過這個成員來提供相應的描述符信息。
?
3. gadget function api(抽象層):上層為function 驅動,使用function api注冊和注銷,下層為composite驅動使用function api和function驅動綁定和匹配。function驅動要實現usb_function_driver數據結構并向function api層注冊,具體api如下:
1.?
usb_function_register
?函數功能:
usb_function_register
?函數用于向USB gadget框架注冊一個新的USB功能驅動。注冊后,該功能驅動可以被USB gadget設備使用,以提供特定的USB功能(如串口、網絡、存儲等)。參數:
struct usb_function_driver *newf
:指向要注冊的新USB功能驅動的結構體指針。這個結構體包含了功能驅動的名稱、功能實現函數等信息。使用案例:
假設我們正在開發一個USB gadget設備,該設備需要提供一個虛擬串口功能。我們可以編寫一個虛擬串口功能驅動,并使用
usb_function_register
函數將其注冊到USB gadget框架中。// 定義虛擬串口功能驅動結構體 static struct usb_function_driver my_serial_driver = {.name = "my_serial",.fops = &my_serial_fops,.bind = my_serial_bind,.unbind = my_serial_unbind,// 其他功能實現函數... };// 在設備初始化時注冊虛擬串口功能驅動 static int __init my_gadget_init(void) {int ret;// 注冊虛擬串口功能驅動ret = usb_function_register(&my_serial_driver);if (ret < 0) {printk(KERN_ERR "Failed to register my_serial driver\n");return ret;}printk(KERN_INFO "my_serial driver registered successfully\n");return 0; }module_init(my_gadget_init);
2.?
usb_function_unregister
?函數功能:
usb_function_unregister
?函數用于從USB gadget框架中注銷一個已注冊的USB功能驅動。注銷后,該功能驅動將不再被USB gadget設備使用。參數:
struct usb_function_driver *fd
:指向要注銷的USB功能驅動的結構體指針。使用案例:
假設我們之前注冊了一個虛擬串口功能驅動,現在由于某種原因(如設備配置變更、驅動更新等),需要將該功能驅動從USB gadget框架中注銷。
// 在設備配置變更時注銷虛擬串口功能驅動 static void my_gadget_config_change(void) {// 注銷虛擬串口功能驅動usb_function_unregister(&my_serial_driver);printk(KERN_INFO "my_serial driver unregistered successfully\n"); }module_init(my_gadget_init);
?
1.?
usb_get_function_instance
?函數參數和功能
- 參數:
const char *name
: 一個字符串,表示要獲取的USB功能實例的名稱。- 功能:
該函數從gadget function API層獲取一個指定名稱的usb_function_instance
結構體實例。這個實例代表了一個特定的USB功能(如Mass Storage、Ethernet等),并且可以被USB gadget設備使用。使用案例
假設你正在開發一個USB gadget設備,該設備需要提供一個Mass Storage功能,以便其他設備可以通過USB接口訪問存儲在設備上的文件。
// 獲取名為"mass_storage"的USB功能實例 struct usb_function_instance *fi = usb_get_function_instance("mass_storage");if (!fi) {// 處理獲取實例失敗的情況printk(KERN_ERR "Failed to get mass storage function instance\n");return -ENODEV; }// 使用獲取到的實例進行進一步的配置和使用 // ...
2.?
usb_put_function_instance
?函數參數和功能
- 參數:
struct usb_function_instance *fi
: 一個指向usb_function_instance
結構體的指針,表示要釋放的USB功能實例。- 功能:
該函數用于釋放一個之前通過usb_get_function_instance
獲取的USB功能實例。這通常在不再需要使用該功能實例時調用,以釋放相關的資源。使用案例
假設你在之前獲取了一個USB功能實例,并且在完成相關操作后,不再需要使用這個實例了。
// 假設fi是之前通過usb_get_function_instance獲取的實例 struct usb_function_instance *fi;// ... 使用fi進行相關操作 ...// 完成操作后,釋放USB功能實例 usb_put_function_instance(fi);
usb_get_function
- 參數:
struct usb_function_instance *fi
: 這是一個指向usb_function_instance
結構體的指針。usb_function_instance
代表一個USB功能實例,它包含了配置和初始化USB功能所需的信息。- 功能:
- 該函數從
usb_function_instance
中獲取一個usb_function
結構體指針。usb_function
結構體描述了具體的USB功能,例如一個USB設備的特定功能模塊(如USB存儲、USB網絡等)。????????使用案例:假設你正在開發一個USB設備驅動程序,該設備需要支持USB存儲功能。你需要從USB功能實例中獲取USB存儲功能的描述信息,以便進一步配置和使用該功能。
// 假設fi是一個已經初始化好的usb_function_instance結構體指針 struct usb_function_instance *fi = ...;// 從fi中獲取USB存儲功能的描述信息 struct usb_function *storage_func = usb_get_function(fi);if (storage_func) {// 配置和使用USB存儲功能// 例如,設置存儲容量、文件系統等configure_storage_function(storage_func);use_storage_function(storage_func); } else {// 處理獲取功能失敗的情況printk(KERN_ERR "Failed to get USB storage function\n"); }
usb_put_function
- 參數:
struct usb_function *f
: 這是一個指向usb_function
結構體的指針。usb_function
結構體描述了具體的USB功能。- 功能:
- 該函數用于釋放一個
usb_function
結構體。它通過調用f->free_func(f)
來執行釋放操作,確保資源被正確清理。????????使用案例:假設你已經使用完了一個USB功能(例如USB存儲功能),現在需要釋放該功能所占用的資源,以避免內存泄漏和資源浪費。
// 假設storage_func是一個已經使用完的usb_function結構體指針 struct usb_function *storage_func = ...;// 釋放USB存儲功能 usb_put_function(storage_func);// 此時storage_func所指向的資源已經被正確釋放,可以安全地將其置為NULL storage_func = NULL;
?
4.在Linux內核中直接使用composite層的USB gadget legacy驅動(如USB音頻設備驅動文件audio.c USB虛擬以太網設備驅動文件ether.c等等)。USB gadget configfs屬于文件系統,可以在用戶空間直接控制內核對象。主要適用于對象很多配置的模塊。
一、Legacy驅動(如audio.c、ether.c)
1.?基本概念
- 定位:Legacy驅動是早期Linux內核中USB Gadget的配置方式,通過直接編寫內核代碼實現功能()。
- 特點:
- 靜態配置:需要在內核源碼中定義功能和參數(如VID/PID),重新編譯內核才能生效。
- 單一功能為主:每個驅動文件(如
audio.c
、ether.c
)通常只實現單一功能(如音頻設備或虛擬網卡)。- 依賴Composite框架:通過
module_usb_composite_driver
宏注冊驅動,將多個功能(Function)組合成一個復合設備()。2.?示例:Legacy驅動的使用
例如,若要將設備配置為USB音頻設備:
- 在內核配置中啟用
CONFIG_USB_G_AUDIO
。- 加載模塊:
modprobe g_audio
。- 設備會被主機識別為一個USB音頻設備。
缺點:靈活性差,修改配置需重新編譯內核,無法動態組合多個功能。
二、USB Gadget ConfigFS
1.?基本概念
- 定位:ConfigFS是一種基于內存的虛擬文件系統,允許用戶空間通過目錄和文件動態配置內核對象。
- 特點:
- 動態配置:無需修改內核代碼,通過腳本或命令行動態組合功能。
- 模塊化:內核提供獨立的功能模塊(如
uac2
、mass_storage
、ecm
等),用戶按需組合。- 用戶空間控制:通過創建目錄、寫入屬性文件、建立符號鏈接完成配置。
2.?核心原理
- ConfigFS掛載:將ConfigFS掛載到
/sys/kernel/config
目錄,生成usb_gadget
子目錄。- 配置步驟:
- 創建Gadget模板:在
usb_gadget
下新建目錄(如g1
),設置VID/PID、字符串描述符等。- 添加功能(Function)?:在
functions
子目錄下創建功能實例(如uac2.0
、ecm
)。- 綁定功能到配置:在
configs
目錄下創建配置,通過符號鏈接將功能關聯到配置。- 綁定UDC控制器:將Gadget綁定到USB設備控制器(UDC),激活設備。
3.?示例:通過ConfigFS配置USB音頻+網卡
# 掛載ConfigFS mount -t configfs none /sys/kernel/config cd /sys/kernel/config/usb_gadget# 創建Gadget模板 mkdir g1 cd g1 echo 0x1d6b > idVendor # Linux Foundation的VID echo 0x0104 > idProduct # 復合設備PID mkdir strings/0x409 # 英文語言ID echo "123456" > strings/0x409/serialnumber echo "My Company" > strings/0x409/manufacturer echo "Composite Device" > strings/0x409/product# 添加音頻功能(uac2) mkdir functions/uac2.0# 添加網卡功能(ecm) mkdir functions/ecm.usb0# 創建配置并綁定功能 mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo "Config 1" > configs/c.1/strings/0x409/configuration ln -s functions/uac2.0 configs/c.1/ ln -s functions/ecm.usb0 configs/c.1/# 綁定UDC控制器(假設UDC為fe200000.dwc3) echo "fe200000.dwc3" > UDC
三、Legacy驅動與ConfigFS的對比
四、實際應用場景
Legacy驅動:
- 嵌入式設備中功能固定的場景(如僅需作為U盤)。
- 示例:加載
g_ether
模塊,直接生成虛擬網卡(。ConfigFS:
- 開發調試:快速測試不同功能組合。
- 復雜設備:如手機通過USB同時提供MTP、ADB、RNDIS等功能(。
- 動態切換配置:通過腳本切換設備模式(如從U盤模式切換到聲卡模式)。
5.UDC驅動函數接口:內核初始化和模塊加載初始化
1.?
usb_udc_init
?函數功能:
usb_udc_init
?函數用于初始化 USB UDC(USB Device Controller)驅動。它創建了一個名為 "udc" 的設備類,并設置了一個設備事件處理函數?usb_udc_uevent
。如果創建設備類失敗,函數會返回相應的錯誤碼。使用案例:
假設你正在開發一個基于 Linux 內核的 USB 設備驅動程序,需要初始化 UDC 驅動。在驅動程序的初始化代碼中,你可以調用?
usb_udc_init
?函數來完成 UDC 驅動的初始化工作。?
static int __init my_usb_driver_init(void) {int ret;ret = usb_udc_init();if (ret) {pr_err("Failed to initialize UDC driver\n");return ret;}// 其他初始化代碼return 0; }
2.?
usb_add_gadget_udc
?函數功能:
usb_add_gadget_udc
?函數用于將一個 USB gadget 設備添加到 UDC 驅動中。它接收一個?struct device *parent
?和一個?struct usb_gadget *gadget
?作為參數,并將?gadget
?添加到 UDC 鏈表中。使用案例:
假設你有一個 USB gadget 設備需要添加到 UDC 驅動中,可以在設備的初始化代碼中調用?
usb_add_gadget_udc
?函數。static int __init my_usb_gadget_init(void) {struct device *parent = /* 獲取 parent 設備 */;struct usb_gadget *gadget = /* 初始化 gadget 設備 */;int ret = usb_add_gadget_udc(parent, gadget);if (ret) {pr_err("Failed to add gadget to UDC\n");return ret;}// 其他初始化代碼return 0; }
3.?
usb_del_gadget_udc
?函數功能:
usb_del_gadget_udc
?函數用于從 UDC 驅動中刪除一個 USB gadget 設備。它接收一個?struct usb_gadget *gadget
?作為參數,并從 UDC 鏈表中移除該?gadget
。使用案例:
假設你需要在驅動程序的退出代碼中刪除一個 USB gadget 設備,可以在退出函數中調用?
usb_del_gadget_udc
?函數。static void __exit my_usb_gadget_exit(void) {struct usb_gadget *gadget = /* 獲取 gadget 設備 */;usb_del_gadget_udc(gadget);// 其他清理代碼 }
6.了解UDC驅動使用usb_udc數據結構進行描述,我們注冊所有的usb_udc都會掛接到udc_list鏈表。usb_gadget_ops是USB設備控制器的硬件操作函數(包含開啟USB設備控制器、停止USB設備控制器,vbus電源等功能)
?