11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Dummy soundcard for virtual rawmidi devices
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds /*
91da177e4SLinus Torvalds * VIRTUAL RAW MIDI DEVICE CARDS
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * This dummy card contains up to 4 virtual rawmidi devices.
121da177e4SLinus Torvalds * They are not real rawmidi devices but just associated with sequencer
131da177e4SLinus Torvalds * clients, so that any input/output sources can be connected as a raw
141da177e4SLinus Torvalds * MIDI device arbitrary.
151da177e4SLinus Torvalds * Also, multiple access is allowed to a single rawmidi device.
161da177e4SLinus Torvalds *
171da177e4SLinus Torvalds * Typical usage is like following:
181da177e4SLinus Torvalds * - Load snd-virmidi module.
191da177e4SLinus Torvalds * # modprobe snd-virmidi index=2
201da177e4SLinus Torvalds * Then, sequencer clients 72:0 to 75:0 will be created, which are
211da177e4SLinus Torvalds * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively.
221da177e4SLinus Torvalds *
231da177e4SLinus Torvalds * - Connect input/output via aconnect.
241da177e4SLinus Torvalds * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0
251da177e4SLinus Torvalds * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0
261da177e4SLinus Torvalds *
271da177e4SLinus Torvalds * - Run application using a midi device (eg. /dev/snd/midiC1D0)
281da177e4SLinus Torvalds */
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds #include <linux/init.h>
311da177e4SLinus Torvalds #include <linux/wait.h>
323564fbb8STakashi Iwai #include <linux/err.h>
333564fbb8STakashi Iwai #include <linux/platform_device.h>
3465a77217SPaul Gortmaker #include <linux/module.h>
351da177e4SLinus Torvalds #include <sound/core.h>
361da177e4SLinus Torvalds #include <sound/seq_kernel.h>
371da177e4SLinus Torvalds #include <sound/seq_virmidi.h>
381da177e4SLinus Torvalds #include <sound/initval.h>
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds /* hack: OSS defines midi_devs, so undefine it (versioned symbols) */
411da177e4SLinus Torvalds #undef midi_devs
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
441da177e4SLinus Torvalds MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices");
451da177e4SLinus Torvalds MODULE_LICENSE("GPL");
461da177e4SLinus Torvalds
47204bdb1bSClemens Ladisch #define MAX_MIDI_DEVICES 4
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
501da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
51a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
521da177e4SLinus Torvalds static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
551da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for virmidi soundcard.");
561da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
571da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for virmidi soundcard.");
581da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
591da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable this soundcard.");
601da177e4SLinus Torvalds module_param_array(midi_devs, int, NULL, 0444);
61204bdb1bSClemens Ladisch MODULE_PARM_DESC(midi_devs, "MIDI devices # (1-4)");
621da177e4SLinus Torvalds
634a4d2cfdSTakashi Iwai struct snd_card_virmidi {
644a4d2cfdSTakashi Iwai struct snd_card *card;
654a4d2cfdSTakashi Iwai struct snd_rawmidi *midi[MAX_MIDI_DEVICES];
664a4d2cfdSTakashi Iwai };
671da177e4SLinus Torvalds
68f7a9275dSClemens Ladisch static struct platform_device *devices[SNDRV_CARDS];
69f7a9275dSClemens Ladisch
701da177e4SLinus Torvalds
snd_virmidi_probe(struct platform_device * devptr)71fbbb01a1SBill Pemberton static int snd_virmidi_probe(struct platform_device *devptr)
721da177e4SLinus Torvalds {
734a4d2cfdSTakashi Iwai struct snd_card *card;
741da177e4SLinus Torvalds struct snd_card_virmidi *vmidi;
751da177e4SLinus Torvalds int idx, err;
763564fbb8STakashi Iwai int dev = devptr->id;
771da177e4SLinus Torvalds
78ed539fc3STakashi Iwai err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
79bd7dd77cSTakashi Iwai sizeof(struct snd_card_virmidi), &card);
80bd7dd77cSTakashi Iwai if (err < 0)
81bd7dd77cSTakashi Iwai return err;
829fe856e4SJoe Perches vmidi = card->private_data;
831da177e4SLinus Torvalds vmidi->card = card;
841da177e4SLinus Torvalds
851da177e4SLinus Torvalds if (midi_devs[dev] > MAX_MIDI_DEVICES) {
86*b5557ef9STakashi Iwai dev_warn(&devptr->dev,
87316638a5SKyle Chamberlin "too much midi devices for virmidi %d: force to use %d\n",
88316638a5SKyle Chamberlin dev, MAX_MIDI_DEVICES);
891da177e4SLinus Torvalds midi_devs[dev] = MAX_MIDI_DEVICES;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds for (idx = 0; idx < midi_devs[dev]; idx++) {
924a4d2cfdSTakashi Iwai struct snd_rawmidi *rmidi;
93316638a5SKyle Chamberlin
94316638a5SKyle Chamberlin err = snd_virmidi_new(card, idx, &rmidi);
95316638a5SKyle Chamberlin if (err < 0)
96ed539fc3STakashi Iwai return err;
971da177e4SLinus Torvalds vmidi->midi[idx] = rmidi;
981da177e4SLinus Torvalds strcpy(rmidi->name, "Virtual Raw MIDI");
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds strcpy(card->driver, "VirMIDI");
1021da177e4SLinus Torvalds strcpy(card->shortname, "VirMIDI");
1031da177e4SLinus Torvalds sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
10416dab54bSTakashi Iwai
105316638a5SKyle Chamberlin err = snd_card_register(card);
106ed539fc3STakashi Iwai if (err)
1071da177e4SLinus Torvalds return err;
1081da177e4SLinus Torvalds
109ed539fc3STakashi Iwai platform_set_drvdata(devptr, card);
1103564fbb8STakashi Iwai return 0;
1113564fbb8STakashi Iwai }
1123564fbb8STakashi Iwai
1133564fbb8STakashi Iwai #define SND_VIRMIDI_DRIVER "snd_virmidi"
1143564fbb8STakashi Iwai
1153564fbb8STakashi Iwai static struct platform_driver snd_virmidi_driver = {
1163564fbb8STakashi Iwai .probe = snd_virmidi_probe,
1173564fbb8STakashi Iwai .driver = {
1188bf01d8aSTakashi Iwai .name = SND_VIRMIDI_DRIVER,
1193564fbb8STakashi Iwai },
1203564fbb8STakashi Iwai };
1213564fbb8STakashi Iwai
snd_virmidi_unregister_all(void)122bdec0c72SRandy Dunlap static void snd_virmidi_unregister_all(void)
123f7a9275dSClemens Ladisch {
124f7a9275dSClemens Ladisch int i;
125f7a9275dSClemens Ladisch
126f7a9275dSClemens Ladisch for (i = 0; i < ARRAY_SIZE(devices); ++i)
127f7a9275dSClemens Ladisch platform_device_unregister(devices[i]);
128f7a9275dSClemens Ladisch platform_driver_unregister(&snd_virmidi_driver);
129f7a9275dSClemens Ladisch }
130f7a9275dSClemens Ladisch
alsa_card_virmidi_init(void)1311da177e4SLinus Torvalds static int __init alsa_card_virmidi_init(void)
1321da177e4SLinus Torvalds {
1333564fbb8STakashi Iwai int i, cards, err;
1341da177e4SLinus Torvalds
135316638a5SKyle Chamberlin err = platform_driver_register(&snd_virmidi_driver);
136316638a5SKyle Chamberlin if (err < 0)
1373564fbb8STakashi Iwai return err;
1383564fbb8STakashi Iwai
1393564fbb8STakashi Iwai cards = 0;
1408278ca8fSTakashi Iwai for (i = 0; i < SNDRV_CARDS; i++) {
1413564fbb8STakashi Iwai struct platform_device *device;
142316638a5SKyle Chamberlin
1438278ca8fSTakashi Iwai if (!enable[i])
1448278ca8fSTakashi Iwai continue;
1453564fbb8STakashi Iwai device = platform_device_register_simple(SND_VIRMIDI_DRIVER,
1463564fbb8STakashi Iwai i, NULL, 0);
147a182ee98SRene Herman if (IS_ERR(device))
148a182ee98SRene Herman continue;
1497152447dSRene Herman if (!platform_get_drvdata(device)) {
1507152447dSRene Herman platform_device_unregister(device);
1517152447dSRene Herman continue;
1527152447dSRene Herman }
153f7a9275dSClemens Ladisch devices[i] = device;
1541da177e4SLinus Torvalds cards++;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds if (!cards) {
1571da177e4SLinus Torvalds #ifdef MODULE
158*b5557ef9STakashi Iwai pr_err("Card-VirMIDI soundcard not found or device busy\n");
1591da177e4SLinus Torvalds #endif
160a182ee98SRene Herman snd_virmidi_unregister_all();
161a182ee98SRene Herman return -ENODEV;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds return 0;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds
alsa_card_virmidi_exit(void)1661da177e4SLinus Torvalds static void __exit alsa_card_virmidi_exit(void)
1671da177e4SLinus Torvalds {
168f7a9275dSClemens Ladisch snd_virmidi_unregister_all();
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds module_init(alsa_card_virmidi_init)
1721da177e4SLinus Torvalds module_exit(alsa_card_virmidi_exit)
173