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 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的调用过程;
- 在数据库上实现类似铁路售票锁票功能
- Android代码判断手机是否已root方法学习
- 实体类的枚举属性--原来支持枚举类型这么简单,没有EF5.0也可以
- Android中Context用法详解学习
- 【自然框架】之 “表单控件”与“实体类”
- Android数据存储实现的5大方式
- Android实现图片异步加载操作
- (PDF.NET框架实例讲解)将存储过程映射为实体类
- 【开源】QuickPager ASP.NET2.0分页控件——使用示例、基本应用和查询功能
- 移花接木:当泛型方法遇上抽象类----我的“内存数据库”诞生记
- Android扫描多媒体文件剖析
- Android的进程与线程使用总结
- Android在WebView中加载HTML并实现交互
- 使用ASP.NET MVC2+PDF.NET 构建一个简单的新闻管理程序 示例过程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python输出结果刷新及进度条的实现操作
- PHP实现微信申请退款功能
- python 读取.nii格式图像实例
- PHP实现八皇后算法
- 浅析php如何实现爬取数据原理
- Python 存取npy格式数据实例
- 微信支付之JSAPI公众号支付详解
- 浅谈keras中的batch_dot,dot方法和TensorFlow的matmul
- Python代码执行时间测量模块timeit用法解析
- Laravel框架实现简单的学生信息管理平台案例【附源码下载】
- numpy 矩阵形状调整:拉伸、变成一位数组的实例
- 使用Nibabel库对nii格式图像的读写操作
- ThinkPHP like模糊查询,like多匹配查询,between查询,in查询,一般查询书写方法
- 使用SimpleITK读取和保存NIfTI/DICOM文件实例
- php中上传文件的的解决方案