Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 25 Apr 2005 12:55:45 -0500
From:      "Conrad J. Sabatier" <conrads@cox.net>
To:        Andre Guibert de Bruet <andy@siliconlandmark.com>
Cc:        Mathew Kanner <mat@cnd.mcgill.ca>
Subject:   Latest MIDI patchset from Mat
Message-ID:  <20050425125545.0dcbf4ec@dolphin.local.net>
In-Reply-To: <20050423161839.P68772@lexi.siliconlandmark.com>
References:  <20050418232455.2d530890@dolphin.local.net> <5265.1113888748@critter.freebsd.dk> <20050419130048.6e545269@dolphin.local.net> <20050423023605.A68772@lexi.siliconlandmark.com> <20050423135112.63c74617@dolphin.local.net> <20050423161839.P68772@lexi.siliconlandmark.com>

index | next in thread | previous in thread | raw e-mail

[-- Attachment #1 --]
On Sat, 23 Apr 2005 16:40:44 -0400 (EDT), Andre Guibert de Bruet
<andy@siliconlandmark.com> wrote:
>
> I have an emu10k1, so I guess I could give it a spin. Any ideas for
> the  whereabout of these patches?
> 
> My synth is an S08.

I've attached the latest patchset Mat sent me.  They *should* apply
cleanly to STABLE, but will yield a couple of failed hunks when applied
to CURRENT, which are quite simple to fix by hand.

Just do the following:

cd /usr/src
patch -p0 < /path/to/midi2-RELENG_5-mar05.diff

Let us know if you need any help.  And also, do be sure to let us know
if you're successful with these!  :-)

Thanks!

-- 
Conrad J. Sabatier <conrads@cox.net> -- "In Unix veritas"

[-- Attachment #2 --]
Index: sys/conf/kmod.mk
===================================================================
RCS file: /home/ncvs/src/sys/conf/kmod.mk,v
retrieving revision 1.166.2.1
diff -u -r1.166.2.1 kmod.mk
--- sys/conf/kmod.mk	25 Feb 2005 21:45:55 -0000	1.166.2.1
+++ sys/conf/kmod.mk	5 Mar 2005 16:29:14 -0000
@@ -299,7 +299,8 @@
 	dev/usb/usb_if.m isa/isa_if.m \
 	kern/bus_if.m kern/cpufreq_if.m kern/device_if.m \
 	libkern/iconv_converter_if.m opencrypto/crypto_if.m \
-	pc98/pc98/canbus_if.m pci/agp_if.m sparc64/pci/ofw_pci_if.m
+	pc98/pc98/canbus_if.m pci/agp_if.m sparc64/pci/ofw_pci_if.m \
+ 	dev/sound/midi/mpu_if.m dev/sound/midi/mpufoi_if.m dev/sound/midi/synth_if.m
 
 .for _srcsrc in ${MFILES}
 .for _ext in c h
Index: sys/dev/sound/midi/midi.c
===================================================================
RCS file: sys/dev/sound/midi/midi.c
diff -N sys/dev/sound/midi/midi.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/midi.c	5 Mar 2005 17:07:39 -0000
@@ -0,0 +1,1514 @@
+/*
+ * (c) 2003 Mathew Kanner
+ * 
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@netbsd.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ /*
+  * Parts of this file started out as NetBSD: midi.c 1.31
+  * They are mostly gone.  Still the most obvious will be the state
+  * machine midi_in
+  */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/conf.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/sbuf.h>
+#include <sys/kobj.h>
+#include <sys/module.h>
+
+#include <dev/sound/midi/midi.h>
+#include "mpu_if.h"
+
+#include <dev/sound/midi/midiq.h>
+#include "synth_if.h"
+MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
+
+
+#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
+#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
+
+#define MIDI_DEV_RAW	2
+#define MIDI_DEV_MIDICTL 12
+
+enum midi_states {
+	MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA
+};
+
+/*
+ * The MPU interface current has init() uninit() inqsize(( outqsize()
+ * callback() : fiddle with the tx|rx status.
+ */
+
+#include "mpu_if.h"
+
+/*
+ * /dev/rmidi	Structure definitions
+ */
+
+#define MIDI_NAMELEN   16
+struct snd_midi {
+	KOBJ_FIELDS;
+	struct mtx      lock;	/* Protects all but queues */
+	void           *cookie;
+
+	int             unit;	/* Should only be used in midistat */
+	int             channel;/* Should only be used in midistat */
+
+	int             busy;
+	int             flags;	/* File flags */
+	char            name[MIDI_NAMELEN];
+	struct mtx      qlock;	/* Protects inq, outq and flags */
+	                MIDIQ_HEAD(, char)inq, outq;
+	int             rchan, wchan;
+	struct selinfo  rsel, wsel;
+	int             hiwat;	/* QLEN(outq)>High-water -> disable writes
+		     * from userland */
+	enum midi_states inq_state;
+	int             inq_status, inq_left;	/* Variables for the state
+			     * machine in Midi_in, this
+			     * is to provide that signals
+			     * only get issued only
+			     * complete command packets. */
+	struct proc    *async;
+	struct cdev    *dev;
+	struct synth_midi *synth;
+	int		synth_flags;
+	TAILQ_ENTRY(snd_midi) link;
+};
+
+struct synth_midi {
+    KOBJ_FIELDS;
+    struct snd_midi *m;
+};
+
+static synth_open_t midisynth_open;
+static synth_close_t midisynth_close;
+static synth_writeraw_t midisynth_writeraw;
+static synth_killnote_t midisynth_killnote;
+static synth_startnote_t midisynth_startnote;
+static synth_setinstr_t midisynth_setinstr;
+static synth_alloc_t midisynth_alloc;
+static synth_controller_t midisynth_controller;
+static synth_bender_t midisynth_bender;
+
+
+static kobj_method_t midisynth_methods[] = {
+            KOBJMETHOD(synth_open,midisynth_open),
+            KOBJMETHOD(synth_close,midisynth_close),
+            KOBJMETHOD(synth_writeraw,midisynth_writeraw),
+            KOBJMETHOD(synth_setinstr,midisynth_setinstr),
+            KOBJMETHOD(synth_startnote,midisynth_startnote),
+            KOBJMETHOD(synth_killnote,midisynth_killnote),
+	    KOBJMETHOD(synth_alloc, midisynth_alloc),
+	    KOBJMETHOD(synth_controller, midisynth_controller),
+	    KOBJMETHOD(synth_bender, midisynth_bender),
+            { 0, 0 }
+};
+
+DEFINE_CLASS(midisynth, midisynth_methods, 0);
+
+/*
+ * Module Exports & Interface
+ * 
+ * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan) int
+ * midi_uninit(struct snd_midi *) 0 == no error EBUSY or other error int
+ * Midi_in(struct midi_chan *, char *buf, int count) int Midi_out(struct
+ * midi_chan *, char *buf, int count)
+ * 
+ * midi_{in,out} return actual size transfered
+ * 
+ */
+
+
+/*
+ * midi_devs tailq, holder of all rmidi instances protected by midistat_lock
+ */
+
+TAILQ_HEAD(, snd_midi) midi_devs;
+
+/*
+ * /dev/midistat variables and declarations, protected by midistat_lock
+ */
+
+static struct mtx midistat_lock;
+static int      midistat_isopen = 0;
+static struct sbuf midistat_sbuf;
+static int      midistat_bufptr;
+static struct cdev *midistat_dev;
+
+/*
+ * /dev/midistat	dev_t declarations
+ */
+
+static d_open_t midistat_open;
+static d_close_t midistat_close;
+static d_read_t midistat_read;
+
+#define MIDI_MAJOR 30
+
+static struct cdevsw midistat_cdevsw = {
+	.d_version = D_VERSION,
+	.d_open = midistat_open,
+	.d_close = midistat_close,
+	.d_read = midistat_read,
+	.d_name = "midistat",
+	.d_maj = MIDI_MAJOR,
+};
+
+
+/*
+ * /dev/rmidi dev_t declarations, struct variable access is protected by
+ * locks contained within the structure.
+ */
+
+static d_open_t midi_open;
+static d_close_t midi_close;
+static d_ioctl_t midi_ioctl;
+static d_read_t midi_read;
+static d_write_t midi_write;
+static d_poll_t midi_poll;
+
+static struct cdevsw midi_cdevsw = {
+	.d_version = D_VERSION,
+	.d_open = midi_open,
+	.d_close = midi_close,
+	.d_read = midi_read,
+	.d_write = midi_write,
+	.d_ioctl = midi_ioctl,
+	.d_poll = midi_poll,
+	.d_name = "rmidi",
+	.d_maj = MIDI_MAJOR,
+};
+
+/*
+ * Prototypes of library functions
+ */
+
+static int      midi_destroy(struct snd_midi *, int);
+static int      midistat_prepare(struct sbuf * s);
+static int      midi_load(void);
+static int      midi_unload(void);
+
+/*
+ * Misc declr.
+ */
+SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver");
+SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD, 0, "Status device");
+
+int             midi_debug;
+SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
+
+int             midi_dumpraw;
+SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, "");
+
+int             midi_instroff;
+SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, "");
+
+int             midistat_verbose;
+SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW, 
+	&midistat_verbose, 0, "");
+
+#define MIDI_DEBUG(l,a)	if(midi_debug>=l) a
+/*
+ * CODE START
+ */
+
+/*
+ * Register a new rmidi device. cls midi_if interface unit == 0 means
+ * auto-assign new unit number unit != 0 already assigned a unit number, eg.
+ * not the first channel provided by this device. channel,	sub-unit
+ * cookie is passed back on MPU calls Typical device drivers will call with
+ * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care
+ * what unit number is used.
+ * 
+ * It is an error to call midi_init with an already used unit/channel combo.
+ * 
+ * Returns NULL on error
+ * 
+ */
+struct snd_midi *
+midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
+{
+	struct snd_midi *m;
+	int             i;
+	int             inqsize, outqsize;
+	MIDI_TYPE      *buf;
+
+	MIDI_DEBUG(1,printf("midiinit: unit %d/%d.\n", unit, channel));
+	mtx_lock(&midistat_lock);
+	/*
+	 * Protect against call with existing unit/channel or auto-allocate a
+	 * new unit number.
+	 */
+	i = -1;
+	TAILQ_FOREACH(m, &midi_devs, link) {
+	    mtx_lock(&m->lock);
+	    if (unit != 0) {
+	    if (m->unit == unit && m->channel == channel) {
+	        mtx_unlock(&m->lock);
+		goto err0;
+		}
+	    } else {
+	/*
+	 * Find a better unit number
+	 */
+	    if (m->unit > i)
+		i = m->unit;
+	    }
+	    mtx_unlock(&m->lock);
+	}
+
+	if (unit == 0)
+	    unit = i + 1;
+
+	MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel));
+	m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO);
+	if (m == NULL)
+	    goto err0;
+
+	m->synth = malloc(sizeof(*m->synth), M_MIDI, M_NOWAIT | M_ZERO);
+	kobj_init((kobj_t)m->synth, &midisynth_class);
+	m->synth->m = m;
+	kobj_init((kobj_t)m, cls);
+	inqsize = MPU_INQSIZE(m, cookie);
+	outqsize = MPU_OUTQSIZE(m, cookie);
+
+	MIDI_DEBUG(1, printf("midiinit queues %d/%d.\n", inqsize, outqsize));
+	if (!inqsize && !outqsize)
+	    goto err1;
+
+	mtx_init(&m->lock, "raw midi", 0, 0);
+	mtx_init(&m->qlock, "q raw midi", 0, 0);
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if (inqsize)
+	    buf = malloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_NOWAIT);
+	else
+	    buf = NULL;
+
+	MIDIQ_INIT(m->inq, buf, inqsize);
+
+	if (outqsize)
+	    buf = malloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_NOWAIT);
+	else
+	    buf = NULL;
+	m->hiwat = outqsize / 2;
+
+	MIDIQ_INIT(m->outq, buf, outqsize);
+
+	if ((inqsize && !MIDIQ_BUF(m->inq)) ||
+	    (outqsize && !MIDIQ_BUF(m->outq)))
+	    goto err2;
+
+
+	m->busy = 0;
+	m->flags = 0;
+	m->unit = unit;
+	m->channel = channel;
+	m->cookie = cookie;
+
+	if (MPU_INIT(m, cookie))
+	    goto err2;
+
+	mtx_unlock(&m->lock);
+	mtx_unlock(&m->qlock);
+
+	TAILQ_INSERT_TAIL(&midi_devs, m, link);
+
+	mtx_unlock(&midistat_lock);
+
+	m->dev = make_dev(&midi_cdevsw,
+			MIDIMKMINOR(unit, MIDI_DEV_RAW, channel),
+	         	UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel);
+	m->dev->si_drv1 = m;
+
+	return m;
+
+err2:	mtx_destroy(&m->qlock);
+	mtx_destroy(&m->lock);
+
+	if (MIDIQ_BUF(m->inq))
+	    free(MIDIQ_BUF(m->inq), M_MIDI);
+	if (MIDIQ_BUF(m->outq))
+	    free(MIDIQ_BUF(m->outq), M_MIDI);
+err1:	free(m, M_MIDI);
+err0:	mtx_unlock(&midistat_lock);
+	MIDI_DEBUG(1, printf("midi_init ended in error\n"));
+	return NULL;
+}
+
+/*
+ * midi_uninit does not call MIDI_UNINIT, as since this is the implementors
+ * entry point. midi_unint if fact, does not send any methods. A call to
+ * midi_uninit is a defacto promise that you won't manipulate ch anymore
+ * 
+ */
+
+int
+midi_uninit(struct snd_midi * m)
+{
+	int             err;
+
+	err = ENXIO;
+	mtx_lock(&midistat_lock);
+	mtx_lock(&m->lock);
+	if (m->busy) {
+	    if (!(m->rchan || m->wchan))
+		goto err;
+
+	    if (m->rchan) {
+		wakeup(&m->rchan);
+		m->rchan = 0;
+	    }
+	    if (m->wchan) {
+		wakeup(&m->wchan);
+		m->wchan = 0;
+	    }
+	}
+	err = midi_destroy(m, 0);
+	if(!err)
+		goto exit;
+
+err:	mtx_unlock(&m->lock);
+exit:	mtx_unlock(&midistat_lock);
+	return err;
+}
+
+/*
+ * midi_in: process all data until the queue is full, then discards the rest.
+ * Since midi_in is a state machine, data discards can cause it to get out of
+ * whack.  Process as much as possible.  It calls, wakeup, selnotify and
+ * psignal at most once.
+ */
+
+#if notdef
+static int      midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
+#endif /* notdef */
+/* Number of bytes in a MIDI command */
+#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
+#define MIDI_ACK	0xfe
+#define MIDI_IS_STATUS(d) ((d) >= 0x80)
+#define MIDI_IS_COMMON(d) ((d) >= 0xf0)
+
+#define MIDI_SYSEX_START	0xF0
+#define MIDI_SYSEX_END	    0xF7
+
+
+int
+midi_in(struct snd_midi * m, MIDI_TYPE * buf, int size)
+{
+	/* int             i, sig, enq; */
+	int             used;
+	/* MIDI_TYPE       data; */
+	MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size));
+
+/*
+ * XXX: locking flub
+ */
+	if (!(m->flags & M_RX))
+	    return size;
+
+	used = 0;
+
+	mtx_lock(&m->qlock);
+#if 0
+	/*
+	 * Don't bother queuing if not in read mode.  Discard everything and
+	 * return size so the caller doesn't freak out.
+	 */
+
+	if (!(m->flags & M_RX))
+	    return size;
+
+	for (i = sig = 0; i < size; i++) {
+
+	    data = buf[i];
+	    enq = 0;
+	    if (data == MIDI_ACK)
+		continue;
+
+	    switch (m->inq_state) {
+	    case MIDI_IN_START:
+		if (MIDI_IS_STATUS(data)) {
+		    switch (data) {
+		    case 0xf0:	/* Sysex */
+			m->inq_state = MIDI_IN_SYSEX;
+			break;
+		    case 0xf1:	/* MTC quarter frame */
+		    case 0xf3:	/* Song select */
+			m->inq_state = MIDI_IN_DATA;
+			enq = 1;
+			m->inq_left = 1;
+			break;
+		    case 0xf2:	/* Song position pointer */
+			m->inq_state = MIDI_IN_DATA;
+			enq = 1;
+			m->inq_left = 2;
+			break;
+		    default:
+			if (MIDI_IS_COMMON(data)) {
+			    enq = 1;
+			    sig = 1;
+			} else {
+			    m->inq_state = MIDI_IN_DATA;
+			    enq = 1;
+			    m->inq_status = data;
+			    m->inq_left = MIDI_LENGTH(data);
+			}
+			break;
+		    }
+		} else if (MIDI_IS_STATUS(m->inq_status)) {
+		    m->inq_state = MIDI_IN_DATA;
+		    if (!MIDIQ_FULL(m->inq)) {
+			used++;
+			MIDIQ_ENQ(m->inq, &m->inq_status, 1);
+		    }
+		    enq = 1;
+		    m->inq_left = MIDI_LENGTH(m->inq_status) - 1;
+		}
+		break;
+		/*
+		 * End of case MIDI_IN_START:
+		 */
+
+	    case MIDI_IN_DATA:
+		enq = 1;
+		if (--m->inq_left <= 0)
+		    sig = 1;	/* deliver data */
+		break;
+	    case MIDI_IN_SYSEX:
+		if (data == MIDI_SYSEX_END)
+		    m->inq_state = MIDI_IN_START;
+		break;
+	    }
+
+	    if (enq)
+		if (!MIDIQ_FULL(m->inq)) {
+		    MIDIQ_ENQ(m->inq, &data, 1);
+		    used++;
+		}
+	    /*
+	     * End of the state machines main "for loop"
+	     */
+	}
+	if (sig) {
+#endif
+	MIDI_DEBUG(6, printf("midi_in: len %jd avail %jd\n", (intmax_t)MIDIQ_LEN(m->inq), (intmax_t)MIDIQ_AVAIL(m->inq))) ;
+	if (MIDIQ_AVAIL(m->inq) > size) {
+		used=size;
+		MIDIQ_ENQ(m->inq, buf, size);
+	} else {
+		MIDI_DEBUG(4,printf("midi_in: Discarding data qu\n"));
+		mtx_unlock(&m->qlock);
+		return 0;
+	}
+	    if (m->rchan) {
+		wakeup(&m->rchan);
+		m->rchan = 0;
+	    }
+	    selwakeup(&m->rsel);
+	    if (m->async) {
+		PROC_LOCK(m->async);
+		psignal(m->async, SIGIO);
+		PROC_UNLOCK(m->async);
+	    }
+#if 0
+	}
+#endif
+	mtx_unlock(&m->qlock);
+	return used;
+}
+
+/*
+ * midi_out: The only clearer of the M_TXEN flag.
+ */
+int
+midi_out(struct snd_midi * m, MIDI_TYPE * buf, int size)
+{
+	int             used;
+
+/*
+ * XXX: locking flub
+ */
+	if (!(m->flags & M_TXEN))
+		return 0;
+
+	MIDI_DEBUG(2, printf("midi_out: %p\n", m)); 
+	mtx_lock(&m->qlock);
+	used = MIN(size, MIDIQ_LEN(m->outq));
+	MIDI_DEBUG(3, printf("midi_out: used %d\n", used));
+	if (used)
+	    MIDIQ_DEQ(m->outq, buf, used);
+	if (MIDIQ_EMPTY(m->outq)) {
+	    m->flags &= ~M_TXEN;
+	    MPU_CALLBACKP(m, m->cookie, m->flags);
+	}
+	if (used && MIDIQ_AVAIL(m->outq) > m->hiwat ) {
+		if (m->wchan) {
+			wakeup(&m->wchan);
+			m->wchan = 0;
+		}
+		selwakeup(&m->wsel);
+		 if (m->async) {
+			PROC_LOCK(m->async);
+			psignal(m->async, SIGIO);
+			PROC_UNLOCK(m->async);
+		}
+	}
+	mtx_unlock(&m->qlock);
+	return used;
+}
+
+
+/*
+ * /dev/rmidi#.#	device access functions
+ */
+int
+midi_open(struct cdev *i_dev, int flags, int mode, struct thread * td)
+{
+	struct snd_midi *m = i_dev->si_drv1;
+	int             retval;
+
+	MIDI_DEBUG(1,printf("midiopen %p %s %s\n", td,
+		flags & FREAD?"M_RX":"", flags & FWRITE?"M_TX":""));
+	if (m == NULL)
+	    return ENXIO;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	retval = 0;
+
+	if (flags & FREAD) {
+	    if (MIDIQ_SIZE(m->inq) == 0)
+		retval = ENXIO;
+	    else if (m->flags & M_RX)
+		retval = EBUSY;
+	    if (retval)
+		goto err;
+	}
+	if (flags & FWRITE) {
+	    if (MIDIQ_SIZE(m->outq) == 0)
+		retval = ENXIO;
+	    else if (m->flags & M_TX)
+		retval = EBUSY;
+	    if (retval)
+		goto err;
+	}
+	m->busy++;
+
+	m->rchan = 0;
+	m->wchan = 0;
+	m->async = 0;
+
+	if (flags & FREAD) {
+	    m->flags |= M_RX | M_RXEN;
+	    /*
+	     * Only clear the inq, the outq might still have data to drain from
+	     * a previous session
+	     */
+	    MIDIQ_CLEAR(m->inq);
+	};
+
+	if (flags & FWRITE)
+	    m->flags |= M_TX;
+
+	MPU_CALLBACK(m, m->cookie, m->flags);
+
+	MIDI_DEBUG(2, printf("midi_open: opened.\n"));
+
+err:	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+	return retval;
+}
+
+int
+midi_close(struct cdev *i_dev, int flags, int mode, struct thread * td)
+{
+	struct snd_midi *m = i_dev->si_drv1;
+	int             retval;
+	int		oldflags;
+
+	MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td,
+		flags & FREAD?"M_RX":"", flags & FWRITE?"M_TX":""));
+
+	if (m == NULL)
+	    return           ENXIO;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if ( (flags & FREAD && !(m->flags & M_RX)) || 
+	     (flags & FWRITE && !(m->flags & M_TX)) ) {
+	    retval = ENXIO;
+	    goto err;
+	}
+
+	m->busy--;
+
+	oldflags = m->flags;
+
+	if (flags & FREAD)
+	    m->flags &= ~(M_RX | M_RXEN);
+	if (flags & FWRITE)
+	    m->flags &= ~M_TX;
+
+	if( (m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)) )
+		MPU_CALLBACK(m, m->cookie, m->flags);
+
+	MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
+
+	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+	retval = 0;
+err:	return retval;
+}
+/*
+ * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon as data is available.
+ */
+int
+midi_read(struct cdev *i_dev, struct uio * uio, int ioflag)
+{
+#define MIDI_RSIZE 32
+	struct snd_midi *m = i_dev->si_drv1;
+	int             retval;
+	int             used;
+	char            buf[MIDI_RSIZE];
+
+	MIDI_DEBUG(5, printf("midiread: count=%lu\n", (unsigned long)uio->uio_resid));
+
+	retval = EIO;
+
+	if (m == NULL)
+	    goto err0;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if (!(m->flags & M_RX))
+	    goto err1;
+
+	while (uio->uio_resid > 0) {
+	    while (MIDIQ_EMPTY(m->inq)) {
+		retval = EWOULDBLOCK;
+		if (ioflag & O_NONBLOCK)
+		    goto err1;
+		mtx_unlock(&m->lock);
+		m->rchan = 1;
+		retval = msleep(&m->rchan, &m->qlock,
+			PCATCH | PDROP, "midi RX", 0);
+		/*
+		 * We slept, maybe things have changed since last
+		 * dying check
+		 */
+		if (retval == EINTR)
+		    goto err0;
+		if (m != i_dev->si_drv1)
+		    retval = ENXIO;
+		/* if (retval && retval != ERESTART) */
+		if (retval)
+		    goto err0;
+		mtx_lock(&m->lock);
+		mtx_lock(&m->qlock);
+		m->rchan = 0;
+		if (!m->busy)
+		    goto err1;
+	    }
+		MIDI_DEBUG(6, printf("midi_read start\n"));
+	    /*
+	     * At this point, it is certain that m->inq has data
+	     */
+
+	    used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
+	    used = MIN(used, MIDI_RSIZE);
+
+	    MIDI_DEBUG(6,printf("midiread: uiomove cc=%d\n", used));
+	    MIDIQ_DEQ(m->inq, buf, used);
+	    retval = uiomove(buf, used, uio);
+	    if (retval)
+		goto err1;
+	}
+
+	/*
+	 * If we Made it here then transfer is good
+	 */
+	retval = 0;
+err1:	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+err0:	MIDI_DEBUG(4, printf("midi_read: ret %d\n",retval));
+	return retval;
+}
+
+/*
+ * midi_write: The only setter of M_TXEN
+ */
+
+int
+midi_write(struct cdev *i_dev, struct uio * uio, int ioflag)
+{
+#define MIDI_WSIZE 32
+	struct snd_midi *m = i_dev->si_drv1;
+	int             retval;
+	int             used;
+	char            buf[MIDI_WSIZE];
+
+
+	MIDI_DEBUG(4, printf("midi_write\n")); 
+	retval = 0;
+	if (m == NULL)
+	    goto err0;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if (!(m->flags & M_TX))
+	    goto err1;
+
+	while (uio->uio_resid > 0) {
+	    while (MIDIQ_AVAIL(m->outq) == 0) {
+		retval = EWOULDBLOCK;
+		if (ioflag & O_NONBLOCK)
+		    goto err1;
+		mtx_unlock(&m->lock);
+		m->wchan = 1;
+		MIDI_DEBUG(3,printf("midi_write msleep\n"));
+		retval = msleep(&m->wchan, &m->qlock,
+			PCATCH | PDROP, "midi TX", 0);
+		/*
+		 * We slept, maybe things have changed since last
+		 * dying check
+		 */
+		if (retval == EINTR)
+		    goto err0;
+		if (m != i_dev->si_drv1)
+		    retval = ENXIO;
+		if (retval)
+		    goto err0;
+		mtx_lock(&m->lock);
+		mtx_lock(&m->qlock);
+		m->wchan = 0;
+		if (!m->busy)
+		    goto err1;
+	    }
+
+	    /*
+	     * We are certain than data can be placed on the queue
+	     */
+
+	    used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
+	    used = MIN(used, MIDI_WSIZE);
+		MIDI_DEBUG(5,printf("midiout: resid %d len %jd avail %jd\n", uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq), (intmax_t)MIDIQ_AVAIL(m->outq)));
+
+
+	    MIDI_DEBUG(5,printf("midi_write: uiomove cc=%d\n", used));
+	    retval = uiomove(buf, used, uio);
+	    if (retval)
+		goto err1;
+	    MIDIQ_ENQ(m->outq, buf, used);
+	    /*
+	     * Inform the bottom half that data can be written
+	     */
+	    if (!(m->flags & M_TXEN)) {
+		m->flags |= M_TXEN;
+		MPU_CALLBACK(m, m->cookie, m->flags);
+	    }
+	}
+	/*
+	 * If we Made it here then transfer is good
+	 */
+	retval = 0;
+err1:	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+err0:	return retval;
+}
+
+int
+midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * td)
+{
+	return ENXIO;
+}
+
+int
+midi_poll(struct cdev *i_dev, int events, struct thread * td)
+{
+	struct snd_midi *m = i_dev->si_drv1;
+	int             revents;
+
+	if (m == NULL)
+	    return 0;
+
+	revents = 0;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if (events & (POLLIN | POLLRDNORM))
+	    if (!MIDIQ_EMPTY(m->inq))
+		events |= events & (POLLIN | POLLRDNORM);
+
+	if (events & (POLLOUT | POLLWRNORM))
+	    if (MIDIQ_AVAIL(m->outq) < m->hiwat)
+		events |= events & (POLLOUT | POLLWRNORM);
+
+	if (revents == 0) {
+	    if (events & (POLLIN | POLLRDNORM))
+		selrecord(td, &m->rsel);
+
+	    if (events & (POLLOUT | POLLWRNORM))
+		selrecord(td, &m->wsel);
+	}
+	mtx_unlock(&m->lock);
+	mtx_unlock(&m->qlock);
+
+	return (revents);
+}
+
+/*
+ * /dev/midistat device functions
+ * 
+ */
+static int
+midistat_open(struct cdev *i_dev, int flags, int mode, struct thread * td)
+{
+	int             error;
+
+
+	MIDI_DEBUG(1,printf("midistat_open\n"));
+	mtx_lock(&midistat_lock);
+
+	if (midistat_isopen) {
+	    mtx_unlock(&midistat_lock);
+	    return EBUSY;
+	}
+	midistat_isopen = 1;
+
+	if (sbuf_new(&midistat_sbuf, NULL, 4096, 0) == NULL) {
+	    error = ENXIO;
+	    goto out;
+	}
+	midistat_bufptr = 0;
+	error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
+
+out:	if (error)
+	    midistat_isopen = 0;
+	mtx_unlock(&midistat_lock);
+	return error;
+}
+
+static int
+midistat_close(struct cdev *i_dev, int flags, int mode, struct thread * td)
+{
+	intrmask_t      s;
+
+	MIDI_DEBUG(1,printf("midistat_close\n"));
+	mtx_lock(&midistat_lock);
+	if (!midistat_isopen) {
+	    mtx_unlock(&midistat_lock);
+	    return EBADF;
+	}
+	sbuf_delete(&midistat_sbuf);
+	midistat_isopen = 0;
+
+	mtx_unlock(&midistat_lock);
+	splx(s);
+	return 0;
+}
+
+static int
+midistat_read(struct cdev *i_dev, struct uio * buf, int flag)
+{
+	int             l, err;
+
+	MIDI_DEBUG(4,printf("midistat_read\n"));
+	mtx_lock(&midistat_lock);
+	if (!midistat_isopen) {
+	    mtx_unlock(&midistat_lock);
+	    return           EBADF;
+	}
+	l = min(buf->uio_resid, sbuf_len(&midistat_sbuf) - midistat_bufptr);
+	err = 0;
+	if (l > 0)
+	    err = uiomove(sbuf_data(&midistat_sbuf) + midistat_bufptr, l, buf);
+	else
+	    l = 0;
+	midistat_bufptr += l;
+	mtx_unlock(&midistat_lock);
+	return err;
+}
+
+/*
+ * Module library functions
+ */
+
+static int
+midistat_prepare(struct sbuf * s)
+{
+	struct snd_midi *m;
+
+	mtx_assert(&midistat_lock, MA_OWNED);
+
+	sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n");
+	if (TAILQ_EMPTY(&midi_devs)) {
+	    sbuf_printf(s, "No devices installed.\n");
+	    sbuf_finish(s);
+	    return sbuf_len(s);
+	}
+	sbuf_printf(s, "Installed devices:\n");
+
+	TAILQ_FOREACH(m, &midi_devs, link) {
+	    mtx_lock(&m->lock);
+	    sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel,
+		    MPU_PROVIDER(m, m->cookie));
+	    sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose));
+	    sbuf_printf(s, "\n");
+	    mtx_unlock(&m->lock);
+	}
+
+	sbuf_finish(s);
+	return sbuf_len(s);
+}
+
+#if notdef
+/*
+ * Convert IOCTL command to string for debugging
+ */
+
+static char    *
+midi_cmdname(int cmd)
+{
+	static struct {
+	    int             cmd;
+	    char           *name;
+	}              *tab, cmdtab_midiioctl[] = {
+#define A(x)	{x, ## x}
+	    /*
+	     * Once we have some real IOCTLs define, the following will
+	     * be relavant.
+	     * 
+	     * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE),
+	     * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO),
+	     * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL),
+	     * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE),
+	     * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE),
+	     * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT),
+	     * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC),
+	     * A(AIOGCAP),
+	     */
+#undef A
+	    {
+		-1, "unknown"
+	    },
+	};
+
+	for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++)
+	    ;
+	return tab->name;
+}
+#endif /* notdef */
+
+/*
+ * midisynth
+ */
+
+
+int
+midisynth_open(void *n, void *arg, int flags)
+{
+	struct snd_midi *m = ((struct synth_midi * ) n)->m;
+	int             retval;
+
+	MIDI_DEBUG(1,printf("midisynth_open %s %s\n",
+		flags & FREAD?"M_RX":"", flags & FWRITE?"M_TX":""));
+
+	if (m == NULL)
+	    return ENXIO;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	retval = 0;
+
+	if (flags & FREAD) {
+	    if (MIDIQ_SIZE(m->inq) == 0)
+		retval = ENXIO;
+	    else if (m->flags & M_RX)
+		retval = EBUSY;
+	    if (retval)
+		goto err;
+	}
+	if (flags & FWRITE) {
+	    if (MIDIQ_SIZE(m->outq) == 0)
+		retval = ENXIO;
+	    else if (m->flags & M_TX)
+		retval = EBUSY;
+	    if (retval)
+		goto err;
+	}
+	m->busy++;
+
+	/*
+	 * TODO: Consider m->async = 0;
+	 */
+
+	if (flags & FREAD) {
+	    m->flags |= M_RX | M_RXEN;
+	    /*
+	     * Only clear the inq, the outq might still have data to drain from
+	     * a previous session
+	     */
+	    MIDIQ_CLEAR(m->inq);
+	    m->rchan = 0;
+	};
+
+	if (flags & FWRITE) {
+	    m->flags |= M_TX;
+	    m->wchan = 0;
+	}
+
+	m->synth_flags = flags & (FREAD | FWRITE);
+	
+	MPU_CALLBACK(m, m->cookie, m->flags);
+
+
+err:	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+	MIDI_DEBUG(2, printf("midisynth_open: return %d.\n", retval));
+	return retval;
+}
+
+int
+midisynth_close(void *n)
+{
+	struct snd_midi *m = ((struct synth_midi *)n)->m;
+	int             retval;
+	int		oldflags;
+
+	MIDI_DEBUG(1, printf("midisynth_close %s %s\n",
+		m->synth_flags & FREAD ? "M_RX" : "", 
+		m->synth_flags & FWRITE ? "M_TX" : ""));
+
+	if (m == NULL)
+	    return ENXIO;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if ( (m->synth_flags & FREAD && !(m->flags & M_RX)) || 
+	     (m->synth_flags & FWRITE && !(m->flags & M_TX)) ) {
+	    retval = ENXIO;
+	    goto err;
+	}
+
+	m->busy--;
+
+	oldflags = m->flags;
+
+	if (m->synth_flags & FREAD)
+	    m->flags &= ~(M_RX | M_RXEN);
+	if (m->synth_flags & FWRITE)
+	    m->flags &= ~M_TX;
+
+	if( (m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)) )
+		MPU_CALLBACK(m, m->cookie, m->flags);
+
+	MIDI_DEBUG(1, printf("midi_close: closed, busy = %d.\n", m->busy));
+
+	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+	retval = 0;
+err:	return retval;
+}
+
+/*
+ * Always blocking.
+ */
+
+int
+midisynth_writeraw(void *n, uint8_t *buf, size_t len)
+{
+	struct snd_midi	*m = ((struct synth_midi *)n)->m;
+	int             retval;
+	int             used;
+	int i;
+
+	MIDI_DEBUG(4, printf("midisynth_writeraw\n")); 
+
+	retval = 0;
+
+	if (m == NULL)
+	    return ENXIO;
+
+	mtx_lock(&m->lock);
+	mtx_lock(&m->qlock);
+
+	if (!(m->flags & M_TX))
+	    goto err1;
+
+	if (midi_dumpraw)
+	    printf("midi dump: ");
+
+	while (len > 0) {
+	    while (MIDIQ_AVAIL(m->outq) == 0) {
+	    if (!(m->flags & M_TXEN)) {
+		m->flags |= M_TXEN;
+		MPU_CALLBACK(m, m->cookie, m->flags);
+	    }
+		mtx_unlock(&m->lock);
+		m->wchan = 1;
+		MIDI_DEBUG(3,printf("midisynth_writeraw msleep\n"));
+		retval = msleep(&m->wchan, &m->qlock,
+			PCATCH | PDROP, "midi TX", 0);
+		/*
+		 * We slept, maybe things have changed since last
+		 * dying check
+		 */
+		if (retval == EINTR)
+		    goto err0;
+
+		if (retval)
+		    goto err0;
+		mtx_lock(&m->lock);
+		mtx_lock(&m->qlock);
+		m->wchan = 0;
+		if (!m->busy)
+		    goto err1;
+	    }
+
+	    /*
+	     * We are certain than data can be placed on the queue
+	     */
+
+	    used = MIN(MIDIQ_AVAIL(m->outq), len);
+	    used = MIN(used, MIDI_WSIZE);
+	    MIDI_DEBUG(5,printf("midi_synth: resid %d len %jd avail %jd\n", 
+			len, (intmax_t)MIDIQ_LEN(m->outq), 
+			(intmax_t)MIDIQ_AVAIL(m->outq)));
+
+	    if (midi_dumpraw)
+		for(i=0;i<used;i++) printf("%x ", buf[i]);
+
+	    MIDIQ_ENQ(m->outq, buf, used);
+	    len -= used;
+
+	    /*
+	     * Inform the bottom half that data can be written
+	     */
+	    if (!(m->flags & M_TXEN)) {
+		m->flags |= M_TXEN;
+		MPU_CALLBACK(m, m->cookie, m->flags);
+	    }
+	}
+	/*
+	 * If we Made it here then transfer is good
+	 */
+	if (midi_dumpraw)
+	    printf("\n");
+
+	retval = 0;
+err1:	mtx_unlock(&m->qlock);
+	mtx_unlock(&m->lock);
+err0:	return retval;
+}
+
+static int
+midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
+{
+	u_char c[3];
+
+
+	if (note > 127 || chn > 15)
+		return (EINVAL);
+
+	if (vel > 127)
+	    vel = 127;
+
+	if (vel == 64) {
+		c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+		c[1] = (u_char)note;
+		c[2] = 0;
+	} else {
+		c[0] = 0x80 | (chn & 0x0f); /* Note off. */
+		c[1] = (u_char)note;
+		c[2] = (u_char)vel;
+	}
+
+	return midisynth_writeraw(n, c, 3);
+}
+
+static int
+midisynth_setinstr(void *n, uint8_t chn, uint16_t instr)
+{
+	u_char c[2];
+
+	if (instr > 127 || chn > 15)
+		return EINVAL;
+
+	c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */
+	c[1] = instr + midi_instroff;
+
+	return midisynth_writeraw(n, c, 2);
+}
+
+static int
+midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
+{
+	u_char c[3];
+
+	if (note > 127 || chn > 15)
+		return EINVAL;
+
+	if (vel > 127)
+	    vel = 127;
+
+	c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+	c[1] = (u_char)note;
+	c[2] = (u_char)vel;
+
+	return midisynth_writeraw(n, c, 3);
+}
+static int
+midisynth_alloc(void *n, uint8_t chan, uint8_t note)
+{
+	return chan;
+}
+
+static int
+midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val)
+{
+	u_char c[3];
+
+	if (ctrlnum > 127 || chn > 15)
+	    return EINVAL;
+
+	c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
+	c[1] = ctrlnum;
+	c[2] = val;
+	return midisynth_writeraw(n, c, 3);
+}
+
+static int
+midisynth_bender(void *n, uint8_t chn, uint16_t val)
+{
+	u_char c[3];
+
+
+	if (val > 16383 || chn > 15)
+		return  EINVAL;
+
+	c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */
+	c[1] = (u_char)val & 0x7f;
+	c[2] = (u_char)(val >> 7) & 0x7f;
+
+	return midisynth_writeraw(n, c, 3);
+}
+
+/*
+ * Single point of midi destructions.
+ */
+static int
+midi_destroy(struct snd_midi * m, int midiuninit)
+{
+
+	mtx_assert(&midistat_lock, MA_OWNED);
+	mtx_assert(&m->lock, MA_OWNED);
+
+	MIDI_DEBUG(3,printf("midi_destroy\n"));
+	m->dev->si_drv1 = NULL;
+	destroy_dev(m->dev);
+	TAILQ_REMOVE(&midi_devs, m, link);
+	if (midiuninit)
+	    MPU_UNINIT(m, m->cookie);
+	free(MIDIQ_BUF(m->inq), M_MIDI);
+	free(MIDIQ_BUF(m->outq), M_MIDI);
+	mtx_destroy(&m->qlock);
+	mtx_destroy(&m->lock);
+	free(m, M_MIDI);
+	return 0;
+}
+
+/*
+ * Load and unload functions, creates the /dev/midistat device
+ */
+
+static int
+midi_load()
+{
+	mtx_init(&midistat_lock, "midistat lock", 0, 0);
+	TAILQ_INIT(&midi_devs);	/* Initialize the queue. */
+
+	midistat_dev = make_dev(&midistat_cdevsw,
+		    MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0),
+		    UID_ROOT, GID_WHEEL, 0666, "midistat");
+
+	return 0;
+}
+
+static int
+midi_unload()
+{
+	struct snd_midi *m;
+	int             retval;
+
+	MIDI_DEBUG(1,printf("midi_unload()\n"));
+	retval = EBUSY;
+	mtx_lock(&midistat_lock);
+	if (midistat_isopen)
+	    goto exit0;
+
+	TAILQ_FOREACH(m, &midi_devs, link) {
+	    mtx_lock(&m->lock);
+	    if (m->busy)
+		retval = EBUSY;
+	    else
+		retval = midi_destroy(m, 1);
+	    if (retval)
+		goto exit1;
+	}
+
+	destroy_dev(midistat_dev);
+	/*
+	 * Made it here then unload is complete
+	 */
+	mtx_destroy(&midistat_lock);
+	return 0;
+
+exit1:
+	mtx_unlock(&m->lock);
+exit0:
+	mtx_unlock(&midistat_lock);
+	if(retval) MIDI_DEBUG(2,printf("midi_unload: failed\n"));
+	return retval;
+}
+
+extern int seq_modevent(module_t mod, int type, void *data);
+
+static int
+midi_modevent(module_t mod, int type, void *data)
+{
+	int             retval;
+
+	retval = 0;
+
+	switch (type) {
+	case MOD_LOAD:
+	    retval = midi_load();
+	    if (retval == 0)
+		retval = seq_modevent(mod, type, data);
+	    break;
+
+	case MOD_UNLOAD:
+	    retval = midi_unload();
+	    if (retval == 0)
+		retval = seq_modevent(mod, type, data);
+	    break;
+
+	default:
+	    break;
+	}
+
+	return retval;
+}
+
+kobj_t
+midimapper_addseq(void *arg1, int *unit, void **cookie)
+{
+	unit = 0;
+
+	return (kobj_t) arg1;
+}
+
+int
+midimapper_open(void *arg1, void **cookie)
+{
+	int retval = 0;
+	struct snd_midi *m;
+
+        mtx_lock(&midistat_lock);
+
+        TAILQ_FOREACH(m, &midi_devs, link) {
+	    retval++;
+	        }
+
+        mtx_unlock(&midistat_lock);
+        return retval;
+}
+
+int
+midimapper_close(void *arg1, void *cookie)
+{
+	return 0;
+}
+
+kobj_t
+midimapper_fetch_synth(void *arg, void *cookie, int unit)
+{
+	struct snd_midi *m;
+	int retval = 0;
+
+	mtx_lock(&midistat_lock);
+
+	TAILQ_FOREACH(m, &midi_devs, link) {
+	   if (unit == retval) {
+	       mtx_unlock(&midistat_lock);
+	       return (kobj_t)m->synth;
+	   }
+	   retval++;
+	}
+
+	mtx_unlock(&midistat_lock);
+	return NULL;
+}
+
+DEV_MODULE(midi, midi_modevent, NULL);
+MODULE_VERSION(midi, 1);
Index: sys/dev/sound/midi/midi.h
===================================================================
RCS file: sys/dev/sound/midi/midi.h
diff -N sys/dev/sound/midi/midi.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/midi.h	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,54 @@
+/*
+ * (c) 2003 Mathew Kanner
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef MIDI_H
+#define MIDI_H
+
+#include <sys/types.h>
+#include <sys/malloc.h>
+
+MALLOC_DECLARE(M_MIDI);
+
+#define M_RX		0x01
+#define M_TX		0x02
+#define M_RXEN		0x04
+#define M_TXEN		0x08
+
+#define MIDI_TYPE unsigned char
+
+struct snd_midi;
+
+struct snd_midi *midi_init(kobj_class_t _mpu_cls, int _unit, int _channel, 
+			void *cookie);
+int midi_uninit(struct snd_midi * _m);
+int midi_out(struct snd_midi * _m, MIDI_TYPE * _buf, int _size);
+int midi_in(struct snd_midi * _m, MIDI_TYPE * _buf, int _size);
+
+kobj_t midimapper_addseq(void *arg1, int *unit, void **cookie);
+int midimapper_open(void *arg1, void **cookie);
+int midimapper_close(void *arg1, void *cookie);
+kobj_t midimapper_fetch_synth(void *arg, void *cookie, int unit);
+
+#endif
Index: sys/dev/sound/midi/midiq.h
===================================================================
RCS file: sys/dev/sound/midi/midiq.h
diff -N sys/dev/sound/midi/midiq.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/midiq.h	5 Mar 2005 17:06:41 -0000
@@ -0,0 +1,103 @@
+
+/*
+ * (c) 2003 Mathew Kanner
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef MIDIQ_H
+
+#define MIDIQ_MOVE(a,b,c) bcopy(b,a,c)
+
+#define MIDIQ_HEAD(name, type)          \
+struct name {                           \
+        int h, t, s;                    \
+        type * b;                        \
+} 
+
+#define MIDIQ_INIT(head, buf, size) do {                \
+        (head).h=(head).t=0;                          \
+        (head).s=size;                                 \
+        (head).b=buf;                                  \
+} while (0)
+
+#define MIDIQ_EMPTY(head)       ((head).h == (head).t )
+
+#define MIDIQ_LENBASE(head)         ((head).h - (head).t < 0 ? \
+                                        (head).h - (head).t + (head).s : \
+                                        (head).h - (head).t)
+
+#define MIDIQ_FULL(head)        ((head).h == -1)
+#define MIDIQ_AVAIL(head)       (MIDIQ_FULL(head) ? 0 : (head).s - MIDIQ_LENBASE(head))
+#define MIDIQ_LEN(head)		((head).s - MIDIQ_AVAIL(head))
+#define MIDIQ_DEBUG 0
+/*
+ * No protection against overflow, underflow
+ */
+#define MIDIQ_ENQ(head, buf, size) do {                                                                 \
+		if(MIDIQ_DEBUG)\
+                	printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n",            \
+			       &(head).b[(head).h], (buf),                                        \
+			       (intmax_t)(sizeof(*(head).b) *                                     \
+					  MIN( (size), (head).s - (head).h) ),                   \
+			       (size), (head).h, (head).t);               \
+                MIDIQ_MOVE(&(head).b[(head).h], (buf), sizeof(*(head).b) * MIN((size), (head).s - (head).h));                       \
+                if( (head).s - (head).h < (size) ) {                                                    \
+			if(MIDIQ_DEBUG) \
+                        	printf("#2 %p %p bytes copied %jd\n",  (head).b, (buf) + (head).s - (head).h, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).h) );      \
+                        MIDIQ_MOVE((head).b, (buf) + (head).s - (head).h, sizeof(*(head).b) * ((size) - (head).s + (head).h) );      \
+		} \
+                (head).h+=(size);                                                                         \
+                (head).h%=(head).s;                                                                     \
+		if(MIDIQ_EMPTY(head)) (head).h=-1; \
+		if(MIDIQ_DEBUG)\
+                	printf("#E h %d t %d\n", (head).h, (head).t);                       \
+} while (0)
+
+#define MIDIQ_DEQ_I(head, buf, size, move, update) do {                                                                 \
+		if(MIDIQ_FULL(head)) (head).h=(head).t; \
+		if(MIDIQ_DEBUG)\
+                	printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n", &(head).b[(head).t], (buf), (intmax_t)sizeof(*(head).b) * MIN((size), (head).s - (head).t), (size), (head).h, (head).t);                       \
+                if (move) MIDIQ_MOVE((buf), &(head).b[(head).t], sizeof(*(head).b) * MIN((size), (head).s - (head).t));                       \
+                if( (head).s - (head).t < (size) ) {                                                    \
+			if(MIDIQ_DEBUG) \
+                        	printf("#2 %p %p bytes copied %jd\n",  (head).b, (buf) + (head).s - (head).t, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).t) );      \
+                        if (move) MIDIQ_MOVE((buf) + (head).s - (head).t, (head).b, sizeof(*(head).b) * ((size) - (head).s + (head).t) );      \
+		} \
+		if (update) { \
+                (head).t+=(size);                                                                         \
+                (head).t%=(head).s;                                                                     \
+		} else { \
+		  if (MIDIQ_EMPTY(head)) (head).h=-1; \
+		} \
+		if(MIDIQ_DEBUG)\
+                	printf("#E h %d t %d\n", (head).h, (head).t);                       \
+} while (0)
+
+#define MIDIQ_SIZE(head) ((head).s)
+#define MIDIQ_CLEAR(head) ((head).h = (head).t = 0)
+#define MIDIQ_BUF(head) ((head).b)
+#define MIDIQ_DEQ(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 1)
+#define MIDIQ_PEEK(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 0)
+#define MIDIQ_POP(head, size) MIDIQ_DEQ_I(head, &head, size, 0, 1)
+
+#endif
Index: sys/dev/sound/midi/mpu401.c
===================================================================
RCS file: sys/dev/sound/midi/mpu401.c
diff -N sys/dev/sound/midi/mpu401.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/mpu401.c	5 Mar 2005 16:55:58 -0000
@@ -0,0 +1,282 @@
+/*
+ * (c) 2003 Mathew Kanner
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/bus.h> /* to get driver_intr_t */
+
+#include <dev/sound/midi/mpu401.h>
+#include <dev/sound/midi/midi.h>
+
+#include "mpu_if.h"
+#include "mpufoi_if.h"
+
+#define MPU_DATAPORT   0
+#define MPU_CMDPORT    1
+#define MPU_STATPORT   1
+#define MPU_RESET      0xff
+#define MPU_UART       0x3f
+#define MPU_ACK        0xfe
+#define MPU_STATMASK   0xc0
+#define MPU_OUTPUTBUSY 0x40
+#define MPU_INPUTBUSY  0x80
+#define MPU_TRYDATA 50
+#define MPU_DELAY   2500
+
+#define CMD(m,d)	MPUFOI_WRITE(m, m->cookie, MPU_CMDPORT,d)
+#define STATUS(m)	MPUFOI_READ(m, m->cookie, MPU_STATPORT)
+#define READ(m)		MPUFOI_READ(m, m->cookie, MPU_DATAPORT)
+#define WRITE(m,d)	MPUFOI_WRITE(m, m->cookie, MPU_DATAPORT,d)
+
+struct mpu401 {
+	KOBJ_FIELDS;
+	struct snd_midi *mid;
+	int flags;
+	driver_intr_t *si;
+	void *cookie;
+	struct callout timer;
+};
+
+static void mpu401_timeout(void *m) ;
+static mpu401_intr_t mpu401_intr;
+
+static int mpu401_minit(kobj_t obj, struct mpu401 *m);
+static int mpu401_muninit(kobj_t obj, struct mpu401 *m);
+static int mpu401_minqsize(kobj_t obj, struct mpu401 *m);
+static int mpu401_moutqsize(kobj_t obj, struct mpu401 *m);
+static void mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags);
+static void mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags);
+static const char *mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity);
+static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m);
+
+static kobj_method_t mpu401_methods[] = {
+	KOBJMETHOD(mpu_init,mpu401_minit),
+	KOBJMETHOD(mpu_uninit,mpu401_muninit),
+	KOBJMETHOD(mpu_inqsize,mpu401_minqsize),
+	KOBJMETHOD(mpu_outqsize,mpu401_moutqsize),
+	KOBJMETHOD(mpu_callback,mpu401_mcallback),
+	KOBJMETHOD(mpu_callbackp,mpu401_mcallbackp),
+	KOBJMETHOD(mpu_descr,mpu401_mdescr),
+	KOBJMETHOD(mpu_provider,mpu401_mprovider),
+        { 0, 0 }
+};
+
+DEFINE_CLASS(mpu401, mpu401_methods, 0);
+
+void
+mpu401_timeout(void *a) 
+{	struct mpu401 *m=(struct mpu401 *)a;
+
+	if (m->si)
+		(m->si)(m->cookie);
+
+}
+static int
+mpu401_intr(struct mpu401 *m)
+{
+#define MPU_INTR_BUF	16
+	MIDI_TYPE b[MPU_INTR_BUF];
+	int i;
+	int s;
+/*
+	printf("mpu401_intr\n");
+*/
+#define RXRDY(m) ( (STATUS(m) & MPU_INPUTBUSY) == 0)
+#define TXRDY(m) ( (STATUS(m) & MPU_OUTPUTBUSY) == 0)
+#if 0
+#define D(x,l) printf("mpu401_intr %d %x %s %s\n",l, x, x&MPU_INPUTBUSY?"RX":"", x&MPU_OUTPUTBUSY?"TX":"")
+#else
+#define D(x,l)
+#endif
+	i=0;
+	s = STATUS(m);
+	D(s,1);
+	while ( (s&MPU_INPUTBUSY) == 0 && i<MPU_INTR_BUF) {
+		b[i]=READ(m);
+/*
+		printf("mpu401_intr in i %d d %d\n", i, b[i]);
+*/
+		i++;
+	s = STATUS(m);
+	}
+	if (i) midi_in(m->mid, b, i);
+	i=0;
+	while ( !(s&MPU_OUTPUTBUSY) && i<MPU_INTR_BUF) {
+		if(midi_out(m->mid, b, 1)) { 
+/*
+			printf("mpu401_intr out i %d d %d\n", i, b[0]);
+*/
+	
+			WRITE(m, *b);
+		}
+		else {
+/*
+			printf("mpu401_intr write: no output\n");
+*/
+			return 0;
+		}
+		i++;
+	/* DELAY(100); */
+	s = STATUS(m);
+	}
+
+	if ((m->flags & M_TXEN) && (m->si) ) {
+	    callout_reset(&m->timer, 1, mpu401_timeout, m);
+	}
+
+	return (m->flags & M_TXEN) == M_TXEN;
+}
+
+struct mpu401 *
+mpu401_init(kobj_class_t cls, void *cookie,driver_intr_t softintr, mpu401_intr_t **cb)
+{
+	struct mpu401 *m;
+
+	*cb = NULL;
+	m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO);
+
+	if(!m)
+		return NULL;
+
+	kobj_init((kobj_t)m, cls);
+
+	callout_init(&m->timer, 1);
+
+	m->si = softintr;
+	m->cookie = cookie;
+	m->flags = 0;
+
+	m->mid = midi_init(&mpu401_class,0,0,m);
+	if (!m->mid)
+		goto err;
+	*cb = mpu401_intr;
+	return m;
+err:
+	printf("mpu401_init error\n");
+	free(m, M_MIDI);
+	return NULL;
+}
+
+int
+mpu401_uninit(struct mpu401 *m)
+{
+	int retval;
+
+	CMD(m, MPU_RESET);
+	retval = midi_uninit(m->mid);
+	if (retval)
+		return retval;
+	free(m, M_MIDI);
+	return 0;
+}
+
+static int
+mpu401_minit(kobj_t obj, struct mpu401 *m)
+{
+	int i;
+
+	CMD(m, MPU_RESET);
+	CMD(m, MPU_UART);
+	return 0;
+	i=0;
+	while(++i<2000) {
+		if(RXRDY(m))
+			if(READ(m) == MPU_ACK)
+				break;
+	}
+
+	if( i < 2000 ) {
+		CMD(m, MPU_UART);
+		return 0;
+	}
+	printf("mpu401_minit failed active sensing\n");
+	return 1;
+}
+
+
+int
+mpu401_muninit(kobj_t obj, struct mpu401 *m)
+{
+
+	return MPUFOI_UNINIT(m, m->cookie);
+}
+
+int
+mpu401_minqsize(kobj_t obj, struct mpu401 *m)
+{
+	return 128;
+}
+
+int
+mpu401_moutqsize(kobj_t obj, struct mpu401 *m)
+{
+	return 128;
+}
+
+static void
+mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags)
+{
+#if 0
+	printf("mpu401_callback %s %s %s %s\n", 
+		flags & M_RX ? "M_RX" : "",
+		flags & M_TX ? "M_TX" : "",
+		flags & M_RXEN ? "M_RXEN" : "",
+		flags & M_TXEN ? "M_TXEN" : "" );
+#endif
+	if (flags & M_TXEN && m->si) {
+		callout_reset(&m->timer, 1, mpu401_timeout, m);
+	}
+
+	m->flags = flags;
+}
+
+static void
+mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags)
+{
+/*	printf("mpu401_callbackp\n"); */
+	mpu401_mcallback(obj, m, flags);
+}
+
+static const char *
+mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity)
+{
+
+	return "descr mpu401";
+}
+
+static const char *
+mpu401_mprovider(kobj_t obj, struct mpu401 *m)
+{
+	return "provider mpu401";
+}
Index: sys/dev/sound/midi/mpu401.h
===================================================================
RCS file: sys/dev/sound/midi/mpu401.h
diff -N sys/dev/sound/midi/mpu401.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/mpu401.h	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,36 @@
+/*
+ * (c) 2003 Mathew Kanner
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef MPU401_H
+#define MPU401_H
+
+struct mpu401;
+
+typedef int mpu401_intr_t(struct mpu401 * _obj);
+
+extern struct mpu401 *mpu401_init(kobj_class_t _cls, void *cookie, 
+			driver_intr_t *_softintr,mpu401_intr_t **_cb);
+extern int mpu401_uninit(struct mpu401 * _obj);
+#endif
Index: sys/dev/sound/midi/mpu_if.m
===================================================================
RCS file: sys/dev/sound/midi/mpu_if.m
diff -N sys/dev/sound/midi/mpu_if.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/mpu_if.m	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,46 @@
+#include <dev/sound/midi/midi.h>
+
+INTERFACE mpu;
+
+METHOD int inqsize{
+	struct snd_midi * _kobj;
+	void *_cookie;
+};
+
+METHOD int outqsize {
+	struct snd_midi * _kobj;
+	void *_cookie;
+};
+
+METHOD int init {
+	struct snd_midi * _kobj;
+	void *_cookie;
+};
+
+METHOD void callbackp {
+	struct snd_midi * _kobj;
+	void *_cookie;
+	int _flags;
+};
+
+METHOD void callback { 
+	struct snd_midi * _kobj;
+	void *_cookie;
+	int _flags;
+};
+
+METHOD const char * provider{
+	struct snd_midi * _kobj;
+	void *_cookie;
+};
+
+METHOD const char * descr { 
+	struct snd_midi * _kobj; 
+	void *_cookie;
+	int _verbosity;
+};
+
+METHOD int uninit { 
+	struct snd_midi * _kobj; 
+	void *_cookie;
+};
Index: sys/dev/sound/midi/mpufoi_if.m
===================================================================
RCS file: sys/dev/sound/midi/mpufoi_if.m
diff -N sys/dev/sound/midi/mpufoi_if.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/mpufoi_if.m	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,22 @@
+#include <sys/bus.h>
+#include <dev/sound/midi/mpu401.h>
+
+INTERFACE mpufoi;
+
+METHOD unsigned char read {
+	struct mpu401 *_kobj;
+	void *_cookie;
+	int _reg;
+};
+
+METHOD void write {
+	struct mpu401 *_kobj;
+	void *_cookie;
+	int _reg;
+	unsigned char _d;
+};
+
+METHOD int uninit {
+	struct mpu401 *_kobj;
+	void *_cookie;
+};
Index: sys/dev/sound/midi/sequencer.c
===================================================================
RCS file: sys/dev/sound/midi/sequencer.c
diff -N sys/dev/sound/midi/sequencer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/sequencer.c	5 Mar 2005 16:57:48 -0000
@@ -0,0 +1,2046 @@
+/*
+ * The sequencer personality manager.
+ * (c) 2003 Mathew Kanner
+ * Copyright by Hannu Savolainen 1993
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/midi/sequencer.c,v 1.15 2003/03/03 12:15:46 phk Exp $
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/lock.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h>      /* for DELAY */
+#include <sys/soundcard.h>
+#include <sys/rman.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/kthread.h>
+#include <sys/unistd.h>
+#include <sys/selinfo.h>
+
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/midi/midiq.h>
+#include "synth_if.h"
+
+#include <dev/sound/midi/sequencer.h>
+
+#define TMR_TIMERBASE 13
+
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MUSIC	8	/* /dev/music, level 2 interface */
+
+/* Length of a sequencer event. */
+#define EV_SZ 8
+#define IEV_SZ 8
+
+/* Lookup modes */
+#define LOOKUP_EXIST	(0)
+#define LOOKUP_OPEN	(1)
+#define LOOKUP_CLOSE	(2)
+
+#define PCMMKMINOR(u, d, c) \
+	    ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
+#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
+#define MIDIUNIT(y) ((minor(y) >> 4) & 0x0f)
+#define MIDIDEV(y) (minor(y) & 0x0f)
+
+/* These are the entries to the sequencer driver. */
+static d_open_t seq_open;
+static d_close_t seq_close;
+static d_ioctl_t seq_ioctl;
+static d_read_t seq_read;
+static d_write_t seq_write;
+static d_poll_t seq_poll;
+#define MIDI_MAJOR 30
+
+static struct cdevsw seq_cdevsw = {
+	.d_version = D_VERSION,
+	.d_open =	seq_open,
+	.d_close =	seq_close,
+	.d_read =	seq_read,
+	.d_write =	seq_write,
+	.d_ioctl =	seq_ioctl,
+	.d_poll =	seq_poll,
+	.d_name =	"sequencer",
+	.d_maj =	MIDI_MAJOR,
+};
+
+struct seq_softc {
+	KOBJ_FIELDS;
+
+	struct mtx seq_lock, q_lock;
+	struct cv empty_cv, reset_cv, in_cv, out_cv, state_cv, th_cv;
+
+	MIDIQ_HEAD(, u_char) in_q, out_q;
+
+	u_long  flags;
+	/* Flags (protected by flag_mtx of mididev_info) */
+	int		fflags;		  /* Access mode */
+	int		music;
+
+	int		out_water;	/* Sequence output threshould */
+	snd_sync_parm	sync_parm;		/* AIOSYNC parameter set */
+	struct thread	*sync_thread;		/* AIOSYNCing thread */
+	struct selinfo in_sel, out_sel;
+	int midi_number;
+	struct cdev *seqdev, *musicdev;
+	int unit;
+	int maxunits;
+	kobj_t *midis;
+	int *midi_flags;
+	kobj_t mapper;
+	void *mapper_cookie;
+	struct timeval timerstop, timersub;
+	int timerbase, tempo;
+	int timerrun;
+	int done;
+	int playing;
+	int recording;
+	int busy;
+	int pre_event_timeout;
+	int waiting;
+};
+
+/*
+ * Module specific stuff, including how many sequecers
+ * we currently own.
+ */
+
+SYSCTL_NODE(_hw_midi, OID_AUTO, seq, CTLFLAG_RD, 0, "Midi sequencer");
+
+int					seq_debug;
+SYSCTL_INT(_hw_midi_seq, OID_AUTO, debug, CTLFLAG_RW, &seq_debug, 0, "");
+
+midi_cmdtab	cmdtab_seqevent[] = {
+	{SEQ_NOTEOFF,		"SEQ_NOTEOFF"},
+	{SEQ_NOTEON,		"SEQ_NOTEON"},
+	{SEQ_WAIT,		"SEQ_WAIT"},
+	{SEQ_PGMCHANGE,		"SEQ_PGMCHANGE"},
+	{SEQ_SYNCTIMER,		"SEQ_SYNCTIMER"},
+	{SEQ_MIDIPUTC,		"SEQ_MIDIPUTC"},
+	{SEQ_DRUMON,		"SEQ_DRUMON"},
+	{SEQ_DRUMOFF,		"SEQ_DRUMOFF"},
+	{SEQ_ECHO,		"SEQ_ECHO"},
+	{SEQ_AFTERTOUCH,	"SEQ_AFTERTOUCH"},
+	{SEQ_CONTROLLER,	"SEQ_CONTROLLER"},
+	{SEQ_BALANCE,		"SEQ_BALANCE"},
+	{SEQ_VOLMODE,		"SEQ_VOLMODE"},
+	{SEQ_FULLSIZE,		"SEQ_FULLSIZE"},
+	{SEQ_PRIVATE,		"SEQ_PRIVATE"},
+	{SEQ_EXTENDED,		"SEQ_EXTENDED"},
+	{EV_SEQ_LOCAL,		"EV_SEQ_LOCAL"},
+	{EV_TIMING,		"EV_TIMING"},
+	{EV_CHN_COMMON,		"EV_CHN_COMMON"},
+	{EV_CHN_VOICE,		"EV_CHN_VOICE"},
+	{EV_SYSEX,		"EV_SYSEX"},
+	{-1,			NULL},
+};
+
+midi_cmdtab	cmdtab_seqioctl[] = {
+	{SNDCTL_SEQ_RESET,	"SNDCTL_SEQ_RESET"},
+	{SNDCTL_SEQ_SYNC,	"SNDCTL_SEQ_SYNC"},
+	{SNDCTL_SYNTH_INFO,	"SNDCTL_SYNTH_INFO"},
+	{SNDCTL_SEQ_CTRLRATE,	"SNDCTL_SEQ_CTRLRATE"},
+	{SNDCTL_SEQ_GETOUTCOUNT,	"SNDCTL_SEQ_GETOUTCOUNT"},
+	{SNDCTL_SEQ_GETINCOUNT,	"SNDCTL_SEQ_GETINCOUNT"},
+	{SNDCTL_SEQ_PERCMODE,	"SNDCTL_SEQ_PERCMODE"},
+	{SNDCTL_FM_LOAD_INSTR,	"SNDCTL_FM_LOAD_INSTR"},
+	{SNDCTL_SEQ_TESTMIDI,	"SNDCTL_SEQ_TESTMIDI"},
+	{SNDCTL_SEQ_RESETSAMPLES,	"SNDCTL_SEQ_RESETSAMPLES"},
+	{SNDCTL_SEQ_NRSYNTHS,	"SNDCTL_SEQ_NRSYNTHS"},
+	{SNDCTL_SEQ_NRMIDIS,	"SNDCTL_SEQ_NRMIDIS"},
+	{SNDCTL_SEQ_GETTIME,	"SNDCTL_SEQ_GETTIME"},
+	{SNDCTL_MIDI_INFO,	"SNDCTL_MIDI_INFO"},
+	{SNDCTL_SEQ_THRESHOLD,	"SNDCTL_SEQ_THRESHOLD"},
+	{SNDCTL_SYNTH_MEMAVL,	"SNDCTL_SYNTH_MEMAVL"},
+	{SNDCTL_FM_4OP_ENABLE,	"SNDCTL_FM_4OP_ENABLE"},
+	{SNDCTL_PMGR_ACCESS,	"SNDCTL_PMGR_ACCESS"},
+	{SNDCTL_SEQ_PANIC,	"SNDCTL_SEQ_PANIC"},
+	{SNDCTL_SEQ_OUTOFBAND,	"SNDCTL_SEQ_OUTOFBAND"},
+	{SNDCTL_TMR_TIMEBASE,	"SNDCTL_TMR_TIMEBASE"},
+	{SNDCTL_TMR_START,	"SNDCTL_TMR_START"},
+	{SNDCTL_TMR_STOP,	"SNDCTL_TMR_STOP"},
+	{SNDCTL_TMR_CONTINUE,	"SNDCTL_TMR_CONTINUE"},
+	{SNDCTL_TMR_TEMPO,	"SNDCTL_TMR_TEMPO"},
+	{SNDCTL_TMR_SOURCE,	"SNDCTL_TMR_SOURCE"},
+	{SNDCTL_TMR_METRONOME,	"SNDCTL_TMR_METRONOME"},
+	{SNDCTL_TMR_SELECT,	"SNDCTL_TMR_SELECT"},
+	{SNDCTL_MIDI_PRETIME,	"SNDCTL_MIDI_PRETIME"},
+	{AIONWRITE,		"AIONWRITE"},
+	{AIOGSIZE,		"AIOGSIZE"},
+	{AIOSSIZE,		"AIOSSIZE"},
+	{AIOGFMT,		"AIOGFMT"},
+	{AIOSFMT,		"AIOSFMT"},
+	{AIOGMIX,		"AIOGMIX"},
+	{AIOSMIX,		"AIOSMIX"},
+	{AIOSTOP,		"AIOSTOP"},
+	{AIOSYNC,		"AIOSYNC"},
+	{AIOGCAP,		"AIOGCAP"},
+	{-1,			NULL},
+};
+
+midi_cmdtab	cmdtab_timer[] = {
+	{TMR_WAIT_REL,	"TMR_WAIT_REL"},
+	{TMR_WAIT_ABS,	"TMR_WAIT_ABS"},
+	{TMR_STOP,	"TMR_STOP"},
+	{TMR_START,	"TMR_START"},
+	{TMR_CONTINUE,	"TMR_CONTINUE"},
+	{TMR_TEMPO,	"TMR_TEMPO"},
+	{TMR_ECHO,	"TMR_ECHO"},
+	{TMR_CLOCK,	"TMR_CLOCK"},
+	{TMR_SPP,	"TMR_SPP"},
+	{TMR_TIMESIG,	"TMR_TIMESIG"},
+	{-1,		NULL},
+};
+
+midi_cmdtab	cmdtab_seqcv[] = {
+	{MIDI_NOTEOFF,		"MIDI_NOTEOFF"},
+	{MIDI_NOTEON,		"MIDI_NOTEON"},
+	{MIDI_KEY_PRESSURE,	"MIDI_KEY_PRESSURE"},
+	{-1,			NULL},
+};
+
+midi_cmdtab	cmdtab_seqccmn[] = {
+	{MIDI_CTL_CHANGE,	"MIDI_CTL_CHANGE"},
+	{MIDI_PGM_CHANGE,	"MIDI_PGM_CHANGE"},
+	{MIDI_CHN_PRESSURE,	"MIDI_CHN_PRESSURE"},
+	{MIDI_PITCH_BEND,	"MIDI_PITCH_BEND"},
+	{MIDI_SYSTEM_PREFIX,	"MIDI_SYSTEM_PREFIX"},
+	{-1,			NULL},
+};
+
+/*
+ * static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m);
+ */
+
+static kobj_method_t seq_methods[] = {
+	/* KOBJMETHOD(mpu_provider,mpu401_mprovider), */
+        { 0, 0 }
+};
+DEFINE_CLASS(sequencer, seq_methods, 0);
+
+/* The followings are the local function. */
+static int seq_convertold(u_char *event, u_char *out);
+/*
+ * static void seq_midiinput(struct seq_softc * scp, void *md);
+ */
+static void seq_reset(struct seq_softc * scp);
+static int seq_sync(struct seq_softc * scp);
+
+static int seq_processevent(struct seq_softc * scp, u_char *event);
+
+static int seq_timing(struct seq_softc * scp, u_char *event);
+static int seq_local(struct seq_softc * scp, u_char *event);
+
+static int seq_chnvoice(struct seq_softc * scp, kobj_t md, u_char *event);
+static int seq_chncommon(struct seq_softc * scp, kobj_t md, u_char *event);
+static int seq_sysex(struct seq_softc * scp, kobj_t md, u_char *event);
+
+static int seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md);
+void seq_copytoinput(struct seq_softc *scp, u_char *event, int len);
+int seq_modevent(module_t mod, int type, void *data);
+struct seq_softc *seqs[10];
+static struct mtx			seqinfo_mtx;
+static u_long				nseq = 0;
+
+static void timer_start(struct seq_softc *t);
+static void timer_stop(struct seq_softc *t);
+static void timer_setvals(struct seq_softc *t, int tempo, int timerbase);
+static void timer_wait(struct seq_softc *t, int ticks, int wait_abs);
+static int timer_now(struct seq_softc *t);
+
+
+static void
+timer_start(struct seq_softc *t)
+{
+	t->timerrun = 1;
+	getmicrotime(&t->timersub);
+}
+
+static void
+timer_continue(struct seq_softc *t)
+{
+	struct timeval now;
+
+	if (t->timerrun == 1)
+	    return;
+	t->timerrun = 1;
+	getmicrotime(&now);
+	timevalsub(&now, &t->timerstop);
+	timevaladd(&t->timersub, &now);
+}
+
+static void
+timer_stop(struct seq_softc *t)
+{
+	t->timerrun = 0;
+	getmicrotime(&t->timerstop);
+}
+
+static void
+timer_setvals(struct seq_softc *t, int tempo, int timerbase)
+{
+	t->tempo = tempo;
+	t->timerbase = timerbase;
+}
+
+static void
+timer_wait(struct seq_softc *t, int ticks, int wait_abs)
+{
+	struct timeval now, when;
+	int ret;
+	unsigned long long i;
+
+	while (t->timerrun == 0) {
+	    SEQ_DEBUG(2,printf("Timer wait when timer isn't running\n"));
+	    /*
+	     * The old sequencer used timeouts that only increased
+	     * the timer when the timer was running.
+	     * Hence the sequencer would stick (?) if the
+	     * timer was disabled.
+	     */
+	    cv_wait(&t->reset_cv, &t->seq_lock);
+	    if (t->playing == 0)
+		return;
+	}
+
+	i = ticks * 60ull * 1000000ull / (t->tempo * t->timerbase);
+
+	when.tv_sec = i / 1000000;
+	when.tv_usec = i % 1000000;
+
+#if 0
+	printf("timer_wait tempo %d timerbase %d ticks %d abs %d u_sec %llu\n", t->tempo, t->timerbase, ticks, wait_abs, i);
+#endif
+
+	if (wait_abs != 0) {
+	    getmicrotime(&now);
+	    timevalsub(&now, &t->timersub);
+	    timevalsub(&when, &now);
+	}
+
+	if (when.tv_sec < 0 || when.tv_usec < 0) {
+	    SEQ_DEBUG(3,printf("seq_timer error negative time %ld.%06ld\n", 
+		    when.tv_sec,when.tv_usec));
+	    return;
+	}
+
+	i = when.tv_sec * 1000000ull;
+	i += when.tv_usec;
+	i *= hz;
+	i /= 1000000ull;
+#if 0
+	printf("seq_timer usec %llu ticks %llu\n", when.tv_sec * 1000000ull + when.tv_usec, i);
+#endif
+	t->waiting = 1;
+	ret = cv_timedwait(&t->reset_cv, &t->seq_lock, i + 1);
+	t->waiting = 0;
+
+	if (ret != EWOULDBLOCK)
+	    SEQ_DEBUG(3,printf("seq_timer didn't timeout\n"));
+
+}
+
+static int
+timer_now(struct seq_softc *t)
+{
+	struct timeval now;
+	unsigned long long i;
+	int ret;
+
+	if (t->timerrun == 0)
+	    now = t->timerstop;
+	else
+	    getmicrotime(&now);
+
+	timevalsub(&now, &t->timersub);
+
+	i = now.tv_sec * 1000000ull;
+	i += now.tv_usec;
+	i *= t->timerbase;
+/*	i /= t->tempo; */
+	i /= 1000000ull;
+
+	ret = i;
+	/*
+	 * printf("timer_now: %llu %d\n", i, ret);
+	 */
+
+	return ret;
+}
+
+static void
+seq_eventthread(void *arg)
+{
+	struct seq_softc *scp = arg;
+	char event[EV_SZ];
+
+	mtx_lock(&scp->seq_lock);
+	printf("seq_eventthread started\n");
+	while(scp->done == 0) {
+restart:
+	    while (scp->playing == 0) {
+		cv_wait(&scp->state_cv, &scp->seq_lock);
+		    if (scp->done)
+			goto done;
+	    }
+
+	    while (MIDIQ_EMPTY(scp->out_q)) {
+		cv_broadcast(&scp->empty_cv);
+		cv_wait(&scp->out_cv, &scp->seq_lock);
+		if (scp->playing == 0)
+		    goto restart;
+		if (scp->done)
+		    goto done;
+	    }
+
+	    MIDIQ_DEQ(scp->out_q, event, EV_SZ);
+
+	    if (MIDIQ_AVAIL(scp->out_q) < scp->out_water) {
+		cv_broadcast(&scp->out_cv);
+		selwakeup(&scp->out_sel);
+	    }
+
+	    seq_processevent(scp, event);
+	}
+
+done:
+	cv_broadcast(&scp->th_cv);
+	mtx_unlock(&scp->seq_lock);
+	mtx_lock(&Giant);
+	printf("seq_eventthread finished\n");
+	kthread_exit(0);
+}
+
+/*
+ * seq_processevent:  This maybe called by the event thread or the IOCTL
+ * handler for queued and out of band events respectively.
+ */
+static int
+seq_processevent(struct seq_softc *scp, u_char *event)
+{
+	int ret;
+	kobj_t m;
+
+	ret = 0;
+
+	if (event[0] == EV_SEQ_LOCAL)
+		ret = seq_local(scp, event);
+	else if (event[0] == EV_TIMING)
+		ret = seq_timing(scp, event);
+	else if (event[0] != EV_CHN_VOICE &&
+			event[0] != EV_CHN_COMMON &&
+			event[0] != EV_SYSEX &&
+			event[0] != SEQ_MIDIPUTC) {
+		ret = 1;
+		SEQ_DEBUG(2,printf("seq_processevent not known %d\n", event[0]));
+	} else if (seq_fetch_mid(scp, event[1], &m) != 0) {
+		ret = 1;
+		SEQ_DEBUG(2,printf("seq_processevent midi unit not found %d\n", event[1]));
+	} else switch(event[0]) {
+		case EV_CHN_VOICE: 
+			ret = seq_chnvoice(scp, m, event);
+			break;
+		case EV_CHN_COMMON:
+			ret = seq_chncommon(scp, m, event);
+			break;
+		case EV_SYSEX:
+			ret = seq_sysex(scp, m, event);
+			break;
+		case SEQ_MIDIPUTC:
+			mtx_unlock(&scp->seq_lock);
+			ret = SYNTH_WRITERAW(m, &event[2], 1);
+			mtx_lock(&scp->seq_lock);
+			break;
+	}
+	return ret;
+}
+
+static int
+seq_addunit(void)
+{
+	struct seq_softc *scp;
+	int ret;
+	u_char *buf;
+
+	/* Allocate the softc. */
+	ret = ENOMEM;
+	scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (scp == NULL) {
+		SEQ_DEBUG(1,printf("seq_addunit: softc allocation failed.\n"));
+		goto err;
+	}
+	kobj_init((kobj_t) scp, &sequencer_class);
+
+	buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO);
+	if (buf == NULL)
+	    goto err;
+	MIDIQ_INIT(scp->in_q, buf, EV_SZ * 1024);
+	buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO);
+	if (buf == NULL)
+	    goto err;
+	MIDIQ_INIT(scp->out_q, buf, EV_SZ * 1024);
+	ret = EINVAL;
+
+	scp->midis = malloc(sizeof(kobj_t) * 32, M_TEMP, M_NOWAIT | M_ZERO);
+	scp->midi_flags = malloc(sizeof(*scp->midi_flags) * 32, M_TEMP,
+		M_NOWAIT | M_ZERO);
+
+	if ( scp->midis == NULL || scp->midi_flags == NULL)
+	    goto err;
+
+	scp->flags = 0;
+
+	mtx_init(&scp->seq_lock, "seqflq", 0, 0);
+	cv_init(&scp->state_cv, "seqstate");
+	cv_init(&scp->empty_cv, "seqempty");
+	cv_init(&scp->reset_cv, "seqtimer");
+	cv_init(&scp->out_cv, "seqqout");
+	cv_init(&scp->in_cv, "seqqin");
+	cv_init(&scp->th_cv, "seqstart");
+
+	/*
+	 * Init the damn timer
+	 */
+
+	scp->mapper = midimapper_addseq(scp, &scp->unit, &scp->mapper_cookie);
+	if (scp->mapper == NULL)
+	    goto err;
+
+	scp->seqdev = make_dev(&seq_cdevsw,
+		MIDIMKMINOR(scp->unit, SND_DEV_SEQ,0), UID_ROOT, 
+		GID_WHEEL, 0666, "sequencer%d", scp->unit);
+
+	scp->musicdev = make_dev(&seq_cdevsw,
+		MIDIMKMINOR(scp->unit, SND_DEV_MUSIC,0), UID_ROOT, 
+		GID_WHEEL, 0666, "music%d", scp->unit);
+
+	if (scp->seqdev == NULL || scp->musicdev == NULL)
+	    goto err;
+	/*
+	 * TODO: Add to list of sequencers this module provides
+	 */
+
+	ret = kthread_create(seq_eventthread, scp, NULL, RFHIGHPID, 0, "sequencer %02d", scp->unit);
+
+	if (ret)
+	    goto err;
+
+	scp->seqdev->si_drv1 = scp->musicdev->si_drv1 = scp;
+
+	printf("sequencer %d created scp %p\n", scp->unit, scp);
+
+	ret = 0;
+
+	mtx_lock(&seqinfo_mtx);
+	seqs[nseq++]=scp;
+	mtx_unlock(&seqinfo_mtx);
+
+	goto ok;
+
+err:
+	if (scp != NULL) {
+	    if (scp->seqdev != NULL)
+		destroy_dev(scp->seqdev);
+	    if (scp->musicdev != NULL)
+		destroy_dev(scp->musicdev);
+	    /*
+	     * TODO: Destroy mutex and cv
+	     */
+	    if (scp->midis != NULL)
+		free(scp->midis, M_TEMP);
+	    if (scp->midi_flags != NULL)
+		free(scp->midi_flags, M_TEMP);
+	    if (scp->out_q.b)
+		free(scp->out_q.b, M_TEMP);
+	    if (scp->in_q.b)
+		free(scp->in_q.b, M_TEMP);
+	    free(scp, M_DEVBUF);
+	}
+ok:
+	return ret;
+}
+
+static int
+seq_delunit(int unit)
+{
+	struct seq_softc *scp = seqs[unit];
+	int i;
+
+	//SEQ_DEBUG(4,printf("seq_delunit: %d\n", unit));
+	printf("seq_delunit: 1 \n");
+	mtx_lock(&scp->seq_lock);
+
+	scp->playing = 0;
+	scp->done = 1;
+	cv_broadcast(&scp->out_cv);
+	cv_broadcast(&scp->state_cv);
+	cv_broadcast(&scp->reset_cv);
+	printf("seq_delunit: 2 \n");
+	cv_wait(&scp->th_cv, &scp->seq_lock);
+	printf("seq_delunit: 3.0 \n");
+	mtx_unlock(&scp->seq_lock);
+	printf("seq_delunit: 3.1 \n");
+
+	cv_destroy(&scp->state_cv);
+	printf("seq_delunit: 4 \n");
+	cv_destroy(&scp->empty_cv);
+	printf("seq_delunit: 5 \n");
+	cv_destroy(&scp->reset_cv);
+	printf("seq_delunit: 6 \n");
+	cv_destroy(&scp->out_cv);
+	printf("seq_delunit: 7 \n");
+	cv_destroy(&scp->in_cv);
+	printf("seq_delunit: 8 \n");
+	cv_destroy(&scp->th_cv);
+
+	printf("seq_delunit: 10 \n");
+	if (scp->seqdev)
+	    destroy_dev(scp->seqdev);
+	printf("seq_delunit: 11 \n");
+	if (scp->musicdev)
+	    destroy_dev(scp->musicdev);
+	printf("seq_delunit: 12 \n");
+	scp->seqdev = scp->musicdev = NULL;
+	if (scp->midis != NULL)
+	    free(scp->midis, M_TEMP);
+	printf("seq_delunit: 13 \n");
+	if (scp->midi_flags != NULL)
+	    free(scp->midi_flags, M_TEMP);
+	printf("seq_delunit: 14 \n");
+	free(scp->out_q.b, M_TEMP);
+	printf("seq_delunit: 15 \n");
+	free(scp->in_q.b, M_TEMP);
+
+	printf("seq_delunit: 16 \n");
+
+	mtx_destroy(&scp->seq_lock);
+	printf("seq_delunit: 17 \n");
+	free(scp, M_DEVBUF);
+
+	mtx_lock(&seqinfo_mtx);
+	for ( i = unit ; i < (nseq - 1) ; i++ )
+	    seqs[i] = seqs[i+1];
+	nseq--;
+	mtx_unlock(&seqinfo_mtx);
+
+	return 0;
+}
+
+int
+seq_modevent(module_t mod, int type, void *data)
+{
+	int retval, r;
+
+	retval = 0;
+
+	switch (type) {
+	    case MOD_LOAD:
+		mtx_init(&seqinfo_mtx, "seqmod", 0, 0);
+		retval = seq_addunit();
+		break;
+
+	    case MOD_UNLOAD:
+		while (nseq) {
+			r=seq_delunit(nseq-1);
+			if (r) {
+				retval = r;
+				break;
+			}
+		}
+		if(nseq == 0) {
+			retval = 0;
+			mtx_destroy(&seqinfo_mtx);
+		}
+		break;
+
+	    default:
+		break;
+	}
+
+	return retval;
+}
+
+static int
+seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md)
+{
+
+	if (unit > scp->midi_number || unit < 0)
+	    return EINVAL;
+
+	*md = scp->midis[unit];
+
+	return 0;
+}
+
+int
+seq_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
+{
+	struct seq_softc *scp = i_dev->si_drv1;
+	int i;
+
+	if (scp == NULL)
+	    return ENXIO;
+
+	SEQ_DEBUG(3,printf("seq_open: scp %p unit %d, flags 0x%x.\n",
+		    scp, scp->unit, flags));
+
+	/*
+	 * Mark this device busy. 
+	 */
+
+	mtx_lock(&scp->seq_lock);
+	if (scp->busy) {
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(2,printf("seq_open: unit %d is busy.\n", scp->unit));
+		return EBUSY;
+	}
+	scp->fflags = flags;
+	/*
+	if ((scp->fflags & O_NONBLOCK) != 0)
+		scp->flags |= SEQ_F_NBIO;
+		*/
+	scp->music = MIDIDEV(i_dev) == SND_DEV_MUSIC;
+
+	/*
+	 * Enumerate the available midi devices
+	 */
+	scp->midi_number = 0;
+	scp->maxunits = midimapper_open(scp->mapper, &scp->mapper_cookie);
+
+	if (scp->maxunits == 0)
+	    SEQ_DEBUG(2,printf("seq_open: no midi devices\n"));
+
+	for (i = 0 ; i < scp->maxunits; i++) {
+	    scp->midis[scp->midi_number] =
+		midimapper_fetch_synth(scp->mapper, scp->mapper_cookie, i);
+	    if (scp->midis[scp->midi_number]) {
+		if ( SYNTH_OPEN(scp->midis[scp->midi_number], scp, scp->fflags)
+			!= 0 )
+		    scp->midis[scp->midi_number] = NULL;
+		else {
+		    scp->midi_flags[scp->midi_number] =
+			SYNTH_QUERY(scp->midis[scp->midi_number]);
+		    scp->midi_number++;
+		}
+	    }
+	}
+
+	timer_setvals(scp, 60, 100);
+
+	timer_start(scp);
+	timer_stop(scp);
+	/*
+	 * actually, if we're in rdonly mode, we should start the timer
+	 */
+	/*
+	 * TODO: Handle recording now
+	 */
+
+	scp->out_water = MIDIQ_SIZE(scp->out_q) / 2;
+
+	scp->busy = 1;
+	mtx_unlock(&scp->seq_lock);
+
+	SEQ_DEBUG(2,printf("seq_open: opened, mode %s.\n",
+		    scp->music ? "music" : "sequencer"));
+	SEQ_DEBUG(2,printf("Sequencer %d %p opened maxunits %d midi_number %d:\n", scp->unit, scp, scp->maxunits, scp->midi_number));
+	for (i=0;i<scp->midi_number;i++)
+		SEQ_DEBUG(3,printf("  midi %d %p\n", i, scp->midis[i]));
+
+	return 0;
+}
+
+/*
+ * seq_close
+ */
+int
+seq_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
+{
+	int i;
+	struct seq_softc *scp = i_dev->si_drv1;
+	int ret;
+
+	if (scp == NULL)
+	    return ENXIO;
+
+	SEQ_DEBUG(2,printf("seq_close: unit %d.\n", scp->unit));
+
+	mtx_lock(&scp->seq_lock);
+
+	ret = ENXIO;
+	if (scp->busy == 0)
+	    goto err;
+
+	seq_reset(scp);
+	seq_sync(scp);
+
+	for (i = 0 ; i < scp->midi_number; i++)
+	    if (scp->midis[i])
+		SYNTH_CLOSE(scp->midis[i]);
+
+	midimapper_close(scp->mapper, scp->mapper_cookie);
+
+	timer_stop(scp);
+
+	scp->busy = 0;
+	ret = 0;
+
+err:
+	SEQ_DEBUG(3,printf("seq_close: closed ret = %d.\n", ret));
+	mtx_unlock(&scp->seq_lock);
+	return ret;
+}
+
+int
+seq_read(struct cdev *i_dev, struct uio *uio, int ioflag)
+{
+	int retval, used;
+	struct seq_softc *scp = i_dev->si_drv1;
+#define SEQ_RSIZE 32
+	u_char buf[SEQ_RSIZE];
+
+	if (scp == NULL)
+	    return ENXIO;
+
+	SEQ_DEBUG(7,printf("seq_read: unit %d, resid %d.\n",
+		    scp->unit, uio->uio_resid));
+
+	mtx_lock(&scp->seq_lock);
+	if ((scp->fflags & FREAD) == 0) {
+	    SEQ_DEBUG(2,printf("seq_read: unit %d is not for reading.\n",
+			scp->unit));
+	    retval = EIO;
+	    goto err1;
+	}
+
+	/*
+	 * Begin recording.
+	 */
+	/*
+	 * if ((scp->flags & SEQ_F_READING) == 0)
+	 */
+	/*
+	 * TODO, start recording if not alread
+	 */
+
+	/*
+	 * I think the semantics are to return as soon
+	 * as possible.
+	 * Second thought, it doens't seem like midimoutain
+	 * expects that at all.
+	 * TODO: Look up in some sort of spec
+	 */
+
+	while (uio->uio_resid > 0) {
+	    while (MIDIQ_EMPTY(scp->in_q)) {
+		retval = EWOULDBLOCK;
+		/*
+		 * I wish I knew which one to care about
+		 */
+
+		if (scp->fflags & O_NONBLOCK)
+		    goto err1;
+		if (ioflag & O_NONBLOCK)
+		    goto err1;
+
+		retval = cv_wait_sig(&scp->in_cv, &scp->seq_lock);
+		if (retval == EINTR)
+		    goto err1;
+	    }
+
+	    used = MIN(MIDIQ_LEN(scp->in_q), uio->uio_resid);
+	    used = MIN(used, SEQ_RSIZE);
+
+	    SEQ_DEBUG(8,printf("midiread: uiomove cc=%d\n", used));
+	    MIDIQ_DEQ(scp->in_q, buf, used);
+	    retval = uiomove(buf, used, uio);
+	    if (retval)
+		goto err1;
+	}
+
+	retval = 0;
+err1:
+	mtx_unlock(&scp->seq_lock);
+	SEQ_DEBUG(6,printf("seq_read: ret %d, resid %d.\n",
+		    retval, uio->uio_resid));
+
+	return retval;
+}
+
+int
+seq_write(struct cdev *i_dev, struct uio *uio, int ioflag)
+{
+	u_char event[EV_SZ], newevent[EV_SZ], ev_code;
+	struct seq_softc *scp = i_dev->si_drv1;
+	int retval;
+	int used;
+
+	SEQ_DEBUG(7,printf("seq_write: unit %d, resid %d.\n",
+		    scp->unit, uio->uio_resid));
+
+	if (scp == NULL)
+	    return ENXIO;
+
+	mtx_lock(&scp->seq_lock);
+
+	if ((scp->fflags & FWRITE) == 0) {
+	    SEQ_DEBUG(2,printf("seq_write: unit %d is not for writing.\n",
+			scp->unit));
+	    retval = EIO;
+	    goto err0;
+	}
+
+	while (uio->uio_resid > 0) {
+	    while (MIDIQ_AVAIL(scp->out_q) == 0) {
+		retval = EWOULDBLOCK;
+		if (scp->fflags & O_NONBLOCK)
+		    goto err0;
+		if (ioflag & O_NONBLOCK)
+		    goto err0;
+		SEQ_DEBUG(8,printf("seq_write cvwait\n"));
+
+		scp->playing = 1;
+		cv_broadcast(&scp->out_cv);
+		cv_broadcast(&scp->state_cv);
+
+		retval = cv_wait_sig(&scp->out_cv, &scp->seq_lock);
+		    /*
+		     * We slept, maybe things have changed since last
+		     * dying check
+		     */
+		if (retval == EINTR)
+		    goto err0;
+#if 0
+		    /*
+		     * Useless test
+		     */
+		if (scp != i_dev->si_drv1)
+		    retval = ENXIO;
+#endif
+	    }
+
+	    used = MIN(uio->uio_resid, 4);
+
+	    SEQ_DEBUG(8,printf("seqout: resid %d len %jd avail %jd\n",
+			uio->uio_resid, (intmax_t)MIDIQ_LEN(scp->out_q),
+			(intmax_t)MIDIQ_AVAIL(scp->out_q)));
+
+	    if (used != 4) {
+		retval = ENXIO;
+		goto err0;
+	    }
+
+	    retval = uiomove(event, used, uio);
+	    if (retval)
+		goto err0;
+
+	    ev_code = event[0];
+	    SEQ_DEBUG(8,printf("seq_write: unit %d, event %s.\n",
+			scp->unit, midi_cmdname(ev_code, cmdtab_seqevent)));
+
+	    /* Have a look at the event code. */
+	    if (ev_code == SEQ_FULLSIZE) {
+
+		/*
+		 * TODO: restore code for SEQ_FULLSIZE
+		 */
+#if 0
+		/* A long event, these are the patches/samples for a synthesizer. */
+		midiunit = *(u_short *)&event[2];
+		mtx_lock(&sd->seq_lock);
+		ret = lookup_mididev(scp, midiunit, LOOKUP_OPEN, &md);
+		mtx_unlock(&sd->seq_lock);
+		if (ret != 0)
+		    return (ret);
+
+		SEQ_DEBUG(printf("seq_write: loading a patch to the unit %d.\n", midiunit));
+
+		ret = md->synth.loadpatch(md, *(short *)&event[0], buf, p + 4, count, 0);
+		return (ret);
+#else
+		/*
+		 * For now, just flush the darn buffer
+		 */
+		SEQ_DEBUG(2,printf("seq_write: SEQ_FULLSIZE flusing buffer.\n"));
+		while (uio->uio_resid > 0) {
+		    retval = uiomove(event, EV_SZ, uio);
+		    if (retval)
+			goto err0;
+
+		}
+		retval = 0;
+		goto err0;
+#endif
+	    }
+
+	    retval = EINVAL;
+	    if (ev_code >= 128) {
+
+		/*
+		 * Some sort of an extended event. The size is eight bytes.
+		 * scoop extra info.
+		 */
+		if (scp->music && ev_code == SEQ_EXTENDED) {
+		    SEQ_DEBUG(2,printf("seq_write: invalid level two event %x.\n", ev_code));
+		    goto err0;
+		}
+		if (uiomove((caddr_t)&event[4], 4, uio)) {
+		    SEQ_DEBUG(2,printf("seq_write: user memory mangled?\n"));
+		    goto err0;
+		}
+	    } else {
+		/*
+		 * Size four event.
+		 */
+		if (scp->music) {
+		    SEQ_DEBUG(2,printf("seq_write: four byte event in music mode.\n"));
+		    goto err0;
+		}
+	    }
+	    if (ev_code == SEQ_MIDIPUTC) {
+		/*
+		 * TODO: event[2] is unit number to receive char.  Range check
+		 * it
+		 */
+	    }
+
+	    if (scp->music) {
+#if not_ever_ever
+		if (event[0] == EV_TIMING && 
+			(event[1] == TMR_START || event[1] == TMR_STOP) ) {
+		    /*
+		     * For now, try to make midimoutain work by
+		     * forcing these events to be processed immediatly
+		     */
+		    seq_processevent(scp, event);
+		}
+		else
+		    MIDIQ_ENQ(scp->out_q, event, EV_SZ);
+#else
+		MIDIQ_ENQ(scp->out_q, event, EV_SZ);
+#endif
+	    } else {
+		if (seq_convertold(event, newevent) > 0)
+		    MIDIQ_ENQ(scp->out_q, newevent, EV_SZ);
+#if 0
+		else
+		    goto err0;
+#endif
+	    }
+
+	}
+
+	scp->playing = 1;
+	cv_broadcast(&scp->state_cv);
+	cv_broadcast(&scp->out_cv);
+
+	retval = 0;
+
+err0:
+	SEQ_DEBUG(6,printf("seq_write done: leftover buffer length %d retval %d\n",
+		    uio->uio_resid, retval));
+	mtx_unlock(&scp->seq_lock);
+	return retval;
+}
+
+int
+seq_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
+{
+	int midiunit, ret, tmp;
+	struct seq_softc *scp = i_dev->si_drv1;
+	struct synth_info *synthinfo;
+	struct midi_info *midiinfo;
+	u_char event[EV_SZ];
+	u_char newevent[EV_SZ];
+
+	kobj_t md;
+	/*
+	 * struct snd_size *sndsize;
+	 */
+
+	if (scp == NULL)
+	    return ENXIO;
+
+	SEQ_DEBUG(6,printf("seq_ioctl: unit %d, cmd %s.\n",
+		    scp->unit, midi_cmdname(cmd, cmdtab_seqioctl)));
+
+	ret = 0;
+
+	switch (cmd) {
+	    case SNDCTL_SEQ_GETTIME:
+		/*
+		 * ioctl needed by libtse
+		 */
+		mtx_lock(&scp->seq_lock);
+		*(int *)arg = timer_now(scp);
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(6,printf("seq_ioctl: gettime %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+	    case SNDCTL_TMR_METRONOME:
+		/* fallthrough */
+	    case SNDCTL_TMR_SOURCE:
+		/*
+		 * Not implemented
+		 */
+		ret = 0;
+		break;
+	    case SNDCTL_TMR_TEMPO:
+		event[1] = TMR_TEMPO;
+		goto timerevent;
+	    case SNDCTL_TMR_TIMEBASE:
+		event[1] = TMR_TIMERBASE;
+		goto timerevent;
+	    case SNDCTL_TMR_START:
+		event[1] = TMR_START;
+		goto timerevent;
+	    case SNDCTL_TMR_STOP:
+		event[1] = TMR_STOP;
+		goto timerevent;
+	    case SNDCTL_TMR_CONTINUE:
+		event[1] = TMR_CONTINUE;
+	    timerevent:
+		event[0] = EV_TIMING;
+		event[4] = *(int *)arg & 0xFF;
+		event[5] = (*(int *)arg >> 8) & 0xFF;
+		event[6] = (*(int *)arg >> 16) & 0xFF;
+		event[7] = (*(int *)arg >> 24) & 0xFF;
+		mtx_lock(&scp->seq_lock);
+		if (!scp->music) {
+		    ret = EINVAL;
+		    mtx_unlock(&scp->seq_lock);
+		    break;
+		}
+		seq_processevent(scp, event);
+		mtx_unlock(&scp->seq_lock);
+		break;
+	    case SNDCTL_TMR_SELECT:
+		SEQ_DEBUG(2,printf("seq_ioctl: SNDCTL_TMR_SELECT not supported\n"));
+		ret = EINVAL;
+		break;
+	    case SNDCTL_SEQ_SYNC:
+		if (mode == O_RDONLY) {
+		    ret = 0;
+		    break;
+		}
+		mtx_lock(&scp->seq_lock);
+		ret = seq_sync(scp);
+		mtx_unlock(&scp->seq_lock);
+		break;
+	    case SNDCTL_SEQ_PANIC:
+		/* fallthrough */
+	    case SNDCTL_SEQ_RESET:
+		/*
+		 * SNDCTL_SEQ_PANIC == SNDCTL_SEQ_RESET
+		 */
+		mtx_lock(&scp->seq_lock);
+		seq_reset(scp);
+		mtx_unlock(&scp->seq_lock);
+		ret = 0;
+		break;
+	    case SNDCTL_SEQ_TESTMIDI:
+		mtx_lock(&scp->seq_lock);
+		/*
+		 * TODO: SNDCTL_SEQ_TESTMIDI now means "can I write to the
+		 * device?".
+		 */
+		mtx_unlock(&scp->seq_lock);
+		break;
+#if 0
+	    case SNDCTL_SEQ_GETINCOUNT:
+		if (mode == O_WRONLY)
+		    *(int *)arg = 0;
+		else {
+		    mtx_lock(&scp->seq_lock);
+		    *(int *)arg = scp->in_q.rl;
+		    mtx_unlock(&scp->seq_lock);
+		    SEQ_DEBUG(printf("seq_ioctl: incount %d.\n", *(int *)arg));
+		}
+		ret = 0;
+		break;
+	    case SNDCTL_SEQ_GETOUTCOUNT:
+		if (mode == O_RDONLY)
+		    *(int *)arg = 0;
+		else {
+		    mtx_lock(&scp->seq_lock);
+		    *(int *)arg = scp->out_q.fl;
+		    mtx_unlock(&scp->seq_lock);
+		    SEQ_DEBUG(printf("seq_ioctl: outcount %d.\n", *(int *)arg));
+		}
+		ret = 0;
+		break;
+#endif
+	    case SNDCTL_SEQ_CTRLRATE:
+		if (*(int *)arg != 0) {
+		    ret = EINVAL;
+		    break;
+		}
+		mtx_lock(&scp->seq_lock);
+		*(int *)arg = scp->timerbase;
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(3,printf("seq_ioctl: ctrlrate %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+		/*
+		 * TODO: ioctl SNDCTL_SEQ_RESETSAMPLES
+		 */
+#if 0
+	    case SNDCTL_SEQ_RESETSAMPLES:
+		mtx_lock(&scp->seq_lock);
+		ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
+		mtx_unlock(&scp->seq_lock);
+		if (ret != 0)
+		    break;
+		ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg, SND_DEV_MIDIN), cmd, arg, mode, td);
+		break;
+#endif
+	    case SNDCTL_SEQ_NRSYNTHS:
+		mtx_lock(&scp->seq_lock);
+		    *(int *)arg = scp->midi_number;
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(3,printf("seq_ioctl: synths %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+	    case SNDCTL_SEQ_NRMIDIS:
+		mtx_lock(&scp->seq_lock);
+		if (scp->music)
+		    *(int *)arg = 0;
+		else {
+		    /*
+		     * TODO: count the numbder of devices that can WRITERAW
+		     */
+		    *(int *)arg = scp->midi_number;
+		}
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(3,printf("seq_ioctl: midis %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+		/*
+		 * TODO: ioctl SNDCTL_SYNTH_MEMAVL
+		 */
+#if 0
+	    case SNDCTL_SYNTH_MEMAVL:
+		mtx_lock(&scp->seq_lock);
+		ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
+		mtx_unlock(&scp->seq_lock);
+		if (ret != 0)
+		    break;
+		ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg, SND_DEV_MIDIN), cmd, arg, mode, td);
+		break;
+#endif
+	    case SNDCTL_SEQ_OUTOFBAND:
+		for (ret = 0; ret < EV_SZ; ret++)
+		    event[ret] = (u_char)arg[0];
+
+		mtx_lock(&scp->seq_lock);
+		if (scp->music)
+		    ret = seq_processevent(scp, event);
+		else {
+		    if (seq_convertold(event, newevent) > 0)
+			ret = seq_processevent(scp, newevent);
+		    else ret = EINVAL;
+		}
+		mtx_unlock(&scp->seq_lock);
+		break;
+	    case SNDCTL_SYNTH_INFO:
+		synthinfo = (struct synth_info *)arg;
+		midiunit = synthinfo->device;
+		mtx_lock(&scp->seq_lock);
+		if (seq_fetch_mid(scp, midiunit, &md) == 0) {
+		    bzero(synthinfo, sizeof(*synthinfo));
+		    synthinfo->name[0] = 'f';
+		    synthinfo->name[1] = 'a';
+		    synthinfo->name[2] = 'k';
+		    synthinfo->name[3] = 'e';
+		    synthinfo->name[4] = 's';
+		    synthinfo->name[5] = 'y';
+		    synthinfo->name[6] = 'n';
+		    synthinfo->name[7] = 't';
+		    synthinfo->name[8] = 'h';
+		    synthinfo->device = midiunit;
+		    synthinfo->synth_type = SYNTH_TYPE_MIDI;
+		    synthinfo->capabilities = scp->midi_flags[midiunit];
+		    ret = 0;
+		} else
+		    ret = EINVAL;
+		mtx_unlock(&scp->seq_lock);
+		break;
+	    case SNDCTL_MIDI_INFO:
+		midiinfo = (struct midi_info *)arg;
+		midiunit = midiinfo->device;
+		mtx_lock(&scp->seq_lock);
+		if (seq_fetch_mid(scp, midiunit, &md) == 0) {
+		    bzero(midiinfo, sizeof(*midiinfo));
+		    midiinfo->name[0] = 'f';
+		    midiinfo->name[1] = 'a';
+		    midiinfo->name[2] = 'k';
+		    midiinfo->name[3] = 'e';
+		    midiinfo->name[4] = 'm';
+		    midiinfo->name[5] = 'i';
+		    midiinfo->name[6] = 'd';
+		    midiinfo->name[7] = 'i';
+		    midiinfo->device = midiunit;
+		    midiinfo->capabilities = scp->midi_flags[midiunit];
+		    /*
+		     * TODO: What devtype?
+		     */
+		    midiinfo->dev_type = 0x01;
+		    ret = 0;
+		} else
+		    ret = EINVAL;
+		mtx_unlock(&scp->seq_lock);
+		break;
+	    case SNDCTL_SEQ_THRESHOLD:
+		mtx_lock(&scp->seq_lock);
+		RANGE(*(int *)arg, 1, MIDIQ_SIZE(scp->out_q) - 1);
+		scp->out_water = *(int *)arg;
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(3,printf("seq_ioctl: water %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+	    case SNDCTL_MIDI_PRETIME:
+		tmp = *(int *)arg;
+		if (tmp < 0)
+		    tmp = 0;
+		mtx_lock(&scp->seq_lock);
+		scp->pre_event_timeout = (hz * tmp) / 10;
+		*(int *)arg = scp->pre_event_timeout;
+		mtx_unlock(&scp->seq_lock);
+		SEQ_DEBUG(3,printf("seq_ioctl: pretime %d.\n", *(int *)arg));
+		ret = 0;
+		break;
+	    case SNDCTL_FM_4OP_ENABLE:
+	    case SNDCTL_PMGR_IFACE:
+	    case SNDCTL_PMGR_ACCESS:
+		/*
+		 * Patch manager and fm are ded, ded, ded.
+		 */
+		/* fallthrough */
+	    default:
+		/*
+		 * TODO: Consider ioctl default case.
+		 * Old code used to
+		 * if ((scp->fflags & O_ACCMODE) == FREAD) {
+		 *	ret = EIO;
+		 *	break;
+		 * }
+		 * Then pass on the ioctl to device 0
+		 */
+		SEQ_DEBUG(2,printf("seq_ioctl: unsupported IOCTL %ld.\n", cmd));
+		ret = EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+int
+seq_poll(struct cdev *i_dev, int events, struct thread *td)
+{
+	int ret, lim;
+	struct seq_softc *scp = i_dev->si_drv1;
+
+	SEQ_DEBUG(3, printf("seq_poll: unit %d.\n", scp->unit));
+	printf("seq_poll: unit %d.\n", scp->unit);
+
+	mtx_lock(&scp->seq_lock);
+
+	ret = 0;
+
+	/* Look up the apropriate queue and select it. */
+	if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+	    /* Start playing. */
+	    scp->playing = 1;
+	    cv_broadcast(&scp->state_cv);
+	    cv_broadcast(&scp->out_cv);
+
+	    lim = scp->out_water;
+
+	    if (MIDIQ_AVAIL(scp->out_q) < lim)
+		/* No enough space, record select. */
+		selrecord(td, &scp->out_sel);
+	    else
+		/* We can write now. */
+		ret |= events & (POLLOUT | POLLWRNORM);
+	}
+
+	if ((events & (POLLIN | POLLRDNORM)) != 0) {
+	    /* TODO: Start recording. */
+
+	    /* Find out the boundary. */
+	    lim = 1;
+	    if (MIDIQ_LEN(scp->in_q) < lim)
+		/* No data ready, record select. */
+		selrecord(td, &scp->in_sel);
+	    else
+		/* We can read now. */
+		ret |= events & (POLLIN | POLLRDNORM);
+	}
+
+	mtx_unlock(&scp->seq_lock);
+
+	return (ret);
+}
+#if 0
+static void
+sein_qtr(void *p, void /* mididev_info */ *md)
+{
+	struct seq_softc *scp;
+
+	scp = (struct seq_softc *)p;
+
+	mtx_lock(&scp->seq_lock);
+
+	/* Restart playing if we have the data to output. */
+	if (scp->queueout_pending)
+		seq_callback(scp, SEQ_CB_START | SEQ_CB_WR);
+	/* Check the midi device if we are reading. */
+	if ((scp->flags & SEQ_F_READING) != 0)
+		seq_midiinput(scp, md);
+
+	mtx_unlock(&scp->seq_lock);
+}
+#endif
+/*
+ * seq_convertold
+ * Was the old playevent.  Use this to convert and old
+ * style /dev/sequencer event to a /dev/music event 
+ */
+static int
+seq_convertold(u_char *event, u_char *out)
+{
+	int used;
+	u_char dev, chn, note, vel;
+
+	out[0] = out[1] = out[2] = out[3] = out[4]  = out[5]  = out[6]  = out[7]   = 0;
+
+	dev = 0;
+	chn = event[1];
+	note = event[2];
+	vel = event[3];
+
+	used = 0;
+
+restart:
+	/*
+	 * TODO: Debug statement
+	 */
+	switch(event[0]) {
+	    case EV_TIMING:
+	    case EV_CHN_VOICE:
+	    case EV_CHN_COMMON:
+case EV_SYSEX:
+case EV_SEQ_LOCAL:
+		out[0] = event[0];
+		out[1] = event[1];
+		out[2] = event[2];
+		out[3] = event[3];
+		out[4] = event[4];
+		out[5] = event[5];
+		out[6] = event[6];
+		out[7] = event[7];
+		used += 8;
+		break;
+	    case SEQ_NOTEOFF:
+		out[0] = EV_CHN_VOICE;
+		out[1] = dev;
+		out[2] = MIDI_NOTEOFF;
+		out[3] = chn;
+		out[4] = note;
+		out[5] = 255;
+	used += 4;
+		break;
+
+	    case SEQ_NOTEON:
+		out[0] = EV_CHN_VOICE;
+		out[1] = dev;
+		out[2] = MIDI_NOTEON;
+		out[3] = chn;
+		out[4] = note;
+		out[5] = vel;
+	used += 4;
+		break;
+
+		/*
+		 * wait delay = (event[2] << 16) + (event[3] << 8) + event[4]
+		 */
+
+	    case SEQ_PGMCHANGE:
+		out[0] = EV_CHN_COMMON;
+		out[1] = dev;
+		out[2] = MIDI_PGM_CHANGE;
+		out[3] = chn;
+		out[4] = note;
+		out[5] = vel;
+	used += 4;
+		break;
+/*
+		out[0] = EV_TIMING;
+		out[1] = dev;
+		out[2] = MIDI_PGM_CHANGE;
+		out[3] = chn;
+		out[4] = note;
+		out[5] = vel;
+		SEQ_DEBUG(4,printf("seq_playevent: synctimer\n"));
+		break;
+*/
+
+	    case SEQ_MIDIPUTC:
+		SEQ_DEBUG(4,printf("seq_playevent: put data 0x%02x, unit %d.\n",
+			    event[1], event[2]));
+		/*
+		 * Pass through to the midi device.
+		 * device = event[2]
+		 * data = event[1]
+		 */
+		out[0] = SEQ_MIDIPUTC;
+		out[1] = dev;
+		out[2] = chn;
+	used += 4;
+		break;
+#if notyet
+	    case SEQ_ECHO:
+		/*
+		 * This isn't handled here yet because I don't know if I can
+		 * just use four bytes events.  There might be consequences 
+		 * in the _read routing
+		 */
+		if (seq_copytoinput(scp, event, 4) == EAGAIN) {
+		    ret = QUEUEFULL;
+		    break;
+		}
+		ret = MORE;
+		break;
+#endif
+	    case SEQ_EXTENDED:
+		switch (event[1]) {
+		    case SEQ_NOTEOFF:
+		    case SEQ_NOTEON:
+		    case SEQ_PGMCHANGE:
+			event++;
+			used = 4;
+			goto restart;
+			break;
+		    case SEQ_AFTERTOUCH:
+			/*
+			 * SYNTH_AFTERTOUCH(md, event[3], event[4])
+			 */
+		    case SEQ_BALANCE:
+			/*
+			 * SYNTH_PANNING(md, event[3], (char)event[4])
+			 */
+		    case SEQ_CONTROLLER:
+			/*
+			 * SYNTH_CONTROLLER(md, event[3], event[4], *(short *)&event[5])
+			 */
+		    case SEQ_VOLMODE:
+			/*
+			 * SYNTH_VOLUMEMETHOD(md, event[3]) 
+			 */
+		    default:
+			SEQ_DEBUG(2,printf("seq_convertold: SEQ_EXTENDED type %d"
+				"not handled\n", event[1]));
+			break;
+		}
+		break;
+	    case SEQ_WAIT:
+		out[0] = EV_TIMING;
+		out[1] = TMR_WAIT_REL;
+		out[4] = event[2];
+		out[5] = event[3];
+		out[6] = event[4];
+
+		SEQ_DEBUG(5,printf("SEQ_WAIT %d", event[2] + (event[3] << 8) + (event[4] << 24)));
+
+		used+= 4;
+		break;
+
+	    case SEQ_ECHO:
+	    case SEQ_SYNCTIMER:
+	    case SEQ_PRIVATE:
+	    default:
+		SEQ_DEBUG(2,printf("seq_convertold: event type %d not handled %d %d %d\n", event[0], event[1], event[2], event[3]));
+		break;
+	}
+	return used;
+}
+
+/*
+ * Writting to the sequencer buffer never blocks and drops
+ * input which cannot be queued
+ */
+void
+seq_copytoinput(struct seq_softc *scp, u_char *event, int len)
+{
+
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+
+	if (MIDIQ_AVAIL(scp->in_q) < len) {
+	    /*
+	     * ENOROOM?  EINPUTDROPPED? ETOUGHLUCK?
+	     */
+	  SEQ_DEBUG(2,printf("seq_copytoinput: queue full\n"));
+	} else {
+	    MIDIQ_ENQ(scp->in_q, event, len);
+	    selwakeup(&scp->in_sel);
+	    cv_broadcast(&scp->in_cv);
+	}
+
+}
+
+static int
+seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event)
+{
+	int ret, voice;
+	u_char cmd, chn, note, parm;
+
+	ret = 0;
+	cmd = event[2];
+	chn = event[3];
+	note = event[4];
+	parm = event[5];
+
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+
+	SEQ_DEBUG(5,printf("seq_chnvoice: unit %d, dev %d, cmd %s,"
+		   " chn %d, note %d, parm %d.\n", scp->unit, event[1], 
+		   midi_cmdname(cmd, cmdtab_seqcv), chn, note, parm));
+
+	voice = SYNTH_ALLOC(md, chn, note);
+
+	mtx_unlock(&scp->seq_lock);
+
+	switch (cmd) {
+	    case MIDI_NOTEON:
+		if (note < 128 || note == 255) {
+#if 0
+		    if (scp->music && chn == 9) {
+			/*
+			 * This channel is a percussion. The note number is the
+			 * patch number. 
+			 */
+			/*
+			mtx_unlock(&scp->seq_lock);
+			if (SYNTH_SETINSTR(md, voice, 128 + note) == EAGAIN) {
+			    mtx_lock(&scp->seq_lock);
+			    return (QUEUEFULL);
+			}
+			mtx_lock(&scp->seq_lock);
+			*/
+			note = 60; /* Middle C. */
+		    }
+#endif
+		    if (scp->music) {
+			/*
+			mtx_unlock(&scp->seq_lock);
+			if (SYNTH_SETUPVOICE(md, voice, chn) == EAGAIN) {
+			    mtx_lock(&scp->seq_lock);
+			    return (QUEUEFULL);
+			}
+			mtx_lock(&scp->seq_lock);
+			*/
+		    }
+		    SYNTH_STARTNOTE(md, voice, note, parm);
+		}
+		break;
+	case MIDI_NOTEOFF:
+		SYNTH_KILLNOTE(md, voice, note, parm);
+		break;
+	case MIDI_KEY_PRESSURE:
+		SYNTH_AFTERTOUCH(md, voice, parm);
+		break;
+	default:
+		ret = 1;
+		SEQ_DEBUG(2,printf("seq_chnvoice event type %d not handled\n", event[1]));
+		break;
+	}
+
+	mtx_lock(&scp->seq_lock);
+	return ret;
+}
+
+static int
+seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event)
+{
+	int ret;
+	u_short w14;
+	u_char cmd, chn, p1;
+
+	ret = 0;
+	cmd = event[2];
+	chn = event[3];
+	p1 = event[4];
+	w14 = *(u_short *)&event[6];
+
+	SEQ_DEBUG(5,printf("seq_chncommon: unit %d, dev %d, cmd %s, chn %d,"
+		    " p1 %d, w14 %d.\n", scp->unit, event[1],
+		    midi_cmdname(cmd, cmdtab_seqccmn), chn, p1, w14));
+	mtx_unlock(&scp->seq_lock);
+	switch (cmd) {
+	    case MIDI_PGM_CHANGE:
+		    SEQ_DEBUG(4,printf("seq_chncommon pgmchn chn %d pg %d\n",
+				chn, p1));
+		    SYNTH_SETINSTR(md, chn, p1);
+		break;
+	    case MIDI_CTL_CHANGE:
+		SEQ_DEBUG(4,printf("seq_chncommon ctlch chn %d pg %d %d\n",
+			    chn, p1, w14));
+		SYNTH_CONTROLLER(md, chn, p1, w14);
+		break;
+	    case MIDI_PITCH_BEND:
+		if (scp->music) {
+		    /*
+		     * TODO: MIDI_PITCH_BEND
+		     */
+#if 0
+		    mtx_lock(&md->synth.vc_mtx);
+		    md->synth.chn_info[chn].bender_value = w14;
+		    if (md->midiunit >= 0) {
+			/* Handle all of the notes playing on this channel. */
+			key = ((int)chn << 8);
+			for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+			    if ((md->synth.alloc.map[i] & 0xff00) == key) {
+				mtx_unlock(&md->synth.vc_mtx);
+				mtx_unlock(&scp->seq_lock);
+				if (md->synth.bender(md, i, w14) == EAGAIN) {
+				    mtx_lock(&scp->seq_lock);
+				    return (QUEUEFULL);
+				}
+				mtx_lock(&scp->seq_lock);
+			    }
+		    } else {
+			mtx_unlock(&md->synth.vc_mtx);
+			mtx_unlock(&scp->seq_lock);
+			if (md->synth.bender(md, chn, w14) == EAGAIN) {
+			    mtx_lock(&scp->seq_lock);
+			    return (QUEUEFULL);
+			}
+			mtx_lock(&scp->seq_lock);
+		    }
+#endif
+		} else
+		    SYNTH_BENDER(md, chn, w14);
+		break;
+	    default:
+		ret = 1;
+		SEQ_DEBUG(2,printf("seq_chncommon event type %d not handled.\n", event[1]));
+		break;
+
+	}
+	mtx_lock(&scp->seq_lock);
+	return ret;
+}
+
+static int
+seq_timing(struct seq_softc *scp, u_char *event)
+{
+	int param;
+	int ret;
+
+	ret = 0;
+	param = event[4] + (event[5] << 8) +
+	    (event[6] << 16) + (event[7] << 24);
+
+	SEQ_DEBUG(5,printf("seq_timing: unit %d, cmd %d, param %d.\n",
+		    scp->unit, event[1], param));
+	switch (event[1]) {
+	case TMR_WAIT_REL:
+	    timer_wait(scp, param, 0);
+		break;
+	case TMR_WAIT_ABS:
+	    timer_wait(scp, param, 1);
+		break;
+	case TMR_START:
+		timer_start(scp);
+		cv_broadcast(&scp->reset_cv);
+		break;
+	case TMR_STOP:
+		timer_stop(scp);
+		/*
+		 * The following cv_broadcast isn't needed since we only
+		 * wait for 0->1 transitions.  It probably won't hurt
+		 */
+		cv_broadcast(&scp->reset_cv);
+		break;
+	case TMR_CONTINUE:
+		timer_continue(scp);
+		cv_broadcast(&scp->reset_cv);
+		break;
+	case TMR_TEMPO:
+		if (param < 8)
+		    param = 8;
+		if (param > 360)
+		    param = 360;
+		SEQ_DEBUG(4,printf("Timer set tempo %d\n", param));
+		timer_setvals(scp, param, scp->timerbase);
+		break;
+	case TMR_TIMERBASE:
+		if (param < 1)
+		    param = 1;
+		if (param > 1000)
+		    param = 1000;
+		SEQ_DEBUG(4,printf("Timer set timerbase %d\n", param));
+		timer_setvals(scp, scp->tempo, param);
+		break;
+	case TMR_ECHO:
+		/*
+		 * TODO: Consider making 4-byte events for /dev/sequencer
+		 * PRO: Maybe needed by legacy apps
+		 * CON: soundcard.h has been warning for a while many years
+		 * to expect 8 byte events.
+		 */
+#if 0
+		if (scp->music)
+			seq_copytoinput(scp, event, 8);
+		else {
+			param = (param << 8 | SEQ_ECHO);
+			seq_copytoinput(scp, (u_char *)&param, 4);
+		}
+#else
+		seq_copytoinput(scp, event, 8);
+#endif
+		break;
+	    default:
+		SEQ_DEBUG(2,printf("seq_timing event type %d not handled.\n", event[1]));
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+static int
+seq_local(struct seq_softc *scp, u_char *event)
+{
+	int ret;
+
+	ret = 0;
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+
+	SEQ_DEBUG(5,printf("seq_local: unit %d, cmd %d\n", scp->unit, event[1]));
+	switch (event[1]) {
+	    default:
+		printf("seq_local event type %d not handled\n", event[1]);
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+static int
+seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event)
+{
+	int i, l;
+
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+	SEQ_DEBUG(5,printf("seq_sysex: unit %d device %d\n", scp->unit, event[1]));
+	l = 0;
+	for (i = 0 ; i < 6 && event[i + 2] != 0xff ; i++)
+		l = i + 1;
+	if (l > 0) {
+		mtx_unlock(&scp->seq_lock);
+		if (SYNTH_SENDSYSEX(md, &event[2], l) == EAGAIN) {
+			mtx_lock(&scp->seq_lock);
+			return 1;
+		}
+		mtx_lock(&scp->seq_lock);
+	}
+	return 0;
+}
+
+/*
+ * Reset no longer closes the raw devices nor seq_sync's
+ * Callers are IOCTL and seq_close
+ */
+static void
+seq_reset(struct seq_softc *scp)
+{
+	int chn,  i;
+	kobj_t m;
+
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+
+	SEQ_DEBUG(5,printf("seq_reset: unit %d.\n", scp->unit));
+
+	/*
+	 * Stop reading and writing.
+	 */
+
+	/* scp->recording = 0; */
+	scp->playing = 0;
+	cv_broadcast(&scp->state_cv);
+	cv_broadcast(&scp->out_cv);
+	cv_broadcast(&scp->reset_cv);
+
+	/*
+	 * For now, don't reset the timers.
+	 */
+	MIDIQ_CLEAR(scp->in_q);
+	MIDIQ_CLEAR(scp->out_q);
+
+	for (i = 0; i < scp->midi_number; i++) {
+	    m = scp->midis[i];
+	    mtx_unlock(&scp->seq_lock);
+	    SYNTH_RESET(m);
+	    for (chn = 0 ; chn < 16 ; chn++) {
+		SYNTH_CONTROLLER(m, chn, 123, 0) ;
+		SYNTH_CONTROLLER(m, chn, 121, 0);
+		SYNTH_BENDER(m, chn, 1 << 13);
+	    }
+	    mtx_lock(&scp->seq_lock);
+	}
+}
+
+/*
+ * seq_sync
+ * *really* flush the output queue
+ * flush the event queue, then flush the synthsisers.
+ * Callers are IOCTL and close
+ */
+
+#define SEQ_SYNC_TIMEOUT 8
+static int
+seq_sync(struct seq_softc *scp)
+{
+	int i, rl, sync[16], done;
+
+	mtx_assert(&scp->seq_lock, MA_OWNED);
+
+	SEQ_DEBUG(4,printf("seq_sync: unit %d.\n", scp->unit));
+
+	/*
+	 * Wait until output queue is empty.  Check every so often to see if
+	 * the queue is moving along.  If it isn't just abort.
+	 */
+	while (!MIDIQ_EMPTY(scp->out_q)) {
+
+	    if (!scp->playing) {
+		scp->playing = 1;
+		cv_broadcast(&scp->state_cv);
+		cv_broadcast(&scp->out_cv);
+	    }
+
+	    rl = MIDIQ_LEN(scp->out_q);
+
+	    i = cv_timedwait_sig(&scp->out_cv, 
+		    &scp->seq_lock, SEQ_SYNC_TIMEOUT * hz);
+
+	    if (i == EINTR || i == ERESTART) {
+		if (i == EINTR) {
+		    /*
+		     * XXX: I don't know why we stop playing
+		     */
+		    scp->playing = 0;
+		    cv_broadcast(&scp->out_cv);
+		}
+		return i;
+	    }
+
+	    if (i == EWOULDBLOCK && rl == MIDIQ_LEN(scp->out_q) &&
+		    scp->waiting == 0) {
+		/*
+		 * A queue seems to be stuck up. Give up and clear queues.
+		 */
+		MIDIQ_CLEAR(scp->out_q);
+		scp->playing = 0;
+		cv_broadcast(&scp->state_cv);
+		cv_broadcast(&scp->out_cv);
+		cv_broadcast(&scp->reset_cv);
+
+		/*
+		 * TODO: Consider if the raw devices need to be flushed
+		 */
+
+		SEQ_DEBUG(1,printf("seq_sync queue stuck, aborting\n"));
+
+		return i;
+	    }
+	}
+
+	scp->playing = 0;
+	/*
+	 * Since syncing a midi device might block, unlock scp->seq_lock.
+	 */
+
+	mtx_unlock(&scp->seq_lock);
+	for(i = 0 ; i < scp->midi_number; i++)
+	    sync[i] = 1;
+
+	do {
+	    done = 1;
+	    for (i = 0 ; i < scp->midi_number; i++)
+		if (sync[i]) {
+		    if (SYNTH_INSYNC(scp->midis[i]) == 0)
+			sync[i] = 0;
+		    else
+			done = 0;
+		}
+
+	   if (!done)
+		DELAY(5000);
+
+	} while (!done);
+
+	mtx_lock(&scp->seq_lock);
+	return 0;
+}
+
+char *
+midi_cmdname(int cmd, midi_cmdtab *tab)
+{
+	while (tab->name != NULL) {
+		if (cmd == tab->cmd)
+			return (tab->name);
+		tab++;
+	}
+
+	return ("unknown");
+}
Index: sys/dev/sound/midi/sequencer.h
===================================================================
RCS file: sys/dev/sound/midi/sequencer.h
diff -N sys/dev/sound/midi/sequencer.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/sequencer.h	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,88 @@
+/*
+ * Include file for midi sequencer driver.
+ * (c) 2003 Mathew Kanner
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/sound/midi/sequencer.h,v 1.5 2002/01/04 01:13:47 tanimura Exp $
+ *
+ */
+
+#ifndef _SEQUENCER_H_
+#define _SEQUENCER_H_
+
+
+#define SEQ_CDEV_MAJOR MIDI_CDEV_MAJOR
+
+#define NSEQ_MAX	16
+
+/*
+ * many variables should be reduced to a range. Here define a macro
+ */
+
+#define RANGE(var, low, high) (var) = \
+((var)<(low)?(low) : (var)>(high)?(high) : (var))
+
+#ifdef _KERNEL
+
+void	seq_timer(void *arg);
+
+SYSCTL_DECL(_hw_midi_seq);
+
+extern int	seq_debug;
+#define SEQ_DEBUG(y, x)			\
+	do {				\
+		if (seq_debug >= y) {	\
+			(x);		\
+		}			\
+	} while(0)
+
+SYSCTL_DECL(_hw_midi);
+
+#endif /* _KERNEL */
+
+#define SYNTHPROP_MIDI		1
+#define SYNTHPROP_SYNTH		2
+#define SYNTHPROP_RX		4
+#define SYNTHPROP_TX		8
+
+struct _midi_cmdtab {
+    int     cmd;
+    char *  name;
+};
+typedef struct _midi_cmdtab      midi_cmdtab;
+extern midi_cmdtab cmdtab_seqevent[];
+extern midi_cmdtab cmdtab_seqioctl[];
+extern midi_cmdtab cmdtab_timer[];
+extern midi_cmdtab cmdtab_seqcv[];
+extern midi_cmdtab cmdtab_seqccmn[];
+
+char     *midi_cmdname(int cmd, midi_cmdtab *tab);
+
+enum {
+	MORE,
+	TIMERARMED,
+	QUEUEFULL
+};
+
+#endif
Index: sys/dev/sound/midi/synth_if.m
===================================================================
RCS file: sys/dev/sound/midi/synth_if.m
diff -N sys/dev/sound/midi/synth_if.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/sound/midi/synth_if.m	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,285 @@
+INTERFACE synth;
+
+#include <sys/systm.h>
+
+CODE {
+
+synth_killnote_t nokillnote;
+synth_startnote_t nostartnote;
+synth_setinstr_t nosetinstr;
+synth_hwcontrol_t nohwcontrol;
+synth_aftertouch_t noaftertouch;
+synth_panning_t nopanning;
+synth_controller_t nocontroller;
+synth_volumemethod_t novolumemethod;
+synth_bender_t nobender;
+synth_setupvoice_t nosetupvoice;
+synth_sendsysex_t nosendsysex;
+synth_allocvoice_t noallocvoice;
+synth_writeraw_t nowriteraw;
+synth_reset_t noreset;
+synth_shortname_t noshortname;
+synth_open_t noopen;
+synth_close_t noclose;
+synth_query_t noquery;
+synth_insync_t noinsync;
+synth_alloc_t noalloc;
+
+    int
+	nokillnote(void *_kobj, uint8_t _chn, uint8_t _note, uint8_t _vel)
+	{
+	    printf("nokillnote\n");
+	    return 0;
+	}
+
+    int
+	noopen(void *_kobj, void *_arg, int mode)
+	{
+	    printf("noopen\n");
+	    return 0;
+	}
+
+    int
+	noquery(void *_kboj)
+	{
+	    printf("noquery\n");
+	    return 0;
+	}
+
+    int
+	nostartnote(void *_kb, uint8_t _voice, uint8_t _note, uint8_t _parm)
+	{
+	    printf("nostartnote\n");
+	    return 0;
+	}
+
+    int
+	nosetinstr(void *_kb, uint8_t _chn, uint16_t _patchno)
+	{
+	    printf("nosetinstr\n");
+	    return 0;
+	}
+
+    int
+	nohwcontrol(void *_kb, uint8_t *_event)
+	{
+	    printf("nohwcontrol\n");
+	    return 0;
+	}
+
+    int 
+	noaftertouch ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2)
+	{
+	    printf("noaftertouch\n");
+	    return 0;
+	}
+
+    int
+	nopanning ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2)
+	{
+	    printf("nopanning\n");
+	    return 0;
+	}
+
+    int 
+	nocontroller ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2, uint16_t _x3)
+	{
+	    printf("nocontroller\n");
+	    return 0;
+	}
+
+    int 
+	novolumemethod (
+		void /* X */ * _kobj,
+		uint8_t _x1)
+	{
+	    printf("novolumemethod\n");
+	    return 0;
+	}
+
+    int 
+	nobender ( void /* X */ * _kobj, uint8_t _voice, uint16_t _bend)
+	{
+	    printf("nobender\n");
+	    return 0;
+	}
+
+    int 
+	nosetupvoice ( void /* X */ * _kobj, uint8_t _voice, uint8_t _chn)
+	{
+
+	    printf("nosetupvoice\n");
+	    return 0;
+	}
+
+    int 
+	nosendsysex ( void /* X */ * _kobj, void * _buf, size_t _len)
+	{
+	    printf("nosendsysex\n");
+	    return 0;
+	}
+
+    int 
+	noallocvoice ( void /* X */ * _kobj, uint8_t _chn, uint8_t _note, void *_x)
+	{
+	    printf("noallocvoice\n");
+	    return 0;
+	}
+
+    int 
+	nowriteraw ( void /* X */ * _kobjt, uint8_t * _buf, size_t _len)
+	{
+	    printf("nowriteraw\n");
+	    return 1;
+	}
+
+    int 
+	noreset ( void /* X */ * _kobjt)
+	{
+
+	    printf("noreset\n");
+	    return 0;
+	}
+
+    char *
+	noshortname (void /* X */ * _kobjt)
+	{
+	    printf("noshortname\n");
+	    return "noshortname";
+	}
+
+    int 
+	noclose ( void /* X */ * _kobjt)
+	{
+
+	    printf("noclose\n");
+	    return 0;
+	}
+
+    int
+	noinsync (void /* X */ * _kobjt)
+	{
+
+	    printf("noinsync\n");
+	    return 0;
+	}
+
+    int 
+	noalloc ( void /* x */ * _kbojt, uint8_t _chn, uint8_t _note)
+	{
+	    printf("noalloc\n");
+	    return 0;
+	}
+}
+
+METHOD int killnote {
+    void /* X */ * _kobj;
+    uint8_t _chan;
+    uint8_t _note;
+    uint8_t _vel;
+} DEFAULT nokillnote;
+
+METHOD int startnote {
+    void /* X */ * _kobj;
+    uint8_t _voice;
+    uint8_t _note;
+    uint8_t _parm;
+} DEFAULT nostartnote;
+
+METHOD int setinstr {
+    void /* X */ * _kobj;
+    uint8_t _chn;
+    uint16_t _patchno;
+} DEFAULT nosetinstr;
+
+METHOD int hwcontrol {
+    void /* X */ * _kobj;
+    uint8_t *_event;
+} DEFAULT nohwcontrol;
+
+METHOD int aftertouch {
+    void /* X */ * _kobj;
+    uint8_t _x1;
+    uint8_t _x2;
+} DEFAULT noaftertouch;
+
+METHOD int panning {
+    void /* X */ * _kobj;
+    uint8_t _x1;
+        uint8_t _x2;
+} DEFAULT nopanning;
+
+METHOD int controller {
+    void /* X */ * _kobj;
+    uint8_t _x1;
+    uint8_t _x2;
+    uint16_t _x3;
+} DEFAULT nocontroller;
+
+METHOD int volumemethod {
+    void /* X */ * _kobj;
+    uint8_t _x1;
+} DEFAULT novolumemethod;
+
+METHOD int bender {
+    void /* X */ * _kobj;
+    uint8_t _voice;
+    uint16_t _bend;
+} DEFAULT nobender;
+
+METHOD int setupvoice {
+    void /* X */ * _kobj;
+    uint8_t _voice;
+    uint8_t _chn;
+} DEFAULT nosetupvoice;
+
+METHOD int sendsysex {
+    void /* X */ * _kobj;
+    void * _buf;
+    size_t _len;
+} DEFAULT nosendsysex;
+
+METHOD int allocvoice {
+    void /* X */ * _kobj;
+    uint8_t _chn;
+    uint8_t _note;
+    void *_x;
+} DEFAULT noallocvoice;
+
+METHOD int writeraw {
+    void /* X */ * _kobjt;
+    uint8_t * _buf;
+    size_t _len;
+} DEFAULT nowriteraw;
+
+METHOD int reset {
+    void /* X */ * _kobjt;
+} DEFAULT noreset;
+
+METHOD char * shortname {
+    void /* X */ * _kobjt;
+} DEFAULT noshortname;
+
+METHOD int open {
+    void /* X */ * _kobjt;
+    void * _sythn;
+    int _mode;
+} DEFAULT noopen;
+
+METHOD int close {
+    void /* X */ * _kobjt;
+} DEFAULT noclose;
+
+METHOD int query {
+    void /* X */ * _kobjt;
+} DEFAULT noquery;
+
+METHOD int insync {
+    void /* X */ * _kobjt;
+} DEFAULT noinsync;
+
+METHOD int alloc {
+    void /* x */ * _kbojt;
+    uint8_t _chn;
+    uint8_t _note;
+} DEFAULT noalloc;
Index: sys/dev/sound/pci/cmi.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pci/cmi.c,v
retrieving revision 1.29.2.1
diff -u -r1.29.2.1 cmi.c
--- sys/dev/sound/pci/cmi.c	30 Jan 2005 01:00:03 -0000	1.29.2.1
+++ sys/dev/sound/pci/cmi.c	5 Mar 2005 16:27:07 -0000
@@ -48,8 +48,10 @@
 #include <dev/pci/pcivar.h>
 
 #include <sys/sysctl.h>
+#include <dev/sound/midi/mpu401.h>
 
 #include "mixer_if.h"
+#include "mpufoi_if.h"
 
 SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/cmi.c,v 1.29.2.1 2005/01/30 01:00:03 imp Exp $");
 
@@ -112,6 +114,13 @@
 	int			spdif_enabled;
 	unsigned int		bufsz;
 	struct sc_chinfo 	pch, rch;
+
+	struct mpu401	*mpu;
+	mpu401_intr_t		*mpu_intr;
+	struct resource *mpu_reg;
+	int mpu_regid;
+	bus_space_tag_t	mpu_bt;
+	bus_space_handle_t	mpu_bh;
 };
 
 /* Channel caps */
@@ -551,6 +560,9 @@
 
 		}
 	}
+	if(sc->mpu_intr) {
+		(sc->mpu_intr)(sc->mpu);
+	}
 	snd_mtxunlock(sc->lock);
 	return;
 }
@@ -747,6 +759,74 @@
 };
 MIXER_DECLARE(cmi_mixer);
 
