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

来源:互联网 发布:知否 书包网 编辑:程序博客网 时间:2024/06/06 18:40

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