当前位置: 首页 > news >正文

linux设备驱动之USB驱动-USB主机、设备与Gadget驱动

一、        Linux USB驱动层次

1、        主机侧与设备侧USB驱动

        USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC),每条总线上只用一个主机控制器,负责协调主机和设备之间的通信,而设备不能主动向主机发送任何消息。在linux系统中,USB主机控制器硬件,在其上运行的是USB主机控制器驱动,在主机控制器上的为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制其中的USB设备,后者控制USB设备如何与主机通信。linux 内核中的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数。向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。下图为linux USB驱动总体结构

        右侧Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget Function API和Gadget Function驱动程序。UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信 ,向上提供与硬件相关操作的回调函数。当前Gadget Function API是UDC驱动程序回调函数的简单包装。Gadget Function驱动程序具体控制USB设备功能的实习,使设备表现出"网络连接"、”打印机“或者''USB Mass Storage''等特性,它使用Gadget Function API控制UDC实现上述功能。Gadget Function API把下层的UDC驱动程序和上层的Gadget Function驱动程序隔离开,使得Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。

2、        设备、配置、接口、端点

        在USB设备的逻辑组织中,包括设备、配置、接口和端点4个层次。

        每个USB设备都提供不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。

        在USB协议中,接口由多个端点组成,代表一个基本的功能,是USB设备驱动程序控制的对象,一个功能复杂的USB设备可以具有多个接口。每个配置中可以有多个接口,而设备接口是端点的汇集(Collection)。例如,USB扬声器可以包含一个音频接口及其堆旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数

        端点是UBS通信的最基本形势,每个USB设备接口在主机看来就是一个端点。主机只能通过端点与设备进行通信,以使用设备的功能。在USB系统中每一个端点都有唯一的地址,这是由设备号和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、宽带、端点号和数据包的最大容量等。一个USB端点只能在一个方向上承载数据,从主机到设备(称为输出端点)或者从设备到主机(称为输入端点),因此端点可看作一个单向的管道。端点0通常作为端点,用于设备初始化等。只要设备连接到USB上并且上电,端点0就可以被访问。端点1、2等一般用作数据端点,存放主机与设备间往来的数据。

        总体而言,USB设备非常复杂,由许多不同的逻辑单元组成,这些单元之间的关系如下:

        1)设备通常有一个或多个配置

        2)配置通常有一个或多个接口

        3)接口通常有一个或多个设置

        4)接口有零个或多个端点

        这种层次配置信息在设备中通过一组标准的描述符来描述,如下所示。

        1)设备描述符:关于设备的通用的信息,如供应商ID、产品ID和修订ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在linux内核中,USB设备用usb_device_descriptor结构体,位于include/uapi/linux/usb/ch9.h文件中,代码如下:

 285 /* USB_DT_DEVICE: Device descriptor */286 struct usb_device_descriptor {287     __u8  bLength;288     __u8  bDescriptorType;289 290     __le16 bcdUSB;291     __u8  bDeviceClass;292     __u8  bDeviceSubClass;293     __u8  bDeviceProtocol;294     __u8  bMaxPacketSize0;295     __le16 idVendor;296     __le16 idProduct;297     __le16 bcdDevice;298     __u8  iManufacturer;299     __u8  iProduct;300     __u8  iSerialNumber;301     __u8  bNumConfigurations;302 } __attribute__ ((packed));

        2)配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求。USB配置在内核中使用usb_host_config结构体描述,而USB配置描述符定义为结构体usb_config_descriptor,代码如下:

 346 struct usb_config_descriptor {347     __u8  bLength;348     __u8  bDescriptorType;349 350     __le16 wTotalLength;351     __u8  bNumInterfaces;352     __u8  bConfigurationValue;353     __u8  iConfiguration;354     __u8  bmAttributes;355     __u8  bMaxPower;356 } __attribute__ ((packed));

        3)接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。USB接口在内核中usb_interface结构体描述,而USB接口描述符定义为结构体usb_interface_descriptor,代码如下:

 389 struct usb_interface_descriptor {390     __u8  bLength;391     __u8  bDescriptorType;392 393     __u8  bInterfaceNumber;394     __u8  bAlternateSetting;395     __u8  bNumEndpoints;396     __u8  bInterfaceClass;397     __u8  bInterfaceSubClass;398     __u8  bInterfaceProtocol;399     __u8  iInterface;400 } __attribute__ ((packed));

        4)端点描述符:端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在linux内核中,USB端点使用usb_host_endpoint结构体来描述,而UBS端点描述符定义为usb_endpoint_desciptor结构体,代码如下:

 406 /* USB_DT_ENDPOINT: Endpoint descriptor */407 struct usb_endpoint_descriptor {408     __u8  bLength;409     __u8  bDescriptorType;410 411     __u8  bEndpointAddress;412     __u8  bmAttributes;413     __le16 wMaxPacketSize;414     __u8  bInterval;415 416     /* NOTE:  these two are _only_ in audio endpoints. */417     /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */418     __u8  bRefresh;419     __u8  bSynchAddress;420 } __attribute__ ((packed));

        5)字符串描述符:在其他描述符中会为某些字段提供字符串索引,它们可被用来检索描述性字符串,可以以多种语言形势提供。字符串描述符是可选的,有的设备有,有的设备没有。字符串描述符对应usb_string_descriptor结构体,代码如下:

 371 /* USB_DT_STRING: String descriptor */372 struct usb_string_descriptor {373     __u8  bLength;374     __u8  bDescriptorType;375 376     union {377         __le16 legacy_padding;378         __DECLARE_FLEX_ARRAY(__le16, wData);    /* UTF-16LE encoded */379     };380 } __attribute__ ((packed));

 二、        USB主机控制器驱动

 1、        USB主机控制器驱动的整体结构

        USB主机控制器有这些规格:OHCI、UHCI、EHCI、和xHCI。OHCI驱动程序用来非PC系统上以及带有SIS和ALi芯片组的PC主体上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(包括Intel和Via)上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(Inte和Via)上的USB芯片提供支持。EHCI由USB2.0规范所提出,它兼容于OHCI和UHCI。由于UHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CPU负荷稍重。xHCI,即可扩展的主机控制器接口是Intel公司开发的一个USB主机控制器接口,它目前主要是面向USB3.0的,同时它也支持USB2.0及以下的设备。

        1.主机控制器驱动

        在linux内核中,用usb_hcd结构体描述USB主机控制器驱动,它包含USB主机控制器的“家务”信息、硬件资源、状态描述和用于操作主机控制器的hc_driver等,其定义如下:

 68 struct usb_hcd {69 70     /*71      * housekeeping72      */73     struct usb_bus      self;       /* hcd is-a bus */74     struct kref     kref;       /* reference counter */75 76     const char      *product_desc;  /* product/vendor string */77     int         speed;      /* Speed for this roothub.78                          * May be different from79                          * hcd->driver->flags & HCD_MASK80                          */81     char            irq_descr[24];  /* driver + bus # */82 83     struct timer_list   rh_timer;   /* drives root-hub polling */84     struct urb      *status_urb;    /* the current status urb */85 #ifdef CONFIG_PM86     struct work_struct  wakeup_work;    /* for remote wakeup */87 #endif88     struct work_struct  died_work;  /* for when the device dies */89 90     /*91      * hardware info/state92      */93     const struct hc_driver  *driver;    /* hw-specific hooks */94 95     /*96      * OTG and some Host controllers need software interaction with phys;97      * other external phys should be software-transparent98      */99     struct usb_phy      *usb_phy;
100     struct usb_phy_roothub  *phy_roothub;
101 
102     /* Flags that need to be manipulated atomically because they can
103      * change while the host controller is running.  Always use
104      * set_bit() or clear_bit() to change their values.
105      */
106     unsigned long       flags;
..............
..............
206 
207     /* memory pool for HCs having local memory, or %NULL */
208     struct gen_pool         *localmem_pool;
209 
210     /* more shared queuing code would be good; it should support
211      * smarter scheduling, handle transaction translators, etc;
212      * input size of periodic table to an interrupt scheduler.
213      * (ohci 32, uhci 1024, ehci 256/512/1024).
214      */
215 
216     /* The HC driver's private data is stored at the end of
217      * this structure.
218      */
219     unsigned long hcd_priv[]
220             __attribute__ ((aligned(sizeof(s64))));
221 };

        usb_hcd结构体中第93行的hc_driver成员非常重要,它包含具体的用于操作主机控制器的钩子函数,即“hw-specific hooks”,其定义如下代码: 

