4. UAC1.0控制端点与中断端点

在 UAC1.0 协议中,音频功能的控制与状态反馈分别由 控制端点 (Endpoint 0, 默认控制管道) 和 中断端点 (Interrupt Endpoint) 承载。控制端点负责主机下发或查询音频属性(如音量、静音、采样率),中断端点则用于设备主动向主机上报状态变化(如音量旋钮被转动、插拔检测)。理解这两类传输的请求格式、数据流向和时序约束,是实现稳定 USB 声卡驱动的核心。

4.1 控制传输请求:Set/Get 与 Cur/Min/Max/Res

UAC1.0 的控制请求通过标准的 USB 控制传输(Setup + Data + Status 阶段)完成。请求格式遵循 Audio Class-Specific Request 规范,其 Setup 包结构如下:

字段 偏移 (字节) 长度 (位) 说明
bmRequestType 0 8 方向 (Host→Dev=0x21, Dev→Host=0xA1) + 类型 (Class=001) + 接收者 (Interface=00001)
bRequest 1 8 SET_CUR=0x01, GET_CUR=0x81, GET_MIN=0x82, GET_MAX=0x83, GET_RES=0x84
wValue 2 16 高字节: 控制选择子 (CS), 低字节: 实体ID (Entity ID)
wIndex 4 16 高字节: 接口号 (Interface), 低字节: 终端/功能单元ID
wLength 6 16 数据阶段传输的字节数 (取决于控制参数类型)
关键概念:控制选择子 (CS)
CS 决定了具体操作哪个属性。例如:
- MUTE_CONTROL (CS=0x01): 静音开关,数据为 1 字节 (0=静音, 1=非静音)
- VOLUME_CONTROL (CS=0x02): 音量,数据为 2 字节 (有符号 16 位, 单位 dB/256)
- SAMPLING_FREQ_CONTROL (CS=0x01 for Clock Source): 采样率,数据为 3 字节 (24 位整数, 单位 Hz)

4.2 控制请求的典型流程:以音量调节为例

主机通过 SET_CUR 设置音量,通过 GET_CUR 读取当前音量。设备端驱动需在控制端点 0 的请求处理函数中解析 wValue 和 wIndex,定位到对应的 Feature Unit,然后执行硬件寄存器读写。

/* 伪代码:UAC1.0 音量 SET_CUR 处理 */
int uac1_feature_unit_set_cur(struct usb_device *udev,
                              u8 entity_id, u8 channel,
                              s16 volume) {
    struct usb_ctrlrequest req;
    req.bRequestType = 0x21;          // Host to Device, Class, Interface
    req.bRequest = 0x01;              // SET_CUR
    req.wValue = cpu_to_le16((0x02 << 8) | entity_id); // CS=VOLUME_CONTROL
    req.wIndex = cpu_to_le16((iface_num << 8) | channel);
    req.wLength = cpu_to_le16(2);     // 2 bytes volume data

    return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                           req.bRequest, req.bRequestType,
                           req.wValue, req.wIndex,
                           &volume, 2, 1000);
}
开发提示: 音量数据使用 有符号 16 位 表示,单位是 dB/256。例如 +0 dB 对应 0x0000,-6 dB 对应 0xFA00(-1536/256 = -6 dB)。注意大小端转换,USB 协议使用小端序。

4.3 中断端点:状态反馈的异步通道

UAC1.0 要求音频接口必须包含一个 中断端点 (Interrupt IN Endpoint),用于设备主动向主机报告音频功能的状态变化。典型场景包括:

  • 音量旋钮/编码器:用户旋转硬件旋钮时,设备通过中断端点发送新的音量值。
  • 静音按钮:硬件静音开关状态变化。
  • 自动增益控制 (AGC) 状态:AGC 使能/失活或增益值变化。
  • 时钟源锁定状态:外部时钟源失锁或重新锁定。

中断端点的传输数据格式为 UAC1.0 Status Interrupt Packet,长度固定为 6 字节:

字节偏移 字段 说明
0 bStatusType 0x01 = 中断由控制变化触发 (Cur变化)
1 bOriginator 产生中断的实体 ID (Feature Unit ID 或 Terminal ID)
2 bControlSelector 发生变化的控制选择子 (如 0x02=音量, 0x01=静音)
3 bAttribute 0x00 = 当前值 (Cur) 变化
4-5 wValue 变化后的新值 (与对应控制的数据类型一致)

4.4 中断端点的工作机制与驱动实现

