asoc 设计思路

参数
1 表明支持哪些参数 2 根据指定的参数 进行硬件设置

使用strace 命令查询执行流程

ANDROID TINYPLAY与TINYCAP流程
​​​ https://www.freesion.com/article/7678406915/​

Android下的音频通道配置文件mixer_paths.xml​

【Audio driver】mixer_paths.xml文件分析​

xml 里面的数据,当其中一个设备节点被改变,那会追踪相关的设备节点一起改变。

audiohardware class作为向下访问的接口 (由厂家提供)
派生自 audiohardwarebase audiohard interface

audiohardware 里面有有 audio_stream_out 还有 audio_stream_in
我感觉 audio_stream_out 这个得到是 pcm_open 得到的呢

Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数​

tinyalsa 里面有 pcm_open pcm_write pcm_read 还有设置 kconfig 的函数

1 驱动程序分配的buffer

2 app 不断写入 一个 period 的数据到buffer
period 里面有多个 frame ,一个 frame 就是一个采样率

3 驱动不断从 buffer 里面 取出 一个 period 的数据送到 声卡
判断
首先 period 是一个dma 的大小

4 pcm_new 是创建声卡时候被调用,很适合申请 dma buff

5 dma 需要虚拟地址 物理地址 size大小

6 申请的buffer,是一个 环形buffer,所以有需要有一个描述的读写的状态

7 prepare 每次调用 snd_pcm_prepare 都会使用这个函数段 ,在这个函数里面会设置硬件参数。

8 trigger 在 pcm 开始 停止 暂停 都会调用它

9 会在 dma 中断回调函数中执行

/* 更新hw_ptr等信息,
* 并且判断:如果buffer里没有数据了,则调用trigger来停止DMA
*/
snd_pcm_period_elapsed(substream);

10 snd_pcm_period_elapsed 会调用 pointer 函数字段来更新地址
11 pointer 函数字段会调用 bytes_to_frames 把 更新的 标号
12 bytes_to_frames 第二个参数的 标号 要手动实时更新
13 传输数据 用的是 ioctl
调用 snd_pcm_ioctl_compat
然后 会调用
snd_pcm_playback_ioctl1
snd_pcm_capture_ioctl1
然后调用
copy_from_user

14 数据是放到 dma buff 里面的

15 当 使用 arecord,控制节点会 使用 ioctl , pcm 也会 ioctl
但是控制节点,不涉及到 硬件操作

16 sndrv_pcm_ioctl_sync_ptr (应该大写,当写入数据以后,就应该更新地址) 指针同步

17 SNDRV_PCM_IOCTL_WRITEL_FRAMES (当调用这个 cmd 就会实现数据的传输)会使用 copy_from_user 把数据与 dma buff 交互

18
case SNDRV_PCM_IOCTL_READI_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_read(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < 0 ? result : 0;
}

上面会调用 snd_pcm_lib_read
snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
{
struct snd_pcm_runtime *runtime;
int nonblock;
int err;

err = pcm_sanity_check(substream);
if (err < 0)
return err;
runtime = substream->runtime;
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);

}
上面调用 snd_pcm_lib_read_transfer
static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
}
会调用 copy_to_user 读到 用户层

19 类比上面的
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
}
copy_from_user 把用户层的数据 搞到 dma buff 里面

20 snd_soc_pcm_runtime 什么时间创建,同时代表的什么意思
1 当底层调用了snd_soc_register_card 时, ASoC核心层会从全局链表中找到dai_link指定的platform、
cpu_dai、codec_dai、codec,


snd_soc_register_card
snd_soc_instantiate_cards


2 并建立一个struct snd_soc_pcm_runtime来保存这些对应关系.