+/*
+ * mpu401 functions
+ */
+
+static unsigned char
+cmi_mread(void *arg, struct sc_info *sc, int reg)
+{	
+	unsigned int d;
+
+		d = bus_space_read_1(0,0, 0x330 + reg); 
+	/*	printf("cmi_mread: reg %x %x\n",reg, d);
+	*/
+	return d;
+}
+
+static void
+cmi_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b)
+{
+
+	bus_space_write_1(0,0,0x330 + reg , b);
+}
+
+static int
+cmi_muninit(void *arg, struct sc_info *sc)
+{
+
+	snd_mtxlock(sc->lock);
+	sc->mpu_intr = 0;
+	sc->mpu = 0;
+	snd_mtxunlock(sc->lock);
+
+	return 0;
+}
+
+static kobj_method_t cmi_mpu_methods[] = {
+    	KOBJMETHOD(mpufoi_read,		cmi_mread),
+    	KOBJMETHOD(mpufoi_write,	cmi_mwrite),
+    	KOBJMETHOD(mpufoi_uninit,	cmi_muninit),
+	{ 0, 0 }
+};
+
+DEFINE_CLASS(cmi_mpu, cmi_mpu_methods, 0);
+
+static void
+cmi_midiattach(struct sc_info *sc) {
+/*
+	const struct {
+		int port,bits;
+	} *p, ports[] = { 
+		{0x330,0}, 
+		{0x320,1}, 
+		{0x310,2}, 
+		{0x300,3}, 
+		{0,0} } ;
+	Notes, CMPCI_REG_VMPUSEL sets the io port for the mpu.  Does
+	anyone know how to bus_space tag?
+*/
+	cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE);
+	cmi_clr4(sc, CMPCI_REG_LEGACY_CTRL, 
+			CMPCI_REG_VMPUSEL_MASK << CMPCI_REG_VMPUSEL_SHIFT);
+	cmi_set4(sc, CMPCI_REG_LEGACY_CTRL, 
+			0 << CMPCI_REG_VMPUSEL_SHIFT );
+	cmi_set4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE);
+	sc->mpu = mpu401_init(&cmi_mpu_class, sc, cmi_intr, &sc->mpu_intr);
+}
+
+
+
 /* ------------------------------------------------------------------------- */
 /* Power and reset */
 
