现代Linux内核版本中注册MIDI设备的实现及兼容问题咨询
Linux内核虚拟MIDI设备4.15+适配及跨版本兼容方案
接口变更说明
4.15版本内核移除了OSS架构遗留的register_sound_midi/unregister_sound_midi系列API,后续版本需使用ALSA架构原生的rawmidi接口实现虚拟MIDI设备注册。
4.15+版本实现逻辑
- 依赖ALSA核心模块,需在代码中引入
<sound/rawmidi.h>头文件 - 调用
snd_card_new()创建ALSA声卡实例 - 调用
snd_rawmidi_new()初始化rawmidi设备,配置输入/输出通道数 - 实现rawmidi的操作回调(打开、关闭、读、写),替换原有
file_operations逻辑 - 调用
snd_card_register()完成声卡注册,即可被用户空间DAW等软件识别 - 模块退出时调用
snd_card_free()释放资源即可
跨版本兼容方案
通过内核版本宏LINUX_VERSION_CODE做分支判断,4.15以下版本保留原有OSS接口逻辑,4.15及以上版本走ALSA接口逻辑,即可实现一套代码兼容新旧内核。
适配后完整代码
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/version.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Module to create dummy midi devices."); MODULE_VERSION("0.01"); #if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0) // 4.15以下版本用原有OSS接口逻辑 #include <linux/sound.h> static int dummy_midi_open(struct inode *, struct file *); static int dummy_midi_release(struct inode *, struct file *); static ssize_t dummy_midi_read(struct file *, char *, size_t, loff_t *); static ssize_t dummy_midi_write(struct file *, const char *, size_t, loff_t *); static struct file_operations dummy_midi_fops = { .owner = THIS_MODULE, .read = dummy_midi_read, .write = dummy_midi_write, .open = dummy_midi_open, .release = dummy_midi_release }; static int minor; static int dummy_midi_open(struct inode *midi_inode, struct file *midi_file) { printk(KERN_INFO"Open\n"); return 0; } static int dummy_midi_release(struct inode *midi_inode, struct file *midi_file) { printk(KERN_INFO"Release\n"); return 0; } static ssize_t dummy_midi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { printk(KERN_INFO"Read\n"); return 0; } static ssize_t dummy_midi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { printk(KERN_INFO"Write\n"); return 0; } static int __init midimodule_init(void) { printk(KERN_INFO"Initializing Module\n"); minor = register_sound_midi(&dummy_midi_fops,-1); printk(KERN_INFO"Registered MIDI Device: %d\n",minor); return 0; } static void __exit midimodule_exit(void) { printk(KERN_INFO"Leaving now\n"); unregister_sound_midi(minor); printk(KERN_INFO"Unregistered MIDI Device: %d\n",minor); } #else // 4.15及以上版本用ALSA rawmidi接口 #include <sound/core.h> #include <sound/rawmidi.h> static struct snd_card *card; static struct snd_rawmidi *rmidi; static int dummy_midi_open(struct snd_rawmidi_substream *substream) { printk(KERN_INFO"Open\n"); return 0; } static int dummy_midi_release(struct snd_rawmidi_substream *substream) { printk(KERN_INFO"Release\n"); return 0; } static ssize_t dummy_midi_read(struct snd_rawmidi_substream *substream, char __user *buf, size_t count) { printk(KERN_INFO"Read\n"); return 0; } static ssize_t dummy_midi_write(struct snd_rawmidi_substream *substream, const char __user *buf, size_t count) { printk(KERN_INFO"Write\n"); return count; } static struct snd_rawmidi_ops dummy_midi_output_ops = { .open = dummy_midi_open, .close = dummy_midi_release, .write = dummy_midi_write, }; static struct snd_rawmidi_ops dummy_midi_input_ops = { .open = dummy_midi_open, .close = dummy_midi_release, .read = dummy_midi_read, }; static int __init midimodule_init(void) { int err; printk(KERN_INFO"Initializing Module\n"); // 创建ALSA声卡实例 err = snd_card_new(&platform_bus, -1, "DummyMIDI", THIS_MODULE, 0, &card); if (err < 0) return err; strcpy(card->driver, "DummyMIDI"); strcpy(card->shortname, "Dummy MIDI Device"); strcpy(card->longname, "Virtual Dummy MIDI Device"); // 创建rawmidi设备,1个输入1个输出通道 err = snd_rawmidi_new(card, "Dummy MIDI", 0, 1, 1, &rmidi); if (err < 0) { snd_card_free(card); return err; } rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; strcpy(rmidi->name, "Dummy MIDI"); // 绑定操作回调 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &dummy_midi_output_ops); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &dummy_midi_input_ops); // 注册声卡 err = snd_card_register(card); if (err < 0) { snd_card_free(card); return err; } printk(KERN_INFO"Registered ALSA MIDI Device\n"); return 0; } static void __exit midimodule_exit(void) { printk(KERN_INFO"Leaving now\n"); snd_card_free(card); printk(KERN_INFO"Unregistered ALSA MIDI Device\n"); } #endif module_init(midimodule_init); module_exit(midimodule_exit);
编译注意事项
模块Makefile需要添加ALSA相关依赖,指定CONFIG_SND=m或者直接链接snd核心模块,编译时会自动根据当前运行的内核版本选择对应分支代码编译。
内容的提问来源于stack exchange,提问作者Even Pardides
相关产品推荐
相关产品推荐

