同步问题原因

USB Isochronous 传输(协议无问题)

Isochronous Transfer 同步问题

电脑播放器播放音乐时:是按一个固定的速率,比如 44.1KHZ,电脑内有一个晶振,可分频出一个 44.1KHZ,进行音乐播放,发给 USB 的数据流速率固定。USB 声卡自己得有一个晶振才能工作,它也可分频出一个 44.1KHZ,供给 I2S 信号或 DAC。

问题来了,晶振是有误差的,这两个 44.1KHZ 不可能完全一模一样,电脑可能是 44.100KHZ,USB 声卡可能是 44.098KHZ,误差约 50ppm,很正常的情况。虽然声卡晶振分频出来是 44.098KHZ,但声卡认为它就是工作在 44.100KHZ 下。好吧,如果二者时钟独立运行,那么 1 个小时会误差 0.2 秒,会出现不同步! 即电脑播了 1 个小时的数据,USB 声卡实际是无法播完的,要多 0.2 秒才能播完。 如果声卡也要 1 小时播完,那这 1 小时就需要丢掉 0.2 秒的数据。

所以二者的时钟必须要同步一致才行,这就是 UAC 同步问题的原因,因此 USB 音频规定了一是采用“等时传输模式”,二是设备需要指定为 3 种同步方式之一:同步(synchronous),适应(adaptive),异步(asynchronous)。

以上摘自 http://bbs.erji.net/forum.php?mod=viewthread&tid=2076674&page=1

USB 音频声卡的时钟同步方式

USB 的 isochronous 模式

USB 音频声卡采用 isochronous“等时传输模式”,能保证实时传输数据,但错误容忍,出错不重传。它是实时传输,所以 USB 电脑端发出多少速率的数据,USB 接收端(USB 声卡)就得接收多少速率。比如电脑发送 44.1KHZ 的,声卡就接收 44.1KHZ。

Asynchronous

cannot synchronize to SOF or any other clock in the USB domain。

Source: Provides implicit feedforward(data stream),Free running Fs

Sink: Provides explicit feedback (isochronous pipe),Free running Fs

Asynchronous sink endpoints must provide explicit feedback information to an adaptive driver.

Asynchronous:要求 Sink 主动提供 data rate 信息,对信息如何使用没做规范要求,Source 和 Sink 都各自按照自己的速率运行,音频质量是最好的

Synchronous

Source:Uses implicit feedback(SOF), Fs locked to SOF

Sink: Uses implicit feedback(SOF) Fs locked to SOF

Source: its sample clock from SOF and produces a fixed number of audio samples every USB (micro)frame,

Sink: its sample clock from SOF and consumes a fixed number of samples every USB (micro)frame.

Synchronous:Source 和 Sink 都与 SOF 同步,这样导致 Source 和 Sink 的 clk 都不够稳定,音频质量会受到影响,但是 Source 和 Sink 都无需提供 feedback 信息,兼容性最好

Adaptive

Source:Uses explicit feedback(isochronous pipe) ,Fs locked to sink

Sink: Uses implicitfeedforward (data stream), Fs locked to data flow

Source: Adaptive source endpoints produce data at a rate that is controlled by the data sink。

Sink:The sink provides feedback to the source,the data rate information is embedded in the data stream。The average number of samples received during a certain averaging time determines the instantaneous data rate

condition:Sink Device contains a fully adaptive sample rate converter (SRC)

Adaptive:使用 Sink 提供的 feedback 信息(明确要求 Sink 提供 data rate 信息),调整自己的 clk 对齐 Sink

Feedback

ASYNC: An asynchronous sink must provide explicit feedback to the host by indicating accurately what its desired data rate (Ff) is, relative to the USB (micro)frame frequency。This allows the host to continuously adjust the number of samples sent to the sink so that neither underflow or overflow of the data buffer occurs

ADAPTIVE: An adaptive source must receive explicit feedback from the host so that it can accurately generate the number of samples required by the host.

Ff: the desired data rate

Fs: actual sampling rate, 至少需要 1s 的测量时间 Tmeas(单位是帧数)。单位是帧数,对于全速设备 1ms 一帧,那么 1s 就需要 1000 帧(Fs>=1000),而对于高速设备,125us 一帧 (Fs≥8000).

Fm: 通常 Fs 是由 master clock Fm 分频得到:

\[ F_m = F_s * 2^P \]

因此可以利用 Fm 来缩短测量时间 Tmeas:

\[ T_{meas} = \frac{2^K}{2^P} = 2^{K-P} \]

这样测量 Fs 的时间就降低为 \(2^(K-P)\) 帧时间了。

