Linux USB 3.0驱动分析(七)——UAC驱动分析


因为项目里面有USB音频外设,所以需要分析一下UAC驱动。
USB Audio Class,USB音频类,一个像USB这样的通用数据接口,可以有很多种实现数字音频数据传输的方式。不同的开发者可以根据自己的喜好和需求,定义任意的控制方式,传输模式,音频格式等等参数。


一.UAC驱动初始化分析
 代码路径:sound\usb\card.c
我们先来看看初始化部分,主要是初始化usb_audio_driver结构体
static const struct usb_device_id usb_audio_ids [] = { //这里是匹配列表
#include "quirks-table.h" //这个文件里面有很多厂商的声卡,主要是根据不同的声卡,进行一些特殊设置和处理
    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
      .bInterfaceClass = USB_CLASS_AUDIO,
      .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
    { }                        /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usb_audio_ids);
/*
 * entry point for linux usb interface
 */
static struct usb_driver usb_audio_driver = {
    .name =        "snd-usb-audio",
    .probe =    usb_audio_probe, //匹配成功后进入这个函数,匹配过程前面的有说明
    .disconnect =    usb_audio_disconnect,
    .suspend =    usb_audio_suspend,
    .resume =    usb_audio_resume,
    .reset_resume =    usb_audio_reset_resume,
    .id_table =    usb_audio_ids,
    .supports_autosuspend = 1, 
};

module_usb_driver(usb_audio_driver); //这里封装了,用usb_register注册一个接口驱动
匹配过后,进入usb_audio_probe函数里面
static int usb_audio_probe(struct usb_interface *intf,
               const struct usb_device_id *usb_id)
{
    alts = &intf->altsetting[0]; //获取当前配置描述符
    ifnum = get_iface_desc(alts)->bInterfaceNumber; //获取接口的个数
    id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
            le16_to_cpu(dev->descriptor.idProduct));
    if (get_alias_id(dev, &id))
        quirk = get_alias_quirk(dev, id);
    if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
        return -ENXIO;

    err = snd_usb_apply_boot_quirk(dev, intf, quirk, id); //根据不同的声卡设备,进行一些操作,也就是相当于补丁
    if (err < 0)
        return err;
    /*
     * found a config.  now register to ALSA
     */
    if (! chip) { //如果没有注册
        /* it's a fresh one.
         * now look for an empty slot and create a new card instance
         */
        for (i = 0; i < SNDRV_CARDS; i++) //最多注册8个
            if (!usb_chip[i] &&
                (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
                (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
                if (enable[i]) {
                    err = snd_usb_audio_create(intf, dev, i, quirk,
                                   id, &chip); //创建一个chip实例并设置它的名称,主要调用snd_card_new创建并初始化一个声卡结构
               .........
            }
    }
    err = 1; /* continue */
    if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
        /* need some special handlings */ //需要一些特殊处理
        err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk);
        if (err < 0)
            goto __error;
    }

    if (err > 0) {
        /* create normal USB audio interfaces */
        err = snd_usb_create_streams(chip, ifnum); //解析音频控制描述符并创建pcm/midi流
        if (err < 0)
            goto __error;
        err = snd_usb_create_mixer(chip, ifnum, ignore_ctl_error); //解析音频控制描述符并创建mixer控制节点
        if (err < 0)
            goto __error;
    }

    /* we are allowed to call snd_card_register() many times */
    err = snd_card_register(chip->card); //注册一个声卡
    if (err < 0)
        goto __error;
}
我们重点分析一下snd_usb_create_streams,它主要调用snd_usb_create_stream,主要是根据不同的协议,进入不同的分支

