转自 https://www.cnblogs.com/wen123456/p/14281917.html

因为项目里面有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 streamelseerr = 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);elsestrcpy(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);elselist_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) //默认一般是1ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?&snd_usb_playback_ops : &snd_usb_capture_ops; //操作函数赋值elseops = 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设备使用elseret = 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) //如果是播放,现在就提交URBsret = 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_urbsnd_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);elseep->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_paramsbreak;case  SND_USB_ENDPOINT_TYPE_SYNC:err = data_ep_set_params(ep); //如果是同步端点,data_ep_set_paramsbreak;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); //分配URBif (!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); 分配URBif (!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_urbif (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); //提交urbif (err == 0)return;
}

参考:
USB播放音乐知识记录 - wulizhi - 博客园 (cnblogs.com)

Linux USB 3.0驱动分析—UAC驱动分析相关推荐

  1. Linux USB On-The-Go(OTG) on OMAP H2 软件架构分析(一)

    原帖地址:http://blog.csdn.net/zkami/article/details/2508136 Linux USB "On-The-Go"(OTG) on OMAP ...

  2. USB总线-Linux内核USB3.0设备控制器之UDC驱动分析(六)

    1.概述 UDC驱动的接口都定义在drivers/usb/gadget/udc/core.c文件中.USB Function驱动通过调用这些接口匹配及访问USB设备控制器,而底层USB控制器驱动要实现 ...

  3. USB总线-Linux内核USB3.0设备控制器中断处理程序分析(九)

    1.概述 USB设备枚举.请求处理.数据交互都涉及USB设备控制器中断.当有事件发生时,USB设备控制器首先将事件信息通过DMA写入到事件缓冲区中,然后向CPU发出中断,随后CPU调用中断处理函数开始 ...

  4. linux usb代码,Linux USB Host Controller的初始化代码框架分析

    usb_hcd_omap_probe (const struct hc_driver *driver) (dev/ohci/ohci-omap.c) 在模块初始化时被platform_driver_r ...

  5. Linux USB On-The-Go(OTG) on OMAP H2 软件架构分析(二)

    原帖地址:http://blog.csdn.net/zkami/article/details/2586831 编程接口的变化 为了支持OTG特性,编程接口上作了尽可能小的改动.这些改动并不会改变已有 ...

  6. linux中流设备_Linux USB的那些事之设备驱动子系统终极篇

    原标题:Linux USB的那些事之设备驱动子系统终极篇 USB博大精深不是一篇文章就能够解释清楚的.想要深入研究USB的话,USB协议(外加Host和OTG协议)是必要的知识,另外,国内有本< ...

  7. Linux USB驱动分析(一)----USB2.0协议分析

    原文地址:http://blog.chinaunix.net/uid-25445243-id-4040449.html 一.USB硬件介绍 1.1.概述 一条USB传输线分别由地线.电源线.D+和D- ...

  8. 【正点原子Linux连载】第六十七章 Linux USB驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. USB总线-Linux内核USB设备驱动之UAC2驱动分析(十)

    1.概述 UVC(USB Audio Class)定义了使用USB协议播放或采集音频数据的设备应当遵循的规范.目前,UAC协议有UAC1.0和UAC2.0.UAC2.0协议相比UAC1.0协议,提供了 ...

最新文章

  1. AI一分钟 | 美女机器人竟然想生孩子,太可怕了!比尔·盖茨当选中国工程院外籍院士
  2. 直接路径读取对于延迟块清除的影响
  3. 给thinkphp加个分页样式
  4. python学习高级篇(part1)--类属性
  5. 牛客练习赛 65 (待补E-网络流)
  6. 深入理解JVM的内存区域划分
  7. 59. C# -- .NET Framework 常用命名空间总结
  8. linux三个命令模式切换,ubuntu16.04命令行模式和图形界面互相切换的两种解决办法,...
  9. 用session监听实现在线统计
  10. python全局变量定义_python全局变量和局部变量的概念
  11. SPOJ VLATTICE Visible Lattice Points 莫比乌斯反演
  12. 【总结】防病毒网关---防毒墙
  13. macos系统安装homebrew包管理工具
  14. unicloud云开发---uniapp云开发(一)---服务空间创建以及部署一个云函数
  15. esxi 服务器上登陆系统,实战:添加ESX/ESXi服务器到vCenter Server
  16. DELL 服务器 PCI-E 6IR 通道卡 6I阵列卡8口SAS SATA (整理)
  17. 简单地获得UTM坐标系
  18. 免疫沉淀常见问题解答 | MedChemExpress
  19. 计算机原理及硬件,计算机原理及硬件介绍
  20. 等级保护和分级保护有什么不一样?

热门文章

  1. 扬帆优配|翻倍牛股“高台跳水”,一度跌停,啥情况
  2. 多国金融监管和投资机构代表齐聚香港“HashKey2019数字资产全球峰会”,共探从“江湖”到“庙堂”之路...
  3. Python经典例题——超市买水果系统
  4. java的内容好复杂_Java I/O 好复杂,傻傻分不清楚,别担心,我们有线索了……...
  5. Linux系统安装盘制作
  6. 【转】想做「互联网金融」产品经理,如何开始学习?
  7. 【如何系统的学习it技术】
  8. 转:MSN君最后的十个瞬间
  9. 电脑开机提示BOOTMGR is compressed无法启动
  10. 各个保险中个人与企业所缴费的比例