USB匯流排-Linux核心USB3.0裝置控制器驅動框架分析(四)
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複合裝置驅動的開發。目前最流行的是使用基於Composite和configfs實現的USB gadget configfs,可在使用者空間靈活的配置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層
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。usb_composite_driver結構體包含了usb_gadget_driver資料結構,用來表示usb裝置驅動。
[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驅動
......
};
4.1.legacy
inux核心中直接使用Composite層的USB gadget legacy驅動大多都在drivers/usb/gadget/legacy/目錄下,如USB音訊裝置驅動檔案audio.c,USB虛擬乙太網裝置驅動檔案ether.c,HID裝置驅動檔案hid.c。legacy驅動可以直接使用核心提供的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)
核心在Composite驅動層實現了usb_gadget_driver,即composite_driver_template變數,所有複合裝置都使用該資料結構,無需驅動實現。
Composite驅動使用usb_composite_probe註冊時,核心會將composite_driver_template中的資料拷貝到usb_composite_driver的gadget_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,
},
};
4.2.USB Gadget Configfs
Configfs是一種基於ram的檔案系統,可以在使用者空間直接控制核心物件,主要適用於核心物件有眾多配置的模組,比如USB複合裝置。Linux 3.11版本引入了USB Gadget Configfs。在使用者層可以通過暴漏出來的API定義USB Gadget裝置的任意功能和配置,極大的方便了USB複合裝置的配置和使用。該部分內容後面將會詳細介紹原理和使用方法。USB Gadget Configfs在drivers/usb/gadget/configfs.c檔案中實現。
5.UDC驅動
5.1.函式介面
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);
使用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)
UDC層還向USB devcie function驅動提供了一些的介面,用來開啟和關閉USB裝置控制器、使能和禁止端點、queues/dequeues I/O請求、分配和釋放usb_request、匹配端點等。這些函式內部會呼叫具體的USB裝置控制器的UDC驅動。RK3399平臺上,就會呼叫dwc3實現的UDC驅動。至於具體內容,後面章節在分析dwc3的UDC驅動時會詳細說明。
[drivers/usb/gadget/udc/core.c]
int usb_ep_enable(struct usb_ep *ep) // 使能端點
int usb_ep_disable(struct usb_ep *ep) // 禁止端點
int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) // queues usb_request
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) // dequeue usb_request
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) // 分配usb_request
void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req) // 釋放usb_request
// 根據描述符,匹配要使用的端點
int usb_gadget_ep_match_desc(struct usb_gadget *gadget, struct usb_ep *ep,
struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp)
5.2.資料結構
UDC驅動使用usb_udc資料結構描述,註冊的所有usb_udc資料結構都會掛到udc_list連結串列上。UDC驅動的功能主要由成員gadget實現,即usb_gadget資料結構。struct usb_gadget_driver由composite層實現,用於連線USB Function驅動和UDC驅動。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_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;
};
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 *);
};