You need to enable JavaScript to run this app. 现代Linux内核版本中注册MIDI设备的实现及兼容问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

现代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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 22:36:00