xref: /linux/sound/drivers/virmidi.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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