USB播放音乐知识记录 - wulizhi

时间:2024-03-10 07:32:54

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, &params); 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()函数。