brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · f8e247d Raw
714 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OSS compatible sequencer driver4 *5 * MIDI device handlers6 *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 * constants24 */25#define SNDRV_SEQ_OSS_MAX_MIDI_NAME	3026 27/*28 * definition of midi device record29 */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 43 44/*45 * midi device table46 */47static int max_midi_devs;48static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];49 50static DEFINE_SPINLOCK(register_lock);51 52/*53 * prototypes54 */55static struct seq_oss_midi *get_mdev(int dev);56static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev);57static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev);58static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev);59 60/*61 * look up the existing ports62 * this looks a very exhausting job.63 */64int65snd_seq_oss_midi_lookup_ports(int client)66{67	struct snd_seq_client_info *clinfo __free(kfree) = NULL;68	struct snd_seq_port_info *pinfo __free(kfree) = NULL;69 70	clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);71	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);72	if (!clinfo || !pinfo)73		return -ENOMEM;74	clinfo->client = -1;75	while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {76		if (clinfo->client == client)77			continue; /* ignore myself */78		pinfo->addr.client = clinfo->client;79		pinfo->addr.port = -1;80		while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)81			snd_seq_oss_midi_check_new_port(pinfo);82	}83	return 0;84}85 86 87/*88 */89static struct seq_oss_midi *90get_mdev(int dev)91{92	struct seq_oss_midi *mdev;93	unsigned long flags;94 95	spin_lock_irqsave(&register_lock, flags);96	mdev = midi_devs[dev];97	if (mdev)98		snd_use_lock_use(&mdev->use_lock);99	spin_unlock_irqrestore(&register_lock, flags);100	return mdev;101}102 103/*104 * look for the identical slot105 */106static struct seq_oss_midi *107find_slot(int client, int port)108{109	int i;110	struct seq_oss_midi *mdev;111	unsigned long flags;112 113	spin_lock_irqsave(&register_lock, flags);114	for (i = 0; i < max_midi_devs; i++) {115		mdev = midi_devs[i];116		if (mdev && mdev->client == client && mdev->port == port) {117			/* found! */118			snd_use_lock_use(&mdev->use_lock);119			spin_unlock_irqrestore(&register_lock, flags);120			return mdev;121		}122	}123	spin_unlock_irqrestore(&register_lock, flags);124	return NULL;125}126 127 128#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)129#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)130/*131 * register a new port if it doesn't exist yet132 */133int134snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)135{136	int i;137	struct seq_oss_midi *mdev;138	unsigned long flags;139 140	/* the port must include generic midi */141	if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))142		return 0;143	/* either read or write subscribable */144	if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&145	    (pinfo->capability & PERM_READ) != PERM_READ)146		return 0;147 148	/*149	 * look for the identical slot150	 */151	mdev = find_slot(pinfo->addr.client, pinfo->addr.port);152	if (mdev) {153		/* already exists */154		snd_use_lock_free(&mdev->use_lock);155		return 0;156	}157 158	/*159	 * allocate midi info record160	 */161	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);162	if (!mdev)163		return -ENOMEM;164 165	/* copy the port information */166	mdev->client = pinfo->addr.client;167	mdev->port = pinfo->addr.port;168	mdev->flags = pinfo->capability;169	mdev->opened = 0;170	snd_use_lock_init(&mdev->use_lock);171	mutex_init(&mdev->open_mutex);172 173	/* copy and truncate the name of synth device */174	strscpy(mdev->name, pinfo->name, sizeof(mdev->name));175 176	/* create MIDI coder */177	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {178		pr_err("ALSA: seq_oss: can't malloc midi coder\n");179		kfree(mdev);180		return -ENOMEM;181	}182	/* OSS sequencer adds running status to all sequences */183	snd_midi_event_no_status(mdev->coder, 1);184 185	/*186	 * look for en empty slot187	 */188	spin_lock_irqsave(&register_lock, flags);189	for (i = 0; i < max_midi_devs; i++) {190		if (midi_devs[i] == NULL)191			break;192	}193	if (i >= max_midi_devs) {194		if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {195			spin_unlock_irqrestore(&register_lock, flags);196			snd_midi_event_free(mdev->coder);197			kfree(mdev);198			return -ENOMEM;199		}200		max_midi_devs++;201	}202	mdev->seq_device = i;203	midi_devs[mdev->seq_device] = mdev;204	spin_unlock_irqrestore(&register_lock, flags);205 206	return 0;207}208 209/*210 * release the midi device if it was registered211 */212int213snd_seq_oss_midi_check_exit_port(int client, int port)214{215	struct seq_oss_midi *mdev;216	unsigned long flags;217	int index;218 219	mdev = find_slot(client, port);220	if (mdev) {221		spin_lock_irqsave(&register_lock, flags);222		midi_devs[mdev->seq_device] = NULL;223		spin_unlock_irqrestore(&register_lock, flags);224		snd_use_lock_free(&mdev->use_lock);225		snd_use_lock_sync(&mdev->use_lock);226		snd_midi_event_free(mdev->coder);227		kfree(mdev);228	}229	spin_lock_irqsave(&register_lock, flags);230	for (index = max_midi_devs - 1; index >= 0; index--) {231		if (midi_devs[index])232			break;233	}234	max_midi_devs = index + 1;235	spin_unlock_irqrestore(&register_lock, flags);236	return 0;237}238 239 240/*241 * release the midi device if it was registered242 */243void244snd_seq_oss_midi_clear_all(void)245{246	int i;247	struct seq_oss_midi *mdev;248	unsigned long flags;249 250	spin_lock_irqsave(&register_lock, flags);251	for (i = 0; i < max_midi_devs; i++) {252		mdev = midi_devs[i];253		if (mdev) {254			snd_midi_event_free(mdev->coder);255			kfree(mdev);256			midi_devs[i] = NULL;257		}258	}259	max_midi_devs = 0;260	spin_unlock_irqrestore(&register_lock, flags);261}262 263 264/*265 * set up midi tables266 */267void268snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)269{270	spin_lock_irq(&register_lock);271	dp->max_mididev = max_midi_devs;272	spin_unlock_irq(&register_lock);273}274 275/*276 * clean up midi tables277 */278void279snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp)280{281	int i;282	for (i = 0; i < dp->max_mididev; i++)283		snd_seq_oss_midi_close(dp, i);284	dp->max_mididev = 0;285}286 287 288/*289 * open all midi devices.  ignore errors.290 */291void292snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode)293{294	int i;295	for (i = 0; i < dp->max_mididev; i++)296		snd_seq_oss_midi_open(dp, i, file_mode);297}298 299 300/*301 * get the midi device information302 */303static struct seq_oss_midi *304get_mididev(struct seq_oss_devinfo *dp, int dev)305{306	if (dev < 0 || dev >= dp->max_mididev)307		return NULL;308	dev = array_index_nospec(dev, dp->max_mididev);309	return get_mdev(dev);310}311 312 313/*314 * open the midi device if not opened yet315 */316int317snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)318{319	int perm;320	struct seq_oss_midi *mdev;321	struct snd_seq_port_subscribe subs;322	int err;323 324	mdev = get_mididev(dp, dev);325	if (!mdev)326		return -ENODEV;327 328	mutex_lock(&mdev->open_mutex);329	/* already used? */330	if (mdev->opened && mdev->devinfo != dp) {331		err = -EBUSY;332		goto unlock;333	}334 335	perm = 0;336	if (is_write_mode(fmode))337		perm |= PERM_WRITE;338	if (is_read_mode(fmode))339		perm |= PERM_READ;340	perm &= mdev->flags;341	if (perm == 0) {342		err = -ENXIO;343		goto unlock;344	}345 346	/* already opened? */347	if ((mdev->opened & perm) == perm) {348		err = 0;349		goto unlock;350	}351 352	perm &= ~mdev->opened;353 354	memset(&subs, 0, sizeof(subs));355 356	if (perm & PERM_WRITE) {357		subs.sender = dp->addr;358		subs.dest.client = mdev->client;359		subs.dest.port = mdev->port;360		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)361			mdev->opened |= PERM_WRITE;362	}363	if (perm & PERM_READ) {364		subs.sender.client = mdev->client;365		subs.sender.port = mdev->port;366		subs.dest = dp->addr;367		subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;368		subs.queue = dp->queue;		/* queue for timestamps */369		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)370			mdev->opened |= PERM_READ;371	}372 373	if (! mdev->opened) {374		err = -ENXIO;375		goto unlock;376	}377 378	mdev->devinfo = dp;379	err = 0;380 381 unlock:382	mutex_unlock(&mdev->open_mutex);383	snd_use_lock_free(&mdev->use_lock);384	return err;385}386 387/*388 * close the midi device if already opened389 */390int391snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)392{393	struct seq_oss_midi *mdev;394	struct snd_seq_port_subscribe subs;395 396	mdev = get_mididev(dp, dev);397	if (!mdev)398		return -ENODEV;399	mutex_lock(&mdev->open_mutex);400	if (!mdev->opened || mdev->devinfo != dp)401		goto unlock;402 403	memset(&subs, 0, sizeof(subs));404	if (mdev->opened & PERM_WRITE) {405		subs.sender = dp->addr;406		subs.dest.client = mdev->client;407		subs.dest.port = mdev->port;408		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);409	}410	if (mdev->opened & PERM_READ) {411		subs.sender.client = mdev->client;412		subs.sender.port = mdev->port;413		subs.dest = dp->addr;414		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);415	}416 417	mdev->opened = 0;418	mdev->devinfo = NULL;419 420 unlock:421	mutex_unlock(&mdev->open_mutex);422	snd_use_lock_free(&mdev->use_lock);423	return 0;424}425 426/*427 * change seq capability flags to file mode flags428 */429int430snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)431{432	struct seq_oss_midi *mdev;433	int mode;434 435	mdev = get_mididev(dp, dev);436	if (!mdev)437		return 0;438 439	mode = 0;440	if (mdev->opened & PERM_WRITE)441		mode |= SNDRV_SEQ_OSS_FILE_WRITE;442	if (mdev->opened & PERM_READ)443		mode |= SNDRV_SEQ_OSS_FILE_READ;444 445	snd_use_lock_free(&mdev->use_lock);446	return mode;447}448 449/*450 * reset the midi device and close it:451 * so far, only close the device.452 */453void454snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)455{456	struct seq_oss_midi *mdev;457 458	mdev = get_mididev(dp, dev);459	if (!mdev)460		return;461	if (! mdev->opened) {462		snd_use_lock_free(&mdev->use_lock);463		return;464	}465 466	if (mdev->opened & PERM_WRITE) {467		struct snd_seq_event ev;468		int c;469 470		memset(&ev, 0, sizeof(ev));471		ev.dest.client = mdev->client;472		ev.dest.port = mdev->port;473		ev.queue = dp->queue;474		ev.source.port = dp->port;475		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {476			ev.type = SNDRV_SEQ_EVENT_SENSING;477			snd_seq_oss_dispatch(dp, &ev, 0, 0);478		}479		for (c = 0; c < 16; c++) {480			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;481			ev.data.control.channel = c;482			ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;483			snd_seq_oss_dispatch(dp, &ev, 0, 0);484			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {485				ev.data.control.param =486					MIDI_CTL_RESET_CONTROLLERS;487				snd_seq_oss_dispatch(dp, &ev, 0, 0);488				ev.type = SNDRV_SEQ_EVENT_PITCHBEND;489				ev.data.control.value = 0;490				snd_seq_oss_dispatch(dp, &ev, 0, 0);491			}492		}493	}494	// snd_seq_oss_midi_close(dp, dev);495	snd_use_lock_free(&mdev->use_lock);496}497 498 499/*500 * get client/port of the specified MIDI device501 */502void503snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)504{505	struct seq_oss_midi *mdev;506 507	mdev = get_mididev(dp, dev);508	if (!mdev)509		return;510	addr->client = mdev->client;511	addr->port = mdev->port;512	snd_use_lock_free(&mdev->use_lock);513}514 515 516/*517 * input callback - this can be atomic518 */519int520snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)521{522	struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;523	struct seq_oss_midi *mdev;524	int rc;525 526	if (dp->readq == NULL)527		return 0;528	mdev = find_slot(ev->source.client, ev->source.port);529	if (!mdev)530		return 0;531	if (! (mdev->opened & PERM_READ)) {532		snd_use_lock_free(&mdev->use_lock);533		return 0;534	}535 536	if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)537		rc = send_synth_event(dp, ev, mdev->seq_device);538	else539		rc = send_midi_event(dp, ev, mdev);540 541	snd_use_lock_free(&mdev->use_lock);542	return rc;543}544 545/*546 * convert ALSA sequencer event to OSS synth event547 */548static int549send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev)550{551	union evrec ossev;552 553	memset(&ossev, 0, sizeof(ossev));554 555	switch (ev->type) {556	case SNDRV_SEQ_EVENT_NOTEON:557		ossev.v.cmd = MIDI_NOTEON; break;558	case SNDRV_SEQ_EVENT_NOTEOFF:559		ossev.v.cmd = MIDI_NOTEOFF; break;560	case SNDRV_SEQ_EVENT_KEYPRESS:561		ossev.v.cmd = MIDI_KEY_PRESSURE; break;562	case SNDRV_SEQ_EVENT_CONTROLLER:563		ossev.l.cmd = MIDI_CTL_CHANGE; break;564	case SNDRV_SEQ_EVENT_PGMCHANGE:565		ossev.l.cmd = MIDI_PGM_CHANGE; break;566	case SNDRV_SEQ_EVENT_CHANPRESS:567		ossev.l.cmd = MIDI_CHN_PRESSURE; break;568	case SNDRV_SEQ_EVENT_PITCHBEND:569		ossev.l.cmd = MIDI_PITCH_BEND; break;570	default:571		return 0; /* not supported */572	}573 574	ossev.v.dev = dev;575 576	switch (ev->type) {577	case SNDRV_SEQ_EVENT_NOTEON:578	case SNDRV_SEQ_EVENT_NOTEOFF:579	case SNDRV_SEQ_EVENT_KEYPRESS:580		ossev.v.code = EV_CHN_VOICE;581		ossev.v.note = ev->data.note.note;582		ossev.v.parm = ev->data.note.velocity;583		ossev.v.chn = ev->data.note.channel;584		break;585	case SNDRV_SEQ_EVENT_CONTROLLER:586	case SNDRV_SEQ_EVENT_PGMCHANGE:587	case SNDRV_SEQ_EVENT_CHANPRESS:588		ossev.l.code = EV_CHN_COMMON;589		ossev.l.p1 = ev->data.control.param;590		ossev.l.val = ev->data.control.value;591		ossev.l.chn = ev->data.control.channel;592		break;593	case SNDRV_SEQ_EVENT_PITCHBEND:594		ossev.l.code = EV_CHN_COMMON;595		ossev.l.val = ev->data.control.value + 8192;596		ossev.l.chn = ev->data.control.channel;597		break;598	}599	600	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);601	snd_seq_oss_readq_put_event(dp->readq, &ossev);602 603	return 0;604}605 606/*607 * decode event and send MIDI bytes to read queue608 */609static int610send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev)611{612	char msg[32];613	int len;614	615	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);616	if (!dp->timer->running)617		len = snd_seq_oss_timer_start(dp->timer);618	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {619		snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);620		snd_midi_event_reset_decode(mdev->coder);621	} else {622		len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);623		if (len > 0)624			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);625	}626 627	return 0;628}629 630 631/*632 * dump midi data633 * return 0 : enqueued634 *        non-zero : invalid - ignored635 */636int637snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)638{639	struct seq_oss_midi *mdev;640 641	mdev = get_mididev(dp, dev);642	if (!mdev)643		return -ENODEV;644	if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {645		snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);646		snd_use_lock_free(&mdev->use_lock);647		return 0;648	}649	snd_use_lock_free(&mdev->use_lock);650	return -EINVAL;651}652 653/*654 * create OSS compatible midi_info record655 */656int657snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)658{659	struct seq_oss_midi *mdev;660 661	mdev = get_mididev(dp, dev);662	if (!mdev)663		return -ENXIO;664	inf->device = dev;665	inf->dev_type = 0; /* FIXME: ?? */666	inf->capabilities = 0; /* FIXME: ?? */667	strscpy(inf->name, mdev->name, sizeof(inf->name));668	snd_use_lock_free(&mdev->use_lock);669	return 0;670}671 672 673#ifdef CONFIG_SND_PROC_FS674/*675 * proc interface676 */677static char *678capmode_str(int val)679{680	val &= PERM_READ|PERM_WRITE;681	if (val == (PERM_READ|PERM_WRITE))682		return "read/write";683	else if (val == PERM_READ)684		return "read";685	else if (val == PERM_WRITE)686		return "write";687	else688		return "none";689}690 691void692snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)693{694	int i;695	struct seq_oss_midi *mdev;696 697	snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);698	for (i = 0; i < max_midi_devs; i++) {699		snd_iprintf(buf, "\nmidi %d: ", i);700		mdev = get_mdev(i);701		if (mdev == NULL) {702			snd_iprintf(buf, "*empty*\n");703			continue;704		}705		snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,706			    mdev->client, mdev->port);707		snd_iprintf(buf, "  capability %s / opened %s\n",708			    capmode_str(mdev->flags),709			    capmode_str(mdev->opened));710		snd_use_lock_free(&mdev->use_lock);711	}712}713#endif /* CONFIG_SND_PROC_FS */714