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 *)¶m, 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>
