第三篇 USB描述符以及请求

一、USB描述符

作为基础,首先要掌握USB设备的标准描述符(不管是什么类型的设备,都有这几个描述符存在),而不同类的设备,又有自己特殊的描述符,后面介绍不同类设备的时候,再进行介绍。

 

1. 设备描述符

每个USB设备都必须并且只有一个设备描述符(在程序中定义好设备描述符)。USB协议对设备描述符的定义如下:

                                                                          设备描述符结构表

偏移量/字节

大小/字节

说明

0

bLength

1

描述符的长度 (18Byte=0x12)

1

bDescriptorType

1

描述符类型(设备描述符 = 0x01)

2

bcdUSB

2

本设备使用的USB协议版本(1.1 or 2.0)

4

bDeviceClass

1

类代码

5

bDeviceSubClass

1

子类代码

6

bDeviceProtocol

1

设备所使用的协议

7

bMaxPacketSize0

1

端点0的最大包长

8

idVendor

2

厂商ID

10

idProduct

2

产品ID

12

bcdDevice

2

设备版本号

14

iManufacturer

1

描述厂商字符串的索引

15

iProduct

1

描述产品字符串的索引

16

iSerialNumber

1

产品***字符串的索引

17

bNumConfigurations

1

可能的配置数(设备描述符下配置描述符的个数)

说明:

1)bcdUSB是该设备所使用的USB协议版本号,长度2字节。比如可以取2.0或者1.1等版本号。需要特别注意的是,协议规定使用BCD码来表示版本号,比如:USB2.0协议就是0x0200,USB1.1协议就是0x0110。对照USB协议分析仪来看的时候,要注意,USB协议中使用的是小端结构,也就是低字节在前。比如说,USB2.0协议拆分成两个字节就是0x00 0x02,那么对照协议分析仪里面的数据就是:00 02 ;USB1.1在协议分析仪里面的数据就是:10 01。

2)bDeviceClass是设备所使用的类代码(XX类接口描述符码)。常用的类如下(根据协议,进行C宏定义):

//HID设备类接口描述符码

#define HID_CLASS                    0x03

//音频类接口描述符码

#define Audio_CLASS                 0x01

//视频类接口描述符码

#define Vedio_CLASS                 0x0E

//大容量设备类接口描述符码

MASS_STORAGE_CLASS           0x08

//杂项类或者混合类接口描述符码

#define MISC_CLASS                           0xEF

//厂商自定义的设备类接口描述符码

#define CUSTOM_CLASS                    0xFF

//特定应用类接口描述符码

#define DFU_DEVICE_CLASS            0xFE

3)bDeviceSubClass设备所使用的子类代码。当类代码不为0也不是0xFF时,子类代码就得根据协议来进行赋值。当类代码为0的时候,子类代码也必须为0。

4)bDeviceProtocol是设备使用的协议。协议代码由USB协议规定。

注:

开发标准USB设备时,设备描述符中的 bDeviceClass、bDeviceSubClass、bDeviceProtocol这三个字段同时设置为0。然后在接口描述符中指定当前的设备类、子类以及协议类型(即:在接口中指定设备的类!),在PC端开发USB上位机软件的时候,需要特别注意的地方。

5)端点0的最大包长,取值可以是:8、16、32、64字节。注意,对应的十六进制数分别就是:0x08、0x10、0x20、0x40(分析源码和协议分析仪里面的数据,注意进行转换)。

6)关于厂商ID (2Byte),在开发中,可以随意设定一个值。真正做产品,要使用公司的ID(向USB协会申请),避免侵权。对于插入的设备,主机是依靠厂商ID号、产品ID号、产品***来安装驱动的。

7)产品ID是生产厂商自己定义的,比较自由。

8)bcdDevice设备版本号。同一个产品,升级之后(比如固件修改,新增功能),可以通过修改设备版本号来进行区别。

9)iManufacturer是描述厂商字符串的索引值。如果设为0,则表示该USB设备没有厂商字符串。主机单独获取厂商字符串的时候,下发的标准请求数据包中,wValue域的第一个字节【低字节】就是厂商字符串的索引值,而高字节就是描述符的类型(字符串描述符0x03)。

厂商字符串就是一串普通的字符串,在设备描述符中,有三个非0的索引值:厂商字符串的索引值为1;产品字符串的索引值为2;产品***字符串的索引为3。设备在收到主机的字符串描述符请求之后,根据索引值,将对应的字符串数据返回给主机。

