2. 声卡驱动模块参数
任务目标
为声卡驱动添加标准ALSA模块参数index和id,使驱动在加载时能够动态指定声卡的编号(card index)和名称(card id),替代编译期硬编码。
核心实现
以下代码实现了一个最小ALSA声卡驱动模板,注册声卡时读取模块参数。完整代码可直接编译为内核模块。
// sound_card_params.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/initval.h>
/* ---- 模块参数 ---- */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; // 默认{-1, -1, ...}
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; // 默认{NULL, NULL, ...}
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Card index (array, up to " __stringify(SNDRV_CARDS) ")");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "Card ID string (array, up to " __stringify(SNDRV_CARDS) ")");
/* ---- 平台驱动 ---- */
static int mycard_probe(struct platform_device *pdev)
{
struct snd_card *card;
int dev = pdev->id; // 设备实例号,对应参数数组下标
int err;
/* 使用模块参数创建声卡:index[dev]为声卡编号,id[dev]为声卡名称 */
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &card);
if (err < 0) {
dev_err(&pdev->dev, "snd_card_new failed: %d\n", err);
return err;
}
/* 此处可添加PCM/control等组件,本案例仅演示参数传递 */
strcpy(card->driver, "mycard");
strcpy(card->shortname, "My ALSA Card");
strcpy(card->longname, "My ALSA Card (parameter demo)");
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
platform_set_drvdata(pdev, card);
return 0;
}
static int mycard_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
if (card)
snd_card_free(card);
return 0;
}
static struct platform_driver mycard_driver = {
.driver = {
.name = "mycard",
},
.probe = mycard_probe,
.remove = mycard_remove,
};
/* ---- 平台设备(模拟硬件存在) ---- */
static struct platform_device *mycard_device;
static int __init mycard_init(void)
{
int err;
/* 注册平台驱动 */
err = platform_driver_register(&mycard_driver);
if (err < 0)
return err;
/* 注册一个设备实例,id=0对应参数数组第0个元素 */
mycard_device = platform_device_register_simple("mycard", 0, NULL, 0);
if (IS_ERR(mycard_device)) {
platform_driver_unregister(&mycard_driver);
return PTR_ERR(mycard_device);
}
return 0;
}
static void __exit mycard_exit(void)
{
platform_device_unregister(mycard_device);
platform_driver_unregister(&mycard_driver);
}
module_init(mycard_init);
module_exit(mycard_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ALSA sound card with index/id module parameters");
编译与加载
# Makefile(与上述源码同目录)
obj-m += sound_card_params.o
# 编译(需内核头文件)
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
# 加载:指定声卡编号为2,名称为"mycard0"
sudo insmod sound_card_params.ko index=2 id="mycard0"
# 查看结果
cat /proc/asound/cards
# 预期输出中应包含:
# 2 [mycard0 ]: mycard - My ALSA Card
# My ALSA Card (parameter demo)
验证方法
- 参数生效验证:加载后执行
cat /proc/asound/cards,确认第2行行首编号为2,方括号内为mycard0,与传入参数一致。 - 默认值验证:不传参数直接
insmod sound_card_params.ko,系统自动分配第一个空闲编号(通常为0),id为空时内核自动生成。 - 参数读取验证:
cat /sys/module/sound_card_params/parameters/index应显示2,-1,-1,...(数组长度由SNDRV_CARDS决定,通常为8或32)。
关键说明
SNDRV_DEFAULT_IDX展开为{[0 ... (SNDRV_CARDS-1)] = -1},-1表示由内核自动分配编号;SNDRV_DEFAULT_STR为全NULL。- 模块参数数组下标对应
platform_device的id字段。本案例仅注册1个设备(id=0),如需多声卡可注册多个设备并分别传参。 - 若指定编号已被占用,
snd_card_new返回-EBUSY,驱动加载失败,可通过dmesg查看错误日志。