Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
new file mode 100644
index 0000000..64cb50d
--- /dev/null
+++ b/sound/core/seq/Makefile
@@ -0,0 +1,44 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+obj-$(CONFIG_SND) += instr/
+ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
+  obj-$(CONFIG_SND_SEQUENCER) += oss/
+endif
+
+snd-seq-device-objs := seq_device.o
+snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+                seq_fifo.o seq_prioq.o seq_timer.o \
+                seq_system.o seq_ports.o seq_info.o
+snd-seq-midi-objs := seq_midi.o
+snd-seq-midi-emul-objs := seq_midi_emul.o
+snd-seq-midi-event-objs := seq_midi_event.o
+snd-seq-instr-objs := seq_instr.o
+snd-seq-dummy-objs := seq_dummy.o
+snd-seq-virmidi-objs := seq_virmidi.o
+
+#
+# this function returns:
+#   "m" - CONFIG_SND_SEQUENCER is m
+#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+#   otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
+ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
+endif
+obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
+obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
+obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
new file mode 100644
index 0000000..69138f3
--- /dev/null
+++ b/sound/core/seq/instr/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ainstr-fm-objs := ainstr_fm.o
+snd-ainstr-simple-objs := ainstr_simple.o
+snd-ainstr-gf1-objs := ainstr_gf1.o
+snd-ainstr-iw-objs := ainstr_iw.o
+
+#
+# this function returns:
+#   "m" - CONFIG_SND_SEQUENCER is m
+#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+#   otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
+obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
+obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
+obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c
new file mode 100644
index 0000000..5c671e6
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_fm.c
@@ -0,0 +1,156 @@
+/*
+ *  FM (OPL2/3) Instrument routines
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/ainstr_fm.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
+MODULE_LICENSE("GPL");
+
+static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr,
+			  char __user *instr_data, long len, int atomic, int cmd)
+{
+	fm_instrument_t *ip;
+	fm_xinstrument_t ix;
+	int idx;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != FM_STRU_INSTR)
+		return -EINVAL;
+	ip = (fm_instrument_t *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->type = ix.type;
+	for (idx = 0; idx < 4; idx++) {
+		ip->op[idx].am_vib = ix.op[idx].am_vib;
+		ip->op[idx].ksl_level = ix.op[idx].ksl_level;
+		ip->op[idx].attack_decay = ix.op[idx].attack_decay;
+		ip->op[idx].sustain_release = ix.op[idx].sustain_release;
+		ip->op[idx].wave_select = ix.op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ip->feedback_connection[idx] = ix.feedback_connection[idx];
+	}
+	ip->echo_delay = ix.echo_delay;
+	ip->echo_atten = ix.echo_atten;
+	ip->chorus_spread = ix.chorus_spread;
+	ip->trnsps = ix.trnsps;
+	ip->fix_dur = ix.fix_dur;
+	ip->modes = ix.modes;
+	ip->fix_key = ix.fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr,
+			  char __user *instr_data, long len, int atomic,
+			  int cmd)
+{
+	fm_instrument_t *ip;
+	fm_xinstrument_t ix;
+	int idx;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (fm_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = FM_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.type = ip->type;
+	for (idx = 0; idx < 4; idx++) {
+		ix.op[idx].am_vib = ip->op[idx].am_vib;
+		ix.op[idx].ksl_level = ip->op[idx].ksl_level;
+		ix.op[idx].attack_decay = ip->op[idx].attack_decay;
+		ix.op[idx].sustain_release = ip->op[idx].sustain_release;
+		ix.op[idx].wave_select = ip->op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ix.feedback_connection[idx] = ip->feedback_connection[idx];
+	}
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	ix.echo_delay = ip->echo_delay;
+	ix.echo_atten = ip->echo_atten;
+	ix.chorus_spread = ip->chorus_spread;
+	ix.trnsps = ip->trnsps;
+	ix.fix_dur = ip->fix_dur;
+	ix.modes = ip->modes;
+	ix.fix_key = ip->fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr,
+			       long *size)
+{
+	*size = sizeof(fm_xinstrument_t);
+	return 0;
+}
+
+int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops,
+		    snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	// ops->private_data = private_data;
+	ops->add_len = sizeof(fm_instrument_t);
+	ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
+	ops->put = snd_seq_fm_put;
+	ops->get = snd_seq_fm_get;
+	ops->get_size = snd_seq_fm_get_size;
+	// ops->remove = snd_seq_fm_remove;
+	// ops->notify = snd_seq_fm_notify;
+	ops->next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_fm_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_fm_exit(void)
+{
+}
+
+module_init(alsa_ainstr_fm_init)
+module_exit(alsa_ainstr_fm_exit)
+
+EXPORT_SYMBOL(snd_seq_fm_init);
diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
new file mode 100644
index 0000000..0779c41
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_gf1.c
@@ -0,0 +1,358 @@
+/*
+ *   GF1 (GUS) Patch - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_gf1.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & GF1_WAVE_16BIT)
+		result <<= 1;
+	if (format & GF1_WAVE_STEREO)
+		result <<= 1;
+	return format;
+}
+
+static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops,
+					     gf1_instrument_t *ip,
+					     char __user **data,
+					     long *len,
+					     int atomic)
+{
+	gf1_wave_t *wp, *prev;
+	gf1_xwave_t xp;
+	int err, gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < (long)sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = kcalloc(1, sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->flags = xp.flags;
+	wp->sample_rate = le32_to_cpu(xp.sample_rate);
+	wp->low_frequency = le32_to_cpu(xp.low_frequency);
+	wp->high_frequency = le32_to_cpu(xp.high_frequency);
+	wp->root_frequency = le32_to_cpu(xp.root_frequency);
+	wp->tune = le16_to_cpu(xp.tune);
+	wp->balance = xp.balance;
+	memcpy(wp->envelope_rate, xp.envelope_rate, 6);
+	memcpy(wp->envelope_offset, xp.envelope_offset, 6);
+	wp->tremolo_sweep = xp.tremolo_sweep;
+	wp->tremolo_rate = xp.tremolo_rate;
+	wp->tremolo_depth = xp.tremolo_depth;
+	wp->vibrato_sweep = xp.vibrato_sweep;
+	wp->vibrato_rate = xp.vibrato_rate;
+	wp->vibrato_depth = xp.vibrato_depth;
+	wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
+	wp->scale_factor = le16_to_cpu(xp.scale_factor);
+	real_size = snd_seq_gf1_size(wp->size, wp->format);
+	if ((long)real_size > *len) {
+		kfree(wp);
+		return -ENOMEM;
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	*data += real_size;
+	*len -= real_size;
+	prev = ip->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		ip->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops,
+				  gf1_wave_t *wave,
+				  int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops,
+				   gf1_instrument_t *ip,
+				   int atomic)
+{
+	gf1_wave_t *wave;
+	
+	while ((wave = ip->wave) != NULL) {
+		ip->wave = wave->next;
+		snd_seq_gf1_wave_free(ops, wave, atomic);
+	}
+}
+
+static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr,
+			   char __user *instr_data, long len, int atomic,
+			   int cmd)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+	gf1_xinstrument_t ix;
+	int err, gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != GF1_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	while (len > (long)sizeof(__u32)) {
+		__u32 stype;
+
+		if (copy_from_user(&stype, instr_data, sizeof(stype)))
+			return -EFAULT;
+		if (stype != GF1_STRU_WAVE) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		err = snd_seq_gf1_copy_wave_from_stream(ops,
+							ip,
+							&instr_data,
+							&len,
+							atomic);
+		if (err < 0) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops,
+					   gf1_instrument_t *ip,
+					   char __user **data,
+					   long *len,
+					   int atomic)
+{
+	gf1_wave_t *wp;
+	gf1_xwave_t xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = ip->wave; wp; wp = wp->next) {
+		if (*len < (long)sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = GF1_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.flags = wp->flags;
+		xp.sample_rate = cpu_to_le32(wp->sample_rate);
+		xp.low_frequency = cpu_to_le32(wp->low_frequency);
+		xp.high_frequency = cpu_to_le32(wp->high_frequency);
+		xp.root_frequency = cpu_to_le32(wp->root_frequency);
+		xp.tune = cpu_to_le16(wp->tune);
+		xp.balance = wp->balance;
+		memcpy(xp.envelope_rate, wp->envelope_rate, 6);
+		memcpy(xp.envelope_offset, wp->envelope_offset, 6);
+		xp.tremolo_sweep = wp->tremolo_sweep;
+		xp.tremolo_rate = wp->tremolo_rate;
+		xp.tremolo_depth = wp->tremolo_depth;
+		xp.vibrato_sweep = wp->vibrato_sweep;
+		xp.vibrato_rate = wp->vibrato_rate;
+		xp.vibrato_depth = wp->vibrato_depth;
+		xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
+		xp.scale_factor = cpu_to_le16(wp->scale_factor);
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_gf1_size(wp->size, wp->format);
+		if (*len < (long)real_size)
+			return -ENOMEM;
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		*data += wp->size;
+		*len -= wp->size;
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr,
+			   char __user *instr_data, long len, int atomic,
+			   int cmd)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+	gf1_xinstrument_t ix;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = GF1_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	return snd_seq_gf1_copy_wave_to_stream(ops,
+					       ip,
+					       &instr_data,
+					       &len,
+					       atomic);
+}
+
+static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				long *size)
+{
+	long result;
+	gf1_instrument_t *ip;
+	gf1_wave_t *wp;
+
+	*size = 0;
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	result = sizeof(gf1_xinstrument_t);
+	for (wp = ip->wave; wp; wp = wp->next) {
+		result += sizeof(gf1_xwave_t);
+		result += wp->size;
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_gf1_remove(void *private_data,
+			      snd_seq_kinstr_t *instr,
+                              int atomic)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+	gf1_instrument_t *ip;
+
+	ip = (gf1_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_gf1_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_gf1_notify(void *private_data,
+			       snd_seq_kinstr_t *instr,
+			       int what)
+{
+	snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_gf1_init(snd_gf1_ops_t *ops,
+		     void *private_data,
+		     snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(gf1_instrument_t);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
+	ops->kops.put = snd_seq_gf1_put;
+	ops->kops.get = snd_seq_gf1_get;
+	ops->kops.get_size = snd_seq_gf1_get_size;
+	ops->kops.remove = snd_seq_gf1_remove;
+	ops->kops.notify = snd_seq_gf1_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_gf1_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_gf1_exit(void)
+{
+}
+
+module_init(alsa_ainstr_gf1_init)
+module_exit(alsa_ainstr_gf1_exit)
+
+EXPORT_SYMBOL(snd_seq_gf1_init);
diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
new file mode 100644
index 0000000..39ff72b
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_iw.c
@@ -0,0 +1,622 @@
+/*
+ *   IWFFFF - AMD InterWave (tm) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_iw.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & IWFFFF_WAVE_16BIT)
+		result <<= 1;
+	if (format & IWFFFF_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp,
+						iwffff_xlfo_t *fx)
+{
+	fp->freq = le16_to_cpu(fx->freq);
+	fp->depth = le16_to_cpu(fx->depth);
+	fp->sweep = le16_to_cpu(fx->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
+					       iwffff_layer_t *lp,
+					       iwffff_env_t *ep,
+					       iwffff_xenv_t *ex,
+					       char __user **data,
+					       long *len,
+					       int gfp_mask)
+{
+	__u32 stype;
+	iwffff_env_record_t *rp, *rp_last;
+	iwffff_xenv_record_t rx;
+	iwffff_env_point_t *pp;
+	iwffff_xenv_point_t px;
+	int points_size, idx;
+
+	ep->flags = ex->flags;
+	ep->mode = ex->mode;
+	ep->index = ex->index;
+	rp_last = NULL;
+	while (1) {
+		if (*len < (long)sizeof(__u32))
+			return -EINVAL;
+		if (copy_from_user(&stype, *data, sizeof(stype)))
+			return -EFAULT;
+		if (stype == IWFFFF_STRU_WAVE)
+			return 0;
+		if (req_stype != stype) {
+			if (stype == IWFFFF_STRU_ENV_RECP ||
+			    stype == IWFFFF_STRU_ENV_RECV)
+				return 0;
+		}
+		if (*len < (long)sizeof(rx))
+			return -EINVAL;
+		if (copy_from_user(&rx, *data, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
+		if (points_size > *len)
+			return -EINVAL;
+		rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask);
+		if (rp == NULL)
+			return -ENOMEM;
+		rp->nattack = le16_to_cpu(rx.nattack);
+		rp->nrelease = le16_to_cpu(rx.nrelease);
+		rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
+		rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
+		rp->release_rate = le16_to_cpu(rx.release_rate);
+		rp->hirange = rx.hirange;
+		pp = (iwffff_env_point_t *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			if (copy_from_user(&px, *data, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+			pp->offset = le16_to_cpu(px.offset);
+			pp->rate = le16_to_cpu(px.rate);
+		}
+		if (ep->record == NULL) {
+			ep->record = rp;
+		} else {
+			rp_last = rp;
+		}
+		rp_last = rp;
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops,
+						iwffff_layer_t *lp,
+					        char __user **data,
+					        long *len,
+					        int atomic)
+{
+	iwffff_wave_t *wp, *prev;
+	iwffff_xwave_t xp;
+	int err, gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < (long)sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = kcalloc(1, sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->address.memory = le32_to_cpu(xp.offset);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
+	wp->attenuation = xp.attenuation;
+	wp->low_note = xp.low_note;
+	wp->high_note = xp.high_note;
+	real_size = snd_seq_iwffff_size(wp->size, wp->format);
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		if ((long)real_size > *len) {
+			kfree(wp);
+			return -ENOMEM;
+		}
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		*data += real_size;
+		*len -= real_size;
+	}
+	prev = lp->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		lp->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops,
+				    iwffff_env_t *env,
+				    int atomic)
+{
+	iwffff_env_record_t *rec;
+	
+	while ((rec = env->record) != NULL) {
+		env->record = rec->next;
+		kfree(rec);
+	}
+}
+				    
+static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops,
+				     iwffff_wave_t *wave,
+				     int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops,
+                                      iwffff_instrument_t *ip,
+                                      int atomic)
+{
+	iwffff_layer_t *layer;
+	iwffff_wave_t *wave;
+	
+	while ((layer = ip->layer) != NULL) {
+		ip->layer = layer->next;
+		snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
+		snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
+		while ((wave = layer->wave) != NULL) {
+			layer->wave = wave->next;
+			snd_seq_iwffff_wave_free(ops, wave, atomic);
+		}
+		kfree(layer);
+	}
+}
+
+static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr,
+			      char __user *instr_data, long len, int atomic,
+			      int cmd)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+	iwffff_xinstrument_t ix;
+	iwffff_layer_t *lp, *prev_lp;
+	iwffff_xlayer_t lx;
+	int err, gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != IWFFFF_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->layer_type = le16_to_cpu(ix.layer_type);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	prev_lp = NULL;
+	while (len > 0) {
+		if (len < (long)sizeof(iwffff_xlayer_t)) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		if (copy_from_user(&lx, instr_data, sizeof(lx)))
+			return -EFAULT;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		if (lx.stype != IWFFFF_STRU_LAYER) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		lp = kcalloc(1, sizeof(*lp), gfp_mask);
+		if (lp == NULL) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -ENOMEM;
+		}
+		if (prev_lp) {
+			prev_lp->next = lp;
+		} else {
+			ip->layer = lp;
+		}
+		prev_lp = lp;
+		lp->flags = lx.flags;
+		lp->velocity_mode = lx.velocity_mode;
+		lp->layer_event = lx.layer_event;
+		lp->low_range = lx.low_range;
+		lp->high_range = lx.high_range;
+		lp->pan = lx.pan;
+		lp->pan_freq_scale = lx.pan_freq_scale;
+		lp->attenuation = lx.attenuation;
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
+		lp->freq_scale = le16_to_cpu(lx.freq_scale);
+		lp->freq_center = lx.freq_center;
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
+							  lp,
+							  &lp->penv, &lx.penv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
+							  lp,
+							  &lp->venv, &lx.venv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		while (len > (long)sizeof(__u32)) {
+			__u32 stype;
+
+			if (copy_from_user(&stype, instr_data, sizeof(stype)))
+				return -EFAULT;
+			if (stype != IWFFFF_STRU_WAVE)
+				break;
+			err = snd_seq_iwffff_copy_wave_from_stream(ops,
+								   lp,
+							    	   &instr_data,
+								   &len,
+								   atomic);
+			if (err < 0) {
+				snd_seq_iwffff_instr_free(ops, ip, atomic);
+				return err;
+			}
+		}
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx,
+					      iwffff_lfo_t *fp)
+{
+	fx->freq = cpu_to_le16(fp->freq);
+	fx->depth = cpu_to_le16(fp->depth);
+	fx->sweep = cpu_to_le16(fp->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
+					     iwffff_layer_t *lp,
+					     iwffff_xenv_t *ex,
+					     iwffff_env_t *ep,
+					     char __user **data,
+					     long *len)
+{
+	iwffff_env_record_t *rp;
+	iwffff_xenv_record_t rx;
+	iwffff_env_point_t *pp;
+	iwffff_xenv_point_t px;
+	int points_size, idx;
+
+	ex->flags = ep->flags;
+	ex->mode = ep->mode;
+	ex->index = ep->index;
+	for (rp = ep->record; rp; rp = rp->next) {
+		if (*len < (long)sizeof(rx))
+			return -ENOMEM;
+		memset(&rx, 0, sizeof(rx));
+		rx.stype = req_stype;
+		rx.nattack = cpu_to_le16(rp->nattack);
+		rx.nrelease = cpu_to_le16(rp->nrelease);
+		rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
+		rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
+		rx.release_rate = cpu_to_le16(rp->release_rate);
+		rx.hirange = cpu_to_le16(rp->hirange);
+		if (copy_to_user(*data, &rx, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+		if (*len < points_size)
+			return -ENOMEM;
+		pp = (iwffff_env_point_t *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			px.offset = cpu_to_le16(pp->offset);
+			px.rate = cpu_to_le16(pp->rate);
+			if (copy_to_user(*data, &px, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops,
+					      iwffff_layer_t *lp,
+					      char __user **data,
+					      long *len,
+					      int atomic)
+{
+	iwffff_wave_t *wp;
+	iwffff_xwave_t xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		if (*len < (long)sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = IWFFFF_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		if (wp->format & IWFFFF_WAVE_ROM)
+			xp.offset = cpu_to_le32(wp->address.memory);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
+		xp.attenuation = wp->attenuation;
+		xp.low_note = wp->low_note;
+		xp.high_note = wp->high_note;
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_iwffff_size(wp->size, wp->format);
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			if (*len < (long)real_size)
+				return -ENOMEM;
+		}
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			*data += real_size;
+			*len -= real_size;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr,
+			      char __user *instr_data, long len, int atomic, int cmd)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+	iwffff_xinstrument_t ix;
+	iwffff_layer_t *lp;
+	iwffff_xlayer_t lx;
+	char __user *layer_instr_data;
+	int err;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = IWFFFF_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.layer_type = cpu_to_le16(ip->layer_type);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		if (len < (long)sizeof(lx))
+			return -ENOMEM;
+		memset(&lx, 0, sizeof(lx));
+		lx.stype = IWFFFF_STRU_LAYER;
+		lx.flags = lp->flags;
+		lx.velocity_mode = lp->velocity_mode;
+		lx.layer_event = lp->layer_event;
+		lx.low_range = lp->low_range;
+		lx.high_range = lp->high_range;
+		lx.pan = lp->pan;
+		lx.pan_freq_scale = lp->pan_freq_scale;
+		lx.attenuation = lp->attenuation;
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
+		layer_instr_data = instr_data;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
+							lp,
+							&lx.penv, &lp->penv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
+							lp,
+							&lx.venv, &lp->venv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		/* layer structure updating is now finished */
+		if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
+			return -EFAULT;
+		err = snd_seq_iwffff_copy_wave_to_stream(ops,
+							 lp,
+							 &instr_data,
+							 &len,
+							 atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep)
+{
+	long result = 0;
+	iwffff_env_record_t *rp;
+
+	for (rp = ep->record; rp; rp = rp->next) {
+		result += sizeof(iwffff_xenv_record_t);
+		result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp)
+{
+	long result = 0;
+	iwffff_wave_t *wp;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		result += sizeof(iwffff_xwave_t);
+		if (!(wp->format & IWFFFF_WAVE_ROM))
+			result += wp->size;
+	}
+	return result;
+}
+
+static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				   long *size)
+{
+	long result;
+	iwffff_instrument_t *ip;
+	iwffff_layer_t *lp;
+
+	*size = 0;
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	result = sizeof(iwffff_xinstrument_t);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		result += sizeof(iwffff_xlayer_t);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
+		result += snd_seq_iwffff_wave_size_in_stream(lp);
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_iwffff_remove(void *private_data,
+				 snd_seq_kinstr_t *instr,
+                                 int atomic)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+	iwffff_instrument_t *ip;
+
+	ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_iwffff_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_iwffff_notify(void *private_data,
+				  snd_seq_kinstr_t *instr,
+                                  int what)
+{
+	snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
+			void *private_data,
+			snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(iwffff_instrument_t);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
+	ops->kops.put = snd_seq_iwffff_put;
+	ops->kops.get = snd_seq_iwffff_get;
+	ops->kops.get_size = snd_seq_iwffff_get_size;
+	ops->kops.remove = snd_seq_iwffff_remove;
+	ops->kops.notify = snd_seq_iwffff_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_iw_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_iw_exit(void)
+{
+}
+
+module_init(alsa_ainstr_iw_init)
+module_exit(alsa_ainstr_iw_exit)
+
+EXPORT_SYMBOL(snd_seq_iwffff_init);
diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c
new file mode 100644
index 0000000..6183d21
--- /dev/null
+++ b/sound/core/seq/instr/ainstr_simple.c
@@ -0,0 +1,215 @@
+/*
+ *   Simple (MOD player) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_simple.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & SIMPLE_WAVE_16BIT)
+		result <<= 1;
+	if (format & SIMPLE_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_simple_instr_free(snd_simple_ops_t *ops,
+				      simple_instrument_t *ip,
+				      int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, ip, atomic);
+}
+
+static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr,
+			      char __user *instr_data, long len,
+			      int atomic, int cmd)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+	simple_xinstrument_t ix;
+	int err, gfp_mask;
+	unsigned int real_size;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != SIMPLE_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->format = le32_to_cpu(ix.format);
+	ip->size = le32_to_cpu(ix.size);
+	ip->start = le32_to_cpu(ix.start);
+	ip->loop_start = le32_to_cpu(ix.loop_start);
+	ip->loop_end = le32_to_cpu(ix.loop_end);
+	ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < (long)real_size)
+		return -EINVAL;
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr,
+			      char __user *instr_data, long len,
+			      int atomic, int cmd)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+	simple_xinstrument_t ix;
+	int err;
+	unsigned int real_size;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	ix.stype = SIMPLE_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.format = cpu_to_le32(ip->format);
+	ix.size = cpu_to_le32(ip->size);
+	ix.start = cpu_to_le32(ip->start);
+	ix.loop_start = cpu_to_le32(ip->loop_start);
+	ix.loop_end = cpu_to_le32(ip->loop_end);
+	ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < (long)real_size)
+		return -ENOMEM;
+	if (ops->get_sample) {
+		err = ops->get_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr,
+				   long *size)
+{
+	simple_instrument_t *ip;
+
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	*size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format);
+	return 0;
+}
+
+static int snd_seq_simple_remove(void *private_data,
+			         snd_seq_kinstr_t *instr,
+                                 int atomic)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+	simple_instrument_t *ip;
+
+	ip = (simple_instrument_t *)KINSTR_DATA(instr);
+	snd_seq_simple_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_simple_notify(void *private_data,
+			          snd_seq_kinstr_t *instr,
+                                  int what)
+{
+	snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_simple_init(snd_simple_ops_t *ops,
+		        void *private_data,
+		        snd_seq_kinstr_ops_t *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(simple_instrument_t);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
+	ops->kops.put = snd_seq_simple_put;
+	ops->kops.get = snd_seq_simple_get;
+	ops->kops.get_size = snd_seq_simple_get_size;
+	ops->kops.remove = snd_seq_simple_remove;
+	ops->kops.notify = snd_seq_simple_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_simple_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_simple_exit(void)
+{
+}
+
+module_init(alsa_ainstr_simple_init)
+module_exit(alsa_ainstr_simple_exit)
+
+EXPORT_SYMBOL(snd_seq_simple_init);
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
new file mode 100644
index 0000000..a37dded
--- /dev/null
+++ b/sound/core/seq/oss/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-seq-oss-objs  := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
+		     seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
+		     seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
+
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
new file mode 100644
index 0000000..4c0558c
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss.c
@@ -0,0 +1,317 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * registration of device and proc
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+
+/*
+ * module option
+ */
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("OSS-compatible sequencer module");
+MODULE_LICENSE("GPL");
+/* Takashi says this is really only for sound-service-0-, but this is OK. */
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
+MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
+
+#ifdef SNDRV_SEQ_OSS_DEBUG
+module_param(seq_oss_debug, int, 0644);
+MODULE_PARM_DESC(seq_oss_debug, "debug option");
+int seq_oss_debug = 0;
+#endif
+
+
+/*
+ * prototypes
+ */
+static int register_device(void);
+static void unregister_device(void);
+static int register_proc(void);
+static void unregister_proc(void);
+
+static int odev_open(struct inode *inode, struct file *file);
+static int odev_release(struct inode *inode, struct file *file);
+static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
+static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
+static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static unsigned int odev_poll(struct file *file, poll_table * wait);
+#ifdef CONFIG_PROC_FS
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf);
+#endif
+
+
+/*
+ * module interface
+ */
+
+static int __init alsa_seq_oss_init(void)
+{
+	int rc;
+	static snd_seq_dev_ops_t ops = {
+		snd_seq_oss_synth_register,
+		snd_seq_oss_synth_unregister,
+	};
+
+	snd_seq_autoload_lock();
+	if ((rc = register_device()) < 0)
+		goto error;
+	if ((rc = register_proc()) < 0) {
+		unregister_device();
+		goto error;
+	}
+	if ((rc = snd_seq_oss_create_client()) < 0) {
+		unregister_proc();
+		unregister_device();
+		goto error;
+	}
+
+	if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
+						 sizeof(snd_seq_oss_reg_t))) < 0) {
+		snd_seq_oss_delete_client();
+		unregister_proc();
+		unregister_device();
+		goto error;
+	}
+
+	/* success */
+	snd_seq_oss_synth_init();
+
+ error:
+	snd_seq_autoload_unlock();
+	return rc;
+}
+
+static void __exit alsa_seq_oss_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
+	snd_seq_oss_delete_client();
+	unregister_proc();
+	unregister_device();
+}
+
+module_init(alsa_seq_oss_init)
+module_exit(alsa_seq_oss_exit)
+
+/*
+ * ALSA minor device interface
+ */
+
+static DECLARE_MUTEX(register_mutex);
+
+static int
+odev_open(struct inode *inode, struct file *file)
+{
+	int level, rc;
+
+	if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
+		level = SNDRV_SEQ_OSS_MODE_MUSIC;
+	else
+		level = SNDRV_SEQ_OSS_MODE_SYNTH;
+
+	down(&register_mutex);
+	rc = snd_seq_oss_open(file, level);
+	up(&register_mutex);
+
+	return rc;
+}
+
+static int
+odev_release(struct inode *inode, struct file *file)
+{
+	seq_oss_devinfo_t *dp;
+
+	if ((dp = file->private_data) == NULL)
+		return 0;
+
+	snd_seq_oss_drain_write(dp);
+
+	down(&register_mutex);
+	snd_seq_oss_release(dp);
+	up(&register_mutex);
+
+	return 0;
+}
+
+static ssize_t
+odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_read(dp, buf, count);
+}
+
+
+static ssize_t
+odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_write(dp, buf, count, file);
+}
+
+static long
+odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return -EIO);
+	return snd_seq_oss_ioctl(dp, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+#define odev_ioctl_compat	odev_ioctl
+#else
+#define odev_ioctl_compat	NULL
+#endif
+
+static unsigned int
+odev_poll(struct file *file, poll_table * wait)
+{
+	seq_oss_devinfo_t *dp;
+	dp = file->private_data;
+	snd_assert(dp != NULL, return 0);
+	return snd_seq_oss_poll(dp, file, wait);
+}
+
+/*
+ * registration of sequencer minor device
+ */
+
+static struct file_operations seq_oss_f_ops =
+{
+	.owner =	THIS_MODULE,
+	.read =		odev_read,
+	.write =	odev_write,
+	.open =		odev_open,
+	.release =	odev_release,
+	.poll =		odev_poll,
+	.unlocked_ioctl =	odev_ioctl,
+	.compat_ioctl =	odev_ioctl_compat,
+};
+
+static snd_minor_t seq_oss_reg = {
+	.comment =	"sequencer",
+	.f_ops =	&seq_oss_f_ops,
+};
+
+static int __init
+register_device(void)
+{
+	int rc;
+
+	down(&register_mutex);
+	if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
+					  NULL, 0,
+					  &seq_oss_reg,
+					  SNDRV_SEQ_OSS_DEVNAME)) < 0) {
+		snd_printk(KERN_ERR "can't register device seq\n");
+		up(&register_mutex);
+		return rc;
+	}
+	if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
+					  NULL, 0,
+					  &seq_oss_reg,
+					  SNDRV_SEQ_OSS_DEVNAME)) < 0) {
+		snd_printk(KERN_ERR "can't register device music\n");
+		snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
+		up(&register_mutex);
+		return rc;
+	}
+	debug_printk(("device registered\n"));
+	up(&register_mutex);
+	return 0;
+}
+
+static void
+unregister_device(void)
+{
+	down(&register_mutex);
+	debug_printk(("device unregistered\n"));
+	if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)		
+		snd_printk(KERN_ERR "error unregister device music\n");
+	if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
+		snd_printk(KERN_ERR "error unregister device seq\n");
+	up(&register_mutex);
+}
+
+/*
+ * /proc interface
+ */
+
+#ifdef CONFIG_PROC_FS
+
+static snd_info_entry_t *info_entry;
+
+static void
+info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf)
+{
+	down(&register_mutex);
+	snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
+	snd_seq_oss_system_info_read(buf);
+	snd_seq_oss_synth_info_read(buf);
+	snd_seq_oss_midi_info_read(buf);
+	up(&register_mutex);
+}
+
+#endif /* CONFIG_PROC_FS */
+
+static int __init
+register_proc(void)
+{
+#ifdef CONFIG_PROC_FS
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
+	if (entry == NULL)
+		return -ENOMEM;
+
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->private_data = NULL;
+	entry->c.text.read_size = 1024;
+	entry->c.text.read = info_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	info_entry = entry;
+#endif
+	return 0;
+}
+
+static void
+unregister_proc(void)
+{
+#ifdef CONFIG_PROC_FS
+	if (info_entry)
+		snd_info_unregister(info_entry);
+	info_entry = NULL;
+#endif
+}
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
new file mode 100644
index 0000000..da23c4d
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -0,0 +1,198 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_DEVICE_H
+#define __SEQ_OSS_DEVICE_H
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/seq_oss.h>
+#include <sound/rawmidi.h>
+#include <sound/seq_kernel.h>
+#include <sound/info.h>
+
+/* enable debug print */
+#define SNDRV_SEQ_OSS_DEBUG
+
+/* max. applications */
+#define SNDRV_SEQ_OSS_MAX_CLIENTS	16
+#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS	16
+#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS	32
+
+/* version */
+#define SNDRV_SEQ_OSS_MAJOR_VERSION	0
+#define SNDRV_SEQ_OSS_MINOR_VERSION	1
+#define SNDRV_SEQ_OSS_TINY_VERSION	8
+#define SNDRV_SEQ_OSS_VERSION_STR	"0.1.8"
+
+/* device and proc interface name */
+#define SNDRV_SEQ_OSS_DEVNAME		"seq_oss"
+#define SNDRV_SEQ_OSS_PROCNAME		"oss"
+
+
+/*
+ * type definitions
+ */
+
+typedef struct seq_oss_devinfo_t seq_oss_devinfo_t;
+typedef struct seq_oss_writeq_t seq_oss_writeq_t;
+typedef struct seq_oss_readq_t seq_oss_readq_t;
+typedef struct seq_oss_timer_t seq_oss_timer_t;
+typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t;
+typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t;
+typedef struct seq_oss_chinfo_t seq_oss_chinfo_t;
+typedef unsigned int reltime_t;
+typedef unsigned int abstime_t;
+typedef union evrec_t evrec_t;
+
+
+/*
+ * synthesizer channel information
+ */
+struct seq_oss_chinfo_t {
+	int note, vel;
+};
+
+/*
+ * synthesizer information
+ */
+struct seq_oss_synthinfo_t {
+	snd_seq_oss_arg_t arg;
+	seq_oss_chinfo_t *ch;
+	seq_oss_synth_sysex_t *sysex;
+	int nr_voices;
+	int opened;
+	int is_midi;
+	int midi_mapped;
+};
+
+
+/*
+ * sequencer client information
+ */
+
+struct seq_oss_devinfo_t {
+
+	int index;	/* application index */
+	int cseq;	/* sequencer client number */
+	int port;	/* sequencer port number */
+	int queue;	/* sequencer queue number */
+
+	snd_seq_addr_t addr;	/* address of this device */
+
+	int seq_mode;	/* sequencer mode */
+	int file_mode;	/* file access */
+
+	/* midi device table */
+	int max_mididev;
+
+	/* synth device table */
+	int max_synthdev;
+	seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
+	int synth_opened;
+
+	/* output queue */
+	seq_oss_writeq_t *writeq;
+
+	/* midi input queue */
+	seq_oss_readq_t *readq;
+
+	/* timer */
+	seq_oss_timer_t *timer;
+};
+
+
+/*
+ * function prototypes
+ */
+
+/* create/delete OSS sequencer client */
+int snd_seq_oss_create_client(void);
+int snd_seq_oss_delete_client(void);
+
+/* device file interface */
+int snd_seq_oss_open(struct file *file, int level);
+void snd_seq_oss_release(seq_oss_devinfo_t *dp);
+int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg);
+int snd_seq_oss_read(seq_oss_devinfo_t *dev, char __user *buf, int count);
+int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt);
+unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait);
+
+void snd_seq_oss_reset(seq_oss_devinfo_t *dp);
+void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp);
+
+/* */
+void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time);
+
+
+/* proc interface */
+void snd_seq_oss_system_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf);
+void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf);
+
+/* file mode macros */
+#define is_read_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_READ)
+#define is_write_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_WRITE)
+#define is_nonblock_mode(mode)	((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK)
+
+/* dispatch event */
+inline static int
+snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop)
+{
+	return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
+}
+
+/* ioctl */
+inline static int
+snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg)
+{
+	return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
+}
+
+/* fill the addresses in header */
+inline static void
+snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev,
+		     int dest_client, int dest_port)
+{
+	ev->queue = dp->queue;
+	ev->source = dp->addr;
+	ev->dest.client = dest_client;
+	ev->dest.port = dest_port;
+}
+
+
+/* misc. functions for proc interface */
+char *enabled_str(int bool);
+
+
+/* for debug */
+#ifdef SNDRV_SEQ_OSS_DEBUG
+extern int seq_oss_debug;
+#define debug_printk(x)	do { if (seq_oss_debug > 0) snd_printk x; } while (0)
+#else
+#define debug_printk(x)	/**/
+#endif
+
+#endif /* __SEQ_OSS_DEVICE_H */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
new file mode 100644
index 0000000..58e52dd
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -0,0 +1,447 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include <sound/seq_oss_legacy.h>
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+
+
+/*
+ * prototypes
+ */
+static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
+static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
+static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
+static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev);
+static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev);
+static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev);
+
+
+/*
+ * convert an OSS event to ALSA event
+ * return 0 : enqueued
+ *        non-zero : invalid - ignored
+ */
+
+int
+snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->s.code) {
+	case SEQ_EXTENDED:
+		return extended_event(dp, q, ev);
+
+	case EV_CHN_VOICE:
+		return chn_voice_event(dp, q, ev);
+
+	case EV_CHN_COMMON:
+		return chn_common_event(dp, q, ev);
+
+	case EV_TIMING:
+		return timing_event(dp, q, ev);
+
+	case EV_SEQ_LOCAL:
+		return local_event(dp, q, ev);
+
+	case EV_SYSEX:
+		return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
+
+	case SEQ_MIDIPUTC:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		/* put a midi byte */
+		if (! is_write_mode(dp->file_mode))
+			break;
+		if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
+			break;
+		if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
+			return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
+		break;
+
+	case SEQ_ECHO:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return set_echo_event(dp, q, ev);
+
+	case SEQ_PRIVATE:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
+
+	default:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return -EINVAL;
+		return old_event(dp, q, ev);
+	}
+	return -EINVAL;
+}
+
+/* old type events: mode1 only */
+static int
+old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->s.code) {
+	case SEQ_NOTEOFF:
+		return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
+
+	case SEQ_NOTEON:
+		return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
+
+	case SEQ_WAIT:
+		/* skip */
+		break;
+
+	case SEQ_PGMCHANGE:
+		return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
+					 q->n.chn, 0, q->n.note, ev);
+
+	case SEQ_SYNCTIMER:
+		return snd_seq_oss_timer_reset(dp->timer);
+	}
+
+	return -EINVAL;
+}
+
+/* 8bytes extended event: mode1 only */
+static int
+extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	int val;
+
+	switch (q->e.cmd) {
+	case SEQ_NOTEOFF:
+		return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
+
+	case SEQ_NOTEON:
+		return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
+
+	case SEQ_PGMCHANGE:
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
+					 q->e.chn, 0, q->e.p1, ev);
+
+	case SEQ_AFTERTOUCH:
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
+					 q->e.chn, 0, q->e.p1, ev);
+
+	case SEQ_BALANCE:
+		/* convert -128:127 to 0:127 */
+		val = (char)q->e.p1;
+		val = (val + 128) / 2;
+		return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
+					 q->e.chn, CTL_PAN, val, ev);
+
+	case SEQ_CONTROLLER:
+		val = ((short)q->e.p3 << 8) | (short)q->e.p2;
+		switch (q->e.p1) {
+		case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
+			/* -0x2000:0x1fff */
+			return set_control_event(dp, q->e.dev,
+						 SNDRV_SEQ_EVENT_PITCHBEND,
+						 q->e.chn, 0, val, ev);
+		case CTRL_PITCH_BENDER_RANGE:
+			/* conversion: 100/semitone -> 128/semitone */
+			return set_control_event(dp, q->e.dev,
+						 SNDRV_SEQ_EVENT_REGPARAM,
+						 q->e.chn, 0, val*128/100, ev);
+		default:
+			return set_control_event(dp, q->e.dev,
+						  SNDRV_SEQ_EVENT_CONTROL14,
+						  q->e.chn, q->e.p1, val, ev);
+		}
+
+	case SEQ_VOLMODE:
+		return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
+
+	}
+	return -EINVAL;
+}
+
+/* channel voice events: mode1 and 2 */
+static int
+chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	if (q->v.chn >= 32)
+		return -EINVAL;
+	switch (q->v.cmd) {
+	case MIDI_NOTEON:
+		return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
+
+	case MIDI_NOTEOFF:
+		return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
+
+	case MIDI_KEY_PRESSURE:
+		return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
+				       q->v.chn, q->v.note, q->v.parm, ev);
+
+	}
+	return -EINVAL;
+}
+
+/* channel common events: mode1 and 2 */
+static int
+chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	if (q->l.chn >= 32)
+		return -EINVAL;
+	switch (q->l.cmd) {
+	case MIDI_PGM_CHANGE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
+					  q->l.chn, 0, q->l.p1, ev);
+
+	case MIDI_CTL_CHANGE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
+					  q->l.chn, q->l.p1, q->l.val, ev);
+
+	case MIDI_PITCH_BEND:
+		/* conversion: 0:0x3fff -> -0x2000:0x1fff */
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
+					  q->l.chn, 0, q->l.val - 8192, ev);
+		
+	case MIDI_CHN_PRESSURE:
+		return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
+					  q->l.chn, 0, q->l.val, ev);
+	}
+	return -EINVAL;
+}
+
+/* timer events: mode1 and mode2 */
+static int
+timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	switch (q->t.cmd) {
+	case TMR_ECHO:
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+			return set_echo_event(dp, q, ev);
+		else {
+			evrec_t tmp;
+			memset(&tmp, 0, sizeof(tmp));
+			/* XXX: only for little-endian! */
+			tmp.echo = (q->t.time << 8) | SEQ_ECHO;
+			return set_echo_event(dp, &tmp, ev);
+		} 
+
+	case TMR_STOP:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_stop(dp->timer);
+		return 0;
+
+	case TMR_CONTINUE:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_continue(dp->timer);
+		return 0;
+
+	case TMR_TEMPO:
+		if (dp->seq_mode)
+			return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* local events: mode1 and 2 */
+static int
+local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
+{
+	return -EINVAL;
+}
+
+/*
+ * process note-on event for OSS synth
+ * three different modes are available:
+ * - SNDRV_SEQ_OSS_PROCESS_EVENTS  (for one-voice per channel mode)
+ *	Accept note 255 as volume change.
+ * - SNDRV_SEQ_OSS_PASS_EVENTS
+ *	Pass all events to lowlevel driver anyway
+ * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS  (mostly for Emu8000)
+ *	Use key-pressure if note >= 128
+ */
+static int
+note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	seq_oss_synthinfo_t *info = &dp->synths[dev];
+	switch (info->arg.event_passing) {
+	case SNDRV_SEQ_OSS_PROCESS_EVENTS:
+		if (! info->ch || ch < 0 || ch >= info->nr_voices) {
+			/* pass directly */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		}
+
+		if (note == 255 && info->ch[ch].note >= 0) {
+			/* volume control */
+			int type;
+			//if (! vel)
+				/* set volume to zero -- note off */
+			//	type = SNDRV_SEQ_EVENT_NOTEOFF;
+			//else
+				if (info->ch[ch].vel)
+				/* sample already started -- volume change */
+				type = SNDRV_SEQ_EVENT_KEYPRESS;
+			else
+				/* sample not started -- start now */
+				type = SNDRV_SEQ_EVENT_NOTEON;
+			info->ch[ch].vel = vel;
+			return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
+		} else if (note >= 128)
+			return -EINVAL; /* invalid */
+
+		if (note != info->ch[ch].note && info->ch[ch].note >= 0)
+			/* note changed - note off at beginning */
+			set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
+		/* set current status */
+		info->ch[ch].note = note;
+		info->ch[ch].vel = vel;
+		if (vel) /* non-zero velocity - start the note now */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		return -EINVAL;
+		
+	case SNDRV_SEQ_OSS_PASS_EVENTS:
+		/* pass the event anyway */
+		return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+
+	case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
+		if (note >= 128) /* key pressure: shifted by 128 */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
+		else /* normal note-on event */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+	}
+	return -EINVAL;
+}
+
+/*
+ * process note-off event for OSS synth
+ */
+static int
+note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	seq_oss_synthinfo_t *info = &dp->synths[dev];
+	switch (info->arg.event_passing) {
+	case SNDRV_SEQ_OSS_PROCESS_EVENTS:
+		if (! info->ch || ch < 0 || ch >= info->nr_voices) {
+			/* pass directly */
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
+		}
+
+		if (info->ch[ch].note >= 0) {
+			note = info->ch[ch].note;
+			info->ch[ch].vel = 0;
+			info->ch[ch].note = -1;
+			return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
+		}
+		return -EINVAL; /* invalid */
+
+	case SNDRV_SEQ_OSS_PASS_EVENTS:
+	case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
+		/* pass the event anyway */
+		return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
+
+	}
+	return -EINVAL;
+}
+
+/*
+ * create a note event
+ */
+static int
+set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+	
+	ev->type = type;
+	snd_seq_oss_synth_addr(dp, dev, ev);
+	ev->data.note.channel = ch;
+	ev->data.note.note = note;
+	ev->data.note.velocity = vel;
+
+	return 0;
+}
+
+/*
+ * create a control event
+ */
+static int
+set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+	
+	ev->type = type;
+	snd_seq_oss_synth_addr(dp, dev, ev);
+	ev->data.control.channel = ch;
+	ev->data.control.param = param;
+	ev->data.control.value = val;
+
+	return 0;
+}
+
+/*
+ * create an echo event
+ */
+static int
+set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev)
+{
+	ev->type = SNDRV_SEQ_EVENT_ECHO;
+	/* echo back to itself */
+	snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
+	memcpy(&ev->data, rec, LONG_EVENT_SIZE);
+	return 0;
+}
+
+/*
+ * event input callback from ALSA sequencer:
+ * the echo event is processed here.
+ */
+int
+snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data,
+			int atomic, int hop)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
+	evrec_t *rec;
+
+	if (ev->type != SNDRV_SEQ_EVENT_ECHO)
+		return snd_seq_oss_midi_input(ev, direct, private_data);
+
+	if (ev->source.client != dp->cseq)
+		return 0; /* ignored */
+
+	rec = (evrec_t*)&ev->data;
+	if (rec->s.code == SEQ_SYNCTIMER) {
+		/* sync echo back */
+		snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
+		
+	} else {
+		/* echo back event */
+		if (dp->readq == NULL)
+			return 0;
+		snd_seq_oss_readq_put_event(dp->readq, rec);
+	}
+	return 0;
+}
+
diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h
new file mode 100644
index 0000000..bf1d4d3
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_event.h
@@ -0,0 +1,112 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_event.h - OSS event queue record
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_EVENT_H
+#define __SEQ_OSS_EVENT_H
+
+#include "seq_oss_device.h"
+
+#define SHORT_EVENT_SIZE	4
+#define LONG_EVENT_SIZE		8
+
+/* short event (4bytes) */
+typedef struct evrec_short_t {
+	unsigned char code;
+	unsigned char parm1;
+	unsigned char dev;
+	unsigned char parm2;
+} evrec_short_t;
+	
+/* short note events (4bytes) */
+typedef struct evrec_note_t {
+	unsigned char code;
+	unsigned char chn;
+	unsigned char note;
+	unsigned char vel;
+} evrec_note_t;
+	
+/* long timer events (8bytes) */
+typedef struct evrec_timer_t {
+	unsigned char code;
+	unsigned char cmd;
+	unsigned char dummy1, dummy2;
+	unsigned int time;
+} evrec_timer_t;
+
+/* long extended events (8bytes) */
+typedef struct evrec_extended_t {
+	unsigned char code;
+	unsigned char cmd;
+	unsigned char dev;
+	unsigned char chn;
+	unsigned char p1, p2, p3, p4;
+} evrec_extended_t;
+
+/* long channel events (8bytes) */
+typedef struct evrec_long_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char cmd;
+	unsigned char chn;
+	unsigned char p1, p2;
+	unsigned short val;
+} evrec_long_t;
+	
+/* channel voice events (8bytes) */
+typedef struct evrec_voice_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char cmd;
+	unsigned char chn;
+	unsigned char note, parm;
+	unsigned short dummy;
+} evrec_voice_t;
+
+/* sysex events (8bytes) */
+typedef struct evrec_sysex_t {
+	unsigned char code;
+	unsigned char dev;
+	unsigned char buf[6];
+} evrec_sysex_t;
+
+/* event record */
+union evrec_t {
+	evrec_short_t s;
+	evrec_note_t n;
+	evrec_long_t l;
+	evrec_voice_t v;
+	evrec_timer_t t;
+	evrec_extended_t e;
+	evrec_sysex_t x;
+	unsigned int echo;
+	unsigned char c[LONG_EVENT_SIZE];
+};
+
+#define ev_is_long(ev) ((ev)->s.code >= 128)
+#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
+
+int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
+int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q);
+int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop);
+
+
+#endif /* __SEQ_OSS_EVENT_H */
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
new file mode 100644
index 0000000..bac4b4f
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -0,0 +1,555 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * open/close and reset interface
+ *
+ * Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_device.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+/*
+ * common variables
+ */
+static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
+module_param(maxqlen, int, 0444);
+MODULE_PARM_DESC(maxqlen, "maximum queue length");
+
+static int system_client = -1; /* ALSA sequencer client number */
+static int system_port = -1;
+
+static int num_clients;
+static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
+
+
+/*
+ * prototypes
+ */
+static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+static int translate_mode(struct file *file);
+static int create_port(seq_oss_devinfo_t *dp);
+static int delete_port(seq_oss_devinfo_t *dp);
+static int alloc_seq_queue(seq_oss_devinfo_t *dp);
+static int delete_seq_queue(int queue);
+static void free_devinfo(void *private);
+
+#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
+
+
+/*
+ * create sequencer client for OSS sequencer
+ */
+int __init
+snd_seq_oss_create_client(void)
+{
+	int rc;
+	snd_seq_client_callback_t callback;
+	snd_seq_client_info_t *info;
+	snd_seq_port_info_t *port;
+	snd_seq_port_callback_t port_callback;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	port = kmalloc(sizeof(*port), GFP_KERNEL);
+	if (!info || !port) {
+		rc = -ENOMEM;
+		goto __error;
+	}
+
+	/* create ALSA client */
+	memset(&callback, 0, sizeof(callback));
+
+	callback.private_data = NULL;
+	callback.allow_input = 1;
+	callback.allow_output = 1;
+
+	rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback);
+	if (rc < 0)
+		goto __error;
+
+	system_client = rc;
+	debug_printk(("new client = %d\n", rc));
+
+	/* set client information */
+	memset(info, 0, sizeof(*info));
+	info->client = system_client;
+	info->type = KERNEL_CLIENT;
+	strcpy(info->name, "OSS sequencer");
+
+	rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
+
+	/* look up midi devices */
+	snd_seq_oss_midi_lookup_ports(system_client);
+
+	/* create annoucement receiver port */
+	memset(port, 0, sizeof(*port));
+	strcpy(port->name, "Receiver");
+	port->addr.client = system_client;
+	port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
+	port->type = 0;
+
+	memset(&port_callback, 0, sizeof(port_callback));
+	/* don't set port_callback.owner here. otherwise the module counter
+	 * is incremented and we can no longer release the module..
+	 */
+	port_callback.event_input = receive_announce;
+	port->kernel = &port_callback;
+	
+	call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+	if ((system_port = port->addr.port) >= 0) {
+		snd_seq_port_subscribe_t subs;
+
+		memset(&subs, 0, sizeof(subs));
+		subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+		subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+		subs.dest.client = system_client;
+		subs.dest.port = system_port;
+		call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
+	}
+	rc = 0;
+
+ __error:
+	kfree(port);
+	kfree(info);
+	return rc;
+}
+
+
+/*
+ * receive annoucement from system port, and check the midi device
+ */
+static int
+receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop)
+{
+	snd_seq_port_info_t pinfo;
+
+	if (atomic)
+		return 0; /* it must not happen */
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_PORT_START:
+	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+		if (ev->data.addr.client == system_client)
+			break; /* ignore myself */
+		memset(&pinfo, 0, sizeof(pinfo));
+		pinfo.addr = ev->data.addr;
+		if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
+			snd_seq_oss_midi_check_new_port(&pinfo);
+		break;
+
+	case SNDRV_SEQ_EVENT_PORT_EXIT:
+		if (ev->data.addr.client == system_client)
+			break; /* ignore myself */
+		snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
+						ev->data.addr.port);
+		break;
+	}
+	return 0;
+}
+
+
+/*
+ * delete OSS sequencer client
+ */
+int
+snd_seq_oss_delete_client(void)
+{
+	if (system_client >= 0)
+		snd_seq_delete_kernel_client(system_client);
+
+	snd_seq_oss_midi_clear_all();
+
+	return 0;
+}
+
+
+/*
+ * open sequencer device
+ */
+int
+snd_seq_oss_open(struct file *file, int level)
+{
+	int i, rc;
+	seq_oss_devinfo_t *dp;
+
+	if ((dp = kcalloc(1, sizeof(*dp), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc device info\n");
+		return -ENOMEM;
+	}
+	debug_printk(("oss_open: dp = %p\n", dp));
+
+	for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
+		if (client_table[i] == NULL)
+			break;
+	}
+	if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
+		snd_printk(KERN_ERR "too many applications\n");
+		kfree(dp);
+		return -ENOMEM;
+	}
+
+	dp->index = i;
+	dp->cseq = system_client;
+	dp->port = -1;
+	dp->queue = -1;
+	dp->readq = NULL;
+	dp->writeq = NULL;
+
+	/* look up synth and midi devices */
+	snd_seq_oss_synth_setup(dp);
+	snd_seq_oss_midi_setup(dp);
+
+	if (dp->synth_opened == 0 && dp->max_mididev == 0) {
+		/* snd_printk(KERN_ERR "no device found\n"); */
+		rc = -ENODEV;
+		goto _error;
+	}
+
+	/* create port */
+	debug_printk(("create new port\n"));
+	if ((rc = create_port(dp)) < 0) {
+		snd_printk(KERN_ERR "can't create port\n");
+		goto _error;
+	}
+
+	/* allocate queue */
+	debug_printk(("allocate queue\n"));
+	if ((rc = alloc_seq_queue(dp)) < 0)
+		goto _error;
+
+	/* set address */
+	dp->addr.client = dp->cseq;
+	dp->addr.port = dp->port;
+	/*dp->addr.queue = dp->queue;*/
+	/*dp->addr.channel = 0;*/
+
+	dp->seq_mode = level;
+
+	/* set up file mode */
+	dp->file_mode = translate_mode(file);
+
+	/* initialize read queue */
+	debug_printk(("initialize read queue\n"));
+	if (is_read_mode(dp->file_mode)) {
+		if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
+			rc = -ENOMEM;
+			goto _error;
+		}
+	}
+
+	/* initialize write queue */
+	debug_printk(("initialize write queue\n"));
+	if (is_write_mode(dp->file_mode)) {
+		dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
+		if (dp->writeq == NULL) {
+			rc = -ENOMEM;
+			goto _error;
+		}
+	}
+
+	/* initialize timer */
+	debug_printk(("initialize timer\n"));
+	if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
+		snd_printk(KERN_ERR "can't alloc timer\n");
+		rc = -ENOMEM;
+		goto _error;
+	}
+	debug_printk(("timer initialized\n"));
+
+	/* set private data pointer */
+	file->private_data = dp;
+
+	/* set up for mode2 */
+	if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
+		snd_seq_oss_synth_setup_midi(dp);
+	else if (is_read_mode(dp->file_mode))
+		snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
+
+	client_table[dp->index] = dp;
+	num_clients++;
+
+	debug_printk(("open done\n"));
+	return 0;
+
+ _error:
+	snd_seq_oss_synth_cleanup(dp);
+	snd_seq_oss_midi_cleanup(dp);
+	i = dp->queue;
+	delete_port(dp);
+	delete_seq_queue(i);
+
+	return rc;
+}
+
+/*
+ * translate file flags to private mode
+ */
+static int
+translate_mode(struct file *file)
+{
+	int file_mode = 0;
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
+	if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+		file_mode |= SNDRV_SEQ_OSS_FILE_READ;
+	if (file->f_flags & O_NONBLOCK)
+		file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
+	return file_mode;
+}
+
+
+/*
+ * create sequencer port
+ */
+static int
+create_port(seq_oss_devinfo_t *dp)
+{
+	int rc;
+	snd_seq_port_info_t port;
+	snd_seq_port_callback_t callback;
+
+	memset(&port, 0, sizeof(port));
+	port.addr.client = dp->cseq;
+	sprintf(port.name, "Sequencer-%d", dp->index);
+	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
+	port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
+	port.midi_channels = 128;
+	port.synth_voices = 128;
+
+	memset(&callback, 0, sizeof(callback));
+	callback.owner = THIS_MODULE;
+	callback.private_data = dp;
+	callback.event_input = snd_seq_oss_event_input;
+	callback.private_free = free_devinfo;
+	port.kernel = &callback;
+
+	rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
+	if (rc < 0)
+		return rc;
+
+	dp->port = port.addr.port;
+	debug_printk(("new port = %d\n", port.addr.port));
+
+	return 0;
+}
+
+/*
+ * delete ALSA port
+ */
+static int
+delete_port(seq_oss_devinfo_t *dp)
+{
+	if (dp->port < 0)
+		return 0;
+
+	debug_printk(("delete_port %i\n", dp->port));
+	return snd_seq_event_port_detach(dp->cseq, dp->port);
+}
+
+/*
+ * allocate a queue
+ */
+static int
+alloc_seq_queue(seq_oss_devinfo_t *dp)
+{
+	snd_seq_queue_info_t qinfo;
+	int rc;
+
+	memset(&qinfo, 0, sizeof(qinfo));
+	qinfo.owner = system_client;
+	qinfo.locked = 1;
+	strcpy(qinfo.name, "OSS Sequencer Emulation");
+	if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
+		return rc;
+	dp->queue = qinfo.queue;
+	return 0;
+}
+
+/*
+ * release queue
+ */
+static int
+delete_seq_queue(int queue)
+{
+	snd_seq_queue_info_t qinfo;
+	int rc;
+
+	if (queue < 0)
+		return 0;
+	memset(&qinfo, 0, sizeof(qinfo));
+	qinfo.queue = queue;
+	rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
+	if (rc < 0)
+		printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
+	return rc;
+}
+
+
+/*
+ * free device informations - private_free callback of port
+ */
+static void
+free_devinfo(void *private)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private;
+
+	if (dp->timer)
+		snd_seq_oss_timer_delete(dp->timer);
+		
+	if (dp->writeq)
+		snd_seq_oss_writeq_delete(dp->writeq);
+
+	if (dp->readq)
+		snd_seq_oss_readq_delete(dp->readq);
+	
+	kfree(dp);
+}
+
+
+/*
+ * close sequencer device
+ */
+void
+snd_seq_oss_release(seq_oss_devinfo_t *dp)
+{
+	int queue;
+
+	client_table[dp->index] = NULL;
+	num_clients--;
+
+	debug_printk(("resetting..\n"));
+	snd_seq_oss_reset(dp);
+
+	debug_printk(("cleaning up..\n"));
+	snd_seq_oss_synth_cleanup(dp);
+	snd_seq_oss_midi_cleanup(dp);
+
+	/* clear slot */
+	debug_printk(("releasing resource..\n"));
+	queue = dp->queue;
+	if (dp->port >= 0)
+		delete_port(dp);
+	delete_seq_queue(queue);
+
+	debug_printk(("release done\n"));
+}
+
+
+/*
+ * Wait until the queue is empty (if we don't have nonblock)
+ */
+void
+snd_seq_oss_drain_write(seq_oss_devinfo_t *dp)
+{
+	if (! dp->timer->running)
+		return;
+	if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
+	    dp->writeq) {
+		debug_printk(("syncing..\n"));
+		while (snd_seq_oss_writeq_sync(dp->writeq))
+			;
+	}
+}
+
+
+/*
+ * reset sequencer devices
+ */
+void
+snd_seq_oss_reset(seq_oss_devinfo_t *dp)
+{
+	int i;
+
+	/* reset all synth devices */
+	for (i = 0; i < dp->max_synthdev; i++)
+		snd_seq_oss_synth_reset(dp, i);
+
+	/* reset all midi devices */
+	if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
+		for (i = 0; i < dp->max_mididev; i++)
+			snd_seq_oss_midi_reset(dp, i);
+	}
+
+	/* remove queues */
+	if (dp->readq)
+		snd_seq_oss_readq_clear(dp->readq);
+	if (dp->writeq)
+		snd_seq_oss_writeq_clear(dp->writeq);
+
+	/* reset timer */
+	snd_seq_oss_timer_stop(dp->timer);
+}
+
+
+/*
+ * misc. functions for proc interface
+ */
+char *
+enabled_str(int bool)
+{
+	return bool ? "enabled" : "disabled";
+}
+
+static char *
+filemode_str(int val)
+{
+	static char *str[] = {
+		"none", "read", "write", "read/write",
+	};
+	return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
+}
+
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_system_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_devinfo_t *dp;
+
+	snd_iprintf(buf, "ALSA client number %d\n", system_client);
+	snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
+
+	snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
+	for (i = 0; i < num_clients; i++) {
+		snd_iprintf(buf, "\nApplication %d: ", i);
+		if ((dp = client_table[i]) == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
+		snd_iprintf(buf, "  sequencer mode = %s : file open mode = %s\n",
+			    (dp->seq_mode ? "music" : "synth"),
+			    filemode_str(dp->file_mode));
+		if (dp->seq_mode)
+			snd_iprintf(buf, "  timer tempo = %d, timebase = %d\n",
+				    dp->timer->oss_tempo, dp->timer->oss_timebase);
+		snd_iprintf(buf, "  max queue length %d\n", maxqlen);
+		if (is_read_mode(dp->file_mode) && dp->readq)
+			snd_seq_oss_readq_info_read(dp->readq, buf);
+	}
+}
+
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
new file mode 100644
index 0000000..e86f18d
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_ioctl.c
@@ -0,0 +1,209 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * OSS compatible i/o control
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_device.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "seq_oss_event.h"
+
+static int snd_seq_oss_synth_info_user(seq_oss_devinfo_t *dp, void __user *arg)
+{
+	struct synth_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0)
+		return -EINVAL;
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_seq_oss_midi_info_user(seq_oss_devinfo_t *dp, void __user *arg)
+{
+	struct midi_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0)
+		return -EINVAL;
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int snd_seq_oss_oob_user(seq_oss_devinfo_t *dp, void __user *arg)
+{
+	unsigned char ev[8];
+	snd_seq_event_t tmpev;
+
+	if (copy_from_user(ev, arg, 8))
+		return -EFAULT;
+	memset(&tmpev, 0, sizeof(tmpev));
+	snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
+	tmpev.time.tick = 0;
+	if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) {
+		snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
+	}
+	return 0;
+}
+
+int
+snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg)
+{
+	int dev, val;
+	void __user *arg = (void __user *)carg;
+	int __user *p = arg;
+
+	switch (cmd) {
+	case SNDCTL_TMR_TIMEBASE:
+	case SNDCTL_TMR_TEMPO:
+	case SNDCTL_TMR_START:
+	case SNDCTL_TMR_STOP:
+	case SNDCTL_TMR_CONTINUE:
+	case SNDCTL_TMR_METRONOME:
+	case SNDCTL_TMR_SOURCE:
+	case SNDCTL_TMR_SELECT:
+	case SNDCTL_SEQ_CTRLRATE:
+		return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
+
+	case SNDCTL_SEQ_PANIC:
+		debug_printk(("panic\n"));
+		snd_seq_oss_reset(dp);
+		return -EINVAL;
+
+	case SNDCTL_SEQ_SYNC:
+		debug_printk(("sync\n"));
+		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+			return 0;
+		while (snd_seq_oss_writeq_sync(dp->writeq))
+			;
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		return 0;
+
+	case SNDCTL_SEQ_RESET:
+		debug_printk(("reset\n"));
+		snd_seq_oss_reset(dp);
+		return 0;
+
+	case SNDCTL_SEQ_TESTMIDI:
+		debug_printk(("test midi\n"));
+		if (get_user(dev, p))
+			return -EFAULT;
+		return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
+
+	case SNDCTL_SEQ_GETINCOUNT:
+		debug_printk(("get in count\n"));
+		if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
+			return 0;
+		return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_GETOUTCOUNT:
+		debug_printk(("get out count\n"));
+		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+			return 0;
+		return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_GETTIME:
+		debug_printk(("get time\n"));
+		return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_RESETSAMPLES:
+		debug_printk(("reset samples\n"));
+		if (get_user(dev, p))
+			return -EFAULT;
+		return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+
+	case SNDCTL_SEQ_NRSYNTHS:
+		debug_printk(("nr synths\n"));
+		return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
+
+	case SNDCTL_SEQ_NRMIDIS:
+		debug_printk(("nr midis\n"));
+		return put_user(dp->max_mididev, p) ? -EFAULT : 0;
+
+	case SNDCTL_SYNTH_MEMAVL:
+		debug_printk(("mem avail\n"));
+		if (get_user(dev, p))
+			return -EFAULT;
+		val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+		return put_user(val, p) ? -EFAULT : 0;
+
+	case SNDCTL_FM_4OP_ENABLE:
+		debug_printk(("4op\n"));
+		if (get_user(dev, p))
+			return -EFAULT;
+		snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
+		return 0;
+
+	case SNDCTL_SYNTH_INFO:
+	case SNDCTL_SYNTH_ID:
+		debug_printk(("synth info\n"));
+		return snd_seq_oss_synth_info_user(dp, arg);
+
+	case SNDCTL_SEQ_OUTOFBAND:
+		debug_printk(("out of band\n"));
+		return snd_seq_oss_oob_user(dp, arg);
+
+	case SNDCTL_MIDI_INFO:
+		debug_printk(("midi info\n"));
+		return snd_seq_oss_midi_info_user(dp, arg);
+
+	case SNDCTL_SEQ_THRESHOLD:
+		debug_printk(("threshold\n"));
+		if (! is_write_mode(dp->file_mode))
+			return 0;
+		if (get_user(val, p))
+			return -EFAULT;
+		if (val < 1)
+			val = 1;
+		if (val >= dp->writeq->maxlen)
+			val = dp->writeq->maxlen - 1;
+		snd_seq_oss_writeq_set_output(dp->writeq, val);
+		return 0;
+
+	case SNDCTL_MIDI_PRETIME:
+		debug_printk(("pretime\n"));
+		if (dp->readq == NULL || !is_read_mode(dp->file_mode))
+			return 0;
+		if (get_user(val, p))
+			return -EFAULT;
+		if (val <= 0)
+			val = -1;
+		else
+			val = (HZ * val) / 10;
+		dp->readq->pre_event_timeout = val;
+		return put_user(val, p) ? -EFAULT : 0;
+
+	default:
+		debug_printk(("others\n"));
+		if (! is_write_mode(dp->file_mode))
+			return -EIO;
+		return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
+	}
+	return 0;
+}
+
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
new file mode 100644
index 0000000..9aece6c
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -0,0 +1,710 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * MIDI device handlers
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_midi.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <sound/seq_midi_event.h>
+#include "../seq_lock.h"
+#include <linux/init.h>
+
+
+/*
+ * constants
+ */
+#define SNDRV_SEQ_OSS_MAX_MIDI_NAME	30
+
+/*
+ * definition of midi device record
+ */
+struct seq_oss_midi_t {
+	int seq_device;		/* device number */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	unsigned int flags;	/* port capability */
+	int opened;		/* flag for opening */
+	unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
+	snd_midi_event_t *coder;	/* MIDI event coder */
+	seq_oss_devinfo_t *devinfo;	/* assigned OSSseq device */
+	snd_use_lock_t use_lock;
+};
+
+
+/*
+ * midi device table
+ */
+static int max_midi_devs;
+static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
+
+static DEFINE_SPINLOCK(register_lock);
+
+/*
+ * prototypes
+ */
+static seq_oss_midi_t *get_mdev(int dev);
+static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
+static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
+static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
+
+/*
+ * look up the existing ports
+ * this looks a very exhausting job.
+ */
+int __init
+snd_seq_oss_midi_lookup_ports(int client)
+{
+	snd_seq_client_info_t *clinfo;
+	snd_seq_port_info_t *pinfo;
+
+	clinfo = kcalloc(1, sizeof(*clinfo), GFP_KERNEL);
+	pinfo = kcalloc(1, sizeof(*pinfo), GFP_KERNEL);
+	if (! clinfo || ! pinfo) {
+		kfree(clinfo);
+		kfree(pinfo);
+		return -ENOMEM;
+	}
+	clinfo->client = -1;
+	while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
+		if (clinfo->client == client)
+			continue; /* ignore myself */
+		pinfo->addr.client = clinfo->client;
+		pinfo->addr.port = -1;
+		while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
+			snd_seq_oss_midi_check_new_port(pinfo);
+	}
+	kfree(clinfo);
+	kfree(pinfo);
+	return 0;
+}
+
+
+/*
+ */
+static seq_oss_midi_t *
+get_mdev(int dev)
+{
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	mdev = midi_devs[dev];
+	if (mdev)
+		snd_use_lock_use(&mdev->use_lock);
+	spin_unlock_irqrestore(&register_lock, flags);
+	return mdev;
+}
+
+/*
+ * look for the identical slot
+ */
+static seq_oss_midi_t *
+find_slot(int client, int port)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		mdev = midi_devs[i];
+		if (mdev && mdev->client == client && mdev->port == port) {
+			/* found! */
+			snd_use_lock_use(&mdev->use_lock);
+			spin_unlock_irqrestore(&register_lock, flags);
+			return mdev;
+		}
+	}
+	spin_unlock_irqrestore(&register_lock, flags);
+	return NULL;
+}
+
+
+#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+/*
+ * register a new port if it doesn't exist yet
+ */
+int
+snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
+	/* the port must include generic midi */
+	if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
+		return 0;
+	/* either read or write subscribable */
+	if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
+	    (pinfo->capability & PERM_READ) != PERM_READ)
+		return 0;
+
+	/*
+	 * look for the identical slot
+	 */
+	if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
+		/* already exists */
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	/*
+	 * allocate midi info record
+	 */
+	if ((mdev = kcalloc(1, sizeof(*mdev), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc midi info\n");
+		return -ENOMEM;
+	}
+
+	/* copy the port information */
+	mdev->client = pinfo->addr.client;
+	mdev->port = pinfo->addr.port;
+	mdev->flags = pinfo->capability;
+	mdev->opened = 0;
+	snd_use_lock_init(&mdev->use_lock);
+
+	/* copy and truncate the name of synth device */
+	strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
+
+	/* create MIDI coder */
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
+		snd_printk(KERN_ERR "can't malloc midi coder\n");
+		kfree(mdev);
+		return -ENOMEM;
+	}
+	/* OSS sequencer adds running status to all sequences */
+	snd_midi_event_no_status(mdev->coder, 1);
+
+	/*
+	 * look for en empty slot
+	 */
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		if (midi_devs[i] == NULL)
+			break;
+	}
+	if (i >= max_midi_devs) {
+		if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
+			spin_unlock_irqrestore(&register_lock, flags);
+			snd_midi_event_free(mdev->coder);
+			kfree(mdev);
+			return -ENOMEM;
+		}
+		max_midi_devs++;
+	}
+	mdev->seq_device = i;
+	midi_devs[mdev->seq_device] = mdev;
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+/*
+ * release the midi device if it was registered
+ */
+int
+snd_seq_oss_midi_check_exit_port(int client, int port)
+{
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+	int index;
+
+	if ((mdev = find_slot(client, port)) != NULL) {
+		spin_lock_irqsave(&register_lock, flags);
+		midi_devs[mdev->seq_device] = NULL;
+		spin_unlock_irqrestore(&register_lock, flags);
+		snd_use_lock_free(&mdev->use_lock);
+		snd_use_lock_sync(&mdev->use_lock);
+		if (mdev->coder)
+			snd_midi_event_free(mdev->coder);
+		kfree(mdev);
+	}
+	spin_lock_irqsave(&register_lock, flags);
+	for (index = max_midi_devs - 1; index >= 0; index--) {
+		if (midi_devs[index])
+			break;
+	}
+	max_midi_devs = index + 1;
+	spin_unlock_irqrestore(&register_lock, flags);
+	return 0;
+}
+
+
+/*
+ * release the midi device if it was registered
+ */
+void
+snd_seq_oss_midi_clear_all(void)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_midi_devs; i++) {
+		if ((mdev = midi_devs[i]) != NULL) {
+			if (mdev->coder)
+				snd_midi_event_free(mdev->coder);
+			kfree(mdev);
+			midi_devs[i] = NULL;
+		}
+	}
+	max_midi_devs = 0;
+	spin_unlock_irqrestore(&register_lock, flags);
+}
+
+
+/*
+ * set up midi tables
+ */
+void
+snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp)
+{
+	dp->max_mididev = max_midi_devs;
+}
+
+/*
+ * clean up midi tables
+ */
+void
+snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	for (i = 0; i < dp->max_mididev; i++)
+		snd_seq_oss_midi_close(dp, i);
+	dp->max_mididev = 0;
+}
+
+
+/*
+ * open all midi devices.  ignore errors.
+ */
+void
+snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode)
+{
+	int i;
+	for (i = 0; i < dp->max_mididev; i++)
+		snd_seq_oss_midi_open(dp, i, file_mode);
+}
+
+
+/*
+ * get the midi device information
+ */
+static seq_oss_midi_t *
+get_mididev(seq_oss_devinfo_t *dp, int dev)
+{
+	if (dev < 0 || dev >= dp->max_mididev)
+		return NULL;
+	return get_mdev(dev);
+}
+
+
+/*
+ * open the midi device if not opened yet
+ */
+int
+snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
+{
+	int perm;
+	seq_oss_midi_t *mdev;
+	snd_seq_port_subscribe_t subs;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+
+	/* already used? */
+	if (mdev->opened && mdev->devinfo != dp) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -EBUSY;
+	}
+
+	perm = 0;
+	if (is_write_mode(fmode))
+		perm |= PERM_WRITE;
+	if (is_read_mode(fmode))
+		perm |= PERM_READ;
+	perm &= mdev->flags;
+	if (perm == 0) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -ENXIO;
+	}
+
+	/* already opened? */
+	if ((mdev->opened & perm) == perm) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	perm &= ~mdev->opened;
+
+	memset(&subs, 0, sizeof(subs));
+
+	if (perm & PERM_WRITE) {
+		subs.sender = dp->addr;
+		subs.dest.client = mdev->client;
+		subs.dest.port = mdev->port;
+		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+			mdev->opened |= PERM_WRITE;
+	}
+	if (perm & PERM_READ) {
+		subs.sender.client = mdev->client;
+		subs.sender.port = mdev->port;
+		subs.dest = dp->addr;
+		subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
+		subs.queue = dp->queue;		/* queue for timestamps */
+		if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
+			mdev->opened |= PERM_READ;
+	}
+
+	if (! mdev->opened) {
+		snd_use_lock_free(&mdev->use_lock);
+		return -ENXIO;
+	}
+
+	mdev->devinfo = dp;
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+/*
+ * close the midi device if already opened
+ */
+int
+snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+	snd_seq_port_subscribe_t subs;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+	if (! mdev->opened || mdev->devinfo != dp) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
+	memset(&subs, 0, sizeof(subs));
+	if (mdev->opened & PERM_WRITE) {
+		subs.sender = dp->addr;
+		subs.dest.client = mdev->client;
+		subs.dest.port = mdev->port;
+		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+	}
+	if (mdev->opened & PERM_READ) {
+		subs.sender.client = mdev->client;
+		subs.sender.port = mdev->port;
+		subs.dest = dp->addr;
+		snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
+	}
+
+	mdev->opened = 0;
+	mdev->devinfo = NULL;
+
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+/*
+ * change seq capability flags to file mode flags
+ */
+int
+snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+	int mode;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return 0;
+
+	mode = 0;
+	if (mdev->opened & PERM_WRITE)
+		mode |= SNDRV_SEQ_OSS_FILE_WRITE;
+	if (mdev->opened & PERM_READ)
+		mode |= SNDRV_SEQ_OSS_FILE_READ;
+
+	snd_use_lock_free(&mdev->use_lock);
+	return mode;
+}
+
+/*
+ * reset the midi device and close it:
+ * so far, only close the device.
+ */
+void
+snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return;
+	if (! mdev->opened) {
+		snd_use_lock_free(&mdev->use_lock);
+		return;
+	}
+
+	if (mdev->opened & PERM_WRITE) {
+		snd_seq_event_t ev;
+		int c;
+
+		debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
+		memset(&ev, 0, sizeof(ev));
+		ev.dest.client = mdev->client;
+		ev.dest.port = mdev->port;
+		ev.queue = dp->queue;
+		ev.source.port = dp->port;
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
+			ev.type = SNDRV_SEQ_EVENT_SENSING;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
+		}
+		for (c = 0; c < 16; c++) {
+			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+			ev.data.control.channel = c;
+			ev.data.control.param = 123;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
+				ev.data.control.param = 121;
+				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+				ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
+				ev.data.control.value = 0;
+				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+			}
+		}
+	}
+	// snd_seq_oss_midi_close(dp, dev);
+	snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * get client/port of the specified MIDI device
+ */
+void
+snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return;
+	addr->client = mdev->client;
+	addr->port = mdev->port;
+	snd_use_lock_free(&mdev->use_lock);
+}
+
+
+/*
+ * input callback - this can be atomic
+ */
+int
+snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data)
+{
+	seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
+	seq_oss_midi_t *mdev;
+	int rc;
+
+	if (dp->readq == NULL)
+		return 0;
+	if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
+		return 0;
+	if (! (mdev->opened & PERM_READ)) {
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+
+	if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
+		rc = send_synth_event(dp, ev, mdev->seq_device);
+	else
+		rc = send_midi_event(dp, ev, mdev);
+
+	snd_use_lock_free(&mdev->use_lock);
+	return rc;
+}
+
+/*
+ * convert ALSA sequencer event to OSS synth event
+ */
+static int
+send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
+{
+	evrec_t ossev;
+
+	memset(&ossev, 0, sizeof(ossev));
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		ossev.v.cmd = MIDI_NOTEON; break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		ossev.v.cmd = MIDI_NOTEOFF; break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		ossev.v.cmd = MIDI_KEY_PRESSURE; break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		ossev.l.cmd = MIDI_CTL_CHANGE; break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		ossev.l.cmd = MIDI_PGM_CHANGE; break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		ossev.l.cmd = MIDI_CHN_PRESSURE; break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		ossev.l.cmd = MIDI_PITCH_BEND; break;
+	default:
+		return 0; /* not supported */
+	}
+
+	ossev.v.dev = dev;
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		ossev.v.code = EV_CHN_VOICE;
+		ossev.v.note = ev->data.note.note;
+		ossev.v.parm = ev->data.note.velocity;
+		ossev.v.chn = ev->data.note.channel;
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		ossev.l.code = EV_CHN_COMMON;
+		ossev.l.p1 = ev->data.control.param;
+		ossev.l.val = ev->data.control.value;
+		ossev.l.chn = ev->data.control.channel;
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		ossev.l.code = EV_CHN_COMMON;
+		ossev.l.val = ev->data.control.value + 8192;
+		ossev.l.chn = ev->data.control.channel;
+		break;
+	}
+	
+	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
+	snd_seq_oss_readq_put_event(dp->readq, &ossev);
+
+	return 0;
+}
+
+/*
+ * decode event and send MIDI bytes to read queue
+ */
+static int
+send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
+{
+	char msg[32];
+	int len;
+	
+	snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
+	if (!dp->timer->running)
+		len = snd_seq_oss_timer_start(dp->timer);
+	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
+					       ev->data.ext.ptr, ev->data.ext.len);
+	} else {
+		len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
+		if (len > 0)
+			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
+	}
+
+	return 0;
+}
+
+
+/*
+ * dump midi data
+ * return 0 : enqueued
+ *        non-zero : invalid - ignored
+ */
+int
+snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENODEV;
+	if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
+		snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
+		snd_use_lock_free(&mdev->use_lock);
+		return 0;
+	}
+	snd_use_lock_free(&mdev->use_lock);
+	return -EINVAL;
+}
+
+/*
+ * create OSS compatible midi_info record
+ */
+int
+snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
+{
+	seq_oss_midi_t *mdev;
+
+	if ((mdev = get_mididev(dp, dev)) == NULL)
+		return -ENXIO;
+	inf->device = dev;
+	inf->dev_type = 0; /* FIXME: ?? */
+	inf->capabilities = 0; /* FIXME: ?? */
+	strlcpy(inf->name, mdev->name, sizeof(inf->name));
+	snd_use_lock_free(&mdev->use_lock);
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static char *
+capmode_str(int val)
+{
+	val &= PERM_READ|PERM_WRITE;
+	if (val == (PERM_READ|PERM_WRITE))
+		return "read/write";
+	else if (val == PERM_READ)
+		return "read";
+	else if (val == PERM_WRITE)
+		return "write";
+	else
+		return "none";
+}
+
+void
+snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_midi_t *mdev;
+
+	snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
+	for (i = 0; i < max_midi_devs; i++) {
+		snd_iprintf(buf, "\nmidi %d: ", i);
+		mdev = get_mdev(i);
+		if (mdev == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
+			    mdev->client, mdev->port);
+		snd_iprintf(buf, "  capability %s / opened %s\n",
+			    capmode_str(mdev->flags),
+			    capmode_str(mdev->opened));
+		snd_use_lock_free(&mdev->use_lock);
+	}
+}
+
diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h
new file mode 100644
index 0000000..462484b
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_midi.h
@@ -0,0 +1,49 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * midi device information
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_MIDI_H
+#define __SEQ_OSS_MIDI_H
+
+#include "seq_oss_device.h"
+#include <sound/seq_oss_legacy.h>
+
+typedef struct seq_oss_midi_t seq_oss_midi_t;
+
+int snd_seq_oss_midi_lookup_ports(int client);
+int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo);
+int snd_seq_oss_midi_check_exit_port(int client, int port);
+void snd_seq_oss_midi_clear_all(void);
+
+void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp);
+void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp);
+
+int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode);
+void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode);
+int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev);
+void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev);
+int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private);
+int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf);
+void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr);
+
+#endif
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
new file mode 100644
index 0000000..0a6f2a6
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -0,0 +1,234 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_readq.c - MIDI input queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_readq.h"
+#include "seq_oss_event.h"
+#include <sound/seq_oss_legacy.h>
+#include "../seq_lock.h"
+#include <linux/wait.h>
+
+/*
+ * constants
+ */
+//#define SNDRV_SEQ_OSS_MAX_TIMEOUT	(unsigned long)(-1)
+#define SNDRV_SEQ_OSS_MAX_TIMEOUT	(HZ * 3600)
+
+
+/*
+ * prototypes
+ */
+
+
+/*
+ * create a read queue
+ */
+seq_oss_readq_t *
+snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen)
+{
+	seq_oss_readq_t *q;
+
+	if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc read queue\n");
+		return NULL;
+	}
+
+	if ((q->q = kcalloc(maxlen, sizeof(evrec_t), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc read queue buffer\n");
+		kfree(q);
+		return NULL;
+	}
+
+	q->maxlen = maxlen;
+	q->qlen = 0;
+	q->head = q->tail = 0;
+	init_waitqueue_head(&q->midi_sleep);
+	spin_lock_init(&q->lock);
+	q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
+	q->input_time = (unsigned long)-1;
+
+	return q;
+}
+
+/*
+ * delete the read queue
+ */
+void
+snd_seq_oss_readq_delete(seq_oss_readq_t *q)
+{
+	if (q) {
+		kfree(q->q);
+		kfree(q);
+	}
+}
+
+/*
+ * reset the read queue
+ */
+void
+snd_seq_oss_readq_clear(seq_oss_readq_t *q)
+{
+	if (q->qlen) {
+		q->qlen = 0;
+		q->head = q->tail = 0;
+	}
+	/* if someone sleeping, wake'em up */
+	if (waitqueue_active(&q->midi_sleep))
+		wake_up(&q->midi_sleep);
+	q->input_time = (unsigned long)-1;
+}
+
+/*
+ * put a midi byte
+ */
+int
+snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len)
+{
+	evrec_t rec;
+	int result;
+
+	memset(&rec, 0, sizeof(rec));
+	rec.c[0] = SEQ_MIDIPUTC;
+	rec.c[2] = dev;
+
+	while (len-- > 0) {
+		rec.c[1] = *data++;
+		result = snd_seq_oss_readq_put_event(q, &rec);
+		if (result < 0)
+			return result;
+	}
+	return 0;
+}
+
+/*
+ * copy an event to input queue:
+ * return zero if enqueued
+ */
+int
+snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->lock, flags);
+	if (q->qlen >= q->maxlen - 1) {
+		spin_unlock_irqrestore(&q->lock, flags);
+		return -ENOMEM;
+	}
+
+	memcpy(&q->q[q->tail], ev, sizeof(*ev));
+	q->tail = (q->tail + 1) % q->maxlen;
+	q->qlen++;
+
+	/* wake up sleeper */
+	if (waitqueue_active(&q->midi_sleep))
+		wake_up(&q->midi_sleep);
+
+	spin_unlock_irqrestore(&q->lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * pop queue
+ * caller must hold lock
+ */
+int
+snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec)
+{
+	if (q->qlen == 0)
+		return -EAGAIN;
+	memcpy(rec, &q->q[q->head], sizeof(*rec));
+	return 0;
+}
+
+/*
+ * sleep until ready
+ */
+void
+snd_seq_oss_readq_wait(seq_oss_readq_t *q)
+{
+	wait_event_interruptible_timeout(q->midi_sleep,
+					 (q->qlen > 0 || q->head == q->tail),
+					 q->pre_event_timeout);
+}
+
+/*
+ * drain one record
+ * caller must hold lock
+ */
+void
+snd_seq_oss_readq_free(seq_oss_readq_t *q)
+{
+	if (q->qlen > 0) {
+		q->head = (q->head + 1) % q->maxlen;
+		q->qlen--;
+	}
+}
+
+/*
+ * polling/select:
+ * return non-zero if readq is not empty.
+ */
+unsigned int
+snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &q->midi_sleep, wait);
+	return q->qlen;
+}
+
+/*
+ * put a timestamp
+ */
+int
+snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode)
+{
+	if (curt != q->input_time) {
+		evrec_t rec;
+		memset(&rec, 0, sizeof(rec));
+		switch (seq_mode) {
+		case SNDRV_SEQ_OSS_MODE_SYNTH:
+			rec.echo = (curt << 8) | SEQ_WAIT;
+			snd_seq_oss_readq_put_event(q, &rec);
+			break;
+		case SNDRV_SEQ_OSS_MODE_MUSIC:
+			rec.t.code = EV_TIMING;
+			rec.t.cmd = TMR_WAIT_ABS;
+			rec.t.time = curt;
+			snd_seq_oss_readq_put_event(q, &rec);
+			break;
+		}
+		q->input_time = curt;
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf)
+{
+	snd_iprintf(buf, "  read queue [%s] length = %d : tick = %ld\n",
+		    (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
+		    q->qlen, q->input_time);
+}
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
new file mode 100644
index 0000000..303b929
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_readq.h
@@ -0,0 +1,56 @@
+/*
+ * OSS compatible sequencer driver
+ * read fifo queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_READQ_H
+#define __SEQ_OSS_READQ_H
+
+#include "seq_oss_device.h"
+
+
+/*
+ * definition of read queue
+ */
+struct seq_oss_readq_t {
+	evrec_t *q;
+	int qlen;
+	int maxlen;
+	int head, tail;
+	unsigned long pre_event_timeout;
+	unsigned long input_time;
+	wait_queue_head_t midi_sleep;
+	spinlock_t lock;
+};
+
+seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen);
+void snd_seq_oss_readq_delete(seq_oss_readq_t *q);
+void snd_seq_oss_readq_clear(seq_oss_readq_t *readq);
+unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait);
+int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len);
+int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev);
+int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode);
+int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec);
+void snd_seq_oss_readq_wait(seq_oss_readq_t *q);
+void snd_seq_oss_readq_free(seq_oss_readq_t *q);
+
+#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags)
+#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags)
+
+#endif
diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
new file mode 100644
index 0000000..1d8fbd2
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_rw.c
@@ -0,0 +1,216 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * read/write/select interface to device file
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_device.h"
+#include "seq_oss_readq.h"
+#include "seq_oss_writeq.h"
+#include "seq_oss_synth.h"
+#include <sound/seq_oss_legacy.h>
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include "../seq_clientmgr.h"
+
+
+/*
+ * protoypes
+ */
+static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt);
+
+
+/*
+ * read interface
+ */
+
+int
+snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count)
+{
+	seq_oss_readq_t *readq = dp->readq;
+	int result = 0, err = 0;
+	int ev_len;
+	evrec_t rec;
+	unsigned long flags;
+
+	if (readq == NULL || ! is_read_mode(dp->file_mode))
+		return -ENXIO;
+
+	while (count >= SHORT_EVENT_SIZE) {
+		snd_seq_oss_readq_lock(readq, flags);
+		err = snd_seq_oss_readq_pick(readq, &rec);
+		if (err == -EAGAIN &&
+		    !is_nonblock_mode(dp->file_mode) && result == 0) {
+			snd_seq_oss_readq_unlock(readq, flags);
+			snd_seq_oss_readq_wait(readq);
+			snd_seq_oss_readq_lock(readq, flags);
+			if (signal_pending(current))
+				err = -ERESTARTSYS;
+			else
+				err = snd_seq_oss_readq_pick(readq, &rec);
+		}
+		if (err < 0) {
+			snd_seq_oss_readq_unlock(readq, flags);
+			break;
+		}
+		ev_len = ev_length(&rec);
+		if (ev_len < count) {
+			snd_seq_oss_readq_unlock(readq, flags);
+			break;
+		}
+		snd_seq_oss_readq_free(readq);
+		snd_seq_oss_readq_unlock(readq, flags);
+		if (copy_to_user(buf, &rec, ev_len)) {
+			err = -EFAULT;
+			break;
+		}
+		result += ev_len;
+		buf += ev_len;
+		count -= ev_len;
+	}
+	return result > 0 ? result : err;
+}
+
+
+/*
+ * write interface
+ */
+
+int
+snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt)
+{
+	int result = 0, err = 0;
+	int ev_size, fmt;
+	evrec_t rec;
+
+	if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
+		return -ENXIO;
+
+	while (count >= SHORT_EVENT_SIZE) {
+		if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
+			err = -EFAULT;
+			break;
+		}
+		if (rec.s.code == SEQ_FULLSIZE) {
+			/* load patch */
+			if (result > 0) {
+				err = -EINVAL;
+				break;
+			}
+			fmt = (*(unsigned short *)rec.c) & 0xffff;
+			/* FIXME the return value isn't correct */
+			return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
+							    fmt, buf, 0, count);
+		}
+		if (ev_is_long(&rec)) {
+			/* extended code */
+			if (rec.s.code == SEQ_EXTENDED &&
+			    dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
+				err = -EINVAL;
+				break;
+			}
+			ev_size = LONG_EVENT_SIZE;
+			if (count < ev_size)
+				break;
+			/* copy the reset 4 bytes */
+			if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
+					   buf + SHORT_EVENT_SIZE,
+					   LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
+				err = -EFAULT;
+				break;
+			}
+		} else {
+			/* old-type code */
+			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
+				err = -EINVAL;
+				break;
+			}
+			ev_size = SHORT_EVENT_SIZE;
+		}
+
+		/* insert queue */
+		if ((err = insert_queue(dp, &rec, opt)) < 0)
+			break;
+
+		result += ev_size;
+		buf += ev_size;
+		count -= ev_size;
+	}
+	return result > 0 ? result : err;
+}
+
+
+/*
+ * insert event record to write queue
+ * return: 0 = OK, non-zero = NG
+ */
+static int
+insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt)
+{
+	int rc = 0;
+	snd_seq_event_t event;
+
+	/* if this is a timing event, process the current time */
+	if (snd_seq_oss_process_timer_event(dp->timer, rec))
+		return 0; /* no need to insert queue */
+
+	/* parse this event */
+	memset(&event, 0, sizeof(event));
+	/* set dummy -- to be sure */
+	event.type = SNDRV_SEQ_EVENT_NOTEOFF;
+	snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
+
+	if (snd_seq_oss_process_event(dp, rec, &event))
+		return 0; /* invalid event - no need to insert queue */
+
+	event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
+	if (dp->timer->realtime || !dp->timer->running) {
+		snd_seq_oss_dispatch(dp, &event, 0, 0);
+	} else {
+		if (is_nonblock_mode(dp->file_mode))
+			rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
+		else
+			rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
+	}
+	return rc;
+}
+		
+
+/*
+ * select / poll
+ */
+  
+unsigned int
+snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	/* input */
+	if (dp->readq && is_read_mode(dp->file_mode)) {
+		if (snd_seq_oss_readq_poll(dp->readq, file, wait))
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	/* output */
+	if (dp->writeq && is_write_mode(dp->file_mode)) {
+		if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	return mask;
+}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
new file mode 100644
index 0000000..638cc14
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -0,0 +1,659 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * synth device handlers
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_synth.h"
+#include "seq_oss_midi.h"
+#include "../seq_lock.h"
+#include <linux/init.h>
+
+/*
+ * constants
+ */
+#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME	30
+#define MAX_SYSEX_BUFLEN		128
+
+
+/*
+ * definition of synth info records
+ */
+
+/* sysex buffer */
+struct seq_oss_synth_sysex_t {
+	int len;
+	int skip;
+	unsigned char buf[MAX_SYSEX_BUFLEN];
+};
+
+/* synth info */
+struct seq_oss_synth_t {
+	int seq_device;
+
+	/* for synth_info */
+	int synth_type;
+	int synth_subtype;
+	int nr_voices;
+
+	char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];
+	snd_seq_oss_callback_t oper;
+
+	int opened;
+
+	void *private_data;
+	snd_use_lock_t use_lock;
+};
+
+
+/*
+ * device table
+ */
+static int max_synth_devs;
+static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
+static seq_oss_synth_t midi_synth_dev = {
+	-1, /* seq_device */
+	SYNTH_TYPE_MIDI, /* synth_type */
+	0, /* synth_subtype */
+	16, /* nr_voices */
+	"MIDI", /* name */
+};
+
+static DEFINE_SPINLOCK(register_lock);
+
+/*
+ * prototypes
+ */
+static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev);
+static void reset_channels(seq_oss_synthinfo_t *info);
+
+/*
+ * global initialization
+ */
+void __init
+snd_seq_oss_synth_init(void)
+{
+	snd_use_lock_init(&midi_synth_dev.use_lock);
+}
+
+/*
+ * registration of the synth device
+ */
+int
+snd_seq_oss_synth_register(snd_seq_device_t *dev)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	unsigned long flags;
+
+	if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL) {
+		snd_printk(KERN_ERR "can't malloc synth info\n");
+		return -ENOMEM;
+	}
+	rec->seq_device = -1;
+	rec->synth_type = reg->type;
+	rec->synth_subtype = reg->subtype;
+	rec->nr_voices = reg->nvoices;
+	rec->oper = reg->oper;
+	rec->private_data = reg->private_data;
+	rec->opened = 0;
+	snd_use_lock_init(&rec->use_lock);
+
+	/* copy and truncate the name of synth device */
+	strlcpy(rec->name, dev->name, sizeof(rec->name));
+
+	/* registration */
+	spin_lock_irqsave(&register_lock, flags);
+	for (i = 0; i < max_synth_devs; i++) {
+		if (synth_devs[i] == NULL)
+			break;
+	}
+	if (i >= max_synth_devs) {
+		if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
+			spin_unlock_irqrestore(&register_lock, flags);
+			snd_printk(KERN_ERR "no more synth slot\n");
+			kfree(rec);
+			return -ENOMEM;
+		}
+		max_synth_devs++;
+	}
+	rec->seq_device = i;
+	synth_devs[i] = rec;
+	debug_printk(("synth %s registered %d\n", rec->name, i));
+	spin_unlock_irqrestore(&register_lock, flags);
+	dev->driver_data = rec;
+#ifdef SNDRV_OSS_INFO_DEV_SYNTH
+	if (i < SNDRV_CARDS)
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
+#endif
+	return 0;
+}
+
+
+int
+snd_seq_oss_synth_unregister(snd_seq_device_t *dev)
+{
+	int index;
+	seq_oss_synth_t *rec = dev->driver_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	for (index = 0; index < max_synth_devs; index++) {
+		if (synth_devs[index] == rec)
+			break;
+	}
+	if (index >= max_synth_devs) {
+		spin_unlock_irqrestore(&register_lock, flags);
+		snd_printk(KERN_ERR "can't unregister synth\n");
+		return -EINVAL;
+	}
+	synth_devs[index] = NULL;
+	if (index == max_synth_devs - 1) {
+		for (index--; index >= 0; index--) {
+			if (synth_devs[index])
+				break;
+		}
+		max_synth_devs = index + 1;
+	}
+	spin_unlock_irqrestore(&register_lock, flags);
+#ifdef SNDRV_OSS_INFO_DEV_SYNTH
+	if (rec->seq_device < SNDRV_CARDS)
+		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
+#endif
+
+	snd_use_lock_sync(&rec->use_lock);
+	kfree(rec);
+
+	return 0;
+}
+
+
+/*
+ */
+static seq_oss_synth_t *
+get_sdev(int dev)
+{
+	seq_oss_synth_t *rec;
+	unsigned long flags;
+
+	spin_lock_irqsave(&register_lock, flags);
+	rec = synth_devs[dev];
+	if (rec)
+		snd_use_lock_use(&rec->use_lock);
+	spin_unlock_irqrestore(&register_lock, flags);
+	return rec;
+}
+
+
+/*
+ * set up synth tables
+ */
+
+void
+snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	dp->max_synthdev = max_synth_devs;
+	dp->synth_opened = 0;
+	memset(dp->synths, 0, sizeof(dp->synths));
+	for (i = 0; i < dp->max_synthdev; i++) {
+		rec = get_sdev(i);
+		if (rec == NULL)
+			continue;
+		if (rec->oper.open == NULL || rec->oper.close == NULL) {
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		info = &dp->synths[i];
+		info->arg.app_index = dp->port;
+		info->arg.file_mode = dp->file_mode;
+		info->arg.seq_mode = dp->seq_mode;
+		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
+			info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
+		else
+			info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
+		info->opened = 0;
+		if (!try_module_get(rec->oper.owner)) {
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		if (rec->oper.open(&info->arg, rec->private_data) < 0) {
+			module_put(rec->oper.owner);
+			snd_use_lock_free(&rec->use_lock);
+			continue;
+		}
+		info->nr_voices = rec->nr_voices;
+		if (info->nr_voices > 0) {
+			info->ch = kcalloc(info->nr_voices, sizeof(seq_oss_chinfo_t), GFP_KERNEL);
+			if (!info->ch)
+				BUG();
+			reset_channels(info);
+		}
+		debug_printk(("synth %d assigned\n", i));
+		info->opened++;
+		rec->opened++;
+		dp->synth_opened++;
+		snd_use_lock_free(&rec->use_lock);
+	}
+}
+
+
+/*
+ * set up synth tables for MIDI emulation - /dev/music mode only
+ */
+
+void
+snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp)
+{
+	int i;
+
+	if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
+		return;
+
+	for (i = 0; i < dp->max_mididev; i++) {
+		seq_oss_synthinfo_t *info;
+		info = &dp->synths[dp->max_synthdev];
+		if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)
+			continue;
+		info->arg.app_index = dp->port;
+		info->arg.file_mode = dp->file_mode;
+		info->arg.seq_mode = dp->seq_mode;
+		info->arg.private_data = info;
+		info->is_midi = 1;
+		info->midi_mapped = i;
+		info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
+		snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);
+		info->opened = 1;
+		midi_synth_dev.opened++;
+		dp->max_synthdev++;
+		if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
+			break;
+	}
+}
+
+
+/*
+ * clean up synth tables
+ */
+
+void
+snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
+{
+	int i;
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
+	for (i = 0; i < dp->max_synthdev; i++) {
+		info = &dp->synths[i];
+		if (! info->opened)
+			continue;
+		if (info->is_midi) {
+			if (midi_synth_dev.opened > 0) {
+				snd_seq_oss_midi_close(dp, info->midi_mapped);
+				midi_synth_dev.opened--;
+			}
+		} else {
+			rec = get_sdev(i);
+			if (rec == NULL)
+				continue;
+			if (rec->opened > 0) {
+				debug_printk(("synth %d closed\n", i));
+				rec->oper.close(&info->arg);
+				module_put(rec->oper.owner);
+				rec->opened = 0;
+			}
+			snd_use_lock_free(&rec->use_lock);
+		}
+		if (info->sysex) {
+			kfree(info->sysex);
+			info->sysex = NULL;
+		}
+		if (info->ch) {
+			kfree(info->ch);
+			info->ch = NULL;
+		}
+	}
+	dp->synth_opened = 0;
+	dp->max_synthdev = 0;
+}
+
+/*
+ * check if the specified device is MIDI mapped device
+ */
+static int
+is_midi_dev(seq_oss_devinfo_t *dp, int dev)
+{
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return 0;
+	if (dp->synths[dev].is_midi)
+		return 1;
+	return 0;
+}
+
+/*
+ * return synth device information pointer
+ */
+static seq_oss_synth_t *
+get_synthdev(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return NULL;
+	if (! dp->synths[dev].opened)
+		return NULL;
+	if (dp->synths[dev].is_midi)
+		return &midi_synth_dev;
+	if ((rec = get_sdev(dev)) == NULL)
+		return NULL;
+	if (! rec->opened) {
+		snd_use_lock_free(&rec->use_lock);
+		return NULL;
+	}
+	return rec;
+}
+
+
+/*
+ * reset note and velocity on each channel.
+ */
+static void
+reset_channels(seq_oss_synthinfo_t *info)
+{
+	int i;
+	if (info->ch == NULL || ! info->nr_voices)
+		return;
+	for (i = 0; i < info->nr_voices; i++) {
+		info->ch[i].note = -1;
+		info->ch[i].vel = 0;
+	}
+}
+
+
+/*
+ * reset synth device:
+ * call reset callback.  if no callback is defined, send a heartbeat
+ * event to the corresponding port.
+ */
+void
+snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	seq_oss_synthinfo_t *info;
+
+	snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
+	info = &dp->synths[dev];
+	if (! info->opened)
+		return;
+	if (info->sysex)
+		info->sysex->len = 0; /* reset sysex */
+	reset_channels(info);
+	if (info->is_midi) {
+		if (midi_synth_dev.opened <= 0)
+			return;
+		snd_seq_oss_midi_reset(dp, info->midi_mapped);
+		/* reopen the device */
+		snd_seq_oss_midi_close(dp, dev);
+		if (snd_seq_oss_midi_open(dp, info->midi_mapped,
+					  dp->file_mode) < 0) {
+			midi_synth_dev.opened--;
+			info->opened = 0;
+			if (info->sysex) {
+				kfree(info->sysex);
+				info->sysex = NULL;
+			}
+			if (info->ch) {
+				kfree(info->ch);
+				info->ch = NULL;
+			}
+		}
+		return;
+	}
+
+	rec = get_sdev(dev);
+	if (rec == NULL)
+		return;
+	if (rec->oper.reset) {
+		rec->oper.reset(&info->arg);
+	} else {
+		snd_seq_event_t ev;
+		memset(&ev, 0, sizeof(ev));
+		snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,
+				      info->arg.addr.port);
+		ev.type = SNDRV_SEQ_EVENT_RESET;
+		snd_seq_oss_dispatch(dp, &ev, 0, 0);
+	}
+	snd_use_lock_free(&rec->use_lock);
+}
+
+
+/*
+ * load a patch record:
+ * call load_patch callback function
+ */
+int
+snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt,
+			    const char __user *buf, int p, int c)
+{
+	seq_oss_synth_t *rec;
+	int rc;
+
+	if (dev < 0 || dev >= dp->max_synthdev)
+		return -ENXIO;
+
+	if (is_midi_dev(dp, dev))
+		return 0;
+	if ((rec = get_synthdev(dp, dev)) == NULL)
+		return -ENXIO;
+
+	if (rec->oper.load_patch == NULL)
+		rc = -ENXIO;
+	else
+		rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
+	snd_use_lock_free(&rec->use_lock);
+	return rc;
+}
+
+/*
+ * check if the device is valid synth device
+ */
+int
+snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev)
+{
+	seq_oss_synth_t *rec;
+	rec = get_synthdev(dp, dev);
+	if (rec) {
+		snd_use_lock_free(&rec->use_lock);
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * receive OSS 6 byte sysex packet:
+ * the full sysex message will be sent if it reaches to the end of data
+ * (0xff).
+ */
+int
+snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev)
+{
+	int i, send;
+	unsigned char *dest;
+	seq_oss_synth_sysex_t *sysex;
+
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -ENXIO;
+
+	sysex = dp->synths[dev].sysex;
+	if (sysex == NULL) {
+		sysex = kcalloc(1, sizeof(*sysex), GFP_KERNEL);
+		if (sysex == NULL)
+			return -ENOMEM;
+		dp->synths[dev].sysex = sysex;
+	}
+
+	send = 0;
+	dest = sysex->buf + sysex->len;
+	/* copy 6 byte packet to the buffer */
+	for (i = 0; i < 6; i++) {
+		if (buf[i] == 0xff) {
+			send = 1;
+			break;
+		}
+		dest[i] = buf[i];
+		sysex->len++;
+		if (sysex->len >= MAX_SYSEX_BUFLEN) {
+			sysex->len = 0;
+			sysex->skip = 1;
+			break;
+		}
+	}
+
+	if (sysex->len && send) {
+		if (sysex->skip) {
+			sysex->skip = 0;
+			sysex->len = 0;
+			return -EINVAL; /* skip */
+		}
+		/* copy the data to event record and send it */
+		ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+		if (snd_seq_oss_synth_addr(dp, dev, ev))
+			return -EINVAL;
+		ev->data.ext.len = sysex->len;
+		ev->data.ext.ptr = sysex->buf;
+		sysex->len = 0;
+		return 0;
+	}
+
+	return -EINVAL; /* skip */
+}
+
+/*
+ * fill the event source/destination addresses
+ */
+int
+snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev))
+		return -EINVAL;
+	snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
+			      dp->synths[dev].arg.addr.port);
+	return 0;
+}
+
+
+/*
+ * OSS compatible ioctl
+ */
+int
+snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr)
+{
+	seq_oss_synth_t *rec;
+	int rc;
+
+	if (is_midi_dev(dp, dev))
+		return -ENXIO;
+	if ((rec = get_synthdev(dp, dev)) == NULL)
+		return -ENXIO;
+	if (rec->oper.ioctl == NULL)
+		rc = -ENXIO;
+	else
+		rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
+	snd_use_lock_free(&rec->use_lock);
+	return rc;
+}
+
+
+/*
+ * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME
+ */
+int
+snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev)
+{
+	if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
+		return -ENXIO;
+	ev->type = SNDRV_SEQ_EVENT_OSS;
+	memcpy(ev->data.raw8.d, data, 8);
+	return snd_seq_oss_synth_addr(dp, dev, ev);
+}
+
+
+/*
+ * create OSS compatible synth_info record
+ */
+int
+snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf)
+{
+	seq_oss_synth_t *rec;
+
+	if (dp->synths[dev].is_midi) {
+		struct midi_info minf;
+		snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
+		inf->synth_type = SYNTH_TYPE_MIDI;
+		inf->synth_subtype = 0;
+		inf->nr_voices = 16;
+		inf->device = dev;
+		strlcpy(inf->name, minf.name, sizeof(inf->name));
+	} else {
+		if ((rec = get_synthdev(dp, dev)) == NULL)
+			return -ENXIO;
+		inf->synth_type = rec->synth_type;
+		inf->synth_subtype = rec->synth_subtype;
+		inf->nr_voices = rec->nr_voices;
+		inf->device = dev;
+		strlcpy(inf->name, rec->name, sizeof(inf->name));
+		snd_use_lock_free(&rec->use_lock);
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+void
+snd_seq_oss_synth_info_read(snd_info_buffer_t *buf)
+{
+	int i;
+	seq_oss_synth_t *rec;
+
+	snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
+	for (i = 0; i < max_synth_devs; i++) {
+		snd_iprintf(buf, "\nsynth %d: ", i);
+		rec = get_sdev(i);
+		if (rec == NULL) {
+			snd_iprintf(buf, "*empty*\n");
+			continue;
+		}
+		snd_iprintf(buf, "[%s]\n", rec->name);
+		snd_iprintf(buf, "  type 0x%x : subtype 0x%x : voices %d\n",
+			    rec->synth_type, rec->synth_subtype,
+			    rec->nr_voices);
+		snd_iprintf(buf, "  capabilities : ioctl %s / load_patch %s\n",
+			    enabled_str((long)rec->oper.ioctl),
+			    enabled_str((long)rec->oper.load_patch));
+		snd_use_lock_free(&rec->use_lock);
+	}
+}
+
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
new file mode 100644
index 0000000..07bc0e2
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -0,0 +1,49 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * synth device information
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_SYNTH_H
+#define __SEQ_OSS_SYNTH_H
+
+#include "seq_oss_device.h"
+#include <sound/seq_oss_legacy.h>
+#include <sound/seq_device.h>
+
+typedef struct seq_oss_synth_t seq_oss_synth_t;
+
+void snd_seq_oss_synth_init(void);
+int snd_seq_oss_synth_register(snd_seq_device_t *dev);
+int snd_seq_oss_synth_unregister(snd_seq_device_t *dev);
+void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp);
+void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp);
+void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp);
+
+void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char __user *buf, int p, int c);
+int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev);
+int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev);
+int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev);
+int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr);
+int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev);
+
+int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf);
+
+#endif
diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
new file mode 100644
index 0000000..42ca949
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_timer.c
@@ -0,0 +1,283 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Timer control routines
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_timer.h"
+#include "seq_oss_event.h"
+#include <sound/seq_oss_legacy.h>
+
+/*
+ */
+#define MIN_OSS_TEMPO		8
+#define MAX_OSS_TEMPO		360
+#define MIN_OSS_TIMEBASE	1
+#define MAX_OSS_TIMEBASE	1000
+
+/*
+ */
+static void calc_alsa_tempo(seq_oss_timer_t *timer);
+static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value);
+
+
+/*
+ * create and register a new timer.
+ * if queue is not started yet, start it.
+ */
+seq_oss_timer_t *
+snd_seq_oss_timer_new(seq_oss_devinfo_t *dp)
+{
+	seq_oss_timer_t *rec;
+
+	rec = kcalloc(1, sizeof(*rec), GFP_KERNEL);
+	if (rec == NULL)
+		return NULL;
+
+	rec->dp = dp;
+	rec->cur_tick = 0;
+	rec->realtime = 0;
+	rec->running = 0;
+	rec->oss_tempo = 60;
+	rec->oss_timebase = 100;
+	calc_alsa_tempo(rec);
+
+	return rec;
+}
+
+
+/*
+ * delete timer.
+ * if no more timer exists, stop the queue.
+ */
+void
+snd_seq_oss_timer_delete(seq_oss_timer_t *rec)
+{
+	if (rec) {
+		snd_seq_oss_timer_stop(rec);
+		kfree(rec);
+	}
+}
+
+
+/*
+ * process one timing event
+ * return 1 : event proceseed -- skip this event
+ *        0 : not a timer event -- enqueue this event
+ */
+int
+snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev)
+{
+	abstime_t parm = ev->t.time;
+
+	if (ev->t.code == EV_TIMING) {
+		switch (ev->t.cmd) {
+		case TMR_WAIT_REL:
+			parm += rec->cur_tick;
+			rec->realtime = 0;
+			/* continue to next */
+		case TMR_WAIT_ABS:
+			if (parm == 0) {
+				rec->realtime = 1;
+			} else if (parm >= rec->cur_tick) {
+				rec->realtime = 0;
+				rec->cur_tick = parm;
+			}
+			return 1;	/* skip this event */
+			
+		case TMR_START:
+			snd_seq_oss_timer_start(rec);
+			return 1;
+
+		}
+	} else if (ev->s.code == SEQ_WAIT) {
+		/* time = from 1 to 3 bytes */
+		parm = (ev->echo >> 8) & 0xffffff;
+		if (parm > rec->cur_tick) {
+			/* set next event time */
+			rec->cur_tick = parm;
+			rec->realtime = 0;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * convert tempo units
+ */
+static void
+calc_alsa_tempo(seq_oss_timer_t *timer)
+{
+	timer->tempo = (60 * 1000000) / timer->oss_tempo;
+	timer->ppq = timer->oss_timebase;
+}
+
+
+/*
+ * dispatch a timer event
+ */
+static int
+send_timer_event(seq_oss_devinfo_t *dp, int type, int value)
+{
+	snd_seq_event_t ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = type;
+	ev.source.client = dp->cseq;
+	ev.source.port = 0;
+	ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	ev.queue = dp->queue;
+	ev.data.queue.queue = dp->queue;
+	ev.data.queue.param.value = value;
+	return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
+}
+
+/*
+ * set queue tempo and start queue
+ */
+int
+snd_seq_oss_timer_start(seq_oss_timer_t *timer)
+{
+	seq_oss_devinfo_t *dp = timer->dp;
+	snd_seq_queue_tempo_t tmprec;
+
+	if (timer->running)
+		snd_seq_oss_timer_stop(timer);
+
+	memset(&tmprec, 0, sizeof(tmprec));
+	tmprec.queue = dp->queue;
+	tmprec.ppq = timer->ppq;
+	tmprec.tempo = timer->tempo;
+	snd_seq_set_queue_tempo(dp->cseq, &tmprec);
+
+	send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
+	timer->running = 1;
+	timer->cur_tick = 0;
+	return 0;
+}
+
+
+/*
+ * stop queue
+ */
+int
+snd_seq_oss_timer_stop(seq_oss_timer_t *timer)
+{
+	if (! timer->running)
+		return 0;
+	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
+	timer->running = 0;
+	return 0;
+}
+
+
+/*
+ * continue queue
+ */
+int
+snd_seq_oss_timer_continue(seq_oss_timer_t *timer)
+{
+	if (timer->running)
+		return 0;
+	send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
+	timer->running = 1;
+	return 0;
+}
+
+
+/*
+ * change queue tempo
+ */
+int
+snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value)
+{
+	if (value < MIN_OSS_TEMPO)
+		value = MIN_OSS_TEMPO;
+	else if (value > MAX_OSS_TEMPO)
+		value = MAX_OSS_TEMPO;
+	timer->oss_tempo = value;
+	calc_alsa_tempo(timer);
+	if (timer->running)
+		send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
+	return 0;
+}
+
+
+/*
+ * ioctls
+ */
+int
+snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg)
+{
+	int value;
+
+	if (cmd == SNDCTL_SEQ_CTRLRATE) {
+		debug_printk(("ctrl rate\n"));
+		/* if *arg == 0, just return the current rate */
+		if (get_user(value, arg))
+			return -EFAULT;
+		if (value)
+			return -EINVAL;
+		value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
+		return put_user(value, arg) ? -EFAULT : 0;
+	}
+
+	if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
+		return 0;
+
+	switch (cmd) {
+	case SNDCTL_TMR_START:
+		debug_printk(("timer start\n"));
+		return snd_seq_oss_timer_start(timer);
+	case SNDCTL_TMR_STOP:
+		debug_printk(("timer stop\n"));
+		return snd_seq_oss_timer_stop(timer);
+	case SNDCTL_TMR_CONTINUE:
+		debug_printk(("timer continue\n"));
+		return snd_seq_oss_timer_continue(timer);
+	case SNDCTL_TMR_TEMPO:
+		debug_printk(("timer tempo\n"));
+		if (get_user(value, arg))
+			return -EFAULT;
+		return snd_seq_oss_timer_tempo(timer, value);
+	case SNDCTL_TMR_TIMEBASE:
+		debug_printk(("timer timebase\n"));
+		if (get_user(value, arg))
+			return -EFAULT;
+		if (value < MIN_OSS_TIMEBASE)
+			value = MIN_OSS_TIMEBASE;
+		else if (value > MAX_OSS_TIMEBASE)
+			value = MAX_OSS_TIMEBASE;
+		timer->oss_timebase = value;
+		calc_alsa_tempo(timer);
+		return 0;
+
+	case SNDCTL_TMR_METRONOME:
+	case SNDCTL_TMR_SELECT:
+	case SNDCTL_TMR_SOURCE:
+		debug_printk(("timer XXX\n"));
+		/* not supported */
+		return 0;
+	}
+	return 0;
+}
diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h
new file mode 100644
index 0000000..6e4dbd8
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_timer.h
@@ -0,0 +1,70 @@
+/*
+ * OSS compatible sequencer driver
+ * timer handling routines
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_TIMER_H
+#define __SEQ_OSS_TIMER_H
+
+#include "seq_oss_device.h"
+
+/*
+ * timer information definition
+ */
+struct seq_oss_timer_t {
+	seq_oss_devinfo_t *dp;
+	reltime_t cur_tick;
+	int realtime;
+	int running;
+	int tempo, ppq;	/* ALSA queue */
+	int oss_tempo, oss_timebase;
+};	
+
+
+seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp);
+void snd_seq_oss_timer_delete(seq_oss_timer_t *dp);
+
+int snd_seq_oss_timer_start(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_stop(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_continue(seq_oss_timer_t *timer);
+int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value);
+#define snd_seq_oss_timer_reset  snd_seq_oss_timer_start
+
+int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg);
+
+/*
+ * get current processed time
+ */
+static inline abstime_t
+snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer)
+{
+	return timer->cur_tick;
+}
+
+
+/*
+ * is realtime event?
+ */
+static inline int
+snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer)
+{
+	return timer->realtime;
+}
+
+#endif
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
new file mode 100644
index 0000000..87f85f7
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -0,0 +1,170 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * seq_oss_writeq.c - write queue and sync
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "seq_oss_writeq.h"
+#include "seq_oss_event.h"
+#include "seq_oss_timer.h"
+#include <sound/seq_oss_legacy.h>
+#include "../seq_lock.h"
+#include "../seq_clientmgr.h"
+#include <linux/wait.h>
+
+
+/*
+ * create a write queue record
+ */
+seq_oss_writeq_t *
+snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen)
+{
+	seq_oss_writeq_t *q;
+	snd_seq_client_pool_t pool;
+
+	if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL)
+		return NULL;
+	q->dp = dp;
+	q->maxlen = maxlen;
+	spin_lock_init(&q->sync_lock);
+	q->sync_event_put = 0;
+	q->sync_time = 0;
+	init_waitqueue_head(&q->sync_sleep);
+
+	memset(&pool, 0, sizeof(pool));
+	pool.client = dp->cseq;
+	pool.output_pool = maxlen;
+	pool.output_room = maxlen / 2;
+
+	snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
+
+	return q;
+}
+
+/*
+ * delete the write queue
+ */
+void
+snd_seq_oss_writeq_delete(seq_oss_writeq_t *q)
+{
+	snd_seq_oss_writeq_clear(q);	/* to be sure */
+	kfree(q);
+}
+
+
+/*
+ * reset the write queue
+ */
+void
+snd_seq_oss_writeq_clear(seq_oss_writeq_t *q)
+{
+	snd_seq_remove_events_t reset;
+
+	memset(&reset, 0, sizeof(reset));
+	reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
+
+	/* wake up sleepers if any */
+	snd_seq_oss_writeq_wakeup(q, 0);
+}
+
+/*
+ * wait until the write buffer has enough room
+ */
+int
+snd_seq_oss_writeq_sync(seq_oss_writeq_t *q)
+{
+	seq_oss_devinfo_t *dp = q->dp;
+	abstime_t time;
+
+	time = snd_seq_oss_timer_cur_tick(dp->timer);
+	if (q->sync_time >= time)
+		return 0; /* already finished */
+
+	if (! q->sync_event_put) {
+		snd_seq_event_t ev;
+		evrec_t *rec;
+
+		/* put echoback event */
+		memset(&ev, 0, sizeof(ev));
+		ev.flags = 0;
+		ev.type = SNDRV_SEQ_EVENT_ECHO;
+		ev.time.tick = time;
+		/* echo back to itself */
+		snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
+		rec = (evrec_t*)&ev.data;
+		rec->t.code = SEQ_SYNCTIMER;
+		rec->t.time = time;
+		q->sync_event_put = 1;
+		snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
+	}
+
+	wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
+	if (signal_pending(current))
+		/* interrupted - return 0 to finish sync */
+		q->sync_event_put = 0;
+	if (! q->sync_event_put || q->sync_time >= time)
+		return 0;
+	return 1;
+}
+
+/*
+ * wake up sync - echo event was catched
+ */
+void
+snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->sync_lock, flags);
+	q->sync_time = time;
+	q->sync_event_put = 0;
+	if (waitqueue_active(&q->sync_sleep)) {
+		wake_up(&q->sync_sleep);
+	}
+	spin_unlock_irqrestore(&q->sync_lock, flags);
+}
+
+
+/*
+ * return the unused pool size
+ */
+int
+snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q)
+{
+	snd_seq_client_pool_t pool;
+	pool.client = q->dp->cseq;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
+	return pool.output_free;
+}
+
+
+/*
+ * set output threshold size from ioctl
+ */
+void
+snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val)
+{
+	snd_seq_client_pool_t pool;
+	pool.client = q->dp->cseq;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
+	pool.output_room = val;
+	snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
+}
+
diff --git a/sound/core/seq/oss/seq_oss_writeq.h b/sound/core/seq/oss/seq_oss_writeq.h
new file mode 100644
index 0000000..6a13c85
--- /dev/null
+++ b/sound/core/seq/oss/seq_oss_writeq.h
@@ -0,0 +1,50 @@
+/*
+ * OSS compatible sequencer driver
+ * write priority queue
+ *
+ * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SEQ_OSS_WRITEQ_H
+#define __SEQ_OSS_WRITEQ_H
+
+#include "seq_oss_device.h"
+
+
+struct seq_oss_writeq_t {
+	seq_oss_devinfo_t *dp;
+	int maxlen;
+	abstime_t sync_time;
+	int sync_event_put;
+	wait_queue_head_t sync_sleep;
+	spinlock_t sync_lock;
+};
+
+
+/*
+ * seq_oss_writeq.c
+ */
+seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen);
+void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q);
+int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time);
+int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q);
+void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size);
+
+
+#endif
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
new file mode 100644
index 0000000..7449d2a
--- /dev/null
+++ b/sound/core/seq/seq.c
@@ -0,0 +1,147 @@
+/*
+ *  ALSA sequencer main module
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_lock.h"
+#include "seq_timer.h"
+#include "seq_system.h"
+#include "seq_info.h"
+#include <sound/seq_device.h>
+
+#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
+int seq_client_load[64] = {[0] = SNDRV_SEQ_CLIENT_DUMMY, [1 ... 63] = -1};
+#else
+int seq_client_load[64] = {[0 ... 63] = -1};
+#endif
+int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
+int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
+int seq_default_timer_card = -1;
+int seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM;
+int seq_default_timer_subdevice = 0;
+int seq_default_timer_resolution = 0;	/* Hz */
+
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
+MODULE_LICENSE("GPL");
+
+module_param_array(seq_client_load, int, NULL, 0444);
+MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod.");
+module_param(seq_default_timer_class, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_class, "The default timer class.");
+module_param(seq_default_timer_sclass, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class.");
+module_param(seq_default_timer_card, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number.");
+module_param(seq_default_timer_device, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number.");
+module_param(seq_default_timer_subdevice, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number.");
+module_param(seq_default_timer_resolution, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
+
+/*
+ *  INIT PART
+ */
+
+static int __init alsa_seq_init(void)
+{
+	int err;
+
+	snd_seq_autoload_lock();
+	if ((err = client_init_data()) < 0)
+		goto error;
+
+	/* init memory, room for selected events */
+	if ((err = snd_sequencer_memory_init()) < 0)
+		goto error;
+
+	/* init event queues */
+	if ((err = snd_seq_queues_init()) < 0)
+		goto error;
+
+	/* register sequencer device */
+	if ((err = snd_sequencer_device_init()) < 0)
+		goto error;
+
+	/* register proc interface */
+	if ((err = snd_seq_info_init()) < 0)
+		goto error;
+
+	/* register our internal client */
+	if ((err = snd_seq_system_client_init()) < 0)
+		goto error;
+
+ error:
+	snd_seq_autoload_unlock();
+	return err;
+}
+
+static void __exit alsa_seq_exit(void)
+{
+	/* unregister our internal client */
+	snd_seq_system_client_done();
+
+	/* unregister proc interface */
+	snd_seq_info_done();
+	
+	/* delete timing queues */
+	snd_seq_queues_delete();
+
+	/* unregister sequencer device */
+	snd_sequencer_device_done();
+
+	/* release event memory */
+	snd_sequencer_memory_done();
+}
+
+module_init(alsa_seq_init)
+module_exit(alsa_seq_exit)
+
+  /* seq_clientmgr.c */
+EXPORT_SYMBOL(snd_seq_create_kernel_client);
+EXPORT_SYMBOL(snd_seq_delete_kernel_client);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
+EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
+EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
+EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+EXPORT_SYMBOL(snd_seq_set_queue_tempo);
+  /* seq_memory.c */
+EXPORT_SYMBOL(snd_seq_expand_var_event);
+EXPORT_SYMBOL(snd_seq_dump_var_event);
+  /* seq_ports.c */
+EXPORT_SYMBOL(snd_seq_event_port_attach);
+EXPORT_SYMBOL(snd_seq_event_port_detach);
+  /* seq_lock.c */
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/
+/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/
+EXPORT_SYMBOL(snd_use_lock_sync_helper);
+#endif
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
new file mode 100644
index 0000000..d8f76af
--- /dev/null
+++ b/sound/core/seq/seq_clientmgr.c
@@ -0,0 +1,2503 @@
+/*
+ *  ALSA sequencer Client Manager
+ *  Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                             Jaroslav Kysela <perex@suse.cz>
+ *                             Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <linux/kmod.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+#include "seq_system.h"
+#include <sound/seq_device.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+
+/* Client Manager
+
+ * this module handles the connections of userland and kernel clients
+ * 
+ */
+
+#define SNDRV_SEQ_LFLG_INPUT	0x0001
+#define SNDRV_SEQ_LFLG_OUTPUT	0x0002
+#define SNDRV_SEQ_LFLG_OPEN	(SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)
+
+static DEFINE_SPINLOCK(clients_lock);
+static DECLARE_MUTEX(register_mutex);
+
+/*
+ * client table
+ */
+static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
+static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS];
+static usage_t client_usage;
+
+/*
+ * prototypes
+ */
+static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop);
+static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop);
+
+/*
+ */
+ 
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+
+/*
+ */
+static inline unsigned short snd_seq_file_flags(struct file *file)
+{
+        switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
+        case FMODE_WRITE:
+                return SNDRV_SEQ_LFLG_OUTPUT;
+        case FMODE_READ:
+                return SNDRV_SEQ_LFLG_INPUT;
+        default:
+                return SNDRV_SEQ_LFLG_OPEN;
+        }
+}
+
+static inline int snd_seq_write_pool_allocated(client_t *client)
+{
+	return snd_seq_total_cells(client->pool) > 0;
+}
+
+/* return pointer to client structure for specified id */
+static client_t *clientptr(int clientid)
+{
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
+		return NULL;
+	}
+	return clienttab[clientid];
+}
+
+extern int seq_client_load[];
+
+client_t *snd_seq_client_use_ptr(int clientid)
+{
+	unsigned long flags;
+	client_t *client;
+
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);
+		return NULL;
+	}
+	spin_lock_irqsave(&clients_lock, flags);
+	client = clientptr(clientid);
+	if (client)
+		goto __lock;
+	if (clienttablock[clientid]) {
+		spin_unlock_irqrestore(&clients_lock, flags);
+		return NULL;
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+#ifdef CONFIG_KMOD
+	if (!in_interrupt() && current->fs->root) {
+		static char client_requested[64];
+		static char card_requested[SNDRV_CARDS];
+		if (clientid < 64) {
+			int idx;
+			
+			if (! client_requested[clientid] && current->fs->root) {
+				client_requested[clientid] = 1;
+				for (idx = 0; idx < 64; idx++) {
+					if (seq_client_load[idx] < 0)
+						break;
+					if (seq_client_load[idx] == clientid) {
+						request_module("snd-seq-client-%i", clientid);
+						break;
+					}
+				}
+			}
+		} else if (clientid >= 64 && clientid < 128) {
+			int card = (clientid - 64) / 8;
+			if (card < snd_ecards_limit) {
+				if (! card_requested[card]) {
+					card_requested[card] = 1;
+					snd_request_card(card);
+				}
+				snd_seq_device_load_drivers();
+			}
+		}
+		spin_lock_irqsave(&clients_lock, flags);
+		client = clientptr(clientid);
+		if (client)
+			goto __lock;
+		spin_unlock_irqrestore(&clients_lock, flags);
+	}
+#endif
+	return NULL;
+
+      __lock:
+	snd_use_lock_use(&client->use_lock);
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return client;
+}
+
+static void usage_alloc(usage_t * res, int num)
+{
+	res->cur += num;
+	if (res->cur > res->peak)
+		res->peak = res->cur;
+}
+
+static void usage_free(usage_t * res, int num)
+{
+	res->cur -= num;
+}
+
+/* initialise data structures */
+int __init client_init_data(void)
+{
+	/* zap out the client table */
+	memset(&clienttablock, 0, sizeof(clienttablock));
+	memset(&clienttab, 0, sizeof(clienttab));
+	return 0;
+}
+
+
+static client_t *seq_create_client1(int client_index, int poolsize)
+{
+	unsigned long flags;
+	int c;
+	client_t *client;
+
+	/* init client data */
+	client = kcalloc(1, sizeof(*client), GFP_KERNEL);
+	if (client == NULL)
+		return NULL;
+	client->pool = snd_seq_pool_new(poolsize);
+	if (client->pool == NULL) {
+		kfree(client);
+		return NULL;
+	}
+	client->type = NO_CLIENT;
+	snd_use_lock_init(&client->use_lock);
+	rwlock_init(&client->ports_lock);
+	init_MUTEX(&client->ports_mutex);
+	INIT_LIST_HEAD(&client->ports_list_head);
+
+	/* find free slot in the client table */
+	spin_lock_irqsave(&clients_lock, flags);
+	if (client_index < 0) {
+		for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+			if (clienttab[c] || clienttablock[c])
+				continue;
+			clienttab[client->number = c] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	} else {
+		if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
+			clienttab[client->number = client_index] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_seq_pool_delete(&client->pool);
+	kfree(client);
+	return NULL;	/* no free slot found or busy, return failure code */
+}
+
+
+static int seq_free_client1(client_t *client)
+{
+	unsigned long flags;
+
+	snd_assert(client != NULL, return -EINVAL);
+	snd_seq_delete_all_ports(client);
+	snd_seq_queue_client_leave(client->number);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 1;
+	clienttab[client->number] = NULL;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_use_lock_sync(&client->use_lock);
+	snd_seq_queue_client_termination(client->number);
+	if (client->pool)
+		snd_seq_pool_delete(&client->pool);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 0;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return 0;
+}
+
+
+static void seq_free_client(client_t * client)
+{
+	down(&register_mutex);
+	switch (client->type) {
+	case NO_CLIENT:
+		snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number);
+		break;
+	case USER_CLIENT:
+	case KERNEL_CLIENT:
+		seq_free_client1(client);
+		usage_free(&client_usage, 1);
+		break;
+
+	default:
+		snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type);
+	}
+	up(&register_mutex);
+
+	snd_seq_system_client_ev_client_exit(client->number);
+}
+
+
+
+/* -------------------------------------------------------- */
+
+/* create a user client */
+static int snd_seq_open(struct inode *inode, struct file *file)
+{
+	int c, mode;			/* client id */
+	client_t *client;
+	user_client_t *user;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -ENOMEM;	/* failure code */
+	}
+
+	mode = snd_seq_file_flags(file);
+	if (mode & SNDRV_SEQ_LFLG_INPUT)
+		client->accept_input = 1;
+	if (mode & SNDRV_SEQ_LFLG_OUTPUT)
+		client->accept_output = 1;
+
+	user = &client->data.user;
+	user->fifo = NULL;
+	user->fifo_pool_size = 0;
+
+	if (mode & SNDRV_SEQ_LFLG_INPUT) {
+		user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
+		user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
+		if (user->fifo == NULL) {
+			seq_free_client1(client);
+			kfree(client);
+			up(&register_mutex);
+			return -ENOMEM;
+		}
+	}
+
+	usage_alloc(&client_usage, 1);
+	client->type = USER_CLIENT;
+	up(&register_mutex);
+
+	c = client->number;
+	file->private_data = client;
+
+	/* fill client data */
+	user->file = file;
+	sprintf(client->name, "Client-%d", c);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(c);
+
+	return 0;
+}
+
+/* delete a user client */
+static int snd_seq_release(struct inode *inode, struct file *file)
+{
+	client_t *client = (client_t *) file->private_data;
+
+	if (client) {
+		seq_free_client(client);
+		if (client->data.user.fifo)
+			snd_seq_fifo_delete(&client->data.user.fifo);
+		kfree(client);
+	}
+
+	return 0;
+}
+
+
+/* handle client read() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOSPC	FIFO overflow (the flag is cleared after this error report)
+ *	-EINVAL	no enough user-space buffer to write the whole event
+ *	-EFAULT	seg. fault during copy to user space
+ */
+static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
+{
+	client_t *client = (client_t *) file->private_data;
+	fifo_t *fifo;
+	int err;
+	long result = 0;
+	snd_seq_event_cell_t *cell;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
+		return -ENXIO;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
+		return -ENXIO;
+
+	if (atomic_read(&fifo->overflow) > 0) {
+		/* buffer overflow is detected */
+		snd_seq_fifo_clear(fifo);
+		/* return error code */
+		return -ENOSPC;
+	}
+
+	cell = NULL;
+	err = 0;
+	snd_seq_fifo_lock(fifo);
+
+	/* while data available in queue */
+	while (count >= sizeof(snd_seq_event_t)) {
+		int nonblock;
+
+		nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
+		if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
+			break;
+		}
+		if (snd_seq_ev_is_variable(&cell->event)) {
+			snd_seq_event_t tmpev;
+			tmpev = cell->event;
+			tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
+			if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) {
+				err = -EFAULT;
+				break;
+			}
+			count -= sizeof(snd_seq_event_t);
+			buf += sizeof(snd_seq_event_t);
+			err = snd_seq_expand_var_event(&cell->event, count, (char *)buf, 0, sizeof(snd_seq_event_t));
+			if (err < 0)
+				break;
+			result += err;
+			count -= err;
+			buf += err;
+		} else {
+			if (copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t))) {
+				err = -EFAULT;
+				break;
+			}
+			count -= sizeof(snd_seq_event_t);
+			buf += sizeof(snd_seq_event_t);
+		}
+		snd_seq_cell_free(cell);
+		cell = NULL; /* to be sure */
+		result += sizeof(snd_seq_event_t);
+	}
+
+	if (err < 0) {
+		if (cell)
+			snd_seq_fifo_cell_putback(fifo, cell);
+		if (err == -EAGAIN && result > 0)
+			err = 0;
+	}
+	snd_seq_fifo_unlock(fifo);
+
+	return (err < 0) ? err : result;
+}
+
+
+/*
+ * check access permission to the port
+ */
+static int check_port_perm(client_port_t *port, unsigned int flags)
+{
+	if ((port->capability & flags) != flags)
+		return 0;
+	return flags;
+}
+
+/*
+ * check if the destination client is available, and return the pointer
+ * if filter is non-zero, client filter bitmap is tested.
+ */
+static client_t *get_event_dest_client(snd_seq_event_t *event, int filter)
+{
+	client_t *dest;
+
+	dest = snd_seq_client_use_ptr(event->dest.client);
+	if (dest == NULL)
+		return NULL;
+	if (! dest->accept_input)
+		goto __not_avail;
+	if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
+	    ! test_bit(event->type, dest->event_filter))
+		goto __not_avail;
+	if (filter && !(dest->filter & filter))
+		goto __not_avail;
+
+	return dest; /* ok - accessible */
+__not_avail:
+	snd_seq_client_unlock(dest);
+	return NULL;
+}
+
+
+/*
+ * Return the error event.
+ *
+ * If the receiver client is a user client, the original event is
+ * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event.  If
+ * the original event is also variable length, the external data is
+ * copied after the event record. 
+ * If the receiver client is a kernel client, the original event is
+ * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
+ * kmalloc.
+ */
+static int bounce_error_event(client_t *client, snd_seq_event_t *event,
+			      int err, int atomic, int hop)
+{
+	snd_seq_event_t bounce_ev;
+	int result;
+
+	if (client == NULL ||
+	    ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) ||
+	    ! client->accept_input)
+		return 0; /* ignored */
+
+	/* set up quoted error */
+	memset(&bounce_ev, 0, sizeof(bounce_ev));
+	bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
+	bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
+	bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	bounce_ev.dest.client = client->number;
+	bounce_ev.dest.port = event->source.port;
+	bounce_ev.data.quote.origin = event->dest;
+	bounce_ev.data.quote.event = event;
+	bounce_ev.data.quote.value = -err; /* use positive value */
+	result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
+	if (result < 0) {
+		client->event_lost++;
+		return result;
+	}
+
+	return result;
+}
+
+
+/*
+ * rewrite the time-stamp of the event record with the curren time
+ * of the given queue.
+ * return non-zero if updated.
+ */
+static int update_timestamp_of_queue(snd_seq_event_t *event, int queue, int real_time)
+{
+	queue_t *q;
+
+	q = queueptr(queue);
+	if (! q)
+		return 0;
+	event->queue = queue;
+	event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
+	if (real_time) {
+		event->time.time = snd_seq_timer_get_cur_time(q->timer);
+		event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+	} else {
+		event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
+		event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+	}
+	queuefree(q);
+	return 1;
+}
+
+
+/*
+ * deliver an event to the specified destination.
+ * if filter is non-zero, client filter bitmap is tested.
+ *
+ *  RETURN VALUE: 0 : if succeeded
+ *		 <0 : error
+ */
+static int snd_seq_deliver_single_event(client_t *client,
+					snd_seq_event_t *event,
+					int filter, int atomic, int hop)
+{
+	client_t *dest = NULL;
+	client_port_t *dest_port = NULL;
+	int result = -ENOENT;
+	int direct;
+
+	direct = snd_seq_ev_is_direct(event);
+
+	dest = get_event_dest_client(event, filter);
+	if (dest == NULL)
+		goto __skip;
+	dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
+	if (dest_port == NULL)
+		goto __skip;
+
+	/* check permission */
+	if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
+		result = -EPERM;
+		goto __skip;
+	}
+		
+	if (dest_port->timestamping)
+		update_timestamp_of_queue(event, dest_port->time_queue,
+					  dest_port->time_real);
+
+	switch (dest->type) {
+	case USER_CLIENT:
+		if (dest->data.user.fifo)
+			result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
+		break;
+
+	case KERNEL_CLIENT:
+		if (dest_port->event_input == NULL)
+			break;
+		result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop);
+		break;
+	default:
+		break;
+	}
+
+  __skip:
+	if (dest_port)
+		snd_seq_port_unlock(dest_port);
+	if (dest)
+		snd_seq_client_unlock(dest);
+
+	if (result < 0 && !direct) {
+		result = bounce_error_event(client, event, result, atomic, hop);
+	}
+	return result;
+}
+
+
+/*
+ * send the event to all subscribers:
+ */
+static int deliver_to_subscribers(client_t *client,
+				  snd_seq_event_t *event,
+				  int atomic, int hop)
+{
+	subscribers_t *subs;
+	int err = 0, num_ev = 0;
+	snd_seq_event_t event_saved;
+	client_port_t *src_port;
+	struct list_head *p;
+	port_subs_info_t *grp;
+
+	src_port = snd_seq_port_use_ptr(client, event->source.port);
+	if (src_port == NULL)
+		return -EINVAL; /* invalid source port */
+	/* save original event record */
+	event_saved = *event;
+	grp = &src_port->c_src;
+	
+	/* lock list */
+	if (atomic)
+		read_lock(&grp->list_lock);
+	else
+		down_read(&grp->list_mutex);
+	list_for_each(p, &grp->list_head) {
+		subs = list_entry(p, subscribers_t, src_list);
+		event->dest = subs->info.dest;
+		if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+			/* convert time according to flag with subscription */
+			update_timestamp_of_queue(event, subs->info.queue,
+						  subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
+		err = snd_seq_deliver_single_event(client, event,
+						   0, atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+		/* restore original event record */
+		*event = event_saved;
+	}
+	if (atomic)
+		read_unlock(&grp->list_lock);
+	else
+		up_read(&grp->list_mutex);
+	*event = event_saved; /* restore */
+	snd_seq_port_unlock(src_port);
+	return (err < 0) ? err : num_ev;
+}
+
+
+#ifdef SUPPORT_BROADCAST 
+/*
+ * broadcast to all ports:
+ */
+static int port_broadcast_event(client_t *client,
+				snd_seq_event_t *event,
+				int atomic, int hop)
+{
+	int num_ev = 0, err = 0;
+	client_t *dest_client;
+	struct list_head *p;
+
+	dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
+	if (dest_client == NULL)
+		return 0; /* no matching destination */
+
+	read_lock(&dest_client->ports_lock);
+	list_for_each(p, &dest_client->ports_list_head) {
+		client_port_t *port = list_entry(p, client_port_t, list);
+		event->dest.port = port->addr.port;
+		/* pass NULL as source client to avoid error bounce */
+		err = snd_seq_deliver_single_event(NULL, event,
+						   SNDRV_SEQ_FILTER_BROADCAST,
+						   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+	}
+	read_unlock(&dest_client->ports_lock);
+	snd_seq_client_unlock(dest_client);
+	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+/*
+ * send the event to all clients:
+ * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
+ */
+static int broadcast_event(client_t *client,
+			   snd_seq_event_t *event, int atomic, int hop)
+{
+	int err = 0, num_ev = 0;
+	int dest;
+	snd_seq_addr_t addr;
+
+	addr = event->dest; /* save */
+
+	for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
+		/* don't send to itself */
+		if (dest == client->number)
+			continue;
+		event->dest.client = dest;
+		event->dest.port = addr.port;
+		if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+			err = port_broadcast_event(client, event, atomic, hop);
+		else
+			/* pass NULL as source client to avoid error bounce */
+			err = snd_seq_deliver_single_event(NULL, event,
+							   SNDRV_SEQ_FILTER_BROADCAST,
+							   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev += err;
+	}
+	event->dest = addr; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+
+/* multicast - not supported yet */
+static int multicast_event(client_t *client, snd_seq_event_t *event,
+			   int atomic, int hop)
+{
+	snd_printd("seq: multicast not supported yet.\n");
+	return 0; /* ignored */
+}
+#endif /* SUPPORT_BROADCAST */
+
+
+/* deliver an event to the destination port(s).
+ * if the event is to subscribers or broadcast, the event is dispatched
+ * to multiple targets.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *               n == 0 : the event was not passed to any client.
+ *               n < 0  : error - event was not processed.
+ */
+static int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event,
+				 int atomic, int hop)
+{
+	int result;
+
+	hop++;
+	if (hop >= SNDRV_SEQ_MAX_HOPS) {
+		snd_printd("too long delivery path (%d:%d->%d:%d)\n",
+			   event->source.client, event->source.port,
+			   event->dest.client, event->dest.port);
+		return -EMLINK;
+	}
+
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
+	    event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
+		result = deliver_to_subscribers(client, event, atomic, hop);
+#ifdef SUPPORT_BROADCAST
+	else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
+		 event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = broadcast_event(client, event, atomic, hop);
+	else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
+		result = multicast_event(client, event, atomic, hop);
+	else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = port_broadcast_event(client, event, atomic, hop);
+#endif
+	else
+		result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
+
+	return result;
+}
+
+/*
+ * dispatch an event cell:
+ * This function is called only from queue check routines in timer
+ * interrupts or after enqueued.
+ * The event cell shall be released or re-queued in this function.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *		 n == 0 : the event was not passed to any client.
+ *		 n < 0  : error - event was not processed.
+ */
+int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop)
+{
+	client_t *client;
+	int result;
+
+	snd_assert(cell != NULL, return -EINVAL);
+
+	client = snd_seq_client_use_ptr(cell->event.source.client);
+	if (client == NULL) {
+		snd_seq_cell_free(cell); /* release this cell */
+		return -EINVAL;
+	}
+
+	if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+		/* NOTE event:
+		 * the event cell is re-used as a NOTE-OFF event and
+		 * enqueued again.
+		 */
+		snd_seq_event_t tmpev, *ev;
+
+		/* reserve this event to enqueue note-off later */
+		tmpev = cell->event;
+		tmpev.type = SNDRV_SEQ_EVENT_NOTEON;
+		result = snd_seq_deliver_event(client, &tmpev, atomic, hop);
+
+		/*
+		 * This was originally a note event.  We now re-use the
+		 * cell for the note-off event.
+		 */
+
+		ev = &cell->event;
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+		ev->flags |= SNDRV_SEQ_PRIORITY_HIGH;
+
+		/* add the duration time */
+		switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			ev->time.tick += ev->data.note.duration;
+			break;
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			/* unit for duration is ms */
+			ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000);
+			ev->time.time.tv_sec += ev->data.note.duration / 1000 +
+						ev->time.time.tv_nsec / 1000000000;
+			ev->time.time.tv_nsec %= 1000000000;
+			break;
+		}
+		ev->data.note.velocity = ev->data.note.off_velocity;
+
+		/* Now queue this cell as the note off event */
+		if (snd_seq_enqueue_event(cell, atomic, hop) < 0)
+			snd_seq_cell_free(cell); /* release this cell */
+
+	} else {
+		/* Normal events:
+		 * event cell is freed after processing the event
+		 */
+
+		result = snd_seq_deliver_event(client, &cell->event, atomic, hop);
+		snd_seq_cell_free(cell);
+	}
+
+	snd_seq_client_unlock(client);
+	return result;
+}
+
+
+/* Allocate a cell from client pool and enqueue it to queue:
+ * if pool is empty and blocking is TRUE, sleep until a new cell is
+ * available.
+ */
+static int snd_seq_client_enqueue_event(client_t *client,
+					snd_seq_event_t *event,
+					struct file *file, int blocking,
+					int atomic, int hop)
+{
+	snd_seq_event_cell_t *cell;
+	int err;
+
+	/* special queue values - force direct passing */
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	} else
+#ifdef SUPPORT_BROADCAST
+		if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
+			event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
+			event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+		}
+#endif
+	if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		/* check presence of source port */
+		client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port);
+		if (src_port == NULL)
+			return -EINVAL;
+		snd_seq_port_unlock(src_port);
+	}
+
+	/* direct event processing without enqueued */
+	if (snd_seq_ev_is_direct(event)) {
+		if (event->type == SNDRV_SEQ_EVENT_NOTE)
+			return -EINVAL; /* this event must be enqueued! */
+		return snd_seq_deliver_event(client, event, atomic, hop);
+	}
+
+	/* Not direct, normal queuing */
+	if (snd_seq_queue_is_used(event->queue, client->number) <= 0)
+		return -EINVAL;  /* invalid queue */
+	if (! snd_seq_write_pool_allocated(client))
+		return -ENXIO; /* queue is not allocated */
+
+	/* allocate an event cell */
+	err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+	if (err < 0)
+		return err;
+
+	/* we got a cell. enqueue it. */
+	if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
+		snd_seq_cell_free(cell);
+		return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * check validity of event type and data length.
+ * return non-zero if invalid.
+ */
+static int check_event_type_and_length(snd_seq_event_t *ev)
+{
+	switch (snd_seq_ev_length_type(ev)) {
+	case SNDRV_SEQ_EVENT_LENGTH_FIXED:
+		if (snd_seq_ev_is_variable_type(ev))
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
+		if (! snd_seq_ev_is_variable_type(ev) ||
+		    (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN)
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
+		if (! snd_seq_ev_is_instr_type(ev) ||
+		    ! snd_seq_ev_is_direct(ev))
+			return -EINVAL;
+		break;
+	}
+	return 0;
+}
+
+
+/* handle write() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOMEM	malloc failed
+ *	-EFAULT	seg. fault during copy from user space
+ *	-EINVAL	invalid event
+ *	-EAGAIN	no space in output pool
+ *	-EINTR	interrupts while sleep
+ *	-EMLINK	too many hops
+ *	others	depends on return value from driver callback
+ */
+static ssize_t snd_seq_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
+{
+	client_t *client = (client_t *) file->private_data;
+	int written = 0, len;
+	int err = -EINVAL;
+	snd_seq_event_t event;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
+		return -ENXIO;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+		
+	if (!client->accept_output || client->pool == NULL)
+		return -ENXIO;
+
+	/* allocate the pool now if the pool is not allocated yet */ 
+	if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
+		if (snd_seq_pool_init(client->pool) < 0)
+			return -ENOMEM;
+	}
+
+	/* only process whole events */
+	while (count >= sizeof(snd_seq_event_t)) {
+		/* Read in the event header from the user */
+		len = sizeof(event);
+		if (copy_from_user(&event, buf, len)) {
+			err = -EFAULT;
+			break;
+		}
+		event.source.client = client->number;	/* fill in client number */
+		/* Check for extension data length */
+		if (check_event_type_and_length(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		/* check for special events */
+		if (event.type == SNDRV_SEQ_EVENT_NONE)
+			goto __skip_event;
+		else if (snd_seq_ev_is_reserved(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		if (snd_seq_ev_is_variable(&event)) {
+			int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+			if ((size_t)(extlen + len) > count) {
+				/* back out, will get an error this time or next */
+				err = -EINVAL;
+				break;
+			}
+			/* set user space pointer */
+			event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+			event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t);
+			len += extlen; /* increment data length */
+		} else {
+#ifdef CONFIG_COMPAT
+			if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
+				void *ptr = compat_ptr(event.data.raw32.d[1]);
+				event.data.ext.ptr = ptr;
+			}
+#endif
+		}
+
+		/* ok, enqueue it */
+		err = snd_seq_client_enqueue_event(client, &event, file,
+						   !(file->f_flags & O_NONBLOCK),
+						   0, 0);
+		if (err < 0)
+			break;
+
+	__skip_event:
+		/* Update pointers and counts */
+		count -= len;
+		buf += len;
+		written += len;
+	}
+
+	return written ? written : err;
+}
+
+
+/*
+ * handle polling
+ */
+static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
+{
+	client_t *client = (client_t *) file->private_data;
+	unsigned int mask = 0;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
+	    client->data.user.fifo) {
+
+		/* check if data is available in the outqueue */
+		if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
+
+		/* check if data is available in the pool */
+		if (!snd_seq_write_pool_allocated(client) ||
+		    snd_seq_pool_poll_wait(client->pool, file, wait))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+
+	return mask;
+}
+
+
+/*-----------------------------------------------------*/
+
+
+/* SYSTEM_INFO ioctl() */
+static int snd_seq_ioctl_system_info(client_t *client, void __user *arg)
+{
+	snd_seq_system_info_t info;
+
+	memset(&info, 0, sizeof(info));
+	/* fill the info fields */
+	info.queues = SNDRV_SEQ_MAX_QUEUES;
+	info.clients = SNDRV_SEQ_MAX_CLIENTS;
+	info.ports = 256;	/* fixed limit */
+	info.channels = 256;	/* fixed limit */
+	info.cur_clients = client_usage.cur;
+	info.cur_queues = snd_seq_queue_get_cur_queues();
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* RUNNING_MODE ioctl() */
+static int snd_seq_ioctl_running_mode(client_t *client, void __user *arg)
+{
+	struct sndrv_seq_running_info info;
+	client_t *cptr;
+	int err = 0;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* requested client number */
+	cptr = snd_seq_client_use_ptr(info.client);
+	if (cptr == NULL)
+		return -ENOENT;		/* don't change !!! */
+
+#ifdef SNDRV_BIG_ENDIAN
+	if (! info.big_endian) {
+		err = -EINVAL;
+		goto __err;
+	}
+#else
+	if (info.big_endian) {
+		err = -EINVAL;
+		goto __err;
+	}
+
+#endif
+	if (info.cpu_mode > sizeof(long)) {
+		err = -EINVAL;
+		goto __err;
+	}
+	cptr->convert32 = (info.cpu_mode < sizeof(long));
+ __err:
+	snd_seq_client_unlock(cptr);
+	return err;
+}
+
+/* CLIENT_INFO ioctl() */
+static void get_client_info(client_t *cptr, snd_seq_client_info_t *info)
+{
+	info->client = cptr->number;
+
+	/* fill the info fields */
+	info->type = cptr->type;
+	strcpy(info->name, cptr->name);
+	info->filter = cptr->filter;
+	info->event_lost = cptr->event_lost;
+	memcpy(info->event_filter, cptr->event_filter, 32);
+	info->num_ports = cptr->num_ports;
+	memset(info->reserved, 0, sizeof(info->reserved));
+}
+
+static int snd_seq_ioctl_get_client_info(client_t * client, void __user *arg)
+{
+	client_t *cptr;
+	snd_seq_client_info_t client_info;
+
+	if (copy_from_user(&client_info, arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* requested client number */
+	cptr = snd_seq_client_use_ptr(client_info.client);
+	if (cptr == NULL)
+		return -ENOENT;		/* don't change !!! */
+
+	get_client_info(cptr, &client_info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &client_info, sizeof(client_info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* CLIENT_INFO ioctl() */
+static int snd_seq_ioctl_set_client_info(client_t * client, void __user *arg)
+{
+	snd_seq_client_info_t client_info;
+
+	if (copy_from_user(&client_info, arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* it is not allowed to set the info fields for an another client */
+	if (client->number != client_info.client)
+		return -EPERM;
+	/* also client type must be set now */
+	if (client->type != client_info.type)
+		return -EINVAL;
+
+	/* fill the info fields */
+	if (client_info.name[0])
+		strlcpy(client->name, client_info.name, sizeof(client->name));
+
+	client->filter = client_info.filter;
+	client->event_lost = client_info.event_lost;
+	memcpy(client->event_filter, client_info.event_filter, 32);
+
+	return 0;
+}
+
+
+/* 
+ * CREATE PORT ioctl() 
+ */
+static int snd_seq_ioctl_create_port(client_t * client, void __user *arg)
+{
+	client_port_t *port;
+	snd_seq_port_info_t info;
+	snd_seq_port_callback_t *callback;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* it is not allowed to create the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
+	if (port == NULL)
+		return -ENOMEM;
+
+	if (client->type == USER_CLIENT && info.kernel) {
+		snd_seq_delete_port(client, port->addr.port);
+		return -EINVAL;
+	}
+	if (client->type == KERNEL_CLIENT) {
+		if ((callback = info.kernel) != NULL) {
+			if (callback->owner)
+				port->owner = callback->owner;
+			port->private_data = callback->private_data;
+			port->private_free = callback->private_free;
+			port->callback_all = callback->callback_all;
+			port->event_input = callback->event_input;
+			port->c_src.open = callback->subscribe;
+			port->c_src.close = callback->unsubscribe;
+			port->c_dest.open = callback->use;
+			port->c_dest.close = callback->unuse;
+		}
+	}
+
+	info.addr = port->addr;
+
+	snd_seq_set_port_info(port, &info);
+	snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* 
+ * DELETE PORT ioctl() 
+ */
+static int snd_seq_ioctl_delete_port(client_t * client, void __user *arg)
+{
+	snd_seq_port_info_t info;
+	int err;
+
+	/* set passed parameters */
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	
+	/* it is not allowed to remove the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	err = snd_seq_delete_port(client, info.addr.port);
+	if (err >= 0)
+		snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+	return err;
+}
+
+
+/* 
+ * GET_PORT_INFO ioctl() (on any client) 
+ */
+static int snd_seq_ioctl_get_port_info(client_t *client, void __user *arg)
+{
+	client_t *cptr;
+	client_port_t *port;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	port = snd_seq_port_use_ptr(cptr, info.addr.port);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;			/* don't change */
+	}
+
+	/* get port info */
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* 
+ * SET_PORT_INFO ioctl() (only ports on this/own client) 
+ */
+static int snd_seq_ioctl_set_port_info(client_t * client, void __user *arg)
+{
+	client_port_t *port;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.addr.client != client->number) /* only set our own ports ! */
+		return -EPERM;
+	port = snd_seq_port_use_ptr(client, info.addr.port);
+	if (port) {
+		snd_seq_set_port_info(port, &info);
+		snd_seq_port_unlock(port);
+	}
+	return 0;
+}
+
+
+/*
+ * port subscription (connection)
+ */
+#define PERM_RD		(SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+#define PERM_WR		(SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+
+static int check_subscription_permission(client_t *client, client_port_t *sport,
+					 client_port_t *dport,
+					 snd_seq_port_subscribe_t *subs)
+{
+	if (client->number != subs->sender.client &&
+	    client->number != subs->dest.client) {
+		/* connection by third client - check export permission */
+		if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+		if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+	}
+
+	/* check read permission */
+	/* if sender or receiver is the subscribing client itself,
+	 * no permission check is necessary
+	 */
+	if (client->number != subs->sender.client) {
+		if (! check_port_perm(sport, PERM_RD))
+			return -EPERM;
+	}
+	/* check write permission */
+	if (client->number != subs->dest.client) {
+		if (! check_port_perm(dport, PERM_WR))
+			return -EPERM;
+	}
+	return 0;
+}
+
+/*
+ * send an subscription notify event to user client:
+ * client must be user client.
+ */
+int snd_seq_client_notify_subscription(int client, int port,
+				       snd_seq_port_subscribe_t *info, int evtype)
+{
+	snd_seq_event_t event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = evtype;
+	event.data.connect.dest = info->dest;
+	event.data.connect.sender = info->sender;
+
+	return snd_seq_system_notify(client, port, &event);  /* non-atomic */
+}
+
+
+/* 
+ * add to port's subscription list IOCTL interface 
+ */
+static int snd_seq_ioctl_subscribe_port(client_t * client, void __user *arg)
+{
+	int result = -EINVAL;
+	client_t *receiver = NULL, *sender = NULL;
+	client_port_t *sport = NULL, *dport = NULL;
+	snd_seq_port_subscribe_t subs;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	/* connect them */
+	result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* 
+ * remove from port's subscription list 
+ */
+static int snd_seq_ioctl_unsubscribe_port(client_t * client, void __user *arg)
+{
+	int result = -ENXIO;
+	client_t *receiver = NULL, *sender = NULL;
+	client_port_t *sport = NULL, *dport = NULL;
+	snd_seq_port_subscribe_t subs;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* CREATE_QUEUE ioctl() */
+static int snd_seq_ioctl_create_queue(client_t *client, void __user *arg)
+{
+	snd_seq_queue_info_t info;
+	int result;
+	queue_t *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
+	if (result < 0)
+		return result;
+
+	q = queueptr(result);
+	if (q == NULL)
+		return -EINVAL;
+
+	info.queue = q->queue;
+	info.locked = q->locked;
+	info.owner = q->owner;
+
+	/* set queue name */
+	if (! info.name[0])
+		snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
+	strlcpy(q->name, info.name, sizeof(q->name));
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* DELETE_QUEUE ioctl() */
+static int snd_seq_ioctl_delete_queue(client_t *client, void __user *arg)
+{
+	snd_seq_queue_info_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	return snd_seq_queue_delete(client->number, info.queue);
+}
+
+/* GET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_get_queue_info(client_t *client, void __user *arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	q = queueptr(info.queue);
+	if (q == NULL)
+		return -EINVAL;
+
+	memset(&info, 0, sizeof(info));
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	strlcpy(info.name, q->name, sizeof(info.name));
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* SET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_set_queue_info(client_t *client, void __user *arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.owner != client->number)
+		return -EINVAL;
+
+	/* change owner/locked permission */
+	if (snd_seq_queue_check_access(info.queue, client->number)) {
+		if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+			return -EPERM;
+		if (info.locked)
+			snd_seq_queue_use(info.queue, client->number, 1);
+	} else {
+		return -EPERM;
+	}	
+
+	q = queueptr(info.queue);
+	if (! q)
+		return -EINVAL;
+	if (q->owner != client->number) {
+		queuefree(q);
+		return -EPERM;
+	}
+	strlcpy(q->name, info.name, sizeof(q->name));
+	queuefree(q);
+
+	return 0;
+}
+
+/* GET_NAMED_QUEUE ioctl() */
+static int snd_seq_ioctl_get_named_queue(client_t *client, void __user *arg)
+{
+	snd_seq_queue_info_t info;
+	queue_t *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	q = snd_seq_queue_find_name(info.name);
+	if (q == NULL)
+		return -EINVAL;
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* GET_QUEUE_STATUS ioctl() */
+static int snd_seq_ioctl_get_queue_status(client_t * client, void __user *arg)
+{
+	snd_seq_queue_status_t status;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&status, arg, sizeof(status)))
+		return -EFAULT;
+
+	queue = queueptr(status.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&status, 0, sizeof(status));
+	status.queue = queue->queue;
+	
+	tmr = queue->timer;
+	status.events = queue->tickq->cells + queue->timeq->cells;
+
+	status.time = snd_seq_timer_get_cur_time(tmr);
+	status.tick = snd_seq_timer_get_cur_tick(tmr);
+
+	status.running = tmr->running;
+
+	status.flags = queue->flags;
+	queuefree(queue);
+
+	if (copy_to_user(arg, &status, sizeof(status)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* GET_QUEUE_TEMPO ioctl() */
+static int snd_seq_ioctl_get_queue_tempo(client_t * client, void __user *arg)
+{
+	snd_seq_queue_tempo_t tempo;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&tempo, arg, sizeof(tempo)))
+		return -EFAULT;
+
+	queue = queueptr(tempo.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&tempo, 0, sizeof(tempo));
+	tempo.queue = queue->queue;
+	
+	tmr = queue->timer;
+
+	tempo.tempo = tmr->tempo;
+	tempo.ppq = tmr->ppq;
+	tempo.skew_value = tmr->skew;
+	tempo.skew_base = tmr->skew_base;
+	queuefree(queue);
+
+	if (copy_to_user(arg, &tempo, sizeof(tempo)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TEMPO ioctl() */
+int snd_seq_set_queue_tempo(int client, snd_seq_queue_tempo_t *tempo)
+{
+	if (!snd_seq_queue_check_access(tempo->queue, client))
+		return -EPERM;
+	return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
+}
+
+static int snd_seq_ioctl_set_queue_tempo(client_t * client, void __user *arg)
+{
+	int result;
+	snd_seq_queue_tempo_t tempo;
+
+	if (copy_from_user(&tempo, arg, sizeof(tempo)))
+		return -EFAULT;
+
+	result = snd_seq_set_queue_tempo(client->number, &tempo);
+	return result < 0 ? result : 0;
+}
+
+
+/* GET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_get_queue_timer(client_t * client, void __user *arg)
+{
+	snd_seq_queue_timer_t timer;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	if (copy_from_user(&timer, arg, sizeof(timer)))
+		return -EFAULT;
+
+	queue = queueptr(timer.queue);
+	if (queue == NULL)
+		return -EINVAL;
+
+	if (down_interruptible(&queue->timer_mutex)) {
+		queuefree(queue);
+		return -ERESTARTSYS;
+	}
+	tmr = queue->timer;
+	memset(&timer, 0, sizeof(timer));
+	timer.queue = queue->queue;
+
+	timer.type = tmr->type;
+	if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+		timer.u.alsa.id = tmr->alsa_id;
+		timer.u.alsa.resolution = tmr->preferred_resolution;
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	
+	if (copy_to_user(arg, &timer, sizeof(timer)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_set_queue_timer(client_t * client, void __user *arg)
+{
+	int result = 0;
+	snd_seq_queue_timer_t timer;
+
+	if (copy_from_user(&timer, arg, sizeof(timer)))
+		return -EFAULT;
+
+	if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+		return -EINVAL;
+
+	if (snd_seq_queue_check_access(timer.queue, client->number)) {
+		queue_t *q;
+		seq_timer_t *tmr;
+
+		q = queueptr(timer.queue);
+		if (q == NULL)
+			return -ENXIO;
+		if (down_interruptible(&q->timer_mutex)) {
+			queuefree(q);
+			return -ERESTARTSYS;
+		}
+		tmr = q->timer;
+		snd_seq_queue_timer_close(timer.queue);
+		tmr->type = timer.type;
+		if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+			tmr->alsa_id = timer.u.alsa.id;
+			tmr->preferred_resolution = timer.u.alsa.resolution;
+		}
+		result = snd_seq_queue_timer_open(timer.queue);
+		up(&q->timer_mutex);
+		queuefree(q);
+	} else {
+		return -EPERM;
+	}	
+
+	return result;
+}
+
+
+/* GET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_get_queue_client(client_t * client, void __user *arg)
+{
+	snd_seq_queue_client_t info;
+	int used;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	used = snd_seq_queue_is_used(info.queue, client->number);
+	if (used < 0)
+		return -EINVAL;
+	info.used = used;
+	info.client = client->number;
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_set_queue_client(client_t * client, void __user *arg)
+{
+	int err;
+	snd_seq_queue_client_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.used >= 0) {
+		err = snd_seq_queue_use(info.queue, client->number, info.used);
+		if (err < 0)
+			return err;
+	}
+
+	return snd_seq_ioctl_get_queue_client(client, arg);
+}
+
+
+/* GET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_get_client_pool(client_t * client, void __user *arg)
+{
+	snd_seq_client_pool_t info;
+	client_t *cptr;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	cptr = snd_seq_client_use_ptr(info.client);
+	if (cptr == NULL)
+		return -ENOENT;
+	memset(&info, 0, sizeof(info));
+	info.output_pool = cptr->pool->size;
+	info.output_room = cptr->pool->room;
+	info.output_free = info.output_pool;
+	if (cptr->pool)
+		info.output_free = snd_seq_unused_cells(cptr->pool);
+	if (cptr->type == USER_CLIENT) {
+		info.input_pool = cptr->data.user.fifo_pool_size;
+		info.input_free = info.input_pool;
+		if (cptr->data.user.fifo)
+			info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+	} else {
+		info.input_pool = 0;
+		info.input_free = 0;
+	}
+	snd_seq_client_unlock(cptr);
+	
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* SET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_set_client_pool(client_t * client, void __user *arg)
+{
+	snd_seq_client_pool_t info;
+	int rc;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (client->number != info.client)
+		return -EINVAL; /* can't change other clients */
+
+	if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+	    (! snd_seq_write_pool_allocated(client) ||
+	     info.output_pool != client->pool->size)) {
+		if (snd_seq_write_pool_allocated(client)) {
+			/* remove all existing cells */
+			snd_seq_queue_client_leave_cells(client->number);
+			snd_seq_pool_done(client->pool);
+		}
+		client->pool->size = info.output_pool;
+		rc = snd_seq_pool_init(client->pool);
+		if (rc < 0)
+			return rc;
+	}
+	if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
+	    info.input_pool >= 1 &&
+	    info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+	    info.input_pool != client->data.user.fifo_pool_size) {
+		/* change pool size */
+		rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+		if (rc < 0)
+			return rc;
+		client->data.user.fifo_pool_size = info.input_pool;
+	}
+	if (info.output_room >= 1 &&
+	    info.output_room <= client->pool->size) {
+		client->pool->room  = info.output_room;
+	}
+
+	return snd_seq_ioctl_get_client_pool(client, arg);
+}
+
+
+/* REMOVE_EVENTS ioctl() */
+static int snd_seq_ioctl_remove_events(client_t * client, void __user *arg)
+{
+	snd_seq_remove_events_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/*
+	 * Input mostly not implemented XXX.
+	 */
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+		/*
+		 * No restrictions so for a user client we can clear
+		 * the whole fifo
+		 */
+		if (client->type == USER_CLIENT)
+			snd_seq_fifo_clear(client->data.user.fifo);
+	}
+
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+		snd_seq_queue_remove_cells(client->number, &info);
+
+	return 0;
+}
+
+
+/*
+ * get subscription info
+ */
+static int snd_seq_ioctl_get_subscription(client_t *client, void __user *arg)
+{
+	int result;
+	client_t *sender = NULL;
+	client_port_t *sport = NULL;
+	snd_seq_port_subscribe_t subs;
+	subscribers_t *p;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	result = -EINVAL;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
+	if (p) {
+		result = 0;
+		subs = p->info;
+	} else
+		result = -ENOENT;
+
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (result >= 0) {
+		if (copy_to_user(arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * get subscription info - check only its presence
+ */
+static int snd_seq_ioctl_query_subs(client_t *client, void __user *arg)
+{
+	int result = -ENXIO;
+	client_t *cptr = NULL;
+	client_port_t *port = NULL;
+	snd_seq_query_subs_t subs;
+	port_subs_info_t *group;
+	struct list_head *p;
+	int i;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
+		goto __end;
+	if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
+		goto __end;
+
+	switch (subs.type) {
+	case SNDRV_SEQ_QUERY_SUBS_READ:
+		group = &port->c_src;
+		break;
+	case SNDRV_SEQ_QUERY_SUBS_WRITE:
+		group = &port->c_dest;
+		break;
+	default:
+		goto __end;
+	}
+
+	down_read(&group->list_mutex);
+	/* search for the subscriber */
+	subs.num_subs = group->count;
+	i = 0;
+	result = -ENOENT;
+	list_for_each(p, &group->list_head) {
+		if (i++ == subs.index) {
+			/* found! */
+			subscribers_t *s;
+			if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+				s = list_entry(p, subscribers_t, src_list);
+				subs.addr = s->info.dest;
+			} else {
+				s = list_entry(p, subscribers_t, dest_list);
+				subs.addr = s->info.sender;
+			}
+			subs.flags = s->info.flags;
+			subs.queue = s->info.queue;
+			result = 0;
+			break;
+		}
+	}
+	up_read(&group->list_mutex);
+
+      __end:
+   	if (port)
+		snd_seq_port_unlock(port);
+	if (cptr)
+		snd_seq_client_unlock(cptr);
+	if (result >= 0) {
+		if (copy_to_user(arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * query next client
+ */
+static int snd_seq_ioctl_query_next_client(client_t *client, void __user *arg)
+{
+	client_t *cptr = NULL;
+	snd_seq_client_info_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* search for next client */
+	info.client++;
+	if (info.client < 0)
+		info.client = 0;
+	for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
+		cptr = snd_seq_client_use_ptr(info.client);
+		if (cptr)
+			break; /* found */
+	}
+	if (cptr == NULL)
+		return -ENOENT;
+
+	get_client_info(cptr, &info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* 
+ * query next port
+ */
+static int snd_seq_ioctl_query_next_port(client_t *client, void __user *arg)
+{
+	client_t *cptr;
+	client_port_t *port = NULL;
+	snd_seq_port_info_t info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	/* search for next port */
+	info.addr.port++;
+	port = snd_seq_port_query_nearest(cptr, &info);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;
+	}
+
+	/* get port info */
+	info.addr = port->addr;
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* -------------------------------------------------------- */
+
+static struct seq_ioctl_table {
+	unsigned int cmd;
+	int (*func)(client_t *client, void __user * arg);
+} ioctl_tables[] = {
+	{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
+	{ SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info },
+	{ SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port },
+	{ SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port },
+	{ SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info },
+	{ SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info },
+	{ SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port },
+	{ SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port },
+	{ SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue },
+	{ SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info },
+	{ SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool },
+	{ SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port },
+	{ SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events },
+	{ SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs },
+	{ 0, NULL },
+};
+
+static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, void __user *arg)
+{
+	struct seq_ioctl_table *p;
+
+	switch (cmd) {
+	case SNDRV_SEQ_IOCTL_PVERSION:
+		/* return sequencer version number */
+		return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
+	case SNDRV_SEQ_IOCTL_CLIENT_ID:
+		/* return the id of this client */
+		return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
+	}
+
+	if (! arg)
+		return -EFAULT;
+	for (p = ioctl_tables; p->cmd; p++) {
+		if (p->cmd == cmd)
+			return p->func(client, arg);
+	}
+	snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
+		   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+	return -ENOTTY;
+}
+
+
+static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	client_t *client = (client_t *) file->private_data;
+
+	snd_assert(client != NULL, return -ENXIO);
+		
+	return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+}
+
+#ifdef CONFIG_COMPAT
+#include "seq_compat.c"
+#else
+#define snd_seq_ioctl_compat	NULL
+#endif
+
+/* -------------------------------------------------------- */
+
+
+/* exported to kernel modules */
+int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback)
+{
+	client_t *client;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	if (callback == NULL)
+		return -EINVAL;
+	if (card && client_index > 7)
+		return -EINVAL;
+	if (card == NULL && client_index > 63)
+		return -EINVAL;
+	if (card)
+		client_index += 64 + (card->number << 3);
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	/* empty write queue as default */
+	client = seq_create_client1(client_index, 0);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -EBUSY;	/* failure code */
+	}
+	usage_alloc(&client_usage, 1);
+
+	client->accept_input = callback->allow_output;
+	client->accept_output = callback->allow_input;
+		
+	/* fill client data */
+	client->data.kernel.card = card;
+	client->data.kernel.private_data = callback->private_data;
+	sprintf(client->name, "Client-%d", client->number);
+
+	client->type = KERNEL_CLIENT;
+	up(&register_mutex);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(client->number);
+	
+	/* return client number to caller */
+	return client->number;
+}
+
+/* exported to kernel modules */
+int snd_seq_delete_kernel_client(int client)
+{
+	client_t *ptr;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	ptr = clientptr(client);
+	if (ptr == NULL)
+		return -EINVAL;
+
+	seq_free_client(ptr);
+	kfree(ptr);
+	return 0;
+}
+
+
+/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
+ * and snd_seq_kernel_client_enqueue_blocking
+ */
+static int kernel_client_enqueue(int client, snd_seq_event_t *ev,
+				 struct file *file, int blocking,
+				 int atomic, int hop)
+{
+	client_t *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return 0; /* ignore this */
+	if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+		return -EINVAL; /* quoted events can't be enqueued */
+
+	/* fill in client number */
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+	
+	if (! cptr->accept_output)
+		result = -EPERM;
+	else /* send it */
+		result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (w/o blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev,
+				  int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (with blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev,
+					   struct file *file,
+					   int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
+}
+
+
+/* 
+ * exported, called by kernel clients to dispatch events directly to other
+ * clients, bypassing the queues.  Event time-stamp will be updated.
+ *
+ * RETURN VALUE: negative = delivery failed,
+ *		 zero, or positive: the number of delivered events
+ */
+int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev,
+				   int atomic, int hop)
+{
+	client_t *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	/* fill in client number */
+	ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+
+	if (!cptr->accept_output)
+		result = -EPERM;
+	else
+		result = snd_seq_deliver_event(cptr, ev, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+
+/*
+ * exported, called by kernel clients to perform same functions as with
+ * userland ioctl() 
+ */
+int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
+{
+	client_t *client;
+	mm_segment_t fs;
+	int result;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+	fs = snd_enter_user();
+	result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
+	snd_leave_user(fs);
+	return result;
+}
+
+
+/* exported (for OSS emulator) */
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
+{
+	client_t *client;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+
+	if (! snd_seq_write_pool_allocated(client))
+		return 1;
+	if (snd_seq_pool_poll_wait(client->pool, file, wait))
+		return 1;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ *  /proc interface
+ */
+static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg)
+{
+	struct list_head *p;
+	subscribers_t *s;
+	int count = 0;
+
+	down_read(&group->list_mutex);
+	if (list_empty(&group->list_head)) {
+		up_read(&group->list_mutex);
+		return;
+	}
+	snd_iprintf(buffer, msg);
+	list_for_each(p, &group->list_head) {
+		if (is_src)
+			s = list_entry(p, subscribers_t, src_list);
+		else
+			s = list_entry(p, subscribers_t, dest_list);
+		if (count++)
+			snd_iprintf(buffer, ", ");
+		snd_iprintf(buffer, "%d:%d",
+			    is_src ? s->info.dest.client : s->info.sender.client,
+			    is_src ? s->info.dest.port : s->info.sender.port);
+		if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+			snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue);
+		if (group->exclusive)
+			snd_iprintf(buffer, "[ex]");
+	}
+	up_read(&group->list_mutex);
+	snd_iprintf(buffer, "\n");
+}
+
+#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')
+#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')
+#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')
+
+#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
+
+static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client)
+{
+	struct list_head *l;
+
+	down(&client->ports_mutex);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		snd_iprintf(buffer, "  Port %3d : \"%s\" (%c%c%c%c)\n",
+			    p->addr.port, p->name,
+			    FLAG_PERM_RD(p->capability),
+			    FLAG_PERM_WR(p->capability),
+			    FLAG_PERM_EX(p->capability),
+			    FLAG_PERM_DUPLEX(p->capability));
+		snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, "    Connecting To: ");
+		snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, "    Connected From: ");
+	}
+	up(&client->ports_mutex);
+}
+
+
+/* exported to seq_info.c */
+void snd_seq_info_clients_read(snd_info_entry_t *entry, 
+			       snd_info_buffer_t * buffer)
+{
+	extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space);
+	int c;
+	client_t *client;
+
+	snd_iprintf(buffer, "Client info\n");
+	snd_iprintf(buffer, "  cur  clients : %d\n", client_usage.cur);
+	snd_iprintf(buffer, "  peak clients : %d\n", client_usage.peak);
+	snd_iprintf(buffer, "  max  clients : %d\n", SNDRV_SEQ_MAX_CLIENTS);
+	snd_iprintf(buffer, "\n");
+
+	/* list the client table */
+	for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+		client = snd_seq_client_use_ptr(c);
+		if (client == NULL)
+			continue;
+		if (client->type == NO_CLIENT) {
+			snd_seq_client_unlock(client);
+			continue;
+		}
+
+		snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+			    c, client->name,
+			    client->type == USER_CLIENT ? "User" : "Kernel");
+		snd_seq_info_dump_ports(buffer, client);
+		if (snd_seq_write_pool_allocated(client)) {
+			snd_iprintf(buffer, "  Output pool :\n");
+			snd_seq_info_pool(buffer, client->pool, "    ");
+		}
+		if (client->type == USER_CLIENT && client->data.user.fifo &&
+		    client->data.user.fifo->pool) {
+			snd_iprintf(buffer, "  Input pool :\n");
+			snd_seq_info_pool(buffer, client->data.user.fifo->pool, "    ");
+		}
+		snd_seq_client_unlock(client);
+	}
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+
+/*
+ *  REGISTRATION PART
+ */
+
+static struct file_operations snd_seq_f_ops =
+{
+	.owner =	THIS_MODULE,
+	.read =		snd_seq_read,
+	.write =	snd_seq_write,
+	.open =		snd_seq_open,
+	.release =	snd_seq_release,
+	.poll =		snd_seq_poll,
+	.unlocked_ioctl =	snd_seq_ioctl,
+	.compat_ioctl =	snd_seq_ioctl_compat,
+};
+
+static snd_minor_t snd_seq_reg =
+{
+	.comment =	"sequencer",
+	.f_ops =	&snd_seq_f_ops,
+};
+
+
+/* 
+ * register sequencer device 
+ */
+int __init snd_sequencer_device_init(void)
+{
+	int err;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) {
+		up(&register_mutex);
+		return err;
+	}
+	
+	up(&register_mutex);
+
+	return 0;
+}
+
+
+
+/* 
+ * unregister sequencer device 
+ */
+void __exit snd_sequencer_device_done(void)
+{
+	snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
+}
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
new file mode 100644
index 0000000..3715c36
--- /dev/null
+++ b/sound/core/seq/seq_clientmgr.h
@@ -0,0 +1,104 @@
+/*
+ *   ALSA sequencer Client Manager
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_CLIENTMGR_H
+#define __SND_SEQ_CLIENTMGR_H
+
+#include <sound/seq_kernel.h>
+#include <linux/bitops.h>
+#include "seq_fifo.h"
+#include "seq_ports.h"
+#include "seq_lock.h"
+
+
+/* client manager */
+
+struct _snd_seq_user_client {
+	struct file *file;	/* file struct of client */
+	/* ... */
+	
+	/* fifo */
+	fifo_t *fifo;	/* queue for incoming events */
+	int fifo_pool_size;
+};
+
+struct _snd_seq_kernel_client {
+	snd_card_t *card;
+	/* pointer to client functions */
+	void *private_data;			/* private data for client */
+	/* ... */
+};
+
+
+struct _snd_seq_client {
+	snd_seq_client_type_t type;
+	unsigned int accept_input: 1,
+		accept_output: 1;
+	char name[64];		/* client name */
+	int number;		/* client number */
+	unsigned int filter;	/* filter flags */
+	DECLARE_BITMAP(event_filter, 256);
+	snd_use_lock_t use_lock;
+	int event_lost;
+	/* ports */
+	int num_ports;		/* number of ports */
+	struct list_head ports_list_head;
+	rwlock_t ports_lock;
+	struct semaphore ports_mutex;
+	int convert32;		/* convert 32->64bit */
+
+	/* output pool */
+	pool_t *pool;		/* memory pool for this client */
+
+	union {
+		user_client_t user;
+		kernel_client_t kernel;
+	} data;
+};
+
+/* usage statistics */
+typedef struct {
+	int cur;
+	int peak;
+} usage_t;
+
+
+extern int client_init_data(void);
+extern int snd_sequencer_device_init(void);
+extern void snd_sequencer_device_done(void);
+
+/* get locked pointer to client */
+extern client_t *snd_seq_client_use_ptr(int clientid);
+
+/* unlock pointer to client */
+#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
+
+/* dispatch event to client(s) */
+extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop);
+
+/* exported to other modules */
+extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data);
+extern int snd_seq_unregister_kernel_client(int client);
+extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop);
+int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop);
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
+int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype);
+
+#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
new file mode 100644
index 0000000..902ad8b
--- /dev/null
+++ b/sound/core/seq/seq_compat.c
@@ -0,0 +1,137 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for sequencer API
+ *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/* This file included from seq.c */
+
+#include <linux/compat.h>
+
+struct sndrv_seq_port_info32 {
+	struct sndrv_seq_addr addr;	/* client/port numbers */
+	char name[64];			/* port name */
+
+	u32 capability;	/* port capability bits */
+	u32 type;		/* port type bits */
+	s32 midi_channels;		/* channels per MIDI port */
+	s32 midi_voices;		/* voices per MIDI port */
+	s32 synth_voices;		/* voices per SYNTH port */
+
+	s32 read_use;			/* R/O: subscribers for output (from this port) */
+	s32 write_use;			/* R/O: subscribers for input (to this port) */
+
+	u32 kernel;			/* reserved for kernel use (must be NULL) */
+	u32 flags;		/* misc. conditioning */
+	unsigned char time_queue;	/* queue # for timestamping */
+	char reserved[59];		/* for future use */
+};
+
+static int snd_seq_call_port_info_ioctl(client_t *client, unsigned int cmd,
+					struct sndrv_seq_port_info32 __user *data32)
+{
+	int err = -EFAULT;
+	snd_seq_port_info_t *data;
+	mm_segment_t fs;
+
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	if (! data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, data32, sizeof(*data32)) ||
+	    get_user(data->flags, &data32->flags) ||
+	    get_user(data->time_queue, &data32->time_queue))
+		goto error;
+	data->kernel = NULL;
+
+	fs = snd_enter_user();
+	err = snd_seq_do_ioctl(client, cmd, data);
+	snd_leave_user(fs);
+	if (err < 0)
+		goto error;
+
+	if (copy_to_user(data32, data, sizeof(*data32)) ||
+	    put_user(data->flags, &data32->flags) ||
+	    put_user(data->time_queue, &data32->time_queue))
+		err = -EFAULT;
+
+ error:
+	kfree(data);
+	return err;
+}
+
+
+
+/*
+ */
+
+enum {
+	SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32),
+	SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32),
+	SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32),
+	SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32),
+	SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32),
+};
+
+static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	client_t *client = (client_t *) file->private_data;
+	void __user *argp = compat_ptr(arg);
+
+	snd_assert(client != NULL, return -ENXIO);
+
+	switch (cmd) {
+	case SNDRV_SEQ_IOCTL_PVERSION:
+	case SNDRV_SEQ_IOCTL_CLIENT_ID:
+	case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
+	case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
+	case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
+	case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
+	case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
+	case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
+	case SNDRV_SEQ_IOCTL_DELETE_QUEUE:
+	case SNDRV_SEQ_IOCTL_GET_QUEUE_INFO:
+	case SNDRV_SEQ_IOCTL_SET_QUEUE_INFO:
+	case SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE:
+	case SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS:
+	case SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO:
+	case SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO:
+	case SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER:
+	case SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER:
+	case SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT:
+	case SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT:
+	case SNDRV_SEQ_IOCTL_GET_CLIENT_POOL:
+	case SNDRV_SEQ_IOCTL_SET_CLIENT_POOL:
+	case SNDRV_SEQ_IOCTL_REMOVE_EVENTS:
+	case SNDRV_SEQ_IOCTL_QUERY_SUBS:
+	case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
+	case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
+	case SNDRV_SEQ_IOCTL_RUNNING_MODE:
+		return snd_seq_do_ioctl(client, cmd, argp);
+	case SNDRV_SEQ_IOCTL_CREATE_PORT32:
+		return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
+	case SNDRV_SEQ_IOCTL_DELETE_PORT32:
+		return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_DELETE_PORT, argp);
+	case SNDRV_SEQ_IOCTL_GET_PORT_INFO32:
+		return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_GET_PORT_INFO, argp);
+	case SNDRV_SEQ_IOCTL_SET_PORT_INFO32:
+		return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_SET_PORT_INFO, argp);
+	case SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32:
+		return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, argp);
+	}
+	return -ENOIOCTLCMD;
+}
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
new file mode 100644
index 0000000..4d80f39
--- /dev/null
+++ b/sound/core/seq/seq_device.c
@@ -0,0 +1,575 @@
+/*
+ *  ALSA sequencer device management
+ *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ *----------------------------------------------------------------
+ *
+ * This device handler separates the card driver module from sequencer
+ * stuff (sequencer core, synth drivers, etc), so that user can avoid
+ * to spend unnecessary resources e.g. if he needs only listening to
+ * MP3s.
+ *
+ * The card (or lowlevel) driver creates a sequencer device entry
+ * via snd_seq_device_new().  This is an entry pointer to communicate
+ * with the sequencer device "driver", which is involved with the
+ * actual part to communicate with the sequencer core.
+ * Each sequencer device entry has an id string and the corresponding
+ * driver with the same id is loaded when required.  For example,
+ * lowlevel codes to access emu8000 chip on sbawe card are included in
+ * emu8000-synth module.  To activate this module, the hardware
+ * resources like i/o port are passed via snd_seq_device argument.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/seq_device.h>
+#include <sound/seq_kernel.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer device management");
+MODULE_LICENSE("GPL");
+
+/*
+ * driver list
+ */
+typedef struct ops_list ops_list_t;
+
+/* driver state */
+#define DRIVER_EMPTY		0
+#define DRIVER_LOADED		(1<<0)
+#define DRIVER_REQUESTED	(1<<1)
+#define DRIVER_LOCKED		(1<<2)
+
+struct ops_list {
+	char id[ID_LEN];	/* driver id */
+	int driver;		/* driver state */
+	int used;		/* reference counter */
+	int argsize;		/* argument size */
+
+	/* operators */
+	snd_seq_dev_ops_t ops;
+
+	/* registred devices */
+	struct list_head dev_list;	/* list of devices */
+	int num_devices;	/* number of associated devices */
+	int num_init_devices;	/* number of initialized devices */
+	struct semaphore reg_mutex;
+
+	struct list_head list;	/* next driver */
+};
+
+
+static LIST_HEAD(opslist);
+static int num_ops;
+static DECLARE_MUTEX(ops_mutex);
+static snd_info_entry_t *info_entry = NULL;
+
+/*
+ * prototypes
+ */
+static int snd_seq_device_free(snd_seq_device_t *dev);
+static int snd_seq_device_dev_free(snd_device_t *device);
+static int snd_seq_device_dev_register(snd_device_t *device);
+static int snd_seq_device_dev_disconnect(snd_device_t *device);
+static int snd_seq_device_dev_unregister(snd_device_t *device);
+
+static int init_device(snd_seq_device_t *dev, ops_list_t *ops);
+static int free_device(snd_seq_device_t *dev, ops_list_t *ops);
+static ops_list_t *find_driver(char *id, int create_if_empty);
+static ops_list_t *create_driver(char *id);
+static void unlock_driver(ops_list_t *ops);
+static void remove_drivers(void);
+
+/*
+ * show all drivers and their status
+ */
+
+static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
+				ops->id,
+				ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
+				ops->driver & DRIVER_REQUESTED ? ",requested" : "",
+				ops->driver & DRIVER_LOCKED ? ",locked" : "",
+				ops->num_devices);
+	}
+	up(&ops_mutex);	
+}
+ 
+/*
+ * load all registered drivers (called from seq_clientmgr.c)
+ */
+
+#ifdef CONFIG_KMOD
+/* avoid auto-loading during module_init() */
+static int snd_seq_in_init;
+void snd_seq_autoload_lock(void)
+{
+	snd_seq_in_init++;
+}
+
+void snd_seq_autoload_unlock(void)
+{
+	snd_seq_in_init--;
+}
+#endif
+
+void snd_seq_device_load_drivers(void)
+{
+#ifdef CONFIG_KMOD
+	struct list_head *head;
+
+	/* Calling request_module during module_init()
+	 * may cause blocking.
+	 */
+	if (snd_seq_in_init)
+		return;
+
+	if (! current->fs->root)
+		return;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ! (ops->driver & DRIVER_REQUESTED)) {
+			ops->used++;
+			up(&ops_mutex);
+			ops->driver |= DRIVER_REQUESTED;
+			request_module("snd-%s", ops->id);
+			down(&ops_mutex);
+			ops->used--;
+		}
+	}
+	up(&ops_mutex);
+#endif
+}
+
+/*
+ * register a sequencer device
+ * card = card info (NULL allowed)
+ * device = device number (if any)
+ * id = id of driver
+ * result = return pointer (NULL allowed if unnecessary)
+ */
+int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize,
+		       snd_seq_device_t **result)
+{
+	snd_seq_device_t *dev;
+	ops_list_t *ops;
+	int err;
+	static snd_device_ops_t dops = {
+		.dev_free = snd_seq_device_dev_free,
+		.dev_register = snd_seq_device_dev_register,
+		.dev_disconnect = snd_seq_device_dev_disconnect,
+		.dev_unregister = snd_seq_device_dev_unregister
+	};
+
+	if (result)
+		*result = NULL;
+
+	snd_assert(id != NULL, return -EINVAL);
+
+	ops = find_driver(id, 1);
+	if (ops == NULL)
+		return -ENOMEM;
+
+	dev = kcalloc(1, sizeof(*dev)*2 + argsize, GFP_KERNEL);
+	if (dev == NULL) {
+		unlock_driver(ops);
+		return -ENOMEM;
+	}
+
+	/* set up device info */
+	dev->card = card;
+	dev->device = device;
+	strlcpy(dev->id, id, sizeof(dev->id));
+	dev->argsize = argsize;
+	dev->status = SNDRV_SEQ_DEVICE_FREE;
+
+	/* add this device to the list */
+	down(&ops->reg_mutex);
+	list_add_tail(&dev->list, &ops->dev_list);
+	ops->num_devices++;
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
+		snd_seq_device_free(dev);
+		return err;
+	}
+	
+	if (result)
+		*result = dev;
+
+	return 0;
+}
+
+/*
+ * free the existing device
+ */
+static int snd_seq_device_free(snd_seq_device_t *dev)
+{
+	ops_list_t *ops;
+
+	snd_assert(dev != NULL, return -EINVAL);
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+
+	/* remove the device from the list */
+	down(&ops->reg_mutex);
+	list_del(&dev->list);
+	ops->num_devices--;
+	up(&ops->reg_mutex);
+
+	free_device(dev, ops);
+	if (dev->private_free)
+		dev->private_free(dev);
+	kfree(dev);
+
+	unlock_driver(ops);
+
+	return 0;
+}
+
+static int snd_seq_device_dev_free(snd_device_t *device)
+{
+	snd_seq_device_t *dev = device->device_data;
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register the device
+ */
+static int snd_seq_device_dev_register(snd_device_t *device)
+{
+	snd_seq_device_t *dev = device->device_data;
+	ops_list_t *ops;
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENOENT;
+
+	/* initialize this device if the corresponding driver was
+	 * already loaded
+	 */
+	if (ops->driver & DRIVER_LOADED)
+		init_device(dev, ops);
+
+	unlock_driver(ops);
+	return 0;
+}
+
+/*
+ * disconnect the device
+ */
+static int snd_seq_device_dev_disconnect(snd_device_t *device)
+{
+	snd_seq_device_t *dev = device->device_data;
+	ops_list_t *ops;
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENOENT;
+
+	free_device(dev, ops);
+
+	unlock_driver(ops);
+	return 0;
+}
+
+/*
+ * unregister the existing device
+ */
+static int snd_seq_device_dev_unregister(snd_device_t *device)
+{
+	snd_seq_device_t *dev = device->device_data;
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register device driver
+ * id = driver id
+ * entry = driver operators - duplicated to each instance
+ */
+int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize)
+{
+	struct list_head *head;
+	ops_list_t *ops;
+
+	if (id == NULL || entry == NULL ||
+	    entry->init_device == NULL || entry->free_device == NULL)
+		return -EINVAL;
+
+	snd_seq_autoload_lock();
+	ops = find_driver(id, 1);
+	if (ops == NULL) {
+		snd_seq_autoload_unlock();
+		return -ENOMEM;
+	}
+	if (ops->driver & DRIVER_LOADED) {
+		snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
+		unlock_driver(ops);
+		snd_seq_autoload_unlock();
+		return -EBUSY;
+	}
+
+	down(&ops->reg_mutex);
+	/* copy driver operators */
+	ops->ops = *entry;
+	ops->driver |= DRIVER_LOADED;
+	ops->argsize = argsize;
+
+	/* initialize existing devices if necessary */
+	list_for_each(head, &ops->dev_list) {
+		snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
+		init_device(dev, ops);
+	}
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+	snd_seq_autoload_unlock();
+
+	return 0;
+}
+
+
+/*
+ * create driver record
+ */
+static ops_list_t * create_driver(char *id)
+{
+	ops_list_t *ops;
+
+	ops = kmalloc(sizeof(*ops), GFP_KERNEL);
+	if (ops == NULL)
+		return ops;
+	memset(ops, 0, sizeof(*ops));
+
+	/* set up driver entry */
+	strlcpy(ops->id, id, sizeof(ops->id));
+	init_MUTEX(&ops->reg_mutex);
+	ops->driver = DRIVER_EMPTY;
+	INIT_LIST_HEAD(&ops->dev_list);
+	/* lock this instance */
+	ops->used = 1;
+
+	/* register driver entry */
+	down(&ops_mutex);
+	list_add_tail(&ops->list, &opslist);
+	num_ops++;
+	up(&ops_mutex);
+
+	return ops;
+}
+
+
+/*
+ * unregister the specified driver
+ */
+int snd_seq_device_unregister_driver(char *id)
+{
+	struct list_head *head;
+	ops_list_t *ops;
+
+	ops = find_driver(id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+	if (! (ops->driver & DRIVER_LOADED) ||
+	    (ops->driver & DRIVER_LOCKED)) {
+		snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver);
+		unlock_driver(ops);
+		return -EBUSY;
+	}
+
+	/* close and release all devices associated with this driver */
+	down(&ops->reg_mutex);
+	ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
+	list_for_each(head, &ops->dev_list) {
+		snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
+		free_device(dev, ops);
+	}
+
+	ops->driver = 0;
+	if (ops->num_init_devices > 0)
+		snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices);
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+
+	/* remove empty driver entries */
+	remove_drivers();
+
+	return 0;
+}
+
+
+/*
+ * remove empty driver entries
+ */
+static void remove_drivers(void)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	head = opslist.next;
+	while (head != &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ops->used == 0 && ops->num_devices == 0) {
+			head = head->next;
+			list_del(&ops->list);
+			kfree(ops);
+			num_ops--;
+		} else
+			head = head->next;
+	}
+	up(&ops_mutex);
+}
+
+/*
+ * initialize the device - call init_device operator
+ */
+static int init_device(snd_seq_device_t *dev, ops_list_t *ops)
+{
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_FREE)
+		return 0; /* already initialized */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if (ops->ops.init_device(dev) >= 0) {
+		dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
+		ops->num_init_devices++;
+	} else {
+		snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * release the device - call free_device operator
+ */
+static int free_device(snd_seq_device_t *dev, ops_list_t *ops)
+{
+	int result;
+
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
+		return 0; /* not registered */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
+		dev->status = SNDRV_SEQ_DEVICE_FREE;
+		dev->driver_data = NULL;
+		ops->num_init_devices--;
+	} else {
+		snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * find the matching driver with given id
+ */
+static ops_list_t * find_driver(char *id, int create_if_empty)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		ops_list_t *ops = list_entry(head, ops_list_t, list);
+		if (strcmp(ops->id, id) == 0) {
+			ops->used++;
+			up(&ops_mutex);
+			return ops;
+		}
+	}
+	up(&ops_mutex);
+	if (create_if_empty)
+		return create_driver(id);
+	return NULL;
+}
+
+static void unlock_driver(ops_list_t *ops)
+{
+	down(&ops_mutex);
+	ops->used--;
+	up(&ops_mutex);
+}
+
+
+/*
+ * module part
+ */
+
+static int __init alsa_seq_device_init(void)
+{
+	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root);
+	if (info_entry == NULL)
+		return -ENOMEM;
+	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
+	info_entry->c.text.read_size = 2048;
+	info_entry->c.text.read = snd_seq_device_info;
+	if (snd_info_register(info_entry) < 0) {
+		snd_info_free_entry(info_entry);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void __exit alsa_seq_device_exit(void)
+{
+	remove_drivers();
+	snd_info_unregister(info_entry);
+	if (num_ops)
+		snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
+}
+
+module_init(alsa_seq_device_init)
+module_exit(alsa_seq_device_exit)
+
+EXPORT_SYMBOL(snd_seq_device_load_drivers);
+EXPORT_SYMBOL(snd_seq_device_new);
+EXPORT_SYMBOL(snd_seq_device_register_driver);
+EXPORT_SYMBOL(snd_seq_device_unregister_driver);
+#ifdef CONFIG_KMOD
+EXPORT_SYMBOL(snd_seq_autoload_lock);
+EXPORT_SYMBOL(snd_seq_autoload_unlock);
+#endif
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
new file mode 100644
index 0000000..e88967c
--- /dev/null
+++ b/sound/core/seq/seq_dummy.c
@@ -0,0 +1,273 @@
+/*
+ * ALSA sequencer MIDI-through client
+ * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+/*
+
+  Sequencer MIDI-through client
+
+  This gives a simple midi-through client.  All the normal input events
+  are redirected to output port immediately.
+  The routing can be done via aconnect program in alsa-utils.
+
+  Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
+  If you want to auto-load this module, you may add the following alias
+  in your /etc/conf.modules file.
+
+	alias snd-seq-client-62  snd-seq-dummy
+
+  The module is loaded on demand for client 62, or /proc/asound/seq/
+  is accessed.  If you don't need this module to be loaded, alias
+  snd-seq-client-62 as "off".  This will help modprobe.
+
+  The number of ports to be created can be specified via the module
+  parameter "ports".  For example, to create four ports, add the
+  following option in /etc/modprobe.conf:
+
+	option snd-seq-dummy ports=4
+
+  The modle option "duplex=1" enables duplex operation to the port.
+  In duplex mode, a pair of ports are created instead of single port,
+  and events are tunneled between pair-ports.  For example, input to
+  port A is sent to output port of another port B and vice versa.
+  In duplex mode, each port has DUPLEX capability.
+
+ */
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
+
+static int ports = 1;
+static int duplex = 0;
+
+module_param(ports, int, 0444);
+MODULE_PARM_DESC(ports, "number of ports to be created");
+module_param(duplex, bool, 0444);
+MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+
+typedef struct snd_seq_dummy_port {
+	int client;
+	int port;
+	int duplex;
+	int connect;
+} snd_seq_dummy_port_t;
+
+static int my_client = -1;
+
+/*
+ * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
+ * to subscribers.
+ * Note: this callback is called only after all subscribers are removed.
+ */
+static int
+dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_seq_dummy_port_t *p;
+	int i;
+	snd_seq_event_t ev;
+
+	p = private_data;
+	memset(&ev, 0, sizeof(ev));
+	if (p->duplex)
+		ev.source.port = p->connect;
+	else
+		ev.source.port = p->port;
+	ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+	for (i = 0; i < 16; i++) {
+		ev.data.control.channel = i;
+		ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+		ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+	}
+	return 0;
+}
+
+/*
+ * event input callback - just redirect events to subscribers
+ */
+static int
+dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+{
+	snd_seq_dummy_port_t *p;
+	snd_seq_event_t tmpev;
+
+	p = private_data;
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
+	    ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+		return 0; /* ignore system messages */
+	tmpev = *ev;
+	if (p->duplex)
+		tmpev.source.port = p->connect;
+	else
+		tmpev.source.port = p->port;
+	tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
+}
+
+/*
+ * free_private callback
+ */
+static void
+dummy_free(void *private_data)
+{
+	snd_seq_dummy_port_t *p;
+
+	p = private_data;
+	kfree(p);
+}
+
+/*
+ * create a port
+ */
+static snd_seq_dummy_port_t __init *
+create_port(int idx, int type)
+{
+	snd_seq_port_info_t pinfo;
+	snd_seq_port_callback_t pcb;
+	snd_seq_dummy_port_t *rec;
+
+	if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	rec->client = my_client;
+	rec->duplex = duplex;
+	rec->connect = 0;
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.addr.client = my_client;
+	if (duplex)
+		sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
+			(type ? 'B' : 'A'));
+	else
+		sprintf(pinfo.name, "Midi Through Port-%d", idx);
+	pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	if (duplex)
+		pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	memset(&pcb, 0, sizeof(pcb));
+	pcb.owner = THIS_MODULE;
+	pcb.unuse = dummy_unuse;
+	pcb.event_input = dummy_input;
+	pcb.private_free = dummy_free;
+	pcb.private_data = rec;
+	pinfo.kernel = &pcb;
+	if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
+		kfree(rec);
+		return NULL;
+	}
+	rec->port = pinfo.addr.port;
+	return rec;
+}
+
+/*
+ * register client and create ports
+ */
+static int __init
+register_client(void)
+{
+	snd_seq_client_callback_t cb;
+	snd_seq_client_info_t cinfo;
+	snd_seq_dummy_port_t *rec1, *rec2;
+	int i;
+
+	if (ports < 1) {
+		snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
+		return -EINVAL;
+	}
+
+	/* create client */
+	memset(&cb, 0, sizeof(cb));
+	cb.allow_input = 1;
+	cb.allow_output = 1;
+	my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb);
+	if (my_client < 0)
+		return my_client;
+
+	/* set client name */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = my_client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, "Midi Through");
+	snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	/* create ports */
+	for (i = 0; i < ports; i++) {
+		rec1 = create_port(i, 0);
+		if (rec1 == NULL) {
+			snd_seq_delete_kernel_client(my_client);
+			return -ENOMEM;
+		}
+		if (duplex) {
+			rec2 = create_port(i, 1);
+			if (rec2 == NULL) {
+				snd_seq_delete_kernel_client(my_client);
+				return -ENOMEM;
+			}
+			rec1->connect = rec2->port;
+			rec2->connect = rec1->port;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * delete client if exists
+ */
+static void __exit
+delete_client(void)
+{
+	if (my_client >= 0)
+		snd_seq_delete_kernel_client(my_client);
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_dummy_init(void)
+{
+	int err;
+	snd_seq_autoload_lock();
+	err = register_client();
+	snd_seq_autoload_unlock();
+	return err;
+}
+
+static void __exit alsa_seq_dummy_exit(void)
+{
+	delete_client();
+}
+
+module_init(alsa_seq_dummy_init)
+module_exit(alsa_seq_dummy_exit)
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
new file mode 100644
index 0000000..3b7647c
--- /dev/null
+++ b/sound/core/seq/seq_fifo.c
@@ -0,0 +1,264 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_fifo.h"
+#include "seq_lock.h"
+
+
+/* FIFO */
+
+/* create new fifo */
+fifo_t *snd_seq_fifo_new(int poolsize)
+{
+	fifo_t *f;
+
+	f = kcalloc(1, sizeof(*f), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("malloc failed for snd_seq_fifo_new() \n");
+		return NULL;
+	}
+
+	f->pool = snd_seq_pool_new(poolsize);
+	if (f->pool == NULL) {
+		kfree(f);
+		return NULL;
+	}
+	if (snd_seq_pool_init(f->pool) < 0) {
+		snd_seq_pool_delete(&f->pool);
+		kfree(f);
+		return NULL;
+	}
+
+	spin_lock_init(&f->lock);
+	snd_use_lock_init(&f->use_lock);
+	init_waitqueue_head(&f->input_sleep);
+	atomic_set(&f->overflow, 0);
+
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+void snd_seq_fifo_delete(fifo_t **fifo)
+{
+	fifo_t *f;
+
+	snd_assert(fifo != NULL, return);
+	f = *fifo;
+	snd_assert(f != NULL, return);
+	*fifo = NULL;
+
+	snd_seq_fifo_clear(f);
+
+	/* wake up clients if any */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	/* release resources...*/
+	/*....................*/
+
+	if (f->pool) {
+		snd_seq_pool_done(f->pool);
+		snd_seq_pool_delete(&f->pool);
+	}
+	
+	kfree(f);
+}
+
+static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f);
+
+/* clear queue */
+void snd_seq_fifo_clear(fifo_t *f)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+
+	/* clear overflow flag */
+	atomic_set(&f->overflow, 0);
+
+	snd_use_lock_sync(&f->use_lock);
+	spin_lock_irqsave(&f->lock, flags);
+	/* drain the fifo */
+	while ((cell = fifo_cell_out(f)) != NULL) {
+		snd_seq_cell_free(cell);
+	}
+	spin_unlock_irqrestore(&f->lock, flags);
+}
+
+
+/* enqueue event to fifo */
+int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+	int err;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	snd_use_lock_use(&f->use_lock);
+	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+	if (err < 0) {
+		if (err == -ENOMEM)
+			atomic_inc(&f->overflow);
+		snd_use_lock_free(&f->use_lock);
+		return err;
+	}
+		
+	/* append new cells to fifo */
+	spin_lock_irqsave(&f->lock, flags);
+	if (f->tail != NULL)
+		f->tail->next = cell;
+	f->tail = cell;
+	if (f->head == NULL)
+		f->head = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* wakeup client */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	snd_use_lock_free(&f->use_lock);
+
+	return 0; /* success */
+
+}
+
+/* dequeue cell from fifo */
+static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f)
+{
+	snd_seq_event_cell_t *cell;
+
+	if ((cell = f->head) != NULL) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	return cell;
+}
+
+/* dequeue cell from fifo and copy on user space */
+int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+	wait_queue_t wait;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	*cellp = NULL;
+	init_waitqueue_entry(&wait, current);
+	spin_lock_irqsave(&f->lock, flags);
+	while ((cell = fifo_cell_out(f)) == NULL) {
+		if (nonblock) {
+			/* non-blocking - return immediately */
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&f->input_sleep, &wait);
+		spin_unlock_irq(&f->lock);
+		schedule();
+		spin_lock_irq(&f->lock);
+		remove_wait_queue(&f->input_sleep, &wait);
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+	spin_unlock_irqrestore(&f->lock, flags);
+	*cellp = cell;
+
+	return 0;
+}
+
+
+void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell)
+{
+	unsigned long flags;
+
+	if (cell) {
+		spin_lock_irqsave(&f->lock, flags);
+		cell->next = f->head;
+		f->head = cell;
+		f->cells++;
+		spin_unlock_irqrestore(&f->lock, flags);
+	}
+}
+
+
+/* polling; return non-zero if queue is available */
+int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &f->input_sleep, wait);
+	return (f->cells > 0);
+}
+
+/* change the size of pool; all old events are removed */
+int snd_seq_fifo_resize(fifo_t *f, int poolsize)
+{
+	unsigned long flags;
+	pool_t *newpool, *oldpool;
+	snd_seq_event_cell_t *cell, *next, *oldhead;
+
+	snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+
+	/* allocate new pool */
+	newpool = snd_seq_pool_new(poolsize);
+	if (newpool == NULL)
+		return -ENOMEM;
+	if (snd_seq_pool_init(newpool) < 0) {
+		snd_seq_pool_delete(&newpool);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&f->lock, flags);
+	/* remember old pool */
+	oldpool = f->pool;
+	oldhead = f->head;
+	/* exchange pools */
+	f->pool = newpool;
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	/* NOTE: overflow flag is not cleared */
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* release cells in old pool */
+	for (cell = oldhead; cell; cell = next) {
+		next = cell->next;
+		snd_seq_cell_free(cell);
+	}
+	snd_seq_pool_delete(&oldpool);
+
+	return 0;
+}
diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h
new file mode 100644
index 0000000..d677c26
--- /dev/null
+++ b/sound/core/seq/seq_fifo.h
@@ -0,0 +1,72 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_FIFO_H
+#define __SND_SEQ_FIFO_H
+
+#include "seq_memory.h"
+#include "seq_lock.h"
+
+
+/* === FIFO === */
+
+typedef struct {
+	pool_t *pool;			/* FIFO pool */
+	snd_seq_event_cell_t* head;    	/* pointer to head of fifo */
+	snd_seq_event_cell_t* tail;    	/* pointer to tail of fifo */
+	int cells;
+	spinlock_t lock;
+	snd_use_lock_t use_lock;
+	wait_queue_head_t input_sleep;
+	atomic_t overflow;
+
+} fifo_t;
+
+/* create new fifo (constructor) */
+extern fifo_t *snd_seq_fifo_new(int poolsize);
+
+/* delete fifo (destructor) */
+extern void snd_seq_fifo_delete(fifo_t **f);
+
+
+/* enqueue event to fifo */
+extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event);
+
+/* lock fifo from release */
+#define snd_seq_fifo_lock(fifo)		snd_use_lock_use(&(fifo)->use_lock)
+#define snd_seq_fifo_unlock(fifo)	snd_use_lock_free(&(fifo)->use_lock)
+
+/* get a cell from fifo - fifo should be locked */
+int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock);
+
+/* free dequeued cell - fifo should be locked */
+extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell);
+
+/* clean up queue */
+extern void snd_seq_fifo_clear(fifo_t *f);
+
+/* polling */
+extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait);
+
+/* resize pool in fifo */
+int snd_seq_fifo_resize(fifo_t *f, int poolsize);
+
+
+#endif
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
new file mode 100644
index 0000000..b50b695
--- /dev/null
+++ b/sound/core/seq/seq_info.c
@@ -0,0 +1,75 @@
+/*
+ *   ALSA sequencer /proc interface
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+
+#include "seq_info.h"
+#include "seq_clientmgr.h"
+#include "seq_timer.h"
+
+
+static snd_info_entry_t *queues_entry;
+static snd_info_entry_t *clients_entry;
+static snd_info_entry_t *timer_entry;
+
+
+static snd_info_entry_t * __init
+create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *))
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root);
+	if (entry == NULL)
+		return NULL;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->c.text.read_size = size;
+	entry->c.text.read = read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return NULL;
+	}
+	return entry;
+}
+
+
+/* create all our /proc entries */
+int __init snd_seq_info_init(void)
+{
+	queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
+					 snd_seq_info_queues_read);
+	clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
+					  snd_seq_info_clients_read);
+	timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
+	return 0;
+}
+
+int __exit snd_seq_info_done(void)
+{
+	if (queues_entry)
+		snd_info_unregister(queues_entry);
+	if (clients_entry)
+		snd_info_unregister(clients_entry);
+	if (timer_entry)
+		snd_info_unregister(timer_entry);
+	return 0;
+}
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
new file mode 100644
index 0000000..efd099a
--- /dev/null
+++ b/sound/core/seq/seq_info.h
@@ -0,0 +1,36 @@
+/*
+ *   ALSA sequencer /proc info
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_INFO_H
+#define __SND_SEQ_INFO_H
+
+#include <sound/info.h>
+#include <sound/seq_kernel.h>
+
+void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+
+
+int snd_seq_info_init( void );
+int snd_seq_info_done( void );
+
+
+#endif
diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c
new file mode 100644
index 0000000..5b40ea2
--- /dev/null
+++ b/sound/core/seq/seq_instr.c
@@ -0,0 +1,653 @@
+/*
+ *   Generic Instrument routines for ALSA sequencer
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/seq_instr.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
+MODULE_LICENSE("GPL");
+
+
+static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_lock_irqsave(&list->ops_lock, list->ops_flags);
+	} else {
+		down(&list->ops_mutex);
+	}
+}
+
+static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
+	} else {
+		up(&list->ops_mutex);
+	}
+}
+
+static snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic)
+{
+	snd_seq_kinstr_t *instr;
+	
+	instr = kcalloc(1, sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
+	if (instr == NULL)
+		return NULL;
+	instr->add_len = add_len;
+	return instr;
+}
+
+static int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic)
+{
+	int result = 0;
+
+	if (instr == NULL)
+		return -EINVAL;
+	if (instr->ops && instr->ops->remove)
+		result = instr->ops->remove(instr->ops->private_data, instr, 1);
+	if (!result)
+		kfree(instr);
+	return result;
+}
+
+snd_seq_kinstr_list_t *snd_seq_instr_list_new(void)
+{
+	snd_seq_kinstr_list_t *list;
+
+	list = kcalloc(1, sizeof(snd_seq_kinstr_list_t), GFP_KERNEL);
+	if (list == NULL)
+		return NULL;
+	spin_lock_init(&list->lock);
+	spin_lock_init(&list->ops_lock);
+	init_MUTEX(&list->ops_mutex);
+	list->owner = -1;
+	return list;
+}
+
+void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr)
+{
+	snd_seq_kinstr_list_t *list;
+	snd_seq_kinstr_t *instr;
+	snd_seq_kcluster_t *cluster;
+	int idx;
+	unsigned long flags;
+
+	if (list_ptr == NULL)
+		return;
+	list = *list_ptr;
+	*list_ptr = NULL;
+	if (list == NULL)
+		return;
+	
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {		
+		while ((instr = list->hash[idx]) != NULL) {
+			list->hash[idx] = instr->next;
+			list->count--;
+			spin_lock_irqsave(&list->lock, flags);
+			while (instr->use) {
+				spin_unlock_irqrestore(&list->lock, flags);
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(1);
+				spin_lock_irqsave(&list->lock, flags);
+			}				
+			spin_unlock_irqrestore(&list->lock, flags);
+			if (snd_seq_instr_free(instr, 0)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+		}
+		while ((cluster = list->chash[idx]) != NULL) {
+			list->chash[idx] = cluster->next;
+			list->ccount--;
+			kfree(cluster);
+		}
+	}
+	kfree(list);
+}
+
+static int instr_free_compare(snd_seq_kinstr_t *instr,
+			      snd_seq_instr_header_t *ifree,
+			      unsigned int client)
+{
+	switch (ifree->cmd) {
+	case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
+		/* all, except private for other clients */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 0;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 1;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+			return 1;
+		}
+		if (((instr->instr.std >> 24) & 0xff) == client) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+		}
+		return 1;
+	}
+	return 1;
+}
+
+int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
+			         snd_seq_instr_header_t *ifree,
+			         int client,
+			         int atomic)
+{
+	snd_seq_kinstr_t *instr, *prev, *next, *flist;
+	int idx;
+	unsigned long flags;
+
+	snd_instr_lock_ops(list);
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[idx];
+		prev = flist = NULL;
+		while (instr) {
+			while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
+				prev = instr;
+				instr = instr->next;
+			}
+			if (instr == NULL)
+				continue;
+			if (instr->ops && instr->ops->notify)
+				instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+			next = instr->next;
+			if (prev == NULL) {
+				list->hash[idx] = next;
+			} else {
+				prev->next = next;
+			}
+			list->count--;
+			instr->next = flist;
+			flist = instr;
+			instr = next;
+		}
+		spin_unlock_irqrestore(&list->lock, flags);
+		while (flist) {
+			instr = flist;
+			flist = instr->next;
+			while (instr->use) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(1);
+			}				
+			if (snd_seq_instr_free(instr, atomic)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+			instr = next;
+		}
+	}
+	snd_instr_unlock_ops(list);
+	return 0;	
+}
+
+static int compute_hash_instr_key(snd_seq_instr_t *instr)
+{
+	int result;
+	
+	result = instr->bank | (instr->prg << 16);
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+
+#if 0
+static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
+{
+	int result;
+	
+	result = cluster;
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+#endif
+
+static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact)
+{
+	if (exact) {
+		if (i1->cluster != i2->cluster ||
+		    i1->bank != i2->bank ||
+		    i1->prg != i2->prg)
+			return 1;
+		if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
+			return 1;
+		if (!(i1->std & i2->std))
+			return 1;
+		return 0;
+	} else {
+		unsigned int client_check;
+		
+		if (i2->cluster && i1->cluster != i2->cluster)
+			return 1;
+		client_check = i2->std & 0xff000000;
+		if (client_check) {
+			if ((i1->std & 0xff000000) != client_check)
+				return 1;
+		} else {
+			if ((i1->std & i2->std) != i2->std)
+				return 1;
+		}
+		return i1->bank != i2->bank || i1->prg != i2->prg;
+	}
+}
+
+snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
+				     snd_seq_instr_t *instr,
+				     int exact,
+				     int follow_alias)
+{
+	unsigned long flags;
+	int depth = 0;
+	snd_seq_kinstr_t *result;
+
+	if (list == NULL || instr == NULL)
+		return NULL;
+	spin_lock_irqsave(&list->lock, flags);
+      __again:
+	result = list->hash[compute_hash_instr_key(instr)];
+	while (result) {
+		if (!compare_instr(&result->instr, instr, exact)) {
+			if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
+				instr = (snd_seq_instr_t *)KINSTR_DATA(result);
+				if (++depth > 10)
+					goto __not_found;
+				goto __again;
+			}
+			result->use++;
+			spin_unlock_irqrestore(&list->lock, flags);
+			return result;
+		}
+		result = result->next;
+	}
+      __not_found:
+	spin_unlock_irqrestore(&list->lock, flags);
+	return NULL;
+}
+
+void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
+			    snd_seq_kinstr_t *instr)
+{
+	unsigned long flags;
+
+	if (list == NULL || instr == NULL)
+		return;
+	spin_lock_irqsave(&list->lock, flags);
+	if (instr->use <= 0) {
+		snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
+	} else {
+		instr->use--;
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type)
+{
+	while (ops) {
+		if (!strcmp(ops->instr_type, instr_type))
+			return ops;
+		ops = ops->next;
+	}
+	return NULL;
+}
+
+static int instr_result(snd_seq_event_t *ev,
+			int type, int result,
+			int atomic)
+{
+	snd_seq_event_t sev;
+	
+	memset(&sev, 0, sizeof(sev));
+	sev.type = SNDRV_SEQ_EVENT_RESULT;
+	sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
+	            SNDRV_SEQ_PRIORITY_NORMAL;
+	sev.source = ev->dest;
+	sev.dest = ev->source;
+	sev.data.result.event = type;
+	sev.data.result.result = result;
+#if 0
+	printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
+				type, result,
+				sev.queue,
+				sev.source.client, sev.source.port,
+				sev.dest.client, sev.dest.port);
+#endif
+	return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
+}
+
+static int instr_begin(snd_seq_kinstr_ops_t *ops,
+		       snd_seq_kinstr_list_t *list,
+		       snd_seq_event_t *ev,
+		       int atomic, int hop)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner >= 0 && list->owner != ev->source.client) {
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
+	}
+	list->owner = ev->source.client;
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
+}
+
+static int instr_end(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+
+	/* TODO: timeout handling */
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner == ev->source.client) {
+		list->owner = -1;
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
+}
+
+static int instr_info(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_format_info(snd_seq_kinstr_ops_t *ops,
+			     snd_seq_kinstr_list_t *list,
+			     snd_seq_event_t *ev,
+			     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_reset(snd_seq_kinstr_ops_t *ops,
+		       snd_seq_kinstr_list_t *list,
+		       snd_seq_event_t *ev,
+		       int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_status(snd_seq_kinstr_ops_t *ops,
+			snd_seq_kinstr_list_t *list,
+			snd_seq_event_t *ev,
+			int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_put(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+	snd_seq_instr_header_t put;
+	snd_seq_kinstr_t *instr;
+	int result = -EINVAL, len, key;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
+		goto __return;
+	if (copy_from_user(&put, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	snd_instr_lock_ops(list);
+	if (put.id.instr.std & 0xff000000) {	/* private instrument */
+		put.id.instr.std &= 0x00ffffff;
+		put.id.instr.std |= (unsigned int)ev->source.client << 24;
+	}
+	if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
+		snd_seq_instr_free_use(list, instr);
+		snd_instr_unlock_ops(list);
+		result = -EBUSY;
+		goto __return;
+	}
+	ops = instr_ops(ops, put.data.data.format);
+	if (ops == NULL) {
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+	len = ops->add_len;
+	if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
+		len = sizeof(snd_seq_instr_t);
+	instr = snd_seq_instr_new(len, atomic);
+	if (instr == NULL) {
+		snd_instr_unlock_ops(list);
+		result = -ENOMEM;
+		goto __return;
+	}
+	instr->ops = ops;
+	instr->instr = put.id.instr;
+	strlcpy(instr->name, put.data.name, sizeof(instr->name));
+	instr->type = put.data.type;
+	if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
+		result = ops->put(ops->private_data,
+				  instr,
+				  (void __user *)ev->data.ext.ptr + sizeof(snd_seq_instr_header_t),
+				  ev->data.ext.len - sizeof(snd_seq_instr_header_t),
+				  atomic,
+				  put.cmd);
+		if (result < 0) {
+			snd_seq_instr_free(instr, atomic);
+			snd_instr_unlock_ops(list);
+			goto __return;
+		}
+	}
+	key = compute_hash_instr_key(&instr->instr);
+	spin_lock_irqsave(&list->lock, flags);
+	instr->next = list->hash[key];
+	list->hash[key] = instr;
+	list->count++;
+	spin_unlock_irqrestore(&list->lock, flags);
+	snd_instr_unlock_ops(list);
+	result = 0;
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
+	return result;
+}
+
+static int instr_get(snd_seq_kinstr_ops_t *ops,
+		     snd_seq_kinstr_list_t *list,
+		     snd_seq_event_t *ev,
+		     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_free(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	snd_seq_instr_header_t ifree;
+	snd_seq_kinstr_t *instr, *prev;
+	int result = -EINVAL;
+	unsigned long flags;
+	unsigned int hash;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
+		goto __return;
+	if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
+	    	result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
+	    	goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
+		if (ifree.id.instr.std & 0xff000000) {
+			ifree.id.instr.std &= 0x00ffffff;
+			ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
+		}
+		hash = compute_hash_instr_key(&ifree.id.instr);
+		snd_instr_lock_ops(list);
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[hash];
+		prev = NULL;
+		while (instr) {
+			if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
+				goto __free_single;
+			prev = instr;
+			instr = instr->next;
+		}
+		result = -ENOENT;
+		spin_unlock_irqrestore(&list->lock, flags);
+		snd_instr_unlock_ops(list);
+		goto __return;
+		
+	      __free_single:
+		if (prev) {
+			prev->next = instr->next;
+		} else {
+			list->hash[hash] = instr->next;
+		}
+		if (instr->ops && instr->ops->notify)
+			instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+		while (instr->use) {
+			spin_unlock_irqrestore(&list->lock, flags);
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(1);
+			spin_lock_irqsave(&list->lock, flags);
+		}				
+		spin_unlock_irqrestore(&list->lock, flags);
+		result = snd_seq_instr_free(instr, atomic);
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
+	return result;
+}
+
+static int instr_list(snd_seq_kinstr_ops_t *ops,
+		      snd_seq_kinstr_list_t *list,
+		      snd_seq_event_t *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_cluster(snd_seq_kinstr_ops_t *ops,
+			 snd_seq_kinstr_list_t *list,
+			 snd_seq_event_t *ev,
+			 int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
+			snd_seq_kinstr_list_t *list,
+			snd_seq_event_t *ev,
+			int client,
+			int atomic,
+			int hop)
+{
+	int direct = 0;
+
+	snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
+	if (snd_seq_ev_is_direct(ev)) {
+		direct = 1;
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_INSTR_BEGIN:
+			return instr_begin(ops, list, ev, atomic, hop);
+		case SNDRV_SEQ_EVENT_INSTR_END:
+			return instr_end(ops, list, ev, atomic, hop);
+		}
+	}
+	if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
+		return -EINVAL;
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_INSTR_INFO:
+		return instr_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FINFO:
+		return instr_format_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_RESET:
+		return instr_reset(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_STATUS:
+		return instr_status(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_PUT:
+		return instr_put(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_GET:
+		return instr_get(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FREE:
+		return instr_free(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_LIST:
+		return instr_list(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
+		return instr_cluster(ops, list, ev, atomic, hop);
+	}
+	return -EINVAL;
+}
+			
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_instr_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_instr_exit(void)
+{
+}
+
+module_init(alsa_seq_instr_init)
+module_exit(alsa_seq_instr_exit)
+
+EXPORT_SYMBOL(snd_seq_instr_list_new);
+EXPORT_SYMBOL(snd_seq_instr_list_free);
+EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
+EXPORT_SYMBOL(snd_seq_instr_find);
+EXPORT_SYMBOL(snd_seq_instr_free_use);
+EXPORT_SYMBOL(snd_seq_instr_event);
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
new file mode 100644
index 0000000..b09cee0
--- /dev/null
+++ b/sound/core/seq/seq_lock.c
@@ -0,0 +1,48 @@
+/*
+ *  Do sleep inside a spin-lock
+ *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include "seq_lock.h"
+
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
+{
+	int max_count = 5 * HZ;
+
+	if (atomic_read(lockp) < 0) {
+		printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
+		return;
+	}
+	while (atomic_read(lockp) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		max_count--;
+	}
+}
+
+#endif
diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h
new file mode 100644
index 0000000..54044bc
--- /dev/null
+++ b/sound/core/seq/seq_lock.h
@@ -0,0 +1,33 @@
+#ifndef __SND_SEQ_LOCK_H
+#define __SND_SEQ_LOCK_H
+
+#include <linux/sched.h>
+
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+
+typedef atomic_t snd_use_lock_t;
+
+/* initialize lock */
+#define snd_use_lock_init(lockp) atomic_set(lockp, 0)
+
+/* increment lock */
+#define snd_use_lock_use(lockp) atomic_inc(lockp)
+
+/* release lock */
+#define snd_use_lock_free(lockp) atomic_dec(lockp)
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
+#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
+
+#else /* SMP || CONFIG_SND_DEBUG */
+
+typedef spinlock_t snd_use_lock_t;	/* dummy */
+#define snd_use_lock_init(lockp) /**/
+#define snd_use_lock_use(lockp) /**/
+#define snd_use_lock_free(lockp) /**/
+#define snd_use_lock_sync(lockp) /**/
+
+#endif /* SMP || CONFIG_SND_DEBUG */
+
+#endif /* __SND_SEQ_LOCK_H */
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
new file mode 100644
index 0000000..00d841e
--- /dev/null
+++ b/sound/core/seq/seq_memory.c
@@ -0,0 +1,510 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *                2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+#include "seq_lock.h"
+
+/* semaphore in struct file record */
+#define semaphore_of(fp)	((fp)->f_dentry->d_inode->i_sem)
+
+
+inline static int snd_seq_pool_available(pool_t *pool)
+{
+	return pool->total_elements - atomic_read(&pool->counter);
+}
+
+inline static int snd_seq_output_ok(pool_t *pool)
+{
+	return snd_seq_pool_available(pool) >= pool->room;
+}
+
+/*
+ * Variable length event:
+ * The event like sysex uses variable length type.
+ * The external data may be stored in three different formats.
+ * 1) kernel space
+ *    This is the normal case.
+ *      ext.data.len = length
+ *      ext.data.ptr = buffer pointer
+ * 2) user space
+ *    When an event is generated via read(), the external data is
+ *    kept in user space until expanded.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_USRPTR
+ *      ext.data.ptr = userspace pointer
+ * 3) chained cells
+ *    When the variable length event is enqueued (in prioq or fifo),
+ *    the external data is decomposed to several cells.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_CHAINED
+ *      ext.data.ptr = the additiona cell head
+ *         -> cell.next -> cell.next -> ..
+ */
+
+/*
+ * exported:
+ * call dump function to expand external data.
+ */
+
+static int get_var_len(const snd_seq_event_t *event)
+{
+	if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+		return -EINVAL;
+
+	return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+}
+
+int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data)
+{
+	int len, err;
+	snd_seq_event_cell_t *cell;
+
+	if ((len = get_var_len(event)) <= 0)
+		return len;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		char buf[32];
+		char __user *curptr = (char __user *)event->data.ext.ptr;
+		while (len > 0) {
+			int size = sizeof(buf);
+			if (len < size)
+				size = len;
+			if (copy_from_user(buf, curptr, size))
+				return -EFAULT;
+			err = func(private_data, buf, size);
+			if (err < 0)
+				return err;
+			curptr += size;
+			len -= size;
+		}
+		return 0;
+	} if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
+		return func(private_data, event->data.ext.ptr, len);
+	}
+
+	cell = (snd_seq_event_cell_t*)event->data.ext.ptr;
+	for (; len > 0 && cell; cell = cell->next) {
+		int size = sizeof(snd_seq_event_t);
+		if (len < size)
+			size = len;
+		err = func(private_data, &cell->event, size);
+		if (err < 0)
+			return err;
+		len -= size;
+	}
+	return 0;
+}
+
+
+/*
+ * exported:
+ * expand the variable length event to linear buffer space.
+ */
+
+static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
+{
+	memcpy(*bufptr, src, size);
+	*bufptr += size;
+	return 0;
+}
+
+static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
+{
+	if (copy_to_user(*bufptr, src, size))
+		return -EFAULT;
+	*bufptr += size;
+	return 0;
+}
+
+int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned)
+{
+	int len, newlen;
+	int err;
+
+	if ((len = get_var_len(event)) < 0)
+		return len;
+	newlen = len;
+	if (size_aligned > 0)
+		newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
+	if (count < newlen)
+		return -EAGAIN;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		if (! in_kernel)
+			return -EINVAL;
+		if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
+			return -EFAULT;
+		return newlen;
+	}
+	err = snd_seq_dump_var_event(event,
+				     in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
+				     (snd_seq_dump_func_t)seq_copy_in_user,
+				     &buf);
+	return err < 0 ? err : newlen;
+}
+
+
+/*
+ * release this cell, free extended data if available
+ */
+
+static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell)
+{
+	cell->next = pool->free;
+	pool->free = cell;
+	atomic_dec(&pool->counter);
+}
+
+void snd_seq_cell_free(snd_seq_event_cell_t * cell)
+{
+	unsigned long flags;
+	pool_t *pool;
+
+	snd_assert(cell != NULL, return);
+	pool = cell->pool;
+	snd_assert(pool != NULL, return);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	free_cell(pool, cell);
+	if (snd_seq_ev_is_variable(&cell->event)) {
+		if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
+			snd_seq_event_cell_t *curp, *nextptr;
+			curp = cell->event.data.ext.ptr;
+			for (; curp; curp = nextptr) {
+				nextptr = curp->next;
+				curp->next = pool->free;
+				free_cell(pool, curp);
+			}
+		}
+	}
+	if (waitqueue_active(&pool->output_sleep)) {
+		/* has enough space now? */
+		if (snd_seq_output_ok(pool))
+			wake_up(&pool->output_sleep);
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+
+/*
+ * allocate an event cell.
+ */
+static int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+	int err = -EAGAIN;
+	wait_queue_t wait;
+
+	if (pool == NULL)
+		return -EINVAL;
+
+	*cellp = NULL;
+
+	init_waitqueue_entry(&wait, current);
+	spin_lock_irqsave(&pool->lock, flags);
+	if (pool->ptr == NULL) {	/* not initialized */
+		snd_printd("seq: pool is not initialized\n");
+		err = -EINVAL;
+		goto __error;
+	}
+	while (pool->free == NULL && ! nonblock && ! pool->closing) {
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&pool->output_sleep, &wait);
+		spin_unlock_irq(&pool->lock);
+		schedule();
+		spin_lock_irq(&pool->lock);
+		remove_wait_queue(&pool->output_sleep, &wait);
+		/* interrupted? */
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto __error;
+		}
+	}
+	if (pool->closing) { /* closing.. */
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	cell = pool->free;
+	if (cell) {
+		int used;
+		pool->free = cell->next;
+		atomic_inc(&pool->counter);
+		used = atomic_read(&pool->counter);
+		if (pool->max_used < used)
+			pool->max_used = used;
+		pool->event_alloc_success++;
+		/* clear cell pointers */
+		cell->next = NULL;
+		err = 0;
+	} else
+		pool->event_alloc_failures++;
+	*cellp = cell;
+
+__error:
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return err;
+}
+
+
+/*
+ * duplicate the event to a cell.
+ * if the event has external data, the data is decomposed to additional
+ * cells.
+ */
+int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
+{
+	int ncells, err;
+	unsigned int extlen;
+	snd_seq_event_cell_t *cell;
+
+	*cellp = NULL;
+
+	ncells = 0;
+	extlen = 0;
+	if (snd_seq_ev_is_variable(event)) {
+		extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+		ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t);
+	}
+	if (ncells >= pool->total_elements)
+		return -ENOMEM;
+
+	err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+	if (err < 0)
+		return err;
+
+	/* copy the event */
+	cell->event = *event;
+
+	/* decompose */
+	if (snd_seq_ev_is_variable(event)) {
+		int len = extlen;
+		int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED;
+		int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR;
+		snd_seq_event_cell_t *src, *tmp, *tail;
+		char *buf;
+
+		cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED;
+		cell->event.data.ext.ptr = NULL;
+
+		src = (snd_seq_event_cell_t*)event->data.ext.ptr;
+		buf = (char *)event->data.ext.ptr;
+		tail = NULL;
+
+		while (ncells-- > 0) {
+			int size = sizeof(snd_seq_event_t);
+			if (len < size)
+				size = len;
+			err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+			if (err < 0)
+				goto __error;
+			if (cell->event.data.ext.ptr == NULL)
+				cell->event.data.ext.ptr = tmp;
+			if (tail)
+				tail->next = tmp;
+			tail = tmp;
+			/* copy chunk */
+			if (is_chained && src) {
+				tmp->event = src->event;
+				src = src->next;
+			} else if (is_usrptr) {
+				if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
+					err = -EFAULT;
+					goto __error;
+				}
+			} else {
+				memcpy(&tmp->event, buf, size);
+			}
+			buf += size;
+			len -= size;
+		}
+	}
+
+	*cellp = cell;
+	return 0;
+
+__error:
+	snd_seq_cell_free(cell);
+	return err;
+}
+  
+
+/* poll wait */
+int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait)
+{
+	poll_wait(file, &pool->output_sleep, wait);
+	return snd_seq_output_ok(pool);
+}
+
+
+/* allocate room specified number of events */
+int snd_seq_pool_init(pool_t *pool)
+{
+	int cell;
+	snd_seq_event_cell_t *cellptr;
+	unsigned long flags;
+
+	snd_assert(pool != NULL, return -EINVAL);
+	if (pool->ptr)			/* should be atomic? */
+		return 0;
+
+	pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size);
+	if (pool->ptr == NULL) {
+		snd_printd("seq: malloc for sequencer events failed\n");
+		return -ENOMEM;
+	}
+
+	/* add new cells to the free cell list */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->free = NULL;
+
+	for (cell = 0; cell < pool->size; cell++) {
+		cellptr = pool->ptr + cell;
+		cellptr->pool = pool;
+		cellptr->next = pool->free;
+		pool->free = cellptr;
+	}
+	pool->room = (pool->size + 1) / 2;
+
+	/* init statistics */
+	pool->max_used = 0;
+	pool->total_elements = pool->size;
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return 0;
+}
+
+/* remove events */
+int snd_seq_pool_done(pool_t *pool)
+{
+	unsigned long flags;
+	snd_seq_event_cell_t *ptr;
+	int max_count = 5 * HZ;
+
+	snd_assert(pool != NULL, return -EINVAL);
+
+	/* wait for closing all threads */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 1;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	if (waitqueue_active(&pool->output_sleep))
+		wake_up(&pool->output_sleep);
+
+	while (atomic_read(&pool->counter) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		max_count--;
+	}
+	
+	/* release all resources */
+	spin_lock_irqsave(&pool->lock, flags);
+	ptr = pool->ptr;
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	vfree(ptr);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	return 0;
+}
+
+
+/* init new memory pool */
+pool_t *snd_seq_pool_new(int poolsize)
+{
+	pool_t *pool;
+
+	/* create pool block */
+	pool = kcalloc(1, sizeof(*pool), GFP_KERNEL);
+	if (pool == NULL) {
+		snd_printd("seq: malloc failed for pool\n");
+		return NULL;
+	}
+	spin_lock_init(&pool->lock);
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	atomic_set(&pool->counter, 0);
+	pool->closing = 0;
+	init_waitqueue_head(&pool->output_sleep);
+	
+	pool->size = poolsize;
+
+	/* init statistics */
+	pool->max_used = 0;
+	return pool;
+}
+
+/* remove memory pool */
+int snd_seq_pool_delete(pool_t **ppool)
+{
+	pool_t *pool = *ppool;
+
+	*ppool = NULL;
+	if (pool == NULL)
+		return 0;
+	snd_seq_pool_done(pool);
+	kfree(pool);
+	return 0;
+}
+
+/* initialize sequencer memory */
+int __init snd_sequencer_memory_init(void)
+{
+	return 0;
+}
+
+/* release sequencer memory */
+void __exit snd_sequencer_memory_done(void)
+{
+}
+
+
+/* exported to seq_clientmgr.c */
+void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space)
+{
+	if (pool == NULL)
+		return;
+	snd_iprintf(buffer, "%sPool size          : %d\n", space, pool->total_elements);
+	snd_iprintf(buffer, "%sCells in use       : %d\n", space, atomic_read(&pool->counter));
+	snd_iprintf(buffer, "%sPeak cells in use  : %d\n", space, pool->max_used);
+	snd_iprintf(buffer, "%sAlloc success      : %d\n", space, pool->event_alloc_success);
+	snd_iprintf(buffer, "%sAlloc failures     : %d\n", space, pool->event_alloc_failures);
+}
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
new file mode 100644
index 0000000..6c4dde5
--- /dev/null
+++ b/sound/core/seq/seq_memory.h
@@ -0,0 +1,104 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_MEMORYMGR_H
+#define __SND_SEQ_MEMORYMGR_H
+
+#include <sound/seq_kernel.h>
+#include <linux/poll.h>
+
+typedef struct pool pool_t;
+
+/* container for sequencer event (internal use) */
+typedef struct snd_seq_event_cell_t {
+	snd_seq_event_t event;
+	pool_t *pool;				/* used pool */
+	struct snd_seq_event_cell_t *next;	/* next cell */
+} snd_seq_event_cell_t;
+
+/* design note: the pool is a contigious block of memory, if we dynamicly
+   want to add additional cells to the pool be better store this in another
+   pool as we need to know the base address of the pool when releasing
+   memory. */
+
+struct pool {
+	snd_seq_event_cell_t *ptr;	/* pointer to first event chunk */
+	snd_seq_event_cell_t *free;	/* pointer to the head of the free list */
+
+	int total_elements;	/* pool size actually allocated */
+	atomic_t counter;	/* cells free */
+
+	int size;		/* pool size to be allocated */
+	int room;		/* watermark for sleep/wakeup */
+
+	int closing;
+
+	/* statistics */
+	int max_used;
+	int event_alloc_nopool;
+	int event_alloc_failures;
+	int event_alloc_success;
+
+	/* Write locking */
+	wait_queue_head_t output_sleep;
+
+	/* Pool lock */
+	spinlock_t lock;
+};
+
+extern void snd_seq_cell_free(snd_seq_event_cell_t* cell);
+
+int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file);
+
+/* return number of unused (free) cells */
+static inline int snd_seq_unused_cells(pool_t *pool)
+{
+	return pool ? pool->total_elements - atomic_read(&pool->counter) : 0;
+}
+
+/* return total number of allocated cells */
+static inline int snd_seq_total_cells(pool_t *pool)
+{
+	return pool ? pool->total_elements : 0;
+}
+
+/* init pool - allocate events */
+int snd_seq_pool_init(pool_t *pool);
+
+/* done pool - free events */
+int snd_seq_pool_done(pool_t *pool);
+
+/* create pool */
+pool_t *snd_seq_pool_new(int poolsize);
+
+/* remove pool */
+int snd_seq_pool_delete(pool_t **pool);
+
+/* init memory */
+int snd_sequencer_memory_init(void);
+            
+/* release event memory */
+void snd_sequencer_memory_done(void);
+
+/* polling */
+int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait);
+
+
+#endif
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
new file mode 100644
index 0000000..18247db
--- /dev/null
+++ b/sound/core/seq/seq_midi.c
@@ -0,0 +1,489 @@
+/*
+ *   Generic MIDI synth driver for ALSA sequencer
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                         Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+/* 
+Possible options for midisynth module:
+	- automatic opening of midi ports on first received event or subscription
+	  (close will be performed when client leaves)
+*/
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/moduleparam.h>
+#include <asm/semaphore.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_device.h>
+#include <sound/seq_midi_event.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
+MODULE_LICENSE("GPL");
+static int output_buffer_size = PAGE_SIZE;
+module_param(output_buffer_size, int, 0644);
+MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
+static int input_buffer_size = PAGE_SIZE;
+module_param(input_buffer_size, int, 0644);
+MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
+
+/* data for this midi synth driver */
+typedef struct {
+	snd_card_t *card;
+	int device;
+	int subdevice;
+	snd_rawmidi_file_t input_rfile;
+	snd_rawmidi_file_t output_rfile;
+	int seq_client;
+	int seq_port;
+	snd_midi_event_t *parser;
+} seq_midisynth_t;
+
+typedef struct {
+	int seq_client;
+	int num_ports;
+	int ports_per_device[SNDRV_RAWMIDI_DEVICES];
+ 	seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES];
+} seq_midisynth_client_t;
+
+static seq_midisynth_client_t *synths[SNDRV_CARDS];
+static DECLARE_MUTEX(register_mutex);
+
+/* handle rawmidi input event (MIDI v1.0 stream) */
+static void snd_midi_input_event(snd_rawmidi_substream_t * substream)
+{
+	snd_rawmidi_runtime_t *runtime;
+	seq_midisynth_t *msynth;
+	snd_seq_event_t ev;
+	char buf[16], *pbuf;
+	long res, count;
+
+	if (substream == NULL)
+		return;
+	runtime = substream->runtime;
+	msynth = (seq_midisynth_t *) runtime->private_data;
+	if (msynth == NULL)
+		return;
+	memset(&ev, 0, sizeof(ev));
+	while (runtime->avail > 0) {
+		res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
+		if (res <= 0)
+			continue;
+		if (msynth->parser == NULL)
+			continue;
+		pbuf = buf;
+		while (res > 0) {
+			count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
+			if (count < 0)
+				break;
+			pbuf += count;
+			res -= count;
+			if (ev.type != SNDRV_SEQ_EVENT_NONE) {
+				ev.source.port = msynth->seq_port;
+				ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+				snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
+				/* clear event and reset header */
+				memset(&ev, 0, sizeof(ev));
+			}
+		}
+	}
+}
+
+static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count)
+{
+	snd_rawmidi_runtime_t *runtime;
+	int tmp;
+
+	snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	if ((tmp = runtime->avail) < count) {
+		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+		return -ENOMEM;
+	}
+	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
+		return -EINVAL;
+	return 0;
+}
+
+static int event_process_midi(snd_seq_event_t * ev, int direct,
+			      void *private_data, int atomic, int hop)
+{
+	seq_midisynth_t *msynth = (seq_midisynth_t *) private_data;
+	unsigned char msg[10];	/* buffer for constructing midi messages */
+	snd_rawmidi_substream_t *substream;
+	int res;
+
+	snd_assert(msynth != NULL, return -EINVAL);
+	substream = msynth->output_rfile.output;
+	if (substream == NULL)
+		return -ENODEV;
+	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {	/* special case, to save space */
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			/* invalid event */
+			snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+			return 0;
+		}
+		res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+		snd_midi_event_reset_decode(msynth->parser);
+		if (res < 0)
+			return res;
+	} else {
+		if (msynth->parser == NULL)
+			return -EIO;
+		res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
+		if (res < 0)
+			return res;
+		if ((res = dump_midi(substream, msg, res)) < 0) {
+			snd_midi_event_reset_decode(msynth->parser);
+			return res;
+		}
+	}
+	return 0;
+}
+
+
+static int snd_seq_midisynth_new(seq_midisynth_t *msynth,
+				 snd_card_t *card,
+				 int device,
+				 int subdevice)
+{
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
+		return -ENOMEM;
+	msynth->card = card;
+	msynth->device = device;
+	msynth->subdevice = subdevice;
+	return 0;
+}
+
+/* open associated midi device for input */
+static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	snd_rawmidi_runtime_t *runtime;
+	snd_rawmidi_params_t params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) {
+		snd_printd("midi input open failed!!!\n");
+		return err;
+	}
+	runtime = msynth->input_rfile.input->runtime;
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = input_buffer_size;
+	if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->input_rfile);
+		return err;
+	}
+	snd_midi_event_reset_encode(msynth->parser);
+	runtime->event = snd_midi_input_event;
+	runtime->private_data = msynth;
+	snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
+	return 0;
+}
+
+/* close associated midi device for input */
+static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+
+	snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+	err = snd_rawmidi_kernel_release(&msynth->input_rfile);
+	return err;
+}
+
+/* open associated midi device for output */
+static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	int err;
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	snd_rawmidi_params_t params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) {
+		snd_printd("midi output open failed!!!\n");
+		return err;
+	}
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = output_buffer_size;
+	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->output_rfile);
+		return err;
+	}
+	snd_midi_event_reset_decode(msynth->parser);
+	return 0;
+}
+
+/* close associated midi device for output */
+static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
+	unsigned char buf = 0xff; /* MIDI reset */
+
+	snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
+	/* sending single MIDI reset message to shut the device up */
+	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
+	snd_rawmidi_drain_output(msynth->output_rfile.output);
+	return snd_rawmidi_kernel_release(&msynth->output_rfile);
+}
+
+/* delete given midi synth port */
+static void snd_seq_midisynth_delete(seq_midisynth_t *msynth)
+{
+	if (msynth == NULL)
+		return;
+
+	if (msynth->seq_client > 0) {
+		/* delete port */
+		snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
+	}
+
+	if (msynth->parser)
+		snd_midi_event_free(msynth->parser);
+}
+
+/* set our client name */
+static int set_client_name(seq_midisynth_client_t *client, snd_card_t *card,
+			   snd_rawmidi_info_t *rmidi)
+{
+	snd_seq_client_info_t cinfo;
+	const char *name;
+
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client->seq_client;
+	cinfo.type = KERNEL_CLIENT;
+	name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
+	strlcpy(cinfo.name, name, sizeof(cinfo.name));
+	return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+}
+
+/* register new midi synth port */
+static int
+snd_seq_midisynth_register_port(snd_seq_device_t *dev)
+{
+	seq_midisynth_client_t *client;
+	seq_midisynth_t *msynth, *ms;
+	snd_seq_port_info_t *port;
+	snd_rawmidi_info_t *info;
+	int newclient = 0;
+	unsigned int p, ports;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_card_t *card = dev->card;
+	int device = dev->device;
+	unsigned int input_count = 0, output_count = 0;
+
+	snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (! info)
+		return -ENOMEM;
+	info->device = device;
+	info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+	info->subdevice = 0;
+	if (snd_rawmidi_info_select(card, info) >= 0)
+		output_count = info->subdevices_count;
+	info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+	if (snd_rawmidi_info_select(card, info) >= 0) {
+		input_count = info->subdevices_count;
+	}
+	ports = output_count;
+	if (ports < input_count)
+		ports = input_count;
+	if (ports == 0) {
+		kfree(info);
+		return -ENODEV;
+	}
+	if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
+		ports = 256 / SNDRV_RAWMIDI_DEVICES;
+
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL) {
+		newclient = 1;
+		client = kcalloc(1, sizeof(*client), GFP_KERNEL);
+		if (client == NULL) {
+			up(&register_mutex);
+			kfree(info);
+			return -ENOMEM;
+		}
+		memset(&callbacks, 0, sizeof(callbacks));
+		callbacks.private_data = client;
+		callbacks.allow_input = callbacks.allow_output = 1;
+		client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks);
+		if (client->seq_client < 0) {
+			kfree(client);
+			up(&register_mutex);
+			kfree(info);
+			return -ENOMEM;
+		}
+		set_client_name(client, card, info);
+	} else if (device == 0)
+		set_client_name(client, card, info); /* use the first device's name */
+
+	msynth = kcalloc(ports, sizeof(seq_midisynth_t), GFP_KERNEL);
+	port = kmalloc(sizeof(*port), GFP_KERNEL);
+	if (msynth == NULL || port == NULL)
+		goto __nomem;
+
+	for (p = 0; p < ports; p++) {
+		ms = &msynth[p];
+
+		if (snd_seq_midisynth_new(ms, card, device, p) < 0)
+			goto __nomem;
+
+		/* declare port */
+		memset(port, 0, sizeof(*port));
+		port->addr.client = client->seq_client;
+		port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
+		port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+		memset(info, 0, sizeof(*info));
+		info->device = device;
+		if (p < output_count)
+			info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+		else
+			info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+		info->subdevice = p;
+		if (snd_rawmidi_info_select(card, info) >= 0)
+			strcpy(port->name, info->subname);
+		if (! port->name[0]) {
+			if (info->name[0]) {
+				if (ports > 1)
+					snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
+				else
+					snprintf(port->name, sizeof(port->name), "%s", info->name);
+			} else {
+				/* last resort */
+				if (ports > 1)
+					sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+				else
+					sprintf(port->name, "MIDI %d-%d", card->number, device);
+			}
+		}
+		if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
+			port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+		if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
+			port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+		if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
+		    info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
+			port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+		port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+		port->midi_channels = 16;
+		memset(&pcallbacks, 0, sizeof(pcallbacks));
+		pcallbacks.owner = THIS_MODULE;
+		pcallbacks.private_data = ms;
+		pcallbacks.subscribe = midisynth_subscribe;
+		pcallbacks.unsubscribe = midisynth_unsubscribe;
+		pcallbacks.use = midisynth_use;
+		pcallbacks.unuse = midisynth_unuse;
+		pcallbacks.event_input = event_process_midi;
+		port->kernel = &pcallbacks;
+		if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
+			goto __nomem;
+		ms->seq_client = client->seq_client;
+		ms->seq_port = port->addr.port;
+	}
+	client->ports_per_device[device] = ports;
+	client->ports[device] = msynth;
+	client->num_ports++;
+	if (newclient)
+		synths[card->number] = client;
+	up(&register_mutex);
+	return 0;	/* success */
+
+      __nomem:
+	if (msynth != NULL) {
+	      	for (p = 0; p < ports; p++)
+	      		snd_seq_midisynth_delete(&msynth[p]);
+		kfree(msynth);
+	}
+	if (newclient) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		kfree(client);
+	}
+	kfree(info);
+	kfree(port);
+	up(&register_mutex);
+	return -ENOMEM;
+}
+
+/* release midi synth port */
+static int
+snd_seq_midisynth_unregister_port(snd_seq_device_t *dev)
+{
+	seq_midisynth_client_t *client;
+	seq_midisynth_t *msynth;
+	snd_card_t *card = dev->card;
+	int device = dev->device, p, ports;
+	
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL || client->ports[device] == NULL) {
+		up(&register_mutex);
+		return -ENODEV;
+	}
+	ports = client->ports_per_device[device];
+	client->ports_per_device[device] = 0;
+	msynth = client->ports[device];
+	client->ports[device] = NULL;
+	snd_runtime_check(msynth != NULL || ports <= 0, goto __skip);
+	for (p = 0; p < ports; p++)
+		snd_seq_midisynth_delete(&msynth[p]);
+	kfree(msynth);
+      __skip:
+	client->num_ports--;
+	if (client->num_ports <= 0) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		synths[card->number] = NULL;
+		kfree(client);
+	}
+	up(&register_mutex);
+	return 0;
+}
+
+
+static int __init alsa_seq_midi_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_seq_midisynth_register_port,
+		snd_seq_midisynth_unregister_port,
+	};
+	memset(&synths, 0, sizeof(synths));
+	snd_seq_autoload_lock();
+	snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
+	snd_seq_autoload_unlock();
+	return 0;
+}
+
+static void __exit alsa_seq_midi_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
+}
+
+module_init(alsa_seq_midi_init)
+module_exit(alsa_seq_midi_exit)
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
new file mode 100644
index 0000000..35fe8a7
--- /dev/null
+++ b/sound/core/seq/seq_midi_emul.c
@@ -0,0 +1,735 @@
+/*
+ *  GM/GS/XG midi module.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *  Based on awe_wave.c by Takashi Iwai
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+/*
+ * This module is used to keep track of the current midi state.
+ * It can be used for drivers that are required to emulate midi when
+ * the hardware doesn't.
+ *
+ * It was written for a AWE64 driver, but there should be no AWE specific
+ * code in here.  If there is it should be reported as a bug.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_emul.h>
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation.");
+MODULE_LICENSE("GPL");
+
+/* Prototypes for static functions */
+static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel);
+static void do_control(snd_midi_op_t *ops, void *private,
+		       snd_midi_channel_set_t *chset, snd_midi_channel_t *chan,
+		       int control, int value);
+static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset);
+static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
+static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
+static void snd_midi_reset_controllers(snd_midi_channel_t *chan);
+static void reset_all_channels(snd_midi_channel_set_t *chset);
+
+
+/*
+ * Process an event in a driver independent way.  This means dealing
+ * with RPN, NRPN, SysEx etc that are defined for common midi applications
+ * such as GM, GS and XG.
+ * There modes that this module will run in are:
+ *   Generic MIDI - no interpretation at all, it will just save current values
+ *                  of controlers etc.
+ *   GM - You can use all gm_ prefixed elements of chan.  Controls, RPN, NRPN,
+ *        SysEx will be interpreded as defined in General Midi.
+ *   GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
+ *        interpreted.
+ *   XG - You can use all xg_ prefixed elements of chan.  Codes for XG will
+ *        be interpreted.
+ */
+void
+snd_midi_process_event(snd_midi_op_t *ops,
+		       snd_seq_event_t *ev, snd_midi_channel_set_t *chanset)
+{
+	snd_midi_channel_t *chan;
+	void *drv;
+	int dest_channel = 0;
+
+	if (ev == NULL || chanset == NULL) {
+		snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
+		return;
+	}
+	if (chanset->channels == NULL)
+		return;
+
+	if (snd_seq_ev_is_channel_type(ev)) {
+		dest_channel = ev->data.note.channel;
+		if (dest_channel >= chanset->max_channels) {
+			snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels);
+			return;
+		}
+	}
+
+	chan = chanset->channels + dest_channel;
+	drv  = chanset->private_data;
+
+	/* EVENT_NOTE should be processed before queued */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTE)
+		return;
+
+	/* Make sure that we don't have a note on that should really be
+	 * a note off */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+
+	/* Make sure the note is within array range */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
+	    ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
+	    ev->type == SNDRV_SEQ_EVENT_KEYPRESS) {
+		if (ev->data.note.note >= 128)
+			return;
+	}
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) {
+			if (ops->note_off)
+				ops->note_off(drv, ev->data.note.note, 0, chan);
+		}
+		chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON;
+		if (ops->note_on)
+			ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON))
+			break;
+		if (ops->note_off)
+			note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		if (ops->key_press)
+			ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		do_control(ops, drv, chanset, chan,
+			   ev->data.control.param, ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		chan->midi_program = ev->data.control.value;
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		chan->midi_pitchbend = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_PITCHBEND, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		chan->midi_pressure = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROL14:
+		/* Best guess is that this is any of the 14 bit controller values */
+		if (ev->data.control.param < 32) {
+			/* set low part first */
+			chan->control[ev->data.control.param + 32] =
+				ev->data.control.value & 0x7f;
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ((ev->data.control.value>>7) & 0x7f));
+		} else
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_NONREGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		nrpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_REGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		rpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			unsigned char sysexbuf[64];
+			int len;
+			len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0);
+			if (len > 0)
+				sysex(ops, drv, sysexbuf, len, chanset);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+	case SNDRV_SEQ_EVENT_SONGSEL:
+	case SNDRV_SEQ_EVENT_CLOCK:
+	case SNDRV_SEQ_EVENT_START:
+	case SNDRV_SEQ_EVENT_CONTINUE:
+	case SNDRV_SEQ_EVENT_STOP:
+	case SNDRV_SEQ_EVENT_QFRAME:
+	case SNDRV_SEQ_EVENT_TEMPO:
+	case SNDRV_SEQ_EVENT_TIMESIGN:
+	case SNDRV_SEQ_EVENT_KEYSIGN:
+		goto not_yet;
+	case SNDRV_SEQ_EVENT_SENSING:
+		break;
+	case SNDRV_SEQ_EVENT_CLIENT_START:
+	case SNDRV_SEQ_EVENT_CLIENT_EXIT:
+	case SNDRV_SEQ_EVENT_CLIENT_CHANGE:
+	case SNDRV_SEQ_EVENT_PORT_START:
+	case SNDRV_SEQ_EVENT_PORT_EXIT:
+	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+	case SNDRV_SEQ_EVENT_SAMPLE:
+	case SNDRV_SEQ_EVENT_SAMPLE_START:
+	case SNDRV_SEQ_EVENT_SAMPLE_STOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
+	case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
+	case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
+	case SNDRV_SEQ_EVENT_ECHO:
+	not_yet:
+	default:
+		/*snd_printd("Unimplemented event %d\n", ev->type);*/
+		break;
+	}
+}
+
+
+/*
+ * release note
+ */
+static void
+note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel)
+{
+	if (chan->gm_hold) {
+		/* Hold this note until pedal is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else if (chan->note[note] & SNDRV_MIDI_NOTE_SOSTENUTO) {
+		/* Mark this note as release; it will be turned off when sostenuto
+		 * is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else {
+		chan->note[note] = 0;
+		if (ops->note_off)
+			ops->note_off(drv, note, vel, chan);
+	}
+}
+
+/*
+ * Do all driver independent operations for this controler and pass
+ * events that need to take place immediately to the driver.
+ */
+static void
+do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset,
+	   snd_midi_channel_t *chan, int control, int value)
+{
+	int  i;
+
+	/* Switches */
+	if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
+		/* These are all switches; either off or on so set to 0 or 127 */
+		value = (value >= 64)? 127: 0;
+	}
+	chan->control[control] = value;
+
+	switch (control) {
+	case MIDI_CTL_SUSTAIN:
+		if (value == 0) {
+			/* Sustain has been released, turn off held notes */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+					chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+					if (ops->note_off)
+						ops->note_off(drv, i, 0, chan);
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_PORTAMENTO:
+		break;
+	case MIDI_CTL_SOSTENUTO:
+		if (value) {
+			/* Mark each note that is currently held down */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
+					chan->note[i] |= SNDRV_MIDI_NOTE_SOSTENUTO;
+			}
+		} else {
+			/* release all notes that were held */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_SOSTENUTO) {
+					chan->note[i] &= ~SNDRV_MIDI_NOTE_SOSTENUTO;
+					if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+						chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+						if (ops->note_off)
+							ops->note_off(drv, i, 0, chan);
+					}
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_MSB_DATA_ENTRY:
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
+		/* go through here */
+	case MIDI_CTL_LSB_DATA_ENTRY:
+		if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
+			rpn(ops, drv, chan, chset);
+		else
+			nrpn(ops, drv, chan, chset);
+		break;
+	case MIDI_CTL_REGIST_PARM_NUM_LSB:
+	case MIDI_CTL_REGIST_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		break;
+	case MIDI_CTL_NONREG_PARM_NUM_LSB:
+	case MIDI_CTL_NONREG_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		break;
+
+	case MIDI_CTL_ALL_SOUNDS_OFF:
+		all_sounds_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_ALL_NOTES_OFF:
+		all_notes_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_MSB_BANK:
+		if (chset->midi_mode == SNDRV_MIDI_MODE_XG) {
+			if (value == 127)
+				chan->drum_channel = 1;
+			else
+				chan->drum_channel = 0;
+		}
+		break;
+	case MIDI_CTL_LSB_BANK:
+		break;
+
+	case MIDI_CTL_RESET_CONTROLLERS:
+		snd_midi_reset_controllers(chan);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+	case MIDI_CTL_LEGATO_FOOTSWITCH:
+	case MIDI_CTL_HOLD2:
+	case MIDI_CTL_SC1_SOUND_VARIATION:
+	case MIDI_CTL_SC2_TIMBRE:
+	case MIDI_CTL_SC3_RELEASE_TIME:
+	case MIDI_CTL_SC4_ATTACK_TIME:
+	case MIDI_CTL_SC5_BRIGHTNESS:
+	case MIDI_CTL_E1_REVERB_DEPTH:
+	case MIDI_CTL_E2_TREMOLO_DEPTH:
+	case MIDI_CTL_E3_CHORUS_DEPTH:
+	case MIDI_CTL_E4_DETUNE_DEPTH:
+	case MIDI_CTL_E5_PHASER_DEPTH:
+		goto notyet;
+	notyet:
+	default:
+		if (ops->control)
+			ops->control(drv, control, chan);
+		break;
+	}
+}
+
+
+/*
+ * initialize the MIDI status
+ */
+void
+snd_midi_channel_set_clear(snd_midi_channel_set_t *chset)
+{
+	int i;
+
+	chset->midi_mode = SNDRV_MIDI_MODE_GM;
+	chset->gs_master_volume = 127;
+
+	for (i = 0; i < chset->max_channels; i++) {
+		snd_midi_channel_t *chan = chset->channels + i;
+		memset(chan->note, 0, sizeof(chan->note));
+
+		chan->midi_aftertouch = 0;
+		chan->midi_pressure = 0;
+		chan->midi_program = 0;
+		chan->midi_pitchbend = 0;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (i == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+/*
+ * Process a rpn message.
+ */
+static void
+rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
+    snd_midi_channel_set_t *chset)
+{
+	int type;
+	int val;
+
+	if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) {
+		type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) |
+			chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB];
+		val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+			chan->control[MIDI_CTL_LSB_DATA_ENTRY];
+
+		switch (type) {
+		case 0x0000: /* Pitch bend sensitivity */
+			/* MSB only / 1 semitone per 128 */
+			chan->gm_rpn_pitch_bend_range = val;
+			break;
+					
+		case 0x0001: /* fine tuning: */
+			/* MSB/LSB, 8192=center, 100/8192 cent step */
+			chan->gm_rpn_fine_tuning = val - 8192;
+			break;
+
+		case 0x0002: /* coarse tuning */
+			/* MSB only / 8192=center, 1 semitone per 128 */
+			chan->gm_rpn_coarse_tuning = val - 8192;
+			break;
+
+		case 0x7F7F: /* "lock-in" RPN */
+			/* ignored */
+			break;
+		}
+	}
+	/* should call nrpn or rpn callback here.. */
+}
+
+/*
+ * Process an nrpn message.
+ */
+static void
+nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
+     snd_midi_channel_set_t *chset)
+{
+	/* parse XG NRPNs here if possible */
+	if (ops->nrpn)
+		ops->nrpn(drv, chan, chset);
+}
+
+
+/*
+ * convert channel parameter in GS sysex
+ */
+static int
+get_channel(unsigned char cmd)
+{
+	int p = cmd & 0x0f;
+	if (p == 0)
+		p = 9;
+	else if (p < 10)
+		p--;
+	return p;
+}
+
+
+/*
+ * Process a sysex message.
+ */
+static void
+sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset)
+{
+	/* GM on */
+	static unsigned char gm_on_macro[] = {
+		0x7e,0x7f,0x09,0x01,
+	};
+	/* XG on */
+	static unsigned char xg_on_macro[] = {
+		0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
+	};
+	/* GS prefix
+	 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
+	 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
+	 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
+	 * master vol:  XX=0x00, YY=0x04, ZZ=0-127
+	 */
+	static unsigned char gs_pfx_macro[] = {
+		0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
+	};
+
+	int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED;
+
+	if (len <= 0 || buf[0] != 0xf0)
+		return;
+	/* skip first byte */
+	buf++;
+	len--;
+
+	/* GM on */
+	if (len >= (int)sizeof(gm_on_macro) &&
+	    memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG) {
+			chset->midi_mode = SNDRV_MIDI_MODE_GM;
+			reset_all_channels(chset);
+			parsed = SNDRV_MIDI_SYSEX_GM_ON;
+		}
+	}
+
+	/* GS macros */
+	else if (len >= 8 &&
+		 memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG)
+			chset->midi_mode = SNDRV_MIDI_MODE_GS;
+
+		if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
+			/* GS reset */
+			parsed = SNDRV_MIDI_SYSEX_GS_RESET;
+			reset_all_channels(chset);
+		}
+
+		else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
+			/* drum pattern */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				if (buf[7])
+					chset->channels[p].drum_channel = 1;
+				else
+					chset->channels[p].drum_channel = 0;
+			}
+
+		} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
+			/* program */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels &&
+			    ! chset->channels[p].drum_channel) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				chset->channels[p].midi_program = buf[7];
+			}
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x30) {
+			/* reverb mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
+			chset->gs_reverb_mode = buf[7];
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x38) {
+			/* chorus mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE;
+			chset->gs_chorus_mode = buf[7];
+
+		} else if (buf[5] == 0x00 && buf[6] == 0x04) {
+			/* master volume */
+			parsed = SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME;
+			chset->gs_master_volume = buf[7];
+
+		}
+	}
+
+	/* XG on */
+	else if (len >= (int)sizeof(xg_on_macro) &&
+		 memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
+		int i;
+		chset->midi_mode = SNDRV_MIDI_MODE_XG;
+		parsed = SNDRV_MIDI_SYSEX_XG_ON;
+		/* reset CC#0 for drums */
+		for (i = 0; i < chset->max_channels; i++) {
+			if (chset->channels[i].drum_channel)
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127;
+			else
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0;
+		}
+	}
+
+	if (ops->sysex)
+		ops->sysex(private, buf - 1, len + 1, parsed, chset);
+}
+
+/*
+ * all sound off
+ */
+static void
+all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
+{
+	int n;
+
+	if (! ops->note_terminate)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n]) {
+			ops->note_terminate(drv, n, chan);
+			chan->note[n] = 0;
+		}
+	}
+}
+
+/*
+ * all notes off
+ */
+static void
+all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
+{
+	int n;
+
+	if (! ops->note_off)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n] == SNDRV_MIDI_NOTE_ON)
+			note_off(ops, drv, chan, n, 0);
+	}
+}
+
+/*
+ * Initialise a single midi channel control block.
+ */
+static void snd_midi_channel_init(snd_midi_channel_t *p, int n)
+{
+	if (p == NULL)
+		return;
+
+	memset(p, 0, sizeof(snd_midi_channel_t));
+	p->private = NULL;
+	p->number = n;
+
+	snd_midi_reset_controllers(p);
+	p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+	p->gm_rpn_fine_tuning = 0;
+	p->gm_rpn_coarse_tuning = 0;
+
+	if (n == 9)
+		p->drum_channel = 1;	/* Default ch 10 as drums */
+}
+
+/*
+ * Allocate and initialise a set of midi channel control blocks.
+ */
+static snd_midi_channel_t *snd_midi_channel_init_set(int n)
+{
+	snd_midi_channel_t *chan;
+	int  i;
+
+	chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL);
+	if (chan) {
+		for (i = 0; i < n; i++)
+			snd_midi_channel_init(chan+i, i);
+	}
+
+	return chan;
+}
+
+/*
+ * reset all midi channels
+ */
+static void
+reset_all_channels(snd_midi_channel_set_t *chset)
+{
+	int ch;
+	for (ch = 0; ch < chset->max_channels; ch++) {
+		snd_midi_channel_t *chan = chset->channels + ch;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (ch == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+
+/*
+ * Allocate and initialise a midi channel set.
+ */
+snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n)
+{
+	snd_midi_channel_set_t *chset;
+
+	chset = kmalloc(sizeof(*chset), GFP_KERNEL);
+	if (chset) {
+		chset->channels = snd_midi_channel_init_set(n);
+		chset->private_data = NULL;
+		chset->max_channels = n;
+	}
+	return chset;
+}
+
+/*
+ * Reset the midi controllers on a particular channel to default values.
+ */
+static void snd_midi_reset_controllers(snd_midi_channel_t *chan)
+{
+	memset(chan->control, 0, sizeof(chan->control));
+	chan->gm_volume = 127;
+	chan->gm_expression = 127;
+	chan->gm_pan = 64;
+}
+
+
+/*
+ * Free a midi channel set.
+ */
+void snd_midi_channel_free_set(snd_midi_channel_set_t *chset)
+{
+	if (chset == NULL)
+		return;
+	kfree(chset->channels);
+	kfree(chset);
+}
+
+static int __init alsa_seq_midi_emul_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_midi_emul_exit(void)
+{
+}
+
+module_init(alsa_seq_midi_emul_init)
+module_exit(alsa_seq_midi_emul_exit)
+
+EXPORT_SYMBOL(snd_midi_process_event);
+EXPORT_SYMBOL(snd_midi_channel_set_clear);
+EXPORT_SYMBOL(snd_midi_channel_alloc_set);
+EXPORT_SYMBOL(snd_midi_channel_free_set);
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
new file mode 100644
index 0000000..21e5690
--- /dev/null
+++ b/sound/core/seq/seq_midi_event.c
@@ -0,0 +1,539 @@
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
+MODULE_LICENSE("GPL");
+
+/* queue type */
+/* from 0 to 7 are normal commands (note off, on, etc.) */
+#define ST_NOTEOFF	0
+#define ST_NOTEON	1
+#define ST_SPECIAL	8
+#define ST_SYSEX	ST_SPECIAL
+/* from 8 to 15 are events for 0xf0-0xf7 */
+
+
+/* status event types */
+typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev);
+typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * prototypes
+ */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void note_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf);
+static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * event list
+ */
+static struct status_event_list_t {
+	int event;
+	int qlen;
+	event_encode_t encode;
+	event_decode_t decode;
+} status_event[] = {
+	/* 0x80 - 0xf0 */
+	{SNDRV_SEQ_EVENT_NOTEOFF,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_NOTEON,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
+	{SNDRV_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0xf0 - 0xff */
+	{SNDRV_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
+	{SNDRV_SEQ_EVENT_QFRAME,	1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SNDRV_SEQ_EVENT_SONGPOS,	2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SNDRV_SEQ_EVENT_SONGSEL,	1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
+	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
+	{SNDRV_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
+	{SNDRV_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
+	{SNDRV_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
+	{SNDRV_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
+	{SNDRV_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
+	{SNDRV_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
+	{SNDRV_SEQ_EVENT_RESET, 	0, NULL, NULL}, /* 0xff */
+};
+
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
+static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev);
+
+static struct extra_event_list_t {
+	int event;
+	int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
+} extra_event[] = {
+	{SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
+	{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_xrpn},
+	{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_xrpn},
+};
+
+/*
+ *  new/delete record
+ */
+
+int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev)
+{
+	snd_midi_event_t *dev;
+
+	*rdev = NULL;
+	dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	if (bufsize > 0) {
+		dev->buf = kmalloc(bufsize, GFP_KERNEL);
+		if (dev->buf == NULL) {
+			kfree(dev);
+			return -ENOMEM;
+		}
+	}
+	dev->bufsize = bufsize;
+	dev->lastcmd = 0xff;
+	spin_lock_init(&dev->lock);
+	*rdev = dev;
+	return 0;
+}
+
+void snd_midi_event_free(snd_midi_event_t *dev)
+{
+	if (dev != NULL) {
+		kfree(dev->buf);
+		kfree(dev);
+	}
+}
+
+/*
+ * initialize record
+ */
+inline static void reset_encode(snd_midi_event_t *dev)
+{
+	dev->read = 0;
+	dev->qlen = 0;
+	dev->type = 0;
+}
+
+void snd_midi_event_reset_encode(snd_midi_event_t *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void snd_midi_event_reset_decode(snd_midi_event_t *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->lastcmd = 0xff;
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void snd_midi_event_init(snd_midi_event_t *dev)
+{
+	snd_midi_event_reset_encode(dev);
+	snd_midi_event_reset_decode(dev);
+}
+
+void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
+{
+	dev->nostat = on ? 1 : 0;
+}
+
+/*
+ * resize buffer
+ */
+int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
+{
+	unsigned char *new_buf, *old_buf;
+	unsigned long flags;
+
+	if (bufsize == dev->bufsize)
+		return 0;
+	new_buf = kmalloc(bufsize, GFP_KERNEL);
+	if (new_buf == NULL)
+		return -ENOMEM;
+	spin_lock_irqsave(&dev->lock, flags);
+	old_buf = dev->buf;
+	dev->buf = new_buf;
+	dev->bufsize = bufsize;
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	kfree(old_buf);
+	return 0;
+}
+
+/*
+ *  read bytes and encode to sequencer event if finished
+ *  return the size of encoded bytes
+ */
+long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
+{
+	long result = 0;
+	int rc;
+
+	ev->type = SNDRV_SEQ_EVENT_NONE;
+
+	while (count-- > 0) {
+		rc = snd_midi_event_encode_byte(dev, *buf++, ev);
+		result++;
+		if (rc < 0)
+			return rc;
+		else if (rc > 0)
+			return result;
+	}
+
+	return result;
+}
+
+/*
+ *  read one byte and encode to sequencer event:
+ *  return 1 if MIDI bytes are encoded to an event
+ *         0 data is not finished
+ *         negative for error
+ */
+int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	c &= 0xff;
+
+	if (c >= MIDI_CMD_COMMON_CLOCK) {
+		/* real-time event */
+		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		return 1;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->qlen > 0) {
+		/* rest of command */
+		dev->buf[dev->read++] = c;
+		if (dev->type != ST_SYSEX)
+			dev->qlen--;
+	} else {
+		/* new command */
+		dev->read = 1;
+		if (c & 0x80) {
+			dev->buf[0] = c;
+			if ((c & 0xf0) == 0xf0) /* special events */
+				dev->type = (c & 0x0f) + ST_SPECIAL;
+			else
+				dev->type = (c >> 4) & 0x07;
+			dev->qlen = status_event[dev->type].qlen;
+		} else {
+			/* process this byte as argument */
+			dev->buf[dev->read++] = c;
+			dev->qlen = status_event[dev->type].qlen - 1;
+		}
+	}
+	if (dev->qlen == 0) {
+		ev->type = status_event[dev->type].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		if (status_event[dev->type].encode) /* set data values */
+			status_event[dev->type].encode(dev, ev);
+		rc = 1;
+	} else 	if (dev->type == ST_SYSEX) {
+		if (c == MIDI_CMD_COMMON_SYSEX_END ||
+		    dev->read >= dev->bufsize) {
+			ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+			ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+			ev->type = SNDRV_SEQ_EVENT_SYSEX;
+			ev->data.ext.len = dev->read;
+			ev->data.ext.ptr = dev->buf;
+			if (c != MIDI_CMD_COMMON_SYSEX_END)
+				dev->read = 0; /* continue to parse */
+			else
+				reset_encode(dev); /* all parsed */
+			rc = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return rc;
+}
+
+/* encode note event */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.note.channel = dev->buf[0] & 0x0f;
+	ev->data.note.note = dev->buf[1];
+	ev->data.note.velocity = dev->buf[2];
+}
+
+/* encode one parameter controls */
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode pitch wheel change */
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
+}
+
+/* encode midi control change */
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.param = dev->buf[1];
+	ev->data.control.value = dev->buf[2];
+}
+
+/* encode one parameter value*/
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode song position */
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
+}
+
+/*
+ * decode from a sequencer event to midi bytes
+ * return the size of decoded midi events
+ */
+long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
+{
+	unsigned int cmd, type;
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return -ENOENT;
+
+	for (type = 0; type < ARRAY_SIZE(status_event); type++) {
+		if (ev->type == status_event[type].event)
+			goto __found;
+	}
+	for (type = 0; type < ARRAY_SIZE(extra_event); type++) {
+		if (ev->type == extra_event[type].event)
+			return extra_event[type].decode(dev, buf, count, ev);
+	}
+	return -ENOENT;
+
+      __found:
+	if (type >= ST_SPECIAL)
+		cmd = 0xf0 + (type - ST_SPECIAL);
+	else
+		/* data.note.channel and data.control.channel is identical */
+		cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
+
+
+	if (cmd == MIDI_CMD_COMMON_SYSEX) {
+		snd_midi_event_reset_decode(dev);
+		return snd_seq_expand_var_event(ev, count, buf, 1, 0);
+	} else {
+		int qlen;
+		unsigned char xbuf[4];
+		unsigned long flags;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
+			dev->lastcmd = cmd;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			xbuf[0] = cmd;
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 1);
+			qlen = status_event[type].qlen + 1;
+		} else {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 0);
+			qlen = status_event[type].qlen;
+		}
+		if (count < qlen)
+			return -ENOMEM;
+		memcpy(buf, xbuf, qlen);
+		return qlen;
+	}
+}
+
+
+/* decode note event */
+static void note_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.note.note & 0x7f;
+	buf[1] = ev->data.note.velocity & 0x7f;
+}
+
+/* decode one parameter controls */
+static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+}
+
+/* decode pitch wheel change */
+static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	int value = ev->data.control.value + 8192;
+	buf[0] = value & 0x7f;
+	buf[1] = (value >> 7) & 0x7f;
+}
+
+/* decode midi control change */
+static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.param & 0x7f;
+	buf[1] = ev->data.control.value & 0x7f;
+}
+
+/* decode song position */
+static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+	buf[1] = (ev->data.control.value >> 7) & 0x7f;
+}
+
+/* decode 14bit control */
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
+{
+	unsigned char cmd;
+	int idx = 0;
+
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	if (ev->data.control.param < 0x20) {
+		if (count < 4)
+			return -ENOMEM;
+		if (dev->nostat && count < 6)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 5)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param;
+		buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
+		if (dev->nostat)
+			buf[idx++] = cmd;
+		buf[idx++] = ev->data.control.param + 0x20;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	} else {
+		if (count < 2)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 3)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param & 0x7f;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	}
+	return idx;
+}
+
+/* decode reg/nonreg param */
+static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
+{
+	unsigned char cmd;
+	char *cbytes;
+	static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
+				       MIDI_CTL_NONREG_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	static char cbytes_rpn[4] =  { MIDI_CTL_REGIST_PARM_NUM_MSB,
+				       MIDI_CTL_REGIST_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	unsigned char bytes[4];
+	int idx = 0, i;
+
+	if (count < 8)
+		return -ENOMEM;
+	if (dev->nostat && count < 12)
+		return -ENOMEM;
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	bytes[0] = ev->data.control.param & 0x007f;
+	bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
+	bytes[2] = ev->data.control.value & 0x007f;
+	bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+	if (cmd != dev->lastcmd && !dev->nostat) {
+		if (count < 9)
+			return -ENOMEM;
+		buf[idx++] = dev->lastcmd = cmd;
+	}
+	cbytes = ev->type == SNDRV_SEQ_EVENT_NONREGPARAM ? cbytes_nrpn : cbytes_rpn;
+	for (i = 0; i < 4; i++) {
+		if (dev->nostat)
+			buf[idx++] = dev->lastcmd = cmd;
+		buf[idx++] = cbytes[i];
+		buf[idx++] = bytes[i];
+	}
+	return idx;
+}
+
+/*
+ *  exports
+ */
+ 
+EXPORT_SYMBOL(snd_midi_event_new);
+EXPORT_SYMBOL(snd_midi_event_free);
+EXPORT_SYMBOL(snd_midi_event_resize_buffer);
+EXPORT_SYMBOL(snd_midi_event_init);
+EXPORT_SYMBOL(snd_midi_event_reset_encode);
+EXPORT_SYMBOL(snd_midi_event_reset_decode);
+EXPORT_SYMBOL(snd_midi_event_no_status);
+EXPORT_SYMBOL(snd_midi_event_encode);
+EXPORT_SYMBOL(snd_midi_event_encode_byte);
+EXPORT_SYMBOL(snd_midi_event_decode);
+
+static int __init alsa_seq_midi_event_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_midi_event_exit(void)
+{
+}
+
+module_init(alsa_seq_midi_event_init)
+module_exit(alsa_seq_midi_event_exit)
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
new file mode 100644
index 0000000..b976951
--- /dev/null
+++ b/sound/core/seq/seq_ports.c
@@ -0,0 +1,674 @@
+/*
+ *   ALSA sequencer Ports
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                         Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_system.h"
+#include "seq_ports.h"
+#include "seq_clientmgr.h"
+
+/*
+
+   registration of client ports
+
+ */
+
+
+/* 
+
+NOTE: the current implementation of the port structure as a linked list is
+not optimal for clients that have many ports. For sending messages to all
+subscribers of a port we first need to find the address of the port
+structure, which means we have to traverse the list. A direct access table
+(array) would be better, but big preallocated arrays waste memory.
+
+Possible actions:
+
+1) leave it this way, a client does normaly does not have more than a few
+ports
+
+2) replace the linked list of ports by a array of pointers which is
+dynamicly kmalloced. When a port is added or deleted we can simply allocate
+a new array, copy the corresponding pointers, and delete the old one. We
+then only need a pointer to this array, and an integer that tells us how
+much elements are in array.
+
+*/
+
+/* return pointer to port structure - port is locked if found */
+client_port_t *snd_seq_port_use_ptr(client_t *client, int num)
+{
+	struct list_head *p;
+	client_port_t *port;
+
+	if (client == NULL)
+		return NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, client_port_t, list);
+		if (port->addr.port == num) {
+			if (port->closing)
+				break; /* deleting now */
+			snd_use_lock_use(&port->use_lock);
+			read_unlock(&client->ports_lock);
+			return port;
+		}
+	}
+	read_unlock(&client->ports_lock);
+	return NULL;		/* not found */
+}
+
+
+/* search for the next port - port is locked if found */
+client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo)
+{
+	int num;
+	struct list_head *p;
+	client_port_t *port, *found;
+
+	num = pinfo->addr.port;
+	found = NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, client_port_t, list);
+		if (port->addr.port < num)
+			continue;
+		if (port->addr.port == num) {
+			found = port;
+			break;
+		}
+		if (found == NULL || port->addr.port < found->addr.port)
+			found = port;
+	}
+	if (found) {
+		if (found->closing)
+			found = NULL;
+		else
+			snd_use_lock_use(&found->use_lock);
+	}
+	read_unlock(&client->ports_lock);
+	return found;
+}
+
+
+/* initialize port_subs_info_t */
+static void port_subs_info_init(port_subs_info_t *grp)
+{
+	INIT_LIST_HEAD(&grp->list_head);
+	grp->count = 0;
+	grp->exclusive = 0;
+	rwlock_init(&grp->list_lock);
+	init_rwsem(&grp->list_mutex);
+	grp->open = NULL;
+	grp->close = NULL;
+}
+
+
+/* create a port, port number is returned (-1 on failure) */
+client_port_t *snd_seq_create_port(client_t *client, int port)
+{
+	unsigned long flags;
+	client_port_t *new_port;
+	struct list_head *l;
+	int num = -1;
+	
+	/* sanity check */
+	snd_assert(client, return NULL);
+
+	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
+		snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
+		return NULL;
+	}
+
+	/* create a new port */
+	new_port = kcalloc(1, sizeof(*new_port), GFP_KERNEL);
+	if (! new_port) {
+		snd_printd("malloc failed for registering client port\n");
+		return NULL;	/* failure, out of memory */
+	}
+	/* init port data */
+	new_port->addr.client = client->number;
+	new_port->addr.port = -1;
+	new_port->owner = THIS_MODULE;
+	sprintf(new_port->name, "port-%d", num);
+	snd_use_lock_init(&new_port->use_lock);
+	port_subs_info_init(&new_port->c_src);
+	port_subs_info_init(&new_port->c_dest);
+
+	num = port >= 0 ? port : 0;
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		if (p->addr.port > num)
+			break;
+		if (port < 0) /* auto-probe mode */
+			num = p->addr.port + 1;
+	}
+	/* insert the new port */
+	list_add_tail(&new_port->list, l);
+	client->num_ports++;
+	new_port->addr.port = num;	/* store the port number in the port */
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	sprintf(new_port->name, "port-%d", num);
+
+	return new_port;
+}
+
+/* */
+enum group_type_t {
+	SRC_LIST, DEST_LIST
+};
+
+static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
+static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
+
+
+static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp)
+{
+	client_port_t *p;
+	*cp = snd_seq_client_use_ptr(addr->client);
+	if (*cp) {
+		p = snd_seq_port_use_ptr(*cp, addr->port);
+		if (! p) {
+			snd_seq_client_unlock(*cp);
+			*cp = NULL;
+		}
+		return p;
+	}
+	return NULL;
+}
+
+/*
+ * remove all subscribers on the list
+ * this is called from port_delete, for each src and dest list.
+ */
+static void clear_subscriber_list(client_t *client, client_port_t *port,
+				  port_subs_info_t *grp, int grptype)
+{
+	struct list_head *p, *n;
+
+	down_write(&grp->list_mutex);
+	list_for_each_safe(p, n, &grp->list_head) {
+		subscribers_t *subs;
+		client_t *c;
+		client_port_t *aport;
+
+		if (grptype == SRC_LIST) {
+			subs = list_entry(p, subscribers_t, src_list);
+			aport = get_client_port(&subs->info.dest, &c);
+		} else {
+			subs = list_entry(p, subscribers_t, dest_list);
+			aport = get_client_port(&subs->info.sender, &c);
+		}
+		list_del(p);
+		unsubscribe_port(client, port, grp, &subs->info, 0);
+		if (!aport) {
+			/* looks like the connected port is being deleted.
+			 * we decrease the counter, and when both ports are deleted
+			 * remove the subscriber info
+			 */
+			if (atomic_dec_and_test(&subs->ref_count))
+				kfree(subs);
+		} else {
+			/* ok we got the connected port */
+			port_subs_info_t *agrp;
+			agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
+			down_write(&agrp->list_mutex);
+			if (grptype == SRC_LIST)
+				list_del(&subs->dest_list);
+			else
+				list_del(&subs->src_list);
+			unsubscribe_port(c, aport, agrp, &subs->info, 1);
+			kfree(subs);
+			up_write(&agrp->list_mutex);
+			snd_seq_port_unlock(aport);
+			snd_seq_client_unlock(c);
+		}
+	}
+	up_write(&grp->list_mutex);
+}
+
+/* delete port data */
+static int port_delete(client_t *client, client_port_t *port)
+{
+	/* set closing flag and wait for all port access are gone */
+	port->closing = 1;
+	snd_use_lock_sync(&port->use_lock); 
+
+	/* clear subscribers info */
+	clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
+	clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+
+	if (port->private_free)
+		port->private_free(port->private_data);
+
+	snd_assert(port->c_src.count == 0,);
+	snd_assert(port->c_dest.count == 0,);
+
+	kfree(port);
+	return 0;
+}
+
+
+/* delete a port with the given port id */
+int snd_seq_delete_port(client_t *client, int port)
+{
+	unsigned long flags;
+	struct list_head *l;
+	client_port_t *found = NULL;
+
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		client_port_t *p = list_entry(l, client_port_t, list);
+		if (p->addr.port == port) {
+			/* ok found.  delete from the list at first */
+			list_del(l);
+			client->num_ports--;
+			found = p;
+			break;
+		}
+	}
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	if (found)
+		return port_delete(client, found);
+	else
+		return -ENOENT;
+}
+
+/* delete the all ports belonging to the given client */
+int snd_seq_delete_all_ports(client_t *client)
+{
+	unsigned long flags;
+	struct list_head deleted_list, *p, *n;
+	
+	/* move the port list to deleted_list, and
+	 * clear the port list in the client data.
+	 */
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	if (! list_empty(&client->ports_list_head)) {
+		__list_add(&deleted_list,
+			   client->ports_list_head.prev,
+			   client->ports_list_head.next);
+		INIT_LIST_HEAD(&client->ports_list_head);
+	} else {
+		INIT_LIST_HEAD(&deleted_list);
+	}
+	client->num_ports = 0;
+	write_unlock_irqrestore(&client->ports_lock, flags);
+
+	/* remove each port in deleted_list */
+	list_for_each_safe(p, n, &deleted_list) {
+		client_port_t *port = list_entry(p, client_port_t, list);
+		list_del(p);
+		snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
+		port_delete(client, port);
+	}
+	up(&client->ports_mutex);
+	return 0;
+}
+
+/* set port info fields */
+int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* set port name */
+	if (info->name[0])
+		strlcpy(port->name, info->name, sizeof(port->name));
+	
+	/* set capabilities */
+	port->capability = info->capability;
+	
+	/* get port type */
+	port->type = info->type;
+
+	/* information about supported channels/voices */
+	port->midi_channels = info->midi_channels;
+	port->midi_voices = info->midi_voices;
+	port->synth_voices = info->synth_voices;
+
+	/* timestamping */
+	port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
+	port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
+	port->time_queue = info->time_queue;
+
+	return 0;
+}
+
+/* get port info fields */
+int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* get port name */
+	strlcpy(info->name, port->name, sizeof(info->name));
+	
+	/* get capabilities */
+	info->capability = port->capability;
+
+	/* get port type */
+	info->type = port->type;
+
+	/* information about supported channels/voices */
+	info->midi_channels = port->midi_channels;
+	info->midi_voices = port->midi_voices;
+	info->synth_voices = port->synth_voices;
+
+	/* get subscriber counts */
+	info->read_use = port->c_src.count;
+	info->write_use = port->c_dest.count;
+	
+	/* timestamping */
+	info->flags = 0;
+	if (port->timestamping) {
+		info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
+		if (port->time_real)
+			info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
+		info->time_queue = port->time_queue;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * call callback functions (if any):
+ * the callbacks are invoked only when the first (for connection) or
+ * the last subscription (for disconnection) is done.  Second or later
+ * subscription results in increment of counter, but no callback is
+ * invoked.
+ * This feature is useful if these callbacks are associated with
+ * initialization or termination of devices (see seq_midi.c).
+ *
+ * If callback_all option is set, the callback function is invoked
+ * at each connnection/disconnection. 
+ */
+
+static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp,
+			  snd_seq_port_subscribe_t *info, int send_ack)
+{
+	int err = 0;
+
+	if (!try_module_get(port->owner))
+		return -EFAULT;
+	grp->count++;
+	if (grp->open && (port->callback_all || grp->count == 1)) {
+		err = grp->open(port->private_data, info);
+		if (err < 0) {
+			module_put(port->owner);
+			grp->count--;
+		}
+	}
+	if (err >= 0 && send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+
+	return err;
+}
+
+static int unsubscribe_port(client_t *client, client_port_t *port,
+			    port_subs_info_t *grp,
+			    snd_seq_port_subscribe_t *info, int send_ack)
+{
+	int err = 0;
+
+	if (! grp->count)
+		return -EINVAL;
+	grp->count--;
+	if (grp->close && (port->callback_all || grp->count == 0))
+		err = grp->close(port->private_data, info);
+	if (send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+	module_put(port->owner);
+	return err;
+}
+
+
+
+/* check if both addresses are identical */
+static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s)
+{
+	return (r->client == s->client) && (r->port == s->port);
+}
+
+/* check the two subscribe info match */
+/* if flags is zero, checks only sender and destination addresses */
+static int match_subs_info(snd_seq_port_subscribe_t *r,
+			   snd_seq_port_subscribe_t *s)
+{
+	if (addr_match(&r->sender, &s->sender) &&
+	    addr_match(&r->dest, &s->dest)) {
+		if (r->flags && r->flags == s->flags)
+			return r->queue == s->queue;
+		else if (! r->flags)
+			return 1;
+	}
+	return 0;
+}
+
+
+/* connect two ports */
+int snd_seq_port_connect(client_t *connector,
+			 client_t *src_client, client_port_t *src_port,
+			 client_t *dest_client, client_port_t *dest_port,
+			 snd_seq_port_subscribe_t *info)
+{
+	port_subs_info_t *src = &src_port->c_src;
+	port_subs_info_t *dest = &dest_port->c_dest;
+	subscribers_t *subs;
+	struct list_head *p;
+	int err, src_called = 0;
+	unsigned long flags;
+	int exclusive;
+
+	subs = kcalloc(1, sizeof(*subs), GFP_KERNEL);
+	if (! subs)
+		return -ENOMEM;
+
+	subs->info = *info;
+	atomic_set(&subs->ref_count, 2);
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+	err = -EBUSY;
+	if (exclusive) {
+		if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+			goto __error;
+	} else {
+		if (src->exclusive || dest->exclusive)
+			goto __error;
+		/* check whether already exists */
+		list_for_each(p, &src->list_head) {
+			subscribers_t *s = list_entry(p, subscribers_t, src_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+		list_for_each(p, &dest->list_head) {
+			subscribers_t *s = list_entry(p, subscribers_t, dest_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+	}
+
+	if ((err = subscribe_port(src_client, src_port, src, info,
+				  connector->number != src_client->number)) < 0)
+		goto __error;
+	src_called = 1;
+
+	if ((err = subscribe_port(dest_client, dest_port, dest, info,
+				  connector->number != dest_client->number)) < 0)
+		goto __error;
+
+	/* add to list */
+	write_lock_irqsave(&src->list_lock, flags);
+	// write_lock(&dest->list_lock); // no other lock yet
+	list_add_tail(&subs->src_list, &src->list_head);
+	list_add_tail(&subs->dest_list, &dest->list_head);
+	// write_unlock(&dest->list_lock); // no other lock yet
+	write_unlock_irqrestore(&src->list_lock, flags);
+
+	src->exclusive = dest->exclusive = exclusive;
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return 0;
+
+ __error:
+	if (src_called)
+		unsubscribe_port(src_client, src_port, src, info,
+				 connector->number != src_client->number);
+	kfree(subs);
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* remove the connection */
+int snd_seq_port_disconnect(client_t *connector,
+			    client_t *src_client, client_port_t *src_port,
+			    client_t *dest_client, client_port_t *dest_port,
+			    snd_seq_port_subscribe_t *info)
+{
+	port_subs_info_t *src = &src_port->c_src;
+	port_subs_info_t *dest = &dest_port->c_dest;
+	subscribers_t *subs;
+	struct list_head *p;
+	int err = -ENOENT;
+	unsigned long flags;
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	/* look for the connection */
+	list_for_each(p, &src->list_head) {
+		subs = list_entry(p, subscribers_t, src_list);
+		if (match_subs_info(info, &subs->info)) {
+			write_lock_irqsave(&src->list_lock, flags);
+			// write_lock(&dest->list_lock);  // no lock yet
+			list_del(&subs->src_list);
+			list_del(&subs->dest_list);
+			// write_unlock(&dest->list_lock);
+			write_unlock_irqrestore(&src->list_lock, flags);
+			src->exclusive = dest->exclusive = 0;
+			unsubscribe_port(src_client, src_port, src, info,
+					 connector->number != src_client->number);
+			unsubscribe_port(dest_client, dest_port, dest, info,
+					 connector->number != dest_client->number);
+			kfree(subs);
+			err = 0;
+			break;
+		}
+	}
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* get matched subscriber */
+subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp,
+					     snd_seq_addr_t *dest_addr)
+{
+	struct list_head *p;
+	subscribers_t *s, *found = NULL;
+
+	down_read(&src_grp->list_mutex);
+	list_for_each(p, &src_grp->list_head) {
+		s = list_entry(p, subscribers_t, src_list);
+		if (addr_match(dest_addr, &s->info.dest)) {
+			found = s;
+			break;
+		}
+	}
+	up_read(&src_grp->list_mutex);
+	return found;
+}
+
+/*
+ * Attach a device driver that wants to receive events from the
+ * sequencer.  Returns the new port number on success.
+ * A driver that wants to receive the events converted to midi, will
+ * use snd_seq_midisynth_register_port().
+ */
+/* exported */
+int snd_seq_event_port_attach(int client,
+			      snd_seq_port_callback_t *pcbp,
+			      int cap, int type, int midi_channels,
+			      int midi_voices, char *portname)
+{
+	snd_seq_port_info_t portinfo;
+	int  ret;
+
+	/* Set up the port */
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	strlcpy(portinfo.name, portname ? portname : "Unamed port",
+		sizeof(portinfo.name));
+
+	portinfo.capability = cap;
+	portinfo.type = type;
+	portinfo.kernel = pcbp;
+	portinfo.midi_channels = midi_channels;
+	portinfo.midi_voices = midi_voices;
+
+	/* Create it */
+	ret = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_CREATE_PORT,
+					&portinfo);
+
+	if (ret >= 0)
+		ret = portinfo.addr.port;
+
+	return ret;
+}
+
+
+/*
+ * Detach the driver from a port.
+ */
+/* exported */
+int snd_seq_event_port_detach(int client, int port)
+{
+	snd_seq_port_info_t portinfo;
+	int  err;
+
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	portinfo.addr.port   = port;
+	err = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_DELETE_PORT,
+					&portinfo);
+
+	return err;
+}
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
new file mode 100644
index 0000000..89fd441
--- /dev/null
+++ b/sound/core/seq/seq_ports.h
@@ -0,0 +1,128 @@
+/*
+ *   ALSA sequencer Ports 
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_PORTS_H
+#define __SND_SEQ_PORTS_H
+
+#include <sound/seq_kernel.h>
+#include "seq_lock.h"
+
+/* list of 'exported' ports */
+
+/* Client ports that are not exported are still accessible, but are
+ anonymous ports. 
+ 
+ If a port supports SUBSCRIPTION, that port can send events to all
+ subscribersto a special address, with address
+ (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all
+ recipients that are registered in the subscription list. A typical
+ application for these SUBSCRIPTION events is handling of incoming MIDI
+ data. The port doesn't 'know' what other clients are interested in this
+ message. If for instance a MIDI recording application would like to receive
+ the events from that port, it will first have to subscribe with that port.
+ 
+*/
+
+typedef struct subscribers_t {
+	snd_seq_port_subscribe_t info;	/* additional info */
+	struct list_head src_list;	/* link of sources */
+	struct list_head dest_list;	/* link of destinations */
+	atomic_t ref_count;
+} subscribers_t;
+
+typedef struct port_subs_info_t {
+	struct list_head list_head;	/* list of subscribed ports */
+	unsigned int count;		/* count of subscribers */
+	unsigned int exclusive: 1;	/* exclusive mode */
+	struct rw_semaphore list_mutex;
+	rwlock_t list_lock;
+	snd_seq_kernel_port_open_t *open;
+	snd_seq_kernel_port_close_t *close;
+} port_subs_info_t;
+
+typedef struct client_port_t {
+
+	snd_seq_addr_t addr;		/* client/port number */
+	struct module *owner;		/* owner of this port */
+	char name[64];			/* port name */	
+	struct list_head list;		/* port list */
+	snd_use_lock_t use_lock;
+
+	/* subscribers */
+	port_subs_info_t c_src;		/* read (sender) list */
+	port_subs_info_t c_dest;	/* write (dest) list */
+
+	snd_seq_kernel_port_input_t *event_input;
+	snd_seq_kernel_port_private_free_t *private_free;
+	void *private_data;
+	unsigned int callback_all : 1;
+	unsigned int closing : 1;
+	unsigned int timestamping: 1;
+	unsigned int time_real: 1;
+	int time_queue;
+	
+	/* capability, inport, output, sync */
+	unsigned int capability;	/* port capability bits */
+	unsigned int type;		/* port type bits */
+
+	/* supported channels */
+	int midi_channels;
+	int midi_voices;
+	int synth_voices;
+		
+} client_port_t;
+
+/* return pointer to port structure and lock port */
+client_port_t *snd_seq_port_use_ptr(client_t *client, int num);
+
+/* search for next port - port is locked if found */
+client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo);
+
+/* unlock the port */
+#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
+
+/* create a port, port number is returned (-1 on failure) */
+client_port_t *snd_seq_create_port(client_t *client, int port_index);
+
+/* delete a port */
+int snd_seq_delete_port(client_t *client, int port);
+
+/* delete all ports */
+int snd_seq_delete_all_ports(client_t *client);
+
+/* set port info fields */
+int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info);
+
+/* get port info fields */
+int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info);
+
+/* add subscriber to subscription list */
+int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
+
+/* remove subscriber from subscription list */ 
+int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
+
+/* subscribe port */
+int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info);
+
+/* get matched subscriber */
+subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr);
+
+#endif
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
new file mode 100644
index 0000000..a519732e
--- /dev/null
+++ b/sound/core/seq/seq_prioq.c
@@ -0,0 +1,449 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_timer.h"
+#include "seq_prioq.h"
+
+
+/* Implementation is a simple linked list for now...
+
+   This priority queue orders the events on timestamp. For events with an
+   equeal timestamp the queue behaves as a FIFO. 
+
+   *
+   *           +-------+
+   *  Head --> | first |
+   *           +-------+
+   *                 |next
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *  Tail --> | last  |
+   *           +-------+
+   *
+
+ */
+
+
+
+/* create new prioq (constructor) */
+prioq_t *snd_seq_prioq_new(void)
+{
+	prioq_t *f;
+
+	f = kcalloc(1, sizeof(*f), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
+		return NULL;
+	}
+	
+	spin_lock_init(&f->lock);
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+/* delete prioq (destructor) */
+void snd_seq_prioq_delete(prioq_t **fifo)
+{
+	prioq_t *f = *fifo;
+	*fifo = NULL;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
+		return;
+	}
+
+	/* release resources...*/
+	/*....................*/
+	
+	if (f->cells > 0) {
+		/* drain prioQ */
+		while (f->cells > 0)
+			snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+	}
+	
+	kfree(f);
+}
+
+
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; 0 */
+static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
+	} else {
+		/* compare real time */
+		return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
+	}
+}
+
+/* compare timestamp between events */
+/* return negative if a < b;
+ *        zero     if a = b;
+ *        positive if a > b;
+ */
+static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		if (a->time.tick > b->time.tick)
+			return 1;
+		else if (a->time.tick == b->time.tick)
+			return 0;
+		else
+			return -1;
+	} else {
+		/* compare real time */
+		if (a->time.time.tv_sec > b->time.time.tv_sec)
+			return 1;
+		else if (a->time.time.tv_sec == b->time.time.tv_sec) {
+			if (a->time.time.tv_nsec > b->time.time.tv_nsec)
+				return 1;
+			else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
+				return 0;
+			else
+				return -1;
+		} else
+			return -1;
+	}
+}
+
+/* enqueue cell to prioq */
+int snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell)
+{
+	snd_seq_event_cell_t *cur, *prev;
+	unsigned long flags;
+	int count;
+	int prior;
+
+	snd_assert(f, return -EINVAL);
+	snd_assert(cell, return -EINVAL);
+	
+	/* check flags */
+	prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
+
+	spin_lock_irqsave(&f->lock, flags);
+
+	/* check if this element needs to inserted at the end (ie. ordered 
+	   data is inserted) This will be very likeley if a sequencer 
+	   application or midi file player is feeding us (sequential) data */
+	if (f->tail && !prior) {
+		if (compare_timestamp(&cell->event, &f->tail->event)) {
+			/* add new cell to tail of the fifo */
+			f->tail->next = cell;
+			f->tail = cell;
+			cell->next = NULL;
+			f->cells++;
+			spin_unlock_irqrestore(&f->lock, flags);
+			return 0;
+		}
+	}
+	/* traverse list of elements to find the place where the new cell is
+	   to be inserted... Note that this is a order n process ! */
+
+	prev = NULL;		/* previous cell */
+	cur = f->head;		/* cursor */
+
+	count = 10000; /* FIXME: enough big, isn't it? */
+	while (cur != NULL) {
+		/* compare timestamps */
+		int rel = compare_timestamp_rel(&cell->event, &cur->event);
+		if (rel < 0)
+			/* new cell has earlier schedule time, */
+			break;
+		else if (rel == 0 && prior)
+			/* equal schedule time and prior to others */
+			break;
+		/* new cell has equal or larger schedule time, */
+		/* move cursor to next cell */
+		prev = cur;
+		cur = cur->next;
+		if (! --count) {
+			spin_unlock_irqrestore(&f->lock, flags);
+			snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
+			return -EINVAL;
+		}
+	}
+
+	/* insert it before cursor */
+	if (prev != NULL)
+		prev->next = cell;
+	cell->next = cur;
+
+	if (f->head == cur) /* this is the first cell, set head to it */
+		f->head = cell;
+	if (cur == NULL) /* reached end of the list */
+		f->tail = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+	return 0;
+}
+
+/* dequeue cell from prioq */
+snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f)
+{
+	snd_seq_event_cell_t *cell;
+	unsigned long flags;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	spin_lock_irqsave(&f->lock, flags);
+
+	cell = f->head;
+	if (cell) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	spin_unlock_irqrestore(&f->lock, flags);
+	return cell;
+}
+
+/* return number of events available in prioq */
+int snd_seq_prioq_avail(prioq_t * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return 0;
+	}
+	return f->cells;
+}
+
+
+/* peek at cell at the head of the prioq */
+snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	return f->head;
+}
+
+
+static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp)
+{
+	if (cell->event.source.client == client ||
+	    cell->event.dest.client == client)
+		return 1;
+	if (!timestamp)
+		return 0;
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		if (cell->event.time.tick)
+			return 1;
+		break;
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+		if (cell->event.time.time.tv_sec ||
+		    cell->event.time.time.tv_nsec)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp)
+{
+	register snd_seq_event_cell_t *cell, *next;
+	unsigned long flags;
+	snd_seq_event_cell_t *prev = NULL;
+	snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+	while (cell) {
+		next = cell->next;
+		if (prioq_match(cell, client, timestamp)) {
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+			freeprev = cell;
+		} else {
+#if 0
+			printk("type = %i, source = %i, dest = %i, client = %i\n",
+				cell->event.type,
+				cell->event.source.client,
+				cell->event.dest.client,
+				client);
+#endif
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+static int prioq_remove_match(snd_seq_remove_events_t *info,
+	snd_seq_event_t *ev)
+{
+	int res;
+
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
+		if (ev->dest.client != info->dest.client ||
+				ev->dest.port != info->dest.port)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
+		if (! snd_seq_ev_is_channel_type(ev))
+			return 0;
+		/* data.note.channel and data.control.channel are identical */
+		if (ev->data.note.channel != info->channel)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (!res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
+		if (ev->type != info->type)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
+		/* Do not remove off events */
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_NOTEOFF:
+		/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
+			return 0;
+		default:
+			break;
+		}
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
+		if (info->tag != ev->tag)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* remove cells matching remove criteria */
+void snd_seq_prioq_remove_events(prioq_t * f, int client,
+	snd_seq_remove_events_t *info)
+{
+	register snd_seq_event_cell_t *cell, *next;
+	unsigned long flags;
+	snd_seq_event_cell_t *prev = NULL;
+	snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+
+	while (cell) {
+		next = cell->next;
+		if (cell->event.source.client == client &&
+			prioq_remove_match(info, &cell->event)) {
+
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+
+			freeprev = cell;
+		} else {
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
new file mode 100644
index 0000000..f12af79
--- /dev/null
+++ b/sound/core/seq/seq_prioq.h
@@ -0,0 +1,62 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_PRIOQ_H
+#define __SND_SEQ_PRIOQ_H
+
+#include "seq_memory.h"
+
+
+/* === PRIOQ === */
+
+typedef struct {
+	snd_seq_event_cell_t* head;      /* pointer to head of prioq */
+	snd_seq_event_cell_t* tail;      /* pointer to tail of prioq */
+	int cells;
+	spinlock_t lock;
+} prioq_t;
+
+
+/* create new prioq (constructor) */
+extern prioq_t *snd_seq_prioq_new(void);
+
+/* delete prioq (destructor) */
+extern void snd_seq_prioq_delete(prioq_t **fifo);
+
+/* enqueue cell to prioq */
+extern int snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell);
+
+/* dequeue cell from prioq */ 
+extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f);
+
+/* return number of events available in prioq */
+extern int snd_seq_prioq_avail(prioq_t *f);
+
+/* peek at cell at the head of the prioq */
+extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f);
+
+/* client left queue */
+extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp);        
+
+/* Remove events */
+void snd_seq_prioq_remove_events(prioq_t * f, int client,
+	snd_seq_remove_events_t *info);
+
+#endif
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
new file mode 100644
index 0000000..3afc7cc0
--- /dev/null
+++ b/sound/core/seq/seq_queue.c
@@ -0,0 +1,783 @@
+/*
+ *   ALSA sequencer Timing queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * MAJOR CHANGES
+ *   Nov. 13, 1999	Takashi Iwai <iwai@ww.uni-erlangen.de>
+ *     - Queues are allocated dynamically via ioctl.
+ *     - When owner client is deleted, all owned queues are deleted, too.
+ *     - Owner of unlocked queue is kept unmodified even if it is
+ *	 manipulated by other clients.
+ *     - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
+ *       caller client.  i.e. Changing owner to a third client is not
+ *       allowed.
+ *
+ *  Aug. 30, 2000	Takashi Iwai
+ *     - Queues are managed in static array again, but with better way.
+ *       The API itself is identical.
+ *     - The queue is locked when queue_t pinter is returned via
+ *       queueptr().  This pointer *MUST* be released afterward by
+ *       queuefree(ptr).
+ *     - Addition of experimental sync support.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_clientmgr.h"
+#include "seq_fifo.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+
+/* list of allocated queues */
+static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES];
+static DEFINE_SPINLOCK(queue_list_lock);
+/* number of queues allocated */
+static int num_queues;
+
+int snd_seq_queue_get_cur_queues(void)
+{
+	return num_queues;
+}
+
+/*----------------------------------------------------------------*/
+
+/* assign queue id and insert to list */
+static int queue_list_add(queue_t *q)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (! queue_list[i]) {
+			queue_list[i] = q;
+			q->queue = i;
+			num_queues++;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return i;
+		}
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return -1;
+}
+
+static queue_t *queue_list_remove(int id, int client)
+{
+	queue_t *q;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[id];
+	if (q) {
+		spin_lock(&q->owner_lock);
+		if (q->owner == client) {
+			/* found */
+			q->klocked = 1;
+			spin_unlock(&q->owner_lock);
+			queue_list[id] = NULL;
+			num_queues--;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return q;
+		}
+		spin_unlock(&q->owner_lock);
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return NULL;
+}
+
+/*----------------------------------------------------------------*/
+
+/* create new queue (constructor) */
+static queue_t *queue_new(int owner, int locked)
+{
+	queue_t *q;
+
+	q = kcalloc(1, sizeof(*q), GFP_KERNEL);
+	if (q == NULL) {
+		snd_printd("malloc failed for snd_seq_queue_new()\n");
+		return NULL;
+	}
+
+	spin_lock_init(&q->owner_lock);
+	spin_lock_init(&q->check_lock);
+	init_MUTEX(&q->timer_mutex);
+	snd_use_lock_init(&q->use_lock);
+	q->queue = -1;
+
+	q->tickq = snd_seq_prioq_new();
+	q->timeq = snd_seq_prioq_new();
+	q->timer = snd_seq_timer_new();
+	if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
+		snd_seq_prioq_delete(&q->tickq);
+		snd_seq_prioq_delete(&q->timeq);
+		snd_seq_timer_delete(&q->timer);
+		kfree(q);
+		return NULL;
+	}
+
+	q->owner = owner;
+	q->locked = locked;
+	q->klocked = 0;
+
+	return q;
+}
+
+/* delete queue (destructor) */
+static void queue_delete(queue_t *q)
+{
+	/* stop and release the timer */
+	snd_seq_timer_stop(q->timer);
+	snd_seq_timer_close(q);
+	/* wait until access free */
+	snd_use_lock_sync(&q->use_lock);
+	/* release resources... */
+	snd_seq_prioq_delete(&q->tickq);
+	snd_seq_prioq_delete(&q->timeq);
+	snd_seq_timer_delete(&q->timer);
+
+	kfree(q);
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* setup queues */
+int __init snd_seq_queues_init(void)
+{
+	/*
+	memset(queue_list, 0, sizeof(queue_list));
+	num_queues = 0;
+	*/
+	return 0;
+}
+
+/* delete all existing queues */
+void __exit snd_seq_queues_delete(void)
+{
+	int i;
+
+	/* clear list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (queue_list[i])
+			queue_delete(queue_list[i]);
+	}
+}
+
+/* allocate a new queue -
+ * return queue index value or negative value for error
+ */
+int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+{
+	queue_t *q;
+
+	q = queue_new(client, locked);
+	if (q == NULL)
+		return -ENOMEM;
+	q->info_flags = info_flags;
+	if (queue_list_add(q) < 0) {
+		queue_delete(q);
+		return -ENOMEM;
+	}
+	snd_seq_queue_use(q->queue, client, 1); /* use this queue */
+	return q->queue;
+}
+
+/* delete a queue - queue must be owned by the client */
+int snd_seq_queue_delete(int client, int queueid)
+{
+	queue_t *q;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return -EINVAL;
+	q = queue_list_remove(queueid, client);
+	if (q == NULL)
+		return -EINVAL;
+	queue_delete(q);
+
+	return 0;
+}
+
+
+/* return pointer to queue structure for specified id */
+queue_t *queueptr(int queueid)
+{
+	queue_t *q;
+	unsigned long flags;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return NULL;
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[queueid];
+	if (q)
+		snd_use_lock_use(&q->use_lock);
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return q;
+}
+
+/* return the (first) queue matching with the specified name */
+queue_t *snd_seq_queue_find_name(char *name)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) != NULL) {
+			if (strncmp(q->name, name, sizeof(q->name)) == 0)
+				return q;
+			queuefree(q);
+		}
+	}
+	return NULL;
+}
+
+
+/* -------------------------------------------------------- */
+
+void snd_seq_check_queue(queue_t *q, int atomic, int hop)
+{
+	unsigned long flags;
+	snd_seq_event_cell_t *cell;
+
+	if (q == NULL)
+		return;
+
+	/* make this function non-reentrant */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_blocked) {
+		q->check_again = 1;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		return;		/* other thread is already checking queues */
+	}
+	q->check_blocked = 1;
+	spin_unlock_irqrestore(&q->check_lock, flags);
+
+      __again:
+	/* Process tick queue... */
+	while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
+		if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) {
+			cell = snd_seq_prioq_cell_out(q->tickq);
+			if (cell)
+				snd_seq_dispatch_event(cell, atomic, hop);
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+
+	/* Process time queue... */
+	while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
+		if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) {
+			cell = snd_seq_prioq_cell_out(q->timeq);
+			if (cell)
+				snd_seq_dispatch_event(cell, atomic, hop);
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+	/* free lock */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_again) {
+		q->check_again = 0;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		goto __again;
+	}
+	q->check_blocked = 0;
+	spin_unlock_irqrestore(&q->check_lock, flags);
+}
+
+
+/* enqueue a event to singe queue */
+int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop)
+{
+	int dest, err;
+	queue_t *q;
+
+	snd_assert(cell != NULL, return -EINVAL);
+	dest = cell->event.queue;	/* destination queue */
+	q = queueptr(dest);
+	if (q == NULL)
+		return -EINVAL;
+	/* handle relative time stamps, convert them into absolute */
+	if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
+		switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			cell->event.time.tick += q->timer->tick.cur_tick;
+			break;
+
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time);
+			break;
+		}
+		cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
+		cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
+	}
+	/* enqueue event in the real-time or midi queue */
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		err = snd_seq_prioq_cell_in(q->tickq, cell);
+		break;
+
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+	default:
+		err = snd_seq_prioq_cell_in(q->timeq, cell);
+		break;
+	}
+
+	if (err < 0) {
+		queuefree(q); /* unlock */
+		return err;
+	}
+
+	/* trigger dispatching */
+	snd_seq_check_queue(q, atomic, hop);
+
+	queuefree(q); /* unlock */
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+static inline int check_access(queue_t *q, int client)
+{
+	return (q->owner == client) || (!q->locked && !q->klocked);
+}
+
+/* check if the client has permission to modify queue parameters.
+ * if it does, lock the queue
+ */
+static int queue_access_lock(queue_t *q, int client)
+{
+	unsigned long flags;
+	int access_ok;
+	
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	if (access_ok)
+		q->klocked = 1;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	return access_ok;
+}
+
+/* unlock the queue */
+static inline void queue_access_unlock(queue_t *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 0;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+}
+
+/* exported - only checking permission */
+int snd_seq_queue_check_access(int queueid, int client)
+{
+	queue_t *q = queueptr(queueid);
+	int access_ok;
+	unsigned long flags;
+
+	if (! q)
+		return 0;
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	queuefree(q);
+	return access_ok;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * change queue's owner and permission
+ */
+int snd_seq_queue_set_owner(int queueid, int client, int locked)
+{
+	queue_t *q = queueptr(queueid);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	q->locked = locked ? 1 : 0;
+	q->owner = client;
+	queue_access_unlock(q);
+	queuefree(q);
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* open timer -
+ * q->use mutex should be down before calling this function to avoid
+ * confliction with snd_seq_queue_use()
+ */
+int snd_seq_queue_timer_open(int queueid)
+{
+	int result = 0;
+	queue_t *queue;
+	seq_timer_t *tmr;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	if ((result = snd_seq_timer_open(queue)) < 0) {
+		snd_seq_timer_defaults(tmr);
+		result = snd_seq_timer_open(queue);
+	}
+	queuefree(queue);
+	return result;
+}
+
+/* close timer -
+ * q->use mutex should be down before calling this function
+ */
+int snd_seq_queue_timer_close(int queueid)
+{
+	queue_t *queue;
+	seq_timer_t *tmr;
+	int result = 0;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	snd_seq_timer_close(queue);
+	queuefree(queue);
+	return result;
+}
+
+/* change queue tempo and ppq */
+int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info)
+{
+	queue_t *q = queueptr(queueid);
+	int result;
+
+	if (q == NULL)
+		return -EINVAL;
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	result = snd_seq_timer_set_tempo(q->timer, info->tempo);
+	if (result >= 0)
+		result = snd_seq_timer_set_ppq(q->timer, info->ppq);
+	if (result >= 0 && info->skew_base > 0)
+		result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base);
+	queue_access_unlock(q);
+	queuefree(q);
+	return result;
+}
+
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+	queue_t *queue;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	down(&queue->timer_mutex);
+	if (use) {
+		if (!test_and_set_bit(client, queue->clients_bitmap))
+			queue->clients++;
+	} else {
+		if (test_and_clear_bit(client, queue->clients_bitmap))
+			queue->clients--;
+	}
+	if (queue->clients) {
+		if (use && queue->clients == 1)
+			snd_seq_timer_defaults(queue->timer);
+		snd_seq_timer_open(queue);
+	} else {
+		snd_seq_timer_close(queue);
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	return 0;
+}
+
+/*
+ * check if queue is used by the client
+ * return negative value if the queue is invalid.
+ * return 0 if not used, 1 if used.
+ */
+int snd_seq_queue_is_used(int queueid, int client)
+{
+	queue_t *q;
+	int result;
+
+	q = queueptr(queueid);
+	if (q == NULL)
+		return -EINVAL; /* invalid queue */
+	result = test_bit(client, q->clients_bitmap) ? 1 : 0;
+	queuefree(q);
+	return result;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* notification that client has left the system -
+ * stop the timer on all queues owned by this client
+ */
+void snd_seq_queue_client_termination(int client)
+{
+	unsigned long flags;
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		spin_lock_irqsave(&q->owner_lock, flags);
+		if (q->owner == client)
+			q->klocked = 1;
+		spin_unlock_irqrestore(&q->owner_lock, flags);
+		if (q->owner == client) {
+			if (q->timer->running)
+				snd_seq_timer_stop(q->timer);
+			snd_seq_timer_reset(q->timer);
+		}
+		queuefree(q);
+	}
+}
+
+/* final stage notification -
+ * remove cells for no longer exist client (for non-owned queue)
+ * or delete this queue (for owned queue)
+ */
+void snd_seq_queue_client_leave(int client)
+{
+	int i;
+	queue_t *q;
+
+	/* delete own queues from queue list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queue_list_remove(i, client)) != NULL)
+			queue_delete(q);
+	}
+
+	/* remove cells from existing queues -
+	 * they are not owned by this client
+	 */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap)) {
+			snd_seq_prioq_leave(q->tickq, client, 0);
+			snd_seq_prioq_leave(q->timeq, client, 0);
+			snd_seq_queue_use(q->queue, client, 0);
+		}
+		queuefree(q);
+	}
+}
+
+
+
+/*----------------------------------------------------------------*/
+
+/* remove cells from all queues */
+void snd_seq_queue_client_leave_cells(int client)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		snd_seq_prioq_leave(q->tickq, client, 0);
+		snd_seq_prioq_leave(q->timeq, client, 0);
+		queuefree(q);
+	}
+}
+
+/* remove cells based on flush criteria */
+void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info)
+{
+	int i;
+	queue_t *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap) &&
+		    (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
+		     q->queue == info->queue)) {
+			snd_seq_prioq_remove_events(q->tickq, client, info);
+			snd_seq_prioq_remove_events(q->timeq, client, info);
+		}
+		queuefree(q);
+	}
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * send events to all subscribed ports
+ */
+static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
+{
+	snd_seq_event_t sev;
+
+	sev = *ev;
+	
+	sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
+	sev.time.tick = q->timer->tick.cur_tick;
+	sev.queue = q->queue;
+	sev.data.queue.queue = q->queue;
+
+	/* broadcast events from Timer port */
+	sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
+}
+
+/*
+ * process a received queue-control event.
+ * this function is exported for seq_sync.c.
+ */
+void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
+{
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_START:
+		snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
+		snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
+		if (! snd_seq_timer_start(q->timer))
+			queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_CONTINUE:
+		if (! snd_seq_timer_continue(q->timer))
+			queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_STOP:
+		snd_seq_timer_stop(q->timer);
+		queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_TEMPO:
+		snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
+		queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TICK:
+		if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TIME:
+		if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_QUEUE_SKEW:
+		if (snd_seq_timer_set_skew(q->timer,
+					   ev->data.queue.param.skew.value,
+					   ev->data.queue.param.skew.base) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+	}
+}
+
+
+/*
+ * Queue control via timer control port:
+ * this function is exported as a callback of timer port.
+ */
+int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop)
+{
+	queue_t *q;
+
+	snd_assert(ev != NULL, return -EINVAL);
+	q = queueptr(ev->data.queue.queue);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, ev->source.client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	snd_seq_queue_process_event(q, ev, atomic, hop);
+
+	queue_access_unlock(q);
+	queuefree(q);
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* exported to seq_info.c */
+void snd_seq_info_queues_read(snd_info_entry_t *entry, 
+			      snd_info_buffer_t * buffer)
+{
+	int i, bpm;
+	queue_t *q;
+	seq_timer_t *tmr;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+
+		tmr = q->timer;
+		if (tmr->tempo)
+			bpm = 60000000 / tmr->tempo;
+		else
+			bpm = 0;
+
+		snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
+		snd_iprintf(buffer, "owned by client    : %d\n", q->owner);
+		snd_iprintf(buffer, "lock status        : %s\n", q->locked ? "Locked" : "Free");
+		snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
+		snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
+		snd_iprintf(buffer, "timer state        : %s\n", tmr->running ? "Running" : "Stopped");
+		snd_iprintf(buffer, "timer PPQ          : %d\n", tmr->ppq);
+		snd_iprintf(buffer, "current tempo      : %d\n", tmr->tempo);
+		snd_iprintf(buffer, "current BPM        : %d\n", bpm);
+		snd_iprintf(buffer, "current time       : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
+		snd_iprintf(buffer, "current tick       : %d\n", tmr->tick.cur_tick);
+		snd_iprintf(buffer, "\n");
+		queuefree(q);
+	}
+}
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
new file mode 100644
index 0000000..b1bf551
--- /dev/null
+++ b/sound/core/seq/seq_queue.h
@@ -0,0 +1,140 @@
+/*
+ *   ALSA sequencer Queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_QUEUE_H
+#define __SND_SEQ_QUEUE_H
+
+#include "seq_memory.h"
+#include "seq_prioq.h"
+#include "seq_timer.h"
+#include "seq_lock.h"
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+
+#define SEQ_QUEUE_NO_OWNER (-1)
+
+struct _snd_seq_queue {
+	int queue;		/* queue number */
+
+	char name[64];		/* name of this queue */
+
+	prioq_t	*tickq;		/* midi tick event queue */
+	prioq_t	*timeq;		/* real-time event queue */	
+	
+	seq_timer_t *timer;	/* time keeper for this queue */
+	int	owner;		/* client that 'owns' the timer */
+	unsigned int	locked:1,	/* timer is only accesibble by owner if set */
+		klocked:1,	/* kernel lock (after START) */	
+		check_again:1,
+		check_blocked:1;
+
+	unsigned int flags;		/* status flags */
+	unsigned int info_flags;	/* info for sync */
+
+	spinlock_t owner_lock;
+	spinlock_t check_lock;
+
+	/* clients which uses this queue (bitmap) */
+	DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
+	unsigned int clients;	/* users of this queue */
+	struct semaphore timer_mutex;
+
+	snd_use_lock_t use_lock;
+};
+
+
+/* get the number of current queues */
+int snd_seq_queue_get_cur_queues(void);
+
+/* init queues structure */
+int snd_seq_queues_init(void);
+
+/* delete queues */ 
+void snd_seq_queues_delete(void);
+
+
+/* create new queue (constructor) */
+int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+
+/* delete queue (destructor) */
+int snd_seq_queue_delete(int client, int queueid);
+
+/* notification that client has left the system */
+void snd_seq_queue_client_termination(int client);
+
+/* final stage */
+void snd_seq_queue_client_leave(int client);
+
+/* enqueue a event received from one the clients */
+int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop);
+
+/* Remove events */
+void snd_seq_queue_client_leave_cells(int client);
+void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info);
+
+/* return pointer to queue structure for specified id */
+queue_t *queueptr(int queueid);
+/* unlock */
+#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
+
+/* return the (first) queue matching with the specified name */
+queue_t *snd_seq_queue_find_name(char *name);
+
+/* check single queue and dispatch events */
+void snd_seq_check_queue(queue_t *q, int atomic, int hop);
+
+/* access to queue's parameters */
+int snd_seq_queue_check_access(int queueid, int client);
+int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info);
+int snd_seq_queue_set_owner(int queueid, int client, int locked);
+int snd_seq_queue_set_locked(int queueid, int client, int locked);
+int snd_seq_queue_timer_open(int queueid);
+int snd_seq_queue_timer_close(int queueid);
+int snd_seq_queue_use(int queueid, int client, int use);
+int snd_seq_queue_is_used(int queueid, int client);
+
+int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
+void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop);
+
+/*
+ * 64bit division - for sync stuff..
+ */
+#if defined(i386) || defined(i486)
+
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divl %4"		\
+	   : "=a" ((u32)(q)),	\
+	     "=d" ((u32)(r))	\
+	   : "0" ((u32)(n0)),	\
+	     "1" ((u32)(n1)),	\
+	     "rm" ((u32)(d)))
+
+#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
+#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
+#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
+
+#else
+#define u64_div(x,y,q)	((q) = (u32)((u64)(x) / (u64)(y)))
+#define u64_mod(x,y,r)	((r) = (u32)((u64)(x) % (u64)(y)))
+#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
+#endif
+
+
+#endif
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
new file mode 100644
index 0000000..e8f0a66
--- /dev/null
+++ b/sound/core/seq/seq_system.c
@@ -0,0 +1,190 @@
+/*
+ *   ALSA sequencer System services Client
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "seq_system.h"
+#include "seq_timer.h"
+#include "seq_queue.h"
+
+/* internal client that provide system services, access to timer etc. */
+
+/*
+ * Port "Timer"
+ *      - send tempo /start/stop etc. events to this port to manipulate the 
+ *        queue's timer. The queue address is specified in
+ *	  data.queue.queue.
+ *      - this port supports subscription. The received timer events are 
+ *        broadcasted to all subscribed clients. The modified tempo
+ *	  value is stored on data.queue.value.
+ *	  The modifier client/port is not send.
+ *
+ * Port "Announce"
+ *      - does not receive message
+ *      - supports supscription. For each client or port attaching to or 
+ *        detaching from the system an announcement is send to the subscribed
+ *        clients.
+ *
+ * Idea: the subscription mechanism might also work handy for distributing 
+ * synchronisation and timing information. In this case we would ideally have
+ * a list of subscribers for each type of sync (time, tick), for each timing
+ * queue.
+ *
+ * NOTE: the queue to be started, stopped, etc. must be specified
+ *	 in data.queue.addr.queue field.  queue is used only for
+ *	 scheduling, and no longer referred as affected queue.
+ *	 They are used only for timer broadcast (see above).
+ *							-- iwai
+ */
+
+
+/* client id of our system client */
+static int sysclient = -1;
+
+/* port id numbers for this client */
+static int announce_port = -1;
+
+
+
+/* fill standard header data, source port & channel are filled in */
+static int setheader(snd_seq_event_t * ev, int client, int port)
+{
+	if (announce_port < 0)
+		return -ENODEV;
+
+	memset(ev, 0, sizeof(snd_seq_event_t));
+
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+
+	/* fill data */
+	/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
+	ev->data.addr.client = client;
+	ev->data.addr.port = port;
+
+	return 0;
+}
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type)
+{
+	snd_seq_event_t ev;
+	
+	if (setheader(&ev, client, port) < 0)
+		return;
+	ev.type = type;
+	snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
+}
+
+/* entry points for broadcasting system events */
+int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev)
+{
+	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = client;
+	ev->dest.port = port;
+	return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
+}
+
+/* call-back handler for timer events */
+static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+	return snd_seq_control_queue(ev, atomic, hop);
+}
+
+/* register our internal client */
+int __init snd_seq_system_client_init(void)
+{
+
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_seq_client_info_t *inf;
+	snd_seq_port_info_t *port;
+
+	inf = kcalloc(1, sizeof(*inf), GFP_KERNEL);
+	port = kcalloc(1, sizeof(*port), GFP_KERNEL);
+	if (! inf || ! port) {
+		kfree(inf);
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.event_input = event_input_timer;
+
+	/* register client */
+	callbacks.allow_input = callbacks.allow_output = 1;
+	sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
+
+	/* set our name */
+	inf->client = 0;
+	inf->type = KERNEL_CLIENT;
+	strcpy(inf->name, "System");
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, inf);
+
+	/* register timer */
+	strcpy(port->name, "Timer");
+	port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
+	port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
+	port->kernel = &pcallbacks;
+	port->type = 0;
+	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port->addr.client = sysclient;
+	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+
+	/* register announcement port */
+	strcpy(port->name, "Announce");
+	port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
+	port->kernel = NULL;
+	port->type = 0;
+	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port->addr.client = sysclient;
+	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+	announce_port = port->addr.port;
+
+	kfree(inf);
+	kfree(port);
+	return 0;
+}
+
+
+/* unregister our internal client */
+void __exit snd_seq_system_client_done(void)
+{
+	int oldsysclient = sysclient;
+
+	if (oldsysclient >= 0) {
+		sysclient = -1;
+		announce_port = -1;
+		snd_seq_delete_kernel_client(oldsysclient);
+	}
+}
diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h
new file mode 100644
index 0000000..9000072
--- /dev/null
+++ b/sound/core/seq/seq_system.h
@@ -0,0 +1,46 @@
+/*
+ *  ALSA sequencer System Client
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_SYSTEM_H
+#define __SND_SEQ_SYSTEM_H
+
+#include <sound/seq_kernel.h>
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type);
+
+#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
+#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
+#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
+#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
+#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
+#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
+
+int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev);
+
+/* register our internal client */
+int snd_seq_system_client_init(void);
+
+/* unregister our internal client */
+void snd_seq_system_client_done(void);
+
+
+#endif
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
new file mode 100644
index 0000000..753f1c0
--- /dev/null
+++ b/sound/core/seq/seq_timer.c
@@ -0,0 +1,435 @@
+/*
+ *   ALSA sequencer Timer
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                              Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_timer.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+
+extern int seq_default_timer_class;
+extern int seq_default_timer_sclass;
+extern int seq_default_timer_card;
+extern int seq_default_timer_device;
+extern int seq_default_timer_subdevice;
+extern int seq_default_timer_resolution;
+
+#define SKEW_BASE	0x10000	/* 16bit shift */
+
+void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
+{
+	if (tempo < 1000000)
+		tick->resolution = (tempo * 1000) / ppq;
+	else {
+		/* might overflow.. */
+		unsigned int s;
+		s = tempo % ppq;
+		s = (s * 1000) / ppq;
+		tick->resolution = (tempo / ppq) * 1000;
+		tick->resolution += s;
+	}
+	if (tick->resolution <= 0)
+		tick->resolution = 1;
+	tick->resolution *= nticks;
+	snd_seq_timer_update_tick(tick, 0);
+}
+
+/* create new timer (constructor) */
+seq_timer_t *snd_seq_timer_new(void)
+{
+	seq_timer_t *tmr;
+	
+	tmr = kcalloc(1, sizeof(*tmr), GFP_KERNEL);
+	if (tmr == NULL) {
+		snd_printd("malloc failed for snd_seq_timer_new() \n");
+		return NULL;
+	}
+	spin_lock_init(&tmr->lock);
+
+	/* reset setup to defaults */
+	snd_seq_timer_defaults(tmr);
+	
+	/* reset time */
+	snd_seq_timer_reset(tmr);
+	
+	return tmr;
+}
+
+/* delete timer (destructor) */
+void snd_seq_timer_delete(seq_timer_t **tmr)
+{
+	seq_timer_t *t = *tmr;
+	*tmr = NULL;
+
+	if (t == NULL) {
+		snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
+		return;
+	}
+	t->running = 0;
+
+	/* reset time */
+	snd_seq_timer_stop(t);
+	snd_seq_timer_reset(t);
+
+	kfree(t);
+}
+
+void snd_seq_timer_defaults(seq_timer_t * tmr)
+{
+	/* setup defaults */
+	tmr->ppq = 96;		/* 96 PPQ */
+	tmr->tempo = 500000;	/* 120 BPM */
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	tmr->running = 0;
+
+	tmr->type = SNDRV_SEQ_TIMER_ALSA;
+	tmr->alsa_id.dev_class = seq_default_timer_class;
+	tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
+	tmr->alsa_id.card = seq_default_timer_card;
+	tmr->alsa_id.device = seq_default_timer_device;
+	tmr->alsa_id.subdevice = seq_default_timer_subdevice;
+	tmr->preferred_resolution = seq_default_timer_resolution;
+
+	tmr->skew = tmr->skew_base = SKEW_BASE;
+}
+
+void snd_seq_timer_reset(seq_timer_t * tmr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* reset time & songposition */
+	tmr->cur_time.tv_sec = 0;
+	tmr->cur_time.tv_nsec = 0;
+
+	tmr->tick.cur_tick = 0;
+	tmr->tick.fraction = 0;
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+}
+
+
+/* called by timer interrupt routine. the period time since previous invocation is passed */
+static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri,
+				    unsigned long resolution,
+				    unsigned long ticks)
+{
+	unsigned long flags;
+	queue_t *q = (queue_t *)timeri->callback_data;
+	seq_timer_t *tmr;
+
+	if (q == NULL)
+		return;
+	tmr = q->timer;
+	if (tmr == NULL)
+		return;
+	if (!tmr->running)
+		return;
+
+	resolution *= ticks;
+	if (tmr->skew != tmr->skew_base) {
+		/* FIXME: assuming skew_base = 0x10000 */
+		resolution = (resolution >> 16) * tmr->skew +
+			(((resolution & 0xffff) * tmr->skew) >> 16);
+	}
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* update timer */
+	snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+
+	/* calculate current tick */
+	snd_seq_timer_update_tick(&tmr->tick, resolution);
+
+	/* register actual time of this timer update */
+	do_gettimeofday(&tmr->last_update);
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+
+	/* check queues and dispatch events */
+	snd_seq_check_queue(q, 1, 0);
+}
+
+/* set current tempo */
+int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (tempo <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if ((unsigned int)tempo != tmr->tempo) {
+		tmr->tempo = tempo;
+		snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	}
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current ppq */
+int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (ppq <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if (tmr->running && (ppq != tmr->ppq)) {
+		/* refuse to change ppq on running timers */
+		/* because it will upset the song position (ticks) */
+		spin_unlock_irqrestore(&tmr->lock, flags);
+		snd_printd("seq: cannot change ppq of a running timer\n");
+		return -EBUSY;
+	}
+
+	tmr->ppq = ppq;
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current tick position */
+int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->tick.cur_tick = position;
+	tmr->tick.fraction = 0;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current real-time position */
+int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	snd_seq_sanity_real_time(&position);
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->cur_time = position;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set timer skew */
+int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	/* FIXME */
+	if (base != SKEW_BASE) {
+		snd_printd("invalid skew base 0x%x\n", base);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->skew = skew;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+int snd_seq_timer_open(queue_t *q)
+{
+	snd_timer_instance_t *t;
+	seq_timer_t *tmr;
+	char str[32];
+	int err;
+
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri)
+		return -EBUSY;
+	sprintf(str, "sequencer queue %i", q->queue);
+	if (tmr->type != SNDRV_SEQ_TIMER_ALSA)	/* standard ALSA timer */
+		return -EINVAL;
+	if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
+		tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+	err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
+	if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
+		if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
+		    tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
+			snd_timer_id_t tid;
+			memset(&tid, 0, sizeof(tid));
+			tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+			tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+			tid.card = -1;
+			tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
+			err = snd_timer_open(&t, str, &tid, q->queue);
+		}
+		if (err < 0) {
+			snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
+			return err;
+		}
+	}
+	t->callback = snd_seq_timer_interrupt;
+	t->callback_data = q;
+	t->flags |= SNDRV_TIMER_IFLG_AUTO;
+	tmr->timeri = t;
+	return 0;
+}
+
+int snd_seq_timer_close(queue_t *q)
+{
+	seq_timer_t *tmr;
+	
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri) {
+		snd_timer_stop(tmr->timeri);
+		snd_timer_close(tmr->timeri);
+		tmr->timeri = NULL;
+	}
+	return 0;
+}
+
+int snd_seq_timer_stop(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (!tmr->running)
+		return 0;
+	tmr->running = 0;
+	snd_timer_pause(tmr->timeri);
+	return 0;
+}
+
+static int initialize_timer(seq_timer_t *tmr)
+{
+	snd_timer_t *t;
+	t = tmr->timeri->timer;
+	snd_assert(t, return -EINVAL);
+
+	tmr->ticks = 1;
+	if (tmr->preferred_resolution &&
+	    ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+		unsigned long r = t->hw.resolution;
+		if (! r && t->hw.c_resolution)
+			r = t->hw.c_resolution(t);
+		if (r) {
+			tmr->ticks = (unsigned int)(1000000000uL / (r * tmr->preferred_resolution));
+			if (! tmr->ticks)
+				tmr->ticks = 1;
+		}
+	}
+	tmr->initialized = 1;
+	return 0;
+}
+
+int snd_seq_timer_start(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (tmr->running)
+		snd_seq_timer_stop(tmr);
+	snd_seq_timer_reset(tmr);
+	if (initialize_timer(tmr) < 0)
+		return -EINVAL;
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+	return 0;
+}
+
+int snd_seq_timer_continue(seq_timer_t * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (tmr->running)
+		return -EBUSY;
+	if (! tmr->initialized) {
+		snd_seq_timer_reset(tmr);
+		if (initialize_timer(tmr) < 0)
+			return -EINVAL;
+	}
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+	return 0;
+}
+
+/* return current 'real' time. use timeofday() to get better granularity. */
+snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr)
+{
+	snd_seq_real_time_t cur_time;
+
+	cur_time = tmr->cur_time;
+	if (tmr->running) { 
+		struct timeval tm;
+		int usec;
+		do_gettimeofday(&tm);
+		usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
+		if (usec < 0) {
+			cur_time.tv_nsec += (1000000 + usec) * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
+		} else {
+			cur_time.tv_nsec += usec * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
+		}
+		snd_seq_sanity_real_time(&cur_time);
+	}
+                
+	return cur_time;	
+}
+
+/* TODO: use interpolation on tick queue (will only be useful for very
+ high PPQ values) */
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr)
+{
+	return tmr->tick.cur_tick;
+}
+
+
+/* exported to seq_info.c */
+void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+	int idx;
+	queue_t *q;
+	seq_timer_t *tmr;
+	snd_timer_instance_t *ti;
+	unsigned long resolution;
+	
+	for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
+		q = queueptr(idx);
+		if (q == NULL)
+			continue;
+		if ((tmr = q->timer) == NULL ||
+		    (ti = tmr->timeri) == NULL) {
+			queuefree(q);
+			continue;
+		}
+		snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
+		resolution = snd_timer_resolution(ti) * tmr->ticks;
+		snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
+		snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+		queuefree(q);
+ 	}
+}
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
new file mode 100644
index 0000000..4c0872d
--- /dev/null
+++ b/sound/core/seq/seq_timer.h
@@ -0,0 +1,141 @@
+/*
+ *  ALSA sequencer Timer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_TIMER_H
+#define __SND_SEQ_TIMER_H
+
+#include <sound/timer.h>
+#include <sound/seq_kernel.h>
+
+typedef struct {
+	snd_seq_tick_time_t	cur_tick;	/* current tick */
+	unsigned long		resolution;	/* time per tick in nsec */
+	unsigned long		fraction;	/* current time per tick in nsec */
+} seq_timer_tick_t;
+
+typedef struct {
+	/* ... tempo / offset / running state */
+
+	unsigned int		running:1,	/* running state of queue */	
+				initialized:1;	/* timer is initialized */
+
+	unsigned int		tempo;		/* current tempo, us/tick */
+	int			ppq;		/* time resolution, ticks/quarter */
+
+	snd_seq_real_time_t	cur_time;	/* current time */
+	seq_timer_tick_t	tick;		/* current tick */
+	int tick_updated;
+	
+	int			type;		/* timer type */
+	snd_timer_id_t		alsa_id;	/* ALSA's timer ID */
+	snd_timer_instance_t	*timeri;	/* timer instance */
+	unsigned int		ticks;
+	unsigned long		preferred_resolution; /* timer resolution, ticks/sec */
+
+	unsigned int skew;
+	unsigned int skew_base;
+
+	struct timeval 		last_update;	 /* time of last clock update, used for interpolation */
+
+	spinlock_t lock;
+} seq_timer_t;
+
+
+/* create new timer (constructor) */
+extern seq_timer_t *snd_seq_timer_new(void);
+
+/* delete timer (destructor) */
+extern void snd_seq_timer_delete(seq_timer_t **tmr);
+
+void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
+
+/* */
+static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
+{
+	if (tick->resolution > 0) {
+		tick->fraction += resolution;
+		tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution);
+		tick->fraction %= tick->resolution;
+	}
+}
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; otherwise return 0 */
+static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b)
+{
+	/* compare ticks */
+	return (*a >= *b);
+}
+
+static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b)
+{
+	/* compare real time */
+	if (a->tv_sec > b->tv_sec)
+		return 1;
+	if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec))
+		return 1;
+	return 0;
+}
+
+
+static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm)
+{
+	while (tm->tv_nsec >= 1000000000) {
+		/* roll-over */
+		tm->tv_nsec -= 1000000000;
+                tm->tv_sec++;
+        }
+}
+
+
+/* increment timestamp */
+static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc)
+{
+	tm->tv_sec  += inc->tv_sec;
+	tm->tv_nsec += inc->tv_nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec)
+{
+	tm->tv_nsec  += nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+/* called by timer isr */
+int snd_seq_timer_open(queue_t *q);
+int snd_seq_timer_close(queue_t *q);
+int snd_seq_timer_midi_open(queue_t *q);
+int snd_seq_timer_midi_close(queue_t *q);
+void snd_seq_timer_defaults(seq_timer_t *tmr);
+void snd_seq_timer_reset(seq_timer_t *tmr);
+int snd_seq_timer_stop(seq_timer_t *tmr);
+int snd_seq_timer_start(seq_timer_t *tmr);
+int snd_seq_timer_continue(seq_timer_t *tmr);
+int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo);
+int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq);
+int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position);
+int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position);
+int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr);
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr);
+
+#endif
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
new file mode 100644
index 0000000..6b4e630
--- /dev/null
+++ b/sound/core/seq/seq_virmidi.c
@@ -0,0 +1,551 @@
+/*
+ *  Virtual Raw MIDI client on Sequencer
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@perex.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * Virtual Raw MIDI client
+ *
+ * The virtual rawmidi client is a sequencer client which associate
+ * a rawmidi device file.  The created rawmidi device file can be
+ * accessed as a normal raw midi, but its MIDI source and destination
+ * are arbitrary.  For example, a user-client software synth connected
+ * to this port can be used as a normal midi device as well.
+ *
+ * The virtual rawmidi device accepts also multiple opens.  Each file
+ * has its own input buffer, so that no conflict would occur.  The drain
+ * of input/output buffer acts only to the local buffer.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/seq_virmidi.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
+MODULE_LICENSE("GPL");
+
+/*
+ * initialize an event record
+ */
+static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev)
+{
+	memset(ev, 0, sizeof(*ev));
+	ev->source.port = vmidi->port;
+	switch (vmidi->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		/* FIXME: source and destination are same - not good.. */
+		ev->dest.client = vmidi->client;
+		ev->dest.port = vmidi->port;
+		break;
+	}
+	ev->type = SNDRV_SEQ_EVENT_NONE;
+}
+
+/*
+ * decode input event and put to read buffer of each opened file
+ */
+static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev)
+{
+	snd_virmidi_t *vmidi;
+	struct list_head *list;
+	unsigned char msg[4];
+	int len;
+
+	read_lock(&rdev->filelist_lock);
+	list_for_each(list, &rdev->filelist) {
+		vmidi = list_entry(list, snd_virmidi_t, list);
+		if (!vmidi->trigger)
+			continue;
+		if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+			if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+				continue;
+			snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+		} else {
+			len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
+			if (len > 0)
+				snd_rawmidi_receive(vmidi->substream, msg, len);
+		}
+	}
+	read_unlock(&rdev->filelist_lock);
+
+	return 0;
+}
+
+/*
+ * receive an event from the remote virmidi port
+ *
+ * for rawmidi inputs, you can call this function from the event
+ * handler of a remote port which is attached to the virmidi via
+ * SNDRV_VIRMIDI_SEQ_ATTACH.
+ */
+/* exported */
+int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = rmidi->private_data;
+	return snd_virmidi_dev_receive_event(rdev, ev);
+}
+
+/*
+ * event handler of virmidi port
+ */
+static int snd_virmidi_event_input(snd_seq_event_t *ev, int direct,
+				   void *private_data, int atomic, int hop)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = private_data;
+	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
+		return 0; /* ignored */
+	return snd_virmidi_dev_receive_event(rdev, ev);
+}
+
+/*
+ * trigger rawmidi stream for input
+ */
+static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_virmidi_t *vmidi = substream->runtime->private_data;
+
+	if (up) {
+		vmidi->trigger = 1;
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * trigger rawmidi stream for output
+ */
+static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_virmidi_t *vmidi = substream->runtime->private_data;
+	int count, res;
+	unsigned char buf[32], *pbuf;
+
+	if (up) {
+		vmidi->trigger = 1;
+		if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
+		    !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+			snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
+			return;		/* ignored */
+		}
+		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+			if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
+				return;
+			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+		}
+		while (1) {
+			count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
+			if (count <= 0)
+				break;
+			pbuf = buf;
+			while (count > 0) {
+				res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
+				if (res < 0) {
+					snd_midi_event_reset_encode(vmidi->parser);
+					continue;
+				}
+				snd_rawmidi_transmit_ack(substream, res);
+				pbuf += res;
+				count -= res;
+				if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+					if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
+						return;
+					vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+				}
+			}
+		}
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * open rawmidi handle for input
+ */
+static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	snd_virmidi_t *vmidi;
+	unsigned long flags;
+
+	vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(0, &vmidi->parser) < 0) {
+		kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;	
+	runtime->private_data = vmidi;
+	write_lock_irqsave(&rdev->filelist_lock, flags);
+	list_add_tail(&vmidi->list, &rdev->filelist);
+	write_unlock_irqrestore(&rdev->filelist_lock, flags);
+	vmidi->rdev = rdev;
+	return 0;
+}
+
+/*
+ * open rawmidi handle for output
+ */
+static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
+	snd_rawmidi_runtime_t *runtime = substream->runtime;
+	snd_virmidi_t *vmidi;
+
+	vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
+		kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;
+	snd_virmidi_init_event(vmidi, &vmidi->event);
+	vmidi->rdev = rdev;
+	runtime->private_data = vmidi;
+	return 0;
+}
+
+/*
+ * close rawmidi handle for input
+ */
+static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_t *vmidi = substream->runtime->private_data;
+	snd_midi_event_free(vmidi->parser);
+	list_del(&vmidi->list);
+	substream->runtime->private_data = NULL;
+	kfree(vmidi);
+	return 0;
+}
+
+/*
+ * close rawmidi handle for output
+ */
+static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream)
+{
+	snd_virmidi_t *vmidi = substream->runtime->private_data;
+	snd_midi_event_free(vmidi->parser);
+	substream->runtime->private_data = NULL;
+	kfree(vmidi);
+	return 0;
+}
+
+/*
+ * subscribe callback - allow output to rawmidi device
+ */
+static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = private_data;
+	if (!try_module_get(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
+	return 0;
+}
+
+/*
+ * unsubscribe callback - disallow output to rawmidi device
+ */
+static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = private_data;
+	rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
+	module_put(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ * use callback - allow input to rawmidi device
+ */
+static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = private_data;
+	if (!try_module_get(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_USE;
+	return 0;
+}
+
+/*
+ * unuse callback - disallow input to rawmidi device
+ */
+static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_virmidi_dev_t *rdev;
+
+	rdev = private_data;
+	rdev->flags &= ~SNDRV_VIRMIDI_USE;
+	module_put(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ *  Register functions
+ */
+
+static snd_rawmidi_ops_t snd_virmidi_input_ops = {
+	.open = snd_virmidi_input_open,
+	.close = snd_virmidi_input_close,
+	.trigger = snd_virmidi_input_trigger,
+};
+
+static snd_rawmidi_ops_t snd_virmidi_output_ops = {
+	.open = snd_virmidi_output_open,
+	.close = snd_virmidi_output_close,
+	.trigger = snd_virmidi_output_trigger,
+};
+
+/*
+ * create a sequencer client and a port
+ */
+static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
+{
+	int client;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_port_callback_t pcallbacks;
+	snd_seq_client_info_t *info;
+	snd_seq_port_info_t *pinfo;
+	int err;
+
+	if (rdev->client >= 0)
+		return 0;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
+	if (! info || ! pinfo) {
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = rdev;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+	client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks);
+	if (client < 0) {
+		err = client;
+		goto __error;
+	}
+	rdev->client = client;
+
+	/* set client name */
+	memset(info, 0, sizeof(*info));
+	info->client = client;
+	info->type = KERNEL_CLIENT;
+	sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
+
+	/* create a port */
+	memset(pinfo, 0, sizeof(*pinfo));
+	pinfo->addr.client = client;
+	sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
+	/* set all capabilities */
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	pinfo->midi_channels = 16;
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.private_data = rdev;
+	pcallbacks.subscribe = snd_virmidi_subscribe;
+	pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
+	pcallbacks.use = snd_virmidi_use;
+	pcallbacks.unuse = snd_virmidi_unuse;
+	pcallbacks.event_input = snd_virmidi_event_input;
+	pinfo->kernel = &pcallbacks;
+	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
+	if (err < 0) {
+		snd_seq_delete_kernel_client(client);
+		rdev->client = -1;
+		goto __error;
+	}
+
+	rdev->port = pinfo->addr.port;
+	err = 0; /* success */
+
+ __error:
+	kfree(info);
+	kfree(pinfo);
+	return err;
+}
+
+
+/*
+ * release the sequencer client
+ */
+static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev)
+{
+	if (rdev->client >= 0) {
+		snd_seq_delete_kernel_client(rdev->client);
+		rdev->client = -1;
+	}
+}
+
+/*
+ * register the device
+ */
+static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = rmidi->private_data;
+	int err;
+
+	switch (rdev->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		err = snd_virmidi_dev_attach_seq(rdev);
+		if (err < 0)
+			return err;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		if (rdev->client == 0)
+			return -EINVAL;
+		/* should check presence of port more strictly.. */
+		break;
+	default:
+		snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/*
+ * unregister the device
+ */
+static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = rmidi->private_data;
+
+	if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
+		snd_virmidi_dev_detach_seq(rdev);
+	return 0;
+}
+
+/*
+ *
+ */
+static snd_rawmidi_global_ops_t snd_virmidi_global_ops = {
+	.dev_register = snd_virmidi_dev_register,
+	.dev_unregister = snd_virmidi_dev_unregister,
+};
+
+/*
+ * free device
+ */
+static void snd_virmidi_free(snd_rawmidi_t *rmidi)
+{
+	snd_virmidi_dev_t *rdev = rmidi->private_data;
+	kfree(rdev);
+}
+
+/*
+ * create a new device
+ *
+ */
+/* exported */
+int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi)
+{
+	snd_rawmidi_t *rmidi;
+	snd_virmidi_dev_t *rdev;
+	int err;
+	
+	*rrmidi = NULL;
+	if ((err = snd_rawmidi_new(card, "VirMidi", device,
+				   16,	/* may be configurable */
+				   16,	/* may be configurable */
+				   &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, rmidi->id);
+	rdev = kcalloc(1, sizeof(*rdev), GFP_KERNEL);
+	if (rdev == NULL) {
+		snd_device_free(card, rmidi);
+		return -ENOMEM;
+	}
+	rdev->card = card;
+	rdev->rmidi = rmidi;
+	rdev->device = device;
+	rdev->client = -1;
+	rwlock_init(&rdev->filelist_lock);
+	INIT_LIST_HEAD(&rdev->filelist);
+	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+	rmidi->private_data = rdev;
+	rmidi->private_free = snd_virmidi_free;
+	rmidi->ops = &snd_virmidi_global_ops;
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
+	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+			    SNDRV_RAWMIDI_INFO_OUTPUT |
+			    SNDRV_RAWMIDI_INFO_DUPLEX;
+	*rrmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static int __init alsa_virmidi_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_virmidi_exit(void)
+{
+}
+
+module_init(alsa_virmidi_init)
+module_exit(alsa_virmidi_exit)
+
+EXPORT_SYMBOL(snd_virmidi_new);
+EXPORT_SYMBOL(snd_virmidi_receive);