@@ -802,6 +882,10 @@
 		 CMPCI_REG_TDMA_INTR_ENABLE);
 	cmi_clr4(sc, CMPCI_REG_FUNC_0,
 		 CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE);
+	cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE);
+
+	if( sc->mpu )
+		sc->mpu_intr = 0;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -859,6 +943,8 @@
 	sc->st = rman_get_bustag(sc->reg);
 	sc->sh = rman_get_bushandle(sc->reg);
 
+	cmi_midiattach(sc);
+
 	sc->irqid = 0;
 	sc->irq   = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
 					   RF_ACTIVE | RF_SHAREABLE);
@@ -938,7 +1024,12 @@
 	bus_dma_tag_destroy(sc->parent_dmat);
 	bus_teardown_intr(dev, sc->irq, sc->ih);
 	bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
+	if(sc->mpu)
+		mpu401_uninit(sc->mpu);
 	bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
+	if (sc->mpu_reg)
+	    bus_release_resource(dev, SYS_RES_IOPORT, sc->mpu_regid, sc->mpu_reg);
+
 	snd_mtxfree(sc->lock);
 	free(sc, M_DEVBUF);
 
@@ -1009,4 +1100,5 @@
 
 DRIVER_MODULE(snd_cmi, pci, cmi_driver, pcm_devclass, 0, 0);
 MODULE_DEPEND(snd_cmi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_DEPEND(snd_cmi, midi, 1,1,1);
 MODULE_VERSION(snd_cmi, 1);
Index: sys/dev/sound/pci/emu10k1.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pci/emu10k1.c,v
retrieving revision 1.52.2.2
diff -u -r1.52.2.2 emu10k1.c
--- sys/dev/sound/pci/emu10k1.c	30 Jan 2005 01:00:04 -0000	1.52.2.2
+++ sys/dev/sound/pci/emu10k1.c	5 Mar 2005 16:32:40 -0000
@@ -35,6 +35,10 @@
 #include <dev/pci/pcivar.h>
 #include <sys/queue.h>
 
+#include <dev/sound/midi/mpu401.h>
+#include "mpufoi_if.h"
+
+
 SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/emu10k1.c,v 1.52.2.2 2005/01/30 01:00:04 imp Exp $");
 
 /* -------------------------------------------------------------------- */
@@ -136,6 +140,9 @@
 	struct emu_voice voice[64];
 	struct sc_pchinfo pch[EMU_MAX_CHANS];
 	struct sc_rchinfo rch[3];
+	struct mpu401   *mpu;
+	mpu401_intr_t           *mpu_intr;
+	int mputx;
 };
 
 /* -------------------------------------------------------------------- */
