UAC(九)UAC 常见问题
修改UAC配置参数后无法正常通信
如果修改了UAC的配置后出现UAC无法正常通信的问题,可以通过修改设备的idVendor和idProduct来解决,只有设备的idVendor和idProduct发生改变Windows才会重新读取设备的配置描述符。
Kernel 5.4内核配置某些参数后UAC通信异常
例如在44100采样率、4channel、32bit参数下Device到Host端通信异常,而Host端到Device端通信正常。 问题的原因是由于Linux的UAC驱动计算的每次传输的usb包大小不正确,导致USB传输格式不匹配出现了通信异常。详细原因如下:
- 该参数下每秒钟的数据量为44100 * 4 * 4 = 705600
- USB每秒传输一千个包,则每个包的大小为 705600 / 1000 = 705.6
- Linux下UAC驱动的做法是每次传输705字节数据,当余数足够多的时候传输705+16=721字节,然后依次循环下去。但是这种方式存在一个问题就是705不是frames大小的整数倍,即705 / (4 * 4) = 44.0625,那么USB每次传输的数据不能完全送到声卡,要等下一次的数据到来后拼成一个完整的frame才能送到声卡,这样显然是非常麻烦的,且会造成更大的延迟
- Windows的做法是每次传输704字节,然后剩余的数据积攒到一帧的数据量后下次传输704 + (4 * 4)= 720字节
- 可以看到Linux和Windows每次传输和接收的大小都不一样,导致了两则无法正常通信,显然Linux的计算方法是错误的,Linux驱动中也没有自己拼接数据的逻辑,这个就是Linux下计算usb包大小的一个Bug。
Linux上游社区已经发现并解决了这个Bug,最终计算到的大小与Windows的大小相同,计算方法如下:
\[ pkt\_size = frame\_size * \frac{sampling\_rate}{1000} \]
即如下代码:
1 | pkt_size = frame_size * (sampling_rate / 1000) // 704 = 16 * (44100 / 1000) = 16 * 44 |
UAC1 无法支持48K、4channel、32bit问题
drivers/usb/gadget/function/u_uac1.h:
1 | #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 |
这里闲置了usb每个包的大小不能超过200字节,我们可以计算一下数据量:
对于48K、4channel、32bit每秒钟的数据量为:48000_4_4 = 768,000Byte
USB每秒发送1000次数据,每次最大3个数据包,则每个包大小需要768000 / 1000 / 3 = 256。这里的宏定义将每个包的大小限制在200了,则带宽不够,需要放开这个限制,将宏大小改为1023就可以了。
💡 驱动中为什么默认设置为200呢,其实是为了保证usb低速设备可以正常使用才设置这么低的,对于usb低速设备1.5Mbit/s = 1.5 * 1024 *1024 / 8 / 1000 = 196.如果usb支持全速或者高速可以将这个限制放开。
UAC2无法支持大于192K、4channel、32bit参数问题
drivers/usb/gadget/function/f_uac2.c:
1 | static struct usb_endpoint_descriptor hs_epout_desc = { |
这里bInterval是控制UAC发包频率的,USB全速总线1ms发送一次数据,USB高速总线可以125us发送一次数据,这里的bInterval就是用来控制UAC多久发送一次数据的,如果bInterval=1,则UAC数据包的间隔就是\(125us * 2^{1-1}\)如果bInterval=4则UAC发送间隔为\(125us * 2^{4-1}=1ms\)。驱动中默认是1ms发送一次数据,则最大的数据量就是1000 * 1024 * 3 = 3072000 = 192K * 4channel * 4Byte。
要解决这个问题只需要将bInterval改为1即可。
💡 bInterval=1具有更低的延迟,更大的带宽,但是对于大部分场景没必要设置这么高的频率,1ms就可以满足要求。
各系统UAC音量调节支持情况
Windows10/11 | Android 11(基于一加6) | ios | Macos | |
---|---|---|---|---|
UAC1 Disable FU | 通过调节音频数据幅值调节音量 | 通过调节音频数据幅值调节音量 | 不支持调节音量(音量滑块变灰不可拖动) | 同ios(未做验证) |
UAC1 Enable FU | 支持控制音量;不支持同步设备音量 | 通过调节音频数据幅值调节音量 | 支持控制音量;支持同步设备音量 | 同ios(未做验证) |
UAC2 Disable FU | 通过调节音频数据幅值调节音量 | 通过调节音频数据幅值调节音量 | 不支持调节音量(音量滑块变灰不可拖动) | 同ios(未做验证) |
UAC2 Enable FU | 支持控制音量;支持同步设备音量 | 通过调节音频数据幅值调节音量 | 支持控制音量;支持同步设备音量 | 同ios(未做验证) |
💡 1. 网上查到的信息是Android 14支持Feature Unit,但是在我的一加6(Android11)上验证是不支持FU的 2. macos应该与ios一致,未作验证
关于windows系统UAC1对FU支持情况的疑问
Windows 的UAC2对Function的支持情况有明确说明:
UAC2的Feature Unit是明确支持Interrupt的。但是没有查到UAC1对Function的支持情况。实际测试发现Windows是可以正确识别Feature unit端点描述符的,如下:
这里面的信息与Spec里都能一一对应上,端点描述符没有任何问题,但是测试发现Windows不会去读取Interrupt端点数据,抓包发现没有任何该端点的读取操作,那么实测得到的UAC1对Feature支持情况是:
- 支持:
- GET CUR
- SET CUR
- GET RANGE
- 不支持:
- Interrupt