1. 音频子系统概述

音频子系统是Linux内核中负责音频数据采集、处理和输出的核心模块。理解其整体架构、历史演变以及在内核中的定位,是开发高质量音频驱动的第一步。本章将系统梳理ALSA与OSS的差异,并深入剖析音频驱动在内核源码树中的组织方式。

1.1 从OSS到ALSA:音频架构的演进

Linux音频子系统经历了从OSS(Open Sound System)到ALSA(Advanced Linux Sound Architecture)的演进。OSS是早期内核的标准音频接口,而ALSA自2.6内核起成为主流,提供了更现代、更灵活的框架。

对比维度 OSS (v3/v4) ALSA (v1.x)
设计哲学 单一设备文件,ioctl驱动 插件化、模块化、用户态库
设备节点 /dev/dsp, /dev/mixer /dev/snd/pcmC0D0p, /dev/snd/controlC0
多音频流 不支持(独占设备) 原生支持多路PCM流
硬件抽象 弱,驱动直接暴露硬件细节 强,通过Card/Device/Subdevice分层
用户态API ioctl + read/write libasound (alsa-lib) + 内核API
混音支持 内核级混音(dmix) 用户态混音(dmix插件)
内核状态 标记为废弃,仅兼容旧硬件 当前标准,持续维护
工程建议: 新项目应优先选择ALSA。若需兼容遗留OSS应用,可通过snd-pcm-oss和snd-mixer-oss内核模块提供OSS模拟层,无需修改应用代码。

1.2 ALSA架构分层详解

ALSA采用自底向上的分层设计,每一层职责清晰,便于驱动开发和调试。下图展示了ALSA的核心组件及其数据流关系。

ALSA 音频架构分层与数据流 用户空间 (User Space) 应用程序 (aplay, arecord, PulseAudio, PipeWire) libasound (alsa-lib) — 插件层 (dmix, rate, route) 内核空间 (Kernel Space) ALSA Core snd / pcm / control / timer PCM中间层 snd_pcm_ops / dmaengine 硬件驱动层 soc / i2s / ac97 / hda 硬件抽象层 (ASoC / HDA / Legacy) Platform Driver | Codec Driver | Machine Driver 硬件层 (Hardware) I2S / TDM / AC97 / HDA 控制器 DMA控制器 | Codec芯片 | 数字麦克风 数据流:应用 → alsa-lib → ALSA Core → PCM中间层 → 硬件驱动 → DMA → 硬件FIFO

1.3 音频驱动在Linux内核中的位置

音频驱动位于内核源码树的 sound/ 目录下,与 drivers/ 中的其他设备驱动(如I2C、SPI、DMA)协同工作。理解其目录结构有助于快速定位代码和调试问题。

linux/sound/
├── core/               # ALSA核心框架
│   ├── pcm_native.c    # PCM设备创建、open/close/ioctl
│   ├── pcm_lib.c       # PCM运行时管理、DMA传输
│   ├── control.c       # 控制接口(mixer、音量)
│   └── timer.c         # 定时器接口
├── pci/                # PCI声卡驱动(HDA、Intel、Creative)
│   ├── hda/            # 高清晰音频(HD Audio)
│   └── intel8x0.c      # Intel ICH系列
├── soc/                # ASoC(ALSA System on Chip)
│   ├── soc-core.c      # ASoC核心注册与组件管理
│   ├── soc-pcm.c       # ASoC PCM操作封装
│   ├── soc-dapm.c      # 动态音频电源管理
│   └── codecs/         # 音频Codec驱动(WM8960, MAX98090等)
├── spi/                # SPI接口音频设备
├── usb/                # USB音频设备(UAC1/2)
├── arm/                # ARM平台特定音频驱动
└── drivers/            # 其他音频相关驱动(ALSA SoC DMA引擎等)
关键路径: 嵌入式音频驱动开发主要工作在 sound/soc/ 目录下,涉及三个核心组件:Platform驱动(DMA传输)、Codec驱动(音频编解码控制)、Machine驱动(板级绑定与路由)。

1.4 音频驱动在内核设备模型中的角色

音频驱动遵循Linux内核标准设备模型,通过 struct snd_card 作为顶层容器,挂载PCM设备、控制设备、MIDI设备等子设备。下图展示了音频设备在内核中的注册与关联关系。

音频驱动在内核设备模型中的位置 struct device (内核设备模型) struct snd_card (声卡实例) struct snd_pcm PCM设备 (playback/capture) struct snd_ctl 控制设备 (mixer/音量) struct snd_rawmidi MIDI设备 (可选) 硬件抽象层 (Platform + Codec + Machine) I2S控制器 | DMA引擎 | Codec寄存器 | GPIO/Irq 注册流程:snd_card_new() → snd_device_new() → snd_card_register() → 创建设备节点

1.5 核心数据结构与驱动入口

每个ALSA音频驱动都必须实现以下核心数据结构,它们构成了驱动与内核框架之间的契约。

数据结构 作用 关键回调/字段
struct snd_card 声卡顶层容器,管理所有子设备 number, driver, private_data
struct snd_pcm PCM设备实例,代表一个音频流方向 streams[SNDRV_PCM_STREAM_LAST]
struct snd_pcm_ops PCM操作函数集,驱动核心实现 open, close, hw_params, prepare, trigger, pointer
struct snd_dmaengine_pcm_config DMA引擎配置,用于dmaengine传输 prepare_slave_config, compat_filter_fn
struct snd_soc_dai ASoC中的数字音频接口(I2S/TDM) ops, driver, playback/capture
常见陷阱: 在实现 snd_pcm_ops->pointer 回调时,必须返回硬件当前的DMA传输位置(以帧为单位)。若返回错误值或未及时更新,会导致音频数据出现重复、跳跃或卡顿。该回调必须在原子上下文中执行,不能睡眠。

1.6 本章小结

  • ALSA 是Linux内核当前标准的音频框架,取代了遗留的OSS系统。
  • ALSA架构分为 用户空间(alsa-lib)、内核核心(ALSA Core)、PCM中间层 和 硬件驱动层 四层。
  • 音频驱动源码位于 sound/ 目录,嵌入式开发主要关注 sound/soc/ 下的ASoC框架。
  • 音频设备通过 struct snd_card 注册到内核设备模型,挂载PCM、Control等子设备。
  • 驱动开发者必须实现 struct snd_pcm_ops 中的关键回调,尤其是 pointer 和 trigger。