brintos

brintos / linux-shallow public Read only

0
0
Text · 14.2 KiB · e339491 Raw
667 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OSS compatible sequencer driver4 *5 * synth device handlers6 *7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>8 */9 10#include "seq_oss_synth.h"11#include "seq_oss_midi.h"12#include "../seq_lock.h"13#include <linux/init.h>14#include <linux/module.h>15#include <linux/slab.h>16#include <linux/nospec.h>17 18/*19 * constants20 */21#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME	3022#define MAX_SYSEX_BUFLEN		12823 24 25/*26 * definition of synth info records27 */28 29/* sysex buffer */30struct seq_oss_synth_sysex {31	int len;32	int skip;33	unsigned char buf[MAX_SYSEX_BUFLEN];34};35 36/* synth info */37struct seq_oss_synth {38	int seq_device;39 40	/* for synth_info */41	int synth_type;42	int synth_subtype;43	int nr_voices;44 45	char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];46	struct snd_seq_oss_callback oper;47 48	int opened;49 50	void *private_data;51	snd_use_lock_t use_lock;52};53 54 55/*56 * device table57 */58static int max_synth_devs;59static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];60static struct seq_oss_synth midi_synth_dev = {61	.seq_device = -1,62	.synth_type = SYNTH_TYPE_MIDI,63	.synth_subtype = 0,64	.nr_voices = 16,65	.name = "MIDI",66};67 68static DEFINE_SPINLOCK(register_lock);69 70/*71 * prototypes72 */73static struct seq_oss_synth *get_synthdev(struct seq_oss_devinfo *dp, int dev);74static void reset_channels(struct seq_oss_synthinfo *info);75 76/*77 * global initialization78 */79void __init80snd_seq_oss_synth_init(void)81{82	snd_use_lock_init(&midi_synth_dev.use_lock);83}84 85/*86 * registration of the synth device87 */88int89snd_seq_oss_synth_probe(struct device *_dev)90{91	struct snd_seq_device *dev = to_seq_dev(_dev);92	int i;93	struct seq_oss_synth *rec;94	struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);95	unsigned long flags;96 97	rec = kzalloc(sizeof(*rec), GFP_KERNEL);98	if (!rec)99		return -ENOMEM;100	rec->seq_device = -1;101	rec->synth_type = reg->type;102	rec->synth_subtype = reg->subtype;103	rec->nr_voices = reg->nvoices;104	rec->oper = reg->oper;105	rec->private_data = reg->private_data;106	rec->opened = 0;107	snd_use_lock_init(&rec->use_lock);108 109	/* copy and truncate the name of synth device */110	strscpy(rec->name, dev->name, sizeof(rec->name));111 112	/* registration */113	spin_lock_irqsave(&register_lock, flags);114	for (i = 0; i < max_synth_devs; i++) {115		if (synth_devs[i] == NULL)116			break;117	}118	if (i >= max_synth_devs) {119		if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {120			spin_unlock_irqrestore(&register_lock, flags);121			pr_err("ALSA: seq_oss: no more synth slot\n");122			kfree(rec);123			return -ENOMEM;124		}125		max_synth_devs++;126	}127	rec->seq_device = i;128	synth_devs[i] = rec;129	spin_unlock_irqrestore(&register_lock, flags);130	dev->driver_data = rec;131#ifdef SNDRV_OSS_INFO_DEV_SYNTH132	if (i < SNDRV_CARDS)133		snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);134#endif135	return 0;136}137 138 139int140snd_seq_oss_synth_remove(struct device *_dev)141{142	struct snd_seq_device *dev = to_seq_dev(_dev);143	int index;144	struct seq_oss_synth *rec = dev->driver_data;145	unsigned long flags;146 147	spin_lock_irqsave(&register_lock, flags);148	for (index = 0; index < max_synth_devs; index++) {149		if (synth_devs[index] == rec)150			break;151	}152	if (index >= max_synth_devs) {153		spin_unlock_irqrestore(&register_lock, flags);154		pr_err("ALSA: seq_oss: can't unregister synth\n");155		return -EINVAL;156	}157	synth_devs[index] = NULL;158	if (index == max_synth_devs - 1) {159		for (index--; index >= 0; index--) {160			if (synth_devs[index])161				break;162		}163		max_synth_devs = index + 1;164	}165	spin_unlock_irqrestore(&register_lock, flags);166#ifdef SNDRV_OSS_INFO_DEV_SYNTH167	if (rec->seq_device < SNDRV_CARDS)168		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);169#endif170 171	snd_use_lock_sync(&rec->use_lock);172	kfree(rec);173 174	return 0;175}176 177 178/*179 */180static struct seq_oss_synth *181get_sdev(int dev)182{183	struct seq_oss_synth *rec;184	unsigned long flags;185 186	spin_lock_irqsave(&register_lock, flags);187	rec = synth_devs[dev];188	if (rec)189		snd_use_lock_use(&rec->use_lock);190	spin_unlock_irqrestore(&register_lock, flags);191	return rec;192}193 194 195/*196 * set up synth tables197 */198 199void200snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)201{202	int i;203	struct seq_oss_synth *rec;204	struct seq_oss_synthinfo *info;205 206	dp->max_synthdev = max_synth_devs;207	dp->synth_opened = 0;208	memset(dp->synths, 0, sizeof(dp->synths));209	for (i = 0; i < dp->max_synthdev; i++) {210		rec = get_sdev(i);211		if (rec == NULL)212			continue;213		if (rec->oper.open == NULL || rec->oper.close == NULL) {214			snd_use_lock_free(&rec->use_lock);215			continue;216		}217		info = &dp->synths[i];218		info->arg.app_index = dp->port;219		info->arg.file_mode = dp->file_mode;220		info->arg.seq_mode = dp->seq_mode;221		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)222			info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;223		else224			info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;225		info->opened = 0;226		if (!try_module_get(rec->oper.owner)) {227			snd_use_lock_free(&rec->use_lock);228			continue;229		}230		if (rec->oper.open(&info->arg, rec->private_data) < 0) {231			module_put(rec->oper.owner);232			snd_use_lock_free(&rec->use_lock);233			continue;234		}235		info->nr_voices = rec->nr_voices;236		if (info->nr_voices > 0) {237			info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);238			if (!info->ch) {239				rec->oper.close(&info->arg);240				module_put(rec->oper.owner);241				snd_use_lock_free(&rec->use_lock);242				continue;243			}244			reset_channels(info);245		}246		info->opened++;247		rec->opened++;248		dp->synth_opened++;249		snd_use_lock_free(&rec->use_lock);250	}251}252 253 254/*255 * set up synth tables for MIDI emulation - /dev/music mode only256 */257 258void259snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp)260{261	int i;262 263	if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)264		return;265 266	for (i = 0; i < dp->max_mididev; i++) {267		struct seq_oss_synthinfo *info;268		info = &dp->synths[dp->max_synthdev];269		if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)270			continue;271		info->arg.app_index = dp->port;272		info->arg.file_mode = dp->file_mode;273		info->arg.seq_mode = dp->seq_mode;274		info->arg.private_data = info;275		info->is_midi = 1;276		info->midi_mapped = i;277		info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;278		snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);279		info->opened = 1;280		midi_synth_dev.opened++;281		dp->max_synthdev++;282		if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)283			break;284	}285}286 287 288/*289 * clean up synth tables290 */291 292void293snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)294{295	int i;296	struct seq_oss_synth *rec;297	struct seq_oss_synthinfo *info;298 299	if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))300		return;301	for (i = 0; i < dp->max_synthdev; i++) {302		info = &dp->synths[i];303		if (! info->opened)304			continue;305		if (info->is_midi) {306			if (midi_synth_dev.opened > 0) {307				snd_seq_oss_midi_close(dp, info->midi_mapped);308				midi_synth_dev.opened--;309			}310		} else {311			rec = get_sdev(i);312			if (rec == NULL)313				continue;314			if (rec->opened > 0) {315				rec->oper.close(&info->arg);316				module_put(rec->oper.owner);317				rec->opened = 0;318			}319			snd_use_lock_free(&rec->use_lock);320		}321		kfree(info->sysex);322		info->sysex = NULL;323		kfree(info->ch);324		info->ch = NULL;325	}326	dp->synth_opened = 0;327	dp->max_synthdev = 0;328}329 330static struct seq_oss_synthinfo *331get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev)332{333	if (dev < 0 || dev >= dp->max_synthdev)334		return NULL;335	dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS);336	return &dp->synths[dev];337}338 339/*340 * return synth device information pointer341 */342static struct seq_oss_synth *343get_synthdev(struct seq_oss_devinfo *dp, int dev)344{345	struct seq_oss_synth *rec;346	struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);347 348	if (!info)349		return NULL;350	if (!info->opened)351		return NULL;352	if (info->is_midi) {353		rec = &midi_synth_dev;354		snd_use_lock_use(&rec->use_lock);355	} else {356		rec = get_sdev(dev);357		if (!rec)358			return NULL;359	}360	if (! rec->opened) {361		snd_use_lock_free(&rec->use_lock);362		return NULL;363	}364	return rec;365}366 367 368/*369 * reset note and velocity on each channel.370 */371static void372reset_channels(struct seq_oss_synthinfo *info)373{374	int i;375	if (info->ch == NULL || ! info->nr_voices)376		return;377	for (i = 0; i < info->nr_voices; i++) {378		info->ch[i].note = -1;379		info->ch[i].vel = 0;380	}381}382 383 384/*385 * reset synth device:386 * call reset callback.  if no callback is defined, send a heartbeat387 * event to the corresponding port.388 */389void390snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)391{392	struct seq_oss_synth *rec;393	struct seq_oss_synthinfo *info;394 395	info = get_synthinfo_nospec(dp, dev);396	if (!info || !info->opened)397		return;398	if (info->sysex)399		info->sysex->len = 0; /* reset sysex */400	reset_channels(info);401	if (info->is_midi) {402		if (midi_synth_dev.opened <= 0)403			return;404		snd_seq_oss_midi_reset(dp, info->midi_mapped);405		/* reopen the device */406		snd_seq_oss_midi_close(dp, dev);407		if (snd_seq_oss_midi_open(dp, info->midi_mapped,408					  dp->file_mode) < 0) {409			midi_synth_dev.opened--;410			info->opened = 0;411			kfree(info->sysex);412			info->sysex = NULL;413			kfree(info->ch);414			info->ch = NULL;415		}416		return;417	}418 419	rec = get_sdev(dev);420	if (rec == NULL)421		return;422	if (rec->oper.reset) {423		rec->oper.reset(&info->arg);424	} else {425		struct snd_seq_event ev;426		memset(&ev, 0, sizeof(ev));427		snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,428				      info->arg.addr.port);429		ev.type = SNDRV_SEQ_EVENT_RESET;430		snd_seq_oss_dispatch(dp, &ev, 0, 0);431	}432	snd_use_lock_free(&rec->use_lock);433}434 435 436/*437 * load a patch record:438 * call load_patch callback function439 */440int441snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,442			    const char __user *buf, int p, int c)443{444	struct seq_oss_synth *rec;445	struct seq_oss_synthinfo *info;446	int rc;447 448	info = get_synthinfo_nospec(dp, dev);449	if (!info)450		return -ENXIO;451 452	if (info->is_midi)453		return 0;454	rec = get_synthdev(dp, dev);455	if (!rec)456		return -ENXIO;457 458	if (rec->oper.load_patch == NULL)459		rc = -ENXIO;460	else461		rc = rec->oper.load_patch(&info->arg, fmt, buf, p, c);462	snd_use_lock_free(&rec->use_lock);463	return rc;464}465 466/*467 * check if the device is valid synth device and return the synth info468 */469struct seq_oss_synthinfo *470snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)471{472	struct seq_oss_synth *rec;473 474	rec = get_synthdev(dp, dev);475	if (rec) {476		snd_use_lock_free(&rec->use_lock);477		return get_synthinfo_nospec(dp, dev);478	}479	return NULL;480}481 482 483/*484 * receive OSS 6 byte sysex packet:485 * the full sysex message will be sent if it reaches to the end of data486 * (0xff).487 */488int489snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)490{491	int i, send;492	unsigned char *dest;493	struct seq_oss_synth_sysex *sysex;494	struct seq_oss_synthinfo *info;495 496	info = snd_seq_oss_synth_info(dp, dev);497	if (!info)498		return -ENXIO;499 500	sysex = info->sysex;501	if (sysex == NULL) {502		sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);503		if (sysex == NULL)504			return -ENOMEM;505		info->sysex = sysex;506	}507 508	send = 0;509	dest = sysex->buf + sysex->len;510	/* copy 6 byte packet to the buffer */511	for (i = 0; i < 6; i++) {512		if (buf[i] == 0xff) {513			send = 1;514			break;515		}516		dest[i] = buf[i];517		sysex->len++;518		if (sysex->len >= MAX_SYSEX_BUFLEN) {519			sysex->len = 0;520			sysex->skip = 1;521			break;522		}523	}524 525	if (sysex->len && send) {526		if (sysex->skip) {527			sysex->skip = 0;528			sysex->len = 0;529			return -EINVAL; /* skip */530		}531		/* copy the data to event record and send it */532		ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;533		if (snd_seq_oss_synth_addr(dp, dev, ev))534			return -EINVAL;535		ev->data.ext.len = sysex->len;536		ev->data.ext.ptr = sysex->buf;537		sysex->len = 0;538		return 0;539	}540 541	return -EINVAL; /* skip */542}543 544/*545 * fill the event source/destination addresses546 */547int548snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)549{550	struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev);551 552	if (!info)553		return -EINVAL;554	snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client,555			      info->arg.addr.port);556	return 0;557}558 559 560/*561 * OSS compatible ioctl562 */563int564snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)565{566	struct seq_oss_synth *rec;567	struct seq_oss_synthinfo *info;568	int rc;569 570	info = get_synthinfo_nospec(dp, dev);571	if (!info || info->is_midi)572		return -ENXIO;573	rec = get_synthdev(dp, dev);574	if (!rec)575		return -ENXIO;576	if (rec->oper.ioctl == NULL)577		rc = -ENXIO;578	else579		rc = rec->oper.ioctl(&info->arg, cmd, addr);580	snd_use_lock_free(&rec->use_lock);581	return rc;582}583 584 585/*586 * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME587 */588int589snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)590{591	struct seq_oss_synthinfo *info;592 593	info = snd_seq_oss_synth_info(dp, dev);594	if (!info || info->is_midi)595		return -ENXIO;596	ev->type = SNDRV_SEQ_EVENT_OSS;597	memcpy(ev->data.raw8.d, data, 8);598	return snd_seq_oss_synth_addr(dp, dev, ev);599}600 601 602/*603 * create OSS compatible synth_info record604 */605int606snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf)607{608	struct seq_oss_synth *rec;609	struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);610 611	if (!info)612		return -ENXIO;613 614	if (info->is_midi) {615		struct midi_info minf;616		if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))617			return -ENXIO;618		inf->synth_type = SYNTH_TYPE_MIDI;619		inf->synth_subtype = 0;620		inf->nr_voices = 16;621		inf->device = dev;622		strscpy(inf->name, minf.name, sizeof(inf->name));623	} else {624		rec = get_synthdev(dp, dev);625		if (!rec)626			return -ENXIO;627		inf->synth_type = rec->synth_type;628		inf->synth_subtype = rec->synth_subtype;629		inf->nr_voices = rec->nr_voices;630		inf->device = dev;631		strscpy(inf->name, rec->name, sizeof(inf->name));632		snd_use_lock_free(&rec->use_lock);633	}634	return 0;635}636 637 638#ifdef CONFIG_SND_PROC_FS639/*640 * proc interface641 */642void643snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)644{645	int i;646	struct seq_oss_synth *rec;647 648	snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);649	for (i = 0; i < max_synth_devs; i++) {650		snd_iprintf(buf, "\nsynth %d: ", i);651		rec = get_sdev(i);652		if (rec == NULL) {653			snd_iprintf(buf, "*empty*\n");654			continue;655		}656		snd_iprintf(buf, "[%s]\n", rec->name);657		snd_iprintf(buf, "  type 0x%x : subtype 0x%x : voices %d\n",658			    rec->synth_type, rec->synth_subtype,659			    rec->nr_voices);660		snd_iprintf(buf, "  capabilities : ioctl %s / load_patch %s\n",661			    enabled_str((long)rec->oper.ioctl),662			    enabled_str((long)rec->oper.load_patch));663		snd_use_lock_free(&rec->use_lock);664	}665}666#endif /* CONFIG_SND_PROC_FS */667