USB1.1 开发

**
1 USB1.1协议概述
USB(Universal Serial Bus)是一种总线的接口形式,协议 1.1 版本由 Microsoft、Intel、IBM 等 7 家公司于 1998 年共同制定。所有设备在设计、生产及制造的过程中都必须严格遵守协议规定。下面分别从几个方面对协议进一步描述。
1.1 USB体系结构
1.1.1 USB 物理结构
USB系统主要由三个部分构成:USB互连、USB设备、USB主机[9]。USB的互连是指USB设备与主机之间进行连接和通信的操作,主要分为以下四个方面:

  1. 总线结构:即主机和USB设备间的各种连接的方式;
  2. 内部层次关系:系统把USB任务分配到各个层次;
  3. 数据流模式:是一种流动方式,即系统中的数据通过USB的方式;
  4. USB的调度:提供一个可以公用的USB连接。调度各个用于同步数据传输的各个连接,同时确保减少优先级别判定时所花费的时间。
    USB接口的作用是为了连接设备与主机,USB的物理连接是层次化的星型拓扑结构。由图 1-1 可看出USB的拓扑结构。

图1-1 USB拓扑结构
如上图所示,最上层的是PC主机,协议规定一个系统内任何时刻只能有一个主机处于活动状态。PC运行支持USB的系统软件,同时支持以下两种功能:一是设备的初始化,二是监控设备的运行。初始化软件不仅在主机启动期间而且在所有运行期间都应该是活动的。因此,USB设备可以随时增加或删除。如果需要进一步增加USB接口的个数。则需要使用集线器。集线器介于根集线器与外设之间,主要用来接收和转发通信数据。之前提及的集线,器是USB的一种设备,另一种设备是USB功能部件。在数据线的后端可以连接各种类型和功能的USB设备,如摄像头、扫描仪、键盘和鼠标等。
1.1.2 USB电气特性
1.1.2.1 USB供电模式
USB设备采用2种供电模式,即自供电和总线供电。所谓自供电,是指该USB设备能够自己提供电源,而无需从VBus上提取电流,这样做的好处是该设备在工作时功率不必受USB协议的限制,可以根据自身电源的能力任意提取电流;但局限性也是显而易见的,该设备必须带有额外的电源设备,增加了体积和成本。而总线供电模式则为耗电量小的设备提供了一种方便的连接方式,能够完全从USB总线的VBus获得所需的电流,但是这样的设备运行时所耗的功率受到USB协议的限制,不能无限制地从总线上取得电流。2种模式的电流情况和典型设备如表1-1所列。
表1-1 USB设备供电模式分类

1.1.2.2 低功耗的实现
USB供电的工作状态也被分为2种,即正常工作状态和挂起状态。如果总线供电设备在3ms内没有总线操作,即USB总线处于空闲状态的话,该设备就需要自动进入挂起状态。这时从总线上提取的电流必须小于500微安。实际上,500微安的电流中包括了集线器和主机端15k欧姆的终端匹配电阻的电流,这个值通常为220微安。因此,对于总线供电的设备而言,在进入挂起状态后,总的电流功耗不超过280微安。由此可见,这个设备的功耗是非常低的。这便是USB低功耗的一个非常重要的特点。
在设备进入挂起状态后,可以通过唤醒操作来恢复到正常工作状态。唤醒操作既可以由主机发送唤醒或复位信号来触发,也可以由设备自行通过远程唤醒来实现。处于挂起状态的设备通过发送信号给主机,使得主机恢复处理其USB事务,这种远程唤醒方式是所USB事务中,惟一能够由设备发起的事务。
1.1.2.3 即插即用技术
即插即用技术包含2个技术层面,即热插拔和自动识别配置。热插拔的关键技术在于电路接插件插、拔期间强电流的处理。USB在电缆以及接插件的设计上充分考虑了这一点,使得这个瞬时的强电流被安全地吸收,从而使USB设备实现了热插拔。即插即用的另一个关键技术层面就是系统设备的自动识别。也就是说,主机系统在没有人工干预的情况下,能够自动检测到设备的接入并能自动配置该设备,也能够自动检测到设备的拔出,从而释放系统资源。USB主要是通过在信号线上的一些特殊处理来实现这一技术。
1.1.3 USB 系统层次对应关系
USB 系统层次对应关系如下图1-2所示。

图1-2 USB系统对应关系
USB 系统分为以下三层:

  1. 功能层:包括客户应用软件和设备功能块,他们之间有直接的逻辑对应关系。这种对应关系说明在逻辑上客户应用软件只需要考虑如何实现具体设备功能即可。例如在开发扫描仪的应用软件时,工程师只需考虑图像显示和处理方法,不需要了解USB的相关内容。
  2. USB 设备层:包括USB的系统设备驱动软件和USB设备的通信软、硬件部分。他们之间的关系也为逻辑上的对应关系。开发USB设备驱动软件和USB设备的通信软、硬件工程师应当知道USB系统的基本通信协议。
  3. USB总线接口层:黑色箭头标示实际信号传输路径。USB串行信号是由主机控制器经线缆传到设备接口芯片的SIE上,再由SIE做数据解码和校验后提交到设备的上一层去进一步解析。
    如上所述的这种层次和模块划分主要有以下的两个好处:一是能够使得设备和主机的连接过程标准化,消除因不同厂商所带来的影响,减少其因兼容性而产生的问题;二是能够进一步细化USB开发的工作流程,硬件工程师仅仅需要专注于总线接口层的设计,但是软件工程师却应侧重于界面优化等方面的处理。
    1.2 USB协议层
    本小节从字段(Field)和包(Packet)的定义开始分析,诠释了USB(Universal Serial Bus)协议。之后对不同事务(Transaction)类型的包事务格式描述。然后是链路层(Link layer)流程控制和事务级别的故障恢复。
    1.2.1位定序
    以串行方式将数据位传输到Bus的时候,先传输到的一直是最低有效位(LSb),而最高有效位(MSb)后到达。在本节后面所提到的几种数据包,包都采用以下形式阐述:位或者字段通过Bus的顺序总是和它在包中由左至右的排列顺序一致。
    1.2.2同步字段
    所谓的同步字段的目的是完成同步接收或者发送的序列,全部包的传输都是起于同步字段(SYNC)字段。在Bus上同步字段的出现状态是空闲状态,以NRZI编码的“KJKJKJKK”(即00000001)表示。根据此 8位的字符串,数据的输入模块根据本地时钟的相位对齐输入数据。同步字段是用于同步的方式。“KK”这最后两位是同步字段的结束标志,同时也象征着接下来是包标识符(PID)的传输。
    1.2.3数据的编码与解码
    在包进行传输时,USB是采用一种 NRZI(None Return Zero Invert,即不归零反向码)编码方案。在该编码方案中,“1”代表了电平不变,“0 ”则代表了电平反转。图 1-7 列出了一个数据流以及它的 NRZI编码,在该图的第二个波形中,高电平即表示J,低电平表示K,后面就是NRZI编码,以此作为示例。

图1-7 NRZI编码格式
1.2.4包标识符格式
包中的字段格式包括标记包、数据包和握手包等。所有的包都分别有包开始(SOP)和包结束(EOP)标识符。除了握手包,其它包均由PID、数据字段和循环冗余校验(CRC)字段构成,握手包仅由PID字段构成。由于本章只是对协议的简单阐述,此处只对 PID 给出介绍。所有包标识符(PID)都紧跟在 USB 包的同步字段后。如图1-8所示,一个4位包类型字段同4位校验字段构成了包标识符。包标识符表明了包的类型,而且据此表明包是何种格式及采用的哪种错误检测类型。包标识符的4位校验字段能够进行自我校验,由此达到保证解码包标识符的可靠性的目的。校验字段是用对包类型字段的二进制码取反的方式得到。如果4个PID校验位不同于包标识符位的取反编码,则表示PID有错误存在。

图 1-8 PID格式
PID 字段必须被主机及全部设备优先解码。任何收到的包标识符不管是包含有错误的或者未定义的字段,那么该标识符都会被认为是无效的,而且包的其余项将被丢弃。如果一个设备接收到它不支持的PID,即使接收无误,也不做出应答。下表1-3列出了包标识符类型、编码以及其详细的描述。
表1-3 PID类型

1.2.5地址字段
功能部件端口使用2个字段:功能部件地址字段和端口字段。功能部件对地址和端口字段都需要进行译码。不允许使用地址或端口别名(Aliasing),并且任何一个字段不匹配,此标记都必须被忽略。另外,对未初始化的端口的访问将使得标记被忽略。
1.2.5.1地址字段
功能部件地址(ADDR)字段通过其地址指定功能部件,至于是数据包的发出地还是目的地,则取决于标记PID的值。如图1-9所示,ADDR<6 :0>指定了总共 128个地址。地址字段被用于输入,建立和输出标记。由定义可知,每个ADDR值都定义了单一的功能部件。刚一复位(Reset)和加电(Power-up)的时候,功能部件的地址默认值为零,并且必须由主机在枚举过程(Enumeration Process)中编程。功能部件地址零被用作为缺省地址,不可被分配作任何别的用途。