对于全速设备,Fs 需要使用定点数 10.10(K=10)格式,需要 3 字节,调整为 10.14 定点数来表示

对于高速设备,Fs 需要使用定点数 12.13(K=13)格式,需要 4 字节,调整为 16.16 定点数来表示

对 P 的要求:

  • P must be in the range [0,K]
  • P 尽量大一些,这样可以减少 Tmeas 测试时间,也可以减少缓存大小
  • P 应该小于 K,保证至少两帧取平均来减少抖动,测量更平滑
  • P 不要为 0,保证在 Ff 丢失的情况下可以用更为近的 Fs 来代替,减小误差相邻数据相似性

Implicit Feedback

有些时候,可以避免 explicit 实现一个 feedback endpoint,如果设备实现了一组 isochronous data endpoints,且:

  • All the endpoints in the group are synchronized (i.e. use sample clocks that are derived from a common master clock)
  • The group contains one or more isochronous data endpoints in one direction that normally would need explicit feedback
  • The group contains at least one isochronous data endpoint in the opposite direction

在这些情况下,设备可以不 explicit feedback endpoint.

有以下可能的情况:

  • One or more asynchronous sink endpoints are accompanied by an asynchronous source endpoint. The data rate on the source endpoint can be used as implicit feedback information to adjust the data rate on the sink endpoint(s).
  • One or more adaptive source endpoints are accompanied by an adaptive sink endpoint. The source endpoint can adjust its data rate based on the data rate received by the sink endpoint

解决 5.x 以前的内核在 Windows10 系统下无法识别 UAC2 的问题

UAC 端点描述符

1
2
3
4
5
6
7
8
9
struct _ENDPOIN_DESCRIPTOR_STRUCT 
{
BYTE bLength; //设备描述符的字节数大小,为 0x7
BYTE bDescriptorType; //描述符类型编号,为 0x05
BYTE bEndpointAddress; //端点地址及输入输出属性
BYTE bmAttribute; //端点的传输类型属性
WORD wMaxPacketSize; //端点收、发的最大包的大小
BYTE bInterval; //主机查询端点的时间间隔
} ENDPOIN_DESCRIPTOR_STRUCT ;
  • bLength : 描述符大小.固定为 0x07.
  • bDescriptorType : 端点描述符类型.固定为 0x05.
  • bEndpointAddress : USB 设备的端点地址.Bit7,方向,对于控制端点可以忽略,1/0:IN/OUT.Bit6-4,保留.BIt3-0:端点号.
  • bmAttributes : 端点属性.Bit7-2,保留(同步有定义).BIt1-0:00 控制,01 同步,02 批量,03 中断. 当为同步传输时,bEndpointType 的 bit3-2 的值不同代表的含义不同:00:无同步,01:异步,10:适配,11:同步;BIT5:4 含义 00: 表示数据端点,01:表示反馈端点 Feedback endpoint,10:表示隐式反馈数据端点 Implicit feedback Data endpoint,11: 保留
  • wMaxPacketSize : 本端点接收或发送的最大信息包大小.

    • USB2.0 时: 对于同步端点,此值用于指示主机在调度中保留的总线时间,这是每(微)帧数据有效负载所需的时间,有效负载时间就是发送一帧数据需要占用的总线时间,在实际数据传输过程中,管道实际使用的带宽可能比保留的带宽少,大家想想,如果实际使用的带宽比保留的还多,那就丢数了;

      对于其类型的端点,bit10~bit0 指定最大数据包大小(以字节为单位);

      bit12~bit11 对于高速传输的同步和中断端点有效:bit12~bit11 可指定每个微帧的额外通信次数,这里大家一定要知道是在高速传输中,当一个事务超时时,在一个微帧时间内重传的次数,如果设置为 00b(None),则表示在一个微帧内只传输一个事务,不进行额外的超时重传,如果设置为 01b,则表示在一个微帧内可以传输两次事务,有一次额外的重传机会,从下面可以看出,一个微帧最多可以有两次重传事务的机会,如果微帧结束了还是失败,就需要等到下一个微帧继续发送该事务;

    • USB3.0 时: wMaxPacketSize 表示包的大小。对于 bulk 为 1024,而对于同步传输,可以为 0~1024 或 1024。

  • bInterval : 轮询数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为 1-255.
    • 对于全速/高速同步端点,此值必须在 1 到 16 之间。bInterval 值用作 2 的指数,例如 bInterval 为 4,表示周期为 8 个单位;
    • 对于全速/低速中断端点,该字段的值可以是 1 到 255,也就是主机多少 ms 给设备发一次数据请求;
    • 对于高速中断端点,使用 bInterval 值作为 2 的指数,例如 bInterval 为 4 表示周期为 8。这个值必须在 1 到 16 之间;
    • 对于高速批量/控制输出端点,bInterval 必须指定端点的最大 NAK 速率。值 0 表示端点永不 NAK。其它值表示每个微帧的 bInterval*125us 时间最多 1 个 NAK。这个值的范围必须在 0 到 255 之间;
      • 00 = None (1 transaction per microframe)
      • 01 = 1 additional (2 per microframe)
      • 10 = 2 additional (3 per microframe)
      • 11 = Reserved
      • 其它位默认为 0,
    • 对于全速/低速批量/控制输出端点,此值无意义,可以任意指定。