237 struct hc_driver {
238     const char  *description;   /* "ehci-hcd" etc */
239     const char  *product_desc;  /* product/vendor string */
240     size_t      hcd_priv_size;  /* size of private data */
241 
242     /* irq handler */
243     irqreturn_t (*irq) (struct usb_hcd *hcd);
244 
245     int flags;
..............
..............
257     /* called to init HCD and root hub */
258     int (*reset) (struct usb_hcd *hcd);
259     int (*start) (struct usb_hcd *hcd);
..............
273     /* cleanly make HCD stop writing memory and doing I/O */
274     void    (*stop) (struct usb_hcd *hcd);
275 
276     /* shutdown HCD */
277     void    (*shutdown) (struct usb_hcd *hcd);
278 
279     /* return current frame number */
280     int (*get_frame_number) (struct usb_hcd *hcd);
281 
282     /* manage i/o requests, device state */
283     int (*urb_enqueue)(struct usb_hcd *hcd,
284                 struct urb *urb, gfp_t mem_flags);
285     int (*urb_dequeue)(struct usb_hcd *hcd,
286                 struct urb *urb, int status);
.............
355         /* Allocate endpoint resources and add them to a new schedule */
356     int (*add_endpoint)(struct usb_hcd *, struct usb_device *,
357                 struct usb_host_endpoint *);
358         /* Drop an endpoint from a new schedule */
359     int (*drop_endpoint)(struct usb_hcd *, struct usb_device *,
360                  struct usb_host_endpoint *);
361         /* Check that a new hardware configuration, set using
362          * endpoint_enable and endpoint_disable, does not exceed bus
363          * bandwidth.  This must be called before any set configuration
364          * or set interface requests are sent to the device.
365          */
366     int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
367         /* Reset the device schedule to the last known good schedule,
368          * which was set from a previous successful call to
369          * check_bandwidth().  This reverts any add_endpoint() and
370          * drop_endpoint() calls since that last successful call.
371          * Used for when a check_bandwidth() call fails due to resource
372          * or bandwidth constraints.
373          */
374     void    (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
375         /* Set the hardware-chosen device address */
376     int (*address_device)(struct usb_hcd *, struct usb_device *udev,
377                   unsigned int timeout_ms);
............
390     int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
391     /* USB 3.0 Link Power Management */
392         /* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
393     int (*enable_usb3_lpm_timeout)(struct usb_hcd *,
394             struct usb_device *, enum usb3_link_state state);
395         /* The xHCI host controller can still fail the command to
396          * disable the LPM timeouts, so this can return an error code.
397          */
398     int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
399             struct usb_device *, enum usb3_link_state state);
400     int (*find_raw_port_number)(struct usb_hcd *, int);
401     /* Call for power on/off the port if necessary */
402     int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
403     /* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */
404 #define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
405     int (*submit_single_step_set_feature)(struct usb_hcd *,
406             struct urb *, int);
407 };

        在linux内核中,使用如下函数来创建HCD:

         

        如下函数被用来增加和移除HCD:

         

        第283行的urb_enqueue()函数非常关键,实际上,上层通过usb_submit_urb()提交1个USB请求后,该函数调用usb_hcd_submit_urb(),并最终调用至usb_hcd的driver成员(hc_driver类型)的urb_enqueue()函数。

               

         

