alsa声卡分析alsa-utils调用过程(一)-tinyplay

时间:2022-05-06
本文章向大家介绍alsa声卡分析alsa-utils调用过程(一)-tinyplay,主要内容包括一、分析tinyplay和tinymix:、1.2 分析alsa-utils源码:、二、tinyplay调用分析(tinyplay.log搜索设备节点“/dev/snd/pcmC0D0p”)、2.2 tinyplay的ioctl过程:、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

如何分析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 failedn");
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 failedn");
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函数中:

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的调用过程;