@@ -1058,8 +1065,65 @@
 };
 CHANNEL_DECLARE(emurchan);
 
+static unsigned char
+emu_mread(void *arg, struct sc_info *sc, int reg)
+{	
+	unsigned int d;
+
+	d = emu_rd(sc, 0x18 + reg, 1); 
+	return d;
+}
+
+static void
+emu_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b)
+{
+
+	emu_wr(sc, 0x18 + reg, b, 1);
+}
+
+static int
+emu_muninit(void *arg, struct sc_info *sc)
+{
+
+	snd_mtxlock(sc->lock);
+	sc->mpu_intr = 0;
+	snd_mtxunlock(sc->lock);
+
+	return 0;
+}
+
+static kobj_method_t emu_mpu_methods[] = {
+    	KOBJMETHOD(mpufoi_read,		emu_mread),
+    	KOBJMETHOD(mpufoi_write,	emu_mwrite),
+    	KOBJMETHOD(mpufoi_uninit,	emu_muninit),
+	{ 0, 0 }
+};
+
+DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
+
+static void
+emu_intr2(void *p)
+{
+	struct sc_info *sc = (struct sc_info *)p;
+
+	if (sc->mpu_intr)
+	    (sc->mpu_intr)(sc->mpu);
+}
+
+static void
+emu_midiattach(struct sc_info *sc)
+{
+	int i;
+
+	i = emu_rd(sc, INTE, 4);
+	i |= INTE_MIDIRXENABLE;
+	emu_wr(sc, INTE, i, 4);
+
+	sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr);
+}
 /* -------------------------------------------------------------------- */
 /* The interrupt handler */