以 fs_epout_desc 为例描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* STD AS ISO OUT Endpoint */
static struct usb_endpoint_descriptor fs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE, //#define USB_DT_ENDPOINT_SIZE 7
.bDescriptorType = USB_DT_ENDPOINT, //#define USB_DT_ENDPOINT 0x05

.bEndpointAddress = USB_DIR_OUT, // #define USB_DIR_OUT 0 /* to device */
// #define USB_ENDPOINT_XFER_ISOC 1 /* 同步传输 */
// #define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) /* 适配 */
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE,
/* The USB limits the maximum data payload size to 1,023 bytes for each full-speed isochronous endpoint.
High-speed endpoints are allowed up to 1024-byte data payloads. */
.wMaxPacketSize = cpu_to_le16(1023),
.bInterval = 1,
};

IN 和 OUT 都是相对于 USB Host 设备来讲的:

对于 OUT Endpoint 不支持 ASYNC 做如下说明:

  • ASYNC 模式下需要 Sink 设备 Provides explicit feedback,而对于 OUT Endpoint,Sink 设备是我们的主机,这说明我们的设备不支持 feedback

对于 IN EndPoint,测试发现支持 ASYNC 做如下说明:

  • ASYNC 模式下需要 Sink 设备 Provides explicit feedback,而对于 IN Endpoint,Sink 设备是测试的 Windows10 系统,说明 Windows10(基于我的测试版本)是支持 feedback

结论:
Windows 的 UAC2 是支持 Feedback 的,而我们的系统不支持,那么将 OUT Endpoint 改成 ADAPTIVE 模式符合 USB isochronous 传输规范,但是因为我们设备没有实现 feedback,有没有利用到 Host 提供的 feedback 信息待验证。同理 OUT Endpoint 改成 ADAPTIVE 模式,那么 IN EndPoint 就可以保持为 ASYNC,因为这都要求 Host 端提供 feedback。当然全部改成 SYNC 模式也可以获得很好的兼容性,但是会造成 Source 和 Sink 的 clk 抖动。 还有另外一种做法,我看到 Linux6.x 驱动里已经支持 feedback 了,移植过来 Device 端就支持三种同步方式了。

fs_epout_desc 端点会由 USB_ENDPOINT_SYNC_ASYNC 改成 USB_ENDPOINT_SYNC_ADAPTIVE,最新的 6.x 内核会根据是否支持 feedback 来决定用哪个同步模式,如果支持 feedback 就用 USB_ENDPOINT_SYNC_ASYNC,如果不支持 feedback 就用 USB_ENDPOINT_SYNC_ADAPTIVE。实际上用 USB_ENDPOINT_SYNC_SYNC 也能够在 windows 上识别。 fs_epin_desc 端点:由 USB_ENDPOINT_SYNC_ASYNC 改成 USB_ENDPOINT_SYNC_SYNC。

总结

实现方式 优点 缺点
SYNC Source 和 Sink 各自运行在自己的 Clock 下,两者之间没有任何反馈,Source 只管发,Sink 只管收 简单 运行时间久了由于两则的 clock 不匹配会导致 hw buffer 溢出或者为空导致 xrun 问题
ADAPTIVE Source 提供自己的 srate 信息给 Sink,Sink 可以自己调节 Clock 或者其他方式匹配 Source 的 Clock 提供了一种匹配 Clock 的方式,可以解决 xrun 问题了 由于 Source 端发送频率和大小固定,只能 Device 端靠调节 Clock 来解决 xrun,调节 clock 会影响音质
ASYNC Sink 提供自己的 srate 给 Source,Source 通过调节每个 USB 包大小来匹配 Sink 的 Clock 提供了匹配 clock 的方式,解决了 xrun 问题
Sink 不需要调节 clock,音质不会受到任何影响,完美

参考文献

《USB2.0 协议规范》
《UAC2 协议规范》