Linux下的usb audio驱动基于ALSA 音频架构,这里只介绍usb audio驱动中的数据流向及反馈的处理,不涉及ALSA架构。
音频文件播放前的调用关系:
PCM的Trigger方法
调用 snd_usb_substream_playback_trigger 设定一些全局指针
PCM的Prepare方法
调用snd_usb_pcm_prepare
1. 调用snd_usb_init_sample_rate,设定usb device采样率等参数
2. 调用start_endpoints,开始USB数据传输
snd_usb_endpoint_start
反馈值的处理:
每次同步传输完成后调用驱动中注册的USB回调函数snd_complete_urb
prepare_outbound_urb -> prepare_playback_urb
prepare_playback_urb函数调用snd_usb_endpoint_next_packet_size来计算下一个发送包的大小,该函数实现如下:
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
{
unsigned long flags;
int ret;
if (ep->fill_max)
return ep->maxframesize;
spin_lock_irqsave(&ep->lock, flags);
ep->phase = (ep->phase & 0xffff)
+ (ep->freqm << ep->datainterval);
ret = min(ep->phase >> 16, ep->maxframesize);
spin_unlock_irqrestore(&ep->lock, flags);
return ret;
}
这里 ep->freqm是反馈端点获取的反馈值,以16:16的方式存储,即高16bit存储反馈值的高10bit,低16bit存储反馈值的低14bit,比如,48K反馈值,freqm 高16bit存储48,低16bit为0。Ep->datainterval在这里值为0,因此snd_usb_endpoint_next_packet_size函数实际上返回值就是usb device反馈的播放速率,如device反馈48K,snd_usb_endpoint_next_packet_size返回48,即下一个包大小为48 frames的audio 数据。这里host实际上发送速率就是device实际的播放速率了。当然如果反馈值是44.K 这样的值,snd_usb_endpoint_next_packet_size返回值会将小数部分累加,即返回值是 44.1K,44.2K,44.3K,44.4K等,直到达到45K,重新返回44.1K
反馈值的获取:
snd_usb_handle_sync_urb函数用于获取device的反馈值,主要实现如下:
void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
struct snd_usb_endpoint *sender,
const struct urb *urb)
{
f = le32_to_cpup(urb->transfer_buffer);
if (urb->iso_frame_desc[0].actual_length == 3)
f &= 0x00ffffff;
else
f &= 0x0fffffff;
if (f == 0)
return;
if (unlikely(ep->freqshift == INT_MIN)) {
shift = 0;
while (f < ep->freqn - ep->freqn / 4) {
f <<= 1;
shift++;
}
while (f > ep->freqn + ep->freqn / 2) {
f >>= 1;
shift--;
}
ep->freqshift = shift;
} else if (ep->freqshift >= 0)
f <<= ep->freqshift;
else
f >>= -ep->freqshift;
if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
/*
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
spin_lock_irqsave(&ep->lock, flags);
ep->freqm = f;
spin_unlock_irqrestore(&ep->lock, flags);
} else {
/*
* Out of range; maybe the shift value is wrong.
* Reset it so that we autodetect again the next time.
*/
ep->freqshift = INT_MIN;
}
}
最终的反馈值会存储在 ep->freqm 中,也即上面我们在snd_usb_endpoint_next_packet_size中使用的那个变量。snd_usb_handle_sync_urb主要是对反馈值的范围进行判断(这里的取值范围前面已讲到),然后转换成16:16 的形式存储在ep->freqm中,这里ep->freqshift表示移位偏移,比如usb device 反馈值是10:14的格式,要转换成16:16格式,需要将device的反馈值左移2bit即可,因此ep->freqshift的值就是2。
windowns下的usb audio异步方式驱动
windows下的可以直接使用xmos的驱动(本人使用的驱动链接:http://download.csdn.net/detail/xjq163/9906107),只需将usb固件中PID和VID改成xmos支持的就行。