USB播放音樂知識記錄


一.  

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()函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM