part 6: 声卡和PCM设备的建立过程

前面几章分析了Codec、Platform、Machine驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知。接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备。

PCM逻辑设备,我们又习惯称之为PCM中间层或pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝;往下是触发codec、platform、machine的操作函数,实现音频数据在dma_buffer<-> cpu_dai <-> codec之间的传输。

后面章节将会对这个过程详细分析,这里还是先从声卡的注册谈起。

声卡驱动中,一般挂载着多个逻辑设备,看看我们计算机的声卡驱动有几个逻辑设备:

$ cat /proc/asound/devices 1:: sequencer 2: [ 0- 7]: digital audio playback 3: [ 0- 3]: digital audio playback 4: [ 0- 2]: digital audio capture 5: [ 0- 0]: digital audio playback 6: [ 0- 0]: digital audio capture 7: [ 0- 3]: hardware dependent 8: [ 0- 0]: hardware dependent 9: [ 0] : control 33:: timer

· digital audio playback:用于回放的PCM设备

· digital audio capture:用于录制的PCM设备

· control:用于声卡控制的CTL设备,如通路控制、音量调整等

· timer:定时器设备

· sequencer:音序器设备

嵌入式系统中,通常我们更关心PCM和CTL这两种设备。

设备节点如下:

$ ll /dev/snddrwxr-xr-x 3 root root260 Feb 26 13:59 ./drwxr-xr-x 16 root root4300 Mar 6 17:07 ../drwxr-xr-x 2 root root60 Feb 26 13:59 by-path/crw-rw—T+ 1 root audio 116, 9 Feb 26 13:59 controlC0crw-rw—T+ 1 root audio 116, 8 Feb 26 13:59 hwC0D0crw-rw—T+ 1 root audio 116, 7 Feb 26 13:59 hwC0D3crw-rw—T+ 1 root audio 116, 6 Feb 26 13:59 pcmC0D0ccrw-rw—T+ 1 root audio 116, 5 Mar 6 19:08 pcmC0D0pcrw-rw—T+ 1 root audio 116, 4 Feb 26 13:59 pcmC0D2ccrw-rw—T+ 1 root audio 116, 3 Feb 26 13:59 pcmC0D3pcrw-rw—T+ 1 root audio 116, 2 Feb 26 13:59 pcmC0D7pcrw-rw—T+ 1 root audio 116, 1 Feb 26 13:59 seqcrw-rw—T+ 1 root audio 116, 33 Feb 26 13:59 timer

可以看到这些设备节点的Major=116,Minor则与/proc/asound/devices所列的对应起来,都是字符设备。上层可以通过open/close/read/write/ioctl等系统调用来操作声卡设备,和其他字符设备类似,但一般情况下我们使用已封装好的用户接口库如tinyalsa、alsa-lib。

6.1. 声卡结构概述

回顾下ASoC是如何注册声卡的,详细请参考章节5.ASoC machine driver,这里仅简单陈述下:

· Machine驱动初始化时,name="soc-audio"的platform_device与platform_driver匹配成功,触发soc_probe()调用;

· 继而调用snd_soc_register_card(),该函数做的事情很多:

1.为每个音频物理链路找到对应的codec、codec_dai、cpu_dai、platform设备实例,完成dai_link的绑定;

2.调用snd_card_create()创建声卡;

3.依次回调cpu_dai、codec、platform、codec_dai的probe()函数,完成物理设备的初始化;

· 随后调用soc_new_pcm()创建pcm逻辑设备:

1.设置pcm native中要使用的pcm操作函数,这些函数用于操作音频物理设备,包括machine、codec_dai、cpu_dai、platform;

2.调用snd_pcm_new()创建pcm设备,回放子流实例和录制子流实例都在这里创建;

3.回调platform驱动的pcm_new(),完成音频dma设备初始化和dma buffer内存分配;

· 最后调用snd_card_register()注册声卡。

关于音频物理设备部分(Codec/Platform/Machine)不再累述,下面详细分析声卡和PCM逻辑设备的注册过程。

上面提到声卡驱动上挂着多个逻辑子设备,有pcm(音频数据流)、control(混音器控制)、midi(迷笛)、timer(定时器)、sequencer(音序器)等。

+———–+| snd_card |+———–+| | |+———–+ | +————+||| +———–+ +———–+ +———–+ | snd_pcm | |snd_control| | snd_timer | … +———–+ +———–+ +———–+

这些与声音相关的逻辑设备都在结构体snd_card管理之下,可以说snd_card是alsa中最顶层的结构。

我们再看看alsa声卡驱动的大致结构图(不是严格的UML类图,有结构体定义、模块关系、函数调用,方便标示结构模块的层次及关系):

snd_cards:记录着所注册的声卡实例,每个声卡实例有着各自的逻辑设备,如PCM设备、CTL设备、MIDI设备等,并一一记录到snd_card的devices链表上。

snd_minors:记录着所有逻辑设备的上下文信息,它是声卡逻辑设备与系统调用API之间的桥梁;每个snd_minor在逻辑设备注册时被填充,在逻辑设备使用时就可以从该结构体中得到相应的信息(主要是系统调用函数集file_operations)。

6.2. 声卡的创建

所有的胜利,与征服自己的胜利比起来,都是微不足道

part 6: 声卡和PCM设备的建立过程

相关文章:

你感兴趣的文章:

标签云: