File Index
Files and directories
Sign in
FILE

seq_oss_midi.c

File preview with syntax highlighting.

linux-6.19.14/sound/core/seq/oss/seq_oss_midi.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * OSS compatible sequencer driver
  4 *
  5 * MIDI device handlers
  6 *
  7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
  8 */
  9
 10#include <sound/asoundef.h>
 11#include "seq_oss_midi.h"
 12#include "seq_oss_readq.h"
 13#include "seq_oss_timer.h"
 14#include "seq_oss_event.h"
 15#include <sound/seq_midi_event.h>
 16#include "../seq_lock.h"
 17#include <linux/init.h>
 18#include <linux/slab.h>
 19#include <linux/nospec.h>
 20
 21
 22/*
 23 * constants
 24 */
 25#define SNDRV_SEQ_OSS_MAX_MIDI_NAME	30
 26
 27/*
 28 * definition of midi device record
 29 */
 30struct seq_oss_midi {
 31	int seq_device;		/* device number */
 32	int client;		/* sequencer client number */
 33	int port;		/* sequencer port number */
 34	unsigned int flags;	/* port capability */
 35	int opened;		/* flag for opening */
 36	unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
 37	struct snd_midi_event *coder;	/* MIDI event coder */
 38	struct seq_oss_devinfo *devinfo;	/* assigned OSSseq device */
 39	snd_use_lock_t use_lock;
 40	struct mutex open_mutex;
 41};
 42
 43DEFINE_FREE(seq_oss_midi, struct seq_oss_midi *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock))
 44
 45/*
 46 * midi device table
 47 */
 48static int max_midi_devs;
 49static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
 50
 51static DEFINE_SPINLOCK(register_lock);
 52
 53/*
 54 * prototypes
 55 */
 56static struct seq_oss_midi *get_mdev(int dev);
 57static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev);
 58static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev);
 59static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev);
 60
 61/*
 62 * look up the existing ports
 63 * this looks a very exhausting job.
 64 */
 65int
 66snd_seq_oss_midi_lookup_ports(int client)
 67{
 68	struct snd_seq_client_info *clinfo __free(kfree) =
 69		kzalloc(sizeof(*clinfo), GFP_KERNEL);
 70	struct snd_seq_port_info *pinfo __free(kfree) =
 71		kzalloc(sizeof(*pinfo), GFP_KERNEL);
 72
 73	if (!clinfo || !pinfo)
 74		return -ENOMEM;
 75	clinfo->client = -1;
 76	while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
 77		if (clinfo->client == client)
 78			continue; /* ignore myself */
 79		pinfo->addr.client = clinfo->client;
 80		pinfo->addr.port = -1;
 81		while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
 82			snd_seq_oss_midi_check_new_port(pinfo);
 83	}
 84	return 0;
 85}
 86
 87
 88/*
 89 */
 90static struct seq_oss_midi *
 91get_mdev(int dev)
 92{
 93	struct seq_oss_midi *mdev;
 94
 95	guard(spinlock_irqsave)(&register_lock);
 96	mdev = midi_devs[dev];
 97	if (mdev)
 98		snd_use_lock_use(&mdev->use_lock);
 99	return mdev;
100}
101
102/*
103 * look for the identical slot
104 */
105static struct seq_oss_midi *
106find_slot(int client, int port)
107{
108	int i;
109	struct seq_oss_midi *mdev;
110
111	guard(spinlock_irqsave)(&register_lock);
112	for (i = 0; i < max_midi_devs; i++) {
113		mdev = midi_devs[i];
114		if (mdev && mdev->client == client && mdev->port == port) {
115			/* found! */
116			snd_use_lock_use(&mdev->use_lock);
117			return mdev;
118		}
119	}
120	return NULL;
121}
122
123
124#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
125#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
126/*
127 * register a new port if it doesn't exist yet
128 */
129int
130snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
131{
132	int i;
133	struct seq_oss_midi *mdev;
134
135	/* the port must include generic midi */
136	if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
137		return 0;
138	/* either read or write subscribable */
139	if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
140	    (pinfo->capability & PERM_READ) != PERM_READ)
141		return 0;
142
143	/*
144	 * look for the identical slot
145	 */
146	mdev = find_slot(pinfo->addr.client, pinfo->addr.port);
147	if (mdev) {
148		/* already exists */
149		snd_use_lock_free(&mdev->use_lock);
150		return 0;
151	}
152
153	/*
154	 * allocate midi info record
155	 */
156	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
157	if (!mdev)
158		return -ENOMEM;
159
160	/* copy the port information */
161	mdev->client = pinfo->addr.client;
162	mdev->port = pinfo->addr.port;
163	mdev->flags = pinfo->capability;
164	mdev->opened = 0;
165	snd_use_lock_init(&mdev->use_lock);
166	mutex_init(&mdev->open_mutex);
167
168	/* copy and truncate the name of synth device */
169	strscpy(mdev->name, pinfo->name, sizeof(mdev->name));
170
171	/* create MIDI coder */
172	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
173		pr_err("ALSA: seq_oss: can't malloc midi coder\n");
174		kfree(mdev);
175		return -ENOMEM;
176	}
177	/* OSS sequencer adds running status to all sequences */
178	snd_midi_event_no_status(mdev->coder, 1);
179
180	/*
181	 * look for en empty slot
182	 */
183	guard(spinlock_irqsave)(&register_lock);
184	for (i = 0; i < max_midi_devs; i++) {
185		if (midi_devs[i] == NULL)
186			break;
187	}
188	if (i >= max_midi_devs) {
189		if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
190			snd_midi_event_free(mdev->coder);
191			kfree(mdev);
192			return -ENOMEM;
193		}
194		max_midi_devs++;
195	}
196	mdev->seq_device = i;
197	midi_devs[mdev->seq_device] = mdev;
198
199	return 0;
200}
201
202/*
203 * release the midi device if it was registered
204 */
205int
206snd_seq_oss_midi_check_exit_port(int client, int port)
207{
208	struct seq_oss_midi *mdev;
209	int index;
210
211	mdev = find_slot(client, port);
212	if (mdev) {
213		scoped_guard(spinlock_irqsave, &register_lock) {
214			midi_devs[mdev->seq_device] = NULL;
215		}
216		snd_use_lock_free(&mdev->use_lock);
217		snd_use_lock_sync(&mdev->use_lock);
218		snd_midi_event_free(mdev->coder);
219		kfree(mdev);
220	}
221	guard(spinlock_irqsave)(&register_lock);
222	for (index = max_midi_devs - 1; index >= 0; index--) {
223		if (midi_devs[index])
224			break;
225	}
226	max_midi_devs = index + 1;
227	return 0;
228}
229
230
231/*
232 * release the midi device if it was registered
233 */
234void
235snd_seq_oss_midi_clear_all(void)
236{
237	int i;
238	struct seq_oss_midi *mdev;
239
240	guard(spinlock_irqsave)(&register_lock);
241	for (i = 0; i < max_midi_devs; i++) {
242		mdev = midi_devs[i];
243		if (mdev) {
244			snd_midi_event_free(mdev->coder);
245			kfree(mdev);
246			midi_devs[i] = NULL;
247		}
248	}
249	max_midi_devs = 0;
250}
251
252
253/*
254 * set up midi tables
255 */
256void
257snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)
258{
259	guard(spinlock_irq)(&register_lock);
260	dp->max_mididev = max_midi_devs;
261}
262
263/*
264 * clean up midi tables
265 */
266void
267snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp)
268{
269	int i;
270	for (i = 0; i < dp->max_mididev; i++)
271		snd_seq_oss_midi_close(dp, i);
272	dp->max_mididev = 0;
273}
274
275
276/*
277 * open all midi devices.  ignore errors.
278 */
279void
280snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode)
281{
282	int i;
283	for (i = 0; i < dp->max_mididev; i++)
284		snd_seq_oss_midi_open(dp, i, file_mode);
285}
286
287
288/*
289 * get the midi device information
290 */
291static struct seq_oss_midi *
292get_mididev(struct seq_oss_devinfo *dp, int dev)
293{
294	if (dev < 0 || dev >= dp->max_mididev)
295		return NULL;
296	dev = array_index_nospec(dev, dp->max_mididev);
297	return get_mdev(dev);
298}
299
300
301/*
302 * open the midi device if not opened yet
303 */
304int
305snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
306{
307	int perm;
308	struct snd_seq_port_subscribe subs;
309	struct seq_oss_midi *mdev __free(seq_oss_midi) =
310		get_mididev(dp, dev);
311
312	if (!mdev)
313		return -ENODEV;
314
315	guard(mutex)(&mdev->open_mutex);
316	/* already used? */
317	if (mdev->opened && mdev->devinfo != dp)
318		return -EBUSY;
319
320	perm = 0;
321	if (is_write_mode(fmode))
322		perm |= PERM_WRITE;
323	if (is_read_mode(fmode))
324		perm |= PERM_READ;
325	perm &= mdev->flags;
326	if (perm == 0)
327		return -ENXIO;
328
329	/* already opened? */
330	if ((mdev->opened & perm) == perm)
331		return 0;
332
333	perm &= ~mdev->opened;
334
335	memset(&subs, 0, sizeof(subs));
336
337	if (perm & PERM_WRITE) {
338		subs.sender = dp->addr;
339		subs.dest.client = mdev->client;
340		subs.dest.port = mdev->port;
341		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
342			mdev->opened |= PERM_WRITE;
343	}
344	if (perm & PERM_READ) {
345		subs.sender.client = mdev->client;
346		subs.sender.port = mdev->port;
347		subs.dest = dp->addr;
348		subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
349		subs.queue = dp->queue;		/* queue for timestamps */
350		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
351			mdev->opened |= PERM_READ;
352	}
353
354	if (!mdev->opened)
355		return -ENXIO;
356
357	mdev->devinfo = dp;
358	return 0;
359}
360
361/*
362 * close the midi device if already opened
363 */
364int
365snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
366{
367	struct snd_seq_port_subscribe subs;
368	struct seq_oss_midi *mdev __free(seq_oss_midi) =
369		get_mididev(dp, dev);
370
371	if (!mdev)
372		return -ENODEV;
373	guard(mutex)(&mdev->open_mutex);
374	if (!mdev->opened || mdev->devinfo != dp)
375		return 0;
376
377	memset(&subs, 0, sizeof(subs));
378	if (mdev->opened & PERM_WRITE) {
379		subs.sender = dp->addr;
380		subs.dest.client = mdev->client;
381		subs.dest.port = mdev->port;
382		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
383	}
384	if (mdev->opened & PERM_READ) {
385		subs.sender.client = mdev->client;
386		subs.sender.port = mdev->port;
387		subs.dest = dp->addr;
388		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
389	}
390
391	mdev->opened = 0;
392	mdev->devinfo = NULL;
393	return 0;
394}
395
396/*
397 * change seq capability flags to file mode flags
398 */
399int
400snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
401{
402	int mode;
403	struct seq_oss_midi *mdev __free(seq_oss_midi) =
404		get_mididev(dp, dev);
405
406	if (!mdev)
407		return 0;
408
409	mode = 0;
410	if (mdev->opened & PERM_WRITE)
411		mode |= SNDRV_SEQ_OSS_FILE_WRITE;
412	if (mdev->opened & PERM_READ)
413		mode |= SNDRV_SEQ_OSS_FILE_READ;
414
415	return mode;
416}
417
418/*
419 * reset the midi device and close it:
420 * so far, only close the device.
421 */
422void
423snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
424{
425	struct seq_oss_midi *mdev __free(seq_oss_midi) =
426		get_mididev(dp, dev);
427
428	if (!mdev)
429		return;
430	if (!mdev->opened)
431		return;
432
433	if (mdev->opened & PERM_WRITE) {
434		struct snd_seq_event ev;
435		int c;
436
437		memset(&ev, 0, sizeof(ev));
438		ev.dest.client = mdev->client;
439		ev.dest.port = mdev->port;
440		ev.queue = dp->queue;
441		ev.source.port = dp->port;
442		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
443			ev.type = SNDRV_SEQ_EVENT_SENSING;
444			snd_seq_oss_dispatch(dp, &ev, 0, 0);
445		}
446		for (c = 0; c < 16; c++) {
447			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
448			ev.data.control.channel = c;
449			ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
450			snd_seq_oss_dispatch(dp, &ev, 0, 0);
451			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
452				ev.data.control.param =
453					MIDI_CTL_RESET_CONTROLLERS;
454				snd_seq_oss_dispatch(dp, &ev, 0, 0);
455				ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
456				ev.data.control.value = 0;
457				snd_seq_oss_dispatch(dp, &ev, 0, 0);
458			}
459		}
460	}
461	// snd_seq_oss_midi_close(dp, dev);
462}
463
464
465/*
466 * get client/port of the specified MIDI device
467 */
468void
469snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)
470{
471	struct seq_oss_midi *mdev __free(seq_oss_midi) =
472		get_mididev(dp, dev);
473
474	if (!mdev)
475		return;
476	addr->client = mdev->client;
477	addr->port = mdev->port;
478}
479
480
481/*
482 * input callback - this can be atomic
483 */
484int
485snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
486{
487	struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
488
489	if (dp->readq == NULL)
490		return 0;
491	struct seq_oss_midi *mdev __free(seq_oss_midi) =
492		find_slot(ev->source.client, ev->source.port);
493	if (!mdev)
494		return 0;
495	if (!(mdev->opened & PERM_READ))
496		return 0;
497
498	if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
499		return send_synth_event(dp, ev, mdev->seq_device);
500	else
501		return send_midi_event(dp, ev, mdev);
502}
503
504/*
505 * convert ALSA sequencer event to OSS synth event
506 */
507static int
508send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev)
509{
510	union evrec ossev;
511
512	memset(&ossev, 0, sizeof(ossev));
513
514	switch (ev->type) {
515	case SNDRV_SEQ_EVENT_NOTEON:
516		ossev.v.cmd = MIDI_NOTEON; break;
517	case SNDRV_SEQ_EVENT_NOTEOFF:
518		ossev.v.cmd = MIDI_NOTEOFF; break;
519	case SNDRV_SEQ_EVENT_KEYPRESS:
520		ossev.v.cmd = MIDI_KEY_PRESSURE; break;
521	case SNDRV_SEQ_EVENT_CONTROLLER:
522		ossev.l.cmd = MIDI_CTL_CHANGE; break;
523	case SNDRV_SEQ_EVENT_PGMCHANGE:
524		ossev.l.cmd = MIDI_PGM_CHANGE; break;
525	case SNDRV_SEQ_EVENT_CHANPRESS:
526		ossev.l.cmd = MIDI_CHN_PRESSURE; break;
527	case SNDRV_SEQ_EVENT_PITCHBEND:
528		ossev.l.cmd = MIDI_PITCH_BEND; break;
529	default:
530		return 0; /* not supported */
531	}
532
533	ossev.v.dev = dev;
534
535	switch (ev->type) {
536	case SNDRV_SEQ_EVENT_NOTEON:
537	case SNDRV_SEQ_EVENT_NOTEOFF:
538	case SNDRV_SEQ_EVENT_KEYPRESS:
539		ossev.v.code = EV_CHN_VOICE;
540		ossev.v.note = ev->data.note.note;
541		ossev.v.parm = ev->data.note.velocity;
542		ossev.v.chn = ev->data.note.channel;
543		break;
544	case SNDRV_SEQ_EVENT_CONTROLLER:
545	case SNDRV_SEQ_EVENT_PGMCHANGE:
546	case SNDRV_SEQ_EVENT_CHANPRESS:
547		ossev.l.code = EV_CHN_COMMON;
548		ossev.l.p1 = ev->data.control.param;
549		ossev.l.val = ev->data.control.value;
550		ossev.l.chn = ev->data.control.channel;
551		break;
552	case SNDRV_SEQ_EVENT_PITCHBEND:
553		ossev.l.code = EV_CHN_COMMON;
554		ossev.l.val = ev->data.control.value + 8192;
555		ossev.l.chn = ev->data.control.channel;
556		break;
557	}
558	
559	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
560	snd_seq_oss_readq_put_event(dp->readq, &ossev);
561
562	return 0;
563}
564
565/*
566 * decode event and send MIDI bytes to read queue
567 */
568static int
569send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev)
570{
571	char msg[32];
572	int len;
573	
574	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
575	if (!dp->timer->running)
576		len = snd_seq_oss_timer_start(dp->timer);
577	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
578		snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);
579		snd_midi_event_reset_decode(mdev->coder);
580	} else {
581		len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
582		if (len > 0)
583			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
584	}
585
586	return 0;
587}
588
589
590/*
591 * dump midi data
592 * return 0 : enqueued
593 *        non-zero : invalid - ignored
594 */
595int
596snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
597{
598	struct seq_oss_midi *mdev __free(seq_oss_midi) =
599		get_mididev(dp, dev);
600
601	if (!mdev)
602		return -ENODEV;
603	if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
604		snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
605		return 0;
606	}
607	return -EINVAL;
608}
609
610/*
611 * create OSS compatible midi_info record
612 */
613int
614snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)
615{
616	struct seq_oss_midi *mdev __free(seq_oss_midi) =
617		get_mididev(dp, dev);
618
619	if (!mdev)
620		return -ENXIO;
621	inf->device = dev;
622	inf->dev_type = 0; /* FIXME: ?? */
623	inf->capabilities = 0; /* FIXME: ?? */
624	strscpy(inf->name, mdev->name, sizeof(inf->name));
625	return 0;
626}
627
628
629#ifdef CONFIG_SND_PROC_FS
630/*
631 * proc interface
632 */
633static char *
634capmode_str(int val)
635{
636	val &= PERM_READ|PERM_WRITE;
637	if (val == (PERM_READ|PERM_WRITE))
638		return "read/write";
639	else if (val == PERM_READ)
640		return "read";
641	else if (val == PERM_WRITE)
642		return "write";
643	else
644		return "none";
645}
646
647void
648snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
649{
650	int i;
651
652	snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
653	for (i = 0; i < max_midi_devs; i++) {
654		snd_iprintf(buf, "\nmidi %d: ", i);
655		struct seq_oss_midi *mdev __free(seq_oss_midi) =
656			get_mdev(i);
657		if (mdev == NULL) {
658			snd_iprintf(buf, "*empty*\n");
659			continue;
660		}
661		snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
662			    mdev->client, mdev->port);
663		snd_iprintf(buf, "  capability %s / opened %s\n",
664			    capmode_str(mdev->flags),
665			    capmode_str(mdev->opened));
666	}
667}
668#endif /* CONFIG_SND_PROC_FS */
~/ Read-only file preview.