162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Dummy soundcard for virtual rawmidi devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * VIRTUAL RAW MIDI DEVICE CARDS 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This dummy card contains up to 4 virtual rawmidi devices. 1262306a36Sopenharmony_ci * They are not real rawmidi devices but just associated with sequencer 1362306a36Sopenharmony_ci * clients, so that any input/output sources can be connected as a raw 1462306a36Sopenharmony_ci * MIDI device arbitrary. 1562306a36Sopenharmony_ci * Also, multiple access is allowed to a single rawmidi device. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Typical usage is like following: 1862306a36Sopenharmony_ci * - Load snd-virmidi module. 1962306a36Sopenharmony_ci * # modprobe snd-virmidi index=2 2062306a36Sopenharmony_ci * Then, sequencer clients 72:0 to 75:0 will be created, which are 2162306a36Sopenharmony_ci * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * - Connect input/output via aconnect. 2462306a36Sopenharmony_ci * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0 2562306a36Sopenharmony_ci * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * - Run application using a midi device (eg. /dev/snd/midiC1D0) 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/init.h> 3162306a36Sopenharmony_ci#include <linux/wait.h> 3262306a36Sopenharmony_ci#include <linux/err.h> 3362306a36Sopenharmony_ci#include <linux/platform_device.h> 3462306a36Sopenharmony_ci#include <linux/module.h> 3562306a36Sopenharmony_ci#include <sound/core.h> 3662306a36Sopenharmony_ci#include <sound/seq_kernel.h> 3762306a36Sopenharmony_ci#include <sound/seq_virmidi.h> 3862306a36Sopenharmony_ci#include <sound/initval.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* hack: OSS defines midi_devs, so undefine it (versioned symbols) */ 4162306a36Sopenharmony_ci#undef midi_devs 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 4462306a36Sopenharmony_ciMODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); 4562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define MAX_MIDI_DEVICES 4 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 5062306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 5162306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; 5262306a36Sopenharmony_cistatic int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for virmidi soundcard."); 5662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for virmidi soundcard."); 5862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 5962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable this soundcard."); 6062306a36Sopenharmony_cimodule_param_array(midi_devs, int, NULL, 0444); 6162306a36Sopenharmony_ciMODULE_PARM_DESC(midi_devs, "MIDI devices # (1-4)"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct snd_card_virmidi { 6462306a36Sopenharmony_ci struct snd_card *card; 6562306a36Sopenharmony_ci struct snd_rawmidi *midi[MAX_MIDI_DEVICES]; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct platform_device *devices[SNDRV_CARDS]; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int snd_virmidi_probe(struct platform_device *devptr) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct snd_card *card; 7462306a36Sopenharmony_ci struct snd_card_virmidi *vmidi; 7562306a36Sopenharmony_ci int idx, err; 7662306a36Sopenharmony_ci int dev = devptr->id; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, 7962306a36Sopenharmony_ci sizeof(struct snd_card_virmidi), &card); 8062306a36Sopenharmony_ci if (err < 0) 8162306a36Sopenharmony_ci return err; 8262306a36Sopenharmony_ci vmidi = card->private_data; 8362306a36Sopenharmony_ci vmidi->card = card; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (midi_devs[dev] > MAX_MIDI_DEVICES) { 8662306a36Sopenharmony_ci snd_printk(KERN_WARNING 8762306a36Sopenharmony_ci "too much midi devices for virmidi %d: force to use %d\n", 8862306a36Sopenharmony_ci dev, MAX_MIDI_DEVICES); 8962306a36Sopenharmony_ci midi_devs[dev] = MAX_MIDI_DEVICES; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci for (idx = 0; idx < midi_devs[dev]; idx++) { 9262306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci err = snd_virmidi_new(card, idx, &rmidi); 9562306a36Sopenharmony_ci if (err < 0) 9662306a36Sopenharmony_ci return err; 9762306a36Sopenharmony_ci vmidi->midi[idx] = rmidi; 9862306a36Sopenharmony_ci strcpy(rmidi->name, "Virtual Raw MIDI"); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci strcpy(card->driver, "VirMIDI"); 10262306a36Sopenharmony_ci strcpy(card->shortname, "VirMIDI"); 10362306a36Sopenharmony_ci sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci err = snd_card_register(card); 10662306a36Sopenharmony_ci if (err) 10762306a36Sopenharmony_ci return err; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci platform_set_drvdata(devptr, card); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define SND_VIRMIDI_DRIVER "snd_virmidi" 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct platform_driver snd_virmidi_driver = { 11662306a36Sopenharmony_ci .probe = snd_virmidi_probe, 11762306a36Sopenharmony_ci .driver = { 11862306a36Sopenharmony_ci .name = SND_VIRMIDI_DRIVER, 11962306a36Sopenharmony_ci }, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void snd_virmidi_unregister_all(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(devices); ++i) 12762306a36Sopenharmony_ci platform_device_unregister(devices[i]); 12862306a36Sopenharmony_ci platform_driver_unregister(&snd_virmidi_driver); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int __init alsa_card_virmidi_init(void) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i, cards, err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = platform_driver_register(&snd_virmidi_driver); 13662306a36Sopenharmony_ci if (err < 0) 13762306a36Sopenharmony_ci return err; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cards = 0; 14062306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 14162306a36Sopenharmony_ci struct platform_device *device; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!enable[i]) 14462306a36Sopenharmony_ci continue; 14562306a36Sopenharmony_ci device = platform_device_register_simple(SND_VIRMIDI_DRIVER, 14662306a36Sopenharmony_ci i, NULL, 0); 14762306a36Sopenharmony_ci if (IS_ERR(device)) 14862306a36Sopenharmony_ci continue; 14962306a36Sopenharmony_ci if (!platform_get_drvdata(device)) { 15062306a36Sopenharmony_ci platform_device_unregister(device); 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci devices[i] = device; 15462306a36Sopenharmony_ci cards++; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci if (!cards) { 15762306a36Sopenharmony_ci#ifdef MODULE 15862306a36Sopenharmony_ci printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n"); 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci snd_virmidi_unregister_all(); 16162306a36Sopenharmony_ci return -ENODEV; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void __exit alsa_card_virmidi_exit(void) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci snd_virmidi_unregister_all(); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cimodule_init(alsa_card_virmidi_init) 17262306a36Sopenharmony_cimodule_exit(alsa_card_virmidi_exit) 173