xref: /openbmc/linux/sound/synth/emux/emux_oss.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Interface for OSS sequencer emulation
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1999 Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Changes
81da177e4SLinus Torvalds  * 19990227   Steve Ratcliffe   Made separate file and merged in latest
91da177e4SLinus Torvalds  * 				midi emulation.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds 
13d81a6d71SPaul Gortmaker #include <linux/export.h>
14976412fbSTakashi Iwai #include <linux/uaccess.h>
151da177e4SLinus Torvalds #include <sound/core.h>
161da177e4SLinus Torvalds #include "emux_voice.h"
171da177e4SLinus Torvalds #include <sound/asoundef.h>
181da177e4SLinus Torvalds 
1903da312aSTakashi Iwai static int snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
2003da312aSTakashi Iwai static int snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg);
2103da312aSTakashi Iwai static int snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
2203da312aSTakashi Iwai 				  unsigned long ioarg);
2303da312aSTakashi Iwai static int snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
2403da312aSTakashi Iwai 				       const char __user *buf, int offs, int count);
2503da312aSTakashi Iwai static int snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg);
2603da312aSTakashi Iwai static int snd_emux_event_oss_input(struct snd_seq_event *ev, int direct,
2703da312aSTakashi Iwai 				    void *private, int atomic, int hop);
2803da312aSTakashi Iwai static void reset_port_mode(struct snd_emux_port *port, int midi_mode);
2903da312aSTakashi Iwai static void emuspec_control(struct snd_emux *emu, struct snd_emux_port *port,
3003da312aSTakashi Iwai 			    int cmd, unsigned char *event, int atomic, int hop);
3103da312aSTakashi Iwai static void gusspec_control(struct snd_emux *emu, struct snd_emux_port *port,
3203da312aSTakashi Iwai 			    int cmd, unsigned char *event, int atomic, int hop);
3303da312aSTakashi Iwai static void fake_event(struct snd_emux *emu, struct snd_emux_port *port,
3403da312aSTakashi Iwai 		       int ch, int param, int val, int atomic, int hop);
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds /* operators */
37*87065d3dSTakashi Iwai static const struct snd_seq_oss_callback oss_callback = {
381da177e4SLinus Torvalds 	.owner = THIS_MODULE,
391da177e4SLinus Torvalds 	.open = snd_emux_open_seq_oss,
401da177e4SLinus Torvalds 	.close = snd_emux_close_seq_oss,
411da177e4SLinus Torvalds 	.ioctl = snd_emux_ioctl_seq_oss,
421da177e4SLinus Torvalds 	.load_patch = snd_emux_load_patch_seq_oss,
431da177e4SLinus Torvalds 	.reset = snd_emux_reset_seq_oss,
441da177e4SLinus Torvalds };
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds  * register OSS synth
491da177e4SLinus Torvalds  */
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds void
snd_emux_init_seq_oss(struct snd_emux * emu)5203da312aSTakashi Iwai snd_emux_init_seq_oss(struct snd_emux *emu)
531da177e4SLinus Torvalds {
5403da312aSTakashi Iwai 	struct snd_seq_oss_reg *arg;
5503da312aSTakashi Iwai 	struct snd_seq_device *dev;
561da177e4SLinus Torvalds 
57225db576STakashi Iwai 	/* using device#1 here for avoiding conflicts with OPL3 */
58225db576STakashi Iwai 	if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS,
5903da312aSTakashi Iwai 			       sizeof(struct snd_seq_oss_reg), &dev) < 0)
601da177e4SLinus Torvalds 		return;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	emu->oss_synth = dev;
631da177e4SLinus Torvalds 	strcpy(dev->name, emu->name);
641da177e4SLinus Torvalds 	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
651da177e4SLinus Torvalds 	arg->type = SYNTH_TYPE_SAMPLE;
661da177e4SLinus Torvalds 	arg->subtype = SAMPLE_TYPE_AWE32;
671da177e4SLinus Torvalds 	arg->nvoices = emu->max_voices;
681da177e4SLinus Torvalds 	arg->oper = oss_callback;
691da177e4SLinus Torvalds 	arg->private_data = emu;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	/* register to OSS synth table */
721da177e4SLinus Torvalds 	snd_device_register(emu->card, dev);
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /*
771da177e4SLinus Torvalds  * unregister
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds void
snd_emux_detach_seq_oss(struct snd_emux * emu)8003da312aSTakashi Iwai snd_emux_detach_seq_oss(struct snd_emux *emu)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	if (emu->oss_synth) {
831da177e4SLinus Torvalds 		snd_device_free(emu->card, emu->oss_synth);
841da177e4SLinus Torvalds 		emu->oss_synth = NULL;
851da177e4SLinus Torvalds 	}
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds /* use port number as a unique soundfont client number */
901da177e4SLinus Torvalds #define SF_CLIENT_NO(p)	((p) + 0x1000)
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds  * open port for OSS sequencer
941da177e4SLinus Torvalds  */
951da177e4SLinus Torvalds static int
snd_emux_open_seq_oss(struct snd_seq_oss_arg * arg,void * closure)9603da312aSTakashi Iwai snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
971da177e4SLinus Torvalds {
9803da312aSTakashi Iwai 	struct snd_emux *emu;
9903da312aSTakashi Iwai 	struct snd_emux_port *p;
10003da312aSTakashi Iwai 	struct snd_seq_port_callback callback;
1011da177e4SLinus Torvalds 	char tmpname[64];
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	emu = closure;
1045e246b85STakashi Iwai 	if (snd_BUG_ON(!arg || !emu))
1055e246b85STakashi Iwai 		return -ENXIO;
1061da177e4SLinus Torvalds 
1071c94e65cSTakashi Iwai 	if (!snd_emux_inc_count(emu))
1081da177e4SLinus Torvalds 		return -EFAULT;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 	memset(&callback, 0, sizeof(callback));
1111da177e4SLinus Torvalds 	callback.owner = THIS_MODULE;
1121da177e4SLinus Torvalds 	callback.event_input = snd_emux_event_oss_input;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	sprintf(tmpname, "%s OSS Port", emu->name);
1151da177e4SLinus Torvalds 	p = snd_emux_create_port(emu, tmpname, 32,
1161da177e4SLinus Torvalds 				 1, &callback);
1171da177e4SLinus Torvalds 	if (p == NULL) {
11842b0158bSTakashi Iwai 		snd_printk(KERN_ERR "can't create port\n");
1191da177e4SLinus Torvalds 		snd_emux_dec_count(emu);
1201da177e4SLinus Torvalds 		return -ENOMEM;
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	/* fill the argument data */
1241da177e4SLinus Torvalds 	arg->private_data = p;
1251da177e4SLinus Torvalds 	arg->addr.client = p->chset.client;
1261da177e4SLinus Torvalds 	arg->addr.port = p->chset.port;
1271da177e4SLinus Torvalds 	p->oss_arg = arg;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	reset_port_mode(p, arg->seq_mode);
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	snd_emux_reset_port(p);
1321da177e4SLinus Torvalds 	return 0;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds #define DEFAULT_DRUM_FLAGS	((1<<9) | (1<<25))
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /*
1391da177e4SLinus Torvalds  * reset port mode
1401da177e4SLinus Torvalds  */
1411da177e4SLinus Torvalds static void
reset_port_mode(struct snd_emux_port * port,int midi_mode)14203da312aSTakashi Iwai reset_port_mode(struct snd_emux_port *port, int midi_mode)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds 	if (midi_mode) {
1451da177e4SLinus Torvalds 		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI;
1461da177e4SLinus Torvalds 		port->drum_flags = DEFAULT_DRUM_FLAGS;
1471da177e4SLinus Torvalds 		port->volume_atten = 0;
1481da177e4SLinus Torvalds 		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS;
1491da177e4SLinus Torvalds 	} else {
1501da177e4SLinus Torvalds 		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH;
1511da177e4SLinus Torvalds 		port->drum_flags = 0;
1521da177e4SLinus Torvalds 		port->volume_atten = 32;
1531da177e4SLinus Torvalds 		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /*
1591da177e4SLinus Torvalds  * close port
1601da177e4SLinus Torvalds  */
1611da177e4SLinus Torvalds static int
snd_emux_close_seq_oss(struct snd_seq_oss_arg * arg)16203da312aSTakashi Iwai snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg)
1631da177e4SLinus Torvalds {
16403da312aSTakashi Iwai 	struct snd_emux *emu;
16503da312aSTakashi Iwai 	struct snd_emux_port *p;
1661da177e4SLinus Torvalds 
1675e246b85STakashi Iwai 	if (snd_BUG_ON(!arg))
1685e246b85STakashi Iwai 		return -ENXIO;
1691da177e4SLinus Torvalds 	p = arg->private_data;
1705e246b85STakashi Iwai 	if (snd_BUG_ON(!p))
1715e246b85STakashi Iwai 		return -ENXIO;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	emu = p->emu;
1745e246b85STakashi Iwai 	if (snd_BUG_ON(!emu))
1755e246b85STakashi Iwai 		return -ENXIO;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	snd_emux_sounds_off_all(p);
1781da177e4SLinus Torvalds 	snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port));
1791da177e4SLinus Torvalds 	snd_seq_event_port_detach(p->chset.client, p->chset.port);
1801da177e4SLinus Torvalds 	snd_emux_dec_count(emu);
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	return 0;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds /*
1871da177e4SLinus Torvalds  * load patch
1881da177e4SLinus Torvalds  */
1891da177e4SLinus Torvalds static int
snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg * arg,int format,const char __user * buf,int offs,int count)19003da312aSTakashi Iwai snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
1911da177e4SLinus Torvalds 			    const char __user *buf, int offs, int count)
1921da177e4SLinus Torvalds {
19303da312aSTakashi Iwai 	struct snd_emux *emu;
19403da312aSTakashi Iwai 	struct snd_emux_port *p;
1951da177e4SLinus Torvalds 	int rc;
1961da177e4SLinus Torvalds 
1975e246b85STakashi Iwai 	if (snd_BUG_ON(!arg))
1985e246b85STakashi Iwai 		return -ENXIO;
1991da177e4SLinus Torvalds 	p = arg->private_data;
2005e246b85STakashi Iwai 	if (snd_BUG_ON(!p))
2015e246b85STakashi Iwai 		return -ENXIO;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	emu = p->emu;
2045e246b85STakashi Iwai 	if (snd_BUG_ON(!emu))
2055e246b85STakashi Iwai 		return -ENXIO;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	if (format == GUS_PATCH)
2081da177e4SLinus Torvalds 		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
2091da177e4SLinus Torvalds 						 SF_CLIENT_NO(p->chset.port));
2101da177e4SLinus Torvalds 	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
21103da312aSTakashi Iwai 		struct soundfont_patch_info patch;
2121da177e4SLinus Torvalds 		if (count < (int)sizeof(patch))
2135885615eSDan Carpenter 			return -EINVAL;
2141da177e4SLinus Torvalds 		if (copy_from_user(&patch, buf, sizeof(patch)))
2155885615eSDan Carpenter 			return -EFAULT;
2161da177e4SLinus Torvalds 		if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
2171da177e4SLinus Torvalds 		    patch.type <= SNDRV_SFNT_PROBE_DATA)
2181da177e4SLinus Torvalds 			rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
2191da177e4SLinus Torvalds 		else {
2201da177e4SLinus Torvalds 			if (emu->ops.load_fx)
2211da177e4SLinus Torvalds 				rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count);
2221da177e4SLinus Torvalds 			else
2231da177e4SLinus Torvalds 				rc = -EINVAL;
2241da177e4SLinus Torvalds 		}
2251da177e4SLinus Torvalds 	} else
2261da177e4SLinus Torvalds 		rc = 0;
2271da177e4SLinus Torvalds 	return rc;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds /*
2321da177e4SLinus Torvalds  * ioctl
2331da177e4SLinus Torvalds  */
2341da177e4SLinus Torvalds static int
snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg * arg,unsigned int cmd,unsigned long ioarg)23503da312aSTakashi Iwai snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg)
2361da177e4SLinus Torvalds {
23703da312aSTakashi Iwai 	struct snd_emux_port *p;
23803da312aSTakashi Iwai 	struct snd_emux *emu;
2391da177e4SLinus Torvalds 
2405e246b85STakashi Iwai 	if (snd_BUG_ON(!arg))
2415e246b85STakashi Iwai 		return -ENXIO;
2421da177e4SLinus Torvalds 	p = arg->private_data;
2435e246b85STakashi Iwai 	if (snd_BUG_ON(!p))
2445e246b85STakashi Iwai 		return -ENXIO;
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	emu = p->emu;
2475e246b85STakashi Iwai 	if (snd_BUG_ON(!emu))
2485e246b85STakashi Iwai 		return -ENXIO;
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	switch (cmd) {
2511da177e4SLinus Torvalds 	case SNDCTL_SEQ_RESETSAMPLES:
2521da177e4SLinus Torvalds 		snd_soundfont_remove_samples(emu->sflist);
2531da177e4SLinus Torvalds 		return 0;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	case SNDCTL_SYNTH_MEMAVL:
2561da177e4SLinus Torvalds 		if (emu->memhdr)
2571da177e4SLinus Torvalds 			return snd_util_mem_avail(emu->memhdr);
2581da177e4SLinus Torvalds 		return 0;
2591da177e4SLinus Torvalds 	}
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	return 0;
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds /*
2661da177e4SLinus Torvalds  * reset device
2671da177e4SLinus Torvalds  */
2681da177e4SLinus Torvalds static int
snd_emux_reset_seq_oss(struct snd_seq_oss_arg * arg)26903da312aSTakashi Iwai snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg)
2701da177e4SLinus Torvalds {
27103da312aSTakashi Iwai 	struct snd_emux_port *p;
2721da177e4SLinus Torvalds 
2735e246b85STakashi Iwai 	if (snd_BUG_ON(!arg))
2745e246b85STakashi Iwai 		return -ENXIO;
2751da177e4SLinus Torvalds 	p = arg->private_data;
2765e246b85STakashi Iwai 	if (snd_BUG_ON(!p))
2775e246b85STakashi Iwai 		return -ENXIO;
2781da177e4SLinus Torvalds 	snd_emux_reset_port(p);
2791da177e4SLinus Torvalds 	return 0;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds /*
2841da177e4SLinus Torvalds  * receive raw events: only SEQ_PRIVATE is accepted.
2851da177e4SLinus Torvalds  */
2861da177e4SLinus Torvalds static int
snd_emux_event_oss_input(struct snd_seq_event * ev,int direct,void * private_data,int atomic,int hop)28703da312aSTakashi Iwai snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_data,
2881da177e4SLinus Torvalds 			 int atomic, int hop)
2891da177e4SLinus Torvalds {
29003da312aSTakashi Iwai 	struct snd_emux *emu;
29103da312aSTakashi Iwai 	struct snd_emux_port *p;
2921da177e4SLinus Torvalds 	unsigned char cmd, *data;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	p = private_data;
2955e246b85STakashi Iwai 	if (snd_BUG_ON(!p))
2965e246b85STakashi Iwai 		return -EINVAL;
2971da177e4SLinus Torvalds 	emu = p->emu;
2985e246b85STakashi Iwai 	if (snd_BUG_ON(!emu))
2995e246b85STakashi Iwai 		return -EINVAL;
3001da177e4SLinus Torvalds 	if (ev->type != SNDRV_SEQ_EVENT_OSS)
3011da177e4SLinus Torvalds 		return snd_emux_event_input(ev, direct, private_data, atomic, hop);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	data = ev->data.raw8.d;
3041da177e4SLinus Torvalds 	/* only SEQ_PRIVATE is accepted */
3051da177e4SLinus Torvalds 	if (data[0] != 0xfe)
3061da177e4SLinus Torvalds 		return 0;
3071da177e4SLinus Torvalds 	cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK;
3081da177e4SLinus Torvalds 	if (data[2] & _EMUX_OSS_MODE_FLAG)
3091da177e4SLinus Torvalds 		emuspec_control(emu, p, cmd, data, atomic, hop);
3101da177e4SLinus Torvalds 	else
3111da177e4SLinus Torvalds 		gusspec_control(emu, p, cmd, data, atomic, hop);
3121da177e4SLinus Torvalds 	return 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds /*
3171da177e4SLinus Torvalds  * OSS/AWE driver specific h/w controls
3181da177e4SLinus Torvalds  */
3191da177e4SLinus Torvalds static void
emuspec_control(struct snd_emux * emu,struct snd_emux_port * port,int cmd,unsigned char * event,int atomic,int hop)32003da312aSTakashi Iwai emuspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
3211da177e4SLinus Torvalds 		unsigned char *event, int atomic, int hop)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	int voice;
3241da177e4SLinus Torvalds 	unsigned short p1;
3251da177e4SLinus Torvalds 	short p2;
3261da177e4SLinus Torvalds 	int i;
32703da312aSTakashi Iwai 	struct snd_midi_channel *chan;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	voice = event[3];
3301da177e4SLinus Torvalds 	if (voice < 0 || voice >= port->chset.max_channels)
3311da177e4SLinus Torvalds 		chan = NULL;
3321da177e4SLinus Torvalds 	else
3331da177e4SLinus Torvalds 		chan = &port->chset.channels[voice];
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	p1 = *(unsigned short *) &event[4];
3361da177e4SLinus Torvalds 	p2 = *(short *) &event[6];
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	switch (cmd) {
3391da177e4SLinus Torvalds #if 0 /* don't do this atomically */
3401da177e4SLinus Torvalds 	case _EMUX_OSS_REMOVE_LAST_SAMPLES:
3411da177e4SLinus Torvalds 		snd_soundfont_remove_unlocked(emu->sflist);
3421da177e4SLinus Torvalds 		break;
3431da177e4SLinus Torvalds #endif
3441da177e4SLinus Torvalds 	case _EMUX_OSS_SEND_EFFECT:
3451da177e4SLinus Torvalds 		if (chan)
3461da177e4SLinus Torvalds 			snd_emux_send_effect_oss(port, chan, p1, p2);
3471da177e4SLinus Torvalds 		break;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	case _EMUX_OSS_TERMINATE_ALL:
3501da177e4SLinus Torvalds 		snd_emux_terminate_all(emu);
3511da177e4SLinus Torvalds 		break;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	case _EMUX_OSS_TERMINATE_CHANNEL:
3541da177e4SLinus Torvalds 		/*snd_emux_mute_channel(emu, chan);*/
3551da177e4SLinus Torvalds 		break;
3561da177e4SLinus Torvalds 	case _EMUX_OSS_RESET_CHANNEL:
3571da177e4SLinus Torvalds 		/*snd_emux_channel_init(chset, chan);*/
3581da177e4SLinus Torvalds 		break;
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	case _EMUX_OSS_RELEASE_ALL:
3611da177e4SLinus Torvalds 		fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop);
3621da177e4SLinus Torvalds 		break;
3631da177e4SLinus Torvalds 	case _EMUX_OSS_NOTEOFF_ALL:
3641da177e4SLinus Torvalds 		fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop);
3651da177e4SLinus Torvalds 		break;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	case _EMUX_OSS_INITIAL_VOLUME:
3681da177e4SLinus Torvalds 		if (p2) {
3691da177e4SLinus Torvalds 			port->volume_atten = (short)p1;
3701da177e4SLinus Torvalds 			snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
3711da177e4SLinus Torvalds 		}
3721da177e4SLinus Torvalds 		break;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	case _EMUX_OSS_CHN_PRESSURE:
3751da177e4SLinus Torvalds 		if (chan) {
3761da177e4SLinus Torvalds 			chan->midi_pressure = p1;
3771da177e4SLinus Torvalds 			snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2);
3781da177e4SLinus Torvalds 		}
3791da177e4SLinus Torvalds 		break;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds 	case _EMUX_OSS_CHANNEL_MODE:
3821da177e4SLinus Torvalds 		reset_port_mode(port, p1);
3831da177e4SLinus Torvalds 		snd_emux_reset_port(port);
3841da177e4SLinus Torvalds 		break;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	case _EMUX_OSS_DRUM_CHANNELS:
3871da177e4SLinus Torvalds 		port->drum_flags = *(unsigned int*)&event[4];
3881da177e4SLinus Torvalds 		for (i = 0; i < port->chset.max_channels; i++) {
3891da177e4SLinus Torvalds 			chan = &port->chset.channels[i];
3901da177e4SLinus Torvalds 			chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
3911da177e4SLinus Torvalds 		}
3921da177e4SLinus Torvalds 		break;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	case _EMUX_OSS_MISC_MODE:
3951da177e4SLinus Torvalds 		if (p1 < EMUX_MD_END)
3961da177e4SLinus Torvalds 			port->ctrls[p1] = p2;
3971da177e4SLinus Torvalds 		break;
3981da177e4SLinus Torvalds 	case _EMUX_OSS_DEBUG_MODE:
3991da177e4SLinus Torvalds 		break;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	default:
4021da177e4SLinus Torvalds 		if (emu->ops.oss_ioctl)
4031da177e4SLinus Torvalds 			emu->ops.oss_ioctl(emu, cmd, p1, p2);
4041da177e4SLinus Torvalds 		break;
4051da177e4SLinus Torvalds 	}
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds /*
4091da177e4SLinus Torvalds  * GUS specific h/w controls
4101da177e4SLinus Torvalds  */
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds #include <linux/ultrasound.h>
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds static void
gusspec_control(struct snd_emux * emu,struct snd_emux_port * port,int cmd,unsigned char * event,int atomic,int hop)41503da312aSTakashi Iwai gusspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
4161da177e4SLinus Torvalds 		unsigned char *event, int atomic, int hop)
4171da177e4SLinus Torvalds {
4181da177e4SLinus Torvalds 	int voice;
4191da177e4SLinus Torvalds 	unsigned short p1;
4201da177e4SLinus Torvalds 	int plong;
42103da312aSTakashi Iwai 	struct snd_midi_channel *chan;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH)
4241da177e4SLinus Torvalds 		return;
4251da177e4SLinus Torvalds 	if (cmd == _GUS_NUMVOICES)
4261da177e4SLinus Torvalds 		return;
4271da177e4SLinus Torvalds 	voice = event[3];
4281da177e4SLinus Torvalds 	if (voice < 0 || voice >= port->chset.max_channels)
4291da177e4SLinus Torvalds 		return;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	chan = &port->chset.channels[voice];
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	p1 = *(unsigned short *) &event[4];
4341da177e4SLinus Torvalds 	plong = *(int*) &event[4];
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	switch (cmd) {
4371da177e4SLinus Torvalds 	case _GUS_VOICESAMPLE:
4381da177e4SLinus Torvalds 		chan->midi_program = p1;
4391da177e4SLinus Torvalds 		return;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	case _GUS_VOICEBALA:
4421da177e4SLinus Torvalds 		/* 0 to 15 --> 0 to 127 */
4431da177e4SLinus Torvalds 		chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3;
4441da177e4SLinus Torvalds 		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
4451da177e4SLinus Torvalds 		return;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	case _GUS_VOICEVOL:
4481da177e4SLinus Torvalds 	case _GUS_VOICEVOL2:
4491da177e4SLinus Torvalds 		/* not supported yet */
4501da177e4SLinus Torvalds 		return;
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	case _GUS_RAMPRANGE:
4531da177e4SLinus Torvalds 	case _GUS_RAMPRATE:
4541da177e4SLinus Torvalds 	case _GUS_RAMPMODE:
4551da177e4SLinus Torvalds 	case _GUS_RAMPON:
4561da177e4SLinus Torvalds 	case _GUS_RAMPOFF:
4571da177e4SLinus Torvalds 		/* volume ramping not supported */
4581da177e4SLinus Torvalds 		return;
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	case _GUS_VOLUME_SCALE:
4611da177e4SLinus Torvalds 		return;
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	case _GUS_VOICE_POS:
4641da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
4651da177e4SLinus Torvalds 		snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START,
4661da177e4SLinus Torvalds 				     (short)(plong & 0x7fff),
4671da177e4SLinus Torvalds 				     EMUX_FX_FLAG_SET);
4681da177e4SLinus Torvalds 		snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START,
4691da177e4SLinus Torvalds 				     (plong >> 15) & 0xffff,
4701da177e4SLinus Torvalds 				     EMUX_FX_FLAG_SET);
4711da177e4SLinus Torvalds #endif
4721da177e4SLinus Torvalds 		return;
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds /*
4781da177e4SLinus Torvalds  * send an event to midi emulation
4791da177e4SLinus Torvalds  */
4801da177e4SLinus Torvalds static void
fake_event(struct snd_emux * emu,struct snd_emux_port * port,int ch,int param,int val,int atomic,int hop)48103da312aSTakashi Iwai fake_event(struct snd_emux *emu, struct snd_emux_port *port, int ch, int param, int val, int atomic, int hop)
4821da177e4SLinus Torvalds {
48303da312aSTakashi Iwai 	struct snd_seq_event ev;
4841da177e4SLinus Torvalds 	memset(&ev, 0, sizeof(ev));
4851da177e4SLinus Torvalds 	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
4861da177e4SLinus Torvalds 	ev.data.control.channel = ch;
4871da177e4SLinus Torvalds 	ev.data.control.param = param;
4881da177e4SLinus Torvalds 	ev.data.control.value = val;
4891da177e4SLinus Torvalds 	snd_emux_event_input(&ev, 0, port, atomic, hop);
4901da177e4SLinus Torvalds }
491