hello 大家好,今天帶領(lǐng)大家學習一下USB設(shè)備端驅(qū)動
內(nèi)核版本:4.4.94
1. Linux USB 子系統(tǒng)在介紹設(shè)備端驅(qū)動前,我們先來看看 Linux USB子系統(tǒng)。這里的子系統(tǒng)是相對于整個Linux kernel 來說的,而非單一設(shè)備。從整體概括了USB主機端和設(shè)備端的通信框架。
Linux kernel 中早已集成了較為完善的USB協(xié)議棧,由于其規(guī)模龐大,包含多個類別的設(shè)備驅(qū)動,所以Linux系統(tǒng)中的USB協(xié)議棧也被稱為USB子系統(tǒng)。
1.1 主機端
主機端,簡化抽象三層:
各種類設(shè)備驅(qū)動:mass sotrage, CDC, HID等
USB 設(shè)備驅(qū)動:USB 核心處理
主機控制器驅(qū)動:不同的USB主機控制器(OHCI/EHCI/UHCI),抽象為HDC。
1.2 設(shè)備端
設(shè)備端,也抽象為三層:
設(shè)備功能驅(qū)動:mass sotage , CDC, HID 等,對應主機端的類設(shè)備驅(qū)動
Gadget 設(shè)備驅(qū)動:中間層,向下直接和UDC通信,建立鏈接;向上提供通用接口,屏蔽USB請求以及傳輸細節(jié)。
設(shè)備控制器驅(qū)動:UDC驅(qū)動,直接處理USB設(shè)備控制器。
2. USB 設(shè)備驅(qū)動2.1 gadget 驅(qū)動框架拆解1
我們將USB 設(shè)備端驅(qū)動拆解一下。
上文提到,Gadget 設(shè)備層起著至關(guān)重要的作用。為上層提供通用的驅(qū)動框架,與下層UDC通過Gadget Interface 建立聯(lián)系。
其中Compsite Framwork 提供了一個通用的usb_gadget_driver 模板,包括各種方法供上層Function driver 使用。(driver/usb/gadget/compsite.c)
從上圖我們可以看出,對于USB設(shè)備端驅(qū)動開發(fā)而言,更多的關(guān)注的是Function driver這層。USB 控制相關(guān)過程,內(nèi)核提供了一個中間層幫我們屏蔽掉了。
2.2 gadget 驅(qū)動框架拆解2
內(nèi)核版本:Linux Kernel 4.4.94,我們以這個版本進行拆解分析
4.x 的內(nèi)核相對于3.x的內(nèi)核在gadget 驅(qū)動上分解的更加完善,顯得目錄結(jié)構(gòu),層次分明,分工合理,更便于理解。
相對于3.x 的版本,4.4.94這個內(nèi)核,將原來的、driver/usb/gadget目錄進行拆分。通用接口保持不變,比如compsite.c以及functions.c。將usb function driver 進行細分,分為legacy和functions。
有了這些背景,我們再看4.4.94這版內(nèi)核,gadget驅(qū)動框架。
legacy:整個Gadget 設(shè)備驅(qū)動的入口。位于driver/usb/gadget/legacy下,里面給出了常用的usb類設(shè)備的驅(qū)動sample。其作用就是配置USB設(shè)備描述符信息,提供一個usb_composite_driver, 然后注冊到composite層。
functions:各種usb 子類設(shè)備功能驅(qū)動。位于driver/usb/gadget/functions,里面也給出了對應的sample。其作用是配置USB子類協(xié)議的接口描述以及其他子類協(xié)議,比如uvc協(xié)議,hid等。
注意:對于一個compsite 設(shè)備一個有一個或者多個function,對應的也就有多個functions driver
從這張圖上,有沒有發(fā)現(xiàn),設(shè)備端驅(qū)動開發(fā)似乎越來越簡單了。沒錯,事實上,我們只需要根據(jù)legacy的源碼,添加對應的usb設(shè)備描述符信息,以及其他若干配置即可。
換言之,我們只需要關(guān)心 legacy 這一丟丟就行,對于functions這層會根據(jù)業(yè)務(wù)需要略微調(diào)整,不過整體變動不大。
usb 驅(qū)動框架之所以復雜,除了需要研究各種復雜的協(xié)議,還融合了各種驅(qū)動,對于初學者來說,理解起來有點困難。事實上,光是legacy這里也包含其他驅(qū)動,比如webcam里有大名鼎鼎的 v4l2 驅(qū)動框架。
所以當我學習USB驅(qū)動框架的時候,一定要抓大放小,【把握主要脈絡(luò),忽略細節(jié)】。當我們把一個復雜的驅(qū)動逐一拆解的話,其實發(fā)現(xiàn),就沒有那么可怕了。
2.3 usb compsite 設(shè)備構(gòu)建
為了便于理解,我們來簡單了解一個usb compsite 設(shè)備的構(gòu)建過程:
假設(shè)構(gòu)建一個usb 復合設(shè)備,需要支持uac, uac, hid 三個功能其驅(qū)動框架。
首先,我們需要一個驅(qū)動入口 legacy,用來配置設(shè)備描述信息,支持的協(xié)議等
然后添加一個配置支持多種接口,這里支持uvc uac hid, 每個接口對應一個functions driver
最后我們把它注冊到compsite 層
對于functions driver 有個usb function driver list,在內(nèi)核注冊function driver 時會自動添加到一個鏈表上。functions.c 就是用來管理所有的function drivers
3. USB gadget 驅(qū)動剖析3.1 相關(guān)數(shù)據(jù)結(jié)構(gòu)
在梳理整個框架前我們先梳理一下幾個重要的數(shù)據(jù)結(jié)構(gòu),從下到上依次介紹:
usb_udc:
udc 使用,內(nèi)嵌usb_gadget_driver 和 usb_gadget
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
bool vbus;
};
usb gadget:
usb 底層操作,包括udc,端點請求等。
struct usb_gadget {
struct work_struct work; /* 工作隊列 */
struct usb_udc *udc; /* udc */
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; /*gadget 設(shè)備操作函數(shù)集*/
struct usb_ep *ep0; /* 控制端點,只對setup包響應*/
struct list_head ep_list; /* 將設(shè)備的所有端點連成鏈表,ep0不在其中 */
enum usb_device_speed speed; /* 高速、全速和低速 */
enum usb_device_speed max_speed; /* 最大速度 */
enum usb_device_state state;
const char *name;
struct device dev;
unsigned out_epnum; /* out ep number */
unsigned in_epnum; /* in ep number */
struct usb_otg_caps *otg_caps;
unsigned sg_supported:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
unsigned quirk_ep_out_aligned_size:1;
unsigned quirk_altset_not_supp:1;
unsigned quirk_stall_not_supp:1;
unsigned quirk_zlp_not_supp:1;
unsigned is_selfpowered:1;
unsigned deactivated:1;
unsigned connected:1;
};
usb_gadget_driver:
usb_gadget_driver - driver for usb ‘slave’ devices. usb 從設(shè)備驅(qū)動通用結(jié)構(gòu)。
作用:提供一個通用的usb gadget driver 模板,向下注冊到udc,向上給functions driver提供bind 回調(diào)等。
關(guān)注:bind 回調(diào)、function 驅(qū)動名、setup 處理請求
struct usb_gadget_driver {
char *function; /* String describing the gadget‘s function */
enum usb_device_speed max_speed; /* Highest speed the driver handles */
int (*bind)(struct usb_gadget *gadget, /* the driver’s bind callback */
struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *, /* 處理ep0 request */
const struct usb_ctrlrequest *);
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
void (*reset)(struct usb_gadget *);
/* FIXME support safe rmmod */
struct device_driver driver;
};
usb_composite_driver:
usb_composite_driver ,設(shè)備驅(qū)動的入口,用來管理設(shè)備配置信息,保存設(shè)備描述符。
重點:關(guān)注 bind 方法。
struct usb_composite_driver {
const char *name; /* 驅(qū)動名字 */
const struct usb_device_descriptor *dev ; /* 設(shè)備描述符 */
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed;
unsigned needs_serial:1;
int (*bind)(struct usb_composite_dev *cdev); /* bind 方法 */
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
struct usb_gadget_driver gadget_driver; /* usb gadget driver */
};
usb_composite_dev:
內(nèi)嵌gadget對象,以及usb 設(shè)備的一些配置和請求,主要用于初始化。
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
struct usb_request *os_desc_req;
struct usb_configuration *config; /* usb 配置信息 */
/* OS String is a custom (yet popular) extension to the USB standard. */
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code;
struct usb_configuration *os_desc_config;
unsigned int use_os_string:1;
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc; /* 設(shè)備描述符 */
struct list_head configs;
struct list_head gstrings;
struct usb_composite_driver *driver; /* composite driver */
u8 next_string_id;
char *def_manufacturer;
/* the gadget driver won‘t enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* the composite driver won’t complete the control transfer‘s
* data/status stages till delayed_status is zero.
*/
int delayed_status;
/* protects deactivations and delayed_status counts*/
spinlock_t lock;
unsigned setup_pending:1;
unsigned os_desc_pending:1;
};
3.2 驅(qū)動剖析
為一個通用的usb gadget 驅(qū)動剖析,框圖中只列出了兩個function,如果有多個function可以繼續(xù)添加。關(guān)于udc控制器部分,,沒有繼續(xù)畫下去,注意我們始終保持一個原則,【抓大放小】,把握重要的脈絡(luò)即可。
分層分塊
上下分層,左右分離的思想。
設(shè)備功能驅(qū)動
legacy 驅(qū)動入口
functions 驅(qū)動實現(xiàn)
Gadget 設(shè)備層:最重要的是compsite_bind 方法,承上啟下的作用。
udc 設(shè)備控制器層。usb 協(xié)議的真正處理。
驅(qū)動走向
向下:usb_composite_driver -》 usb_gadget_driver-》usb_udc
向上回調(diào):udc_bind_to_driver -》 composite_bind -》 webcam_bind其中其主要作用的兩個結(jié)構(gòu)就是usb_gadget_driver 和 usb_compsite_dev。前者向下注冊到udc list 里面,與udc控制器建立綁定關(guān)系;后者向上提供接口,供上層配置usb 設(shè)備的各種functions 和其他配置信息。
代碼分析
注冊usb_composite_driver
module_usb_composite_driver(webcam_driver)
module_driver(webcam_driver, usb_composite_probe,
usb_composite_unregister)
usb_composite_probe
usb_composite_probe(webcam_driver);
driver-》gadget_driver = composite_driver_template;
gadget_driver = &driver-》gadget_driver;
。。。
usb_gadget_probe_driver(composite_driver_template);
udc_bind_to_driver(udc, driver);
composite_driver_template-》bind(udc-》gadget, composite_driver_template);
usb_gadget_udc_start(udc);
composite_bind
composite_bind(udc-》gadget,composite_driver_template);
cdev-》gadget = gadget;
composite_dev_prepare(webcam_driver,cdev);
cdev-》req = usb_ep_alloc_request(gadget-》ep0, GFP_KERNEL); /* 申請端點0 */
cdev-》req-》complete = composite_setup_complete;
cdev-》driver = webcam_driver;
usb_ep_autoconfig_reset(gadget);
webcam_driver-》bind(cdev);
webcam_bind
webcam_bind(cdev);
usb_get_function_instance(“uvc”);
try_get_usb_function_instance(“uvc”);
uvc_alloc_inst();
usb_add_config();
webcam_config_bind();
usb_get_function();
usb_add_function();
others_config_bind();
其他
關(guān)于function driver 我們這里沒有詳細介紹,這個框圖只是一個通用的usb 設(shè)備驅(qū)動框架圖,對于具體的usb function driver 我們這里沒有做具體分析。
以f_uvc簡單舉例,詳細過程見內(nèi)核源碼。
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
usb_function_register(&uvcusb_func);
list_for_each_entry(fd, &func_list, list)
list_add_tail();
DECLARE_USB_FUNCTION_INIT
一個通用的驅(qū)動模板,用來注冊usb_function_driver,并添加到func_list上。
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)
static struct usb_function_driver _name ## usb_func = {
.name = __stringify(_name),
.mod = THIS_MODULE,
.alloc_inst = _inst_alloc,
.alloc_func = _func_alloc,
};
MODULE_ALIAS(“usbfunc:”__stringify(_name));#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);
}
static void __exit _name ## mod_exit(void)
{
usb_function_unregister(&_name ## usb_func);
}
module_init(_name ## mod_init);
module_exit(_name ## mod_exit)
4. 總結(jié)本文以拆解的方式,逐步剝離 usb 設(shè)備端驅(qū)動框架,帶領(lǐng)大家來重新認識usb 設(shè)備端驅(qū)動,同時給出了一個 compsite 設(shè)備的通用驅(qū)動框架模型,并從源碼層次分析整個驅(qū)動流程。
有關(guān)USB 或者 其他類似的高級驅(qū)動,筆者有個建議,在初學時一點更要【把握主次,忽略細節(jié)】。
比如一個復合的usb 設(shè)備可能包含,uvc,uac,hid,等等,視頻有uvc function驅(qū)動和v4l2驅(qū)動,uac也有相應的驅(qū)動,衍生展開會非常復雜。
所以當我們先掌握設(shè)備端驅(qū)動框架以及流程,等后面需要加入其他usb function 驅(qū)動再去研究其協(xié)議或者驅(qū)動,以及衍生驅(qū)動。
編輯:jq
-
usb
+關(guān)注
關(guān)注
60文章
7945瀏覽量
264703 -
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209536
原文標題:一文搞懂 USB 設(shè)備端驅(qū)動框架
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論