http://www.dtcms.com/a/332066.html

相关文章:

  • 【Java|第十九篇】面向对象九——String类和枚举类
  • AI更换商品背景,智能融合,无痕修图
  • Java中加载语义模型
  • Windows bypassUAC 提权技法详解(一)
  • 洗浴中心泡池水过滤系统原理深度解析与工程实践
  • RocketMQ 4.9.3源码解读-客户端Consumer消费者组件启动流程分析
  • 具身智能Scaling Law缺失:机器人界的“摩尔定律“何时诞生?
  • Ansible企业级实战
  • centos部署chrome和chromedriver
  • C#WPF实战出真汁03--登录界面设计
  • C#WPF实战出真汁04--登录功能实现
  • 单目操作符与逗号表达式
  • CoreShop商城框架开启多租户(2)
  • 莫队 + 离散化 Ann and Books
  • 【19-模型训练细节 】
  • 业务敏捷性对SAP驱动型企业意味着什么?如何保持企业敏捷性?
  • 零信任架构(Zero Trust Architecture, ZTA)(通过动态验证和最小权限控制,实现对所有访问请求的严格授权和持续监控)
  • latex 中破折号的输入
  • 介绍java中atomic及相关类
  • PERC初探暨小试牛刀
  • Vue3 vxeTree树形组件完全指南:从入门到精通的完整使用教程
  • QT6(可视化UI设计代码实现)
  • MATLAB实现图像增强(直方图均衡化)
  • 数学分析| 极限论| 1.数列极限常用方法总结
  • App冷启动阶段Open Dexfiles实现原理【ART虚拟机系列2】
  • docker nginx 定时脚本保存30天日志信息
  • MFC的使用——使用ChartCtrl绘制曲线
  • 2025.8.13~14 实习总结
  • 计算机网络技术学习-day1《网络乾坤:从比特洪流到协议星河的奇幻之旅》​
  • MCU中的LTDC(LCD-TFT Display Controller)