图1-9 地址字段
1.2.5.2 端口字段
如图1-10所示,附加的4位的端口(ENDP)字段在功能部件需要一个以上端口时候允许更灵活的寻址。除了端口地址0之外,端口个数是由功能部件决定的。端口字段只对输入,建立和输出标记PID有定义。所有的功能部件都必须在端口0提供一个控制管道(缺省控制管道)。对于低速(Low Speed)设备,每个功能部件最多提供3个管道:在端口0的控制管道加上2个附加管道(或是2个控制管道,或是1个控制管道和1个中断端口,或是2个中断端口)。全速(Full Speed)功能部件可以支持最多可达16个的任何类型的端口。

图1-10 端口字段
1.2.6帧号字段
帧号字段是一个11位的字段,主机每过一帧就将其内容加一。帧号字段达到其最大值7FFH时归零,且它仅每个帧最初时刻在SOF标记中被发送。
1.2.7数据字段
数据字段可以在0到1,023字节之间变动,但必须是整数个字节。图1-11为多字节显示格式。每个字节的范围内的数据位移出时都是最低位(LSb)在前。

图1-11 数据字段格式
1.2.8循环冗余校验
在通信系统中,通常增加信道编码器来提高数字信号传输的可靠性。是数据通信领域中最常用的一种校验方式,其特征是信息字段和校验字段的长度可以任意使用。实际使用中,发送装置计算CRC值并添加在数据之后一同发送给接收装置,接收装置对收到的数据重新计算CRC并与收到的CRC相比较。USB系统中CRC用于校验标记包和数据包中除PID字段以外的所有字段。USB传输包中除去握手包以外都含有CRC位来验证数据信息的正确性。根据数据包的大小,每个信息包都包括5位或者16位CRC校验位。下文中会对CRC校验设计分析说明,在此只提及其概念。
令牌包5位CRC字段产生的多项式为 G(X)= X5 + X2 + 1。
数据包16位CRC字段产生的多项式为 G(X)= X16 + X15 + X2 + 1。
1.2.9 USB传输格式
USB数据必需要通过总线进行传输,其Bus是属于轮询的访问模式,主机控制端口用来初始化全部的数据传输,任何的事物请求均由主机发送给设备。
每一条Bus最多可以传输三个数据包。依据传输以前规定好的原则,每次开始进行传输时,主机发送一个数据包,这个数据包是用来描述传输的类型、方向、USB设备地址以及设备终端号的USB数据包,通常把这个数据包称为标记包(Token)。USB设备从合适的位置萃取数据。数据传输的方向要么从主机到设备,要么从设备到主机,接收方法出相应的握手信号来表明数据是否传输成功。
USB事务处理是值主机和设备之间数据传输的过程,都是由某些具有特定格式的信息包组成,下面简单介绍USB包含的4种包格式。
1.2.9.1 TOKEN包
图 1-12 表明了标记包的字段格式。标记包由PID、ADDR和ENDP等组成,其中PID指定了包是输入、输出还是建立类型。对于输出以及建立事务,ADDR和ENDP将确定唯一收到数据包的端点。针对输入事务,这些字段能够唯一的确定发送数据所使用的端点。标记包只能由主机发送。从设备到主机的事务传输由输入PID定义,反之,从主机到设备的传输由输出PID标示。

图1-12 标记包格式
如上图所示,标记包中的5位CRC校验涵盖了对地址和端点的验证。CRC并不验证PID,因为PID由自身的反码进行校验。标记包以及帧开始包(SOP)由12位字段的包结束(EOP)界定的。
1.2.9.2 SOF包
主机发送帧开始包是以每(1.00,误差0.0005)ms的额定频率发送的。如图1-13所示,SOF包是由指示包类型的PID和其后的11位的帧号字段组成的。

图1-13 SOF包
SOF包中携带着帧号。任何全速设备都可收到SOF包,其中包括集线器。设备并不对SOF产生应答。合法的标记包界定在三个字节的SOF包字段数据后面有无包结束标志(两个 SE0)。

1.2.9.3 DATA包
如图1-14所示,数据包由PID和包括至少0字节的数据区及16位CRC构成。存在有两种不同的数据包,根据PID解析出的DATA0和DATA1来区分。为了达到数据能够同步切换的目的,这两种数据包是在传送过程中需要能自动切换。数据必须以整字节发送。在此单独阐述下空包的情况,USB数据传输中经常遇到空包发送情况。空包在数据区的长度为0,即只含有1BYTE的PID包及2BYTES的CRC。同以上两种包,CRC不包括 PID。

图1-14 数据包格式
1.2.9.4 Handshake包
握手包只由PID构成,能够用来表示端点的当前状态,同时能表示上一个事务传输的成功接收、传输失败且需要重发或者停止等信息。一般在数据传输及配置的事务类型才能给出握手信号。握手包一直是在所有事务的握手阶段返回,表征当前传输事务的结束。握手包的结束也是由包字段之后的EOP界定。如果握手包被翻译成为合法的信号,但是并没有在1个字节以内的发现EOP,则被断定是无效的信号,将会被接收者忽略。
握手包以如下3种类型存在:

  1. ACK表示数据包不存在位填充错误而且数据字段的CRC没有差错,并且PID 标识被无误接收。在以下情况时ACK将会被发送:情况一,当时序(Sequence Bit)能够正常匹配并且接受器能够接收数据时;情况二,当时序失配,且发送方与接收器必须再次同步时。所有ACK握手信号均由接收方返回。
  2. NAK 表示接收方还没有准备好接收数据。仅在输入事务的数据阶段和输出事务的握手阶段才能返回NAK,主机不允许发出NAK信号。NAK只是表示设备当前正忙,不能进行数据传输。但是在HOST不干涉的情况下,最后还是能够完成数据的传输或者接收事务。
  3. STALL表示设备不能进行数据传输,或者不支持一个控制管道的请求。在任何条件下都不允许主机返回STALL。
    1.2.10 USBError Detection and Recovery
    1.2.10.1 Packet Error Categories
    USB支持主机系统与USB设备之间的数据无损传输,USB设备可以检测三种类型的包错误。
    PID检查:每个USB数据包都以一个包ID(PID),它由四位组成,后紧接一个四位的校验字段,此校验字段是PID前4bit的反码。所有的USB设备必须进行PID的校验,若发现错误,则由于无法确定这个信息包的类型,所以这个信息包就会被忽略。
    循环冗余校验: USB的每个信息包也都有5位或16位的CRC,用来验证那些跟在PID后面的信息。
    位填充错误检查: USB用位填充方式实现NRZI数据的发送方和接收方同步,USB接收方在数据流中收到6个连续的“1”之后,若没有出现跳变(填充位),那么就说明这个包已经被破坏了,或者是发送方没有正确地产生填充位,或者接收方没有对NRZI数据进行正确的解码。包错误类型如下图所示:

1.2.10.2Data Toggle Synchronization and Retry
USB 传输为了提高数据传输的准确性,使用的是两种数据包交替传送。这种机制也能够保证传输过程中收发双方的同步。在控制传输中,数据切换位设置比较容易,DATA0用来发送数据,DATA1用来发送确认包,然后进行翻转;在批量传输中切换位的设置要复杂些,首先对于输入和输出端口,切换位是分离的,它们在各自的数据传输中独立切换,通常要设定两个全局变量 toggle1 和 toggle2 来记录和设定。每次接收方比较当前的数据切换位同接收过来的数据包的切换位是否一致,当发生不一致时,会产生数据传输错误,接收方也不会反馈确认的握手包。

  1. Initialization via SETUP Token
    控制传送使用建立标记初始化主机和功能部件的时序位。如下图所示,主机向功能部件发送建立包,其后跟着输出事务。圆圈里的数代表发送器和接收器的时序位。功能部件必须接受数据并返回。当功能部件接受事务的时候,它必须设置其时序位,以便主机和功能部件的时序位在建立事务的最后保持相同。

  2. Successful Data Transactions
    下图表示有两个成功的数据传输事务的情况。对于USB发送器,它根据的接收端情况来切换其时序位。仅当USB接收器收到合法的数据包,且数据和时序位相匹配的时候,才切换其时序位。发送器收到数据包的ACK时才切换其时序位。
    在每个事务中,接收器会对发送器的时序位接收器的时序位进行比较。如果数据不能被接受,接收器必须发出NAK,且发送器和接收器的时序位保持不变。如果数据能被接受,并且时序位相匹配,则数据被接受,并且时序位被切换。没有数据包事务不得使发送器和接收器改变时序位。

  3. Data Corrupted or Not Accepted
    如果数据不能被接受或数据包被损坏,接收器将根据情况发出NAK或STALL握手,或者超时(Timeout),且接收器不切换时序位。下图说明了事务被返回NAK,然后被重试的情况。任何非ACK握手或超时都将产生重试动作。没有收到ACK握手的发送器,将不切换其时序位。结果是错误的数据包事务使得发送器和接收器的时序位同步并不切换。然后事务将被重试,如果成功,将引发发送器和接收器时序位的切换。

  4. Corrupted ACK Handshake
    发送器是根据其收到ACK握手来确定事务传输成功。如下图所示,丢失或者损坏的ACK握手使USB发送器和接收器暂时失去同步。发送器在发出合法的数据包,且接收机成功地收到但是握手损坏。

在事务i的结尾,由它们各自的时序位的不匹配可看出发送器和接收器暂时失去同步。接收器已经收到了正确的数据,但发送器并不知道。在下一次事务中,发送器将重发之前的数据,此时接收器时序位和数据将不匹配,于是接收器知道它以前接受了这个数据,从而它丢弃此数据包且不切换其时序位。然后接收器发送ACK,使得发送器知道重试的事务是成功的,发送器切换其时序位,在事务i+1的开头,已经同步了。
1.2.10.3 Timeout
在一次传输事务的处理中,发送方和接收方要协调好超时时间。发送方在发送数据之后,接收方在一段时间之后仍然没有收到数据,通常这个时间为16个单位时间,超过这个时间可以判定为总线传输响应超时。当超时发生时,USB系统对错误进行记录,并重新传输该数据包,尝试3次后,仍然无法处理该错误,则放弃该次传输,返回错误代码,等待应用软件处理。例如:USB设备在输入时没有做出响应或者在输出事务时没有返回握手信号,常规的解决方法是,等待一段时间重发请求命令。但多次尝试失败后,则会重新枚举该USB设备。
1.3 USB 系统的设备枚举
USB 枚举是发生在设备接入或复位时的信号传输过程,是正常工作的重要部分。设备枚举主要包括主机如何与设备建立联系,如何对设备进行配置,设备如何响应主机的命令以及整个系统如何实现即插即用功能。在介绍设备枚举之前,优先介绍下USB设备的状态。
1.3.1 USB 设备的状态
USB设备在运行时有明显的状态区分。USB设备以FSM的形式在状态图中根据内外条件的变化状态转移。USB设备具有6个状态,分别是连接(Attached)、加电(Powered)、缺省(Default)、地址(Address)、配置(Configured)和挂起(Suspended),这些状态及其之间的关系如下表1-2所示。
表1-2 USB设备状态表

下面对各状态的具体含义进行说明。

  1. Attached状态:
    该状态表示设备刚接入主机或者集线器。
  2. Powered状态:
    USB总线设备的电源可以采用外接的电源或者经由USB接口直接进行获取。自给电源设备即采用外部电源的USB设备。使用USB总线供电的设备称总线供电设备。
    一个设备可能有既支持自给电源的,又支持总线电源式的配置。有些支持其中的一种,而另一些设备配置只有在自给电源下才能被使用。设备对电源支持的能力是通过配置描述表(configuration descriptor)来反映的。当前的电源供给形式被作为设备状态的一部分被反映出来。设备可在任何时候改变它们的供电来源,比如:从自给式向总线式改变,如果一个配置同时支持两种模式,那此状态的最大电源需求就是指设备在两种模式下从VBUS上获取电能的最大值。设备必须以此最大电源作为参照,而究竟处于何状态是不考虑的。如果有一配置仅支持一种电源模式,那么电源模式的改变会使得设备失去当前配置与地址,返回加电状态。如果一个设备是自给电源式,并且当前配置需要大于100mA电流,那么如果此设备转到了总线电源式,它必须返回Addressstate。自给电源式集线器使用VBUS来为集线控制器(Hub controller)提供电源,可以仍然保持Configured state,尽管自给电源停止提供电源。
  3. Default状态:
    针对设备进行供电以后,不应该对Bus传输进行响应直至它接收到一个复位信号的标志。
  4. Address状态:
    所有USB总线设备在复位时均使用缺省地址。在将其插入主机以后或者是复位以后通过主机给其分配唯一的地址。当其被置于Stall状态时,就能够维持已经获得的地址。在设备的地址状态只有默认的管道反应,不管其是否被分配地址。
  5. Configured状态:
    设备必须被正确设置才能使得其能够无误的工作。从设备的角度看,设备将配置寄存器写入非初始值。该接口地址上所有相关的端点状态与配置寄存器会设置成默认值,端点所使用的数据也会被切换为DATA0。
  6. Suspended状态:
    为了达到降低功耗的目的,USB设备在Bus不活动时将会进入Stall状态。同时,USB总线设备会维持其本身的全部状态,其中包含有设备的配置。任何设备在3ms内检查不到 Bus活动必须进入Stall状态,不管设备是否已被设置。已连接的设备在任何时刻必须都能够进入Stall状态,同时Stall状态在Bus活动到来时必须结束。USB总线设备能够将远程唤醒的信号上行至主机接收,使设备退出Stall状态。当设备Reset时,远程唤醒不能被使能。

图1-3 设备状态转移图
1.3.2 USB 设备的枚举步骤
USB协议定义了设备的6种状态,仅在枚举过程中,设备就经历了4个状态的迁移:上电状态(Powered),默认状态(Default),地址状态(Address)和配置状态(Configured)(其他两种是连接状态和挂起状态(Suspend))。

  1. 用户把USB设备插入USB端口或给系统启动时设备上电
    这里指的USB端口指的是主机下的根hub或主机下行端口上的hub端口。Hub给端口供电,连接着的设备处于上电状态。此时,USB设备处于加电状态,它所连接的端口是无效的。

  2. Hub监测它各个端口数据线上(D+/D-)的电压
    在hub端,数据线D+和D-都有一个阻值在14.25k到24.8k的下拉电阻Rpd,而在设备端,D+(全速,高速)和D-(低速)上有一个1.5k的上拉电阻Rpu。当设备插入到hub端口时,有上拉电阻的一根数据线被拉高到幅值的90%的电压(大致是3V)。hub检测到它的一根数据线是高电平,就认为是有设备插入,并能根据是D+还是D-被拉高来判断到底是什么设备(全速/低速)插入端口。如下图。检测到设备后,hub继续给设备供电,但并不急于与设备进行USB传输。

  3. Host了解连接的设备
    每个hub利用它自己的中断端点向主机报告它的各个端口的状态(对于这个过程,设备是看不到的,也不必关心),报告的内容只是hub端口的设备连接/断开的事件。如果有连接/断开事件发生,那么host会发送一个 Get_Port_Status请求(request)给hub以了解此次状态改变的确切含义。Get_Port_Status等请求属于所有hub都要求支持的hub类标准请求(standard hub-class requests)。

  4. Hub检测所插入的设备是全速还是低速设备
    hub通过检测USB总线空闲(Idle)时差分线的高低电压来判断所连接设备的速度类型,当host发来Get_Port_Status请求时,hub就可以将此设备的速度类型信息回复给host。

  5. hub复位设备
    主机一旦得知新设备已连上以后,它至少等待100ms以使得插入操作的完成以及设备电源稳定工作。然后主机控制器就向hub发出一个Set_Port_Feature请求让hub复位其管理的端口(刚才设备插上的端口)。hub通过驱动数据线到复位状态(D+和D-全为低电平),并持续至少10ms。当然,hub不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该hub上的设备自然看不到复位信号,不受影响。

  6. Hub建立设备和主机之间的信息通道
    主机不停地向hub发送Get_Port_Status请求,以查询设备是否复位成功。Hub返回的报告信息中有专门的一位用来标志设备的复位状态。
    当hub撤销了复位信号,设备就处于默认/空闲状态(Default state),准备接收主机发来的请求。设备和主机之间的通信通过控制传输,默认地址0,端点号0进行。此时,设备能从总线上得到的最大电流是100mA。(所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信)。
    7)主机发送Get_Descriptor请求获取默认管道的最大包长度
    默认管道(Default Pipe)在设备一端来看就是端点0。主机此时发送的请求是默认地址0,端点0,虽然所有未分配地址的设备都是通过地址0来获取主机发来的请求,但由于枚举过程不是多个设备并行处理,而是一次枚举一个设备的方式进行,所以不会发生多个设备同时响应主机发来的请求。
    设备描述符的第8字节代表设备端点0的最大包大小。虽然说设备所返回的设备描述符(Device Descriptor)长度只有18字节,但系统也不在乎,此时,描述符的长度信息对它来说是最重要的,其他的瞄一眼就过了。当完成第一次的控制传输后,也就是完成控制传输的状态阶段,系统会要求hub对设备进行再一次的复位操作(USB规范里面可没这要求)。再次复位的目的是使设备进入一个确定的状态。
    8)主机给设备分配一个地址
    主机控制器通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态(Address state),之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。
    9)主机获取设备的信息
    主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配),PID(Product ID,由厂商自己定制)等信息。Get_Descriptor请求(Device type)和设备描述符。
    之后主机发送Get_Descriptor请求,读取配置描述符(Configuration Descriptor),字符串等,逐一了解设备更详细的信息。事实上,对于配置描述符的标准请求中,有时wLength一项会大于实际配置描述符的长度(9字节)。
    接下来,主机就会获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后,根据里面的配置集合总长度,再获取配置集合。配置集合包括配置描述符,接口描述符,端点描符等等。
    如果有字符串描述符的话,还要获取字符串描述符。另外HID设备还有HID描述符等。

  7. 主机给设备挂载驱动
    主机通过解析描述符后对设备有足够的了解,会选择一个最合适的驱动给设备。然后tell the world(announce_device)说明设备已经找到了,最后调用设备模型提供的接口device_add将设备添加到usb总线的设备列表里,然后usb总线会遍历驱动列表里的每个驱动,调用自己的match(usb_device_match)函数看它们和你的设备或接口是否匹配,匹配的话调用device_bind_driver函数,现在就将控制权交到设备驱动了。对于复合设备,通常应该是不同的接口(Interface)配置给不同的驱动,因此,需要等到当设备被配置并把接口使能后才可以把驱动挂载上去。

  8. 设备驱动选择一个配置
    驱动(注意,这里是驱动,之后的事情都是有驱动来接管负责与设备的通信)根据前面设备回复的信息,发送Set_Configuration请求来正式确定选择设备的哪个配置(Configuration)作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态 (Configured),当然,设备也应该使能它的各个接口(Interface)。
    2 USB1.1设备控制器
    2.1 USB传输模型
    USB总线有两条数据线,并且这两条数据线是由所有设备共享的。USB主机负责管理总线上的数据传输,它会将传输依据时间来分割成若干帧或微帧。每一个帧的开头是一个开端信息包,然后是数据传输的事务。每一次USB传输是由一个或几个事务组成,而每一个事务又是由信息包所组成,信息包是USB传输的载体。要了解事务、包以及其具体内容,首先需要了解的概念是端点和管道,这些被称为USB传输要件。如下图所示:

2.1.1 USB传输要件
2.1.1.1Device Endpoints
设备端点USB 设备的一个部分,所有的传输都是将端点作为发出点或者接收点。通常情况下,设备端点是一个内存区域,或者是控制芯片的一个缓存器,端点的作用是数据缓存。每个USB设备有一个惟一的地址,这个地址是在设备连上主机时由主机分配的,而设备中的每个端点在设备内部有惟一的端点号,这个端点号是在设备设计时被给定的。端点号可以是0-15,方向可以是输入(设备发送数据给主机)或者输出(主机发送数据给设备)。每个设备必须将端点0设置为控制端点,控制端点是双向数据传输的,而其他类型的端点都是单向数据传输。
2.1.1.2 Pipes
管道是设备端点和主机控制器之间的连接,在一个传输发生之前,必须首先建立一个管道。管道随着主机和设备连接的建立而建立,当移除设备时,管道也跟着被移除。每一个设备都会和主机之间建立一个默认的控制管道,此管道使用端点0。
不同的传输类型使用不同的管道。管道可以被分为消息管道和流管道。消息管道是指具有某种USB定义格式的数据流,是双向的管道;而流管道则是不具有USB定义格式的数据流,不具有双向性。控制传输是惟一使用双向消息管道的传输,其他的传输都使用单方向的流管道。
2.1.1.3Transactions和Packets
每一次USB传输包含一个或者多个事务,而每一个事务又包括一个或者多个信息包。USB传输的模型如图2-1所示。

图2-1 USB传输模型示意图
事务根据其数据流方向以及目的,可以分为3类:输入、输出与设置。每一种传输类型的传输包括一个或者多个阶段,而每个阶段又可以分为一个或者几个信号包。不同的传输类型的阶段、信号包如表2-1示。
表2-1传输类型、阶段、信号包关系表

2.1.2 传输类型
USB在主机与设备间的数据传输以通道方式进行,传递的数据需满足USB所规定的格式。USB要求在通道上传送的任何数据均以包形式进行传输,USB提供多种数据传输格式,使之尽可能满足客户软件和应用软件的要求,一个IRP需要一个或者多个BUS处理事务来完成,其中客户软件和应用层软件则负责对解析而来的数据进行解释。
包事务格式是根据端点类型而变化的。USB定义了4种端点类型:控制、批量、中断和同步端点。相对应于端点类型,协议中规定了四种事务类型,包括控制(Control)传输、批量(Bulk)传输、中断(Interrupt)传输和同步(Isochronous)传输。
2.1.2.1 Contorl传输
控制传输用于设备连接时对设备进行设置,还可对管道等指定设备进行控制。控制传输至少要有两个事务阶段:建立阶段和状态阶段。之间还有可能存在数据阶段。
建立阶段是一次控制传输,在这个阶段,主机将特定的控制信息按照一定的顺序发送给设备,设备接收到信息后将准备接下来的数据阶段或者状态阶段。此阶段的数据传输都是用 DATA0的PID,一般在Get Descriptor或者Set Descriptor过程中需要一定的数据传输。数据阶段是为有数据传输要求的控制传输定义的。某些数据传输不要求必须有数据阶段。此阶段的数据发送要在DATA0和DATA1中间进行切换。状态阶段是控制传输序列的最后一个阶段。用来向主机表明当前完整传输事务的正确性。状态阶段和数据阶段的数据流方向相反,并且总是使用DATA1。

图2-2 控制读写序列
必须具有USB所定义的包格式才能进行控制传输。控制传输能够进行双向传输,对于全速设备,允许传输最大容量为8、16、32或64字节的数据包,对于低速设备只支持8字节。
端点所传的数据净负荷区长度必须小于或等于其最大容量,当一个数据区不能容纳所传数据时,会自动分成几个包来传。除最后一个区外,其它区都应达到最大长度。在此问题上及数据阶段被认为结束上,几种传输方式都完全相同。当端点做了如下两件事,控制传输的数据阶段可被认为结束:

  1. 恰好传完了由 Setup阶段指定的数据量。
  2. 传了一个数据包,它的长度为0或它的数据区长度小于最大长度。
    2.1.2.2 Bulk传输
    批量传输,顾名思义是用来大批量传输并使用数据,传输数据的速率具有很大的动态范围。批量数据由大量的数据组成的且是连续的,且只在全速模式下才进行。批量传输事务类型的特点是:以错误检测和重试的方式保证主机和外设之间的数据无错发送。如下图2-3所示,批量传输事务是由标记、数据和握手包三阶段事务组成。但在某些流控制和挂起条件下,数据阶段被握手信号替换,从而产生没有数据传输的两阶段事务。
    批量传输是为了能够使得在某些时间进行大批量数据通信而提出的。它可以利用任何能够获得的带宽。批传输有以下几个特点:
  3. 任何时间都能获得访问总线的带宽;
  4. 如果事务传输出现错误或者传送失败,都可重发;
  5. 保证必定能够传输数据,但带宽和延迟不能确定。

图2-3批量传输事务格式
批量传输只会发生在有可用带宽的情况下。也就是说对于控制传输,可保证在什么时间传输,而批量传输则不行。在有足够带宽前提下,主机准备好接收批处理数据的时候,向外设发送输入标记。外设端点在接收到主机信号后返回数据包,或者在不能返回数据包时返回NAK或STALL握手作为应答。如果主机受到合法的数据包,则对设备返回ACK作为应答。如果主机在接收到错误的数据,它不返回握手包给外设。

  1. ACK表示无错地接收到数据包,通知主机可以发送下一个包;
  2. NAK表示无错地收到数据包,但主机应重新发送数据,因为设备当前正忙,无法进行接收;
  3. 如果端点被停止,则返回STALL,通知主机外设存在不可自己恢复的错误,不用再次发送;
  4. 如果收到CRC错误或者Bit stuff错误的数据包,则不发送任何信号。
    2.1.2.3Interrupt传输
    中断传输用来传输中断数据,专用于字符或反馈响应字符的传输,是为键盘、鼠标等设备设计的。此类设备只传输少量数据且不经常传送,但具有确定的一个服务周期。
    中断事务可由输入或输出构成。当接收到输入标志,设备便可返回数据。若没有新的端口中断信息可供返回,在数据阶段里功能部件则返回NAK握手;如果中断端口被设置为STALL,功能部件返回STALL握手信号;如果中断时等待事务,功能部件则返回类似数据包传输时的中断信息。类似于数据包的传输,主机如果无错的接收数据,则返ACK信号;如果数据包传输过程中出现错误,则不返回任何信号。下图2-4解释了中断事务格式。

图2-4 中断事务格式
中断事务有输入和输出两个方向。在数据阶段,当端点为中断数据传输而使用中断传输机制的时候,必须遵循数据切换协议。另外,对中断传输还有以下两点要求:

  1. 管道的最大服务周期得到保证;
  2. 由于错误而引起的重发在下一个服务期进行。
    2.1.2.4 Isochronous传输
    如图2-5所示,同步(ISO)事务有标记和数据时相,而没有握手时相。主机发出输入或输出标记,后跟着端口(输入)或主机(输出)传送数据的数据时相。ISO事务不支持握手或重试能力。

图2-5 同步传输模式
注解:设备或主机控制器都应该能接受DATA0和DATA1。设备或主机控制器应该只发送DATA0。ISO事务不支持切换时序。
2.2 USB描述符
设备描述符包含的是设备信息,标准USB描述符包括设备描述符、配置描述符、接口描述符、端点描述符以及字符串描述符等。不同的描述符从不同的层级来表示设备的属性。设备描述符包含了整个设备的信息以及设备支持的配置号码,每一个设备只能有一个设备描述符。但一个设备可以有几个配置描述符,它包含了电源管理信息以及所支持的接口号码。接口描述符包含了与端点通信所需要的信息,它可以有零个或者多个端点描述符。
在USB1.X中,规定了5种标准描述符:设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor)、接口描述符(Interface Descriptor)、端点描述符(Endpoint Descriptor)和字符串描述符(String Descriptor)。
2.2.1设备描述符
设备描述符给出了USB设备的一般信息,包括对设备及在设备配置中起全程作用的信息,包括制造商标识号ID、产品序列号、所属设备类号、默认端点的最大包长度和配置描述符的个数等。一个USB设备必须有且仅有一个设备描述符。设备描述符是设备连接到总线上时USB主机所读取的第一个描述符,它包含了14个字段,结构如下:

偏移量 域 大小 值 描述
0 bLength 1 数字 此描述表的字节数
1 bDecriptorType 1 常量 描述符的类型(此处应为0x01,即设备描述符)
2 bcdUSB 2 BCD码 此设备与描述表兼容的USB设备说明版本号(BCD 码)
4 bDeviceClass 1 类 设备类码:
如果此域的值为0则一个设置下每个接口指出它自己的类,各个接口各自独立工作。
如果此域的值处于1~FEH之间,则设备在不同的接口上支持不同的类。并这些接口可能不能独立工作。此值指出了这些接口集体的类定义。
如果此域设为FFH,则此设备的类由厂商定义。
5 bDeviceSubClass 1 子类 子类掩码
这些码值的具体含义根据bDeviceClass 域来看。
如bDeviceClass 域为零,此域也须为零
如bDeviceClass 域为FFH,此域的所有值保留。
6 bDevicePortocol 1 协议 协议码
这些码的值视bDeviceClass 和 bDeviceSubClass 的值而定。
如果设备支持设备类相关的协议,此码标志了设备类的值。如果此域的值为零,则此设备不支持设备类相关的协议,然而,可能它的接口支持设备类相关的协议。如果此域的值为FFH,此设备使用厂商定义的协议。
7 bMaxPacketSize0 1 数字 端点0的最大包大小(仅8,16,32,64
为合法值)
8 idVendor 2 ID 厂商标志(由USB-IF组织赋值)
10 idProduct 2 ID 产品标志(由厂商赋值)
12 bcdDevice 2 BCD 码 设备发行号(BCD 码)
14 iManufacturer 1 索引 描述厂商信息的字符串描述符的索引值。
15 iProduct 1 索引 描述产品信息的字串描述符的索引值。
16 iSerialNumber 1 索引 描述设备序列号信息的字串描述符的索引值。
17 bNumConfigurations 1 数字 可能的配置描述符数目

其中bDescriptorType为描述符的类型,其含义可查下表(此表也适用于标准命令Get_Descriptor中wValue域高字节的取值含义):

类型 描述符 描述符值
标准描述符 设备描述符(Device Descriptor) 0x01
配置描述符(Configuration Descriptor) 0x02
字符串描述符(String Descriptor) 0x03
接口描述符(Interface Descriptor) 0x04
端点描述符(EndPont Descriptor) 0x05
类描述符 集线器类描述符(Hub Descriptor) 0x29
人机接口类描述符(HID) 0x21
厂商定义的描述符 0xFF

设备类代码bDeviceClass可查下表:
值(十进制) 值(十六进制) 说明
0 0x00 接口描述符中提供类的值
2 0x02 通信类
9 0x09 集线器类
220 0xDC 用于诊断用途的设备类
224 0xE0 无线通信设备类
255 0xFF 厂商定义的设备类

下表列出了一个USB鼠标的设备描述符的例子,供大家分析一下:
字段 描述符值(十六制)
bLength 0x12
bDecriptorType 0x01
bcdUSB x0110
bDeviceClass 0x00
bDeviceSubClass 0x00
bDevicePortocol 0x00
bMaxPacketSize0 0x08
idVendor 0x045E(Microsoft Corporation)
idProduct 0x0047
bcdDevice 0x300
iManufacturer 0x01
iProduct 0x03
iSerialNumber 0x00
bNumConfigurations 0x01
2.2.2配置描述符
配置描述符中包括了描述符的长度(属于此描述符的所有接口描述符和端点描述符的长度的和)、供电方式(自供电/总线供电)、最大耗电量等。主果主机发出USB标准命令Get_Descriptor要求得到设备的某个配置描述符,那么除了此配置描述符以外,此配置包含的所有接口描述符与端点描述符都将提供给USB主机。

偏移量 域 大小 值 描述
0 bLength 1 数字 此描述表的字节数长度。
1 bDescriptorType 1 常量 配置描述表类型(此处为0x02)
2 wTotalLength 2 数字 此配置信息的总长(包括配置,接口,端点和设备类及厂商定义的描述符)
4 bNumInterfaces 1 数字 此配置所支持的接口个数
5 bCongfigurationValue 1 数字 在SetConfiguration()请求中用作参数来选定此配置。
6 iConfiguration 1 索引 描述此配置的字串描述表索引
7 bmAttributes 1 位图 配置特性:
D7:保留(设为一)
D6:自给电源
D5:远程唤醒
D4…0:保留(设为一)
一个既用总线电源又有自给电源的设备会在MaxPower域指出需要从总线取的电量。并设置D6为一。运行时期的实际电源模式可由GetStatus(DEVICE) 请求得到。
8 MaxPower 1 mA 在此配置下的总线电源耗费量。以 2mA 为一个单位。

下面是一种硬盘的配置描述符示例:
字段 描述符值(十六进制)
bLength 0x09
bDescriptorType 0x02
wTotalLength 0x01F
bNumInterfaces 0x01
bCongfigurationValue 0x01
iConfiguration 0x00
bmAttributes 0x0C
MaxPower 0x32
2.2.3接口描述符
配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。
  如果一个配置描述符不止支持一个接口描述符,并且每个接口描述符都有一个或多个端点描述符,那么在响应USB主机的配置描述符命令时,USB设备的端点描述符总是紧跟着相关的接口描述符后面,作为配置描述符的一部分被返回。接口描述符不可直接用Set_Descriptor和Get_Descriptor来存取。
如果一个接口仅使用端点0,则接口描述符以后就不再返回端点描述符,并且此接口表现的是一个控制接口的特性,它使用与端点0相关联的默认管道进行数据传输。在这种情况下bNumberEndpoints域应被设置成0。接口描述符在说明端点个数并不把端点0计算在内。

偏移量 域 大小 值 说明
0 bLength 1 数字 此表的字节数
1 bDescriptorType 1 常量 接口描述表类(此处应为0x04)
2 bInterfaceNumber 1 数字 接口号,当前配置支持的接口数组索引(从零开始)。
3 bAlternateSetting 1 数字 可选设置的索引值。
4 bNumEndpoints 1 数字 此接口用的端点数量,如果是零则说明此接口只用缺省控制管道。
5 bInterfaceClass 1 类 接口所属的类值:
零值为将来的标准保留。
如果此域的值设为FFH,则此接口类由厂商说明。
所有其它的值由USB 说明保留。
6 bInterfaceSubClass 1 子类 子类码
这些值的定义视bInterfaceClass域而定。
如果bInterfaceClass域的值为零则此域的值必须为零。
bInterfaceClass域不为FFH则所有值由USB 所保留。
7 bInterfaceProtocol 1 协议 协议码:bInterfaceClass 和bInterfaceSubClass 域的值而定.如果一个接口支持设备类相关的请求此域的值指出了设备类说明中所定义的协议.
8 iInterface 1 索引 描述此接口的字串描述表的索引值。

对于bInterfaceClass字段,表示接口所属的类别,USB协议根据功能将不同的接口划分成不同的类,其具体含义如下表所示:
值(十六进制) 类别
0x01 音频类
0x02 CDC控制类
0x03 人机接口类(HID)
0x05 物理类
0x06 图像类
0x07 打印机类
0x08 大数据存储类
0x09 集线器类
0x0A CDC数据类
0x0B 智能卡类
0x0D 安全类
0xDC 诊断设备类
0xE0 无线控制器类
0xFE 特定应用类(包括红外的桥接器等)
0xFF 厂商定义的设备
2.2.4端点描述符
  端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。
除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

偏移量 域 大小 值 说明
0 bLength 1 数字 此描述表的字节数长度
1 bDescriptorType 1 常量 端点描述表类(此处应为0x05)
2 bEndpointAddress 1 端点 此描述表所描述的端点的地址、方向:
Bit 3…0 : 端点号.
Bit 6…4 : 保留,为零
Bit 7: 方向,如果控制端点则略。
0:输出端点(主机到设备)
1:输入端点(设备到主机)
3 bmAttributes 1 位图 此域的值描述的是在bConfigurationValue域所指的配置下端点的特性。
Bit 1…0 :传送类型
00=控制传送
01=同步传送
10=批传送
11=中断传送
所有其它的位都保留。
4 wMaxPacketSize 2 数字 当前配置下此端点能够接收或发送的最大数据包的大小。
对于实进传输,此值用于为每帧的数据净负荷预留时间。在实际运行时,管道可能不完全需要预留的带宽,实际带宽可由设备通过一种非USB定义的机制汇报给主机。对于中断传输,批量传输和控制传输,端点可能发送比之短的数据包

