USB-详解描述符
USB描述符描述了USB设备的信息和功能,是USB实现功能的说明书,十分重要。
主机通过USB请求获取设备描述符,USB请求和USB描述符各有很多种类,本章通过USB枚举过程,介绍常用的描述符。
一,USB枚举过程
USB枚举过程主要有四部,分别获取不同的信息。
- 第一次插入,复位设备并请求获取部分设备描述符。
- 再次复位,请求设置地址。
- 用新地址,获取全部设备描述符。
- 获取配置描述符,紧接着获取配置集合(剩余描述符)。
- 如果有字符串描述符,获取字符串描述符。
该过程中,涉及USB标准请求和描述符结构,全部使用控制传输传输数据。与端点0进行通信。
二,预获取描述符
第一次插入时,会获取设备描述符,标准设备描述符共有18字节,但有些设备端点0只有8字节。此时,USB主机仅发送一次数据输入请求,获取8字节后就停止,即数据过程主机只发送一次输入令牌包。因为第8字节说明了端点0的最大包长度。
USB标准请求
USB标准设备请求一共8字节,在控制传输的建立过程向端点0发送。任何设备都能接收(USB规定端点0最小8字节)。
USB标准请求的结构

bmRequestType的D6-5为00时,为USB标准请求,共有11个,由bRequest的值决定。
wValue和wIndex的含义随着请求不同而变化。
wLength表示了数据过程要传输的字节数量。
各种标准请求传输的数据如下:
| bRequest | Value | bRequest | Value |
|---|---|---|---|
| GET_STATUS | 00H | GET_CONFIGURATION | 08H |
| CLEAR_FEATURE | 01H | SET_CONFIGURATION | 09H |
| SET_FEATURE | 03H | GET_INTERFACE | 0AH |
| SET_ADDRESS | 05H | SET_INTERFACE | 0BH |
| GET_DESCRIPTOR | 06H | SYNCH_FRAME | 0CH |
| SET_DESCRIPTOR | 07H |

其中,获取配置的请求如下:
GET_DESCRIPTOR请求
| bmRequestType | bmRequest | wValue | wIndex | wLength | 数据过程 |
|---|---|---|---|---|---|
| 1000 0000B(0x80) | 06H(GET_DESCRIPTOR) | 描述符类型和索引 | 0或语言ID | 描述符长度 | 描述符 |
wValue的第一个字节(低字节)是索引号,用于选择同一种描述符的第几个(如字符串描述符和配置描述符)。第二个字节表示描述符的类型编号。
wIndex用于在字符串描述符指定语言ID,其余为0。
wLength表示请求设备返回的字节数,但实际返回一般比这个少。
对全速和低速设备,只有三种获取描述符:设备描述符、配置描述符、字符串描述符。接口描述符、端点描述符、类描述符都是随着配置描述符一起返回(配置集合)。
设备在收到该请求后,在数据过程返回相应描述符。
描述符类型编号:
| 描述符类型 | 编号 |
|---|---|
| 设备描述符 (DEVICE) | 1 |
| 配置描述符 (CONFIGURATION) | 2 |
| 字符串描述符 (STRING) | 3 |
| 接口描述符 (INTERFACE) | 4 |
| 端点描述符 (ENDPOINT) | 5 |
设备描述符
设备描述符一共18字节,第一次获取可能只会获取8个,得到端点0最大包长。
设备描述符的结构
| 偏移量 | 名称 | 大小 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符长度(18字节) |
| 1 | bDescriptorType | 1 | 描述符类型(设备描述符为 0x01) |
| 2 | bcdUSB | 2 | 设备使用的 USB 协议版本 |
| 4 | bDeviceClass | 1 | 类代码 |
| 5 | bDeviceSubClass | 1 | 子类代码 |
| 6 | bDeviceProtocol | 1 | 设备使用的协议 |
| 7 | bMaxPacketSize0 | 1 | 端点 0 最大包长(仅 8、16、32、64 为合法值) |
| 8 | idVendor | 2 | 厂商 ID |
| 10 | idProduct | 2 | 产品 ID |
| 12 | bcdDevice | 2 | 设备版本号 |
| 14 | iManufacturer | 1 | 描述厂商的字符串的索引 |
| 15 | iProduct | 1 | 描述产品的字符串的索引 |
| 16 | iSerialNumber | 1 | 产品序列号字符串的索引 |
| 17 | bNumConfigurations | 1 | 可能的配置数 |
bLength——描述符长度,这里为0x12(18)bDescriptorType——描述符类型,见上面的表格,这里为0x01(1)bcdUSB——USB协议版本BCD表示,2.0(0x0200) 1.0(0x0110)bDeviceClass——设备类别,一般为0,具体类别在接口描述符中定义。如果接口描述符没有定义,就会使用这里的。具体类别见USB文档,0xFF为厂商自定义设备。bDeviceSubClass——子类代码,由协议规定。当bDeviceClass为0,该项也必须为0.bDeviceProtocol——设备使用协议。必须和设备类结合才有意义,因此设备类为0,该项也要为0。bMaxPackeSize0——端点0的最大包长度。可以为8、16、32、64.由具体硬件决定。
idVender——厂商ID号(VID),由USB协会分配(买的)。个人学习就随便设置。idVender、idProduct、字符串描述符中的产品序列号三者被用于寻找加载设备驱动,需要注意做实验时需要修改其中一项防止加载了旧驱动导致无法工作。idProduct——产品ID号(PID),由厂商编号。个人就随便设置。bcdDevice——设备bcd版本号,用于区别版本。iManufacturer——厂商字符串在字符串描述符的索引,为0为没有。iProduct——描述产品字符串的索引值,为0为没有。第一次插上设备时,Win弹出的对话框显示的设备名称就是这个字符串。iSerialNumber——设备序列号的索引值,为0为没有。最好每个产品都指定一个唯一值,因为可能被用于联合VID、PID匹配驱动。bNumConfigurations——配置数量,一般设备都只有一种配置,为1.
一个设备描述符可以写成一个c数组,内容依次排列。
设备描述符的返回逻辑
设备通过控制端点0来返回设备描述符。
在端点0的输出中断中,判断获取描述符的请求,将描述符写入到端点0的输入缓冲区中,并使能发送。这样,主机下次发送IN令牌时,外设就会返回数据。
如果需要发送的数据比端点长度长,那么可以在端点输入中断中继续发送(写入缓冲区)。数据包长刚好是整个数据包时,还需要再返回一个0长数据包。
对初次获取描述符的情况,可能需要进行判断请求输入的长度。有的USB外设会在收到下一个SETUP包时自动将端点的输入缓冲清零。
三,设置地址
再次对设备进行复位后,主机会发送一个设置地址请求,为设备分别新地址。
SET_ADDRESS请求
| bmRequestType | bmRequest | wValue | wIndex | wLength | 数据过程 |
|---|---|---|---|---|---|
| 0000 0000B(0x00) | 05H(SET_ADDRESS) | 设备地址 | 0x0000 | 0x0000 | 无 |
设置地址比较简单,地址直接在请求中,没有数据过程。
复位后初始地址都是0,当设备收到设置地址请求后,直接进入状态过程,返回长度0的状态数据包。等待主机读取状态包返回ACK响应后,设置启用新地址。
等待ACK使能地址可以由USB外设自动完成。
四,获取其他描述符
-
启用新地址后,主机会再次发送请求设备描述符的请求,这次需要返回全部的描述符(会有多个IN数据包)。
-
获取完整设备描述符后,主机会发送一个获取9字节长度的配置描述符申请。在后面,会根据描述符中的配置集合的长度之后再次发送一个配置描述符申请获取整个配置集合。
-
获取完9字节的配置描述符后,主机接下来会获取字符串描述符。首先,主机会请求索引为0的字符串,但实际没有字符串使用索引0,因此,实际返回的是语言ID描述符。获取语言ID后,接着获取了产品序列号字符串。
-
主机获取全部配置描述符集合。
-
主机再次获得语言ID和产品描述字符串。
-
主机再次获取设备描述符和配置描述符集合。
-
主机发送设置配置请求。
配置描述符
每个USB设备至少要有一个配置描述符。该描述符一共9字节。
配置描述符结构
| 偏移量/字节 | 域 | 大小/字节 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 该描述符的长度(9字节) |
| 1 | bDescriptorType | 1 | 描述符类型(配置描述符为 0x02) |
| 2 | wTotalLength | 2 | 配置描述符集合总长度 |
| 4 | bNumInterfaces | 1 | 该配置所支持的接口数 |
| 5 | bConfigurationValue | 1 | 该配置的值 |
| 6 | iConfiguration | 1 | 描述该配置的字符串的索引值 |
| 7 | bmAttributes | 1 | 该设备的属性 |
| 8 | bMaxPower | 1 | 设备所需要的电流(单位为 2 mA) |
bLength——描述符长度,配置描述符为9字节。bDescriptorType——描述符类型,配置描述符为0x02。wTotalLength——整个配置描述符集合的总长度(配置描述符、接口描述符、类描述符、端点描述符)bNumInterfaces——配置的接口数量bConfigurationValue——该配置的值,每个配置的标识iConfiguration——配置的字符串索引,为0则没有。bmAttributes——一些特性,如供电方式、是否支持远程唤醒。D7保留为1,D6为1表示自供电否则总线供电,D5为1表示支持远程唤醒。其余保留为0bMaxPower——表示设备需要最大电流量,单位为2mA
第一次获取后,主机知道了wTotalLength,下次就会获取整个配置集合。
字符串描述符
字符串描述符结构
请求字符串索引为0,表示获取语言ID。
- 语言ID
| 偏移量/字节 | 域 | 大小/字节 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 该描述符的长度 |
| 1 | bDescriptorType | 1 | 描述符类型(字符串为 0x03) |
| 2 | wLANGID[0] | 2 | 语言 ID 号 0 |
| ⋮ | ⋮ | ⋮ | ⋮ |
| 2·n + 2 | wLANGID[n] | 2 | 语言 ID 号 n |
请求中会包含请求那种语言ID,但这里统一使用美式英语0x0409(驱动支持)。
- 字符串描述符
| 偏移量/字节 | 域 | 大小/字节 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 该描述符的长度 |
| 1 | bDescriptorType | 1 | 描述符类型(字符串为 0x03) |
| 2 | bString | N | UNICODE 编码的字符串 |
返回UNICODE编码的字符,因此可以支持中文(需要软件转换)。
配置描述符集合
以下描述符必须和配置描述符一起返回,因为单独返回的话就不知道属于哪个配置了。
因此,第二次获取配置描述符时,这些描述符和配置描述符打包成配置描述符集合一起返回。
接口描述符
接口描述符结构如下,共9字节:
| 偏移量/字节 | 域 | 大小/字节 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 该描述符的长度(9 字节) |
| 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 | 描述该接口的字符串的索引值 |
bLength——描述符长度,接口描述符为9字节。bDescriptorType——描述符类型,接口描述符为0x04。bInterfaceNumber——接口编号,从0开始,同配置不重复。bAlternateSetting——备用编号,一般为0.wNumEndpoints——该接口非0端点数.若为0,表示只有控制端点0。bInterfaceClass\bInterfaceSubClass\bInterfaceProtocol——接口类、子类和协议,具体代码由设备类决定。比如HID设备类。功能同设备描述符中字段一致,但作用范围不同,为了灵活性,一般在这里定义。iInterface——该接口字符串索引值。为0则没有。
接口描述符是USB功能的基本单元,一个USB功能(如HID设备)在这里定义,多个接口变成多个功能,变成复合USB设备。
类描述符
类描述符跟在接口描述符后面,定义了一个功能类。其长度不定,由实际内容决定。
如USB鼠标、键盘等属于HID类设备,需要在这里添加一个HID类描述符。在之后可能还有类请求等相关数据传输。
HID类在之后的文章中讲解,这里只用知道需要这样一个类描述符决定功能类即可。
端点描述符
端点描述符结构如下,一共有7字节:
| 偏移量/字节 | 域 | 大小/字节 | 说明 |
|---|---|---|---|
| 0 | bLength | 1 | 该描述符的长度(7 字节) |
| 1 | bDescriptorType | 1 | 描述符的类型(端点描述符为 0x05) |
| 2 | bEndpointAddress | 1 | 该端点的地址 |
| 3 | bmAttributes | 1 | 该端点的属性 |
| 4 | wMaxPacketSize | 2 | 该端点支持的最大包长度 |
| 6 | bInterval | 1 | 端点的查询时间 |
bLength——端点描述符为7字节bDescriptorType——0x05bEndpointAddress——端点地址。D7为方向:1输入、0输出;D3-0为端点号。其余保留为0.bmAttributes——端点属性。D1-D0为传输类型:0控制传输、1等时、2批量、3中断。等时传输时,D3-2为同步类型:0无同步、1异步、2适配、3同步;D5-4为用途:0数据、1反馈、2暗反馈数据。非等时时,D7-D2保留为0.wMaxPackeSize——端点最大包长bInterval——对中断端点,表示端点查询时间;对其他,看协议描述。
SET_CONFIGURATION请求
在完成了所有配置集合的获取后,通用USB枚举进入了之后一步,设置配置,使能相应端点。
| bmRequestType | bRequest | wValue | wIndex | wLength | 数据过程 |
|---|---|---|---|---|---|
| 0x00 | SET_CONFIGURATION (0x09) | 配置值 | 0x0000 | 0x0000 | 没有 |
wValue的第一字节为配置的值,与配置描述符的一个对应,选中该配置。
设置配置后才能使能非0端点。
配置描述符实现逻辑
返回配置描述符时,有两种情况:
- 请求长度为9,返回9即可
- 请求长度大于等于集合,返回全部配置集合即可。
最后设置配置请求收到后,使能相应端点,然后返回一个0长度的状态速记包即可。只有到此时,才能使能非0的端点。
五,其他信息获取
上面的过程是USB的通用过程。而通常,在设置配置后,会有一些类请求来获取所需的信息。
比如,如果是HID类,在设置配置完成后,会发送一个Set_Idle的类请求。
然后,HID类,主机会请求类描述符,如报告描述符,报告描述符定义了使用的功能和中断端点返回数据的结构。
关于这部分内容,在后面的学习中进行讲解。
参考文章
圈圈教你学USB

浙公网安备 33010602011771号