2. ALSA 体系架构概览

ALSA(Advanced Linux Sound Architecture)是 Linux 内核中用于音频设备驱动的标准框架,也是用户空间音频应用编程的主要接口。理解 ALSA 在 Linux 音频栈中的位置、内核空间与用户空间的分界,以及 Card、Device、Subdevice 这三个核心组件,是后续进行 ALSA 用户空间编程的基础。

2.1 ALSA 在 Linux 音频栈中的位置

在 Linux 系统中,音频数据从应用程序到物理硬件(如 Codec、DAC/ADC)需要经过多个软件层。ALSA 位于中间层,向上提供统一的用户空间 API,向下管理具体的硬件驱动。

音频栈分层(从上到下):
  1. 用户空间应用:播放器(aplay、mpg123)、录音工具(arecord)、VoIP 软件、游戏引擎等。
  2. ALSA 用户空间库(libasound):提供 snd_pcm_*、snd_ctl_*、snd_mixer_* 等 API,简化对内核设备的操作。
  3. ALSA 内核核心(snd):管理 PCM 设备、控制接口、定时器、MIDI 等子系统。
  4. ALSA 硬件驱动(snd-xxx):直接与 I2S、AC97、HDMI、USB Audio Class 等硬件控制器交互。
  5. 物理硬件:SoC 内置 I2S 控制器、外部 Codec 芯片、USB 音频设备等。
Linux 音频栈分层架构 Linux 音频栈分层架构 用户空间应用 (aplay, arecord, 游戏, VoIP) ALSA 用户空间库 (libasound) 用户空间 内核空间 ALSA 内核核心 (snd) ALSA 硬件驱动 (snd-soc-xxx, snd-usb-audio) 物理硬件 (I2S Codec, USB Audio, HDMI)

2.2 内核空间与用户空间的分界

ALSA 严格遵循 Linux 内核空间与用户空间的分离原则。用户空间的应用程序不能直接访问硬件寄存器,必须通过系统调用进入内核,由内核驱动完成实际操作。

维度 用户空间 内核空间
运行权限 非特权(Ring 3) 特权(Ring 0)
主要组件 libasound、应用进程 snd 核心、硬件驱动
接口方式 调用 snd_pcm_* 等 API 通过 ioctl、mmap、read/write 系统调用
数据路径 应用缓冲区 → libasound 缓冲区 内核环形缓冲区(DMA)→ 硬件 FIFO
错误处理 返回负的 errno 值(如 -EINVAL) 内核日志(dmesg)
关键点:用户空间编程时,所有对音频设备的操作(打开、配置、读写、关闭)最终都会转化为对 /dev/snd/ 下设备文件的 ioctl 或 mmap 系统调用。libasound 封装了这些底层细节,开发者只需关注 API 语义。

2.3 ALSA 核心组件:Card、Device、Subdevice

ALSA 使用三级层次结构来组织音频硬件资源:Card(声卡) → Device(设备) → Subdevice(子设备)。理解这一模型是正确操作 ALSA 设备的前提。

2.3.1 Card(声卡)

Card 代表一个物理或逻辑的音频接口单元。每个 Card 有一个唯一的编号(从 0 开始),以及一个名称(如 hw:0、hw:1)。在嵌入式系统中,一个 SoC 通常只有一个 Card;如果外接 USB 音频设备,则会注册为新的 Card。

  • 标识符:hw:0、hw:1、default(指向系统默认声卡)。
  • 查看方式:cat /proc/asound/cards 或 aplay -l。
  • 典型场景:嵌入式开发板上,Card 0 通常是 SoC 内置音频接口,Card 1 可能是 HDMI 或 USB 音频。

2.3.2 Device(设备)

每个 Card 下可以包含多个 Device,每个 Device 代表一种功能接口。最常见的 Device 类型是 PCM(Pulse Code Modulation)设备,用于播放和录音。此外还有 Control 设备(用于音量、开关控制)、MIDI 设备、Timer 设备等。

  • PCM 设备编号:通常 device 0 为播放,device 1 为录音,但并非绝对。
  • 标识符:hw:0,0(Card 0 的 Device 0)、hw:0,1(Card 0 的 Device 1)。
  • 查看方式:cat /proc/asound/card0/pcm0p/info(p 表示 playback,c 表示 capture)。

2.3.3 Subdevice(子设备)

Subdevice 是 Device 下的更细粒度单元,用于支持多路复用。例如,一个支持多通道独立播放的硬件,可以将每个通道映射为一个 Subdevice。大多数嵌入式音频设备每个 Device 只有一个 Subdevice(编号 0)。

  • 标识符:hw:0,0,0(Card 0, Device 0, Subdevice 0)。
  • 典型用途:多声道声卡、支持多个独立音频流的专业音频接口。
  • 默认值:如果硬件不支持多子设备,Subdevice 编号固定为 0。
ALSA Card-Device-Subdevice 层次结构 ALSA Card-Device-Subdevice 层次结构 Card 0 (SoC 内置音频) Device 0 (PCM 播放) Device 1 (PCM 录音) Device 2 (Control) Subdevice 0 (默认) Subdevice 0 (默认) Subdevice 0 (默认) 标识符示例:hw:0,0,0 → Card 0, Device 0, Subdevice 0 大多数嵌入式设备 Subdevice 只有 0,可省略:hw:0,0

2.4 设备文件与用户空间访问入口

ALSA 在 /dev/snd/ 目录下暴露一组设备文件,用户空间通过它们与内核交互。常见的设备文件类型如下:

设备文件 类型 说明
/dev/snd/controlC0 Control Card 0 的控制接口(音量、开关、混音器)
/dev/snd/pcmC0D0p PCM 播放 Card 0 Device 0 播放(playback)
/dev/snd/pcmC0D0c PCM 录音 Card 0 Device 0 录音(capture)
/dev/snd/timer Timer 全局音频定时器
/dev/snd/seq Sequencer MIDI 序列器
注意事项:
  • 嵌入式系统中,如果内核未编译 CONFIG_SND 或相关驱动,/dev/snd/ 目录可能为空或不存在。
  • 用户空间程序需要 rw 权限访问这些设备文件。通常将用户加入 audio 组可解决权限问题。
  • 不要直接 open 和 ioctl 操作 /dev/snd/pcmC* 文件,除非你正在编写极底层的测试代码。生产代码应使用 libasound 提供的 API。

2.5 本章小结

  • ALSA 是 Linux 音频栈的核心,位于用户空间应用与硬件驱动之间。
  • 用户空间通过 libasound 库和 /dev/snd/ 设备文件与内核 ALSA 子系统通信。
  • ALSA 使用 Card → Device → Subdevice 三级模型管理音频硬件,标识符格式为 hw:Card,Device,Subdevice。
  • 嵌入式开发中,最常操作的是 PCM 设备(hw:0,0)和 Control 设备(hw:0)。