zoukankan      html  css  js  c++  java
  • alsa声卡分析alsa-utils调用过程(一)-tinyplay

    如何分析tinyplay 播放音频和tinymix的过程?需要相应的工具来支持追查;

    一、分析tinyplay和tinymix:

    1.1 利用strace工具:

    strace -o tinyplay.log tinyplay 1.wav

    strace -o tinymixer.log tinymixer "SEC_MI2S_RX Audio Mixer MultiMedia1" 1

     利用strace工具获取APP的log,从应用层往下看;

    1.2 分析alsa-utils源码:

    tiny工具源码在android/external/tinyalsa目录下;

    二、tinyplay调用分析(tinyplay.log搜索设备节点“/dev/snd/pcmC0D0p”)

    2.1 tinyplay的open过程:

    snd_pcm_f_ops[0]是播放音频的file_operations,snd_pcm_f_ops[1]则是录音的file_operations:

     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_pcm_playback_open函数开始向下分析:

     1 static int snd_pcm_playback_open(struct inode *inode, struct file *file)
     2 {
     3     struct snd_pcm *pcm;
     4     int err = nonseekable_open(inode, file);
     5     if (err < 0)
     6         return err;
     7     pcm = snd_lookup_minor_data(iminor(inode),
     8                     SNDRV_DEVICE_TYPE_PCM_PLAYBACK);  //取得其私有数据并返回的
     9     err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
    10     if (pcm)
    11         snd_card_unref(pcm->card);      //减少设备对象的引用计数 snd_card_unref(card);
    12     return err;
    13 }

    在下面调用了snd_pcm_open函数:

     1 static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
     2 {
     3     int err;
     4     wait_queue_t wait;
     5 
     6     if (pcm == NULL) {
     7         err = -ENODEV;
     8         goto __error1;
     9     }
    10     err = snd_card_file_add(pcm->card, file);
    11     if (err < 0)
    12         goto __error1;
    13     if (!try_module_get(pcm->card->module)) {
    14         err = -EFAULT;
    15         goto __error2;
    16     }
    17     init_waitqueue_entry(&wait, current);
    18     add_wait_queue(&pcm->open_wait, &wait);
    19     mutex_lock(&pcm->open_mutex);
    20     while (1) {
    21         err = snd_pcm_open_file(file, pcm, stream);  // 将操作该声卡card的应用程序添加到card->files_list
    22         if (err >= 0)
    23             break;
    24         if (err == -EAGAIN) {
    25             if (file->f_flags & O_NONBLOCK) {
    26                 err = -EBUSY;
    27                 break;
    28             }
    29         } else
    30             break;
    31         set_current_state(TASK_INTERRUPTIBLE);
    32         mutex_unlock(&pcm->open_mutex);
    33         schedule();
    34         mutex_lock(&pcm->open_mutex);
    35         if (pcm->card->shutdown) {
    36             err = -ENODEV;
    37             break;
    38         }
    39         if (signal_pending(current)) {
    40             err = -ERESTARTSYS;
    41             break;
    42         }
    43     }
    44     remove_wait_queue(&pcm->open_wait, &wait);
    45     mutex_unlock(&pcm->open_mutex);
    46     if (err < 0)
    47         goto __error;
    48     return err;
    49 
    50       __error:
    51     module_put(pcm->card->module);
    52       __error2:
    53           snd_card_file_remove(pcm->card, file);
    54       __error1:
    55           return err;
    56 }

     再从snd_pcm_open_file继续向下看:

     1 static int snd_pcm_open_file(struct file *file,
     2                  struct snd_pcm *pcm,
     3                  int stream)
     4 {
     5     struct snd_pcm_file *pcm_file;
     6     struct snd_pcm_substream *substream;
     7     int err;
     8 
     9     err = snd_pcm_open_substream(pcm, stream, file, &substream);    //打开substream结构体
    10     if (err < 0)
    11         return err;
    12 
    13     pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
    14     if (pcm_file == NULL) {
    15         snd_pcm_release_substream(substream);
    16         return -ENOMEM;
    17     }
    18     pcm_file->substream = substream;
    19     if (substream->ref_count == 1) {
    20         substream->file = pcm_file;
    21         substream->pcm_release = pcm_release_private;
    22     }
    23     file->private_data = pcm_file;
    24 
    25     return 0;
    26 }
     1 int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
     2                struct file *file,
     3                struct snd_pcm_substream **rsubstream)
     4 {
     5     struct snd_pcm_substream *substream;
     6     int err;
     7 
     8     err = snd_pcm_attach_substream(pcm, stream, file, &substream);
     9     if (err < 0)
    10         return err;
    11     if (substream->ref_count > 1) {
    12         *rsubstream = substream;
    13         return 0;
    14     }
    15 
    16     err = snd_pcm_hw_constraints_init(substream);    //初始化substream结构体
    17     if (err < 0) {
    18         snd_printd("snd_pcm_hw_constraints_init failed
    ");
    19         goto error;
    20     }
    21 
    22     if ((err = substream->ops->open(substream)) < 0)
    23         goto error;
    24 
    25     substream->hw_opened = 1;
    26 
    27     err = snd_pcm_hw_constraints_complete(substream);
    28     if (err < 0) {
    29         snd_printd("snd_pcm_hw_constraints_complete failed
    ");
    30         goto error;
    31     }
    32 
    33     *rsubstream = substream;
    34     return 0;
    35 
    36  error:
    37     snd_pcm_release_substream(substream);
    38     return err;
    39 }
    snd_pcm_open_substream

     在snd_pcm_open_substream函数中:

    1     if ((err = substream->ops->open(substream)) < 0)    // substream->ops : snd_pcm_ops结构体
    2         goto error;

     依次调用cpu_dai, dma, codec_dai, machine(三大模块)的open或startup函数;

    msm_mi2s_snd_startup函数:

    1 struct snd_soc_pcm_runtime *rtd = substream->private_data;
    2 struct snd_soc_card *card = rtd->card;
    3 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    4 struct snd_soc_codec *codec = rtd->codec;

    设置snd_soc_pcm_runtime的cpu、codec等模块;然后在snd_soc_pcm_runtime函数中对codec函数进行相应的设置,之后通过音频数据流通道播放出声音;

    调用过程如下图:

    2.2 tinyplay的ioctl过程:

     同样也是snd_pcm_f_ops[0]结构体的file_operations:

     1 {
     2         .owner =        THIS_MODULE,
     3         .write =        snd_pcm_write,
     4         .aio_write =        snd_pcm_aio_write,
     5         .open =            snd_pcm_playback_open,
     6         .release =        snd_pcm_release,
     7         .llseek =        no_llseek,
     8         .poll =            snd_pcm_playback_poll,
     9         .unlocked_ioctl =    snd_pcm_playback_ioctl,
    10         .compat_ioctl =     snd_pcm_ioctl_compat,
    11         .mmap =            snd_pcm_mmap,
    12         .fasync =        snd_pcm_fasync,
    13         .get_unmapped_area =    snd_pcm_get_unmapped_area,
    14 },

    从snd_pcm_playback_ioctl函数向下看:

     1 static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
     2                    unsigned long arg)
     3 {
     4     struct snd_pcm_file *pcm_file;
     5 
     6     pcm_file = file->private_data;    //获取相应的私有数据
     7 
     8     if ((((cmd >> 8) & 0xff) != 'A') && (((cmd >> 8) & 0xff) != 'C'))
     9         return -ENOTTY;
    10 
    11     return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
    12                        (void __user *)arg);
    13 }

    snd_pcm_playback_ioctl1:

     1 static int snd_pcm_playback_ioctl1(struct file *file,
     2                    struct snd_pcm_substream *substream,
     3                    unsigned int cmd, void __user *arg)
     4 {
     5     if (snd_BUG_ON(!substream))
     6         return -ENXIO;
     7     if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
     8         return -EINVAL;
     9         //根据case不同,对播放进行相应的不同操作
    10     switch (cmd) {
    11     case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
    12     {
    13         struct snd_xferi xferi;
    14         struct snd_xferi __user *_xferi = arg;
    15         struct snd_pcm_runtime *runtime = substream->runtime;
    16         snd_pcm_sframes_t result;
    17         if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
    18             return -EBADFD;
    19         if (put_user(0, &_xferi->result))
    20             return -EFAULT;
    21         if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
    22             return -EFAULT;
    23         result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
    24         __put_user(result, &_xferi->result);
    25         return result < 0 ? result : 0;
    26     }
    27     case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
    28     {
    29         struct snd_xfern xfern;
    30         struct snd_xfern __user *_xfern = arg;
    31         struct snd_pcm_runtime *runtime = substream->runtime;
    32         void __user **bufs;
    33         snd_pcm_sframes_t result;
    34         if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
    35             return -EBADFD;
    36         if (runtime->channels > 128)
    37             return -EINVAL;
    38         if (put_user(0, &_xfern->result))
    39             return -EFAULT;
    40         if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
    41             return -EFAULT;
    42 
    43         bufs = memdup_user(xfern.bufs,
    44                    sizeof(void *) * runtime->channels);
    45         if (IS_ERR(bufs))
    46             return PTR_ERR(bufs);
    47         result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
    48         kfree(bufs);
    49         __put_user(result, &_xfern->result);
    50         return result < 0 ? result : 0;
    51     }
    52     case SNDRV_PCM_IOCTL_REWIND:
    53     {
    54         snd_pcm_uframes_t frames;
    55         snd_pcm_uframes_t __user *_frames = arg;
    56         snd_pcm_sframes_t result;
    57         if (get_user(frames, _frames))
    58             return -EFAULT;
    59         if (put_user(0, _frames))
    60             return -EFAULT;
    61         result = snd_pcm_playback_rewind(substream, frames);
    62         __put_user(result, _frames);
    63         return result < 0 ? result : 0;
    64     }
    65     case SNDRV_PCM_IOCTL_FORWARD:
    66     {
    67         snd_pcm_uframes_t frames;
    68         snd_pcm_uframes_t __user *_frames = arg;
    69         snd_pcm_sframes_t result;
    70         if (get_user(frames, _frames))
    71             return -EFAULT;
    72         if (put_user(0, _frames))
    73             return -EFAULT;
    74         result = snd_pcm_playback_forward(substream, frames);
    75         __put_user(result, _frames);
    76         return result < 0 ? result : 0;
    77     }
    78     }
    79     return snd_pcm_common_ioctl1(file, substream, cmd, arg);
    80 }

    从snd_pcm_common_ioctl1继续分析,进入函数的prepare中:

    当函数prepare完毕后,就一切准备就绪了,只等一个trigger;而trigger的执行会在上层的alsalib调用write的函数触发;prepare过程可以看下图,具体就不继续分析了:

    下一节我们将来分析tinymixer的调用过程;

    alsa声卡分析alsa-utils调用过程(二)-tinymixer

  • 相关阅读:
    无线鼠标换电池了
    Jython Interactive Servlet Console YOU WILL NEVER KNOW IT EXECLLENT!!! GOOD
    Accessing Jython from Java Without Using jythonc
    jython podcast cool isnt't it?
    Python里pycurl使用记录
    Creating an Interactive JRuby Console for the Eclipse Environment
    微软为AJAX和jQuery类库提供CDN服务
    Download A File Using Cygwin and cURL
    What is JMRI?这个是做什么用的,我真没看懂但看着又很强大
    用curl 发送指定的大cookie的http/https request
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/8515277.html
Copyright © 2011-2022 走看看