3. USB描述符体系

USB描述符是USB设备向主机报告自身能力与配置的核心数据结构。对于UAC(USB Audio Class)设备而言,描述符体系不仅包含标准的USB描述符,还包含音频特定的类描述符,它们共同构成了主机枚举和驱动加载的基础。理解这一层级结构是开发USB声卡固件的首要任务。

3.1 标准USB描述符层级结构

标准USB描述符采用严格的树形层级结构,从设备级到功能级逐层细化。每个USB设备只有一个设备描述符,但可以包含多个配置描述符,每个配置下又包含多个接口和端点描述符。

关键层级关系: 设备描述符 → 配置描述符 → 接口描述符 → 端点描述符。主机通过逐层解析这些描述符来构建设备的逻辑视图。
标准USB描述符层级结构 设备描述符 配置描述符 接口描述符 0 端点描述符 (OUT) 接口描述符 1 端点描述符 (IN) 说明: 每个配置描述符后紧跟接口描述符,接口描述符后紧跟端点描述符。 UAC设备通常包含至少两个接口:一个用于音频控制(AC),一个用于音频数据流(AS)。

3.2 各标准描述符的结构与字段

每个标准描述符都有固定的字节布局和字段定义。以下是UAC开发中最关键的几个描述符结构。

3.2.1 设备描述符 (Device Descriptor)

设备描述符是主机读取的第一个描述符,长度为18字节。对于UAC设备,关键字段是 bDeviceClass、bDeviceSubClass 和 bDeviceProtocol,它们通常设置为0xFF(厂商自定义)或0x00(在接口级别定义),因为UAC功能在接口描述符中声明。

struct usb_device_descriptor {
    uint8_t  bLength;            // 描述符长度,固定为18
    uint8_t  bDescriptorType;    // 描述符类型,设备描述符为0x01
    uint16_t bcdUSB;             // USB规范版本号,如0x0200表示USB 2.0
    uint8_t  bDeviceClass;       // 类代码,UAC设备通常设为0x00
    uint8_t  bDeviceSubClass;    // 子类代码
    uint8_t  bDeviceProtocol;    // 协议代码
    uint8_t  bMaxPacketSize0;    // 端点0最大包大小
    uint16_t idVendor;           // 厂商ID
    uint16_t idProduct;          // 产品ID
    uint16_t bcdDevice;          // 设备版本号
    uint8_t  iManufacturer;      // 厂商字符串描述符索引
    uint8_t  iProduct;           // 产品字符串描述符索引
    uint8_t  iSerialNumber;      // 序列号字符串描述符索引
    uint8_t  bNumConfigurations; // 支持的配置数量
} __attribute__((packed));

3.2.2 配置描述符 (Configuration Descriptor)

配置描述符长度为9字节,描述了设备的电源管理属性和接口总数。UAC设备通常只使用一个配置,但该配置下包含多个接口。

字段 偏移 大小 说明 UAC典型值
bLength 0 1 描述符长度 0x09
bDescriptorType 1 1 配置描述符类型 0x02
wTotalLength 2 2 整个配置的总字节数(含所有子描述符) 根据实际描述符链计算
bNumInterfaces 4 1 该配置下的接口数量 ≥2(AC接口 + AS接口)
bConfigurationValue 5 1 用于SetConfiguration()的值 0x01
iConfiguration 6 1 配置字符串描述符索引 0x00(无字符串)
bmAttributes 7 1 电源属性:D7=总线供电,D6=自供电,D5=远程唤醒 0x80(总线供电)
bMaxPower 8 1 最大电流,单位2mA 0x32(100mA)

3.2.3 接口描述符 (Interface Descriptor)

接口描述符长度为9字节,是UAC设备的核心描述符。UAC1.0设备通常包含两个接口:音频控制接口(AC Interface,bInterfaceClass=0x01)和音频流接口(AS Interface,bInterfaceClass=0x01)。

UAC接口识别: 音频控制接口的 bInterfaceSubClass 为0x01,音频流接口的 bInterfaceSubClass 为0x02。主机通过这两个子类值区分控制路径和数据路径。

3.2.4 端点描述符 (Endpoint Descriptor)

端点描述符长度为7字节(标准)或9字节(含附加属性)。对于UAC音频流接口,端点描述符必须使用9字节的 音频数据端点描述符,其中包含同步类型和刷新率等信息。

struct usb_endpoint_descriptor {
    uint8_t  bLength;            // 长度:标准7字节,音频9字节
    uint8_t  bDescriptorType;    // 端点描述符类型0x05
    uint8_t  bEndpointAddress;   // 端点地址:bit7=方向(1:IN,0:OUT),bit3-0=端点号
    uint8_t  bmAttributes;       // 传输类型:bit1-0=00控制,01等时,02批量,03中断
    uint16_t wMaxPacketSize;     // 最大包大小
    uint8_t  bInterval;          // 轮询间隔(帧/微帧)
    // 以下为音频特定扩展字段(仅UAC等时端点使用)
    uint8_t  bRefresh;           // 刷新率
    uint8_t  bSynchAddress;      // 同步端点地址
} __attribute__((packed));