所以,如果解析到主机字符串描述符请求的数据包,如果wValue=0x0301,则表示主机请求获得厂商字符串。

10)iProduct是描述产品的字符串的索引值。同样的,如果设置为0,则表示该USB设备没有产品字符串。第一次插上设备时,提示发现新硬件,并显示设备的名称,其实这里显示的信息就是从产品字符串中获取的。实验:可通过修改产品字符串,再编译固件烧录,插入设备,就可以看到提示信息了。

同理,当主机请求产品字符串的时候,wValue这个域的值应该是:0x0302

11)iSerialNumber是设备的***字符串的索引值。同样的,如果设置为0,则表示该USB设备没有设备***。最好一个产品指定一个唯一的***,因为有可能主机会结合产品***和VID、PID来进行设备的区分和加载对应的驱动。同理,当主机请求***字符串的时候,wValue这个域的值应该是:0x0303

 

2. 配置描述符

一个USB设置至少有一个配置描述符。

                                                                        标准配置描述符结构

偏移量/字节

大小/字节

说明

0

bLength

1

该描述符的长度(9 Byte=0x09)

1

bDescriptorType

1

该描述符的类型(配置描述符是0x02)

2

wTotalLength

2

配置描述符集合 的总长度

4

bNumInterfaces

1

该配置下的接口数

5

bConfigurationValue

1

该配置的值

6

iConfiguration

1

描述该配置的字符串的索引值

7

bmAttributes

1

该设备的属性

8

bMaxPower

1

该设备所需的电流(单位 2mA)

说明:

1)wTotalLength是整个配置描述符集合的总长度,配置描述符集合就包括:配置描述符自身的长度、接口描述符、类特殊描述符、端点描述符等。

2)bNumInterfaces是该设备所支持的接口数量。通常来说,功能单一的设备就只有一个接口(比如鼠标、键盘),而复合设备则具有多个接口(比如音频设备,一般是HID + UAC)。

3)bConfigurationValue:一个USB设备可以有很多个配置。bConfigurationValue就是每个配置的标识(ID)!接下来会讲到设置配置这个标准请求,进行设置配置这个请求的时候,主机会发送一个配置值,如果某个配置的bConfigurationValue和主机请求的配置值相匹配,就表示该配置被**,USB设备就使用这个配置。(主机决定,设备使用哪个配置)

4)iConfiguration为0,则表示没有字符串来描述该配置描述符。

5)bmAttributes:大小为一字节,不同的位,表示不同的特性。

(1)第7位D7是保留的,必须为1。

(2)第6位D6表示供电方式:1设备自供电;0设备是总线供电的。

(3)第5位D5表示是否支持远程唤醒:1 支持远程唤醒  0 不支持远程唤醒。

(4)第0位到第4位D4~D0是保留的,默认为0。

6)bMaxPower:大小为1字节。表示设备从总线获取的最大电流量,单位是2mA。比如,如果需要200mA的电流,那么该字节的值就是100(100 x 2mA = 200mA)。

3. 接口描述符

接口描述符不能单独返回,要附着配置描述符后一并返回(Host请求配置描述符集合后,Device返回一堆数据)。

偏移量/字节

大小/字节

说明

0

bLength

1

描述符的长度(9 Byte)

1

bDescriptorType

1

该描述符的类型(0x04)

2

bInterfaceNumber

1

该接口的编号(从0开始)

3

bAlternateSetting

1

该接口的备用编号

4

bNumEndpoints

1

该接口所使用的端点数

5

bInterfaceClass

1

该接口所使用的的类

6

bInterfaceSubClass

1

该接口所使用的的子类

7

bInterfaceProtocol

1

该接口所使用的的协议

8

iInterface

1

描述该接口的字符串的索引值

说明:

1)bInterfaceNumber:当一个配置有多个接口时,每个接口的编号都不相同,从0开始递增对一个配置的接口进行编号。这里需要注意,对照一下,配置描述符里面,支持多少个接口(这个域:bNumInterfaces)。

2)bAlternateSetting:备用端口号,也是从0开始。一般很少用到该字段(开发UAC就必须用到这个字段),设置为0即可。Alternate:备用

3)bNumEndpoints:是该接口使用的端点数,不包括0端点。如果该字段设置为0,那么表示没有非0端点。

