2. ALSA驱动框架:声卡、设备、子设备与PCM/Control
ALSA(Advanced Linux Sound Architecture)驱动框架的核心在于将复杂的音频硬件抽象为层次化的软件对象。理解声卡(Card)、设备(Device)、子设备(Subdevice)以及PCM和Control设备的概念,是掌握ALSA驱动开发与调试的基础。本章将逐一拆解这些核心抽象层,并展示它们之间的组织关系。
2.1 核心概念:Card、Device、Subdevice
ALSA采用三级层次结构来管理音频硬件:
- 声卡(Card):代表一个物理或逻辑的音频接口单元。通常对应一个硬件设备(如PCI声卡、USB音频设备、SoC内部音频控制器)。每个Card拥有唯一的编号(card index,如0、1、2)。
- 设备(Device):Card内部的独立功能单元。一个Card可以包含多个Device,例如:一个用于播放的PCM设备(device 0)、一个用于录音的PCM设备(device 1)、一个MIDI设备(device 2)、一个Control设备(device 0)。Device在Card内通过device index区分。
- 子设备(Subdevice):Device内部的进一步细分。用于支持多路独立音频流。例如,一个支持多通道独立播放的PCM设备,可以包含多个Subdevice,每个Subdevice对应一个独立的音频流通道。Subdevice通过subdevice index标识。
关键理解: 用户空间通过
hw:Card,Device,Subdevice 三元组来唯一标识一个音频端点。例如 hw:0,0,0 表示Card 0、Device 0、Subdevice 0。
类比: Card就像一台多功能的音频接口机架;Device是机架上的独立模块(如播放模块、录音模块、效果器模块);Subdevice则是模块上的独立通道(如播放模块上的通道1、通道2)。
2.2 PCM设备:音频数据传输的核心
PCM(Pulse Code Modulation)设备是ALSA驱动中负责数字音频数据播放(Playback)和录音(Capture)的接口。它是音频流传输的核心通道。
2.2.1 PCM设备的主要特征
- 方向性:一个PCM设备可以是只播放、只录音或全双工。
- 硬件参数:包括采样率(Sample Rate)、位深(Sample Format)、通道数(Channels)、周期大小(Period Size)等。
- DMA传输:PCM设备通常通过DMA(直接内存访问)在内存和音频硬件之间搬运数据,减少CPU负载。
- 数据流管理:通过
snd_pcm_open()、snd_pcm_writei()、snd_pcm_readi()、snd_pcm_close()等API进行控制。
常见误区: PCM设备并不直接对应物理的“扬声器”或“麦克风”。它代表的是与音频编解码器(Codec)或数字音频接口(DAI)之间的数字数据通道。物理换能器(扬声器/麦克风)通常由更底层的Codec驱动管理。
2.3 Control设备:音频路径与参数的管理者
Control设备是ALSA驱动中用于配置和控制音频路径、音量、增益、开关、混音器、路由等非数据流参数的接口。它不传输音频数据,而是提供“旋钮”和“开关”。
2.3.1 Control设备的核心元素
- Control Element:一个具体的控制项,例如“Master Playback Volume”、“Mic Capture Switch”、“Left Input Mixer”等。
- 属性:每个Element包含名称(Name)、类型(Type,如Integer、Boolean、Enum)、访问权限(Read/Write)、数值范围(Min/Max)等。
- 用户空间接口:通过
amixer工具或snd_ctl_open()/snd_ctl_elem_read()/snd_ctl_elem_write()等API进行访问。
PCM vs Control 对比:
| 特性 | PCM 设备 | Control 设备 |
|---|---|---|
| 主要功能 | 传输音频数据流 | 配置音频参数与路由 |
| 数据类型 | 连续的PCM采样数据 | 离散的控制值(整数、布尔、枚举) |
| 典型操作 | read / write | read / write element value |
| 用户工具 | aplay, arecord, speaker-test | amixer, alsamixer |
| 驱动接口 | snd_pcm_ops | snd_kcontrol_new |
2.4 设备节点与用户空间访问
在用户空间,ALSA设备通过 /dev/snd/ 目录下的设备文件暴露。理解这些文件与Card/Device/Subdevice的对应关系至关重要。
| 设备文件 | 对应关系 | 说明 |
|---|---|---|
/dev/snd/controlC0 | Card 0 的 Control 设备 | 用于访问混音器、音量等控制元素 |
/dev/snd/pcmC0D0p | Card 0, Device 0, Playback | PCM播放设备 |
/dev/snd/pcmC0D0c | Card 0, Device 0, Capture | PCM录音设备 |
/dev/snd/pcmC0D1p | Card 0, Device 1, Playback | 第二个PCM播放设备(如果存在) |
/dev/snd/timer | 全局定时器 | 用于音频同步和时间戳 |
命名规则:
pcmC<Card>D<Device><方向>。方向中 p 表示播放(Playback),c 表示录音(Capture)。例如 pcmC0D0p 是Card 0的第一个播放设备。
2.5 驱动注册流程简述
在ALSA驱动中,创建Card、Device、Subdevice的典型流程如下:
- 创建Card:调用
snd_card_new()分配并初始化一个struct snd_card实例。 - 创建PCM设备:调用
snd_pcm_new()在Card上创建一个PCM设备,并设置其播放/录音子设备数量。 - 设置PCM操作集:填充
struct snd_pcm_ops结构体,实现open、close、hw_params、prepare、trigger、pointer等回调函数。 - 创建Control元素:调用
snd_ctl_new1()和snd_ctl_add()注册音量、开关等控制元素。 - 注册Card:调用
snd_card_register()将Card注册到ALSA核心,此时会在/dev/snd/下创建设备节点。
关键数据结构:
struct snd_card {
int number; // Card 索引
char id[32]; // Card ID 字符串
struct list_head devices; // 设备链表
// ... 其他成员
};
struct snd_pcm {
struct snd_card *card; // 所属 Card
unsigned int device; // Device 索引
struct snd_pcm_str streams[2]; // 播放和录音流 (SNDRV_PCM_STREAM_PLAYBACK / CAPTURE)
};
struct snd_pcm_substream {
struct snd_pcm *pcm; // 所属 PCM 设备
unsigned int number; // Subdevice 索引
struct snd_pcm_ops *ops; // 操作函数集
struct snd_dma_buffer dma_buffer; // DMA 缓冲区
};
2.6 本章小结
- ALSA驱动采用 Card → Device → Subdevice 三级层次结构管理音频硬件。
- PCM设备是音频数据流的核心通道,负责播放和录音的DMA传输。
- Control设备是音频参数和路由的配置接口,提供音量、开关、混音器等控制元素。
- 用户空间通过
/dev/snd/下的设备文件访问这些抽象对象,命名规则为pcmC<Card>D<Device><方向>。 - 驱动注册流程遵循
snd_card_new() → snd_pcm_new() → 设置ops → 添加Control → snd_card_register()的步骤。