static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
{
    alts = &iface->altsetting[0]; //获取当前设置描述符
    altsd = get_iface_desc(alts); //获取接口描述符

    /*
     * Android with both accessory and audio interfaces enabled gets the
     * interface numbers wrong.
     */
    if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) ||
         chip->usb_id == USB_ID(0x18d1, 0x2d05)) && //主要针对于安卓
        interface == 0 &&
        altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
        altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) {
        interface = 2;
        iface = usb_ifnum_to_if(dev, interface);
        if (!iface)
            return -EINVAL;
        alts = &iface->altsetting[0];
        altsd = get_iface_desc(alts);
    }

    if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
         altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
        altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { //如果是midi接口
        int err = __snd_usbmidi_create(chip->card, iface,
                         &chip->midi_list, NULL,
                         chip->usb_id);
        if (err < 0) {
            dev_err(&dev->dev,
                "%u:%d: cannot create sequencer device\n",
                ctrlif, interface);
            return -EINVAL;
        }
        usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
        return 0;
    }

    if (snd_usb_get_speed(dev) == USB_SPEED_LOW) { //UAV不知道低速设备
        dev_err(&dev->dev, "low speed audio streaming not supported\n");
        return -EINVAL;
    }

    if (! snd_usb_parse_audio_interface(chip, interface)) { //解析USB音频接口
        usb_set_interface(dev, interface, 0); /* reset the current interface */
        usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); //绑定一个驱动到接口
    }

    return 0;
}
这里面有一个重要的函数,snd_usb_parse_audio_interface,用来解析音频接口,也就是pcm。这里面UAC1.0,2.0和UAC3.0有些一样。
UAC1.0,2.0使用snd_usb_add_audio_stream,UAC3.0使用snd_usb_add_audio_stream_v3。我们只看UAC1.0,2.0
大概的流程是
snd_usb_parse_audio_interface -> snd_usb_parse_audio_interface 
static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
                       int iface_no,
                       bool *has_non_pcm, bool non_pcm)
{
    for (i = 0; i < num; i++) {
        alts = &iface->altsetting[i];
        altsd = get_iface_desc(alts);
        protocol = altsd->bInterfaceProtocol;
        switch (protocol) {
        default:
            dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
                iface_no, altno, protocol);
            protocol = UAC_VERSION_1;
            /* fall through */
        case UAC_VERSION_1:
            /* fall through */
        case UAC_VERSION_2: {
            int bm_quirk = 0;
            /*
             * Blue Microphones workaround: The last altsetting is
             * identical with the previous one, except for a larger
             * packet size, but is actually a mislabeled two-channel
             * setting; ignore it.
             *
             * Part 1: prepare quirk flag
             */
            if (altno == 2 && num == 3 &&
                fp && fp->altsetting == 1 && fp->channels == 1 &&
                fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
                protocol == UAC_VERSION_1 &&
                le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
                            fp->maxpacksize * 2)
                bm_quirk = 1;
            fp = snd_usb_get_audioformat_uac12(chip, alts, protocol,
                               iface_no, i, altno,
                               stream, bm_quirk); //获取UAC12的音频格式
            break;
        }
        case UAC_VERSION_3:
            fp = snd_usb_get_audioformat_uac3(chip, alts, &pd,
                        iface_no, i, altno, stream); //获取UAC3的音频格式
            break;
        }
        dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
        if (protocol == UAC_VERSION_3)
            err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd); //UAC3添加audio stream
        else
            err = snd_usb_add_audio_stream(chip, stream, fp); //UAC12添加audio stream

        /* try to set the interface... */
        usb_set_interface(chip->dev, iface_no, altno);
        snd_usb_init_pitch(chip, iface_no, alts, fp);
        snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max); //初始化采样率
    }
    return 0;
}
snd_usb_add_audio_stream和snd_usb_add_audio_stream_v3都会调用__snd_usb_add_audio_stream,这里创建stream.该接口函数中根据stream的类型会分别创建playback_stream和capture_stream,并分别创建对应的playback的pcm接口和capture的接口:
static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
                      int stream,
                      struct audioformat *fp,
                      struct snd_usb_power_domain *pd)


{
    struct snd_usb_stream *as;
    struct snd_usb_substream *subs;
    struct snd_pcm *pcm;
    int err;

    list_for_each_entry(as, &chip->pcm_list, list) { //如果具有相同端点的流已经存在,则对其进行追加
        if (as->fmt_type != fp->fmt_type)
            continue;
        subs = &as->substream[stream];
        if (subs->ep_num == fp->endpoint) {
            list_add_tail(&fp->list, &subs->fmt_list);
            subs->num_formats++;
            subs->formats |= fp->formats;
            return 0;
        }
    }
    /* look for an empty stream */
    list_for_each_entry(as, &chip->pcm_list, list) {
        if (as->fmt_type != fp->fmt_type)
            continue;
        subs = &as->substream[stream];
        if (subs->ep_num)
            continue;
        err = snd_pcm_new_stream(as->pcm, stream, 1);
        if (err < 0)
            return err;
        snd_usb_init_substream(as, stream, fp, pd);
        return add_chmap(as->pcm, stream, subs);
    }


    /* create a new pcm */
    as = kzalloc(sizeof(*as), GFP_KERNEL);
    if (!as)
        return -ENOMEM;
    as->pcm_index = chip->pcm_devs;
    as->chip = chip;
    as->fmt_type = fp->fmt_type;
    err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
              stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
              stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
              &pcm); //创建一个新的PCM实例
    if (err < 0) {
        kfree(as);
        return err;
    }
    as->pcm = pcm;
    pcm->private_data = as;
    pcm->private_free = snd_usb_audio_pcm_free;
    pcm->info_flags = 0;
    if (chip->pcm_devs > 0)
        sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
    else
        strcpy(pcm->name, "USB Audio");

    snd_usb_init_substream(as, stream, fp, pd); //创建一个neinitialize子流实例PCM实例
    /*
     * Keep using head insertion for M-Audio Audiophile USB (tm) which has a
     * fix to swap capture stream order in conf/cards/USB-audio.conf
     */
    if (chip->usb_id == USB_ID(0x0763, 0x2003))
        list_add(&as->list, &chip->pcm_list);
    else
        list_add_tail(&as->list, &chip->pcm_list);
    chip->pcm_devs++;
    snd_usb_proc_pcm_format_add(as);

    return add_chmap(pcm, stream, &as->substream[stream]);
}
其中最重要的是snd_usb_set_pcm_ops()函数,它设置了pcm_substream的操作接口函数.
static const struct snd_pcm_ops snd_usb_playback_ops = {
    .open =        snd_usb_pcm_open,
    .close =    snd_usb_pcm_close,
    .ioctl =    snd_pcm_lib_ioctl,
    .hw_params =    snd_usb_hw_params,
    .hw_free =    snd_usb_hw_free,
    .prepare =    snd_usb_pcm_prepare,
    .trigger =    snd_usb_substream_playback_trigger,
    .pointer =    snd_usb_pcm_pointer,
    .page =        snd_pcm_lib_get_vmalloc_page,
};


static const struct snd_pcm_ops snd_usb_capture_ops = {
    .open =        snd_usb_pcm_open,
    .close =    snd_usb_pcm_close,
    .ioctl =    snd_pcm_lib_ioctl,
    .hw_params =    snd_usb_hw_params,
    .hw_free =    snd_usb_hw_free,
    .prepare =    snd_usb_pcm_prepare,
    .trigger =    snd_usb_substream_capture_trigger,
    .pointer =    snd_usb_pcm_pointer,
    .page =        snd_pcm_lib_get_vmalloc_page,
};


static const struct snd_pcm_ops snd_usb_playback_dev_ops = {
    .open =        snd_usb_pcm_open,
    .close =    snd_usb_pcm_close,
    .ioctl =    snd_pcm_lib_ioctl,
    .hw_params =    snd_usb_hw_params,
    .hw_free =    snd_usb_hw_free,
    .prepare =    snd_usb_pcm_prepare,
    .trigger =    snd_usb_substream_playback_trigger,
    .pointer =    snd_usb_pcm_pointer,
    .page =        snd_pcm_sgbuf_ops_page,
};


static const struct snd_pcm_ops snd_usb_capture_dev_ops = {
    .open =        snd_usb_pcm_open,
    .close =    snd_usb_pcm_close,
    .ioctl =    snd_pcm_lib_ioctl,
    .hw_params =    snd_usb_hw_params,
    .hw_free =    snd_usb_hw_free,
    .prepare =    snd_usb_pcm_prepare,
    .trigger =    snd_usb_substream_capture_trigger,
    .pointer =    snd_usb_pcm_pointer,
    .page =        snd_pcm_sgbuf_ops_page,
};


void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
{
    const struct snd_pcm_ops *ops;


    if (snd_usb_use_vmalloc) //默认一般是1
        ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?
            &snd_usb_playback_ops : &snd_usb_capture_ops; //操作函数赋值
    else
        ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?
            &snd_usb_playback_dev_ops : &snd_usb_capture_dev_ops;
    snd_pcm_set_ops(pcm, stream, ops);
}
snd_usb_use_vmalloc默认是1,上面函数给pcm->substream->ops赋值,根据stream类型,分别赋值为snd_usb_playback_ops 或者 snd_usb_capture_ops。到这里初始化基本上已经完成了。


二.UAC驱动读写分析
ALSA音频播放之前已经有分析过来,具体参考《  rk音频驱动分析之tinyplay播放
前面部分都是一样的,只是调用到不同的substream调用不同的substream的ops函数

1.open
应用打开设备:open(fn, O_RDWR);  //比如是/dev/snd/pcmC0D0p
对于UAC设备通过一系列掉用就会到snd_usb_pcm_open
主要是一些结构体初始化,还有建立硬件方面的信息。snd_usb_stream下面有snd_usb_substream,snd_usb_substream下面有snd_pcm_substream
ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info) //获取PCM信息static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
{
    int direction = substream->stream;
    struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
    struct snd_pcm_runtime *runtime = substream->runtime;
    struct snd_usb_substream *subs = &as->substream[direction];
    int ret;
    
    subs->interface = -1;
    subs->altset_idx = 0;
    runtime->hw = snd_usb_hardware;
    runtime->private_data = subs;
    subs->pcm_substream = substream;
    /* runtime PM is also done there */
    /* initialize DSD/DOP context */
    subs->dsd_dop.byte_idx = 0;
    subs->dsd_dop.channel = 0;
    subs->dsd_dop.marker = 1;

    ret = setup_hw_info(runtime, subs); //设置运行时硬件信息,比如采样率,通道等
    if (ret == 0) {
        ret = snd_media_stream_init(subs, as->pcm, direction);
        if (ret)
            snd_usb_autosuspend(subs->stream->chip);
    }
    return ret;
}

2.设置音频参数
应用调用ioctl:ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params) //设置音频硬件参数
对于UAC设备通过一系列掉用就会到snd_usb_hw_params,主要是设置一些音频参数,还有接口和端点方面的设置。
static int snd_usb_hw_params(struct snd_pcm_substream *substream,
                 struct snd_pcm_hw_params *hw_params)
{
    struct snd_usb_substream *subs = substream->runtime->private_data;
    struct audioformat *fmt;
    int ret;

    ret = snd_media_start_pipeline(subs);
    if (ret)
        return ret;

    if (snd_usb_use_vmalloc)
        ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
                               params_buffer_bytes(hw_params)); //分配虚拟DMA内存,只能被内核代码使用,不能被DMA设备使用
    else
        ret = snd_pcm_lib_malloc_pages(substream,
                           params_buffer_bytes(hw_params));
    if (ret < 0)
        goto stop_pipeline;

    subs->pcm_format = params_format(hw_params); //格式
    subs->period_bytes = params_period_bytes(hw_params); //周期
    subs->period_frames = params_period_size(hw_params); 
    subs->buffer_periods = params_periods(hw_params);
    subs->channels = params_channels(hw_params);
    subs->cur_rate = params_rate(hw_params);

    fmt = find_format(subs); //从snd_usb_substream找到对应的格式,找不到就报错
    if (!fmt) {
        dev_dbg(&subs->dev->dev,
            "cannot set format: format = %#x, rate = %d, channels = %d\n",
               subs->pcm_format, subs->cur_rate, subs->channels);
        ret = -EINVAL;
        goto stop_pipeline;
    }

    ret = snd_usb_lock_shutdown(subs->stream->chip); //锁定关机(断开)任务并自动恢复
    if (ret < 0)
        goto stop_pipeline;

    ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);  //设置电源状态
    if (ret < 0)
        goto unlock;

    ret = set_format(subs, fmt); //找到匹配的格式并设置接口,里面会设置同步端点,也就是这是同步传输
    if (ret < 0)
        goto unlock;

    subs->interface = fmt->iface;
    subs->altset_idx = fmt->altset_idx;
    subs->need_setup_ep = true;
}

