一文搞懂 USB 設備端驅動框架【轉】


轉自:https://www.eet-china.com/mp/a55310.html

hello 大家好,今天帶領大家學習一下USB 設備端驅動 

內核版本:4.4.94

1. Linux USB 子系統

在介紹設備端驅動前,我們先來看看 Linux USB子系統。這里的子系統是相對於整個Linux kernel 來說的,而非單一設備。從整體概括了USB主機端和設備端的通信框架。

Linux kernel 中早已集成了較為完善的USB協議棧,由於其規模龐大,包含多個類別的設備驅動,所以Linux系統中的USB協議棧也被稱為USB子系統。

1.1 主機端

主機端,簡化抽象三層:

  • 各種類設備驅動:mass sotrage, CDC, HID等
  • USB 設備驅動:USB 核心處理
  • 主機控制器驅動:不同的USB主機控制器(OHCI/EHCI/UHCI),抽象為HDC。

1.2 設備端

設備端,也抽象為三層:

  • 設備功能驅動:mass sotage , CDC, HID 等,對應主機端的類設備驅動
  • Gadget 設備驅動:中間層,向下直接和UDC通信,建立鏈接;向上提供通用接口,屏蔽USB請求以及傳輸細節。
  • 設備控制器驅動:UDC驅動,直接處理USB設備控制器。

2. USB 設備驅動

2.1 gadget 驅動框架拆解1

我們將USB 設備端驅動拆解一下,其驅動框架如下:

上文提到,Gadget 設備層起着至關重要的作用。為上層提供通用的驅動框架,與下層UDC通過Gadget Interface 建立聯系。

其中Compsite Framwork 提供了一個通用的usb_gadget_driver 模板,包括各種方法供上層Function driver 使用。(driver/usb/gadget/compsite.c)

從上圖我們可以看出,對於USB設備端驅動開發而言,更多的關注的是Function driver這層。USB 控制相關過程,內核提供了一個中間層幫我們屏蔽掉了。

2.2 gadget 驅動框架拆解2

內核版本:Linux Kernel 4.4.94,我們以這個版本進行拆解分析

4.x 的內核相對於3.x的內核在gadget 驅動上分解的更加完善,顯得目錄結構,層次分明,分工合理,更便於理解。

相對於3.x 的版本,4.4.94這個內核,將原來的、driver/usb/gadget目錄進行拆分。通用接口保持不變,比如compsite.c以及functions.c。將usb function driver 進行細分,分為legacy和functions。

有了這些背景,我們再看4.4.94這版內核,gadget驅動框架,如圖所示(下圖筆者按自己的理解繪制的):

  • legacy:整個Gadget 設備驅動的入口。位於driver/usb/gadget/legacy下,里面給出了常用的usb類設備的驅動sample。其作用就是配置USB設備描述符信息,提供一個usb_composite_driver, 然后注冊到composite層。
  • functions:各種usb 子類設備功能驅動。位於driver/usb/gadget/functions,里面也給出了對應的sample。其作用是配置USB子類協議的接口描述以及其他子類協議,比如uvc協議,hid等。
  • 注意:對於一個compsite 設備一個有一個或者多個function,對應的也就有多個functions driver

從這張圖上,有沒有發現,設備端驅動開發似乎越來越簡單了。沒錯,事實上,我們只需要根據legacy的源碼,添加對應的usb設備描述符信息,以及其他若干配置即可。

換言之,我們只需要關心 legacy 這一丟丟就行,對於functions這層會根據業務需要略微調整,不過整體變動不大。

usb 驅動框架之所以復雜,除了需要研究各種復雜的協議,還融合了各種驅動,對於初學者來說,理解起來有點困難。事實上,光是legacy這里也包含其他驅動,比如webcam里有大名鼎鼎的 v4l2 驅動框架。

所以當我學習USB驅動框架的時候,一定要抓大放小,【把握主要脈絡,忽略細節】。當我們把一個復雜的驅動逐一拆解的話,其實發現,就沒有那么可怕了。

