本文从零开始,逐步完成一个基于 ASoC 框架的虚拟音频 Codec 驱动,覆盖 Codec 组件注册 → DAI 定义 → Machine Driver 绑定 → DT 配置 → QEMU ALSA 验证 全流程。
1. 前置条件
确保以下环境已就绪(参考同级页面的 QEMU + 交叉编译工具链搭建指南):
交叉编译器
arm-linux-gnueabihf-gcc 可用已编译 v6.1 内核源码,且开启
CONFIG_SND_SOC、CONFIG_SND_SOC_IMX_SAI 支持QEMU mcimx6ul-evk(Cortex-A7)能正常启动 BusyBox rootfs
内核配置中已启用
CONFIG_SND、CONFIG_SND_PCM、CONFIG_SND_SOC为什么选 ASoC? ASoC(ALSA System on Chip)是 Linux 为嵌入式音频设计的子系统,将音频路径拆分为 Codec、Platform(CPU DAI)、Machine 三层,实现 Codec 驱动与 SoC 平台驱动的解耦复用。
2. ASoC 子系统关键概念(分层视角)
Layer 1 — Codec(音频编解码芯片)
概念 | 说明 |
snd_soc_component_driver | 描述 Codec 组件,包含 probe/remove 回调、控件定义和电源管理策略 |
snd_soc_dai_driver | 描述 Codec 侧 DAI,定义 playback/capture 支持的格式、采样率、通道数 |
snd_soc_dai_ops | Codec DAI 操作集: hw_params 配置硬件参数,set_fmt 设置 I2S/TDM 格式 |
devm_snd_soc_register_component | 注册 Codec 组件 + DAI,生命周期由 devm 自动管理 |
Layer 2 — Platform(SoC CPU DAI + DMA)
概念 | 说明 |
snd_soc_component_driver | 同样用于描述 Platform 组件(DMA 引擎),提供 PCM 操作回调 |
snd_soc_dai_driver | 描述 CPU 侧 DAI(如 i.MX SAI),定义 SoC 端支持的音频能力 |
Codec 层与 Platform 层共用snd_soc_component_driver+snd_soc_dai_driver结构体,通过不同的驱动实例实现解耦。
Layer 3 — Machine(声卡绑定)
概念 | 说明 |
snd_soc_card | Machine Driver 核心结构,绑定 CPU DAI + Codec DAI + 音频路由,对应一块声卡 |
snd_soc_dai_link | 定义一条音频链路:指定 CPU 侧 DAI、Codec 侧 DAI 和 Platform |
SND_SOC_DAILINK_DEFS | 宏定义 DAI link 的 CPU、Codec、Platform 组件数组 |
of_parse_phandle | 从 DT 获取 CPU DAI / Codec 节点引用,实现 Machine Driver 与具体硬件解耦 |
devm_snd_soc_register_card | 注册声卡,触发所有 DAI link 匹配 → Codec probe → PCM 创建 |
ASoC 三层架构
3. 驱动源码
3.1 Codec 驱动 — fake_codec.c
3.2 Machine 驱动 — fake_audio_card.c
3.3 关键结构说明
元素 | 作用 |
snd_soc_component_driver | 描述 Codec 组件,包含 probe 回调和电源管理策略(idle_bias_on 等) |
snd_soc_dai_driver | 定义 Codec DAI 的 playback/capture 能力(采样率、格式、通道数) |
snd_soc_dai_ops | DAI 操作集: hw_params 配置硬件参数,set_fmt 设置 I2S/TDM 格式 |
SND_SOC_DAILINK_DEFS | 宏定义 DAI link 的 CPU、Codec、Platform 组件数组 |
snd_soc_dai_link | 绑定一条音频链路, dai_fmt 指定 I2S 模式 + 极性 + 主从关系 |
of_parse_phandle | 从 DT 获取 CPU DAI / Codec 节点引用,实现 Machine Driver 与具体硬件解耦 |
devm_snd_soc_register_card | 注册声卡,ASoC 核心自动执行 DAI link 匹配 → Codec probe → PCM 创建 |
ASoC 三层解耦:Codec 驱动只关心音频编解码芯片本身(寄存器、DAI 能力),Platform 驱动只关心 SoC 的 DMA + I2S 控制器,Machine 驱动负责将两者「连线」。更换 SoC 只需换 Platform + Machine,Codec 驱动可直接复用。
4. Makefile
双模块编译:obj-m同时包含 Codec 驱动和 Machine 驱动,make一次产出fake_codec.ko+fake_audio_card.ko。
5. 编译模块
编译成功后确认产出:
注意depends: snd-soc-core:ASoC 核心是独立模块,加载驱动前必须确保snd-soc-core.ko已加载(或内核内建CONFIG_SND_SOC=y)。
6. 设备树配置
6.1 I2S + Codec 节点说明
ASoC 驱动需要三个 DT 节点配合:
节点 | 角色 | 说明 |
sai1 / sai2 | CPU DAI (Platform) | i.MX6UL SAI 控制器,已存在于基础 DTB 中( sai@2028000) |
fake_codec@1a | Codec | 挂载在 I2C 总线上,提供音频编解码能力 |
fake-audio-card | Machine | 根节点下,通过 phandle 引用 CPU DAI 和 Codec,完成绑定 |
6.2 需要注入的 DT 节点
6.3 直接修改主 DTB(QEMU 方式)
7. 一键构建脚本 — build.sh
运行方式:
8. QEMU 内加载验证
8.1 启动 QEMU — run_qemu.sh
8.2 加载模块
8.3 确认 probe 调用
8.4 查看 ALSA 声卡信息
8.5 自动化验证脚本 — verify_audio.sh
预期输出:
QEMU 音频限制:QEMU mcimx6ul-evk 未完整模拟 SAI DMA 通道,aplay实际播放可能报错或无声。本实战重点验证 ASoC 框架注册链路(probe → component → card → PCM),实际音频播放在真实硬件上验证。
9. 验证清单
make 编译无 warning,产出 fake_codec.ko + fake_audio_card.komodinfo 输出 depends: snd-soc-corefile *.ko 显示 ARM ELF relocatable设备树中
i2c1 节点下存在 fake_codec@1a 子节点设备树根节点下存在
fake-audio-card 节点insmod fake_codec.ko 后 dmesg 显示 I2C probe OK, addr=0x1ainsmod fake_audio_card.ko 后 dmesg 显示 Fake-Audio-Card registered OK/proc/asound/cards 显示声卡/proc/asound/pcm 显示 PCM 设备verify_audio.sh 全部步骤通过rmmod 后 dmesg 显示 remove called10. 常见问题排查
现象 | 原因 | 解决方案 |
insmod: unknown symbol snd_soc_register_component | ASoC 核心未加载 | 先 insmod snd-soc-core.ko,或内核配置 CONFIG_SND_SOC=y |
Codec probe 成功但 Card 注册失败 | DT 中 audio-cpu phandle 指向的 SAI 节点未启用或不存在 | 确认 SAI 节点 status="okay",检查 &sai2 在 DTB 中是否存在 |
ASoC: CPU DAI ... not registered | SAI Platform 驱动未加载 | 确认 CONFIG_SND_SOC_IMX_SAI=y 或 =m 并加载对应 .ko |
/proc/asound/cards 为空 | 声卡注册失败或 /proc 未挂载 | 检查 dmesg 错误信息;确认 mount -t proc none /proc |
aplay: device busy 或 DMA 错误 | QEMU 未完整模拟 SAI DMA | 正常限制,QEMU 下只验证注册链路。实际播放需真实硬件 |
Machine driver probe 报 -EPROBE_DEFER | Codec 或 CPU DAI 尚未就绪 | 确认 Codec 模块先于 Machine 加载;检查加载顺序 |
insmod: invalid module format | vermagic 不匹配 | 确保 KERNDIR 指向 QEMU 启动时使用的同一内核树 |
11. 向真实硬件(WM8960)迁移
完成虚拟驱动验证后,迁移到真实 WM8960 Codec 需替换以下部分:
- Codec 驱动替换为内核自带 — 使用
sound/soc/codecs/wm8960.c(CONFIG_SND_SOC_WM8960=m)
- DT compatible 更新
- Machine 驱动添加 DAPM 音频路由
- 添加时钟配置 — WM8960 需要 MCLK,通过
clk_get+clk_set_rate配置
- 添加电源管理 —
snd_soc_component_driver中实现suspend/resume
12. 下一步扩展方向
- DAPM 动态电源管理 — 添加 Widget + Route,实现音频路径按需上下电
- ALSA Controls — 添加音量控制、静音开关等 Mixer 控件
- Triggered Buffer + DMA — 配合 SAI DMA,实现连续音频流传输
- 用户态 ALSA 应用 — 使用
alsa-lib编写播放/录音程序
- 对接 V4L2 音视频同步 — IMU + Camera + Audio 多传感器时间戳同步