3.预备传输
应用调用ioctl: ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)  //主要是传输前的准备
对于UAC设备通过一系列掉用就会到snd_usb_pcm_prepare,
static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{
    ret = set_format(subs, subs->cur_audiofmt); //设置,前面也有这个操作
    if (ret < 0)
        goto unlock;

    if (subs->need_setup_ep) {
        iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
        alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
        ret = snd_usb_init_sample_rate(subs->stream->chip,
                           subs->cur_audiofmt->iface,
                           alts,
                           subs->cur_audiofmt,
                           subs->cur_rate); //初始化采样率
        if (ret < 0)
            goto unlock;

        ret = configure_endpoint(subs); //设置端点,报错数据和同步端点
        if (ret < 0)
            goto unlock;
        subs->need_setup_ep = false;
    }

    /* some unit conversions in runtime */
    subs->data_endpoint->maxframesize =
        bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
    subs->data_endpoint->curframesize =
        bytes_to_frames(runtime, subs->data_endpoint->curpacksize);

    /* reset the pointer */
    subs->hwptr_done = 0;
    subs->transfer_done = 0;
    subs->last_delay = 0;
    subs->last_frame_number = 0;
    runtime->delay = 0;

    /* for playback, submit the URBs now; otherwise, the first hwptr_done
     * updates for all URBs would happen at the same time when starting */
    if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) //如果是播放,现在就提交URBs
        ret = start_endpoints(subs);
}
configure_endpoint里面会调用snd_usb_endpoint_set_params设置一个snd_usb_endpoint。数据端点用于发送音频数据,同步端点用于接收设备端的反馈。
int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, prepare_playback_urb
                snd_pcm_format_t pcm_format,
                unsigned int channels,
                unsigned int period_bytes,
                unsigned int period_frames,
                unsigned int buffer_periods,
                unsigned int rate,
                struct audioformat *fmt,
                struct snd_usb_endpoint *sync_ep)
{
    if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
        ep->freqn = get_usb_full_speed_rate(rate);
    else
        ep->freqn = get_usb_high_speed_rate(rate);

    /* calculate the frequency in 16.16 format */
    ep->freqm = ep->freqn;
    ep->freqshift = INT_MIN;
    ep->phase = 0;
    switch (ep->type) {
    case  SND_USB_ENDPOINT_TYPE_DATA:
        err = data_ep_set_params(ep, pcm_format, channels,
                     period_bytes, period_frames,
                     buffer_periods, fmt, sync_ep); //如果是数据端点,使用data_ep_set_params
        break;
    case  SND_USB_ENDPOINT_TYPE_SYNC:
        err = data_ep_set_params(ep); //如果是同步端点,data_ep_set_params
        break;
    default:
        err = -EINVAL;
    }
    usb_audio_dbg(ep->chip,
        "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
        ep->ep_num, ep->type, ep->nurbs, err);

data_ep_set_params
data_ep_set_params里面会分配URB,传输完成后调用snd_complete_urb。因为等时 urb 没有初始化函数,必须手动初始化,所以下面是包括分配和初始化了。
static int data_ep_set_params(struct snd_usb_endpoint *ep,
                  snd_pcm_format_t pcm_format,
                  unsigned int channels,
                  unsigned int period_bytes,
                  unsigned int frames_per_period,
                  unsigned int periods_per_buffer,
                  struct audioformat *fmt,
                  struct snd_usb_endpoint *sync_ep)
{
     .......前面是一系列的计算URB.....

