一文搞懂 USB 设备端驱动框架

著论准过秦,作赋拟子虚。这篇文章主要讲述一文搞懂 USB 设备端驱动框架相关的知识,希望能为你提供帮助。



hello 大家好,今天带领大家学习一下USB 设备端驱动
内核版本:4.4.94


?1. Linux USB 子系统? 在介绍设备端驱动前,我们先来看看 Linux USB子系统。这里的子系统是相对于整个Linux kernel 来说的,而非单一设备。从整体概括了USB主机端和设备端的通信框架。
Linux kernel 中早已集成了较为完善的USB协议栈,由于其规模庞大,包含多个类别的设备驱动,所以Linux系统中的USB协议栈也被称为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 设备端驱动拆解一下,其驱动框架如下:
一文搞懂 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驱动框架,如图所示(下图笔者按自己的理解绘制的):
一文搞懂 USB 设备端驱动框架

文章图片


  • 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 设备端驱动框架】usb 驱动框架之所以复杂,除了需要研究各种复杂的协议,还融合了各种驱动,对于初学者来说,理解起来有点困难。事实上,光是legacy这里也包含其他驱动,比如webcam里有大名鼎鼎的 v4l2 驱动框架。
所以当我学习USB驱动框架的时候,一定要抓大放小,【把握主要脉络,忽略细节】。当我们把一个复杂的驱动逐一拆解的话,其实发现,就没有那么可怕了。
2.3 usb compsite 设备构建
为了便于理解,我们来简单了解一个usb compsite 设备的构建过程:
假设构建一个usb 复合设备,需要支持uac, uac, hid 三个功能 其驱动框架如下:


一文搞懂 USB 设备端驱动框架

文章图片


  • 首先,我们需要一个驱动入口 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 devicedev;
struct list_headlist;
boolvbus;
;

?usb gadget:?
usb 底层操作,包括udc,端点请求等。
struct usb_gadget
struct work_structwork; /* 工作队列 */
struct usb_udc*udc; /* udc */
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; /*gadget 设备操作函数集*/
struct usb_ep*ep0; /* 控制端点,只对setup包响应*/
struct list_headep_list; /* 将设备的所有端点连成链表,ep0不在其中 */
enum usb_device_speedspeed; /* 高速、全速和低速 */
enum usb_device_speedmax_speed; /* 最大速度 */
enum usb_device_statestate;
const char*name;
struct devicedev;
unsignedout_epnum; /* out ep number */
unsignedin_epnum; /* in ep number */
struct usb_otg_caps*otg_caps;

unsignedsg_supported:1;
unsignedis_otg:1;
unsignedis_a_peripheral:1;
unsignedb_hnp_enable:1;
unsigneda_hnp_support:1;
unsigneda_alt_hnp_support:1;
unsignedquirk_ep_out_aligned_size:1;
unsignedquirk_altset_not_supp:1;
unsignedquirk_stall_not_supp:1;
unsignedquirk_zlp_not_supp:1;
unsignedis_selfpowered:1;
unsigneddeactivated:1;
unsignedconnected: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 gadgets function */
enum usb_device_speed max_speed; /* Highest speed the driver handles */
int(*bind)(struct usb_gadget *gadget, /* the drivers 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_speedmax_speed;
unsignedneeds_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_drivergadget_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. */
u8qw_sign[OS_STRING_QW_SIGN_LEN];
u8b_vendor_code;
struct usb_configuration *os_desc_config;
unsigned intuse_os_string:1;

/* private: */
/* internals */
unsigned intsuspended:1;
struct usb_device_descriptor desc; /* 设备描述符 */
struct list_headconfigs;
struct list_headgstrings;
struct usb_composite_driver *driver; /* composite driver */
u8next_string_id;
char*def_manufacturer;

/* the gadget driver wont enable the data pullup
* while the deactivation count is nonzero.
*/
unsigneddeactivations;

/* the composite driver wont complete the control transfers
* data/status stages till delayed_status is zero.
*/
intdelayed_status;

/* protects deactivations and delayed_status counts*/
spinlock_tlock;

unsignedsetup_pending:1;
unsignedos_desc_pending:1;
;

3.2 驱动剖析
如图为一个通用的usb gadget 驱动剖析,框图中只列出了两个function,如果有多个function可以继续添加。关于udc控制器部分,,没有继续画下去,注意我们始终保持一个原则,【抓大放小】,把握重要的脉络即可。
有关 usb 设备端驱动,比较复杂图片包含内容比较多,如果觉得不清楚,后台回复【gadget 驱动】获取高清图


一文搞懂 USB 设备端驱动框架

文章图片

?分层分块?
上下分层,左右分离的思想。
  • 设备功能驱动

  • 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 驱动再去研究其协议或者驱动,以及衍生驱动。
后台回复【gadget 驱动】获取高清图
如果觉得,文章对你有所帮助,欢迎点赞,分享,收藏,谢谢!
一文搞懂 USB 设备端驱动框架

文章图片




    推荐阅读