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:
const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .write = snd_pcm_write, .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_playback_poll, .unlocked_ioctl = snd_pcm_playback_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, .read = snd_pcm_read, .aio_read = snd_pcm_aio_read, .open = snd_pcm_capture_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_capture_poll, .unlocked_ioctl = snd_pcm_capture_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, } };
我们从snd_pcm_playback_open函数开始向下分析:
static int snd_pcm_playback_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; int err = nonseekable_open(inode, file); if (err < 0) return err; pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK); //取得其私有数据并返回的 err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); if (pcm) snd_card_unref(pcm->card); //减少设备对象的引用计数 snd_card_unref(card); return err; }
在下面调用了snd_pcm_open函数:
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) { int err; wait_queue_t wait; if (pcm == NULL) { err = -ENODEV; goto __error1; } err = snd_card_file_add(pcm->card, file); if (err < 0) goto __error1; if (!try_module_get(pcm->card->module)) { err = -EFAULT; goto __error2; } init_waitqueue_entry(&wait, current); add_wait_queue(&pcm->open_wait, &wait); mutex_lock(&pcm->open_mutex); while (1) { err = snd_pcm_open_file(file, pcm, stream); // 将操作该声卡card的应用程序添加到card->files_list if (err >= 0) break; if (err == -EAGAIN) { if (file->f_flags & O_NONBLOCK) { err = -EBUSY; break; } } else break; set_current_state(TASK_INTERRUPTIBLE); mutex_unlock(&pcm->open_mutex); schedule(); mutex_lock(&pcm->open_mutex); if (pcm->card->shutdown) { err = -ENODEV; break; } if (signal_pending(current)) { err = -ERESTARTSYS; break; } } remove_wait_queue(&pcm->open_wait, &wait); mutex_unlock(&pcm->open_mutex); if (err < 0) goto __error; return err; __error: module_put(pcm->card->module); __error2: snd_card_file_remove(pcm->card, file); __error1: return err; }
再从snd_pcm_open_file继续向下看:
static int snd_pcm_open_file(struct file *file, struct snd_pcm *pcm, int stream) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; int err; err = snd_pcm_open_substream(pcm, stream, file, &substream); //打开substream结构体 if (err < 0) return err; pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); if (pcm_file == NULL) { snd_pcm_release_substream(substream); return -ENOMEM; } pcm_file->substream = substream; if (substream->ref_count == 1) { substream->file = pcm_file; substream->pcm_release = pcm_release_private; } file->private_data = pcm_file; return 0; }
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream) { struct snd_pcm_substream *substream; int err; err = snd_pcm_attach_substream(pcm, stream, file, &substream); if (err < 0) return err; if (substream->ref_count > 1) { *rsubstream = substream; return 0; } err = snd_pcm_hw_constraints_init(substream); //初始化substream结构体 if (err < 0) { snd_printd("snd_pcm_hw_constraints_init failed\n"); goto error; } if ((err = substream->ops->open(substream)) < 0) goto error; substream->hw_opened = 1; err = snd_pcm_hw_constraints_complete(substream); if (err < 0) { snd_printd("snd_pcm_hw_constraints_complete failed\n"); goto error; } *rsubstream = substream; return 0; error: snd_pcm_release_substream(substream); return err; }snd_pcm_open_substream
在snd_pcm_open_substream函数中:
if ((err = substream->ops->open(substream)) < 0) // substream->ops : snd_pcm_ops结构体 goto error;
依次调用cpu_dai, dma, codec_dai, machine(三大模块)的open或startup函数;
msm_mi2s_snd_startup函数:
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 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:
{ .owner = THIS_MODULE, .write = snd_pcm_write, .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_playback_poll, .unlocked_ioctl = snd_pcm_playback_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, },
从snd_pcm_playback_ioctl函数向下看:
static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_pcm_file *pcm_file; pcm_file = file->private_data; //获取相应的私有数据 if ((((cmd >> 8) & 0xff) != 'A') && (((cmd >> 8) & 0xff) != 'C')) return -ENOTTY; return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, (void __user *)arg); }
snd_pcm_playback_ioctl1:
static int snd_pcm_playback_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { if (snd_BUG_ON(!substream)) return -ENXIO; if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) return -EINVAL; //根据case不同,对播放进行相应的不同操作 switch (cmd) { case SNDRV_PCM_IOCTL_WRITEI_FRAMES: { struct snd_xferi xferi; struct snd_xferi __user *_xferi = arg; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t result; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (put_user(0, &_xferi->result)) return -EFAULT; if (copy_from_user(&xferi, _xferi, sizeof(xferi))) return -EFAULT; result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); __put_user(result, &_xferi->result); return result < 0 ? result : 0; } case SNDRV_PCM_IOCTL_WRITEN_FRAMES: { struct snd_xfern xfern; struct snd_xfern __user *_xfern = arg; struct snd_pcm_runtime *runtime = substream->runtime; void __user **bufs; snd_pcm_sframes_t result; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (runtime->channels > 128) return -EINVAL; if (put_user(0, &_xfern->result)) return -EFAULT; if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels); if (IS_ERR(bufs)) return PTR_ERR(bufs); result = snd_pcm_lib_writev(substream, bufs, xfern.frames); kfree(bufs); __put_user(result, &_xfern->result); return result < 0 ? result : 0; } case SNDRV_PCM_IOCTL_REWIND: { snd_pcm_uframes_t frames; snd_pcm_uframes_t __user *_frames = arg; snd_pcm_sframes_t result; if (get_user(frames, _frames)) return -EFAULT; if (put_user(0, _frames)) return -EFAULT; result = snd_pcm_playback_rewind(substream, frames); __put_user(result, _frames); return result < 0 ? result : 0; } case SNDRV_PCM_IOCTL_FORWARD: { snd_pcm_uframes_t frames; snd_pcm_uframes_t __user *_frames = arg; snd_pcm_sframes_t result; if (get_user(frames, _frames)) return -EFAULT; if (put_user(0, _frames)) return -EFAULT; result = snd_pcm_playback_forward(substream, frames); __put_user(result, _frames); return result < 0 ? result : 0; } } return snd_pcm_common_ioctl1(file, substream, cmd, arg); }
从snd_pcm_common_ioctl1继续分析,进入函数的prepare中:
当函数prepare完毕后,就一切准备就绪了,只等一个trigger;而trigger的执行会在上层的alsalib调用write的函数触发;prepare过程可以看下图,具体就不继续分析了:
下一节我们将来分析tinymixer的调用过程;
- alsa声卡分析alsa-utils调用过程(二)-tinymixer
- 函数调用的过程stack动态分析
- 进程调度 以及softirq中定时器会调用的进程调度过程分析
- 通过分析system_call中断处理过程来深入理解系统调用
- Openstack Nova 源码分析 — RPC 远程调用过程
- ALSA声卡07_分析调用过程_学习笔记
- 存储过程调用权限浅分析
- c函数调用过程原理及函数栈帧分析
- java jni调用过程分析
- 第26讲 js函数调用过程内存分析 js函数细节
- 驱动调试之段错误分析_根据栈信息确定函数调用过程
- c函数调用过程原理及函数栈帧分析
- 窥探 kernel,just for fun --- 系统调用过程分析
- c函数调用过程原理及函数栈帧分析
- IDA 汇编命令分析以及函数调用过程
- C/C++函数调用过程分析
- java jni调用过程分析
- 韩顺平_轻松搞定网页设计(html+css+javascript)_第26讲_js函数调用过程内存分析_js函数细节_学习笔记_源代码图解_PPT文档整理
- C++函数调用过程深入分析<转>
- Linux内核:基于int指令的经典系统调用过程分析