    /* allocate and initialize data urbs */
    for (i = 0; i < ep->nurbs; i++) {
        struct snd_urb_ctx *u = &ep->urb[i];
        u->index = i;
        u->ep = ep;
        u->packets = urb_packs;
        u->buffer_size = maxsize * u->packets;

        if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
            u->packets++; /* for transfer delimiter */
        u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); //分配URB
        if (!u->urb)
            goto out_of_memory;
        u->urb->transfer_buffer =
            usb_alloc_coherent(ep->chip->dev, u->buffer_size,
                       GFP_KERNEL, &u->urb->transfer_dma);
        if (!u->urb->transfer_buffer)
            goto out_of_memory;
        u->urb->pipe = ep->pipe;
        u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
        u->urb->interval = 1 << ep->datainterval;
        u->urb->context = u;
        u->urb->complete = snd_complete_urb; //传输完成后调用
        INIT_LIST_HEAD(&u->ready_list);
    }
sync_ep_set_params
里面会分配URB,传输完成后调用snd_complete_urb。因为等时 urb 没有初始化函数,必须手动初始化,所以下面是包括分配和初始化了。
static int sync_ep_set_params(struct snd_usb_endpoint *ep)
{
    int i;

    ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
                     GFP_KERNEL, &ep->sync_dma);
    if (!ep->syncbuf)
        return -ENOMEM;

    for (i = 0; i < SYNC_URBS; i++) {
        struct snd_urb_ctx *u = &ep->urb[i];
        u->index = i;
        u->ep = ep;
        u->packets = 1;
        u->urb = usb_alloc_urb(1, GFP_KERNEL); 分配URB
        if (!u->urb)
            goto out_of_memory;
        u->urb->transfer_buffer = ep->syncbuf + i * 4;
        u->urb->transfer_dma = ep->sync_dma + i * 4;
        u->urb->transfer_buffer_length = 4;
        u->urb->pipe = ep->pipe;
        u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
        u->urb->number_of_packets = 1;
        u->urb->interval = 1 << ep->syncinterval;
        u->urb->context = u;
        u->urb->complete = snd_complete_urb;
    }
    ep->nurbs = SYNC_URBS;
    return 0;
}
start_endpoints调用snd_usb_endpoint_start。
int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
    /*
     * If this endpoint has a data endpoint as implicit feedback source,
     * don't start the urbs here. Instead, mark them all as available,
     * wait for the record urbs to return and queue the playback urbs
     * from that context.
     */
    set_bit(EP_FLAG_RUNNING, &ep->flags);
    if (snd_usb_endpoint_implicit_feedback_sink(ep)) { //如果这个端点有一个数据端点是作为隐含的反馈源,不在这里启动urb.
        for (i = 0; i < ep->nurbs; i++) {
            struct snd_urb_ctx *ctx = ep->urb + i;
            list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
        }
        return 0;
    }

    for (i = 0; i < ep->nurbs; i++) {
        struct urb *urb = ep->urb[i].urb;

        if (snd_BUG_ON(!urb))
            goto __error;

        if (usb_pipeout(ep->pipe)) {
            prepare_outbound_urb(ep, urb->context); //调用prepare_playback_urb准备urb,主要是缓冲区的操作
        } else {
            prepare_inbound_urb(ep, urb->context); 
        }

        err = usb_submit_urb(urb, GFP_ATOMIC); //提交URB,成功后会调用snd_complete_urb
        if (err < 0) {
            usb_audio_err(ep->chip,
                "cannot submit urb %d, error %d: %s\n",
                i, err, usb_error_string(err));
            goto __error;
        }
        set_bit(i, &ep->active_mask);
    }
    return 0;