6 bInterval 1 数字 周期数据传输端点的时间间隙。
此域的值对于批传送的端点及控制传送的端点无意义。对于同步传送的端点此域必需为1,表示周期为1ms。对于中断传送的端点此域值的范围为1ms到255ms。

下表是一种鼠标的端点描述符的示例,该端点是一个中断端点:
域 值(十六进制)
bLength 0x07
bDescriptorType 0x05
bEndpointAddress 0x81
bmAttributes 0x03
wMaxPacketSize 0x04
bInterval 0x0A
5、字符串描述符
  
2.2.5字符描述符
字符串描述符是一种可选的USB标准描述符,描述了如制商、设备名称或序列号等信息。如果一个设备无字符串描述符,则其它描述符中与字符串有关的索引值都必须为0。字符串使用的是Unicode编码。
主机请示得到某个字符串描述符时一般分成两步:首先主机向设备发出USB标准命令Get_Descriptor,其中所使用的字符串的索引值为0,设备返回一个字符串描述符,此描述符的结构如下:

偏移量 域 大小 值 描述
0 bLength 1 N+2 此描述表的字节数
1 bDescriptorType 1 常量 字串描述表类型(此处应为0x03)
2 wLANGID[0] 2 数字 语言标识(LANGID)
码0

… … … … …
N wLANGID[x] 2 数字 语言标识(LANGID)
码X
该字符串描述符双字节的语言ID的数组,wLANGID[0]~wLANGID[x]指明了设备支持的语言,主机根据自己需要的语言,再次向设备发出USB标准命令Get_Descriptor,指明所要求得到的字符串的索引值和语言。这次设备所返回的是Unicode编号的字符串描述符,其结构如下:
偏移量 域 大小 值 描述
0 bLength 1 数字 此描述表的字节数(bString域的数值N+2)
1 bDescriptorType 1 常量 字串描述表类型(此处应为0x03)
2 bString N 数字 UNICODE 编码的字串
2.3 USB请求
所有的USB设备在设备的缺省控制通道(Default Control Pipe)处对主机的请求发出响应。这些请求是通过使用控制传输来达到的,请求及请求的参数通过Setup包发向设备,由主机负责设置Setup包内的每个域的值。每个Setup包有8个字节。

偏移量 域 长度 值 描述
0 bmRequestType 1 位图 请求特征:
D7:传输方向
0=主机至设备
1=设备至主机
D6…5:种类
0=标准
1=类
2=厂商
3=保留
D4…0:接受者
0=设备
1=接口
2=端点
3=其他
4…31 保留
1 bRequest 1 值 命令类型编码值(见表3)
2 wValue 2 值 根据不同的命令,含义也不同
4 wIndex 2 索引或偏移 根据不同的命令,含义也不同,主要用于传送索引或偏移值
6 wLength 2 如有数据传送阶段,此为数据字节数。

bmRequestType域:
这个域表明此请求的特性。特别地,这个域表明了第二阶段控制传输方向。如果 wLength 域被设作0的话,表明没有数据传送阶段,那Direction位就会被忽略。
bRequest域:
这个域标识特别的请求。bmRequestType域Type可修改此域的含义。本说明仅定义Type 字位为0即标准设备请求时bRequest域值的含义。
wValue域:
此域用来传送当前请求的参数,随请求不同而变。
wIndex域:
wIndex域用来表明是哪一个接口或端结点,图2-6 表明 wIndex 的格式(当标识端结点时)。Direction位在设为0时表示出结点,设为1时表示是入结点,Endpoint Number是结点号。图 2-7表明wIndex用于标识接口时的格式。

图2-6 端点时wIndex格式

图2-7 接口时wIndex格式
wLength 域:
这个域表明第二阶段的数据传输长度。传输方向由bmRequstType域的Direction位指出。wLength 域为0则表明无数据传输。在输入请求下,设备返回的数据长度不应多于wLength,但可以少于这个值。在输出请求下,wLength指出主机发出的确切数据量。如果主机发送多于wLength的数据,设备做出的响应是无定义的。
2.4 USB标准设备请求
这部分描述的所有USB设备都定义的标准设备请求,表2-2将它们列出,而表2-3给出了对应的标准请求编码值。不管设备是否被分配了非缺省地址或设备当前是已被配置,它们都应当对标准请求产生响应。
表2-2 标准设备请求
命令 bmRequestType bRequest wValue wIndex wLength Data
Clear_Feature 00000000B
00000001B
00000010B CLEAR_FEATURE 特性选择符 零
接口号
端点号 零 无
Get_Configuration 10000000B GET_CONFIGURATION 零 零 一 配置值
Get_Descriptor 10000000B GET_DESCRIPTOR 描述表种类(高字节,见表5)和索引(低字节) 零或语言标志 描述表长 描述表
Get_Interface 10000001B GET_INTERFACE 零 接口号 一 可选设置
Get_Status 10000000B
10000001B
10000010B GET_STATUS 零 零(返回设备状态)
接口号(对像时接口时)
端点号(对象是端点时) 二 设备,
接口 ,或
端点状态
Set_Address 00000000B SET_ADDRESS 设备地址 零 零 无
Set_Configuration 00000000B SET_CONFIGURATION 配置值(高字节为0,低字节表示要设置的配置值) 零 零 无
Set_Descriptor 00000000B SET_DESCRIPTOR 描述表种类(高字节,见表5)和索引(低字节) 零或语言标志 描述表长 描述表
Set_Feature 00000000B
00000001B
00000010B SET_FEATURE 特性选择符(1表示设备,0表示端点) 零
接口号
端点号 零 无
Set_Interface 00000001B SET_INTERFACE 可选设置 接口号 零 无
Synch_Frame 100000010B SYNCH_FRAME 零 端点号 二 帧号
表2-3 标准命令编码值
bRequest Value
GET_STATUS 0
CLEAR_FEATURE 1
为将来保留 2
SET_FEATURE 3
为将来保留 4
SET_ADDRESS 5
GET_DESCRIPTOR 6
SET_DESCRIPTOR 7
GET_CONFIGURATION 8
SET_CONFIGURATION 9
GET_INTERFACE 10
SET_INTERFACE 11
SYNCH_FRAME 12
3 USB鼠标实例
3.1 USB HID设备类协议

  1. HID设备类简介
    HID设备类,即人机接口设备。典型的HID设备有键盘、鼠标等。所有设备类都必须支持标准USB描述符和标准USB设备请求。如果有必要,设备类还可以自行定义其专用的描述符和设备请求。
    HID设备既可以是低速设备也可以是全速设备,其典型的数据传输类型为中断输入传输,即它适用于主机接收USB设备发来的小量到中等量的数据。HID具有以下的功能特点:
    1) 适用于传输少量或中量的数据;
    2) 传输的数据具有突发性;
    3) 传输的最大速率有限制;
    4) 无固定的传输率。

  2. HID的数据传输方式
    HID类仅支持USB的4种传输方式中的2种,即控制传输和中断传输。因此,HID设备和主机上的HID类驱动程序之间就是通过缺省的控制管道和中断管道来传输数据的,如图3-1所示。

图3-1 HID的数据传输方式
其中,控制管道的主要作用有:

  1. 接收并响应主机的控制请求,以及类协议的特定请求;
  2. 接受HID类驱动程序通过Get Report请求发出的轮询检测并发送要求的数据:
  3. 接收主机发来的数据。
    中断管道的主要作用有:
  4. 主机接收设备发来的异步传输数据;
  5. 主机给设备发送有实时性要求的数据。
    图3-1中的中断管道是输入的,而中断输出管道则是可选的。也就是说,HID设备必须支持控制管道和中断输入管道,并可根据实际情况选择是否支持中断输出管道。但是,HID设备仍然需要从主机获取一些数据,并以输出报告的形式由主机发出。当设备支持中断输出管道时,这些输出报告就通过该管道发送。当设备不支持中断输出管道时,这些数据就利用Set Report请求通过控制管道发送。表3-1总结了HID设备支持的数据传输类型。
    表3-1 HID设备支持的数据传输类型

3、HID的属性描述符
HID类支持标准的USB描述符,即设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符。此外,作为一个特定的设备类,HID有其独有的类描述符,即HID描述符、报告描述符和物理描述符。HID的描述符体系结构如图3-2所示。

