一、USB总线介绍

1.1 简介

我们之前接触过的通信协议有串口、I2C、SPI以及CAN总线,这里我们又去学习USB总线,那USB和之前我们介绍过那些总线有什么区别呢。

通用串行总线(英语:Universal Serial Bus,缩写:USB)是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,并扩展至摄影器材、数字电视(机顶盒)、游戏机等其它相关领域。

在USB总线出现之前,计算机与键盘、鼠标、扫描仪、打印机等的设备连接都使用专用的接口连接,不同的设备的接口不能互用,扩展性很差,每次插拔设备都要关闭计算机,不支持热插拔,且通信速率很低。

为了解决上述问题,USB总线诞生了。USB总线就好像一条管道,管道里流过的东西只要符合USB协议,至于具体流的是什么东西,USB总线并不关心。对应具体的设备上,只要是支持USB协议的设备,都可以连接计算机,如USB键盘、USB鼠标、USB摄像头、USB音箱等。USB的出现简化了计算机与外围设备的连接,增强了扩展性,支持热插拔,且通信速度很快。

1.2 USB协议版本

从USB协议诞生至今,出现了多个USB协议版本,如USB1.0、USB1.1、USB2.0、USB3.0、USB3.1、USB3.2。最新的是USB4.0协议,可直连CPU的PCIe总线,最大速度可达40Gbps,使用Type-C接口,兼容DP视频协议、PD快充协议等,最高支持100W供电。

USB协议版本速率称号最大速率电源类型推出时间
USB1.0 低速(Low-Speed) 1.5Mbps 5V/500mA 半双工 1996年1月
USB1.1 全速(Full-Speed) 12Mbps 5V/500mA 半双工 1998年9月
USB2.0 高速(High-Speed) 480Mbps 5V/500mA 半双工 2000年4月
USB3.0(USB3.2 Gen1) 超高速(SuperSpeed USB) 5Gbps 5V/900mA 全双工 2008年月11月
USB3.1(USB3.2 Gen2) SuperSpeed USB 10Gbps 10Gbps 20V/5A 全双工 2013年月7月
USB3.2(USB3.2 Gen2×2) SuperSpeed USB 20Gps 20Gbps 20V/5A dual-lane 2017年月9月
USB4.0(Gen3×2) ---- 40Gbps 100瓦 single-lane或dual-lane,兼容Thunderbolt3 2019年月8月

注:USB3.2推出时,USB-IF公布了新的命名规范,将USB3.0改名为USB3.2 Gen1,USB3.1改名为USB3.2 Gen2,而将能够使用两个USB Type-C Rx/Tx针脚的USB3.2改名为USB3.2 Gen2×2。

下图是各个版本USB协议使用的标志及接口,USB3.2以后,只使用Type-C接口,包括图中未画出的USB4.0和Thunderbolt3。

伴随着USB的版本迭代,USB又产生了多种连接器类型规范,比如Type A、Type B、Type-C。

下图是USB3.2协议使用的标志,此标志是USB-IF网站上最新的。

下图是USB4.0协议使用的标志,USB4.0使用Gen3 lane,single-lane可达到20Gps,dual-lane为40Gps。USB4™ 20Gbps使用single-lane,USB4™ 40Gbps使用dual-lane。

 

二、USB总线特点

2.1 主从模式

USB是主从模式的总线,主机称为Host,丛机称为Device(设备)。从机与从机之间、主机与主机之间(不包括USB4.0),不能互联。每次通信都是由主机发起,从机不能主动发起通信,只能被动的应答主机的请求。

USB3.0及以后的USB协议,主机也可以和集线器(Hub)通信。为了增加灵活性,又出现了USB OTG(On The Go),USB OTG支持主从切换,同一个设备,在不同场合下,可以在主机和从机之间切换。

USB OTG线中增加了一根USB ID线,当USB ID线上拉时,处于从机模式,当USB ID线接地时,处于主机模式。

2.2 总线结构

在有些场景下,我们期望将一个USB接口扩展为多个USB接口,这时我们就会使用到一个装置,也就是USB Hub,如下图所示:

因此,USB总线可能呈现出树状的拓扑接口吗,USB标准上说USB总线拓扑是一种分层星型结构,如下图所示:

 

从树结构我们可以看到:

  • 树的根节点是USB Host控制器(上图没有画出来),连接在USB Host控制器上的是USB根集线器(Root Hub);
  • USB集线器(Hub)可以将一个USB接口扩展成多个USB接口,扩展出的USB接口又可以通过USB集线器(Hub)扩展,每个USB接口都可以接USB设备;
  • 集线器只能扩展出更多的USB接口,而不能扩展出更多的带宽,所有USB设备共享USB Host控制器的带宽,当有多个USB设备需要较大带宽时,可以考虑将他们接到不同USB Host控制器上的根集线器上,以避免带宽不足;
  • 集线器级联最多5层,因此最大为7层星型结构;这个最大7层星形结构,代表的是一条USB总线,一个USB分层星型结构有且仅有一个USB主机控制器,但并不是说一台计算机就只有一个USB总线;

比如我的计算机内部就是1个USB主控制器+Root Hub,从Windows设备管理器可以看到,下图是其中一条USB总线:

 

2.3 电气特性

下图是USB3.2线缆的示意图,同时兼容USB2.0:

USB使用差分信号传输数据,图中的D+和D-是一对差分线,SSTX+和SSTX-是一对差分线,SSRX+和SSRX-是一对差分线:

  • USB2.0只有一对差分线,即下图中的D+、D-,因此USB2.0是半双工的,不能同时收发数据;
  • USB3.2拥有两对差分线,即SSTX+和SSTX-及SSRX+和SSRX-,因此USB3.2是全双工的,可同时收发数据;

USB3.2和USB2.0使用不同的差分线传输数据,两者互不干扰,可同时工作。USB3.2线缆中保留了USB2.0的数据传输通道,实现了对USB2.0的兼容。USB主机可通过$V_{BUS}$线向设备供电,最大可输出20V/5A,GND是地线。

2.4 热插拔

当一个USB设备插入PC机,PC机怎么知道有设备插入?

如下图所示,USB2.0接口只有4条线:$V_{BUS}$(5V),GND,D-,D+。

           

                       USB低速设备硬件接线图        


                     USB全速(高速)设备硬件接线图            

注意:上图中VCC同$V_{BUS}$。

PC机的USB插孔的D-和D+数据线均连接15K欧姆的下拉电阻。而USB设备端的D-或D+数据线连接1.5K欧姆的上拉电阻。当设备插入PC机的时候,会将PC机的D-或D+端的电压拉高,当PC机在D-或D+端检测到高电平时,就知道有设备插入了。

如果是PC机D-端被拉高,接入的则是USB低速设备;如果是PC机D+端被拉高,接入的则是USB全速或高速设备,具体是全速设备还是高速设备,会由PC机和USB设备发包握手确定。

三、USB传输基础

3.1 传输类型

USB架构包含四种基本类型的数据传输:

  • 控制传输:控制传输用于配置设备、获取设备信息、发送命令到设备、获取设备的状态。每个USB设备都有端点0的控制端点,当USB设备插入到USB主机拓扑网络中时,USB主机就通过端点0与USB设备通信,对USB设备进行配置,便于后续的数据传输。USB协议保证控制传输有足够的带宽。控制传输可靠,时间有保证,但传输的数据量不大。如USB设备的枚举过程就采用的是控制传输;
  • 中断传输:当USB主机请求USB设备传输数据时,中断传输以一个固定的速率传送少量的数据。中断端点的数据传输方式为中断传输,数据传输可靠,实时性高,这里的中断并不是USB设备产生中断,而是USB主机每隔一个固定的时间主动查询USB设备是否有数据要传输,以轮询的方式提高实时性。如USB鼠标采用的是中断传输;
  • 批量传输:批量传输用于传输大量数据。USB协议不保证这些数据传输可以在特定的时间内完成,但保证数据的准确性。如果总线上的带宽不足以发送整个批量包,则将数据拆分为多个包传输。批量传输数据可靠,但实时性较低。如USB硬盘、打印机等设备就采用的是批量传输方式;
  • 等时传输:等时传输也可以传输大量数据,但数据的可靠性无法保证。采用等时传输的USB设备更加注重保持一个恒定的数据传输速度,对数据的可靠性要求不高。如USB摄像头就使用的是等时传输方式;

下表是这四类传输在不同速度模式下支持的最大包长度:

速度模式 低速 全速 高速
控制传输 8 8/16/32/64 64
同步传输 不支持 1023 1024
中断传输 0~8 0~64 0~1024
批量传输 不支持 8/16/32/64 512

3.2 传输要素

USB2.0主机控制器通过把时间在低速、全速模式下分成1毫秒宽的帧(frame),在高速模式下分成125微妙宽的微帧(microfranme),以此来管理传输。主机控制器将每个帧或微帧的一部分分配给各个传输。每个帧(或微帧)以带有时序参考的帧(Start-of-Frame,SOF)开始。超高速总线不使用SOF,但主机控制器仍可以在125微妙的总线时间内安排超高速传输。

USB传输可以安排在1个或多个帧或微帧中,每个传输包含多个事务,每个事务又进一步含有多个信息包(packets)。信息包必须在一个帧或微帧中传输完毕,不能跨帧或微帧。信息包分为4类,令牌类信息包确认事务类型,数据类信息包携带数据和状态代码,握手类信息包携带状态代码,最后一种是特殊类信息包。
USB传输由一个或多个事务(transaction)组成,这些事务可将数据载入端点或从端点取出。USB2.0事务开始于主机在总线上发送的令牌信息包(token packet)。令牌信息包含有目标端点号和方向。IN令牌信息包表示向端点请求数据信息包。OUT令牌信息包则是主机派发数据信息包的先行信息。除了数据,每个数据包还含有错误检查位和一个带有数据顺序值的信息包ID(PID)。许多事务还拥有握手信息包(handshake packet),数据的接收端用它来报告事务成功或失败。对于超高速传输事务,信息包类型和协议有所不同,但却含有相同的地址、错误检查和与数据相配合的数据顺序值。

信息包类型PID名字取值(二进制)传输类型来源说明
令牌 OUT 0001 全部 主机 IN事务中需要的设备和端点地址
令牌 IN 1001 全部 主机 IN事务中需要的设备和端点地址
令牌 SOF 0101 帧开始 主机 SOF标识符和帧号
令牌 SETUP 1101 控制 主机 用于Setup事务的设备和端点地址
数据 DATA0 0011 全部 主机、设备 数据交替或数据PID序列
数据 DATA1 1011 全部 主机、设备 数据交替或数据PID序列
数据 DATA2 0111 等时 主机、设备 数据PID序列
数据 MDATA 1111 等时、分割事务 主机、设备 数据PID序列
握手 ACK 0010 控制、批量、中断 主机、设备 接收端接收到正确的数据信息包
握手 NAK 1010 控制、批量、中断 设备 接收端不能接收数据,或者发送端无法发送数据或无数据要发送
握手 STALL 1110 控制、批量、中断 设备 控制请求不被支持或端点被停止
握手 NYET 0110 控制写、批量、OUT、分割事务 设备 正确的接收了数据信息包,但还没准备好接收下一个,或集线器还没有将数据信息包分割完成
特殊 PRE 1100 控制、中断 主机 主机发出的先行信号
特殊 ERR 1100 全部 集线器 由集线器返回的错误
特殊 SPLIT 1000 全部 主机 分割事务
特殊 PING 0100 控制写、批量、OUT 主机 PING测试
特殊 EXT 0000 主机 扩展,未使用

更多细节参考:USB协议详解

四、USB驱动分析

4.1 驱动分类

USB的驱动可以分为3类:

  • USB主机控制器驱动,主要包括:
    • usb主机控制器驱动的创建;
    • 根hub设备的创建和注册;
  • 主机端USB设备驱动(更准确的说是usb接口驱动);
  • 设备端USB Gadget驱动(专业术语,用于描述连接到计算机的USB的设备的驱动);

通常,对于USB这种标准化的设备,内核已经将USB主机控制器驱动编写好了,设备端Gadget驱动通常只运行固件程序而不是基于Linux, 所以驱动工程师的主要工作就是编写主机端USB设备驱动。

4.2 USB驱动框架

USB驱动框架结构图如下:

USB总线驱动程序包括USB Core和USB HCD两部分,其作用如下:

  • 识别USB设备:
    • 分配地址;
    • 并告诉USB设备;
    • 发出命令获取描述符;
  • 查找并安装对应的USB设备驱动程序(更准确的说是usb接口驱动);;
  • 提供USB读写函数;

USB总线上的所有通信都是由主机发起的,所以本质上,USB都是采用轮询的方式进行的:

  • 主机会使用轮询的方式不断检测总线上是否有设备接入,如果有设备接入相应的D+、D-就会有电平变化;
  • 然后USB总线就会按照USB规定的协议与设备进行通信,设备将存储在自身的设备信息依次交给主机;
  • 主机将这些信息组织起来,上报到内核,内核中的USB子系统再去匹配相应的设备驱动;

上面所说的主机具体指的是USB主机控制器。

4.3  USB Core

USB Core这个模块是纯软件部分,并不代表一个设备,是独立于硬件的协议栈,它是所有USB设备赖以生存的模块,即USB子系统的核心。代码位于kernel/driver/usb/core目录下。

USB核心层向上为USB设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口,维护整个系统的USB设备信息,完成设备热插拔控制、总线数据传输控制等。

USB核心层将用户的请求映射到相关的USB HCD,用户不能直接访问USB HCD,USB核心层就是USB HCD和USB设备的桥梁。

4.4 USB HCD(Host Controller Driver)

硬件主机控制器HOST Contrller之上运行的是USB HCD,是对USB主机控制器的一个抽象,实现USB核心层与主机控制器之间的对话接口。代码位于drivers/usb/host目录下,比如ohci-s3c2410.c文件。

USB主机控制器包含多种不同的类型,即OHCI和UHCI,EHCI,和xHCI,不同类型的USB主机控制器的区别和联系如下:

4.5 USB Devices Driver

USB设备驱动框架如下图所示:

 

为了更好地描述USB设备的特征,USB提出了设备架构的概念。从这个角度来看,可以认为USB设备是由一些配置(configuration)、接口(interface)和端点(endpoint)组成,即一个USB设备可以含有一个或多个配置,在每个配置中可含有一个或多个接口,在每个接口中可含有若干个端点。其中:

  • 配置和接口是对USB设备功能的抽象;
  • 实际的数据传输由端点来完成;
  • 每个USB设备,都可能有多个接口,每个接口都实现了一个功能。在linux,每个USB设备都是一个device(具体是struct usb_device)、每一个接口也是一个device(具体是struct usb_interface,代表一个逻辑上的设备)。不管是USB设备还是USB接口,都会被注册到同一个总线上,也就是usb_bus_type,其之间的区别会在match函数中区分,之后再去绑定不同的driver。
  • 所有的USB设备都会和usb_generic_driver设备驱动匹配,当一个USB设备被插入的时候,USB设备驱动,也就是usb_generic_driver会跟USB设备交互,得到其所有的各种描述符,并为每个接口都定义成为一个device,之后再加载到usb_bus上,让其去匹配其对应的接口驱动程序。

任何USB设备都包含设备描述符,主要用与说明设备树形(设备描述符,配置描述符,接口描述符,端点描述符之间的关系),通常都被固话在设备内部,当主机检测到有设备插入的时候,就会通过控制传输模式将设备描述信息读出来,这个步骤一般是在设备接入主机时设备进行枚举时完成的。

4.6 USB设备地址和端点

每个USB设备都有唯一的设备地址,比如root hub的设备地址为1,设备地址是usb设备连接上主机时由usb主机控制器分配的,usb主机控制器主要依靠这个设备地址对usb设备进行访问。

但是在usb设备内部地址会被分的更细,设备会分出一些端点来,每个端点在设备都会有一个端点号,这个端点号是设备生产时给设定的。如端点0、端点1等,一个usb设备最多可以包含16个端点,每个端点的地址为0~15。

其中每个端点地址对应一个方向。例如端点3-IN,端点3-OUT,这另个含义不同。但是需要注意的是有一个特殊端点-端点0,每个usb必须要有一个端点0,其作用为对设备枚举和对设备进行一些基本的控制功能,端点0也被称作控制端点,并且它与其他的端点还有一个不同之处在于端点0的数据传输方向是双向的,即端点0既可以给主机发送数据,也可以接收主机发送过来的数据,而其他端点都是单向的。

虽然有16个端点,单通常我们只用到3个如下:

  • EP0:传输配置和控制信息;
  • EP1:数据输入IN_EP;
  • EP2:数据输出OUT_EP;

除了端点0,其余的端点在设备配置之前不能与主机通信,只有向主机报告这些端点的特性并确认后才能被激活。

 

五、USB设备驱动架构中的描述符

USB设备使用各种描述符来说明其设备架构,包括设备描述符、配置描述符、接口描述符、端点描述符。

5.1 端点

USB通信最基本的形式是通过端点的东西。USB端点只能往一个方向传送数据,从主机到设备(称为输出端点out)或者从设备到主机(称为输入端点in),端点可以看作是单向的管道。

USB端点有四种不同的类型,分别具有不同的传送数据的方式:对应着我们之前介绍的四种基本类型的数据传输:控制、中断、批量、等时。

内核中使用struct usb_host_endpoint结构体来描述USB端点,其定义在include/linux/usb.h:

/**
 * struct usb_host_endpoint - host-side endpoint descriptor and queue
 * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder
 * @ss_ep_comp: SuperSpeed companion descriptor for this endpoint
 * @ssp_isoc_ep_comp: SuperSpeedPlus isoc companion descriptor for this endpoint
 * @urb_list: urbs queued to this endpoint; maintained by usbcore
 * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
 *      with one or more transfer descriptors (TDs) per urb
 * @ep_dev: ep_device for sysfs info
 * @extra: descriptors following this endpoint in the configuration
 * @extralen: how many bytes of "extra" are valid
 * @enabled: URBs may be submitted to this endpoint
 * @streams: number of USB-3 streams allocated on the endpoint
 *
 * USB requests are always queued to a given endpoint, identified by a
 * descriptor within an active interface in a given USB configuration.
 */
struct usb_host_endpoint {
        struct usb_endpoint_descriptor          desc;
        struct usb_ss_ep_comp_descriptor        ss_ep_comp;
        struct usb_ssp_isoc_ep_comp_descriptor  ssp_isoc_ep_comp;
        struct list_head                urb_list;
        void                            *hcpriv;
        struct ep_device                *ep_dev;        /* For sysfs info */

        unsigned char *extra;   /* Extra descriptors */
        int extralen;
        int enabled;
        int streams;
};

其中部分参数含义如下:

  • desc:端点描述符;
  • ss_ep_comp:超快速端点描述符;
  • urb_list:本端口对应的urb链表;
  • enabled:使能的话urb才能被提交到此端口;

usb_host_endpoint 结构体在另一个名为struct usb_endpoint_descriptor的结构体中包含真正的端点信息,其定义在include/uapi/linux/usb/ch9.h:

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;

        __u8  bEndpointAddress;
        __u8  bmAttributes;
        __le16 wMaxPacketSize;
        __u8  bInterval;

        /* NOTE:  these two are _only_ in audio endpoints. */
        /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
        __u8  bRefresh;
        __u8  bSynchAddress;
} __attribute__ ((packed));

其中部分参数含义如下:

  • bLength:描述符长度;
  • bDescriptorType:描述符类型, 端点描述符类型值是5;
  • bEndpointAddress:端点地址:位[3:0]表示端点号,第7位是方向:0位输出、1为输入;
  • bmAttributes:端点属性:位[1:0]值00表示控制、01表示等时、10表示批量、11表示中断;
  • wMaxPacketSize:本端点接受或发送的最大信息包的大小;
  • bInterval:轮询数据传输端点的时间间隔,用在中断传输上,比如间隔时间查询鼠标的数据;

5.2 接口

USB端口被捆绑为接口,USB接口只处理一种USB逻辑,一个USB接口代表一个逻辑上的设备,比如声卡驱动,就有两个接口:录音接口和播放接口。

