1. ALSA驱动框架初探

任务目标

编写一个最小的虚拟声卡驱动,完成以下功能:

  • 注册一个 snd_card 实例
  • 在声卡上创建一个 PCM 播放设备
  • 驱动加载后,通过 /proc/asound/cards 能看到新声卡条目

环境与依赖

  • Linux 内核源码树(本案例基于 5.15 版本验证)
  • 已安装内核头文件与编译工具链(make、gcc)
  • 内核配置需启用 ALSA 支持(CONFIG_SND)

核心实现

驱动通过 snd_card_new 创建声卡,注册 PCM 设备后调用 snd_card_register 使声卡在 ALSA 核心中可见。PCM 设备的 open/close 回调仅做空操作,用于满足设备创建的最小要求。

#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>

static struct snd_card *card;
static struct snd_pcm *pcm;

static int dummy_pcm_open(struct snd_pcm_substream *substream)
{
    return 0;
}

static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
    return 0;
}

static const struct snd_pcm_ops dummy_pcm_ops = {
    .open = dummy_pcm_open,
    .close = dummy_pcm_close,
};

static int dummy_snd_probe(struct platform_device *pdev)
{
    int ret;

    ret = snd_card_new(&pdev->dev, -1, "dummycard", THIS_MODULE,
                       sizeof(struct snd_card *), &card);
    if (ret < 0)
        return ret;

    ret = snd_pcm_new(card, "dummy PCM", 0, 1, 0, &pcm);
    if (ret < 0)
        goto fail_pcm;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &dummy_pcm_ops);
    strcpy(pcm->name, "Dummy PCM Device");

    ret = snd_card_register(card);
    if (ret < 0)
        goto fail_register;

    platform_set_drvdata(pdev, card);
    return 0;

fail_register:
fail_pcm:
    snd_card_free(card);
    return ret;
}

static int dummy_snd_remove(struct platform_device *pdev)
{
    card = platform_get_drvdata(pdev);
    if (card)
        snd_card_free(card);
    return 0;
}

static struct platform_driver dummy_snd_driver = {
    .probe = dummy_snd_probe,
    .remove = dummy_snd_remove,
    .driver = {
        .name = "dummy_alsa_card",
    },
};

static struct platform_device *dummy_snd_device;

static int __init dummy_snd_init(void)
{
    int ret;

    ret = platform_driver_register(&dummy_snd_driver);
    if (ret < 0)
        return ret;

    dummy_snd_device = platform_device_register_simple("dummy_alsa_card",
                                                       -1, NULL, 0);
    if (IS_ERR(dummy_snd_device)) {
        platform_driver_unregister(&dummy_snd_driver);
        return PTR_ERR(dummy_snd_device);
    }

    return 0;
}

static void __exit dummy_snd_exit(void)
{
    platform_device_unregister(dummy_snd_device);
    platform_driver_unregister(&dummy_snd_driver);
}

module_init(dummy_snd_init);
module_exit(dummy_snd_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALSA Course");
MODULE_DESCRIPTION("Minimal dummy ALSA sound card driver");

Makefile

obj-m := dummy_alsa.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 dummy_alsa.ko

# 查看声卡是否注册成功
cat /proc/asound/cards

验证方法

执行 cat /proc/asound/cards 后,预期输出中应包含类似以下条目:

 1 [dummycard      ]: dummycard - dummycard
                      dummycard

其中 dummycard 为驱动中通过 snd_card_new 设置的声卡名称。同时执行 ls /proc/asound/ 应能看到对应的声卡目录(如 card1)。若上述条目出现,则说明声卡注册成功,PCM 设备已创建。

卸载驱动

sudo rmmod dummy_alsa

卸载后再次执行 cat /proc/asound/cards,确认对应条目消失。