4)bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol:分别是接口所使用的类、子类、和协议,对应的代码(编号)由USB协议来定义。跟设备描述符中的意义很相像,设备描述符也有类似的三个域 (如果要开发自己的上位机,则这三个字段都设置为0xFF)。

5)iInterface:如果设置为0,则表示没有字符串。

 

4. 端点描述符

端点描述符不能单独返回,要附着配置描述符后一并返回(Host请求配置描述符集合后,Device返回一堆数据)。

偏移量/字节

大小/字节

说明

0

bLength

1

该描述符的长度(7 Byte)

1

bDescriptorType

1

该描述符的类型(0x05)

2

bEndpointAddress

1

该端点的地址及输入输出属性

3

bmAttributes

1

该端点的传输类型属性

4

wMaxPacketSize

2

该端点支持的最大包长

6

bInterval

1

端点的查询时间

说明:

1)bEndpointAddress:大小1字节,端点的地址。

最高位,也就是第7位D7:表示该端点的传输方向。1表示输入(像Input的第一个字母) 0表示输出(像Output的第一个字母)

D6~D4:保留位,默认为0。

D3~D0:才是端点号

2)bmAttributes是端点的属性。

(1)最低两位D1~D0:表示该端点的传输类型。0为控制传输;1为等时传输;2为批量传输;3为中断传输。两个位,也就四种情况:00   01   10   11

(2)如果是非等时传输(控制传输、批量传输、中断传输中的一种),那么D7~D2都为0

(3)如果该端点时等时传输,则:

D3~D2表示同步的类型:0为无同步、1为异步、2为适配、3为同步。

D5~D4表示用途:0为数据端点、1为反馈端点、2为暗含反馈的数据端点、3是保留值。

D7~D6:保留,设为0。

3)wMaxPacketSize:大小2字节,表示该端点每次传输的最大包长(单位:字节)。关于端点的最大包长,需要注意两点,其一是USB协议规定的最大包长上限,其二是需要注意结合实际开发场合来规定端点最大包长(比如:开发UAC的时候,端点最大包长要根据采样率、通道数等来计算并设置,否则PC端就不能正确进行重采样,声音就会失真)。

4)bInterval:表示端点查询的时间。对于中断端点以及同步传输,表示端点的轮询时间间隔,而对于块传输,该字段没有意义(介绍到具体的类设备时,该字段会作为重点解析的字段)。USB协议中对该字段的描述如下图:

 

5. 字符串描述符

主机在获得配置描述符集合之后,会下发获得语言ID的请求(索引值为0),以及获得字符串描述符的请求。在USB协议中,字符串描述符是可选的。在设备描述符中,申请了三个非0的索引值:

1:是厂商字符串的索引值

2:是产品字符串的索引值

3:是产品***的索引值

主机通过索引值来获取对应的字符串数据。索引值为0则表示获取的是语言ID字符串。字符串描述符的结构很简单,如下:

首先是语言ID描述符的结构

偏移量/字节

大小/字节

说明

0

bLength

1

该描述符的长度(4字节=0x04)

1

bDescriptorType

1

描述符类型(字符串类型为0x03)

2

wLANGID

2

语言ID号

字符串描述符(产品+厂商+产品***)的结构

偏移量/字节

大小/字节

说明

0

bLength

1

该描述符的长度(n)

1

bDescriptorType

1

描述符类型(字符串类型为0x03)

2

bString

N

UNICODE编码的字符串

说明:

1)语言ID,只使用美式英语的一种,即0x0409。

2)bString字段使用的是UNICODE编码的字符串,使用两个字节来表示一个字符。

3)如果设备描述符中的iManufacturer、iProduct、iSerialNumber都设置为0的话,主机就不会下发对设备字符串描述符的请求(调试),所以说,开发过程中,字符串描述符并不是必须的。但是,针对产品的研发,它又是必须的。

 

6. 各个描述符之间的关系

1)一个设备,只有一个设备描述符。

2)一个设备描述符可以包含多个配置描述符。

3)一个配置描述符可以包含多个接口描述符。

4)一个接口描述符可以包含多个端点描述符。

描述符之间的包含关系如下图(站在集合的角度去理解):

首先要知道,USB描述符之间的关系是一层一层的,理解了这一点,有助于我们去分析整个设备枚举过程(下一篇介绍)。Host获取描述符的顺序,是从最顶层开始,获取的顺序为:设备描述符---------->配置描述符-------->……。

 

二、请求

请求和描述符一样,有标准的设备请求,而对于不同类的设备,又有自己特定的请求。先介绍标准的设备请求,对于特殊的请求,介绍每个不同的类设备时,再做解析。

 

