USB播放音乐知识记录
2018-01-04 15:00 wulizhi 阅读(1683) 评论(0) 编辑 收藏 举报一.
usb audio 在hal层中有单独的so文件,例如audio.usb.8909.so, 在创建AudioPolicyManager的时候会Load 该module。hal层中的文件都在hardware/libhardware/modules/usbaudio/目录下:
usb audio的播放流程和正常的播放流程并无不同,最主要的差异在于,音频是从AudioFlinger通过USB hal送到kernel中的。如下图:
在AudioFinger中调用open_output_stream的时候,调用的是usb hal文件中的open_output_stream, 具体定义在audio_hw.c中, hal层中的文件都在hardware/libhardware/modules/usbaudio/目录下。可以看到,和primary hal一样,它也提供标准的输入输出设备操作接口函数,调用方法和primary hal一样。
1 static int adev_open(const hw_module_t *module, const char *name, 2 hw_device_t **device) 3 { 4 adev->device.common.tag = HARDWARE_DEVICE_TAG; 5 adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; 6 adev->device.common.module = (struct hw_module_t *)module; 7 adev->device.common.close = adev_close; 8 9 adev->device.init_check = adev_init_check; 10 adev->device.set_voice_volume = adev_set_voice_volume; 11 adev->device.set_master_volume = adev_set_master_volume; 12 adev->device.get_master_volume = adev_get_master_volume; 13 adev->device.set_master_mute = adev_set_master_mute; 14 adev->device.get_master_mute = adev_get_master_mute; 15 adev->device.set_mode = adev_set_mode; 16 adev->device.set_mic_mute = adev_set_mic_mute; 17 adev->device.get_mic_mute = adev_get_mic_mute; 18 adev->device.set_parameters = adev_set_parameters; 19 adev->device.get_parameters = adev_get_parameters; 20 adev->device.get_input_buffer_size = adev_get_input_buffer_size; 21 adev->device.open_output_stream = adev_open_output_stream; 22 adev->device.close_output_stream = adev_close_output_stream; 23 adev->device.open_input_stream = adev_open_input_stream; 24 adev->device.close_input_stream = adev_close_input_stream; 25 adev->device.dump = adev_dump; 26 }
同样的,对于播放来说,它提供的也是标准的操作输出设备的接口函数:
1 static int adev_open_output_stream(struct audio_hw_device *dev, 2 audio_io_handle_t handle, 3 audio_devices_t devices, 4 audio_output_flags_t flags, 5 struct audio_config *config, 6 struct audio_stream_out **stream_out, 7 const char *address __unused) 8 { 9 out->stream.common.get_sample_rate = out_get_sample_rate; 10 out->stream.common.set_sample_rate = out_set_sample_rate; 11 out->stream.common.get_buffer_size = out_get_buffer_size; 12 out->stream.common.get_channels = out_get_channels; 13 out->stream.common.get_format = out_get_format; 14 out->stream.common.set_format = out_set_format; 15 out->stream.common.standby = out_standby; 16 out->stream.common.dump = out_dump; 17 out->stream.common.set_parameters = out_set_parameters; 18 out->stream.common.get_parameters = out_get_parameters; 19 out->stream.common.add_audio_effect = out_add_audio_effect; 20 out->stream.common.remove_audio_effect = out_remove_audio_effect; 21 out->stream.get_latency = out_get_latency; 22 out->stream.set_volume = out_set_volume; 23 out->stream.write = out_write; 24 out->stream.get_render_position = out_get_render_position; 25 out->stream.get_presentation_position = out_get_presentation_position; 26 out->stream.get_next_write_timestamp = out_get_next_write_timestamp; 27 }
我们重点看看out_write接口函数, AudioFlinger通过该接口函数,将音频数据从framework层搬运到hal层再送到Kernel中。
1 static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes) 2 { 3 /*如果暂停,则使能输出, 这里是调用pcm_open来实现的*/ 4 if (out->standby) { 5 ret = start_output_stream(out); 6 if (ret != 0) { 7 pthread_mutex_unlock(&out->dev->lock); 8 goto err; 9 } 10 out->standby = false; 11 } 12 13 /*调用pcm_write写入数据*/ 14 if (write_buff != NULL && num_write_buff_bytes != 0) { 15 proxy_write(&out->proxy, write_buff, num_write_buff_bytes); 16 } 17 }
可以看到,最终都是通过pcm接口函数来操作的。所以,在kernel侧对于USB audio是创建了对应的pcm设备的。
二.
kernel侧的USB audio驱动是在kernel/sound/usb/目录下,以card.c文件为入口查看:
在cards文件中,初始化的时候,注册了一个USB驱动, 将此驱动加入到USB驱动链表中,当有USB设备插入时,USB Host回去扫描链表,将驱动和设备进行匹配,匹配成功后,就会触发
probe函数的调用, 驱动创建成功后,在/sys/bus/usb/drivers/目录下面,可以看到snd-usb-audio目录。
1 static struct usb_driver usb_audio_driver = { 2 .name = "snd-usb-audio", 3 .probe = usb_audio_probe, 4 .disconnect = usb_audio_disconnect, 5 .suspend = usb_audio_suspend, 6 .resume = usb_audio_resume, 7 .id_table = usb_audio_ids, 8 .supports_autosuspend = 1, 9 };
在probe函数中执行了声卡的创建和音频接口的创建工作, 看下创建pcm函数的地方,其中stream的值根据USB描述符中USB的类型是USB_DIR_IN,则赋值为CAPTURE, USB_DIR_OUT
则赋值为PLAYBACK
1 int snd_usb_add_audio_stream(struct snd_usb_audio *chip, 2 int stream, 3 struct audioformat *fp) 4 { 5 /*创建pcm*/ 6 err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, 7 stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, 8 stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, 9 &pcm); 10 11 /*初始化stream接口函数*/ 12 snd_usb_init_substream(as, stream, fp); 13 }
snd_pcm_new()函数为标准的创建pcm设备的接口函数,该接口函数中根据stream的类型会分别创建playback_stream和capture_stream,并分别创建对应的playback的pcm接口和capture的接口:
标准的pcm接口操作函数为:
1 const struct file_operations snd_pcm_f_ops[2] = { 2 { 3 .owner = THIS_MODULE, 4 .write = snd_pcm_write, 5 .aio_write = snd_pcm_aio_write, 6 .open = snd_pcm_playback_open, 7 .release = snd_pcm_release, 8 .llseek = no_llseek, 9 .poll = snd_pcm_playback_poll, 10 .unlocked_ioctl = snd_pcm_playback_ioctl, 11 .compat_ioctl = snd_pcm_ioctl_compat, 12 .mmap = snd_pcm_mmap, 13 .fasync = snd_pcm_fasync, 14 .get_unmapped_area = snd_pcm_get_unmapped_area, 15 }, 16 { 17 .owner = THIS_MODULE, 18 .read = snd_pcm_read, 19 .aio_read = snd_pcm_aio_read, 20 .open = snd_pcm_capture_open, 21 .release = snd_pcm_release, 22 .llseek = no_llseek, 23 .poll = snd_pcm_capture_poll, 24 .unlocked_ioctl = snd_pcm_capture_ioctl, 25 .compat_ioctl = snd_pcm_ioctl_compat, 26 .mmap = snd_pcm_mmap, 27 .fasync = snd_pcm_fasync, 28 .get_unmapped_area = snd_pcm_get_unmapped_area, 29 } 30 };
我们再看看snd_usb_init_substream()函数:
1 static void snd_usb_init_substream(struct snd_usb_stream *as, 2 int stream, 3 struct audioformat *fp) 4 { 5 struct snd_usb_substream *subs = &as->substream[stream]; 6 7 INIT_LIST_HEAD(&subs->fmt_list); 8 spin_lock_init(&subs->lock); 9 10 subs->stream = as; //赋值为对应的usb_stream 11 subs->direction = stream; //方向取决于usb类型为usb_dir_in还是usb_dir_out 12 subs->dev = as->chip->dev; 13 subs->txfr_quirk = as->chip->txfr_quirk; 14 subs->speed = snd_usb_get_speed(subs->dev); 15 subs->pkt_offset_adj = 0; 16 17 snd_usb_set_pcm_ops(as->pcm, stream); //设置stream的操作接口函数 18 19 list_add_tail(&fp->list, &subs->fmt_list); 20 subs->formats |= fp->formats; 21 subs->num_formats++; 22 subs->fmt_type = fp->fmt_type; 23 subs->ep_num = fp->endpoint; 24 if (fp->channels > subs->channels_max) 25 subs->channels_max = fp->channels; 26 }
其中最重要的是snd_usb_set_pcm_ops()函数,它设置了pcm_substream的操作接口函数:
1 void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) 2 { 3 snd_pcm_set_ops(pcm, stream, 4 stream == SNDRV_PCM_STREAM_PLAYBACK ? 5 &snd_usb_playback_ops : &snd_usb_capture_ops); 6 }
1 static struct snd_pcm_ops snd_usb_playback_ops = { 2 .open = snd_usb_playback_open, 3 .close = snd_usb_playback_close, 4 .ioctl = snd_pcm_lib_ioctl, 5 .hw_params = snd_usb_hw_params, 6 .hw_free = snd_usb_hw_free, 7 .prepare = snd_usb_pcm_prepare, 8 .trigger = snd_usb_substream_playback_trigger, 9 .pointer = snd_usb_pcm_pointer, 10 .page = snd_pcm_lib_get_vmalloc_page, 11 .mmap = snd_pcm_lib_mmap_vmalloc, 12 }; 13 14 static struct snd_pcm_ops snd_usb_capture_ops = { 15 .open = snd_usb_capture_open, 16 .close = snd_usb_capture_close, 17 .ioctl = snd_pcm_lib_ioctl, 18 .hw_params = snd_usb_hw_params, 19 .hw_free = snd_usb_hw_free, 20 .prepare = snd_usb_pcm_prepare, 21 .trigger = snd_usb_substream_capture_trigger, 22 .pointer = snd_usb_pcm_pointer, 23 .page = snd_pcm_lib_get_vmalloc_page, 24 .mmap = snd_pcm_lib_mmap_vmalloc, 25 };
上面函数给pcm->substream->ops赋值,根据stream类型,分别赋值为snd_usb_playback_ops 或者 snd_usb_capture_ops:
类如,如果应用层调用pcm_write接口函数写入数据,则其调用的流程如下:
pcm_write调用的是ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x), 对应到kernel中,调用的就是上面的snd_pcm_playback_ioctl()函数:
1 static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, 2 unsigned long arg) 3 { 4 return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, 5 (void __user *)arg); 6 } 7 9 static int snd_pcm_playback_ioctl1(struct file *file, 10 struct snd_pcm_substream *substream, 11 unsigned int cmd, void __user *arg) 12 { 13 switch (cmd) { 14 case SNDRV_PCM_IOCTL_WRITEI_FRAMES: 15 { 16 struct snd_xferi xferi; 17 struct snd_xferi __user *_xferi = arg; 18 struct snd_pcm_runtime *runtime = substream->runtime; 19 snd_pcm_sframes_t result; 20 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 21 return -EBADFD; 22 if (put_user(0, &_xferi->result)) 23 return -EFAULT; 24 if (copy_from_user(&xferi, _xferi, sizeof(xferi))) 25 return -EFAULT; 26 result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); 27 __put_user(result, &_xferi->result); 28 return result < 0 ? result : 0; 29 } 30 } 31 } 32 33 snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 34 { 35 return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 36 snd_pcm_lib_write_transfer); 37 } 38 39 static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 40 unsigned long data, 41 snd_pcm_uframes_t size, 42 int nonblock, 43 transfer_f transfer) 44 { 45 err = transfer(substream, appl_ofs, data, offset, frames); 46 } 47 48 static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 49 unsigned int hwoff, 50 unsigned long data, unsigned int off, 51 snd_pcm_uframes_t frames) 52 { 53 struct snd_pcm_runtime *runtime = substream->runtime; 54 int err; 55 char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 56 if (substream->ops->copy) { 57 if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 58 return err; 59 } else { 60 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 61 if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 62 return -EFAULT; 63 } 64 return 0; 65 }
最终,如果定义了substream->ops->copy接口函数,则会调用substream->ops->copy执行数据的传输,否则,就将数据放到substream->runtime->dma_area + frames_to_bytes(runtime, hwoff) 地址处,在这里,是没有定义copy函数的,所以pcm_write写下来的数据都是放在substream->runtime->dma_area里面。
我们知道usb数据传输是通过urb完成的,程序中将数据拷贝到urb中,再提交给主控制器,由主控制器来进行实际的数据传输,这里也是一样的。
在hw_params设置参数的函数中调用snd_usb_init_substream_urbs()为substream分配初始化urb.
1 int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, 2 unsigned int period_bytes, 3 unsigned int rate, 4 unsigned int frame_bits) 5 { 6 /*根据上面计算的urb的个数和大小,来创建urb*/ 7 /* allocate and initialize data urbs */ 8 for (i = 0; i < subs->nurbs; i++) { 9 struct snd_urb_ctx *u = &subs->dataurb[i]; 10 u->index = i; 11 u->subs = subs; 12 u->packets = (i + 1) * total_packs / subs->nurbs 13 - i * total_packs / subs->nurbs; 14 u->buffer_size = maxsize * u->packets; 15 if (subs->fmt_type == UAC_FORMAT_TYPE_II) 16 u->packets++; /* for transfer delimiter */ 17 u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); 18 if (!u->urb) 19 goto out_of_memory; 20 u->urb->transfer_buffer = 21 usb_alloc_coherent(subs->dev, u->buffer_size, 22 GFP_KERNEL, &u->urb->transfer_dma); 23 if (!u->urb->transfer_buffer) 24 goto out_of_memory; 25 u->urb->pipe = subs->datapipe; 26 u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; 27 u->urb->interval = 1 << subs->datainterval; 28 u->urb->context = u; 29 u->urb->complete = snd_complete_urb; //urb传输完成后会调用此函数 30 } 31 }
函数一开始的大段程序,都是根据当前写入的buffer大小,采样率,采样精度,声道数,来计算需要的urb的大小和个数,之后就开始创建urb,注意到最后一个complete成员,这个是个回调函数数,在urb传输完成后会调用此函数,在这个函数中会重新准备urb中的数据,继续提交urb给USB总控制器。
1 static void snd_complete_urb(struct urb *urb) 2 { 3 struct snd_urb_ctx *ctx = urb->context; 4 struct snd_usb_substream *subs = ctx->subs; 5 struct snd_pcm_substream *substream = ctx->subs->pcm_substream; 6 int err = 0; 7 8 /*准备urb中的数据,继续提交urb*/ 9 if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || 10 !subs->running || /* can be stopped during retire callback */ 11 (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || 12 (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { 13 clear_bit(ctx->index, &subs->active_mask); 14 if (err < 0) { 15 snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); 16 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 17 } 18 } 19 }
我们整理下整个播放流程:
hal层中:
开始播放: out_write(audio_hw.c) ==> start_output_stream(audio_hw.c) ==> proxy_open(alsa_device_proxy.c) ==> pcm_open() 播放: out_write(audio_hw.c) ==> proxy_write(alsa_device_proxy.c) ==> pcm_write() 暂停和停止播放: out_standby(audio_hw.c) ==> proxy_close(alsa_device_proxy.c) ==> pcm_close()
tinyalsa层中:
开始播放:
pcm_open(pcm.c) ==> open(fn, O_RDWR); ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms); ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams);
播放: pcm_write(pcm.c) ==> /*第一次执行pcm_prepare*/ pcm_prepare(pcm); ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x);
暂停和停止播放: pcm_close(pcm.c) ==> close(pcm->fd);
kernel中:
函数中调用prepare_playback_urb()准备urb中的数据,然后叫urb提交给USB总控制器进行数据传输。其实,大概可以想象得到,在prepare_playback_urb()中,我们是将 上面存放音频数据的
substream->runtime->dma_area中的数据拷贝到urb的buffer中。我们看看prepare_playback_urb()函数。