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

strongerHuang 2021-06-06 10:02

关注+星标公众,不错过精彩内容

转自 | 漫谈嵌入式


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』阅读更多相关文章。


欢迎关注我的公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

欢迎关注我的视频号:


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

strongerHuang 作者黄工,高级嵌入式软件工程师,分享嵌入式软硬件、物联网、单片机、开发工具、电子等内容。
评论
  • 百佳泰特为您整理2024年12月各大Logo的最新规格信息。——————————USB▶ 百佳泰获授权进行 USB Active Cable 认证。▶ 所有符合 USB PD 3.2 标准的产品都有资格获得USB-IF 认证——————————Bluetooth®▶ Remote UPF Testing针对所有低功耗音频(LE Audio)和网格(Mesh)规范的远程互操作性测试已开放,蓝牙会员可使用该测试,这是随时测试产品的又一绝佳途径。——————————PCI Express▶ 2025年
    百佳泰测试实验室 2024-12-20 10:33 96浏览
  • 户外照明的“璀璨王者”,艾迈斯欧司朗OSCONIQ® C3030降临啦全球领先的光学解决方案供应商艾迈斯欧司朗(瑞士证券交易所股票代码:AMS)近日宣布,推出新一代高性能LED——OSCONIQ® C 3030。这款尖端LED系列专为严苛的户外及体育场照明环境而设计,兼具出色的发光强度与卓越的散热效能。其支持高达3A的驱动电流及最大9W的功率输出,以紧凑扁平封装呈现卓越亮度和可靠性,确保高强度照明持久耐用且性能出众。应用领域01体育场及高杆照明OSCONIQ® C 3030以卓越的光通量密度、出
    艾迈斯欧司朗 2024-12-18 14:25 135浏览
  • ​本文介绍PC电脑端运行VMware环境下,同时烧录固件检测不到设备的解决方法。触觉智能Purple Pi OH鸿蒙开发板演示,搭载了瑞芯微RK3566芯片,类树莓派设计,Laval官方社区主荐,已适配全新OpenHarmony5.0 Release系统!PC端烧录固件时提示没有发现设备按照各型号烧录手册中进入loader模式的操作方法,让开发板连接到PC端。正常来说开发板烧录时会显示“发现一个LOADER设备”,异常情况下,会提示“没有发现设备”,如下图所示: 解决步骤当在烧录系统固
    Industio_触觉智能 2024-12-18 18:07 79浏览
  • 随着工业自动化和智能化的发展,电机控制系统正向更高精度、更快响应和更高稳定性的方向发展。高速光耦作为一种电气隔离与信号传输的核心器件,在现代电机控制中扮演着至关重要的角色。本文将详细介绍高速光耦在电机控制中的应用优势及其在实际工控系统中的重要性。高速光耦的基本原理及优势高速光耦是一种光电耦合器件,通过光信号传递电信号,实现输入输出端的电气隔离。这种隔离可以有效保护电路免受高压、电流浪涌等干扰。相比传统的光耦,高速光耦具备更快的响应速度,通常可以达到几百纳秒到几微秒级别的传输延迟。电气隔离:高速光
    晶台光耦 2024-12-20 10:18 121浏览
  • //```c #include "..\..\comm\AI8051U.h"  // 包含头文件,定义了硬件寄存器和常量 #include "stdio.h"              // 标准输入输出库 #include "intrins.h"         &n
    丙丁先生 2024-12-20 10:18 79浏览
  •         不卖关子先说感受,真本书真是相见恨晚啊。字面意思,见到太晚了,我刚毕业或者刚做电子行业就应该接触到这本书的。我自己跌跌撞撞那么多年走了多少弯路,掉过多少坑,都是血泪史啊,要是提前能看到这本书很多弯路很多坑都是可以避免的,可惜这本书是今年出的,羡慕现在的年轻人能有这么丰富完善的资料可以学习,想当年我纯靠百度和论坛搜索、求助啊,连个正经师傅都没有,从软件安装到一步一布操作纯靠自己瞎摸索,然后就是搜索各种教程视频,说出来都是泪啊。  &
    DrouSherry 2024-12-19 20:00 82浏览
  • 由于该文反应热烈,受到了众多工程师的关注,衷心感谢广大优秀工程师同仁的建言献策。特针对该技术点更新一版相关内容! 再次感谢大家的宝贵建议!填充铜(Solid Copper)和网格铜(Hatched Copper)是PCB设计中两种不同的铺铜方式,它们在电气性能、热管理、加工工艺和成本方面存在一些区别:1. 电气性能:填充铜:提供连续的导电层,具有极低的电阻和最小的电压降。适合大电流应用,并能提供优秀的电磁屏蔽效果,显著提高电磁兼容性。网格铜:由于铜线之间存在间隔,电阻相对较高,电压降也
    为昕科技 2024-12-18 17:11 133浏览
  • 汽车驾驶员监控系统又称DMS,是一种集中在车辆中的技术,用于实时跟踪和评估驾驶员状态及驾驶行为。随着汽车产业智能化转型,整合AI技术的DMS逐渐成为主流,AI模型通过大量数据进行持续训练,使得驾驶监控更加高效和精准。 驾驶员监测系统主要通过传感器、摄像头收集驾驶员的面部图像,定位头部姿势、人脸特征及行为特征,并通过各种异常驾驶行为检测模型运算来识别驾驶员的当前状态。如果出现任何异常驾驶行为(如疲劳,分心,抽烟,接打电话,无安全带等),将发出声音及视觉警报。此外,驾驶员的行为数据会被记录
    启扬ARM嵌入式 2024-12-20 09:14 79浏览
  • You are correct that the length of the via affects its inductance. Not only the length of the via, but also the shape and proximity of the return-current path determines the inductance.   For example, let's work with a four-layer board h
    tao180539_524066311 2024-12-18 15:56 127浏览
  • 在强调可移植性(portable)的年代,人称「二合一笔电」的平板笔电便成为许多消费者趋之若鹜的3C产品。说到平板笔电,不论是其双向连接设计,面板与键盘底座可分离的独特功能,再加上兼具笔电模式、平板模式、翻转模式及帐篷模式等多种使用方式,让使用者在不同的使用情境下都能随意调整,轻巧灵活的便利性也为多数消费者提供了绝佳的使用体验。然而也正是这样的独特设计,潜藏着传统笔电供货商在产品设计上容易忽视的潜在风险。平板笔电Surface Pro 7+ 的各种使用模式。图片出处:Microsoft Comm
    百佳泰测试实验室 2024-12-19 17:40 164浏览
  • By Toradex秦海1). 简介为了保证基于 IEEE 802.3 协议设计的以太网设备接口可以互相兼容互联互通,需要进行 Ethernet Compliance 一致性测试,相关的技术原理说明请参考如下文章,本文就不赘述,主要展示基于 NXP i.MX8M Mini ARM 处理器平台进行 1000M/100M/10M 以太网端口进行一致性测试的测试流程。https://www.toradex.com
    hai.qin_651820742 2024-12-19 15:20 138浏览
  •         在上文中,我们介绍了IEEE 802.3cz[1]协议提出背景,旨在定义一套光纤以太网在车载领域的应用标准,并介绍了XMII以及PCS子层的相关机制,在本篇中,将围绕IEEE 802.3cz-MultiGBASE-AU物理层的两个可选功能进行介绍。EEE功能        节能以太网(Energy-Efficient Ethernet)是用于在网络空闲时降低设备功耗的功能,在802.3cz的定义中,链
    经纬恒润 2024-12-19 18:47 78浏览
  • 沉寂已久的无人出租车赛道,在2024年突然升温了。前脚百度旗下萝卜快跑,宣布无人驾驶单量突破800万单;后脚特斯拉就于北京时间10月11日上午,召开了以“We,Robot”为主题的发布会,公布了无人驾驶车型Cybercab和Robovan,就连低调了好几个月的滴滴也在悄悄扩编,大手笔加码Robotaxi。不止是滴滴、百度、特斯拉,作为Robotaxi的重磅选手,文远知行与小马智行,也分别在10月份先后启动美股IPO,极氪也在近日宣布,其与Waymo合作开发的无人驾驶出行汽车将大规模量产交付,无人
    刘旷 2024-12-19 11:39 135浏览
  • 耳机虽看似一个简单的设备,但不仅只是听音乐功能,它已经成为日常生活和专业领域中不可或缺的一部分。从个人娱乐到专业录音,再到公共和私人通讯,耳机的使用无处不在。使用高质量的耳机不仅可以提供优良的声音体验,还能在长时间使用中保护使用者听力健康。耳机产品的质量,除了验证产品是否符合法规标准,也能透过全面性的测试和认证过程,确保耳机在各方面:从音质到耐用性,再到用户舒适度,都能达到或超越行业标准。这不仅保护了消费者的投资,也提升了该公司在整个行业的产品质量和信誉!客户面临到的各种困难一家耳机制造商想要透
    百佳泰测试实验室 2024-12-20 10:37 138浏览
我要评论
1
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