1. 标准设备请求的数据结构

USB协议中规定,标准请求的长度为8个字节。在设备枚举过程中,Host会下发一系列的标准请求,设备端需要去解析这些标准请求(SETUP事务),并作出正确响应,设备才能成功枚举。成功枚举之后,才能调用相关接口进行数据通信。

8字节的标准请求结构如下:

bmRequestTtype(1 Byte)

bRequest(1 Byte)

wValue(2 Byte)

wIndex(2 Byte)

wLength(2 Byte)

0xXX

0xXX

0xXXXX

0xXXXX

0xXXXX

每个域的解析如下表

                                                                         标准请求结构表

偏移量

大小/字节

取值类型

字段的描述

0

bmRequestTtype

1

位图

当前请求的特性,每个位的取值以及含义如下:

D7:数据阶段,数据的传输方向。也就是Host请求设备返回数据还是Host告诉设备它即将要发出数据。

0:主机--->设备(输出)

1:设备--->主机(输入)

 

D6~D5:请求的类型

0:表示这是一个标准请求

1:这是一个特定的类请求

2:这是一个厂商自定义的请求

3:缺省值

 

D4~D0:请求的接收方

0:这是发给设备的请求

1:这是发给接口的请求

2:这是发给端点的请求

3:不是以上三者之一

4~31:缺省值

1

bRequest

1

数值

请求码

2

wValue

2

数值

看具体的请求,不同的请求含义不同

4

wIndex

2

数值

看具体的请求,不同的请求含义不同

6

wLength

2

数值

数据过程的需要传输的字节数(0或非0)

对于标准的请求,D6~D5 = 00,USB协议规定了11个标准请求,请求码(bRequest)如下表。

                                                                           标准请求代码表

bRequest

代码值

GET_STATUS

0(0x00)

CLEAR_FEATURE

1(0x01)

SET_FEATURE

3(0x03)

SET_ADDRESS

5(0x05)

GET_DESCRIPTOR

6(0x06)

SET_DESCRIPTOR

7(0x07)

GET_CONFIGURATION

8(0x08)

SET_CONFIGURATION

9(0x09)

GET_INTERFACE

10(0x0a)

SET_INTERFACE

11(0x0b)

SYNCH_FRAME

12(0x0c)

下面重点介绍三个标准请求。

 

2. GET_DESCRIPTOR请求

获取描述符请求。该请求由Host发出,设备端解析请求,并返回指定描述符数据到Host,是设备枚举过程中用的最多的一个请求。

 

2.1 获取设备描述符请求结构

 

bmRequestTtype(1 Byte)

bRequest(1 Byte)

wValue(2 Byte)

wIndex(2 Byte)

wLength(2 Byte)

10000000B=80H

(0x80)

GET_DESCRIPTOR

(0x06)

 

描述符的类型和索引号

0或字符串描述符的语言ID号

描述符的长度

对每个域的解析如下

1)wValue有两个字节,低字节表示:索引号。即同一类描述符里面的哪一个描述符。

例1:字符串描述符可分为厂商字符串、产品字符串、产品***字符串,对应的索引值分别为1、2、3。如果索引值为0,则表示获取的是语音ID字符串。

例2:假如一个设备有多种配置(定义了多个配置描述符),那么索引号就是配置描述符的ID号(bConfigurationValue)。

wValue的高字节表示描述符的类型,USB协议规定不同的类型由一个唯一的码值来表示,如下表:

                                                                       标准描述符类型及其对应的编号

描述符的类型

编号

设备描述符(Device Descriptor)

1(0x01)

配置描述符(Configuration Descriptor)

2(0x02)

字符串描述符(String Descriptor)

3(0x03)

接口描述符(Interface Descriptor)

4(0x04)

端点描述符(Endpoint Descriptor)

5(0x05)

2)wIndex

这个域是专门为获取字符串描述符而设置的。当主机请求获取其他描述符时,这个域的值一定是0;当主机请求获取那三个字符串描述符时,这个域是字符串的语言ID号,一般用的是美式英语,值固定为0x0409,该值就是主机获取字符串描述符语言ID的时候,设备返回的值。所以,主机获取设备字符串描述符的时候,顺序一般都是(假如在设备描述符中设置字符串描述符的索引值为非0):

A. 最先请求的是字符串描述符的语言ID

B. 请求厂商字符串