内核使用struct usb_interface结构体来描述USB接口。USB核心把该结构体传递给USB设备驱动程序,之后由USB设备驱动程序来负责控制该结构体,该结构定义在include/linux/usb.h:

/**
 * struct usb_interface - what usb device drivers talk to
 * @altsetting: array of interface structures, one for each alternate
 *      setting that may be selected.  Each one includes a set of
 *      endpoint configurations.  They will be in no particular order.
 * @cur_altsetting: the current altsetting.
 * @num_altsetting: number of altsettings defined.
 * @intf_assoc: interface association descriptor
 * @minor: the minor number assigned to this interface, if this
 *      interface is bound to a driver that uses the USB major number.
 *      If this interface does not use the USB major, this field should
 *      be unused.  The driver should set this value in the probe()
 *      function of the driver, after it has been assigned a minor
 *      number from the USB core by calling usb_register_dev().
 * @condition: binding state of the interface: not bound, binding
 *      (in probe()), bound to a driver, or unbinding (in disconnect())
 * @sysfs_files_created: sysfs attributes exist
 * @ep_devs_created: endpoint child pseudo-devices exist
 * @unregistering: flag set when the interface is being unregistered
 * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
 *      capability during autosuspend.
 * @needs_altsetting0: flag set when a set-interface request for altsetting 0
 *      has been deferred.
 * @needs_binding: flag set when the driver should be re-probed or unbound
 *      following a reset or suspend operation it doesn't support.
 * @authorized: This allows to (de)authorize individual interfaces instead
 *      a whole device in contrast to the device authorization.
 * @dev: driver model's view of this device
 * @usb_dev: if an interface is bound to the USB major, this will point
 *      to the sysfs representation for that device.
 * @reset_ws: Used for scheduling resets from atomic context.
 * @resetting_device: USB core reset the device, so use alt setting 0 as
 *      current; needs bandwidth alloc after reset.
 *
 * USB device drivers attach to interfaces on a physical device.  Each
 * interface encapsulates a single high level function, such as feeding
 * an audio stream to a speaker or reporting a change in a volume control.
 * Many USB devices only have one interface.  The protocol used to talk to
 * an interface's endpoints can be defined in a usb "class" specification,
 * or by a product's vendor.  The (default) control endpoint is part of
 * every interface, but is never listed among the interface's descriptors.
 *
 * The driver that is bound to the interface can use standard driver model
 * calls such as dev_get_drvdata() on the dev member of this structure.
 *
 * Each interface may have alternate settings.  The initial configuration
 * of a device sets altsetting 0, but the device driver can change
 * that setting using usb_set_interface().  Alternate settings are often
 * used to control the use of periodic endpoints, such as by having
 * different endpoints use different amounts of reserved USB bandwidth.
 * All standards-conformant USB devices that use isochronous endpoints
 * will use them in non-default settings.
 *
 * The USB specification says that alternate setting numbers must run from
 * 0 to one less than the total number of alternate settings.  But some
 * devices manage to mess this up, and the structures aren't necessarily
 * stored in numerical order anyhow.  Use usb_altnum_to_altsetting() to
 * look up an alternate setting in the altsetting array based on its number.
 */
struct usb_interface {
        /* array of alternate settings for this interface,
         * stored in no particular order */
        struct usb_host_interface *altsetting;

        struct usb_host_interface *cur_altsetting;      /* the currently
                                         * active alternate setting */
        unsigned num_altsetting;        /* number of alternate settings */

        /* If there is an interface association descriptor then it will list
         * the associated interfaces */
        struct usb_interface_assoc_descriptor *intf_assoc;

        int minor;                      /* minor number this interface is
                                         * bound to */
        enum usb_interface_condition condition;         /* state of binding */
        unsigned sysfs_files_created:1; /* the sysfs attributes exist */
        unsigned ep_devs_created:1;     /* endpoint "devices" exist */
        unsigned unregistering:1;       /* unregistration is in progress */
        unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
        unsigned needs_altsetting0:1;   /* switch to altsetting 0 is pending */
        unsigned needs_binding:1;       /* needs delayed unbind/rebind */
        unsigned resetting_device:1;    /* true: bandwidth alloc after reset */
        unsigned authorized:1;          /* used for interface authorization */


        struct device dev;              /* interface specific device info */
        struct device *usb_dev;
        struct work_struct reset_ws;    /* for resets in atomic context */
};

其中部分参数含义如下:

  • altsetting:包含所有可用于该接口的可选设置的接口结构数组。每个 struct usb_host_interface 包含一套端点配置,即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序;
  • cur_altsetting: 指向altsetting内部的指针,表示当前激活的接口配置;
  • num_altsetting:可选设置的数量;
  • minor:如果绑定到这个接口的 USB 驱动使用USB主设备号, 这个变量包含由USB核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效;

其中usb_host_interface 结构定义如下:

/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
        struct usb_interface_descriptor desc;

        int extralen;
        unsigned char *extra;   /* Extra descriptors */

        /* array of desc.bNumEndpoints endpoints associated with this
         * interface setting.  these will be in no particular order.
         */
        struct usb_host_endpoint *endpoint;

        char *string;           /* iInterface string, if present */
};

可以看到该结构成员包含了:

  • desc:接口描述符;
  • endpoint:这个接口包含的所有端点;
  • extra:额外的描述符;

我们重点看一下接口描述符usb_interface_descriptor ,其定义在include/uapi/linux/usb/ch9.h:

/* USB_DT_INTERFACE: Interface descriptor */
struct usb_interface_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;

        __u8  bInterfaceNumber;
        __u8  bAlternateSetting;
        __u8  bNumEndpoints;
        __u8  bInterfaceClass;
        __u8  bInterfaceSubClass;
        __u8  bInterfaceProtocol;
        __u8  iInterface;
} __attribute__ ((packed));

其中部分参数含义如下:

  • bLength:描述符长度;
  • bDescriptorType:描述符类型,接口描述符类型值是4;
  • bInterfaceNumber:接口的编号,相同的编号代表相同的功能接口;
  • bAlternateSetting:接口的设置的编号,同一个接口可以有一个或者多个设置代表该接口下不同的参数;
  • bNumEndpoints:要使用的端点个数(不包括端点0), 表示有多少个端点描述符,比如鼠标就只有一个端点;
  • bInterfaceClass:接口类型;
  • bInterfaceSubClass:接口子类型;
  • bInterfaceProtocol:接口所遵循的协议;
  • iInterface:描述该接口的字符串索引值;

5.3 配置

USB接口被捆绑为配置。一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。例如,一些允许下载固件到其上的设备包含多个配置以完成这个工作,而一个时刻只能激活一个配置。

内核使用usb_host_config来描述USB设备的配置,定义在include/linux/usb.h:

/**
 * struct usb_host_config - representation of a device's configuration
 * @desc: the device's configuration descriptor.
 * @string: pointer to the cached version of the iConfiguration string, if
 *      present for this configuration.
 * @intf_assoc: list of any interface association descriptors in this config
 * @interface: array of pointers to usb_interface structures, one for each
 *      interface in the configuration.  The number of interfaces is stored
 *      in desc.bNumInterfaces.  These pointers are valid only while the
 *      the configuration is active.
 * @intf_cache: array of pointers to usb_interface_cache structures, one
 *      for each interface in the configuration.  These structures exist
 *      for the entire life of the device.
 * @extra: pointer to buffer containing all extra descriptors associated
 *      with this configuration (those preceding the first interface
 *      descriptor).
 * @extralen: length of the extra descriptors buffer.
 *
 * USB devices may have multiple configurations, but only one can be active
 * at any time.  Each encapsulates a different operational environment;
 * for example, a dual-speed device would have separate configurations for
 * full-speed and high-speed operation.  The number of configurations
 * available is stored in the device descriptor as bNumConfigurations.
 *
 * A configuration can contain multiple interfaces.  Each corresponds to
 * a different function of the USB device, and all are available whenever
 * the configuration is active.  The USB standard says that interfaces
 * are supposed to be numbered from 0 to desc.bNumInterfaces-1, but a lot
 * of devices get this wrong.  In addition, the interface array is not
 * guaranteed to be sorted in numerical order.  Use usb_ifnum_to_if() to
 * look up an interface entry based on its number.
 *
 * Device drivers should not attempt to activate configurations.  The choice
 * of which configuration to install is a policy decision based on such
 * considerations as available power, functionality provided, and the user's
 * desires (expressed through userspace tools).  However, drivers can call
 * usb_reset_configuration() to reinitialize the current configuration and
 * all its interfaces.
 */
struct usb_host_config {
        struct usb_config_descriptor    desc;

        char *string;           /* iConfiguration string, if present */

        /* List of any Interface Association Descriptors in this
         * configuration. */
        struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];

        /* the interfaces associated with this configuration,
         * stored in no particular order */
        struct usb_interface *interface[USB_MAXINTERFACES];

        /* Interface information available even when this is not the
         * active configuration */
        struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];

        unsigned char *extra;   /* Extra descriptors */
        int extralen;
};

其中部分参数含义如下:

  • desc:配置描述符;
  • interface:这个配置包含的所有接口;
  • extra:额外的描述符;

其中struct usb_config_descriptor 结构体定义在include/uapi/linux/usb/ch9.h:

/* USB_DT_CONFIG: Configuration descriptor information.
 *
 * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
 * descriptor type is different.  Highspeed-capable devices can look
 * different depending on what speed they're currently running.  Only
 * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG
 * descriptors.
 */
struct usb_config_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;

        __le16 wTotalLength;
        __u8  bNumInterfaces;
        __u8  bConfigurationValue;
        __u8  iConfiguration;
        __u8  bmAttributes;
        __u8  bMaxPower;
} __attribute__ ((packed));

其中部分参数含义如下:

  • bLength:描述符长度;
  • bDescriptorType:描述符类型,配置描述符类型值是2;
  • wTotalLength:配置下面所有描述符的长度,包括这个配置描述符;
  • bNumInterfaces:配置所支持的接口数;
  • bConfigurationValue:配置值,实际上存的就是当前配置在usb_device->config数组中的索引;
  • iConfiguration:描述该配置的字符串的索引值;
  • bmAttributes:供电模式的选择;
  • bMaxPower:设备从总线提取的最大电流;

5.4 设备

一个设备里包含了不同级别的配置,可以有一个或者多个配置。在linux中,结构体usb_device 对应着USB设备,定义在include/linux/usb.h:

/**
 * struct usb_device - kernel's representation of a USB device
 * @devnum: device number; address on a USB bus
 * @devpath: device ID string for use in messages (e.g., /port/...)
 * @route: tree topology hex string for use with xHCI
 * @state: device state: configured, not attached, etc.
 * @speed: device speed: high/full/low (or error)
 * @rx_lanes: number of rx lanes in use, USB 3.2 adds dual-lane support
 * @tx_lanes: number of tx lanes in use, USB 3.2 adds dual-lane support
 * @tt: Transaction Translator info; used with low/full speed dev, highspeed hub
 * @ttport: device port on that tt hub
 * @toggle: one bit for each endpoint, with ([0] = IN, [1] = OUT) endpoints
 * @parent: our hub, unless we're the root
 * @bus: bus we're part of
 * @ep0: endpoint 0 data (default control pipe)
 * @dev: generic device interface
 * @descriptor: USB device descriptor
 * @bos: USB device BOS descriptor set
 * @config: all of the device's configs
 * @actconfig: the active configuration
 * @ep_in: array of IN endpoints
 * @ep_out: array of OUT endpoints
 * @rawdescriptors: raw descriptors for each config
 * @bus_mA: Current available from the bus
 * @portnum: parent port number (origin 1)
 * @level: number of USB hub ancestors
 * @can_submit: URBs may be submitted
 * @persist_enabled:  USB_PERSIST enabled for this device
 * @have_langid: whether string_langid is valid
 * @authorized: policy has said we can use it;
 *      (user space) policy determines if we authorize this device to be
 *      used or not. By default, wired USB devices are authorized.
 *      WUSB devices are not, until we authorize them from user space.
 *      FIXME -- complete doc
 * @authenticated: Crypto authentication passed
 * @wusb: device is Wireless USB
 * @lpm_capable: device supports LPM
 * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
 * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
 * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
 * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
 * @usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled
 * @usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled
 * @string_langid: language ID for strings
 * @product: iProduct string, if present (static)
 * @manufacturer: iManufacturer string, if present (static)
 * @serial: iSerialNumber string, if present (static)
 * @filelist: usbfs files that are open to this device
 * @maxchild: number of ports if hub
 * @quirks: quirks of the whole device
 * @urbnum: number of URBs submitted for the whole device
 * @active_duration: total time device is not suspended
 * @connect_time: time device was first connected
 * @do_remote_wakeup:  remote wakeup should be enabled
 * @reset_resume: needs reset instead of resume
 * @port_is_suspended: the upstream port is suspended (L2 or U3)
 * @wusb_dev: if this is a Wireless USB device, link to the WUSB
 *      specific data for the device.
 * @slot_id: Slot ID assigned by xHCI
 * @removable: Device can be physically removed from this port
 * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
 * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
 * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout.
 * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm()
 *      to keep track of the number of functions that require USB 3.0 Link Power
 *      Management to be disabled for this usb_device.  This count should only
 *      be manipulated by those functions, with the bandwidth_mutex is held.
 * @hub_delay: cached value consisting of:
 *              parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns)
 *
 *      Will be used as wValue for SetIsochDelay requests.
 *
 * Notes:
 * Usbcore drivers should not set usbdev->state directly.  Instead use
 * usb_set_device_state().
 */