4.写入数据
应用调用ioctl:ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)
pcm_write写下来的数据都是放在substream->runtime->dma_area里面。
对于UAC设备通过一系列掉用就会到snd_usb_substream_playback_trigger启动传输。前面准备阶段已经提交了URB,这里同时执行SNDRV_PCM_TRIGGER_START和SNDRV_PCM_TRIGGER_PAUSE_RELEASE两个分支。
static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,  int cmd)
{
    struct snd_usb_substream *subs = substream->runtime->private_data;

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
        subs->trigger_tstamp_pending_update = true; //启动只更新标志位
        /* fall through */
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:  //注意,前面的case没有return语句,所以会下来执行这个
        subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
        subs->data_endpoint->retire_data_urb = retire_playback_urb;
        subs->running = 1;
        return 0;
    case SNDRV_PCM_TRIGGER_STOP: //停止
        stop_endpoints(subs, false);
        subs->running = 0;
        return 0;
    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        subs->data_endpoint->prepare_data_urb = NULL;
        /* keep retire_data_urb for delay calculation */
        subs->data_endpoint->retire_data_urb = retire_playback_urb;
        subs->running = 0;
        return 0;
    }
    return -EINVAL;
}
为什么这里就能启动播放能呢。准备阶段数据URB发送的是静音数据,启动之后才发送正在的数据。
static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                 struct snd_urb_ctx *ctx)
{
    switch (ep->type) {
    case SND_USB_ENDPOINT_TYPE_DATA: 
        if (ep->prepare_data_urb) { //启动之后,给ep->prepare_data_urb赋值之后,才准备真正的数据
            ep->prepare_data_urb(ep->data_subs, urb);
        } else { //这里是准备阶段,只是发送静音数据
            /* no data provider, so send silence */
            prepare_silent_urb(ep, ctx);
        }
        break;
    case SND_USB_ENDPOINT_TYPE_SYNC:
        if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
            /*
             * fill the length and offset of each urb descriptor.
             * the fixed 12.13 frequency is passed as 16.16 through the pipe.
             */
            urb->iso_frame_desc[0].length = 4;
            urb->iso_frame_desc[0].offset = 0;
            cp[0] = ep->freqn;
            cp[1] = ep->freqn >> 8;
            cp[2] = ep->freqn >> 16;
            cp[3] = ep->freqn >> 24;
        } else {
            /*
             * fill the length and offset of each urb descriptor.
             * the fixed 10.14 frequency is passed through the pipe.
             */
            urb->iso_frame_desc[0].length = 3;
            urb->iso_frame_desc[0].offset = 0;
            cp[0] = ep->freqn >> 2;
            cp[1] = ep->freqn >> 10;
            cp[2] = ep->freqn >> 18;
        }
        break;
    }
}

5.传输回调
传输成功后会调用snd_complete_urb。
static void snd_complete_urb(struct urb *urb)
{
    if (usb_pipeout(ep->pipe)) { //如果是输出通道,这里是处理播放的情况
        retire_outbound_urb(ep, ctx); //调用retire_playback_urb,数据播放完成后进行处理,降低延迟
        /* can be stopped during retire callback */
        if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
            goto exit_clear;

        if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
            spin_lock_irqsave(&ep->lock, flags);
            list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
            spin_unlock_irqrestore(&ep->lock, flags);
            queue_pending_output_urbs(ep);
            goto exit_clear;
        }
        prepare_outbound_urb(ep, ctx); //准备一个播放urb提交到总线。
        /* can be stopped during prepare callback */
        if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
            goto exit_clear;
    } else { //如果是输入通道,这里是处理录音的情况
        retire_inbound_urb(ep, ctx); //如果是同步端点,使用snd_usb_handle_sync_urb解析usb同步包;如果不是,使用retire_capture_urb,双缓存避免溢出
        /* can be stopped during retire callback */
        if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
            goto exit_clear;
        prepare_inbound_urb(ep, ctx); //准备一个录音或同步urb以提交到总线
    }

    err = usb_submit_urb(urb, GFP_ATOMIC); //提交urb
    if (err == 0)
        return;
}

参考:



posted @ 2021-01-15 14:34  luoyuna  阅读(6277)  评论(0编辑  收藏  举报