USB 设备描述符USB Device Descriptor)是 USB 设备在枚举早期返回给主机的固定结构,用来说明设备的基础身份、协议版本、类信息、VID:PID 和配置数量。

它是主机认识一个 USB 设备的第一层元数据。后续的配置描述符、接口描述符、端点描述符和类特定描述符都建立在这一步之后。

核心问题

USB 主机不能靠猜测理解新插入设备。它必须先读一段标准格式的数据,知道:

  • 设备遵循哪个 USB 版本。
  • 设备由哪个厂商和产品编号标识。
  • 设备类是在设备级声明,还是要到接口级再判断。
  • 有多少个可选配置。
  • 是否提供厂商名、产品名、序列号等字符串索引。

USB 设备描述符解决的就是“主机用统一格式获取设备基础身份”的问题。

核心对象

字段含义工程关注点
bLength该描述符长度设备描述符通常是 18 字节
bDescriptorType描述符类型device descriptor 的类型值为 1
bcdUSBUSB 规范版本,BCD 编码例如 USB 2.0 常见为 0x0200
bDeviceClass设备级 class0x00 常表示 class 在 interface 级声明
bDeviceSubClass设备级 subclass与 class 一起解释
bDeviceProtocol设备级 protocol与 class/subclass 一起决定通用驱动匹配
bMaxPacketSize0控制端点 0 最大包长枚举控制传输的基础参数
idVendorVendor ID[[VID-PID(USB 设备标识)
idProductProduct ID[[VID-PID(USB 设备标识)
bcdDevice设备版本号,BCD 编码常表示硬件或固件版本
iManufacturer厂商字符串索引索引为 0 表示没有字符串
iProduct产品字符串索引展示名,不适合作唯一识别
iSerialNumber序列号字符串索引可用于区分同型号多设备,但设备可能不提供
bNumConfigurations配置数量主机之后会读取 configuration descriptor

BCD 是 Binary-Coded Decimal,不是普通二进制整数。例如 0x0200 按 USB 版本语义读作 2.00,而不是十进制 512。

核心机制

1. 主机通过控制传输读取描述符

设备刚连接时,主机会通过默认控制端点 0 发起标准请求:

GET_DESCRIPTOR(Device)

设备返回固定格式的 device descriptor。这个过程不依赖厂商驱动,因为它是 USB 标准控制请求的一部分。

2. 描述符提供下一步枚举线索

主机读到设备描述符后,典型决策路径是:

read device descriptor
  -> learn VID/PID and basic class fields
  -> read configuration descriptor
  -> inspect interfaces and endpoints
  -> bind class driver or vendor-specific driver

如果 bDeviceClass0x00,主机通常要继续看每个 interface descriptor 的 class/subclass/protocol。很多复合设备都采用这种方式,因为一个设备可以同时提供多个功能。

3. 字符串索引不是字符串本身

iManufactureriProductiSerialNumber 不是直接存放文本,而是字符串描述符的索引。主机需要再发起 GET_DESCRIPTOR(String, index) 才能读取具体文字。

这就是为什么一些工具能显示厂商名和产品名,而另一些场景只能看到 VID:PID:字符串描述符可能不存在、读取失败,或语言 ID 未匹配。

工程用途

  • lsusb -v 检查设备返回的 idVendoridProductbcdDevice、class 和字符串索引。
  • 在固件开发中确认 USB device stack 生成的 descriptor 是否与预期一致。
  • 排查设备不被识别的问题:描述符长度错误、端点 0 包长错误、class 字段错误都可能导致枚举失败。
  • 区分设备级 class 与接口级 class,避免把复合设备误判成单一功能设备。
  • udev 规则 或驱动匹配表时,确认匹配字段来自哪一层描述符。

边界与常见坑

  • 设备描述符不是完整能力清单:端点、接口、带宽、视频格式、HID report 等能力在后续描述符中。
  • 产品字符串不是稳定 IDiProduct 只是显示文本,可能随固件、语言或厂商习惯变化。
  • 序列号可能为空:很多低成本设备不提供 iSerialNumber,同型号多设备会更难区分。
  • class 字段可能在接口级:看到 bDeviceClass = 0x00 不代表设备没有类别,而是要继续看 interface descriptor。
  • 描述符可被伪造:安全策略不能只相信设备自报的 VID/PID 或产品名。

相关术语