C. 请求产品字符串

D. 请求产品***字符串

3)wLength

数据过程,要求设备返回的数据长度。设备端返回的数据可以小于这个值,比如枚举过程中,Host(win7)会一次性请求长度为0xFF的配置描述符。

注:

(1) 如果设备工作在全速模式(Full-Speed Mode)下,那么主机端获取标准描述符,只有三个标准请求:A.获取设备描述符的请求;B.获取配置描述符的请求;C.获取字符串描述符的请求。对于接口描述符和端点描述符,是在主机端请求配置描述符集合的时候,一并返回。

(2) USB协议规定,总线的传输方式是串行方式,并且是LSB在前,……,MSB在最后。这是分析BusHound或者协议分析仪上的数据需要注意的地方。

 

3. SET_ADDRESS请求

主机在收到第一个数据包(设备描述符数据包),解析无误后,接下来就进入设置地址阶段。

设置地址请求也是一个USB标准设备请求。这是一个分配地址的过程(主机给设备分配一个地址)。既然是标准请求,那么它也是有8个字节的数据。指定的地址包含在wValue字段中。

主机从地址为0的设备获取设备描述符,一旦第一次成功获取到设备描述符之后,主机就会立刻发送设置地址的请求,减少设备使用公共地址0的时间(每个设备插入,都先被复位,默认的地址为0,也就是0地址是所有的USB设备的初始化地址,即可以理解为公共地址)。

 

3.1 设置地址请求的结构

设置地址的请求结构如下:

bmRequestType

bRequest

wValue(2Byte)

wIndex(2Byte)

wLength(2Byte)

数据过程

0x0000 0000

0x00

SET_ADDRESS

0x05

设备地址

0x0000

0x0000

没有

说明:

(1) 设置地址的请求过程,是没有数据的,所以,数据长度就是0。

(2) 索引也用不着(请求字符串描述符,索引才用的上),所以也为0。

 

3.2 实例

主机下发的数据包为:    00            05           1D 00           00 00    00 00

解析:       数据方向【主机--->设备】 设置地址请求  地址数据为0x001D=29

协议分析仪中的地址数据

分析:wValue域的值是0x001D。转成十进制就是29,也就是主机分配给设备的地址是29。USB数据传输使用的是小端模式,低字节在前,所以,在协议分析中的数据就是:1D 00。

 

4. SET_CONFIGURATION请求

设置配置的请求下发后,设备端的USB控制器进入配置状态。设备端根据配置值,使用对应的配置,USB设备才能正常工作。

 

4.1 设置配置的请求结构

 

bmRequestType

bRequest

wValue(2 Byte)

wIndex(2 Byte)

wLength(2 Byte)

数据过程

0x00

0x09

配置描述符的ID

0x0000

0x0000

没有

说明:

(1) 在设置配置请求中,wValue的第一字节(低字节)为配置的值。当该值与某配置描述符中的配置编号一致时(相等时),表示选中的是该配置,接下来设备就使用这个配置。

 

4.2 实例

一个USB设备可以有很多个配置。bConfigurationValue就是每个配置的标识!主机请求设置配置的时候,会下发一个配置值,如果某个配置的bConfigurationValue和主机请求的配置值相匹配,就表示该配置被**,USB设备就使用这个配置(由主机决定,设备使用哪个配置)。

设置配置请求是一个输出请求,根据所请求的配置值,使能相应的端点。设备收到之后,返回一个0长度的状态数据包。设备收到非0的配置值之后,才会使能非0端点。否则会禁用非零端点。

分析协议分析仪上的数据:

结合上面协议分析仪里面的数据,wValue=0x0001。也就是说,主机告诉设备,使用的是编号为1的配置。在程序中,假如定义了一个配置描述符,并且该配置的编号是1。实际上,主机请求的配置编号都是存在的,因为按照枚举过程的先后顺序,Host已经先获取到了配置描述符,所以主机下发的请求中,配置值是合法的。同理,主机也可以通过解析配置描述符数据就可以知道,当前设备总共有多少个配置。

 

三、设备端状态变化

USB设备在枚举完成之前,控制器会有一系列的状态变化。这些状态分别是:连接状态(attached)、供电状态(powered)、默认状态(default)、地址状态(address)、配置状态(configures)、挂起状态(suspended)。这些状态都可以根据具体的IC设计情况,配置寄存器,让USB控制器产生事件中断。

                                                                                       图:枚举过程中设备状态