1.概述
如下圖所示,USB控制器可以呈現出兩種不同的狀态。USB控制器作為Host時,稱為USB主機控制器,使用USB主機控制器驅動。USB控制器作為Device時,稱為USB裝置控制器,使用UDC(usb device controller)驅動。本節隻分析USB控制器作為Device時的驅動架構。
USB控制器作為Device時,驅動架構可分為5層。最上層的是Gadget Function驅動,代表了具體裝置的驅動,如大容量儲存設備驅動(U盤、移動硬碟等)、通訊類裝置驅動(USB序列槽、USB虛拟網卡等)、UAC驅動(USB麥克風、USB聲霸卡等USB音頻類裝置)。接下來是Gadget Funcation API層,該層是一個抽象層,向上和向下提供統一的API,屏蔽了差異,提高了驅動的相容性。Composite驅動層是一個可選的中間層,可通過一種配置或多種配置高效的支援多種功能的裝置,簡化了USB複合裝置驅動的開發。UDC驅動直接通路硬體,控制USB裝置與USB主機之間的通信。USB裝置控制器通過USB線纜連接配接USB主機控制器,負責USB資料的發送和接收。
2.Gadget Function驅動
Linux核心的USB Gadget Function驅動都在drivers/usb/gadget/function/目錄下,有通訊裝置類(Communication Device Class)驅動(f_acm.c、f_ecm、f_serial.c等)、USB音頻裝置類驅動(f_uac1.c、f_uac2.c、u_audio.c)、大容量儲存設備驅動(f_mass_storage.c)、USB視訊裝置類驅動(f_uvc.c)等。
Gadget Function驅動的入口使用
usb_function_driver
資料結構描述,驅動需要實作
alloc_inst
和
alloc_func
函數。
alloc_inst
建立
usb_function_instance
資料結構并初始化。
alloc_func
建立
usb_function
并初始化,重點是設定裡面的回調函數,通常情況下,不直接使用
usb_function
資料結構,而是嵌入到驅動的資料結構中使用。Composite驅動會通過Gadget Function API回調
alloc_inst
和
alloc_func
函數。
usb_function
了描述Gadget Function驅動,Gadget Function驅動的重點是實作這些回調函數。
[include/linux/usb/composite.h]
struct usb_function_driver {
const char *name;
struct module *mod;
struct list_head list;
// 建立usb_function_instance并初始化
struct usb_function_instance *(*alloc_inst)(void);
// 建立usb_function并初始化
struct usb_function *(*alloc_func)(struct usb_function_instance *inst);
};
struct usb_function { // 描述了一個gadget Function驅動
const char *name; // gadget Function驅動名稱
struct usb_gadget_strings **strings; // 字元串表,由bind配置設定和控制請求提供的語言IDs
struct usb_descriptor_header **fs_descriptors; // full speed描述符
struct usb_descriptor_header **hs_descriptors; // high speed描述符
struct usb_descriptor_header **ss_descriptors; // super speed描述符
struct usb_configuration *config; // usb_add_function函數添加的配置
// 驅動的bind回調函數,配置設定驅動所需的資源,如配置、端點、I/O緩沖區等
int (*bind)(struct usb_configuration *, struct usb_function *);
// 釋放bind配置設定的資源
void (*unbind)(struct usb_configuration *, struct usb_function *);
void (*free_func)(struct usb_function *f); // 釋放usb_function
// 設定可選的配置,有時候驅動可能有多個配置,需要使用set_alt進行切換
int (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
// 擷取目前的設定的可選配置,如果沒有多個配置,則預設使用配置0,則傳回0
int (*get_alt)(struct usb_function *, unsigned interface);
// disable gadget function驅動,主機複位、主機重新配置gadget、斷開連接配接時使用
void (*disable)(struct usb_function *);
// 用于特殊接口的控制請求
int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
// 測試某些裝置類請求能否被處理
bool (*req_match)(struct usb_function *, const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
/* USB 3.0 additions */
// 向GetStatus請求傳回目前gadget Function驅動的狀态
int (*get_status)(struct usb_function *);
// 當接收到SetFeature(FUNCTION_SUSPEND)時,回調該函數
int (*func_suspend)(struct usb_function *, u8 suspend_opt);
/* private: internals */
struct list_head list;
DECLARE_BITMAP(endpoints, 32); // 端點位圖
const struct usb_function_instance *fi;
unsigned int bind_deactivated:1;
};
usb_function_driver
通常使用
DECLARE_USB_FUNCTION_INIT
宏定義并初始化。将宏展開後,其定義了
usb_function_driver
結構體執行個體,主要設定
alloc_inst
和
alloc_func
成員,前置用于建立
usb_function_instance
,表示一個Gadget Function執行個體,後者用于建立
usb_function
并初始化。
usb_function
中的方法實作了具體的Gadget Function驅動。
usb_function_register
和
usb_function_unregister
函數完成
usb_function_driver
結構體的注冊和登出。
[include/linux/usb/composite.h]
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static int __init _name ## mod_init(void) \
{ \
return usb_function_register(&_name ## usb_func); \ // 注冊UAC裝置驅動
} \
static void __exit _name ## mod_exit(void) \
{ \
usb_function_unregister(&_name ## usb_func); \ // 登出UAC裝置驅動
} \
module_init(_name ## mod_init); \ // 子產品初始化
module_exit(_name ## mod_exit) // 子產品解除安裝
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
// 定義UAC2.0的Gadget Function驅動,名稱為uac2_usb_func
static struct usb_function_driver _name ## usb_func = { \
.name = __stringify(_name), \ // 驅動名稱為uac2
.mod = THIS_MODULE, \
.alloc_inst = _inst_alloc, \
.alloc_func = _func_alloc, \
}; \
MODULE_ALIAS("usbfunc:"__stringify(_name));
3.Gadget Function API
Gadget Funcation API是一個抽象層,上層的Gadget Function驅動使用Gadget Funcation API注冊和登出,下層的Composite驅動使用Gadget Funcation API和Gadget Function驅動綁定和比對。Gadget Function驅動需要實作
usb_function_driver
資料結構并向Gadget Funcation API層注冊。
Gadget Function API的主要API如下。
usb_function_register
将注冊的
usb_function_driver
挂到
func_list
連結清單中。
usb_function_instance
函數會周遊
func_list
連結清單,将參數
name
和
usb_function_driver
的
name
進行對比,若名稱一緻,則比對成功,接着調用比對成功的
usb_function_driver
中的
alloc_inst
回調函數擷取
usb_function_instance
,然後将
usb_function_driver
的指針設定到
usb_function_instance
中,最後傳回
usb_function_instance
的指針。
usb_get_function
函數通過回調
alloc_func
函數擷取并初始化
usb_function
。其他API可參考源代碼。
[drivers/usb/gadget/functions.c]
// 向Gadget Function API層注冊Gadget Function驅動
int usb_function_register(struct usb_function_driver *newf)
// 登出Gadget Function驅動
void usb_function_unregister(struct usb_function_driver *fd)
// 從Gadget Function API層擷取usb_function_instance
struct usb_function_instance *usb_get_function_instance(const char *name)
// 回調free_func_inst銷毀usb_function_instance
void usb_put_function_instance(struct usb_function_instance *fi)
// 從Gadget Function API層擷取usb_function_instance
struct usb_function *usb_get_function(struct usb_function_instance *fi)
// 回調free_func銷毀usb_function
void usb_put_function(struct usb_function *f)
4.Composite驅動
Linux核心中的USB composite驅動大多都在drivers/usb/gadget/legacy/目錄下,如USB音頻裝置驅動檔案audio.c,USB虛拟以太網裝置驅動檔案ether.c,HID裝置驅動檔案hid.c。USB composite驅動的核心資料結構為
usb_composite_driver
。composite驅動必須實作裝置描述符
dev
和
bind
回調函數。Composite(複合)裝置使用
usb_composite_dev
資料結構描述,該資料結構在Composite驅動注冊的時候核心會在驅動
bind
函數調用之前自動建立,不需要驅動建立。
gadget
指向dwc3結構體中的
usb_gadget
。
req
在Composite驅動注冊的時候就提前配置設定好,用于響應主機發送的控制請求。
config
指向目前使用的usb配置。
desc
是目前裝置的描述符,在Composite驅動注冊的時候設定。
driver
指向對應的
usb_composite_driver
。
[include/linux/usb/composite.h]
struct usb_composite_driver {
const char *name; // 驅動名稱
const struct usb_device_descriptor *dev; // 裝置描述符,必須定義
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed; // 裝置支援的最大速度
unsigned needs_serial:1;
// 用于配置設定整個裝置共享的資源,使用usb_add_config添加配置,必須實作
int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *); // 銷毀資源
void (*disconnect)(struct usb_composite_dev *); // 可選的驅動disconnect method
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
// composite驅動層提供了預設的實作,即composite_driver_template
struct usb_gadget_driver gadget_driver;
};
struct usb_composite_dev { // 複合裝置
// 隻讀,usb裝置控制器的抽象,指向dwc3結構體中的usb_gadget
struct usb_gadget *gadget;
struct usb_request *req; // 用于響應控制請求,緩沖區提前配置設定好
struct usb_request *os_desc_req; // 用于響應OS描述符,緩沖區提前配置設定
struct usb_configuration *config; // 目前使用配置
// qwSignature part of the OS string
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code; // bMS_VendorCode part of the OS string
struct usb_configuration *os_desc_config; // OS描述符使用的配置
unsigned int use_os_string:1;
unsigned int suspended:1;
struct usb_device_descriptor desc; // 裝置描述符
struct list_head configs;
struct list_head gstrings;
struct usb_composite_driver *driver; // 指向Composite驅動
......
};
核心提供了
module_usb_composite_driver
宏,友善驅動定義Composite驅動。參數為
usb_composite_driver
結構體。使用
usb_composite_probe
注冊Composite驅動。使用
usb_composite_unregister
函數登出Composite驅動。
[include/linux/usb/composite.h]
#define module_usb_composite_driver(__usb_composite_driver) \
module_driver(__usb_composite_driver, usb_composite_probe, \
usb_composite_unregister)
[include/linux/device.h]
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \ // 初始化函數
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \ // 登出函數
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
usb_composite_probe
和
usb_composite_unregister
函數的定義如下。
usb_composite_probe
初始化複合裝置驅動,
usb_composite_unregister
解除安裝複合裝置驅動。
[include/linux/usb/composite.h]
/**
* usb_composite_probe() - register a composite driver
* @driver: the driver to register
*
* Context: single threaded during gadget setup
*
* This function is used to register drivers using the composite driver
* framework. The return value is zero, or a negative errno value.
* Those values normally come from the driver's @bind method, which does
* all the work of setting up the driver to match the hardware.
*
* On successful return, the gadget is ready to respond to requests from
* the host, unless one of its components invokes usb_gadget_disconnect()
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
int usb_composite_probe(struct usb_composite_driver *driver)
/**
* usb_composite_unregister() - unregister a composite driver
* @driver: the driver to unregister
*
* This function is used to unregister drivers using the composite
* driver framework.
*/
void usb_composite_unregister(struct usb_composite_driver *driver)
usb_composite_driver
結構體包含了
usb_gadget_driver
資料結構,用來表示usb裝置驅動。核心在Composite驅動層實作了
usb_gadget_driver
,即
composite_driver_template
變量,所有複合裝置都使用該資料結構,無需驅動實作。Composite驅動使用
usb_composite_probe
注冊時,核心會将
composite_driver_template
中的資料拷貝到
usb_composite_driver
的
gadget_driver
成員。
[include/linux/usb/gadget.h]
struct usb_gadget_driver {
char *function; // 描述usb_gadget_driver的字元串
enum usb_device_speed max_speed; // 該驅動可處理的最大速度
// 回調函數,可通過該函數綁定上層的gadget function驅動
int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
// 端點0控制請求調用,用于描述符和配置的管理,通常在中斷中調用,不可睡眠
int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *);
// 當主機斷開時,所有傳輸停止後調用,可能會在中斷中調用,不可睡眠
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
// usb總線複位時調用,必須實作,在中斷中調用
void (*reset)(struct usb_gadget *);
struct device_driver driver;
};
[drivers/usb/gadget/composite.c]
static const struct usb_gadget_driver composite_driver_template = { // 核心實作的usb裝置驅動
.bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
.reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
5.UDC驅動
UDC驅動子產品定義如下,核心初始化或子產品加載時初始化,建立
udc_class
,設定
uevent
的回調函數為
usb_udc_uevent
。
[drivers/usb/gadget/udc/core.c]
static struct class *udc_class;
static int __init usb_udc_init(void)
{
udc_class = class_create(THIS_MODULE, "udc");
......
udc_class->dev_uevent = usb_udc_uevent;
return 0;
}
subsys_initcall(usb_udc_init);
static void __exit usb_udc_exit(void)
{
class_destroy(udc_class);
}
module_exit(usb_udc_exit);
UDC驅動使用
usb_udc
資料結構描述,注冊的所有
usb_udc
資料結構都會挂到
udc_list
連結清單上。UDC驅動的功能主要由成員
gadget
實作,即
usb_gadget
資料結構。
usb_gadget_ops
是USB裝置控制器的硬體操作函數,包含啟動USB裝置控制器、停止USB裝置控制器、vbus電源等功能。
ep0
表示端點0,驅動注冊時會提前配置設定好,用于響應控制請求。除端點0外,USB裝置驅動還會使用其他的端點,這些端點資料結構挂到
ep_list
連結清單中。
speed
表示USB裝置控制器目前的速度。
max_speed
表示USB裝置控制器最大的速度。
[drivers/usb/gadget/udc/core.c]
static LIST_HEAD(udc_list);
struct usb_udc { // 描述usb裝置控制器
// 指向Composite驅動中的usb_gadget_driver
struct usb_gadget_driver *driver;
// 實作udc驅動的結構體,包含usb裝置控制器硬體操作函數
struct usb_gadget *gadget;
struct device dev;
// usb_udc結構體可以組成一個連結清單
struct list_head list;
bool vbus; // 對于不關心vbus狀态的udc,該值始終為true
};
[include/linux/usb/gadget.h]
struct usb_gadget {
// 用于sysfs_notify的工作隊列
struct work_struct work;
struct usb_udc *udc; // 指向usb_udc
// usb裝置控制器硬體操作函數,不涉及io操作
const struct usb_gadget_ops *ops;
struct usb_ep *ep0; // 端點0,用于響應控制讀寫請求
struct list_head ep_list; // 該usb裝置驅動所需的所有端點連結清單
enum usb_device_speed speed; // 目前連接配接usb主機的速度
enum usb_device_speed max_speed; // udc驅動支援的最大速度
enum usb_device_state state; // 目前的狀态
const char *name; // udc驅動名稱,用與确認控制器硬體類型
struct device dev;
unsigned out_epnum; // 最近使用的輸出端點編号
unsigned in_epnum; // 最近使用的輸入端點編号
unsigned mA; // 最近設定的mA值
struct usb_otg_caps *otg_caps; // OTG的能力
unsigned sg_supported:1; // 是否支援聚合DMA
unsigned is_otg:1; // 是否支援OTG,支援OTG必須提供OTG描述符
unsigned is_a_peripheral:1; // 一般為false除非支援OTG
// 輸出端點的請求緩沖區大小按MaxPacketSize對齊
unsigned quirk_ep_out_aligned_size:1;
unsigned is_selfpowered:1; // 是否是自供電
unsigned connected:1; // 是否連接配接成功
unsigned uvc_enabled:1; // uvc功能是否使能
......
};
struct usb_gadget_ops { // usb裝置控制器硬體操作函數,不涉及端點和io
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
// 下拉讓usb主機感覺到usb裝置接入usb總線,usb主機會枚舉usb裝置
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); // 啟動udc
int (*udc_stop)(struct usb_gadget *); // 停止udc
// 比對usb端點
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *);
};
使用
usb_add_gadget_udc
注冊UDC驅動,首先配置設定一個
usb_udc
資料結構,初始化相關成員,最後将
usb_udc
挂到
udc_list
連結清單中,注冊成功後UDC的狀态為
USB_STATE_NOTATTACHED
。使用
usb_del_gadget_udc
删除UDC驅動,首先回調
pullup
斷開連接配接,然後回調
udc_stop
停止USB裝置控制器,最後從
udc_list
連結清單中删除
usb_udc
。
[drivers/usb/gadget/udc/core.c]
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
/**
* usb_del_gadget_udc - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
* This, will call usb_gadget_unregister_driver() if
* the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget);
Composite驅動調用
usb_gadget_probe_driver
和UDC驅動比對,首先周遊
udc_list
連結清單,若有
usb_udc
的
driver
成員為空,則表示比對成功,接着Composite驅動和UDC驅動綁定,通過将Composite驅動的
usb_composite_driver.gadget_driver
的位址設定到
usb_udc.driver
成員中完成綁定,最後回調
udc_start
啟動USB裝置控制器。調用
usb_gadget_unregister_driver
解除Composite驅動和UDC驅動的綁定關系。
[drivers/usb/gadget/udc/core.c]
int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);