+
 static void
 emu_intr(void *data)
 {
@@ -1099,6 +1163,11 @@
 #endif
 		}
 
+	    if (stat & IPR_MIDIRECVBUFEMPTY)
+		if (sc->mpu_intr) {
+		    (sc->mpu_intr)(sc->mpu);
+		    ack |= IPR_MIDIRECVBUFEMPTY | IPR_MIDITRANSBUFEMPTY;
+ 		}
 		if (stat & ~ack)
 			device_printf(sc->dev, "dodgy irq: %x (harmless)\n",
 			    stat & ~ack);
@@ -1818,7 +1887,6 @@
 			}
 		}
 	}
-
 	return 0;
 }
 
@@ -1870,6 +1938,8 @@
 	emu_free(sc, sc->mem.ptb_pages);
 	emu_free(sc, sc->mem.silent_page);
 
+	if(sc->mpu)
+	    mpu401_uninit(sc->mpu);
 	return 0;
 }
 
@@ -1958,6 +2028,8 @@
 	gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0;
 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
 
+	emu_midiattach(sc);
+
 	i = 0;
 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
 	    RF_ACTIVE | RF_SHAREABLE);
@@ -2035,6 +2107,7 @@
 DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, 0, 0);
 MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
 MODULE_VERSION(snd_emu10k1, 1);
