2. ALSA声卡驱动基础框架实现
任务目标
在Linux内核驱动中,为声卡创建PCM设备实例,并注册基本的open/close回调函数。本案例基于ALSA框架,使用snd_pcm_new()创建PCM设备,通过snd_pcm_set_ops()绑定操作集,最终在模块卸载时销毁设备。
环境与依赖
- 内核源码树已编译,且
CONFIG_SND与CONFIG_SND_PCM已启用 - 本案例代码为独立模块,不依赖具体硬件平台,仅验证PCM设备生命周期管理
核心实现
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
static struct snd_card *card;
static struct snd_pcm *pcm;
static int my_pcm_open(struct snd_pcm_substream *substream)
{
pr_info("my_pcm: open called\n");
return 0;
}
static int my_pcm_close(struct snd_pcm_substream *substream)
{
pr_info("my_pcm: close called\n");
return 0;
}
static const struct snd_pcm_ops my_pcm_ops = {
.open = my_pcm_open,
.close = my_pcm_close,
};
static int __init my_pcm_init(void)
{
int ret;
/* 创建声卡,card_idx=0,id字符串可自定义 */
ret = snd_card_new(&platform_device, -1, "mycard", THIS_MODULE, 0, &card);
if (ret < 0) {
pr_err("my_pcm: snd_card_new failed: %d\n", ret);
return ret;
}
/* 创建PCM设备:device=0,PCM类型为播放,设备编号为0 */
ret = snd_pcm_new(card, "my_pcm_dev", 0, 1, 0, &pcm);
if (ret < 0) {
pr_err("my_pcm: snd_pcm_new failed: %d\n", ret);
snd_card_free(card);
return ret;
}
/* 绑定操作集:PCM播放方向使用my_pcm_ops */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &my_pcm_ops);
/* 注册声卡,使设备在/dev/snd/下可见 */
ret = snd_card_register(card);
if (ret < 0) {
pr_err("my_pcm: snd_card_register failed: %d\n", ret);
snd_card_free(card);
return ret;
}
pr_info("my_pcm: PCM device created successfully\n");
return 0;
}
static void __exit my_pcm_exit(void)
{
/* 释放声卡时会自动销毁其下所有PCM设备 */
snd_card_free(card);
pr_info("my_pcm: PCM device destroyed\n");
}
module_init(my_pcm_init);
module_exit(my_pcm_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Minimal ALSA PCM device create/destroy example");
编译与加载
# 使用内核构建系统编译为模块
obj-m := my_pcm.o
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
# 加载模块
sudo insmod my_pcm.ko
# 卸载模块
sudo rmmod my_pcm
验证方法
- 加载后检查设备节点:执行
cat /proc/asound/cards,应能看到名为mycard的声卡条目。 - 检查PCM设备列表:执行
cat /proc/asound/mycard/pcm0p/info,应显示设备名my_pcm_dev,且状态为closed。 - 验证回调触发:使用
aplay -D hw:mycard,0 /dev/zero播放空数据,然后执行dmesg | tail,应看到my_pcm: open called;播放结束后(Ctrl+C或播放完毕),再次查看dmesg,应看到my_pcm: close called。 - 卸载验证:执行
sudo rmmod my_pcm后,再次查看/proc/asound/cards,mycard条目应消失。
判定标准
- 模块加载无错误,
/proc/asound/cards中出现新声卡 - PCM设备信息节点存在且可读
- 调用
aplay时dmesg中依次出现open和close日志,证明回调已正确绑定并执行 - 模块卸载后所有设备节点被清理,无内存泄漏或内核错误
关键说明
snd_card_new()的第一个参数传入&platform_device仅为满足接口要求,实际驱动应传入对应的总线设备指针;本案例不依赖具体硬件,因此使用platform_device占位。- PCM创建时参数
playback_count=1表示支持1个播放子流,capture_count=0表示不支持录音。若需双向支持,可相应调整。 - 销毁PCM设备无需单独调用
snd_pcm_free(),snd_card_free()会递归释放声卡下所有PCM实例。