3. Linux音频子系统架构
ALSA(Advanced Linux Sound Architecture)是 Linux 内核中标准的音频子系统,取代了早期的 OSS(Open Sound System)。ALSA 采用分层设计,将用户空间的音频应用与内核空间的硬件驱动解耦,同时通过 ASoC(ALSA System on Chip)框架为嵌入式 SoC 平台提供统一的音频驱动模型。
核心设计目标: 提供
mmap 零拷贝音频传输、硬件参数精确控制(采样率/格式/通道)、多音频设备并发访问、以及插件化的音频处理管道。
3.1 用户空间组件
用户空间是音频应用与 ALSA 交互的入口,核心组件包括 alsa-lib 库和命令行工具 aplay / arecord。
| 组件 | 功能描述 | 关键接口/文件 |
|---|---|---|
| alsa-lib | 提供 PCM 播放/录制、混音器控制、设备枚举、插件框架 | snd_pcm_open() / snd_pcm_writei() |
| aplay | 命令行 PCM 播放工具,支持 WAV/raw 格式 | aplay -D hw:0,0 test.wav |
| arecord | 命令行 PCM 录制工具,支持 WAV/raw 格式 | arecord -D hw:0,0 -f S16_LE -r 48000 test.wav |
| amixer | 混音器控制工具,调节音量/开关/路由 | amixer sset 'Master' 80% |
| aconnect | ALSA 序列器连接管理(MIDI 相关) | aconnect 20:0 128:0 |
调试技巧: 使用
arecord -l 列出所有录音设备,aplay -l 列出播放设备。speaker-test -t wav -c 2 可快速验证音频输出通道是否正常。
3.2 内核空间:ASoC 框架三组件
ASoC(ALSA System on Chip)是 ALSA 针对嵌入式 SoC 音频的扩展框架,将音频驱动拆分为三个独立组件:Codec 驱动、Platform 驱动、Machine 驱动。这种拆分实现了代码复用——同一个 Codec 驱动可跨多个平台使用,同一个 Platform 驱动可适配不同 Codec。
3.3 Codec 驱动
Codec 驱动负责管理音频编解码器芯片(如 WM8960、ES8316、TLV320AIC3x),核心职责包括:
- DAPM(动态音频电源管理): 根据音频流路径自动开关内部电源域,降低功耗。
- 混音器/音量控制: 通过 ALSA 控制接口暴露音量、静音、EQ 等参数。
- DAI 接口配置: 设置 I2S 格式(标准/左对齐/DSP)、位宽、主从模式。
- 时钟与采样率管理: 配置 PLL 分频、MCLK/LRCK 频率关系。
// Codec 驱动注册示例(简化)
static struct snd_soc_dai_driver wm8960_dai = {
.name = "wm8960-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
.probe = wm8960_probe,
.remove = wm8960_remove,
.controls = wm8960_snd_controls,
.num_controls = ARRAY_SIZE(wm8960_snd_controls),
.dapm_widgets = wm8960_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8960_dapm_widgets),
};
3.4 Platform 驱动
Platform 驱动代表 SoC 侧的音频接口控制器和 DMA 引擎,通常与具体 SoC 绑定(如 STM32 I2S、i.MX SAI、Rockchip I2S)。主要职责:
- CPU DAI 配置: 初始化 I2S/SAI/SPDIF 控制器寄存器,设置时钟极性、帧格式。
- DMA 传输管理: 配置 DMA 通道,实现音频数据在内存与 I2S FIFO 之间的搬运。
- PCM 操作集: 实现
open/close/hw_params/prepare/trigger/pointer回调。
// Platform 驱动 PCM 操作集示例
static const struct snd_pcm_ops stm32_i2s_pcm_ops = {
.open = stm32_i2s_pcm_open,
.close = stm32_i2s_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = stm32_i2s_pcm_hw_params,
.hw_free = stm32_i2s_pcm_hw_free,
.prepare = stm32_i2s_pcm_prepare,
.trigger = stm32_i2s_pcm_trigger,
.pointer = stm32_i2s_pcm_pointer,
.mmap = stm32_i2s_pcm_mmap,
};
3.5 Machine 驱动
Machine 驱动是板级绑定层,将 Codec 驱动和 Platform 驱动通过 snd_soc_dai_link 连接起来,并描述板级音频路由。Machine 驱动通常由设备树触发注册。
| 字段 | 说明 | 典型值 |
|---|---|---|
name |
DAI link 名称 | "wm8960-hifi" |
stream_name |
音频流名称 | "WM8960 HiFi" |
cpu_dai_name |
CPU 侧 DAI 名称 | "stm32-sai1-a" |
codec_dai_name |
Codec 侧 DAI 名称 | "wm8960-hifi" |
codec_name |
Codec 设备名称 | "wm8960.0-001a" |
ops |
DAI link 操作回调 | &wm8960_ops |
init |
初始化回调 | wm8960_machine_init |
// Machine 驱动 DAI link 定义示例
static struct snd_soc_dai_link stm32_wm8960_dai_links[] = {
{
.name = "WM8960",
.stream_name = "WM8960 HiFi",
.cpu_dai_name = "stm32-sai1-a",
.codec_dai_name = "wm8960-hifi",
.codec_name = "wm8960.0-001a",
.ops = &stm32_wm8960_ops,
.init = stm32_wm8960_machine_init,
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
};
static struct snd_soc_card stm32_wm8960_card = {
.name = "stm32-wm8960",
.owner = THIS_MODULE,
.dai_link = stm32_wm8960_dai_links,
.num_links = ARRAY_SIZE(stm32_wm8960_dai_links),
.controls = stm32_wm8960_controls,
.num_controls = ARRAY_SIZE(stm32_wm8960_controls),
.dapm_widgets = stm32_wm8960_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(stm32_wm8960_dapm_widgets),
.dapm_routes = stm32_wm8960_audio_map,
.num_dapm_routes = ARRAY_SIZE(stm32_wm8960_audio_map),
};
常见配置错误:
dai_fmt 中的主从模式必须与硬件连接一致。如果 Codec 作为 I2S 主设备(CBS_CFS),则 CPU DAI 必须配置为从模式;反之亦然。主从不匹配会导致无声音或时钟冲突。
3.6 音频数据流路径
从用户空间应用写入音频数据到 Codec 输出,完整路径如下:
- 应用层:
aplay调用snd_pcm_writei()写入 PCM 数据到 alsa-lib。 - 内核层: alsa-lib 通过
ioctl将数据送入内核 PCM 设备文件(/dev/snd/pcmC0D0p)。 - Platform 驱动: DMA 引擎将数据从内存搬运到 I2S 控制器 FIFO。
- CPU DAI: I2S 控制器按配置的位时钟和帧时钟将数据串行发送到总线。
- Codec DAI: Codec 芯片接收 I2S 信号,经 DAC 转换为模拟音频输出。
3.7 关键数据结构关系
ASoC 框架中几个核心数据结构的关系如下:
| 数据结构 | 所属组件 | 作用 |
|---|---|---|
snd_soc_card |
Machine | 声卡顶层描述,包含 DAI links、控件、DAPM 路由 |
snd_soc_dai_link |
Machine | 连接 CPU DAI 与 Codec DAI 的桥梁 |
snd_soc_dai_driver |
Codec / Platform | 描述 DAI 能力(格式、速率、操作回调) |
snd_soc_codec_driver |
Codec | Codec 设备驱动,包含控件、DAPM、寄存器访问 |
snd_soc_platform_driver |
Platform | Platform 设备驱动,包含 PCM 操作集、DMA 配置 |
snd_pcm_ops |
Platform | PCM 设备底层操作(open/hw_params/trigger/pointer) |
调试入口: 在
/proc/asound/card0/ 目录下可以查看声卡拓扑信息,包括 pcm0p(播放设备)、pcm0c(录制设备)、codec#0(Codec 寄存器转储)。使用 cat /proc/asound/cards 查看系统所有声卡。