3.3 音频特定类描述符 (Audio Class-Specific Descriptors)

UAC协议在标准描述符的基础上,定义了音频特定的类描述符。这些描述符位于标准接口描述符之后,用于描述音频功能的具体细节,如采样率、位深、声道数、音量控制范围等。

3.3.1 音频控制接口的类描述符

音频控制接口(AC Interface)的类描述符链通常包含以下结构:

  • AC接口头描述符 (Header Descriptor): 描述UAC版本号、总字节数、包含的音频流接口数量。
  • 输入端子描述符 (Input Terminal Descriptor): 描述音频输入源的类型(如麦克风、Line In)。
  • 输出端子描述符 (Output Terminal Descriptor): 描述音频输出目标的类型(如扬声器、耳机)。
  • 特征单元描述符 (Feature Unit Descriptor): 描述音量控制、静音控制等音频处理功能。
  • 选择单元描述符 (Selector Unit Descriptor): 描述输入源选择功能(可选)。
  • 处理单元描述符 (Processing Unit Descriptor): 描述音频处理效果(可选)。

3.3.2 音频流接口的类描述符

音频流接口(AS Interface)的类描述符链相对简单,主要包含:

  • AS接口头描述符 (General Descriptor): 描述音频流格式类型(如PCM、AC-3)。
  • 格式类型描述符 (Format Type Descriptor): 描述采样率、位深、声道数等具体格式参数。
UAC音频类描述符完整链 配置描述符 AC接口描述符 (bInterfaceSubClass=0x01) AC类描述符链 • AC接口头描述符 (Header) • 输入端子描述符 (IT) • 输出端子描述符 (OT) • 特征单元描述符 (FU) AS接口描述符 (bInterfaceSubClass=0x02) AS类描述符链 • AS接口头描述符 (General) • 格式类型描述符 (Format Type) 等时端点描述符 (9字节,含bRefresh/bSynchAddress) 描述符解析顺序: 1. 主机先读取配置描述符,获取wTotalLength确定总大小。 2. 然后遍历所有接口描述符,根据bInterfaceSubClass识别AC接口(0x01)和AS接口(0x02)。 3. 在每个接口描述符后,解析对应的类特定描述符链,提取音频控制参数和格式信息。

3.4 描述符间的依赖关系与解析顺序

主机在枚举USB声卡时,按照严格的顺序解析描述符。理解这一顺序对于固件中描述符的组织至关重要。

步骤 主机操作 读取的描述符 关键信息获取
1 获取设备描述符 设备描述符 (18字节) USB版本、厂商/产品ID、配置数量
2 选择配置 配置描述符 (9字节) 接口数量、总描述符长度、电源属性
3 读取完整配置 配置描述符 + 所有子描述符 根据wTotalLength读取完整描述符链
4 解析接口 接口描述符 识别AC接口(bSubClass=0x01)和AS接口(bSubClass=0x02)
5 解析类描述符 音频类特定描述符 端子类型、特征单元、格式类型、采样率等
6 解析端点 端点描述符 端点地址、传输类型、最大包大小、同步参数

3.5 描述符组织的最佳实践

常见陷阱: 描述符链中每个描述符的 bLength 字段必须与实际长度一致。如果某个描述符长度错误,主机将无法正确解析后续描述符,导致枚举失败。建议在固件中使用 sizeof() 宏自动计算长度,避免手动填写错误。

在嵌入式Linux USB声卡开发中,描述符的组织应遵循以下原则:

  • 紧凑排列: 所有描述符在内存中连续排列,没有空隙。配置描述符的 wTotalLength 必须精确等于所有描述符字节数之和。
  • 接口交替设置: UAC1.0要求音频流接口支持至少两个接口交替设置(Alternate Setting)。Alternate Setting 0 表示无数据流(零带宽),Alternate Setting 1 表示激活的音频流(带端点描述符)。
  • 字符串描述符可选: 字符串描述符用于提供人类可读的厂商、产品信息,但不是必须的。如果不需要,将对应的索引字段设为0x00。
  • 类描述符顺序固定: 音频类描述符必须按照UAC规范定义的顺序排列,例如AC接口中端子描述符必须在特征单元描述符之前。

3.6 描述符调试技巧

在开发过程中,可以使用以下方法验证描述符的正确性:

# 在Linux主机上使用lsusb查看描述符树
lsusb -v -d 1234:5678

# 使用usbmon捕获USB枚举过程
sudo modprobe usbmon
sudo cat /sys/kernel/debug/usb/usbmon/0u | grep "1234:5678"

# 在嵌入式设备上使用USB分析仪或打印描述符数据
# 内核日志中会显示描述符解析错误信息
dmesg | grep -i usb
快速验证: 在固件中实现一个调试函数,将整个描述符链以十六进制格式通过串口打印出来,然后与USB规范中的标准布局逐字节比对。这是最直接有效的调试方法。