4. 标准设备请求详解:GetDescriptor、SetAddress、SetConfiguration 等标准请求的 bmRequestType、bRequest、wValue、wIndex、wLength 格式

USB 枚举过程的核心是主机通过 默认控制管道(Endpoint 0) 向设备发送一系列标准设备请求。每个请求都是一个固定格式的 8 字节 Setup 包,随后可能跟随数据阶段(Data Stage)和状态阶段(Status Stage)。理解这 8 字节的编码规则,是解读枚举日志、定位枚举失败故障的基石。

调试要点: 在 USB 分析仪或主机端日志中,标准请求的 8 字节 Setup 包通常以十六进制序列呈现。掌握每个字段的位含义,能让你直接从原始数据中还原出主机意图,快速判断是请求格式错误还是设备响应异常。

4.1 标准请求的 8 字节 Setup 包结构

所有 USB 标准设备请求均通过控制传输的 Setup 阶段发送,其数据结构由 USB 2.0 规范第 9.3 节定义。8 字节的布局如下:

偏移量(字节) 字段名称 长度(位) 说明
0 bmRequestType 8 请求特性:方向、类型、接收者
1 bRequest 8 请求编号(如 GET_DESCRIPTOR = 0x06)
2-3 wValue 16 请求参数(高字节/低字节,含义因请求而异)
4-5 wIndex 16 索引或偏移(如接口号、端点号、语言 ID)
6-7 wLength 16 数据阶段期望传输的字节数
关键原则: 主机发送 Setup 包后,设备必须在 5ms(高速)或 50ms(全速/低速)内完成握手。如果设备无法解析 bmRequestType 或 bRequest,将返回 STALL 握手,导致枚举失败。

4.2 bmRequestType:请求的方向、类型与接收者

bmRequestType 是 8 位位图,决定了请求的传输方向、请求类型以及目标接收者。其位定义如下:

位范围 字段 值 含义
D7 方向 0 = 主机到设备(Host-to-Device)
1 = 设备到主机(Device-to-Host)
控制传输数据阶段的方向
D6:D5 类型 00 = 标准(Standard)
01 = 类(Class)
10 = 厂商(Vendor)
11 = 保留
请求所属的规范层级
D4:D0 接收者 00000 = 设备(Device)
00001 = 接口(Interface)
00010 = 端点(Endpoint)
00011 = 其他(Other)
其他 = 保留
请求的目标对象

常见枚举请求的 bmRequestType 值:

  • 0x80:设备到主机,标准类型,接收者为设备(如 GetDescriptor)。
  • 0x00:主机到设备,标准类型,接收者为设备(如 SetAddress、SetConfiguration)。
  • 0x01:主机到设备,标准类型,接收者为接口(如 SetInterface)。
  • 0x02:主机到设备,标准类型,接收者为端点(如 ClearFeature)。
常见故障点: 设备固件在解析 bmRequestType 时,如果错误地检查了保留位(如 D6:D5 = 11),或者将方向位与数据阶段实际方向混淆,会导致设备 STALL 请求。调试时,务必确认设备固件对 bmRequestType 的位掩码处理是否正确。
bmRequestType 位结构(8 位) D7 D6:D5 D4:D0 方向 类型 接收者 0=主机→设备 1=设备→主机 00=标准 01=类 10=厂商 00000=设备 00001=接口 00010=端点 示例:0x80 D7=1, D6:D5=00, D4:D0=00000 → 设备到主机,标准,设备

4.3 bRequest:标准请求码

bRequest 字段标识具体的请求操作。USB 2.0 规范定义了 10 个标准请求码,枚举过程中最常用的有 4 个:

bRequest 值 请求名称 枚举阶段 方向(数据阶段)
0x00 GetStatus 可选(状态查询) 设备 → 主机
0x01 ClearFeature 可选(清除特性) 主机 → 设备
0x03 SetFeature 可选(设置特性) 主机 → 设备
0x05 SetAddress 地址分配 主机 → 设备(无数据阶段)
0x06 GetDescriptor 描述符获取 设备 → 主机
0x07 SetDescriptor 极少使用 主机 → 设备
0x08 GetConfiguration 配置查询 设备 → 主机
0x09 SetConfiguration 配置激活 主机 → 设备(无数据阶段)
0x0A GetInterface 可选 设备 → 主机
0x0B SetInterface 可选(备用设置) 主机 → 设备
调试技巧: 在枚举日志中,如果看到 bRequest = 0x06 但设备返回 STALL,首先检查 wValue 中的描述符类型是否被设备支持。例如,某些设备可能不支持字符串描述符(类型 0x03),或者只支持有限的语言 ID。

4.4 wValue、wIndex、wLength:请求参数详解

这三个字段的含义随 bRequest 变化。下面以枚举中最关键的三个请求为例,说明其编码规则。

4.4.1 GetDescriptor(bRequest = 0x06)

主机通过此请求获取设备的描述符信息。wValue 的高字节(wValueH)指定描述符类型,低字节(wValueL)指定描述符索引。wIndex 通常用于指定字符串描述符的语言 ID,其他描述符时设为 0。wLength 指定主机期望接收的字节数。

字段 值示例 含义
bmRequestType 0x80 设备到主机,标准,设备
bRequest 0x06 GetDescriptor
wValueH 0x01 描述符类型:设备描述符(DEVICE)
wValueL 0x00 描述符索引:第 0 个(通常只有一个)
wIndex 0x0000 语言 ID(仅字符串描述符时有效)
wLength 0x0012 期望 18 字节(设备描述符标准长度)

