usb audio(4)--linux usb audio driver简要分析

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支持的就行。

你可能感兴趣的:(音频,usb-audio,stm32f4音频)