struct usb_device {
        int             devnum;
        char            devpath[16];
        u32             route;
        enum usb_device_state   state;
        enum usb_device_speed   speed;
        unsigned int            rx_lanes;
        unsigned int            tx_lanes;

        struct usb_tt   *tt;
        int             ttport;

        unsigned int toggle[2];

        struct usb_device *parent;
        struct usb_bus *bus;
        struct usb_host_endpoint ep0;

        struct device dev;

        struct usb_device_descriptor descriptor;
        struct usb_host_bos *bos;
        struct usb_host_config *config;

        struct usb_host_config *actconfig;
        struct usb_host_endpoint *ep_in[16];
        struct usb_host_endpoint *ep_out[16];

        char **rawdescriptors;

        unsigned short bus_mA;
        u8 portnum;
        u8 level;

        unsigned can_submit:1;
        unsigned persist_enabled:1;
        unsigned have_langid:1;
        unsigned authorized:1;
        unsigned authenticated:1;
        unsigned wusb:1;
        unsigned lpm_capable:1;
        unsigned usb2_hw_lpm_capable:1;
        unsigned usb2_hw_lpm_besl_capable:1;
        unsigned usb2_hw_lpm_enabled:1;
        unsigned usb2_hw_lpm_allowed:1;
        unsigned usb3_lpm_u1_enabled:1;
        unsigned usb3_lpm_u2_enabled:1;
        int string_langid;

        /* static strings from the device */
        char *product;
        char *manufacturer;
        char *serial;

        struct list_head filelist;

        int maxchild;

        u32 quirks;
        atomic_t urbnum;

        unsigned long active_duration;

#ifdef CONFIG_PM
        unsigned long connect_time;

        unsigned do_remote_wakeup:1;
        unsigned reset_resume:1;
        unsigned port_is_suspended:1;
#endif
        struct wusb_dev *wusb_dev;
        int slot_id;
        enum usb_device_removable removable;
        struct usb2_lpm_parameters l1_params;
        struct usb3_lpm_parameters u1_params;
        struct usb3_lpm_parameters u2_params;
        unsigned lpm_disable_count;

        u16 hub_delay;
};

其中部分参数含义如下:

  • devnum; 设备号,是在USB总线的地址;
  • devpath :用于消息的设备ID字符串;
  • route:用于 xHCI 的树拓扑十六进制字符串;
  • state:设备状态:已配置、未连接等等;
  • speed:设备速度:高速、全速、低速或错误;
  • rx_lanes:正在使用的rx lanes数,USB 3.2 增加了dual-lane支持;
  • tx_lanes:正在使用的tx lanes数,USB 3.2 增加了dual-lane支持;
  • tt:处理传输者信息;用于低速、全速设备和高速HUB;
  • ttport:位于tt HUB的设备口;      
  • toggle:每个端点的占一位,表明端点的方向([0] = IN, [1] = OUT);
  • parent:上一级HUB指针,除非是根节点;
  • bus:总线指针;   
  • ep0:端点0,需要注意的是端点0是单独存放在usb_device中的;   
  • dev:一般的设备接口数据结构 ;
  • descriptor:USB设备描述符,   
  • config:设备的所有配置,配置结构体里包含了配置描述符;
  • actconfig:被激活的设备配置;
  • ep_in:所有的USB输入端点数组; 
  • ep_ou:所有的USB输出端点数组;
  • rawdescriptors:每个配置的raw描述符;
  • bus_mA:可使用的总线电流;
  • portnum:父端口号;
  • level:USB HUB的层数;
  • can_submit:URB可被提交标志;
  • persist_enabled:USB_PERSIST使能标志;
  • have_langid:string_langid存在标志;
  • authorized:授权策略;
  • authenticated:加密认证通过;
  • discon_suspended:暂停时断开标志;
  • wusb:无线USB标志;
  • lpm_capable:设备支持LPM标志位;
  • usb2_hw_lpm_capable:device can perform USB2 hardware LPM;
  • usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM;
  • usb2_hw_lpm_enabled: USB2 hardware LPM is enabled ;
  • usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled;
  • usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled;
  • usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled;
  • string_langid:字符串语言ID;
  • product:产品名;
  • manufacturer:厂商名;
  • serial:产品串号; 
  • filelist:此设备打开的usbfs文件;
  • maxchild; (若为HUB)接口数;
  • quirks: quirks of the whole device;
  • urbnum: 为整个设备提交的URBs数量;
  • active_duration:设备未挂起的总时间;
  • connect_time:设备首次连接的时间;
  • do_remote_wakeup:远程唤醒 ;
  • reset_resume:使用复位替代唤醒; 
  • port_is_suspended:上游端口被挂起(L2 或 U3);
  • wusb_dev:(如果为无线USB)连接到WUSB特定的数据结构;
  • slot_id:xHCI 分配的槽位 ID;
  • removable: Device can be physically removed from this port;
  • ......

usb_device_descriptor对应于协议中的设备描述符,其定义在include/uapi/linux/usb/ch9.h:

/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;

        __le16 bcdUSB;
        __u8  bDeviceClass;
        __u8  bDeviceSubClass;
        __u8  bDeviceProtocol;
        __u8  bMaxPacketSize0;
        __le16 idVendor;
        __le16 idProduct;
        __le16 bcdDevice;
        __u8  iManufacturer;
        __u8  iProduct;
        __u8  iSerialNumber;
        __u8  bNumConfigurations;
} __attribute__ ((packed));

其中部分参数含义如下:

  • bLength:描述符的长度;
  • bDescriptorType:描述符的类型,设备描述符类型值为1;
  • bcdUSB:usb版本号,比如usb2.0;
  • bDeviceClass:USB分配的设备类code;
  • bDeviceSubClass:USB分配的设备子类code;
  • bDeviceProtocol:USB分配的协议code;
  • bMaxPacketSize0:端点0最大包大小;
  • idVendor:厂商编号;
  • idProduct:产品编号;
  • bcdDevice:设备出厂编号;
  • iManufacturer:描述厂商字符串的索引;
  • iProduct:描述产品字符串的索引;
  • iSerialNumber:描述设备序列号字符串的索引;
  • bNumConfigurations:配置描述符的个数,表示有多少个配置描述符;

5.5 URB(USB Request Block)

linux内核通过一个称为urb(usb请求块)的东西和所有的USB设备通信,urb是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,这个请求块使用struct urb结构体来形容,定义在include/linux/usb.h:

/**
 * struct urb - USB Request Block
 * @urb_list: For use by current owner of the URB.
 * @anchor_list: membership in the list of an anchor
 * @anchor: to anchor URBs to a common mooring
 * @ep: Points to the endpoint's data structure.  Will eventually
 *      replace @pipe.
 * @pipe: Holds endpoint number, direction, type, and more.
 *      Create these values with the eight macros available;
 *      usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
 *      (control), "bulk", "int" (interrupt), or "iso" (isochronous).
 *      For example usb_sndbulkpipe() or usb_rcvintpipe().  Endpoint
 *      numbers range from zero to fifteen.  Note that "in" endpoint two
 *      is a different endpoint (and pipe) from "out" endpoint two.
 *      The current configuration controls the existence, type, and
 *      maximum packet size of any given endpoint.
 * @stream_id: the endpoint's stream ID for bulk streams
 * @dev: Identifies the USB device to perform the request.
 * @status: This is read in non-iso completion functions to get the
 *      status of the particular request.  ISO requests only use it
 *      to tell whether the URB was unlinked; detailed status for
 *      each frame is in the fields of the iso_frame-desc.
 * @transfer_flags: A variety of flags may be used to affect how URB
 *      submission, unlinking, or operation are handled.  Different
 *      kinds of URB can use different flags.
 * @transfer_buffer:  This identifies the buffer to (or from) which the I/O
 *      request will be performed unless URB_NO_TRANSFER_DMA_MAP is set
 *      (however, do not leave garbage in transfer_buffer even then).
 *      This buffer must be suitable for DMA; allocate it with
 *      kmalloc() or equivalent.  For transfers to "in" endpoints, contents
 *      of this buffer will be modified.  This buffer is used for the data
 *      stage of control transfers.
 * @transfer_dma: When transfer_flags includes URB_NO_TRANSFER_DMA_MAP,
 *      the device driver is saying that it provided this DMA address,
 *      which the host controller driver should use in preference to the
 *      transfer_buffer.
 * @sg: scatter gather buffer list, the buffer size of each element in
 *      the list (except the last) must be divisible by the endpoint's
 *      max packet size if no_sg_constraint isn't set in 'struct usb_bus'
 * @num_mapped_sgs: (internal) number of mapped sg entries
 * @num_sgs: number of entries in the sg list
 * @transfer_buffer_length: How big is transfer_buffer.  The transfer may
 *      be broken up into chunks according to the current maximum packet
 *      size for the endpoint, which is a function of the configuration
 *      and is encoded in the pipe.  When the length is zero, neither
 *      transfer_buffer nor transfer_dma is used.
 * @actual_length: This is read in non-iso completion functions, and
 *      it tells how many bytes (out of transfer_buffer_length) were
 *      transferred.  It will normally be the same as requested, unless
 *      either an error was reported or a short read was performed.
 *      The URB_SHORT_NOT_OK transfer flag may be used to make such
 *      short reads be reported as errors.
 * @setup_packet: Only used for control transfers, this points to eight bytes
 *      of setup data.  Control transfers always start by sending this data
 *      to the device.  Then transfer_buffer is read or written, if needed.
 * @setup_dma: DMA pointer for the setup packet.  The caller must not use
 *      this field; setup_packet must point to a valid buffer.
 * @start_frame: Returns the initial frame for isochronous transfers.
 * @number_of_packets: Lists the number of ISO transfer buffers.
 * @interval: Specifies the polling interval for interrupt or isochronous
 *      transfers.  The units are frames (milliseconds) for full and low
 *      speed devices, and microframes (1/8 millisecond) for highspeed
 *      and SuperSpeed devices.
 * @error_count: Returns the number of ISO transfers that reported errors.
 * @context: For use in completion functions.  This normally points to
 *      request-specific driver context.
 * @complete: Completion handler. This URB is passed as the parameter to the
 *      completion function.  The completion function may then do what
 *      it likes with the URB, including resubmitting or freeing it.
 * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to
  collect the transfer status for each buffer.
 *
 * This structure identifies USB transfer requests.  URBs must be allocated by
 * calling usb_alloc_urb() and freed with a call to usb_free_urb().
 * Initialization may be done using various usb_fill_*_urb() functions.  URBs
 * are submitted using usb_submit_urb(), and pending requests may be canceled
 * using usb_unlink_urb() or usb_kill_urb().
 *
 * Data Transfer Buffers:
 *
 * Normally drivers provide I/O buffers allocated with kmalloc() or otherwise
 * taken from the general page pool.  That is provided by transfer_buffer
 * (control requests also use setup_packet), and host controller drivers
 * perform a dma mapping (and unmapping) for each buffer transferred.  Those
 * mapping operations can be expensive on some platforms (perhaps using a dma
 * bounce buffer or talking to an IOMMU),
 * although they're cheap on commodity x86 and ppc hardware.
 *
 * Alternatively, drivers may pass the URB_NO_TRANSFER_DMA_MAP transfer flag,
 * which tells the host controller driver that no such mapping is needed for
 * the transfer_buffer since
 * the device driver is DMA-aware.  For example, a device driver might
 * allocate a DMA buffer with usb_alloc_coherent() or call usb_buffer_map().
 * When this transfer flag is provided, host controller drivers will
 * attempt to use the dma address found in the transfer_dma
 * field rather than determining a dma address themselves.
 *
 * Note that transfer_buffer must still be set if the controller
 * does not support DMA (as indicated by bus.uses_dma) and when talking
 * to root hub. If you have to trasfer between highmem zone and the device
 * on such controller, create a bounce buffer or bail out with an error.
 * If transfer_buffer cannot be set (is in highmem) and the controller is DMA
 * capable, assign NULL to it, so that usbmon knows not to use the value.
 * The setup_packet must always be set, so it cannot be located in highmem.
 *
 * Initialization:
 *
 * All URBs submitted must initialize the dev, pipe, transfer_flags (may be
 * zero), and complete fields.  All URBs must also initialize
 * transfer_buffer and transfer_buffer_length.  They may provide the
 * URB_SHORT_NOT_OK transfer flag, indicating that short reads are
 * to be treated as errors; that flag is invalid for write requests.
 *
 * Bulk URBs may
 * use the URB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
 * should always terminate with a short packet, even if it means adding an
 * extra zero length packet.
 *
 * Control URBs must provide a valid pointer in the setup_packet field.
 * Unlike the transfer_buffer, the setup_packet may not be mapped for DMA
 * beforehand.
 *
 * Interrupt URBs must provide an interval, saying how often (in milliseconds
 * or, for highspeed devices, 125 microsecond units)
 * to poll for transfers.  After the URB has been submitted, the interval
 * field reflects how the transfer was actually scheduled.
 * The polling interval may be more frequent than requested.
 * For example, some controllers have a maximum interval of 32 milliseconds,
 * while others support intervals of up to 1024 milliseconds.
 * Isochronous URBs also have transfer intervals.  (Note that for isochronous
 * endpoints, as well as high speed interrupt endpoints, the encoding of
 * the transfer interval in the endpoint descriptor is logarithmic.
 * Device drivers must convert that value to linear units themselves.)
*
 * If an isochronous endpoint queue isn't already running, the host
 * controller will schedule a new URB to start as soon as bandwidth
 * utilization allows.  If the queue is running then a new URB will be
 * scheduled to start in the first transfer slot following the end of the
 * preceding URB, if that slot has not already expired.  If the slot has
 * expired (which can happen when IRQ delivery is delayed for a long time),
 * the scheduling behavior depends on the URB_ISO_ASAP flag.  If the flag
 * is clear then the URB will be scheduled to start in the expired slot,
 * implying that some of its packets will not be transferred; if the flag
 * is set then the URB will be scheduled in the first unexpired slot,
 * breaking the queue's synchronization.  Upon URB completion, the
 * start_frame field will be set to the (micro)frame number in which the
 * transfer was scheduled.  Ranges for frame counter values are HC-specific
 * and can go from as low as 256 to as high as 65536 frames.
 *
 * Isochronous URBs have a different data transfer model, in part because
 * the quality of service is only "best effort".  Callers provide specially
 * allocated URBs, with number_of_packets worth of iso_frame_desc structures
 * at the end.  Each such packet is an individual ISO transfer.  Isochronous
 * URBs are normally queued, submitted by drivers to arrange that
 * transfers are at least double buffered, and then explicitly resubmitted
 * in completion handlers, so
 * that data (such as audio or video) streams at as constant a rate as the
 * host controller scheduler can support.
 *
 * Completion Callbacks:
 *
 * The completion callback is made in_interrupt(), and one of the first
 * things that a completion handler should do is check the status field.
 * The status field is provided for all URBs.  It is used to report
 * unlinked URBs, and status for all non-ISO transfers.  It should not
 * be examined before the URB is returned to the completion handler.
 *
 * The context field is normally used to link URBs back to the relevant
 * driver or request state.
 *
 * When the completion callback is invoked for non-isochronous URBs, the
 * actual_length field tells how many bytes were transferred.  This field
 * is updated even when the URB terminated with an error or was unlinked.
 *
 * ISO transfer status is reported in the status and actual_length fields
 * of the iso_frame_desc array, and the number of errors is reported in
 * error_count.  Completion callbacks for ISO transfers will normally
 * (re)submit URBs to ensure a constant transfer rate.
 *
 * Note that even fields marked "public" should not be touched by the driver
 * when the urb is owned by the hcd, that is, since the call to
 * usb_submit_urb() till the entry into the completion routine.
 */
struct urb {
        /* private: usb core and host controller only fields in the urb */
        struct kref kref;               /* reference count of the URB */
        int unlinked;                   /* unlink error code */
        void *hcpriv;                   /* private data for host controller */
        atomic_t use_count;             /* concurrent submissions counter */
        atomic_t reject;                /* submissions will fail */

        /* public: documented fields in the urb that can be used by drivers */
        struct list_head urb_list;      /* list head for use by the urb's
                                         * current owner */
        struct list_head anchor_list;   /* the URB may be anchored */
        struct usb_anchor *anchor;
        struct usb_device *dev;         /* (in) pointer to associated device */
        struct usb_host_endpoint *ep;   /* (internal) pointer to endpoint */
        unsigned int pipe;              /* (in) pipe information */
        unsigned int stream_id;         /* (in) stream ID */
        int status;                     /* (return) non-ISO status */
        unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
        void *transfer_buffer;          /* (in) associated data buffer */
        dma_addr_t transfer_dma;        /* (in) dma addr for transfer_buffer */
        struct scatterlist *sg;         /* (in) scatter gather buffer list */
        int num_mapped_sgs;             /* (internal) mapped sg entries */
        int num_sgs;                    /* (in) number of entries in the sg list */
        u32 transfer_buffer_length;     /* (in) data buffer length */
        u32 actual_length;              /* (return) actual transfer length */
        unsigned char *setup_packet;    /* (in) setup packet (control only) */
        dma_addr_t setup_dma;           /* (in) dma addr for setup_packet */
        int start_frame;                /* (modify) start frame (ISO) */
        int number_of_packets;          /* (in) number of ISO packets */
        int interval;                   /* (modify) transfer interval
                                         * (INT/ISO) */
        int error_count;                /* (return) number of ISO errors */
        void *context;                  /* (in) context for completion */
        usb_complete_t complete;        /* (in) completion routine */
        struct usb_iso_packet_descriptor iso_frame_desc[0];
                                        /* (in) ISO ONLY */
};

其中:

  • kref:urb引用计数;
  • hcpriv:主机控制器私有数据;
  • use_count:并发传输计数;
  • reject:传输将失效;
  • urb_list:链表头;
  • dev:关联的usb设备;
  • ep:指向端点的数据结构;
  • pipe:指向端点管道;
  • status:urb的当前状态;当status=0时,表示数据被成功的收到/发送;
  • transfer_buffer:发送到设备或从设备接收数据的缓冲区;
  • transfer_dma:用来以DMA方式向设备传输数据的缓冲区;
  • actual_length:urb结束后,发送或接收数据的实际长度;
  • setup_package:指向控制urb设置数据包的指针;
  • setup_dma:控制urb的设置数据包的DMA缓冲区;
  • start_frame:等待传输中用于设置或返回初始帧;
  • number_of_package:等时传输找那个等时缓冲区数量;
  • interval:urb被轮询到的事件间隔(对中断和等时urb有效);
  • error_count:等时传输错误数量:
  • context:complete函数上下文;
  • complete:当urb被完全传输或发生错误时,被调用;
  • iso_frame_desc:单个urb一次可定义多个等时传输时,描述各个等时传输。

urb典型的生命周期如下:

  • usb设备驱动程序创建并初始化一个访问特定usb设备特定端点的urb,并提交给usb core;
  • usb core 提交该urb到usb主控制器驱动程序;
  • usb主控制器驱动程序根据 urb 描述的信息,来访问usb设备;
  • 当设备访问结束后,usb主控制器驱动程序通知 usb core(调用这个函数usb_complete_t complete;)然后其再通知usb设备驱动程序。

六、开发板启动

由于内核自带了USB驱动,所以我们在开发板上接一个USB鼠标,然后启动开发板,串口就会输出如下信息:

usb 1-1: new low-speed USB device number 2 using s3c2410-ohci

可以看到linux内核已经识别到我们的USB鼠标设备了,那linux内核是如何设备到我们的USB设备的呢,这将在后面章节介绍。

参考文章

[1]十四、Linux驱动之USB驱动分析

[2]19.Linux-USB总线驱动分析

[3]Linux USB总线驱动框架分析

[4]常见硬件通信(SPI、I2C、CAN、USB、UART)协议介绍

[5]USART(RS232/422/485)、I2C、SPI、CAN、USB总线

[6]USB总线-USB协议简介(一)(转载)

[7]图解USB标准之架构概览

[8]USB驱动框架

[9]USB_4大描述符

[10]Linux USB 3.0驱动分析(一)—— USB设备基础概念

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/zyly/p/16213185.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程