1、设备描述符
设备描述符是USB设备中的一个标准化的数据结构,它包含了该设备最基本、最核心的信息。当USB设备首次连接到电脑(主机)时,主机会首先向设备请求“设备描述符”,通过读取这段信息,主机才能知道“我连接上了一个什么东西”,并据此决定如何与这个设备进行后续的通信。
可以将其想象成设备的“身份证”或者是“产品规格说明书首页”。
根据USB规范,设备描述符是一个长度为18字节的固定结构。包含以下字段:
| 字段偏移 | 字段名 | 长度(字节) | 描述与示例 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符本身的长度。对于设备描述符,这个值固定为 18 (0x12)。 |
| 1 | bDescriptorType | 1 | 描述符类型。固定为 1 (0x01),代表这是一个“设备描述符”。 |
| 2 | bcdUSB | 2 | USB 规范发布号。表示设备所遵循的 USB 版本。 • 0x0200 表示 USB 2.0• 0x0310 表示 USB 3.1• 0x0210 表示 USB 2.1 |
| 4 | bDeviceClass | 1 | 设备类代码。由 USB-IF 定义,标识设备的宏观类别。 • 0x00:由接口描述符定义类• 0x02:通信设备(如 Modem)• 0x09:Hub(集线器)• 0xEF:多功能设备• 0xFF:厂商自定义类 |
| 5 | bDeviceSubClass | 1 | 设备子类代码。对设备类进行进一步细分。 |
| 6 | bDeviceProtocol | 1 | 设备协议代码。指定设备使用的协议。 |
| 7 | bMaxPacketSize0 | 1 | 端点0的最大数据包大小。端点0是每个USB设备都必须有的控制端点。 • 全速设备:64 字节 • 高速设备:64 字节 • 低速设备:8 字节 |
| 8 | idVendor | 2 | 厂商ID。由 USB-IF 分配给设备制造商的唯一标识符。 例如, 0x05AC 代表苹果公司,0x04F2 代表群光电子。 |
| 10 | idProduct | 2 | 产品ID。由制造商自行分配,用于区分其不同产品。 |
| 12 | bcdDevice | 2 | 设备发布号。表示设备的版本号,由制造商定义。 |
| 14 | iManufacturer | 1 | 制造商字符串描述符的索引。指向描述制造商名称的字符串。 |
| 15 | iProduct | 1 | 产品字符串描述符的索引。指向描述产品名称的字符串。 |
| 16 | iSerialNumber | 1 | 序列号字符串描述符的索引。指向设备的唯一序列号。 |
| 17 | bNumConfigurations | 1 | 设备支持的配置数量。至少为1。 |
示例:一个普通USB 2.0U盘
//设备描述符
CTL 80 06 00 01 00 00 12 00
IN 12 01 10 01 00 00 00 08 2C 1A 24 21 10 01 01 02 00 01
原始数据:12 01 00 02 00 00 00 40 5E 04 36 05 01 02 00 01 01
| 偏移 | 字段 | 值(16进制) | 值(10进制) | 解释 |
|---|---|---|---|---|
| 0 | bLength | 12 | 18 | 描述符长度:18字节,正确。 |
| 1 | bDescriptorType | 01 | 1 | 描述符类型:1,代表是设备描述符。 |
| 2-3 | bcdUSB | 00 02 | 0x0200 | USB版本:USB 2.0。 |
| 4 | bDeviceClass | 00 | 0 | 设备类:0,表示类别在接口描述符中定义。 |
| 5 | bDeviceSubClass | 00 | 0 | 设备子类:0。 |
| 6 | bDeviceProtocol | 00 | 0 | 设备协议:0。 |
| 7 | bMaxPacketSize0 | 40 | 64 | 端点0最大包大小:64字节,这是USB 2.0高速设备的典型值。 |
| 8-9 | idVendor | 5E 04 | 0x045E | 厂商ID:0x045E 是 Microsoft Corp.。 |
| 10-11 | idProduct | 36 05 | 0x0536 | 产品ID:这是微软分配给某个具体产品(如Xbox控制器、鼠标等)的ID。 |
| 12-13 | bcdDevice | 01 02 | 0x0201 | 设备版本号:2.01。 |
| 14 | iManufacturer | 01 | 1 | 制造商字符串索引:指向字符串描述符1,通常是"Microsoft"。 |
| 15 | iProduct | 02 | 2 | 产品字符串索引:指向字符串描述符2,可能是"Xbox 360 Controller"等。 |
| 16 | iSerialNumber | 00 | 0 | 序列号索引:0,表示该设备没有提供序列号。 |
| 17 | bNumConfigurations | 01 | 1 | 配置数量:该设备只有1种配置。 |
2、配置描述符
配置描述符是USB设备中用于描述其一种完整工作模式的数据结构。一个USB设备可以有一种或多种配置,但同一时间只能启用一种配置。
为什么需要配置?一个复杂的设备可能具有多种功能。例如,一个带麦克风的网络摄像头,可能有以下几种配置:
(1)配置1(高功耗模式):同时启用高清视频和高质量音频
(2)配置2(低功耗模式):仅启用低分辨率视频和单声道音频。
主机可以根据当前的电量清空或用户需求,选择激活其中一种配置。
配置的组成:配置描述符本身指是一个“头”,它后面会紧跟这零个或多个接口描述符,以及每个接口描述符后面的零个或多个端点描述符。这些描述符共同定义了一种完整的设备工作方式。
根据USB规范,配置描述符是一个长度为9字节的固定结构
| 字段偏移 | 字段名 | 长度(字节) | 描述 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符本身的长度。对于配置描述符,这个值固定为 9 (0x09)。 |
| 1 | bDescriptorType | 1 | 描述符类型。固定为 2 (0x02),代表这是一个“配置描述符”。 |
| 2-3 | wTotalLength | 2 | 配置信息总长度。这是本配置描述符及其下属的所有接口描述符、端点描述符、设备类特定描述符和供应商特定描述符的字节数之和。主机依靠这个值来一次性读取整个配置的所有信息。 |
| 4 | bNumInterfaces | 1 | 此配置支持的接口数量。这是该配置下最核心的参数,指明了这个配置包含几个“功能集”。 |
| 5 | bConfigurationValue | 1 | 用作 Set Configuration 命令参数的配置值。主机通过这个值来选择激活此配置。 |
| 6 | iConfiguration | 1 | 描述此配置的字符串描述符的索引。如果为0,则表示没有字符串描述。 |
| 7 | bmAttributes | 1 | 配置特性。这是一个按位编码的字段: • Bit 7: 保留(必须设为1) • Bit 6: 自供电(1-自供电,0-总线供电) • Bit 5: 远程唤醒(1-支持,0-不支持) • Bit 4-0: 保留(设为0) |
| 8 | bMaxPower | 1 | 最大功耗。单位是 2mA。例如,0x32 (50) 表示 50 * 2mA = 100mA。对于高功耗设备,这个值可能达到 0xFA (250),即 500mA。 |
//获取配置描述符
CTL 80 06 00 02 00 00 09 00
IN 09 02 22 00 01 01 00 A0 31
示例:USB网络摄像头
原始数据:09 02 49 00 02 01 00 A0 32
| 偏移 | 字段 | 值(16进制) | 值(10进制) | 解释 |
|---|---|---|---|---|
| 0 | bLength | 09 | 9 | 描述符长度:9字节,正确。 |
| 1 | bDescriptorType | 02 | 2 | 描述符类型:2,代表是配置描述符。 |
| 2-3 | wTotalLength | 49 00 | 0x0049 | 配置信息总长度:73字节。这意味着这个配置(包括其所有接口和端点)总共需要73字节来描述。 |
| 4 | bNumInterfaces | 02 | 2 | 接口数量:2。这告诉我们,这个摄像头配置包含两个独立的功能集(接口)。 |
| 5 | bConfigurationValue | 01 | 1 | 配置值:1。主机发送 Set Configuration(1) 命令即可激活此配置。 |
| 6 | iConfiguration | 00 | 0 | 配置字符串索引:0,表示没有描述此配置的字符串。 |
| 7 | bmAttributes | A0 | 0xA0 | 配置特性: • Bit 7: 1 (保留位,必须为1) • Bit 6: 1 → 设备为自供电 • Bit 5: 0 → 不支持远程唤醒 • Bit 4-0: 0 0000 |
| 8 | bMaxPower | 32 | 50 | 最大功耗:50 * 2mA = 100mA。 |
3、接口描述符
接口描述符是USB设备种用于描述一个独立功能或功能集的数据结构。一个配置下可以包含一个或多个接口,每个接口代表一个逻辑上的设备。
为什么需要接口?这是为了实现复合设备。一个物理的USB设备,在操作系统看来可以是多个逻辑设备的集合。
经典例子:USB音响
配置:全功能模式
接口0:音频控制接口-用于调节音量、高低音等
接口1:音频流接口-专门用于传输时间的音频数据流
接口2:HID接口-用于控制音箱上的物理按钮(如静音键)
操作系统会为每个接口单独加载一个驱动程序。在上面的例子中,系统可能会为接口0和1加载通用的USB音频驱动,为接口2加载标准的HID驱动。
接口与端点的关系:接口是功能的抽象,而实现这个功能所需要的具体数据通道则由端点来描述。一个接口必须包含至少一个端点(控制端点0是默认存在的,不算在内)。
根据USB规范,接口描述符是一个长度为9字节的固定结构。
| 字段偏移 | 字段名 | 长度(字节) | 描述 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符本身的长度。固定为 9 (0x09)。 |
| 1 | bDescriptorType | 1 | 描述符类型。固定为 4 (0x04),代表这是一个“接口描述符”。 |
| 2 | bInterfaceNumber | 1 | 接口编号。用于区分同一配置下的不同接口。从0开始顺序编号。这是Set Interface命令用的参数。 |
| 3 | bAlternateSetting | 1 | 备用设置编号。允许一个接口在不同的带宽、功耗或协议下工作。通常默认为0。如果存在备用设置,主机可以通过Set Interface命令在不同设置间切换。 |
| 4 | bNumEndpoints | 1 | 此接口使用的端点数量(不包括默认的控制端点0)。如果这个值为0,表示此接口只使用端点0进行通信。 |
| 5 | bInterfaceClass | 1 | 接口类代码。这是最重要的字段之一,直接告诉主机应该加载什么驱动。 |
| 6 | bInterfaceSubClass | 1 | 接口子类代码。对接口类进行进一步细分。 |
| 7 | bInterfaceProtocol | 1 | 接口协议代码。指定接口所使用的协议。 |
| 8 | iInterface | 1 | 描述此接口的字符串描述符的索引。如果为0,则表示没有字符串描述。 |
关键字段详解:bInterfaceClass
| 类代码 (16进制) | 描述 | 常见设备 |
|---|---|---|
0x01 | Audio | 音箱、麦克风、耳机 |
0x03 | HID | 键盘、鼠标、游戏手柄 |
0x08 | Mass Storage | U盘、移动硬盘 |
0x0E | Video | 摄像头 |
0x0B | Chip/Smart Card | 智能卡读卡器 |
0xE0 | Wireless Controller | 蓝牙适配器、Wi-Fi适配器 |
0xFF | Vendor Specific |
厂商自定义设备,需要专用驱动 |
实际示例:
USB网络摄像头,查看它的两个接口:
接口0:HID接口(用于控制物理按钮)
原始数据(16进制):09 04 00 00 01 03 01 02 00
| 偏移 | 字段 | 值 | 解释 |
|---|---|---|---|
| 0 | bLength | 09 | 描述符长度:9字节。 |
| 1 | bDescriptorType | 04 | 描述符类型:接口描述符。 |
| 2 | bInterfaceNumber | 00 | 接口编号:0(第一个接口)。 |
| 3 | bAlternateSetting | 00 | 备用设置:0(默认设置)。 |
| 4 | bNumEndpoints | 01 | 端点数量:1(此接口有1个非0端点)。 |
| 5 | bInterfaceClass | 03 | 接口类:0x03,代表这是HID设备。 |
| 6 | bInterfaceSubClass | 01 | 接口子类:0x01,代表支持BIOS引导协议。 |
| 7 | bInterfaceProtocol | 02 | 接口协议:0x02,代表是鼠标设备。 |
| 8 | iInterface | 00 | 接口字符串:0,无描述。 |
解读:这个接口告诉主机,它是一个HID设备,很可能用于处理摄像头上的物理按钮(如拍照键),并将按键事件作为鼠标类事件上报。它拥有1个额外的端点(通常是一个中断输入端点)来传输这些事件数据。
4、HID描述符
HID描述符是一个类特定描述符,它的主要作用是作为HID设备通信协议的“头”或“索引”,用于向主机指明后续至关重要的报告描述符的位置和大小。
定位:它紧随在接口描述符之后
核心功能:他回答了这样一个问题:“要理解这个HID设备,你需要去读取的报告描述符在哪里?有多大?”
身份:它是HID设备的“身份证”,表明了其遵循的HID规范版本
| 字节偏移 | 字段名 | 长度(字节) | 值(示例) | 含义与说明 |
|---|---|---|---|---|
| 0 | bLength | 1 | 0x09 | 描述符长度。固定为9字节。这是HID描述符的标准大小。 |
| 1 | bDescriptorType | 1 | 0x21 | 描述符类型。固定为0x21。这个魔法数字明确标识了这是一个“HID描述符”。 |
| 2-3 | bcdHID | 2 | 0x0100 | HID规范版本。以BCD码格式表示设备遵循的HID规范版本。例如: • 0x0100: HID 1.00• 0x0111: HID 1.11 (非常常见) |
| 4 | bCountryCode | 1 | 0x00 | 国家/地区代码。表示硬件是否针对特定地区进行了本地化。 • 0x00: 不支持本地化(最常见)• 0x21: 法语(加拿大)键盘布局等。 |
| 5 | bNumDescriptors | 1 | 0x01 | 附属类描述符数量。指示在此HID描述符之后,还关联了多少个其他类描述符。至少为1(必须有一个报告描述符)。 |
| 6 | bDescriptorType | 1 | 0x22 | 附属描述符类型。指定第一个附属描述符的类型。 • 0x22: 报告描述符(几乎总是这个)• 0x23: 物理描述符(极其罕见) |
| 7-8 | wDescriptorLength | 2 | 0x0021 | 附属描述符长度。指定第一个附属描述符(通常是报告描述符)的总长度,以字节为单位。例如 0x0021 表示报告描述符有 33 字节。 |
5、端点描述符
端点描述符描述了USB设备中除默认控制端点(端点0)之外的一个数据通道。每个端点都是一个单向的通信端口,要么用于设备向主机发送数据(IN),要么用于主机向设备发送数据(OUT)。
端点是数据的出入口:可以将USB设备想象成一个房子,接口是房子里的不同房间(客厅、厨房),而端点就是每个房间的门(IN门是进,OUT是出)。数据必须通过门(端点)才能进出房间(接口)。
端点0:每个USB设备都必须有一个控制端点0.它是一个双向端点,用于所有基本的USB命令和控制传输,例如读取描述符。端点0没有端点描述符,它的存在是隐式的。
端点的地址:每个端点由一个端点号和方向唯一标识
端点号:4位,范围1-15
方向:IN(设备到主机)或 OUT(主机到设备)
根据USB规范,端点描述符是一个长度为7字节(USB 2.0)或(USB 3.0+)的固定结构
| 字段偏移 | 字段名 | 长度(字节) | 描述 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符本身的长度。对于USB 2.0端点描述符,这个值固定为 7 (0x07)。 |
| 1 | bDescriptorType | 1 | 描述符类型。固定为 5 (0x05),代表这是一个“端点描述符”。 |
| 2 | bEndpointAddress | 1 | 端点地址。这是一个按位编码的字段: • Bit 7: 方向 (1=IN, 0=OUT) • Bit 6-4: 保留(设为0) • Bit 3-0: 端点号 (1-15) |
| 3 | bmAttributes | 1 | 端点的属性。主要定义传输类型。 • Bit 1-0: 传输类型 - 00: 控制传输- 01: 等时传输- 10: 批量传输- 11: 中断传输• Bit 7-2: 其他属性,取决于传输类型(尤其是等时传输)。 |
| 4-5 | wMaxPacketSize | 2 | 端点支持的最大数据包大小。这是单次传输能承载的最大字节数。对于高速设备,这个字段有更复杂的编码。 |
| 6 | bInterval | 1 | 轮询间隔。主机检查该端点是否有数据的时间间隔。单位是帧/微帧(1ms/125μs)。 • 对中断/等时端点:值越小,频率越高(如 0x01 表示每1ms轮询一次)。• 对批量/控制端点:忽略。 |
示例:
USB键盘(中断IN端点)
原始数据:07 05 81 03 08 00 0A
| 偏移 | 字段 | 值 | 解释 |
|---|---|---|---|
| 0 | bLength | 07 | 描述符长度:7字节。 |
| 1 | bDescriptorType | 05 | 描述符类型:端点描述符。 |
| 2 | bEndpointAddress | 81 | 端点地址:1000 0001 → 端点 1 IN。 |
| 3 | bmAttributes | 03 | 属性:0000 0011 → 传输类型 = 11 (中断传输)。 |
| 4-5 | wMaxPacketSize | 08 00 | 最大包大小:0x0008 = 8 字节。一个键盘报告通常就是8字节。 |
| 6 | bInterval | 0A | 轮询间隔:0x0A (10) → 10ms。主机每10ms检查一次键盘是否有新数据。 |
6、字符串描述符
字符串描述是可选的可读文本信息,用于向用户和开发者提供设备、制造商、产品型号等的友好名称。它不是设备运行所必需的。
字符串描述符的结构相对灵活,其长度取决于字符串的字符数量。
| 字段偏移 | 字段名 | 长度(字节) | 描述 |
|---|---|---|---|
| 0 | bLength | 1 | 描述符本身的长度。值为 2 + (2 * N),其中 N 是字符串的字符数。 |
| 1 | bDescriptorType | 1 | 描述符类型。固定为 3 (0x03),代表这是一个“字符串描述符”。 |
| 2, 4, ... | bString | bLength - 2 | 字符串本身。以 UNICODE (UTF-16LE) 格式存储的字符串。 |
特殊字符串索引0:
索引0保留给一个特殊的字符串描述符,它包含语言标识符的数组,用于告知主机设备支持哪些语言的字符串。
示例:
示例:0x0a,0x03,'a',0x00,'b',0x00,'c',0x00,'d',0x00
0x0a:描述符长度 ,有10个字符
0x03:描述符类型为字符串描述符
‘a',0x00:UNICODE 字符’a‘
............
7、HID类报表描述符
简化的鼠标报告描述符例子:
这个描述符告诉主机:我将发送一个包含3个按钮(各占1位)和两个8位有符号整数(X位移和Y位移)的报告
//这是一个简化的逻辑表示,并非原始字节
USAGE_PAGE (Generic Desktop) 05 01 //这是一个桌面控制设备
USAGE (Mouse) 09 02 //具体来说,是个鼠标
COLLECTION (Application) A1 01 //开始一个应用集合
USAGE (Pointer) 09 01 //这是一个指针设备
COLLECTION (Physical) A1 00 //开始一个物理集合
USAGE_PAGE (Buttons) 05 09 //接下来描述按钮
USAGE_MINIMUM (1) 19 01
USAGE_MAXIMUM (3) 29 03 //我有三个按钮,编号1到3
LOGICAL_MINIMUM (0) 15 00
LOGICAL_MAXIMUM (1) 25 01 //按钮的值:0(表示松开) 1(表示按下)
REPORT COUNT (3) 95 03 //报告中有3个按钮字段
REPORT_SIZE (1) 75 01 //每个按钮字段占1位
INPUT (Data,Variable,Absolute) 81 02 //这是输入数据
REPORT_COUNT (1) 75 01 //接下来是1个字段.....
REPORT_SIZE (5) 95 05 //...占位5位(填充,使字节对齐)
INPUT (Constat) 81 03 //这是常量(填充为,主机忽略)
USAGE_PAGE (Generic Desktop) 05 01 //切换回通用桌面用法页
USAGE (X) 09 30 //X位移
USAGE (Y) 09 31 //Y位移
LOGICAL_MINIMUM (-127) 15 81
LOGICAL_MAXIMUM (127) 25 7F //位移范围是-127到127
REPORT_SIZE (8) 75 08 //每个位移占8位(1字节)
REPORT_COUNT (2) 95 02 //有2个这样的字段(X和Y)
INPUT(Data,Variable,Relative) 81 06 //这是输入数据,值是相对的
END_COLLECTION C0 //结束物理集合
COLLECTION (Physical) A1 00 //垂直滚轮
USAGE_PAGE (Generic Desktop Controls) 05 01
USAGE (Wheel) 09 38 //
REPORT_SIZE (8) 75 08 //每个位移占8位(1字节)
REPORT_COUNT (1) 95 02 //有1个这样的字段()
LOGICAL_MINIMUM (-127) 15 81
LOGICAL_MAXIMUM (127) 25 7F //位移范围是-127到127
INPUT(Data,Variable,Relative) 81 06 //这是输入数据,值是相对的
END_COLLECTION C0 //结束物理集合
END_COLLECTION C0 //结束应用集合
报告数据示例:
uint8_t report[4] = {
0x01; //字节0:左键按下(00000001)
0x05; //字节1:X = +5
0xfd; //字节2:Y = -3 (0xfd = -1 的补码)
0xff //字节3:滚轮 = -1 (0xff = -1 的补码)
}
1395

被折叠的 条评论
为什么被折叠?



