3. ALSA 核心数据结构:驱动骨架的基石

在 ALSA 驱动框架中,核心数据结构是连接硬件、音频流与用户空间的桥梁。理解 snd_card、snd_pcm、snd_pcm_substream、snd_pcm_ops、snd_soc_dai、snd_soc_codec 和 snd_soc_platform 的职责与关系,是编写音频驱动的第一步。这些结构体共同构成了 ASoC(ALSA System on Chip)分层架构的骨骼。

3.1 顶层容器:snd_card

snd_card 是 ALSA 驱动的顶层管理结构,代表一个物理声卡。每个声卡在系统中对应一个 cardX 设备节点(如 card0)。驱动初始化时,必须首先创建并注册 snd_card。

  • 职责:管理声卡下的所有子设备(PCM、Control、MIDI 等),提供设备索引、ID 字符串、内存分配与释放接口。
  • 关键函数:snd_card_new() 创建,snd_card_register() 注册,snd_card_free() 释放。
  • 生命周期:驱动加载时创建,卸载时释放;一个 snd_card 可包含多个 snd_pcm 实例。
💡 开发提示:snd_card_new() 的第四个参数 size 用于在 card 结构体尾部附加私有数据,常用于存放硬件资源句柄(如寄存器基址、中断号),避免全局变量。

3.2 PCM 设备核心:snd_pcm

snd_pcm 代表一个 PCM 设备实例,对应 /dev/snd/pcmC0D0p 或 /dev/snd/pcmC0D0c 等设备文件。它封装了 PCM 流的配置参数(采样率、格式、通道数)和运行时状态。

字段/概念说明
streams[2]包含 playback 和 capture 两个方向的 snd_pcm_str 结构
name设备名称,如 "AC97 PCM"
card指向所属 snd_card 的指针
private_data驱动私有数据,通常指向硬件描述结构
ops指向 snd_pcm_ops 操作函数集

3.3 流实例:snd_pcm_substream

snd_pcm_substream 是 PCM 数据流的运行时实例。当应用程序打开 PCM 设备时,内核会分配一个 substream。它包含了当前流的硬件参数、软件参数、DMA 缓冲区指针、运行时状态等。

  • 关键字段:runtime(snd_pcm_runtime 结构,包含 hw_params、sw_params、DMA 地址等)、pcm(所属 snd_pcm)、number(子流编号)。
  • 方向:每个 substream 只属于 playback 或 capture 之一,由 stream 字段标识。
  • 多实例:一个 snd_pcm 可支持多个 substream(如多声道录音),但通常嵌入式设备只使用一个 playback 和一个 capture substream。
🔑 关键理解:snd_pcm_substream 是驱动与应用程序交互的直接对象。中断处理函数、DMA 完成回调、指针更新都通过 substream 访问运行时数据。

3.4 操作函数集:snd_pcm_ops

snd_pcm_ops 是驱动必须实现的操作函数表,定义了 PCM 设备的标准行为。ALSA 核心在用户调用 ioctl 时,会回调这些函数。

函数指针触发时机典型实现
open设备打开时初始化硬件、分配 DMA 缓冲区
close设备关闭时释放资源、停止 DMA
ioctl特殊控制命令处理非标准硬件控制
hw_params设置硬件参数时配置采样率、格式、DMA 通道
hw_free释放硬件参数时释放 DMA 缓冲区
prepare准备播放/录音时重置 DMA 指针、配置硬件 FIFO
trigger启动/停止/暂停时操作 DMA 引擎启停
pointer查询当前 DMA 位置时返回硬件指针(DMA 传输进度)
copy/silence数据拷贝/静音填充用于非 MMAP 模式或特殊硬件

3.5 ASoC 分层结构:DAI、Codec、Platform

ASoC 将嵌入式音频驱动分为三个独立组件,通过标准接口组合,提高代码复用性。

3.5.1 snd_soc_dai:数字音频接口

snd_soc_dai 代表 CPU 侧的 DAI 控制器(如 I2S、PCM 接口)。它负责时钟生成、帧同步、数据移位等数字音频协议。

  • 关键字段:name(如 "i2s-hifi")、playback/capture(snd_soc_pcm_stream 结构,描述支持的格式)、ops(snd_soc_dai_ops 函数集)。
  • 驱动职责:实现 hw_params、trigger、set_fmt 等回调,配置 I2S 时钟极性、位宽、主从模式。

3.5.2 snd_soc_codec:音频编解码器

snd_soc_codec 代表外部或内部的音频 Codec 芯片(如 WM8960、ES8316)。它负责 DAC/ADC 转换、音量控制、混音器、输入输出选择。

  • 关键字段:name、regmap(寄存器读写接口)、controls(kcontrol 列表)、dapm(动态音频电源管理)。
  • 驱动职责:实现 Codec 寄存器初始化、采样率配置、电源管理序列。

3.5.3 snd_soc_platform:DMA 引擎抽象

snd_soc_platform 代表 SoC 侧的 DMA 引擎或音频接口控制器。它负责将音频数据从内存搬运到 DAI 或从 DAI 搬运到内存。

  • 关键字段:name、pcm_ops(snd_pcm_ops 实现)、component(通用组件层)。
  • 驱动职责:实现 DMA 通道申请、描述符配置、中断处理、指针更新。
⚠️ 常见误区:在旧版 ASoC 中,Platform 也包含 DMA 驱动;在新版(Linux 5.x+)中,Platform 逐渐被 snd_soc_component 统一,但核心概念不变。编写驱动时请确认内核版本对应的 API。

3.6 核心数据结构关系图

下图展示了 ALSA 核心数据结构在 ASoC 框架中的组织关系,从顶层 snd_card 到底层硬件组件:

ALSA 核心数据结构关系图 ALSA 核心数据结构关系图(ASoC 框架) snd_card(声卡) snd_pcm(PCM 设备) snd_pcm_substream(流实例) snd_pcm_ops(操作函数集) 包含 包含 调用 ASoC 分层组件 snd_soc_dai(数字接口) snd_soc_codec(编解码器) snd_soc_platform(DMA 引擎) 说明:snd_pcm_substream 通过 ops 调用 DAI/Codec/Platform 的底层回调,完成音频数据传输

3.7 数据结构初始化流程示例

以下代码片段展示了在 ASoC 驱动中如何创建并关联这些核心数据结构:

/* 1. 创建 snd_card */
struct snd_card *card;
int ret = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
                        THIS_MODULE, sizeof(struct my_private_data), &card);

/* 2. 创建 snd_pcm 设备 */
struct snd_pcm *pcm;
ret = snd_pcm_new(card, "my-audio", 0, 1, 1, &pcm);
pcm->private_data = priv;

/* 3. 设置 snd_pcm_ops */
static const struct snd_pcm_ops my_pcm_ops = {
    .open      = my_pcm_open,
    .close     = my_pcm_close,
    .hw_params = my_pcm_hw_params,
    .hw_free   = my_pcm_hw_free,
    .prepare   = my_pcm_prepare,
    .trigger   = my_pcm_trigger,
    .pointer   = my_pcm_pointer,
};
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &my_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &my_pcm_ops);

/* 4. 注册声卡 */
ret = snd_card_register(card);

3.8 数据结构对比总结表

数据结构所属层次核心职责典型数量
snd_card顶层声卡管理、设备注册1 个/声卡
snd_pcmPCM 层PCM 设备实例、流方向管理1~N 个/声卡
snd_pcm_substream流层运行时状态、DMA 缓冲区1~N 个/PCM 设备
snd_pcm_ops操作层硬件控制回调函数集1 套/PCM 设备
snd_soc_daiASoC 组件数字音频接口控制1~N 个/平台
snd_soc_codecASoC 组件音频编解码器控制1~N 个/平台
snd_soc_platformASoC 组件DMA 传输与数据搬运1 个/平台
📌 学习路径建议:先掌握 snd_card → snd_pcm → snd_pcm_ops 这条主线,再深入 ASoC 的三个组件。理解每个结构体的生命周期和回调时机,是调试音频驱动的基础。