+MODULE_DEPEND(snd_emu10k1, midi, 1, 1, 1);
 
 /* dummy driver to silence the joystick device */
 static int
Index: sys/modules/sound/Makefile
===================================================================
RCS file: /home/ncvs/src/sys/modules/sound/Makefile,v
retrieving revision 1.2
diff -u -r1.2 Makefile
--- sys/modules/sound/Makefile	16 Jul 2004 03:58:46 -0000	1.2
+++ sys/modules/sound/Makefile	5 Mar 2005 16:27:07 -0000
@@ -3,5 +3,6 @@
 SUBDIR =
 SUBDIR += sound
 SUBDIR += driver
+SUBDIR += midi
 
 .include <bsd.subdir.mk>
Index: sys/modules/sound/driver/cmi/Makefile
===================================================================
RCS file: /home/ncvs/src/sys/modules/sound/driver/cmi/Makefile,v
retrieving revision 1.3
diff -u -r1.3 Makefile
--- sys/modules/sound/driver/cmi/Makefile	7 Feb 2003 13:56:31 -0000	1.3
+++ sys/modules/sound/driver/cmi/Makefile	5 Mar 2005 16:27:07 -0000
@@ -4,6 +4,7 @@
 
 KMOD=	snd_cmi
 SRCS=	device_if.h bus_if.h pci_if.h
