3. Linux音频子系统架构

ALSA(Advanced Linux Sound Architecture)是 Linux 内核中标准的音频子系统,取代了早期的 OSS(Open Sound System)。ALSA 采用分层设计,将用户空间的音频应用与内核空间的硬件驱动解耦,同时通过 ASoC(ALSA System on Chip)框架为嵌入式 SoC 平台提供统一的音频驱动模型。

核心设计目标: 提供 mmap 零拷贝音频传输、硬件参数精确控制(采样率/格式/通道)、多音频设备并发访问、以及插件化的音频处理管道。
ALSA 音频子系统分层架构 用户空间 (User Space) 应用程序 (aplay / arecord / 媒体播放器 / VoIP) ALSA 库 (alsa-lib) — 插件 / PCM 接口 / 混音器 / 控制接口 内核空间 (Kernel Space) ASoC 核心层 (Core) PCM 设备注册 / DAPM 电源管理 / 动态路由 / 控制接口 Codec 驱动 音频编解码器控制 Platform 驱动 DMA / CPU DAI 接口 Machine 驱动 板级设备绑定 / 路由 硬件层 (Hardware) I2S 总线 / SPI / I2C 控制 / DMA 控制器 / 音频 Codec 芯片

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。

ASoC 三组件协作关系 Machine 驱动 snd_soc_dai_link Codec 驱动 snd_soc_codec_driver Platform 驱动 snd_soc_platform_driver Codec DAI I2S 发送/接收 CPU DAI I2S 控制器

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 输出,完整路径如下:

  1. 应用层: aplay 调用 snd_pcm_writei() 写入 PCM 数据到 alsa-lib。
  2. 内核层: alsa-lib 通过 ioctl 将数据送入内核 PCM 设备文件(/dev/snd/pcmC0D0p)。
  3. Platform 驱动: DMA 引擎将数据从内存搬运到 I2S 控制器 FIFO。
  4. CPU DAI: I2S 控制器按配置的位时钟和帧时钟将数据串行发送到总线。
  5. Codec DAI: Codec 芯片接收 I2S 信号,经 DAC 转换为模拟音频输出。
音频数据流路径(播放方向) 应用 alsa-lib Platform DMA CPU DAI (I2S) Codec DAC 录制方向路径相反:Codec ADC → CPU DAI (I2S) → Platform DMA → alsa-lib → 应用

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 查看系统所有声卡。