图3-2 ID的描述符体系结构
5种标准的USB描述符的用法前面已经介绍过,下面介绍3种新的HID特定的类描述符的特点。

  1. HID描述符
    在HID的标准USB描述符中定义的仅是一些通用的信息,基本上没有包含HID的传输特性等信息。因此,HID描述符就用来描述这些额外的信息。同时,可以从图3-2中看出,HID描述符是整个HID特定的类描述符的上层描述符,而报告描述符和物理描述符则是其下层描述符。因此,在HID描述符中就定义了其下层描述符的长度和类型。
  2. 报告描述符
    报告描述符用于定义报告数据的格式和用法。报告描述符由一系列的条目组成。总共有3种类型的条目:主条目、全局条目和局部条目。
    下面这个报告描述符是USB鼠标报告描述符,比起键盘的来说要简单些。它描述了4个字节,第一个字节表示按键,第二个字节表示x轴(即鼠标左右移动,0表示不动,正值表示往右移,负值表示往左移),第三个字节表示y轴(即鼠标上下移动,0表示不动,正值表示往下移动,负值表示往上移动),第四个字节表示鼠标滚轮(正值为往上滚动,负值为往下滚动)。
    code char MouseReportDescriptor[52] = {
    //通用桌面设备
    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    //鼠标
    0x09, 0x02, // USAGE (Mouse)
    //集合
    0xa1, 0x01, // COLLECTION (Application)
    //指针设备
    0x09, 0x01, // USAGE (Pointer)
    //集合
    0xa1, 0x00, // COLLECTION (Physical)
    //按键
    0x05, 0x09, // USAGE_PAGE (Button)
    //使用最小值1
    0x19, 0x01, // USAGE_MINIMUM (Button 1)
    //使用最大值3。1表示左键,2表示右键,3表示中键
    0x29, 0x03, // USAGE_MAXIMUM (Button 3)
    //逻辑最小值0
    0x15, 0x00, // LOGICAL_MINIMUM (0)
    //逻辑最大值1
    0x25, 0x01, // LOGICAL_MAXIMUM (1)
    //数量为3
    0x95, 0x03, // REPORT_COUNT (3)
    //大小为1bit
    0x75, 0x01, // REPORT_SIZE (1)
    //输入,变量,数值,绝对值
    //以上3个bit分别表示鼠标的三个按键情况,最低位(bit-0)为左键
    //bit-1为右键,bit-2为中键,按下时对应的位值为1,释放时对应的值为0
    0x81, 0x02, // INPUT (Data,Var,Abs)
    //填充5个bit,补足一个字节
    0x95, 0x01, // REPORT_COUNT (1)
    0x75, 0x05, // REPORT_SIZE (5)
    0x81, 0x03, // INPUT (Cnst,Var,Abs)
    //用途页为通用桌面
    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    //用途为X
    0x09, 0x30, // USAGE (X)
    //用途为Y
    0x09, 0x31, // USAGE (Y)
    //用途为滚轮
    0x09, 0x38, // USAGE (Wheel)
    //逻辑最小值为-127
    0x15, 0x81, // LOGICAL_MINIMUM (-127)
    //逻辑最大值为+127
    0x25, 0x7f, // LOGICAL_MAXIMUM (127)
    //大小为8个bits
    0x75, 0x08, // REPORT_SIZE (8)
    //数量为3个,即分别代表x,y,滚轮
    0x95, 0x03, // REPORT_COUNT (3)
    //输入,变量,值,相对值
    0x81, 0x06, // INPUT (Data,Var,Rel)
    //关集合
    0xc0, // END_COLLECTION
    0xc0 // END_COLLECTION
    };
    通过对上面的报告分析,我们知道报告返回4个字节,没有报告ID。如果鼠标左键按下,则返回01 00 00 00(十六进制值),如果右键按下,则返回02 00 00 00,如果中键按下,则返回04 00 00 00,如果三个键同时按下,则返回07 00 00 00。如果鼠标往右移动则第二字节返回正值,值越大移动速度越快。其它的类推。
  3. 物理描述符
    物理描述符用于指示当前控制某个HID活动的人体的部位。比如正在按某个键的是人的食指等诸如此类。这个描述符是可选的。
    4、HID的特定设备类请求
    HID设备除了支持USB的标准请求外,自己也定义了特定的6个类请求。这些请求并非每种HID设备都必须支持,而是有选择性的。HID的特定设备类请求如表3-2所示。
    表3-2 HID的特定设备类请求

3.2 USB 抓包图详解
Device:17.0 —— 表示ID为17的设备的0号端点
Phase:阶段列
Cmd.phase.ofs:命令数.阶段数.每个阶段中字节的偏移量

CTL:表示8字节的USB控制传输的Setup包
bmRequestType —— 80:数据方向从USB到PC;标准的请求;USB设备接收
bRequest —— 06:表示接下来发送的数据时GET_DESCRIPTOR(主控器读取USB描述符)
wValue —— 00 01:从偏移地址0开始读取设备描述符
wIndex —— 00 00:一般用于说明端点号或者接口标识
wLength —— 12 00:下一阶段发送数据的长度为18个字节(小端格式理解)

First 【IN】:数据传输(USB设备到PC)
bLength —— 12:数据字节长度为18
bDescriptorType —— 01:设备描述符
bcdUSB —— 00 02:USB设备2.0协议
bDeviceClass —— ef:USB_DEVICE_CLASS_MISCELLANEOUS(杂项)
bDeviceSubClass —— 02
bDeviceProtocol —— 01
bMaxPacketSize0 —— 40:最大包长64个字节
idVendor —— 1Fc9
idProduct —— 100B
bcdDevice —— 0100
iManufacturer —— 01
iProduct —— 02
iSerialNumber —— 03
bNumConfigurations —— 01

Second 【IN】
bLength —— 09:该描述符结构体的大小为9个字节
bDescriptorType ——02:配置描述符
wTotalLength ——29 00:配置返回的所有数据大小为41个字节(其实是指明下一次传输的数据字节大小)
bNumInterfaces ——01:此配置的接口数量
bConfigurationValue ——01:此配置所需要的参数值
iConfiguration ——00:描述该配置的字符串的索引值
bmAttributes ——C0:供电模式的选择USB_CONFIG_SELF_POWERED(自供电)
bMaxPower ——32:设备从总线提取的最大的电流100mA

Third 【IN】(前面数据和第二个IN一样,从32后开始分析)
bLength —— 09:接口描述符结构体大小
bDescriptorType —— 04:USB_INTERFACE_DESCRIPTOR_TYPE
bInterfaceNumber —— 01:该接口的编号
bAlternateSetting —— 00:备用的接口描述符编号
bNumEndpoints —— 02:该接口使用的端点数,不包括端点0
bInterfaceClass —— 03:接口类型为USB_DEVICE_CLASS_HUMAN_INTERFACE(HID)
bInterfaceSubClass —— 00:接口子类型
bInterfaceProtocol —— 00:接口遵循的协议
iInterface —— 04:描述该接口的字符串索引值

HID Class Descriptor
bLength ——09:该HID描述符的大小
bDescriptorType —— 21:HID_HID_DESCRIPTOR_TYPE
bcdHID —— 00 01:HID类协议版本号,为1.1
bCountryCode —— 00:固件的国家地区代码
bNumDescriptors —— 01:下级描述符的个数
bDescriptorType —— 22:下级描述符为HID_REPORT_DESCRIPTOR_TYPE(报告描述符)
wDescriptorLength —— 2C 00:下级描述符的长度

Endpoint HID Interrupt In
bLength —— 07
bDescriptorType —— 05:端点描述符
bEndpointAddress —— 84:HID_EP_IN Address
bmAttributes —— 03:USB_ENDPOINT_TYPE_INTERRUPT
wMaxPacketSize —— 04
bInterval —— 00 20:间隔为16ms

Endpoint HID Interrupt Out
bLength —— 07
bDescriptorType —— 05:端点描述符
bEndpointAddress —— 04:HID_EP_OUT Address
bmAttributes —— 03:USB_ENDPOINT_TYPE_INTERRUPT
wMaxPacketSize —— 04
bInterval —— 00 20:间隔为16ms

CTL:表示8字节的USB控制传输的Setup包
bmRequestType —— 00:数据方向从PC到USB;标准的请求;USB设备接收
bRequest —— 09:表示接下来的请求是SET_CONFIGURATION (设置配置)
wValue —— 00 01:从偏移地址0开始读取设备描述符
wIndex —— 00 00:一般用于说明端点号或者接口标识
wLength —— 00 00
CTL:表示8字节的USB控制传输的Setup包
bmRequestType —— 21:数据方向从PC到USB;Class类的请求;接口接收
bRequest —— 0a:表示接下来的请求是GET_INTERFACE (获取接口)
wValue —— 00 00:从偏移地址0开始读取设备描述符
wIndex —— 01 00:接口标识为01
wLength —— 00 00

CTL:表示8字节的USB控制传输的Setup包
bmRequestType —— 81:数据方向从USB到PC;标准请求;接口接收
bRequest —— 06:表示接下来的请求是获取描述符
wValue —— 00 22:从偏移地址00 22开始读取描述符
wIndex —— 01 00:接口标识为01
wLength —— 6C 00:数据长度为108个字节

Fourth 【IN】——HID Report Descriptor
具体参考usbdesc.c内的设置

分析整个通信过程则可知其传输事务过程如下:

  1. Setup事务,传输Setup数据格式包,告诉设备接下要获取USB描述符,但不知道描述符的类型,并指明了接下来的数据传输方向和传输的数据大小
  2. IN传输事务,表示设备传输数据到PC,传输中指明了这次传输的数据大小,类型是设备描述符以及其他相关的设备信息
  3. Setup事务,改变了读取描述符的偏移地址和传输数据的大小
  4. IN传输事务,但描述符类型是配置描述符,指明了接口数量、供电模式、最大电流等参数
  5. Setup事务,再次获取配置描述符,仅仅改变了获取的传输数据的大小为41个字节,即下一次准备接收41个字节
  6. IN事务,返回配置描述符时连并接口、端点描述符都一起返回,向PC指明了这是个HID接口以及其使用的端点数
  7. Setup事务,设置配置
  8. Setup事务,设置接口闲置状态
  9. Setup事务,获取描述符
  10. IN事务,设备发送HID报告描述符给PC
    3.3 USB鼠标抓包实例
    这个是USB鼠标插入Linux系统的枚举过程的抓包图。

Transfer0:

从图中可以看出,Transfer0由5个Transaction构成。这里所说的USB主机是指USB主控制器。可以看出它是一个控制传输。Transaction0为SETUP Transaction,Transaction1~Transaction3是IN Transaction,Transaction4是OUT Transaction。
Transaction0:
Packet 134是由host发向device,指明是SETUP包。SETUP包的作用是指明当前Transaction为SETUP类型。Packet 134 的ADDR域为0,ENDP域为0,说明这次传输是在设备驱动里的缓冲区与地址为0的设备的端点0之间进行的。地址0是在主机给设备分配地址之前使用的默认地址,是不能分配给设备的,端点0是专门用于控制传输的端点,每个设备都必须有的。而且既是IN端点又是OUT端点,使用message管道。记住每个包必须由Sync字段开始。Packet 135是数据包,是从host发向device,现在来分析它的Data域。Packet136是device发向host的握手包,表示它接受到了这个请求。
Transaction1:
Transaction1是一个IN Transaction。首先host向device发送令牌包IN包,注意IN包中的ADDR域为0,ENDP域为0。Packet 138是device发向host的DATA包,DATA的类型是DATA1。DATA包分两种,DATA0和DATA1,在USB的数据传输中,每个Transaction的数据包的类型需要反转data0,data1,data0,data……这样的顺序发送下去,以保证数据的完整性。所以这里是DATA1,上一个是DATA0。查看DATA包的Data域。
Transaction1~ Transaction3中的Data域得到的正好是这18个字节。Transaction1中的数据域0x12为bLength(描述符的长度),0x01为bDescriptorType(描述符的类型,USB_DT_DEVICE),0x0200为bcdUSB(USB spec的版本号),0x00为bDeviceClass,0x00为bDeviceSubClass,0x00为bDeviceProtocol,0x08为bMaxPacketSize0(端点0一次可以处理的最大字节数)。
Transaction2:
Transaction2的Data域:0x046D为idVendor(厂商ID号),0xc018(产品的ID号),0x4301(设备的版本号),iManufacturer(厂商对应的字符串描述符的索引值),iProduct(产品对应的字符串描述符的索引值)。
Transaction3:
Transaction3的Data域:0x00为iSerialNumber(序列号对应的字符串描述符的索引值),0x01为bNumConfigurations(设备当前速度模式下支持的配置数量)。
Transaction4:
首先发送一个OUT包,注意令牌包主要是指明传输类型,这里就为OUT类型。然后发一个大小为0的DATA包,device回一个ACK握手包。本次传输结束。这是个控制传输过程,在控制传输中有3个阶段:setup阶段,数据阶段和状态阶段。Setup阶段指明了一次主机的请求类型,数据阶段为请求类型所需要传输的数据,最后的状态阶段用于表示一次控制传输的结束。
然后,host向device发送了一个Reset信号,由上边的状态图可以看出,USB由powered模式进入了default模式。
Transfer1:

通过bRequest可以看出这是一个SET_ADDRESS的过程,一共包含两个Transaction。注意这里的ADDR域和ENDP域都是0。Packet275中的Data域,由于这是一个SETUP Transaction,所以这个域里放的是host对device的请求。05表示bRequest为SET_ADDRESS,值为03,所以新地址为3。但是不会立即生效,需要等到这个请求的状态阶段完成地址更改才完成。Transaction6首先host向device发送一个IN类型令牌包,然后device向host发送一个字节为0的DATA包,host回复一个ACK握手包,这个结束。
Transfer 2:

Transfer 2完成的是获得设备描述符。与Transaction0的不同是,这次有了明确的地址。第一次获得设备描述符主要为了得到端点0可以发送的数据的大小,所以只要一个Transaction1的8个字节数据就可。Transfer 2完成了整个设备描述符的读取工作。

USB1.1 协议开发相关推荐

  1. 协议开发 中移动CMPP2.0协议API(三)

    协议开发 中移动CMPP2.0协议API(三) 云网(jimzj@21cn.com) 接上篇... 五.发送接口 对于API来说,最重要的一部分就是去做发送数据了.通过SOCKET套接字与网关相连接后 ...

  2. 《OpenHarmony 3GPP协议开发深度剖析》之--搜网流程之PLMN选择

    协议开发最大的法宝就是反复阅读3GPP协议标准,然后结合标准梳理信令流程,同时比对modem日志或者ap侧日志,阅读modem侧源码或者ap侧源码.本系列主题在操作系统OpenHarmony 侧即ap ...

  3. wireshark编译基于openflow1.3协议开发

    基于wireshark的openflow1.3二次开发(协议拓展) 一.编译 1.1编译前言 本来按照一个中文博客的过程走的,过程本没有错误,只是是多年以前的流程,所需要的软件已经不一样了,,,各种软 ...

  4. Netty(3)之WebSocket协议开发时间服务器

    WebSocket协议开发 1. Http协议弊端 半双工协议:同一时刻,只有一个方向上的数据传送(客户端 --> 服务端 或者 服务端 --> 客户端) 消息冗长繁琐 针对服务器推送的黑 ...

  5. qt的LGPL协议开发商业软件

    现转载http://blog.csdn.net/ojoe/article/details/6283082的文章 查过很多资料了,将商业软件与Qt LGPL的关系归结如下: 1. 必须使用Qt的LGPL ...

  6. 基于SECS协议开发的简明教程

    很有必要把苦程序员久矣的SECS/GEM了结了,于是诞生了本系列文章八篇和开发包,不需要看近500页PDF就可以轻松完成SECS功能支持. 0.SECS SDK 开发包 这个开发包是对SEMI SEC ...

  7. 基于SECS协议开发的简明教程(7)

    接着前面6篇SECS/GEM开发教程系列. 基于SECS协议开发的简明教程(1)-怎么搭建支持SECS工程 基于SECS协议开发的简明教程(2)-怎么编辑交换数据的ID 基于SECS协议开发的简明教程 ...

  8. 基于SECS协议开发的简明教程(6)

    接着前面的五篇SECS/GEM协议开发的系列文章,继续讲本SECS SDK的以下几个功能 怎么触发警报.解除警报(EQP端) 怎么定义Trace(Host端),怎么触发Trace数据(EQP端) 怎么 ...

  9. 基于SECS协议开发的简明教程(5)

    接着前面4篇SECS/GEM开发教程系列. 基于SECS协议开发的简明教程(1)-怎么搭建支持SECS工程 基于SECS协议开发的简明教程(2)-怎么编辑交换数据的ID 基于SECS协议开发的简明教程 ...

最新文章

  1. C什么k什么_G、D、C、Z、T、K、L、Y,这些字母和火车级别有什么关系
  2. MySQL 数据库常用命令
  3. SQL四种语言:DDL,DML,DCL,TCL
  4. 论文浅尝 | 用异源监督进行关系抽取:一种表示学习方法
  5. php字符串转int,php怎样将字符串转为int类型
  6. ruby 将字符串转为数组_Ruby程序将数组打印为字符串
  7. 图像膨胀、腐蚀算法实现 python源码
  8. 【项目管理】在IDEA中使用MyBatis_Generator生成Dto、Dao、Mapping
  9. 小米笔记安装双系统linux,小米笔记本电脑怎么安装双系统?-小米win7
  10. python pyhook_python中使用pyhook实现键盘监控的例子
  11. 【XGantt教程】为甘特图分组添加舒适排序选项的三个步骤
  12. 【转载】使用theano进行深度学习实践(一) - CSDN博客
  13. 用断点续存实现视频快速上传
  14. [2001-2003美/新等合拍经典奇幻大片][魔戒1-3][BD-RMVB][中英字幕/1280x720高清晰版]
  15. 为什么要配置JAVA_HOME,有什么用?谁在用JAVA_HOME
  16. python的信号量机制
  17. linux查看硬盘命令 ok,linux查看硬件信息大全
  18. 当Java、C++、Python等编程语言都变成软妹子
  19. ActiveX控件打包cab时INF文件的编写[转]
  20. python连接sql server数据库(pyodbc)

热门文章

  1. AI读懂说话人情绪,语音情感识别数据等你Pick!
  2. 凸包问题的GRAHAM-SCAN解法(附C++代码)
  3. 闽南师范大学计算机科学学院副书记,CCF闽南师范大学学生分会完成换届
  4. Python Tkinter 之Listbox控件(Python GUI 系列12)
  5. 【探花交友】day06—即时通信
  6. 怎么计算计算机的网络地址,如何计算IP地址的网络号和主机号?
  7. java线程池newfi_Java 线程池中的线程复用是如何实现的?
  8. AcWing 188. 武士风度的牛
  9. Windows2016 L2TP配置(预共享密钥模式)
  10. mac如何把html转成word,Pages怎么保存为word格式 pages保存格式教程