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, ¶ms) //设置音频硬件参数
对于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;
}
参考: