162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Interface for OSS sequencer emulation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1999 Takashi Iwai <tiwai@suse.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Changes
862306a36Sopenharmony_ci * 19990227   Steve Ratcliffe   Made separate file and merged in latest
962306a36Sopenharmony_ci * 				midi emulation.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/export.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <sound/core.h>
1662306a36Sopenharmony_ci#include "emux_voice.h"
1762306a36Sopenharmony_ci#include <sound/asoundef.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
2062306a36Sopenharmony_cistatic int snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg);
2162306a36Sopenharmony_cistatic int snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
2262306a36Sopenharmony_ci				  unsigned long ioarg);
2362306a36Sopenharmony_cistatic int snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
2462306a36Sopenharmony_ci				       const char __user *buf, int offs, int count);
2562306a36Sopenharmony_cistatic int snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg);
2662306a36Sopenharmony_cistatic int snd_emux_event_oss_input(struct snd_seq_event *ev, int direct,
2762306a36Sopenharmony_ci				    void *private, int atomic, int hop);
2862306a36Sopenharmony_cistatic void reset_port_mode(struct snd_emux_port *port, int midi_mode);
2962306a36Sopenharmony_cistatic void emuspec_control(struct snd_emux *emu, struct snd_emux_port *port,
3062306a36Sopenharmony_ci			    int cmd, unsigned char *event, int atomic, int hop);
3162306a36Sopenharmony_cistatic void gusspec_control(struct snd_emux *emu, struct snd_emux_port *port,
3262306a36Sopenharmony_ci			    int cmd, unsigned char *event, int atomic, int hop);
3362306a36Sopenharmony_cistatic void fake_event(struct snd_emux *emu, struct snd_emux_port *port,
3462306a36Sopenharmony_ci		       int ch, int param, int val, int atomic, int hop);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* operators */
3762306a36Sopenharmony_cistatic const struct snd_seq_oss_callback oss_callback = {
3862306a36Sopenharmony_ci	.owner = THIS_MODULE,
3962306a36Sopenharmony_ci	.open = snd_emux_open_seq_oss,
4062306a36Sopenharmony_ci	.close = snd_emux_close_seq_oss,
4162306a36Sopenharmony_ci	.ioctl = snd_emux_ioctl_seq_oss,
4262306a36Sopenharmony_ci	.load_patch = snd_emux_load_patch_seq_oss,
4362306a36Sopenharmony_ci	.reset = snd_emux_reset_seq_oss,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * register OSS synth
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid
5262306a36Sopenharmony_cisnd_emux_init_seq_oss(struct snd_emux *emu)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct snd_seq_oss_reg *arg;
5562306a36Sopenharmony_ci	struct snd_seq_device *dev;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* using device#1 here for avoiding conflicts with OPL3 */
5862306a36Sopenharmony_ci	if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS,
5962306a36Sopenharmony_ci			       sizeof(struct snd_seq_oss_reg), &dev) < 0)
6062306a36Sopenharmony_ci		return;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	emu->oss_synth = dev;
6362306a36Sopenharmony_ci	strcpy(dev->name, emu->name);
6462306a36Sopenharmony_ci	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
6562306a36Sopenharmony_ci	arg->type = SYNTH_TYPE_SAMPLE;
6662306a36Sopenharmony_ci	arg->subtype = SAMPLE_TYPE_AWE32;
6762306a36Sopenharmony_ci	arg->nvoices = emu->max_voices;
6862306a36Sopenharmony_ci	arg->oper = oss_callback;
6962306a36Sopenharmony_ci	arg->private_data = emu;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* register to OSS synth table */
7262306a36Sopenharmony_ci	snd_device_register(emu->card, dev);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * unregister
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_civoid
8062306a36Sopenharmony_cisnd_emux_detach_seq_oss(struct snd_emux *emu)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	if (emu->oss_synth) {
8362306a36Sopenharmony_ci		snd_device_free(emu->card, emu->oss_synth);
8462306a36Sopenharmony_ci		emu->oss_synth = NULL;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* use port number as a unique soundfont client number */
9062306a36Sopenharmony_ci#define SF_CLIENT_NO(p)	((p) + 0x1000)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/*
9362306a36Sopenharmony_ci * open port for OSS sequencer
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_cistatic int
9662306a36Sopenharmony_cisnd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct snd_emux *emu;
9962306a36Sopenharmony_ci	struct snd_emux_port *p;
10062306a36Sopenharmony_ci	struct snd_seq_port_callback callback;
10162306a36Sopenharmony_ci	char tmpname[64];
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	emu = closure;
10462306a36Sopenharmony_ci	if (snd_BUG_ON(!arg || !emu))
10562306a36Sopenharmony_ci		return -ENXIO;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!snd_emux_inc_count(emu))
10862306a36Sopenharmony_ci		return -EFAULT;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	memset(&callback, 0, sizeof(callback));
11162306a36Sopenharmony_ci	callback.owner = THIS_MODULE;
11262306a36Sopenharmony_ci	callback.event_input = snd_emux_event_oss_input;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	sprintf(tmpname, "%s OSS Port", emu->name);
11562306a36Sopenharmony_ci	p = snd_emux_create_port(emu, tmpname, 32,
11662306a36Sopenharmony_ci				 1, &callback);
11762306a36Sopenharmony_ci	if (p == NULL) {
11862306a36Sopenharmony_ci		snd_printk(KERN_ERR "can't create port\n");
11962306a36Sopenharmony_ci		snd_emux_dec_count(emu);
12062306a36Sopenharmony_ci		return -ENOMEM;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* fill the argument data */
12462306a36Sopenharmony_ci	arg->private_data = p;
12562306a36Sopenharmony_ci	arg->addr.client = p->chset.client;
12662306a36Sopenharmony_ci	arg->addr.port = p->chset.port;
12762306a36Sopenharmony_ci	p->oss_arg = arg;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	reset_port_mode(p, arg->seq_mode);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	snd_emux_reset_port(p);
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define DEFAULT_DRUM_FLAGS	((1<<9) | (1<<25))
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * reset port mode
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic void
14262306a36Sopenharmony_cireset_port_mode(struct snd_emux_port *port, int midi_mode)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	if (midi_mode) {
14562306a36Sopenharmony_ci		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI;
14662306a36Sopenharmony_ci		port->drum_flags = DEFAULT_DRUM_FLAGS;
14762306a36Sopenharmony_ci		port->volume_atten = 0;
14862306a36Sopenharmony_ci		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS;
14962306a36Sopenharmony_ci	} else {
15062306a36Sopenharmony_ci		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH;
15162306a36Sopenharmony_ci		port->drum_flags = 0;
15262306a36Sopenharmony_ci		port->volume_atten = 32;
15362306a36Sopenharmony_ci		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * close port
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int
16262306a36Sopenharmony_cisnd_emux_close_seq_oss(struct snd_seq_oss_arg *arg)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct snd_emux *emu;
16562306a36Sopenharmony_ci	struct snd_emux_port *p;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (snd_BUG_ON(!arg))
16862306a36Sopenharmony_ci		return -ENXIO;
16962306a36Sopenharmony_ci	p = arg->private_data;
17062306a36Sopenharmony_ci	if (snd_BUG_ON(!p))
17162306a36Sopenharmony_ci		return -ENXIO;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	emu = p->emu;
17462306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
17562306a36Sopenharmony_ci		return -ENXIO;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	snd_emux_sounds_off_all(p);
17862306a36Sopenharmony_ci	snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port));
17962306a36Sopenharmony_ci	snd_seq_event_port_detach(p->chset.client, p->chset.port);
18062306a36Sopenharmony_ci	snd_emux_dec_count(emu);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * load patch
18862306a36Sopenharmony_ci */
18962306a36Sopenharmony_cistatic int
19062306a36Sopenharmony_cisnd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
19162306a36Sopenharmony_ci			    const char __user *buf, int offs, int count)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct snd_emux *emu;
19462306a36Sopenharmony_ci	struct snd_emux_port *p;
19562306a36Sopenharmony_ci	int rc;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (snd_BUG_ON(!arg))
19862306a36Sopenharmony_ci		return -ENXIO;
19962306a36Sopenharmony_ci	p = arg->private_data;
20062306a36Sopenharmony_ci	if (snd_BUG_ON(!p))
20162306a36Sopenharmony_ci		return -ENXIO;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	emu = p->emu;
20462306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
20562306a36Sopenharmony_ci		return -ENXIO;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (format == GUS_PATCH)
20862306a36Sopenharmony_ci		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
20962306a36Sopenharmony_ci						 SF_CLIENT_NO(p->chset.port));
21062306a36Sopenharmony_ci	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
21162306a36Sopenharmony_ci		struct soundfont_patch_info patch;
21262306a36Sopenharmony_ci		if (count < (int)sizeof(patch))
21362306a36Sopenharmony_ci			return -EINVAL;
21462306a36Sopenharmony_ci		if (copy_from_user(&patch, buf, sizeof(patch)))
21562306a36Sopenharmony_ci			return -EFAULT;
21662306a36Sopenharmony_ci		if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
21762306a36Sopenharmony_ci		    patch.type <= SNDRV_SFNT_PROBE_DATA)
21862306a36Sopenharmony_ci			rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
21962306a36Sopenharmony_ci		else {
22062306a36Sopenharmony_ci			if (emu->ops.load_fx)
22162306a36Sopenharmony_ci				rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count);
22262306a36Sopenharmony_ci			else
22362306a36Sopenharmony_ci				rc = -EINVAL;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci	} else
22662306a36Sopenharmony_ci		rc = 0;
22762306a36Sopenharmony_ci	return rc;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/*
23262306a36Sopenharmony_ci * ioctl
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic int
23562306a36Sopenharmony_cisnd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct snd_emux_port *p;
23862306a36Sopenharmony_ci	struct snd_emux *emu;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (snd_BUG_ON(!arg))
24162306a36Sopenharmony_ci		return -ENXIO;
24262306a36Sopenharmony_ci	p = arg->private_data;
24362306a36Sopenharmony_ci	if (snd_BUG_ON(!p))
24462306a36Sopenharmony_ci		return -ENXIO;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	emu = p->emu;
24762306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
24862306a36Sopenharmony_ci		return -ENXIO;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	switch (cmd) {
25162306a36Sopenharmony_ci	case SNDCTL_SEQ_RESETSAMPLES:
25262306a36Sopenharmony_ci		snd_soundfont_remove_samples(emu->sflist);
25362306a36Sopenharmony_ci		return 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	case SNDCTL_SYNTH_MEMAVL:
25662306a36Sopenharmony_ci		if (emu->memhdr)
25762306a36Sopenharmony_ci			return snd_util_mem_avail(emu->memhdr);
25862306a36Sopenharmony_ci		return 0;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/*
26662306a36Sopenharmony_ci * reset device
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_cistatic int
26962306a36Sopenharmony_cisnd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct snd_emux_port *p;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (snd_BUG_ON(!arg))
27462306a36Sopenharmony_ci		return -ENXIO;
27562306a36Sopenharmony_ci	p = arg->private_data;
27662306a36Sopenharmony_ci	if (snd_BUG_ON(!p))
27762306a36Sopenharmony_ci		return -ENXIO;
27862306a36Sopenharmony_ci	snd_emux_reset_port(p);
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/*
28462306a36Sopenharmony_ci * receive raw events: only SEQ_PRIVATE is accepted.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic int
28762306a36Sopenharmony_cisnd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_data,
28862306a36Sopenharmony_ci			 int atomic, int hop)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct snd_emux *emu;
29162306a36Sopenharmony_ci	struct snd_emux_port *p;
29262306a36Sopenharmony_ci	unsigned char cmd, *data;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	p = private_data;
29562306a36Sopenharmony_ci	if (snd_BUG_ON(!p))
29662306a36Sopenharmony_ci		return -EINVAL;
29762306a36Sopenharmony_ci	emu = p->emu;
29862306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci	if (ev->type != SNDRV_SEQ_EVENT_OSS)
30162306a36Sopenharmony_ci		return snd_emux_event_input(ev, direct, private_data, atomic, hop);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	data = ev->data.raw8.d;
30462306a36Sopenharmony_ci	/* only SEQ_PRIVATE is accepted */
30562306a36Sopenharmony_ci	if (data[0] != 0xfe)
30662306a36Sopenharmony_ci		return 0;
30762306a36Sopenharmony_ci	cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK;
30862306a36Sopenharmony_ci	if (data[2] & _EMUX_OSS_MODE_FLAG)
30962306a36Sopenharmony_ci		emuspec_control(emu, p, cmd, data, atomic, hop);
31062306a36Sopenharmony_ci	else
31162306a36Sopenharmony_ci		gusspec_control(emu, p, cmd, data, atomic, hop);
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * OSS/AWE driver specific h/w controls
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_cistatic void
32062306a36Sopenharmony_ciemuspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
32162306a36Sopenharmony_ci		unsigned char *event, int atomic, int hop)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int voice;
32462306a36Sopenharmony_ci	unsigned short p1;
32562306a36Sopenharmony_ci	short p2;
32662306a36Sopenharmony_ci	int i;
32762306a36Sopenharmony_ci	struct snd_midi_channel *chan;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	voice = event[3];
33062306a36Sopenharmony_ci	if (voice < 0 || voice >= port->chset.max_channels)
33162306a36Sopenharmony_ci		chan = NULL;
33262306a36Sopenharmony_ci	else
33362306a36Sopenharmony_ci		chan = &port->chset.channels[voice];
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	p1 = *(unsigned short *) &event[4];
33662306a36Sopenharmony_ci	p2 = *(short *) &event[6];
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	switch (cmd) {
33962306a36Sopenharmony_ci#if 0 /* don't do this atomically */
34062306a36Sopenharmony_ci	case _EMUX_OSS_REMOVE_LAST_SAMPLES:
34162306a36Sopenharmony_ci		snd_soundfont_remove_unlocked(emu->sflist);
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci#endif
34462306a36Sopenharmony_ci	case _EMUX_OSS_SEND_EFFECT:
34562306a36Sopenharmony_ci		if (chan)
34662306a36Sopenharmony_ci			snd_emux_send_effect_oss(port, chan, p1, p2);
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	case _EMUX_OSS_TERMINATE_ALL:
35062306a36Sopenharmony_ci		snd_emux_terminate_all(emu);
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	case _EMUX_OSS_TERMINATE_CHANNEL:
35462306a36Sopenharmony_ci		/*snd_emux_mute_channel(emu, chan);*/
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	case _EMUX_OSS_RESET_CHANNEL:
35762306a36Sopenharmony_ci		/*snd_emux_channel_init(chset, chan);*/
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	case _EMUX_OSS_RELEASE_ALL:
36162306a36Sopenharmony_ci		fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop);
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	case _EMUX_OSS_NOTEOFF_ALL:
36462306a36Sopenharmony_ci		fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop);
36562306a36Sopenharmony_ci		break;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	case _EMUX_OSS_INITIAL_VOLUME:
36862306a36Sopenharmony_ci		if (p2) {
36962306a36Sopenharmony_ci			port->volume_atten = (short)p1;
37062306a36Sopenharmony_ci			snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci		break;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	case _EMUX_OSS_CHN_PRESSURE:
37562306a36Sopenharmony_ci		if (chan) {
37662306a36Sopenharmony_ci			chan->midi_pressure = p1;
37762306a36Sopenharmony_ci			snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2);
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	case _EMUX_OSS_CHANNEL_MODE:
38262306a36Sopenharmony_ci		reset_port_mode(port, p1);
38362306a36Sopenharmony_ci		snd_emux_reset_port(port);
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	case _EMUX_OSS_DRUM_CHANNELS:
38762306a36Sopenharmony_ci		port->drum_flags = *(unsigned int*)&event[4];
38862306a36Sopenharmony_ci		for (i = 0; i < port->chset.max_channels; i++) {
38962306a36Sopenharmony_ci			chan = &port->chset.channels[i];
39062306a36Sopenharmony_ci			chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci		break;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	case _EMUX_OSS_MISC_MODE:
39562306a36Sopenharmony_ci		if (p1 < EMUX_MD_END)
39662306a36Sopenharmony_ci			port->ctrls[p1] = p2;
39762306a36Sopenharmony_ci		break;
39862306a36Sopenharmony_ci	case _EMUX_OSS_DEBUG_MODE:
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	default:
40262306a36Sopenharmony_ci		if (emu->ops.oss_ioctl)
40362306a36Sopenharmony_ci			emu->ops.oss_ioctl(emu, cmd, p1, p2);
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/*
40962306a36Sopenharmony_ci * GUS specific h/w controls
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci#include <linux/ultrasound.h>
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void
41562306a36Sopenharmony_cigusspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
41662306a36Sopenharmony_ci		unsigned char *event, int atomic, int hop)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	int voice;
41962306a36Sopenharmony_ci	unsigned short p1;
42062306a36Sopenharmony_ci	int plong;
42162306a36Sopenharmony_ci	struct snd_midi_channel *chan;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH)
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	if (cmd == _GUS_NUMVOICES)
42662306a36Sopenharmony_ci		return;
42762306a36Sopenharmony_ci	voice = event[3];
42862306a36Sopenharmony_ci	if (voice < 0 || voice >= port->chset.max_channels)
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	chan = &port->chset.channels[voice];
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	p1 = *(unsigned short *) &event[4];
43462306a36Sopenharmony_ci	plong = *(int*) &event[4];
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	switch (cmd) {
43762306a36Sopenharmony_ci	case _GUS_VOICESAMPLE:
43862306a36Sopenharmony_ci		chan->midi_program = p1;
43962306a36Sopenharmony_ci		return;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	case _GUS_VOICEBALA:
44262306a36Sopenharmony_ci		/* 0 to 15 --> 0 to 127 */
44362306a36Sopenharmony_ci		chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3;
44462306a36Sopenharmony_ci		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	case _GUS_VOICEVOL:
44862306a36Sopenharmony_ci	case _GUS_VOICEVOL2:
44962306a36Sopenharmony_ci		/* not supported yet */
45062306a36Sopenharmony_ci		return;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	case _GUS_RAMPRANGE:
45362306a36Sopenharmony_ci	case _GUS_RAMPRATE:
45462306a36Sopenharmony_ci	case _GUS_RAMPMODE:
45562306a36Sopenharmony_ci	case _GUS_RAMPON:
45662306a36Sopenharmony_ci	case _GUS_RAMPOFF:
45762306a36Sopenharmony_ci		/* volume ramping not supported */
45862306a36Sopenharmony_ci		return;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	case _GUS_VOLUME_SCALE:
46162306a36Sopenharmony_ci		return;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	case _GUS_VOICE_POS:
46462306a36Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT
46562306a36Sopenharmony_ci		snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START,
46662306a36Sopenharmony_ci				     (short)(plong & 0x7fff),
46762306a36Sopenharmony_ci				     EMUX_FX_FLAG_SET);
46862306a36Sopenharmony_ci		snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START,
46962306a36Sopenharmony_ci				     (plong >> 15) & 0xffff,
47062306a36Sopenharmony_ci				     EMUX_FX_FLAG_SET);
47162306a36Sopenharmony_ci#endif
47262306a36Sopenharmony_ci		return;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/*
47862306a36Sopenharmony_ci * send an event to midi emulation
47962306a36Sopenharmony_ci */
48062306a36Sopenharmony_cistatic void
48162306a36Sopenharmony_cifake_event(struct snd_emux *emu, struct snd_emux_port *port, int ch, int param, int val, int atomic, int hop)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct snd_seq_event ev;
48462306a36Sopenharmony_ci	memset(&ev, 0, sizeof(ev));
48562306a36Sopenharmony_ci	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
48662306a36Sopenharmony_ci	ev.data.control.channel = ch;
48762306a36Sopenharmony_ci	ev.data.control.param = param;
48862306a36Sopenharmony_ci	ev.data.control.value = val;
48962306a36Sopenharmony_ci	snd_emux_event_input(&ev, 0, port, atomic, hop);
49062306a36Sopenharmony_ci}
491