+SRCS+= mpufoi_if.h
 SRCS+=	cmi.c
 
 .include <bsd.kmod.mk>
Index: sys/modules/sound/driver/emu10k1/Makefile
===================================================================
RCS file: /home/ncvs/src/sys/modules/sound/driver/emu10k1/Makefile,v
retrieving revision 1.4
diff -u -r1.4 Makefile
--- sys/modules/sound/driver/emu10k1/Makefile	11 Jan 2004 10:30:56 -0000	1.4
+++ sys/modules/sound/driver/emu10k1/Makefile	5 Mar 2005 16:27:07 -0000
@@ -5,6 +5,7 @@
 
 KMOD=	snd_emu10k1
 SRCS=	device_if.h bus_if.h pci_if.h emu10k1-alsa%diked.h
+SRCS+= mpufoi_if.h
 SRCS+=	emu10k1.c
 
 CLEANFILES+= emu10k1-alsa%diked.h
Index: sys/modules/sound/midi/Makefile
===================================================================
RCS file: sys/modules/sound/midi/Makefile
diff -N sys/modules/sound/midi/Makefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/modules/sound/midi/Makefile	5 Mar 2005 16:27:07 -0000
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../dev/sound/midi
+
+KMOD=	midi
+
+SRCS=	midi.c mpu401.c sequencer.c
+SRCS+=	device_if.h bus_if.h mpu_if.h mpufoi_if.h synth_if.h
+SRCS+=	mpu_if.c mpufoi_if.c synth_if.c
+
+EXPORT_SYMS=	YES	# XXX evaluate
+
+.include <bsd.kmod.mk>
help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050425125545.0dcbf4ec>