2.3 usb compsite 設備構建

為了便於理解,我們來簡單了解一個usb compsite 設備的構建過程:

假設構建一個usb 復合設備,需要支持uac, uac, hid 三個功能 其驅動框架如下:

  • 首先,我們需要一個驅動入口 legacy,用來配置設備描述信息,支持的協議等
  • 然后添加一個配置支持多種接口,這里支持uvc uac hid, 每個接口對應一個functions driver
  • 最后我們把它注冊到compsite 層
  • 對於functions driver 有個usb function driver list,在內核注冊function driver 時會自動添加到一個鏈表上。functions.c 就是用來管理所有的function drivers

3. USB gadget 驅動剖析

3.1 相關數據結構

在梳理整個框架前我們先梳理一下幾個重要的數據結構,從下到上依次介紹:

usb_udc:

udc 使用,內嵌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 設備操作函數集*/
 struct usb_ep   *ep0;           /* 控制端點,只對setup包響應*/
 struct list_head  ep_list;      /* 將設備的所有端點連成鏈表,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 從設備驅動通用結構。

  • 作用:提供一個通用的usb gadget driver 模板,向下注冊到udc,向上給functions driver提供bind 回調等。

  • 關注:bind 回調、function 驅動名、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 ,設備驅動的入口,用來管理設備配置信息,保存設備描述符。 

重點:關注 bind 方法。

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;

 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:

內嵌gadget對象,以及usb 設備的一些配置和請求,主要用於初始化。

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; /* 設備描述符 */
 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 驅動剖析

如圖為一個通用的usb gadget 驅動剖析,框圖中只列出了兩個function,如果有多個function可以繼續添加。關於udc控制器部分,,沒有繼續畫下去,注意我們始終保持一個原則,【抓大放小】,把握重要的脈絡即可。

有關 usb 設備端驅動,比較復雜圖片包含內容比較多,如果覺得不清楚,后台回復【gadget 驅動】獲取高清圖

分層分塊

上下分層,左右分離的思想。

  • 設備功能驅動
    • legacy 驅動入口
    • functions 驅動實現
  • Gadget 設備層:最重要的是compsite_bind 方法,承上啟下的作用。
  • udc 設備控制器層。usb 協議的真正處理。

驅動走向

  • 向下:usb_composite_driver -> usb_gadget_driver->usb_udc
  • 向上回調:udc_bind_to_driver -> composite_bind -> webcam_bind 其中其主要作用的兩個結構就是usb_gadget_driver 和 usb_compsite_dev。前者向下注冊到udc list 里面,與udc控制器建立綁定關系;后者向上提供接口,供上層配置usb 設備的各種functions 和其他配置信息。

代碼分析

  1. 注冊usb_composite_driver
module_usb_composite_driver(webcam_driver)
     module_driver(webcam_driver, usb_composite_probe, \
         usb_composite_unregister)
  1. 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);
  1. 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);
  1. 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();

其他

關於function driver 我們這里沒有詳細介紹,這個框圖只是一個通用的usb 設備驅動框架圖,對於具體的usb function driver 我們這里沒有做具體分析。

以f_uvc簡單舉例,詳細過程見內核源碼。

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 

一個通用的驅動模板,用來注冊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. 總結

本文以拆解的方式,逐步剝離 usb 設備端驅動框架,帶領大家來重新認識usb 設備端驅動,同時給出了一個 compsite 設備的通用驅動框架模型,並從源碼層次分析整個驅動流程。

有關USB 或者 其他類似的高級驅動,筆者有個建議,在初學時一點更要【把握主次,忽略細節】。

比如一個復合的usb 設備可能包含,uvc,uac,hid,等等,視頻有uvc function驅動和v4l2驅動,uac也有相應的驅動,衍生展開會非常復雜。

所以當我們先掌握設備端驅動框架以及流程,等后面需要加入其他usb function 驅動再去研究其協議或者驅動,以及衍生驅動。

------------ END ------------

 

后台回復『USB』『Linux』閱讀更多相關文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM