1. 程式人生 > 其它 >USB匯流排-Linux核心USB3.0裝置控制器驅動框架分析(四)

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 *);
};