常见描述符类型值(wValueH):

  • 0x01:设备描述符(Device Descriptor)
  • 0x02:配置描述符(Configuration Descriptor)
  • 0x03:字符串描述符(String Descriptor)
  • 0x04:接口描述符(Interface Descriptor)
  • 0x05:端点描述符(Endpoint Descriptor)
  • 0x06:设备限定描述符(Device Qualifier Descriptor,USB 2.0 高速)
  • 0x07:其他速度配置描述符(Other Speed Configuration)
  • 0x08:接口功率描述符(Interface Power Descriptor)
故障案例: 主机发送 GetDescriptor(配置描述符, wLength=9) 请求前 9 字节,设备必须正确返回配置描述符的前 9 字节(包括总长度字段)。如果设备返回的数据中总长度字段错误(例如小于实际描述符长度),主机可能无法完成后续的完整描述符读取,导致枚举失败。

4.4.2 SetAddress(bRequest = 0x05)

此请求为主机分配一个唯一的地址给设备。SetAddress 是唯一一个在数据阶段没有数据包的控制请求(即只有 Setup 和 Status 阶段)。

字段 值示例 含义
bmRequestType 0x00 主机到设备,标准,设备
bRequest 0x05 SetAddress
wValue 0x0003 新地址 = 3(有效范围 1-127)
wIndex 0x0000 未使用,必须为 0
wLength 0x0000 无数据阶段
时序关键点: 设备在收到 SetAddress 请求并完成状态阶段后,必须在 2ms 内切换到新地址。主机在发送 SetAddress 后,会等待至少 2ms 再使用新地址发送后续请求。如果设备切换地址延迟过长,主机将无法与设备通信,导致枚举超时。

4.4.3 SetConfiguration(bRequest = 0x09)

主机通过此请求激活设备的某个配置。wValue 的低字节指定配置值(bConfigurationValue),该值必须与配置描述符中的 bConfigurationValue 字段匹配。

字段 值示例 含义
bmRequestType 0x00 主机到设备,标准,设备
bRequest 0x09 SetConfiguration
wValue 0x0001 选择配置值 1
wIndex 0x0000 未使用,必须为 0
wLength 0x0000 无数据阶段
常见故障: 如果设备固件在 SetConfiguration 后未能正确初始化端点(例如端点 FIFO 未清空、端点使能位未设置),主机后续的批量或中断传输将失败。调试时,应在 SetConfiguration 后立即检查端点状态寄存器和端点描述符的匹配情况。
枚举过程中标准请求的典型序列 主机(Host) 设备(Device) 1. GetDescriptor(设备, wLength=18) 返回 18 字节设备描述符 2. SetAddress(地址=3) 切换地址 → 3 3. GetDescriptor(配置, wLength=9) 返回前 9 字节 4. GetDescriptor(配置, wLength=总长度) 返回完整配置描述符 5. SetConfiguration(配置值=1) 激活配置,初始化端点

4.5 标准请求的固件处理要点

在嵌入式固件中实现标准请求处理时,以下要点直接影响枚举成功率:

  1. Setup 包解析顺序: 先检查 bmRequestType 的 D6:D5 位是否为 00(标准类型),再检查 bRequest 值。非标准请求应返回 STALL。
  2. wLength 校验: 设备返回的数据长度不能超过 wLength。如果描述符实际长度大于 wLength,设备应截断返回;如果小于 wLength,设备应返回实际长度(主机通过数据包结束判断)。
  3. SetAddress 的特殊处理: 设备必须在状态阶段完成后立即启用新地址,但不应在状态阶段完成前改变地址。建议在状态阶段完成中断中设置新地址。
  4. SetConfiguration 后的端点初始化: 设备必须在收到 SetConfiguration 后,根据所选配置描述符中的接口和端点描述符,正确初始化所有非默认端点(包括端点类型、最大包大小、FIFO 分配等)。
  5. 描述符缓存对齐: 描述符数据通常存储在 Flash 或 RAM 中,返回给 USB 外设时需注意字节对齐(通常要求 4 字节对齐),否则可能导致 DMA 传输错误。
调试日志示例: 在固件中打印每个标准请求的 8 字节 Setup 包,格式如 [SETUP] 80 06 00 01 00 00 12 00,可以快速与 USB 分析仪日志对比,定位是主机发送错误还是设备解析错误。

4.6 标准请求故障排查速查表

故障现象 可能原因 检查点
GetDescriptor 返回 STALL 描述符类型不支持或描述符索引越界 检查 wValueH 描述符类型、wValueL 索引
SetAddress 后主机超时 设备地址切换延迟超过 2ms 检查固件中地址切换的时序逻辑
SetConfiguration 后端点无响应 端点未正确初始化或端点描述符不匹配 检查端点类型、最大包大小、FIFO 地址
设备返回数据长度与 wLength 不匹配 描述符总长度字段错误或固件截断逻辑有误 验证描述符中的 bLength 和 wTotalLength
主机重复发送同一请求 设备未正确握手(ACK/STALL) 检查 USB 外设中断状态和端点就绪位
总结: 标准设备请求是 USB 枚举的通信语言。bmRequestType 定义了对话的“方向、话题和对象”,bRequest 指定了“动作”,wValue/wIndex/wLength 提供了“参数”。掌握这 8 字节的编码规则,就等于拿到了解读枚举日志的钥匙。在下一章中,我们将深入分析描述符的结构与常见错误,进一步巩固枚举故障排查能力。