1. 车载音频系统ALSA驱动开发环境搭建

车载音频系统ALSA驱动开发环境搭建 学习卡片

任务目标

编写一个最小化的ALSA驱动模块,向内核注册一个虚拟声卡(card),加载后能在 /proc/asound/cards 中看到该声卡条目,卸载后条目消失。本案例不涉及PCM数据通路,仅完成声卡设备的注册与注销,用于理解ALSA驱动的基本骨架。

环境与依赖

  • Linux内核源码树(版本需与运行内核匹配),已配置好内核模块编译环境
  • 内核需开启 CONFIG_SND 与 CONFIG_SND_DYNAMIC_MINORS(通常默认开启)
  • 目标板或开发机可加载/卸载内核模块(root权限)

核心实现

驱动通过 snd_card_new 创建声卡对象,设置驱动名称和ID后调用 snd_card_register 注册。模块加载时执行创建与注册,卸载时执行注销与释放。完整代码如下:

#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>

static int my_card_index = -1;  // 由内核自动分配
static struct snd_card *card;

static int __init my_alsa_init(void)
{
    int err;

    // 第一步:创建声卡对象
    // idx=-1 表示由内核自动选择编号;id 为声卡标识字符串
    err = snd_card_new(&platform_device, my_card_index, "mycard",
                       THIS_MODULE, 0, &card);
    if (err < 0) {
        pr_err("myalsa: snd_card_new failed: %d\n", err);
        return err;
    }

    // 第二步:注册声卡到 ALSA 核心
    err = snd_card_register(card);
    if (err < 0) {
        pr_err("myalsa: snd_card_register failed: %d\n", err);
        snd_card_free(card);
        return err;
    }

    pr_info("myalsa: virtual sound card registered\n");
    return 0;
}

static void __exit my_alsa_exit(void)
{
    if (card) {
        snd_card_unregister(card);  // 注销声卡
        snd_card_free(card);        // 释放声卡对象
        card = NULL;
    }
    pr_info("myalsa: virtual sound card unregistered\n");
}

module_init(my_alsa_init);
module_exit(my_alsa_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Minimal ALSA virtual sound card driver");

注意:snd_card_new 的第一个参数需要传入一个有效的 struct device *。在无具体硬件总线的情况下,可使用 platform_device 或直接传 NULL(内核版本较新时允许)。若编译报错,请包含 <linux/platform_device.h> 并定义一个全局 struct platform_device *pdev,在 snd_card_new 前调用 platform_device_register_simple("myalsa", -1, NULL, 0) 获取设备指针。

编译与加载

创建 Makefile:

obj-m := myalsa.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

执行编译与加载:

make
sudo insmod myalsa.ko

验证方法

加载模块后执行以下命令,观察输出:

cat /proc/asound/cards

预期能看到类似条目:

 1 [mycard         ]: myalsa - myalsa
                      myalsa

其中编号可能不同,但 mycard 标识和 myalsa 驱动名必须出现。同时执行 ls /dev/snd/ 应能看到 controlC1(编号与cards中一致)设备节点。卸载模块:

sudo rmmod myalsa
cat /proc/asound/cards   # mycard 条目消失

判定通过条件:加载后 /proc/asound/cards 出现 mycard 条目,/dev/snd/ 下生成对应 controlC* 节点;卸载后两者均消失,且 dmesg 无错误日志。

常见问题

  • insmod报错“Unknown symbol snd_card_new”:说明内核未加载ALSA核心模块,先执行 modprobe snd 或确认内核配置 CONFIG_SND 已开启。
  • snd_card_new传入NULL编译失败:内核版本较老要求非空device指针,按上文说明注册一个platform device即可。
  • 声卡编号不连续:属正常现象,内核按空闲编号分配,不影响功能验证。