设备端驱动需在初始化时注册中断端点的 URB,并提交一个 周期性中断传输。主机端通过轮询中断端点(通常每 1~8 帧,即 1~8 ms)获取状态更新。当设备无状态变化时,中断端点应返回 NAK;当有事件发生时,设备在中断端点上返回有效数据包。

/* 伪代码:中断端点 URB 提交与回调 */
static void uac1_interrupt_complete(struct urb *urb) {
    struct uac1_status_packet *pkt = urb->transfer_buffer;

    if (urb->status == 0 && pkt->bStatusType == 0x01) {
        // 解析状态变化
        u8 entity = pkt->bOriginator;
        u8 cs = pkt->bControlSelector;
        u16 new_val = le16_to_cpu(pkt->wValue);

        // 通知上层应用或更新内部状态
        audio_driver_notify_change(entity, cs, new_val);
    }

    // 重新提交 URB 以继续监听
    usb_submit_urb(urb, GFP_ATOMIC);
}

int uac1_init_interrupt_endpoint(struct usb_device *udev,
                                 struct usb_endpoint_descriptor *ep) {
    struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
    unsigned char *buf = kmalloc(6, GFP_KERNEL);

    usb_fill_int_urb(urb, udev,
                     usb_rcvintpipe(udev, ep->bEndpointAddress),
                     buf, 6,
                     uac1_interrupt_complete, NULL,
                     ep->bInterval);  // 轮询间隔 (帧数)

    return usb_submit_urb(urb, GFP_KERNEL);
}
常见陷阱:
- 中断端点必须配置为 IN 方向,且最大包长通常为 6 字节。
- 如果硬件不支持主动中断,驱动应返回 NAK 或空包,不要返回错误 STALL,否则主机可能认为端点出错。
- 中断 URB 的回调中必须重新提交 URB,否则只能收到一次状态变化。

4.5 控制与中断的协同:状态同步流程

下图展示了主机通过控制端点查询/设置音频属性,以及设备通过中断端点主动上报状态变化的完整交互流程:

UAC1.0 控制与中断端点交互流程 USB 主机 (Host) USB 设备 (Device) 控制端点 (EP0) 中断端点 (IN) ① GET_CUR / SET_CUR (控制传输) 解析请求 & 操作硬件 ② 数据阶段返回结果 硬件状态变化触发 ③ 中断 IN 数据包 (6字节状态) 中断 URB 回调处理 说明: 控制传输是同步的、主机发起的;中断传输是异步的、设备发起的。两者配合实现完整的音频控制闭环。

4.6 中断端点的 bInterval 与实时性权衡

中断端点的轮询间隔由描述符中的 bInterval 字段决定。对于全速 (Full Speed) 设备,bInterval 表示帧数 (1 帧 = 1 ms);对于高速 (High Speed) 设备,bInterval 表示微帧数 (1 微帧 = 125 μs)。

bInterval 值 全速 (FS) 轮询间隔 高速 (HS) 轮询间隔 适用场景
1 1 ms 125 μs 高实时性要求 (如专业音频控制台)
4 4 ms 500 μs 普通消费级声卡
8 8 ms 1 ms 低功耗/低优先级状态反馈
设计建议: 对于嵌入式 Linux 声卡,推荐 bInterval 设置为 4(全速)或 8(高速)。过小的间隔会增加 USB 总线带宽占用和 CPU 中断频率;过大的间隔会导致音量旋钮等操作产生可感知的延迟。

4.7 常见问题与调试方法

  • 控制请求返回 STALL:检查 wValue 中的 CS 和实体 ID 是否匹配描述符中的定义;确认 Feature Unit 的 bmControls 位图是否声明支持该控制。
  • 中断端点无数据:使用 usbmon 或 lsusb -v 确认中断端点描述符的 bEndpointAddress 和 wMaxPacketSize 是否正确;检查硬件是否真正产生了中断事件。
  • 音量值反向:UAC1.0 音量使用有符号数,+1 dB 对应 0x0100,-1 dB 对应 0xFF00。注意符号扩展和硬件寄存器映射。
  • 中断 URB 提交失败:确认中断端点未在 usb_set_interface 后被重置;检查 URB 的 interval 参数是否与描述符一致。
调试命令速查:
lsusb -v -d [vid:pid] | grep -A 10 "Audio Control" — 查看音频控制接口描述符
usbmon -i 1 -t -s 0 — 实时监控 USB 控制传输和中断传输
cat /sys/kernel/debug/usb/usbmon/1t — 查看 USB 监控文本输出