summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/midi
diff options
context:
space:
mode:
authortanimura <tanimura@FreeBSD.org>2000-07-11 11:49:33 +0000
committertanimura <tanimura@FreeBSD.org>2000-07-11 11:49:33 +0000
commit7716c5370ad4c724650bb6def35750d561bc11d2 (patch)
tree9a29f2cea6e8dc8339d0261df8deae0201372989 /sys/dev/sound/midi
parent6cecb051f3990a02c86edb2060aa93c44e6d49da (diff)
downloadFreeBSD-src-7716c5370ad4c724650bb6def35750d561bc11d2.zip
FreeBSD-src-7716c5370ad4c724650bb6def35750d561bc11d2.tar.gz
Finally merge newmidi.
(I had been busy for my own research activity until the last weekend) Supported devices: SB Midi Port (sbc + midi) SB OPL3 (sbc + midi) 16550 UART (midi, needs a trick in your hint) CS461x Midi Port (csa + midi) OSS-compatible sequencer (seq) Supported playing software: playmidi (We definitely need more) Notes: /dev/midistat now reports installed midi drivers. /dev/sndstat reports only pcm drivers. We need the new name(pcmstat?). EMU8000(SB AWE) does not sound yet but does get probed so that the OPL3 synth on an AWE card works. TODO: MSS/PCI bridge drivers Midi-tty interface to support general serial devices Modules
Diffstat (limited to 'sys/dev/sound/midi')
-rw-r--r--sys/dev/sound/midi/midi.c702
-rw-r--r--sys/dev/sound/midi/midi.h294
-rw-r--r--sys/dev/sound/midi/midibuf.c424
-rw-r--r--sys/dev/sound/midi/midibuf.h64
-rw-r--r--sys/dev/sound/midi/midisynth.c632
-rw-r--r--sys/dev/sound/midi/midisynth.h97
-rw-r--r--sys/dev/sound/midi/miditypes.h34
-rw-r--r--sys/dev/sound/midi/sequencer.c2040
-rw-r--r--sys/dev/sound/midi/sequencer.h234
9 files changed, 4521 insertions, 0 deletions
diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c
new file mode 100644
index 0000000..83152cf
--- /dev/null
+++ b/sys/dev/sound/midi/midi.c
@@ -0,0 +1,702 @@
+/*
+ * Main midi driver for FreeBSD. This file provides the main
+ * entry points for probe/attach and all i/o demultiplexing, including
+ * default routines for generic devices.
+ *
+ * (C) 1999 Seigo Tanimura
+ *
+ * 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.
+ *
+ *
+ * For each card type a template "mididev_info" structure contains
+ * all the relevant parameters, both for configuration and runtime.
+ *
+ * In this file we build tables of pointers to the descriptors for
+ * the various supported cards. The generic probe routine scans
+ * the table(s) looking for a matching entry, then invokes the
+ * board-specific probe routine. If successful, a pointer to the
+ * correct mididev_info is stored in mididev_last_probed, for subsequent
+ * use in the attach routine. The generic attach routine copies
+ * the template to a permanent descriptor (midi_info[unit] and
+ * friends), initializes all generic parameters, and calls the
+ * board-specific attach routine.
+ *
+ * On device calls, the generic routines do the checks on unit and
+ * device parameters, then call the board-specific routines if
+ * available, or try to perform the task using the default code.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+
+static devclass_t midi_devclass;
+
+static d_open_t midiopen;
+static d_close_t midiclose;
+static d_ioctl_t midiioctl;
+static d_read_t midiread;
+static d_write_t midiwrite;
+static d_poll_t midipoll;
+
+/* These functions are local. */
+static d_open_t midistat_open;
+static d_close_t midistat_close;
+static d_read_t midistat_read;
+static int midi_initstatus(char *buf, int size);
+static int midi_readstatus(char *buf, int *ptr, struct uio *uio);
+
+#define CDEV_MAJOR MIDI_CDEV_MAJOR
+static struct cdevsw midi_cdevsw = {
+ /* open */ midiopen,
+ /* close */ midiclose,
+ /* read */ midiread,
+ /* write */ midiwrite,
+ /* ioctl */ midiioctl,
+ /* poll */ midipoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "midi",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+/*
+ * descriptors for active devices. also used as the public softc
+ * of a device.
+ */
+mididev_info midi_info[NMIDI_MAX];
+
+u_long nmidi; /* total number of midi devices, filled in by the driver */
+u_long nsynth; /* total number of synthesizers, filled in by the driver */
+
+/* These make the buffer for /dev/midistat */
+static int midistatbusy;
+static char midistatbuf[4096];
+static int midistatptr;
+
+/*
+ * This is the generic init routine
+ */
+int
+midiinit(mididev_info *d, device_t dev)
+{
+ int unit;
+
+ if (midi_devclass == NULL) {
+ midi_devclass = device_get_devclass(dev);
+ make_dev(&midi_cdevsw, MIDIMKMINOR(0, MIDI_DEV_STATUS),
+ UID_ROOT, GID_WHEEL, 0444, "midistat");
+ }
+
+ unit = device_get_unit(dev);
+ make_dev(&midi_cdevsw, MIDIMKMINOR(unit, MIDI_DEV_MIDIN),
+ UID_ROOT, GID_WHEEL, 0666, "midi%d", unit);
+
+ /*
+ * initialize standard parameters for the device. This can be
+ * overridden by device-specific configurations but better do
+ * here the generic things.
+ */
+
+ d->unit = device_get_unit(dev);
+ d->softc = device_get_softc(dev);
+ d->dev = dev;
+ d->magic = MAGIC(d->unit); /* debugging... */
+
+ return 0 ;
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated mididev_info struct, and sets the unit
+ * number.
+ */
+mididev_info *
+get_mididev_info(dev_t i_dev, int *unit)
+{
+ int u;
+ mididev_info *d = NULL;
+
+ if (MIDIDEV(i_dev) != MIDI_DEV_MIDIN)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u;
+
+ if (u >= nmidi + nsynth) {
+ DEB(printf("get_mididev_info: unit %d is not configured.\n", u));
+ return NULL;
+ }
+ d = &midi_info[u];
+
+ return d;
+}
+
+/*
+ * here are the switches for the main functions. The switches do
+ * all necessary checks on the device number to make sure
+ * that the device is configured. They also provide some default
+ * functionalities so that device-specific drivers have to deal
+ * only with special cases.
+ */
+
+static int
+midiopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_open(i_dev, flags, mode, p);
+ case MIDI_DEV_STATUS:
+ return midistat_open(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_close(i_dev, flags, mode, p);
+ case MIDI_DEV_STATUS:
+ return midistat_close(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiread(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_read(i_dev, buf, flag);
+ case MIDI_DEV_STATUS:
+ return midistat_read(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_write(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_ioctl(i_dev, cmd, arg, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midipoll(dev_t i_dev, int events, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_poll(i_dev, events, p);
+ }
+
+ return (ENXIO);
+}
+
+/*
+ * Followings are the generic methods in midi drivers.
+ */
+
+int
+midi_open(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit, s, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("open midi%d subdev %d flags 0x%08x mode 0x%08x\n",
+ unit, dev & 0xf, flags, mode));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ s = splmidi();
+
+ /* Mark this device busy. */
+ device_busy(d->dev);
+ if ((d->flags & MIDI_F_BUSY) != 0) {
+ splx(s);
+ DEB(printf("opl_open: unit %d is busy.\n", unit));
+ return (EBUSY);
+ }
+ d->flags |= MIDI_F_BUSY;
+ d->flags &= ~(MIDI_F_READING | MIDI_F_WRITING);
+ d->fflags = flags;
+
+ /* Init the queue. */
+ if ((d->fflags & FREAD) != 0)
+ midibuf_init(&d->midi_dbuf_in);
+ if ((d->fflags & FWRITE) != 0) {
+ midibuf_init(&d->midi_dbuf_out);
+ midibuf_init(&d->midi_dbuf_passthru);
+ }
+
+ if (d->open == NULL)
+ ret = 0;
+ else
+ ret = d->open(i_dev, flags, mode, p);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_close(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit, s, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("close midi%d subdev %d\n", unit, dev & 0xf));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ s = splmidi();
+
+ /* Clear the queues. */
+ if ((d->fflags & FREAD) != 0)
+ midibuf_init(&d->midi_dbuf_in);
+ if ((d->fflags & FWRITE) != 0) {
+ midibuf_init(&d->midi_dbuf_out);
+ midibuf_init(&d->midi_dbuf_passthru);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ d->flags &= ~MIDI_F_BUSY;
+ d->fflags = 0;
+
+ device_unbusy(d->dev);
+
+ if (d->close == NULL)
+ ret = 0;
+ else
+ ret = d->close(i_dev, flags, mode, p);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_read(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, s, len, ret;
+ mididev_info *d ;
+
+ dev = minor(i_dev);
+
+ d = get_mididev_info(i_dev, &unit);
+ DEB(printf("read midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Begin recording. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_RD);
+
+ /* Have we got the data to read? */
+ if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_in.rl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ ret = midibuf_uioread(&d->midi_dbuf_in, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+
+ if (ret == 0 && d->read != NULL)
+ ret = d->read(i_dev, buf, flag);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_write(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, s, len, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("write midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Begin playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ /* Have we got the data to write? */
+ if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_out.fl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ if (len > d->midi_dbuf_out.fl &&
+ (d->flags & MIDI_F_NBIO))
+ len = d->midi_dbuf_out.fl;
+ ret = midibuf_uiowrite(&d->midi_dbuf_out, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+
+ /* Begin playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ if (ret == 0 && d->write != NULL)
+ ret = d->write(i_dev, buf, flag);
+
+ splx(s);
+
+ return (ret);
+}
+
+/*
+ * generic midi ioctl. Functions of the default driver can be
+ * overridden by the device-specific ioctl call.
+ * If a device-specific call returns ENOSYS (Function not implemented),
+ * the default driver is called. Otherwise, the returned value
+ * is passed up.
+ *
+ * The default handler, for many parameters, sets the value in the
+ * descriptor, sets MIDI_F_INIT, and calls the callback function with
+ * reason INIT. If successful, the callback returns 1 and the caller
+ * can update the parameter.
+ */
+
+int
+midi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int ret = ENOSYS, dev, unit;
+ mididev_info *d;
+ struct snd_size *sndsize;
+ u_long s;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ if (d == NULL)
+ return (ENXIO);
+
+ if (d->ioctl)
+ ret = d->ioctl(i_dev, cmd, arg, mode, p);
+ if (ret != ENOSYS)
+ return ret;
+
+ /*
+ * pass control to the default ioctl handler. Set ret to 0 now.
+ */
+ ret = 0;
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ s = splmidi();
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+ *(int *)arg = d->midi_dbuf_out.fl;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ if (sndsize->play_size <= d->midi_dbuf_out.unit_size && sndsize->rec_size <= d->midi_dbuf_in.unit_size) {
+ d->flags &= ~MIDI_F_HAS_SIZE;
+ d->midi_dbuf_out.blocksize = d->midi_dbuf_out.unit_size;
+ d->midi_dbuf_in.blocksize = d->midi_dbuf_in.unit_size;
+ }
+ else {
+ if (sndsize->play_size > d->midi_dbuf_out.bufsize / 4)
+ sndsize->play_size = d->midi_dbuf_out.bufsize / 4;
+ if (sndsize->rec_size > d->midi_dbuf_in.bufsize / 4)
+ sndsize->rec_size = d->midi_dbuf_in.bufsize / 4;
+ /* Round up the size to the multiple of EV_SZ. */
+ d->midi_dbuf_out.blocksize =
+ ((sndsize->play_size + d->midi_dbuf_out.unit_size - 1)
+ / d->midi_dbuf_out.unit_size) * d->midi_dbuf_out.unit_size;
+ d->midi_dbuf_in.blocksize =
+ ((sndsize->rec_size + d->midi_dbuf_in.unit_size - 1)
+ / d->midi_dbuf_in.unit_size) * d->midi_dbuf_in.unit_size;
+ d->flags |= MIDI_F_HAS_SIZE;
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ sndsize->play_size = d->midi_dbuf_out.blocksize;
+ sndsize->rec_size = d->midi_dbuf_in.blocksize;
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) /* play */
+ *(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_WR);
+ else if (*(int *)arg == AIOSYNC_CAPTURE)
+ *(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_RD);
+ else {
+ splx(s);
+ DEB(printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg));
+ *(int *)arg = 0 ;
+ }
+ break ;
+
+ case AIOSYNC:
+ DEB(printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
+ ((snd_sync_parm *)arg)->chan,
+ ((snd_sync_parm *)arg)->pos));
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+ *(int *)arg = d->midi_dbuf_in.rl;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ DEB( printf("FIOASYNC\n") ; )
+ break;
+
+ case FIONBIO: /* set/clear non-blocking i/o */
+ if ( *(int *)arg == 0 )
+ d->flags &= ~MIDI_F_NBIO ;
+ else
+ d->flags |= MIDI_F_NBIO ;
+ break ;
+
+ case MIOSPASSTHRU: /* set/clear passthru */
+ if ( *(int *)arg == 0 )
+ d->flags &= ~MIDI_F_PASSTHRU ;
+ else
+ d->flags |= MIDI_F_PASSTHRU ;
+
+ /* Init the queue. */
+ midibuf_init(&d->midi_dbuf_passthru);
+
+ /* FALLTHROUGH */
+ case MIOGPASSTHRU: /* get passthru */
+ if ((d->flags & MIDI_F_PASSTHRU) != 0)
+ (int *)arg = 1;
+ else
+ (int *)arg = 0;
+ break ;
+
+ default:
+ DEB(printf("default ioctl midi%d subdev %d fn 0x%08x fail\n",
+ unit, dev & 0xf, cmd));
+ ret = EINVAL;
+ break ;
+ }
+ splx(s);
+ return ret ;
+}
+
+int
+midi_poll(dev_t i_dev, int events, struct proc * p)
+{
+ int unit, dev, ret, s, lim;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ if (d == NULL)
+ return (ENXIO);
+
+ if (d->poll)
+ ret = d->poll(i_dev, events, p);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Look up the apropriate queue and select it. */
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ /* Start playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ /* Find out the boundary. */
+ if ((d->flags & MIDI_F_HAS_SIZE) != 0)
+ lim = d->midi_dbuf_out.blocksize;
+ else
+ lim = d->midi_dbuf_out.unit_size;
+ if (d->midi_dbuf_out.fl < lim)
+ /* No enough space, record select. */
+ selrecord(p, &d->midi_dbuf_out.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLOUT | POLLWRNORM);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ /* Start recording. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_RD);
+
+ /* Find out the boundary. */
+ if ((d->flags & MIDI_F_HAS_SIZE) != 0)
+ lim = d->midi_dbuf_in.blocksize;
+ else
+ lim = d->midi_dbuf_in.unit_size;
+ if (d->midi_dbuf_in.rl < lim)
+ /* No data ready, record select. */
+ selrecord(p, &d->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+ splx(s);
+
+ return (ret);
+}
+
+void
+midi_intr(mididev_info *d)
+{
+ if (d->intr != NULL)
+ d->intr(d->intrarg, d);
+}
+
+/*
+ * These handle the status message of the midi drivers.
+ */
+
+int
+midistat_open(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ if (midistatbusy)
+ return (EBUSY);
+
+ bzero(midistatbuf, sizeof(midistatbuf));
+ midistatptr = 0;
+ if (midi_initstatus(midistatbuf, sizeof(midistatbuf) - 1))
+ return (ENOMEM);
+
+ midistatbusy = 1;
+
+ return (0);
+}
+
+int
+midistat_close(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ midistatbusy = 0;
+
+ return (0);
+}
+
+int
+midistat_read(dev_t i_dev, struct uio * buf, int flag)
+{
+ return midi_readstatus(midistatbuf, &midistatptr, buf);
+}
+
+/*
+ * finally, some "libraries"
+ */
+
+/* Inits the buffer for /dev/midistat. */
+static int
+midi_initstatus(char *buf, int size)
+{
+ int i, p;
+ device_t dev;
+ mididev_info *md;
+
+ p = 0;
+ p += snprintf(buf, size, "FreeBSD Midi Driver (newmidi) %s %s\nInstalled devices:\n", __DATE__, __TIME__);
+ for (i = 0 ; i < NMIDI_MAX ; i++) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+ dev = devclass_get_device(midi_devclass, i);
+ if (p < size)
+ p += snprintf(&buf[p], size - p, "midi%d: <%s> %s\n", i, device_get_desc(dev), md->midistat);
+ else
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Reads the status message. */
+static int
+midi_readstatus(char *buf, int *ptr, struct uio *uio)
+{
+ int s, len;
+
+ s = splmidi();
+ len = min(uio->uio_resid, strlen(&buf[*ptr]));
+ if (len > 0) {
+ uiomove(&buf[*ptr], len, uio);
+ *ptr += len;
+ }
+ splx(s);
+
+ return (0);
+}
diff --git a/sys/dev/sound/midi/midi.h b/sys/dev/sound/midi/midi.h
new file mode 100644
index 0000000..2e36ed0
--- /dev/null
+++ b/sys/dev/sound/midi/midi.h
@@ -0,0 +1,294 @@
+/*
+ * Include file for midi driver.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _MIDI_H_
+#define _MIDI_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.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/clock.h> /* for DELAY */
+#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 <dev/sound/midi/miditypes.h>
+#include <dev/sound/midi/midibuf.h>
+#include <dev/sound/midi/midisynth.h>
+
+#define MIDI_CDEV_MAJOR 30
+
+/*
+ * descriptor of midi operations ...
+ *
+ */
+
+struct _mididev_info {
+
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[64];
+
+ int type;
+
+ d_open_t *open;
+ d_close_t *close;
+ d_read_t *read;
+ d_write_t *write;
+ d_ioctl_t *ioctl;
+ d_poll_t *poll;
+ midi_callback_t *callback;
+
+ /*
+ * combinations of the following flags are used as second argument in
+ * the callback from the dma module to the device-specific routines.
+ */
+
+#define MIDI_CB_RD 0x100 /* read callback */
+#define MIDI_CB_WR 0x200 /* write callback */
+#define MIDI_CB_REASON_MASK 0xff
+#define MIDI_CB_START 0x01 /* start dma op */
+#define MIDI_CB_STOP 0x03 /* stop dma op */
+#define MIDI_CB_ABORT 0x04 /* abort dma op */
+#define MIDI_CB_INIT 0x05 /* init board parameters */
+
+ /*
+ * callback extensions
+ */
+#define MIDI_CB_DMADONE 0x10
+#define MIDI_CB_DMAUPDATE 0x11
+#define MIDI_CB_DMASTOP 0x12
+
+ /* init can only be called with int enabled and
+ * no pending DMA activity.
+ */
+
+ /*
+ * whereas from here, parameters are set at runtime.
+ * resources are stored in the softc of the device,
+ * not in the common structure.
+ */
+
+ int unit; /* unit number of the device */
+ void *softc; /* softc for the device */
+ device_t dev; /* device_t for the device */
+
+ int bd_id ; /* used to hold board-id info, eg. sb version,
+ * mss codec type, etc. etc.
+ */
+
+ midi_dbuf midi_dbuf_in; /* midi input event/message queue */
+ midi_dbuf midi_dbuf_out; /* midi output event/message queue */
+ midi_dbuf midi_dbuf_passthru; /* midi passthru event/message queue */
+
+ /*
+ * these parameters describe the operation of the board.
+ * Generic things like busy flag, speed, etc are here.
+ */
+
+ volatile u_long flags ; /* 32 bits, used for various purposes. */
+ int fflags; /* file flag */
+
+ /*
+ * we have separate flags for read and write, although in some
+ * cases this is probably not necessary (e.g. because we cannot
+ * know how many processes are using the device, we cannot
+ * distinguish if open, close, abort are for a write or for a
+ * read).
+ */
+
+ /*
+ * the following flag is used by open-close routines
+ * to mark the status of the device.
+ */
+#define MIDI_F_BUSY 0x0001 /* has been opened */
+ /*
+ * the next two are used to allow only one pending operation of
+ * each type.
+ */
+#define MIDI_F_READING 0x0004 /* have a pending read */
+#define MIDI_F_WRITING 0x0008 /* have a pending write */
+
+ /*
+ * flag used to mark a pending close.
+ */
+#define MIDI_F_CLOSING 0x0040 /* a pending close */
+
+ /*
+ * if user has not set block size, then make it adaptive
+ * (0.25s, or the perhaps last read/write ?)
+ */
+#define MIDI_F_HAS_SIZE 0x0080 /* user set block size */
+ /*
+ * assorted flags related to operating mode.
+ */
+#define MIDI_F_STEREO 0x0100 /* doing stereo */
+#define MIDI_F_NBIO 0x0200 /* do non-blocking i/o */
+#define MIDI_F_PASSTHRU 0x0400 /* pass received data to output port */
+
+ /*
+ * these flags mark a pending abort on a r/w operation.
+ */
+#define MIDI_F_ABORTING 0x1000 /* a pending abort */
+
+ /*
+ * this is used to mark that board initialization is needed, e.g.
+ * because of a change in sampling rate, format, etc. -- It will
+ * be done at the next convenient time.
+ */
+#define MIDI_F_INIT 0x4000 /* changed parameters. need init */
+
+ int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
+
+#define mwsel midi_dbuf_out.sel
+#define mrsel midi_dbuf_in.sel
+ u_long interrupts; /* counter of interrupts */
+ u_long magic;
+#define MAGIC(unit) ( 0xa4d10de0 + unit )
+ void *device_data ; /* just in case it is needed...*/
+
+ midi_intr_t *intr; /* interrupt handler of the upper layer (ie sequencer) */
+ void *intrarg; /* argument to interrupt handler */
+
+ /* The following is the interface from a midi sequencer to a midi device. */
+ synthdev_info synth;
+
+ /* This is the status message to display via /dev/midistat */
+ char midistat[128];
+} ;
+
+/*
+ * then ioctls and other stuff
+ */
+
+#define NMIDI_MAX 64 /* Number of supported devices */
+
+/*
+ * 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))
+
+/*
+ * convert dev_t to unit and dev
+ */
+#define MIDIMINOR(x) (minor(x))
+#define MIDIUNIT(x) ((MIDIMINOR(x) & 0x000000f0) >> 4)
+#define MIDIDEV(x) (MIDIMINOR(x) & 0x0000000f)
+#define MIDIMKMINOR(u, d) (((u) & 0x0f) << 4 | ((d) & 0x0f))
+#define MIDIMKDEV(m, u, d) (makedev((m), MIDIMKMINOR((u), (d))))
+
+/*
+ * see if the device is configured
+ */
+#define MIDICONFED(x) ((x)->ioctl != NULL)
+
+/*
+ * finally, all default parameters
+ */
+#define MIDI_BUFFSIZE (4 * 1024) /* XXX */
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+ extern mididev_info midi_info[NMIDI_MAX];
+
+ extern u_long nmidi;
+ extern u_long nsynth;
+
+/* This is the generic midi drvier initializer. */
+ int midiinit(mididev_info *d, device_t dev);
+
+/* This provides an access to the mididev_info. */
+ mididev_info *get_mididev_info(dev_t i_dev, int *unit);
+
+/* These are the generic methods for a midi driver. */
+ d_open_t midi_open;
+ d_close_t midi_close;
+ d_ioctl_t midi_ioctl;
+ d_read_t midi_read;
+ d_write_t midi_write;
+ d_poll_t midi_poll;
+
+/* Common interrupt handler */
+void midi_intr(mididev_info *);
+
+/*
+ * library functions (in midi.c)
+ */
+#define splmidi() spltty()
+
+/*
+ * Minor numbers for the midi driver.
+ */
+
+#define MIDI_DEV_MIDIN 2 /* Raw midi access */
+#define MIDI_DEV_STATUS 11 /* /dev/midistat */
+
+#endif /* _MIDI_H_ */
diff --git a/sys/dev/sound/midi/midibuf.c b/sys/dev/sound/midi/midibuf.c
new file mode 100644
index 0000000..4e23800
--- /dev/null
+++ b/sys/dev/sound/midi/midibuf.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 1999 Seigo Tanimura
+ * All rights reserved.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * This file implements a midi event/message queue. A midi
+ * event/message queue holds midi events and messages to
+ * transmit to or received from a midi interface.
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+
+/* Some macros to handle the queue. */
+#define DATA_AVAIL(dbuf) ((dbuf)->rl)
+#define SPACE_AVAIL(dbuf) ((dbuf)->fl)
+
+static void queuerawdata(midi_dbuf *dbuf, char *data, int len);
+static void queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
+static void dequeuerawdata(midi_dbuf *dbuf, char *data, int len);
+static void copyrawdata(midi_dbuf *dbuf, char *data, int len);
+static void dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
+
+/*
+ * Here are the functions to interact to the midi device drivers.
+ * These are called from midi device driver functions under sys/i386/isa/snd.
+ */
+
+int
+midibuf_init(midi_dbuf *dbuf)
+{
+ if (dbuf->buf != NULL)
+ free(dbuf->buf, M_DEVBUF);
+ dbuf->buf = malloc(MIDI_BUFFSIZE, M_DEVBUF, M_NOWAIT);
+ bzero(dbuf->buf, MIDI_BUFFSIZE);
+ dbuf->bufsize = MIDI_BUFFSIZE;
+ dbuf->rp = dbuf->fp = 0;
+ dbuf->dl = 0;
+ dbuf->rl = 0;
+ dbuf->fl = dbuf->bufsize;
+ dbuf->int_count = 0;
+ dbuf->chan = 0;
+ /*dbuf->unit_size = 1;*/ /* The drivers are responsible. */
+ bzero(&dbuf->sel, sizeof(dbuf->sel));
+ dbuf->total = 0;
+ dbuf->prev_total = 0;
+ dbuf->blocksize = dbuf->bufsize / 4;
+
+ return (0);
+}
+
+/* The sequencer calls this function to queue data. */
+int
+midibuf_seqwrite(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lwrt, lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Find out the number of bytes to write. */
+ lwrt = SPACE_AVAIL(dbuf);
+ if (lwrt > len)
+ lwrt = len;
+ if (lwrt > 0) {
+ /* We can write some now. Queue the data. */
+ queuerawdata(dbuf, data, lwrt);
+
+ lwritten += lwrt;
+ len -= lwrt;
+ data += lwrt;
+ }
+
+ /* Have we got still more data to write? */
+ if (len > 0) {
+ /* Yes, sleep until we have enough space. */
+ i = tsleep((void *)&dbuf->tsleep_out, PRIBIO | PCATCH, "mbsqwt", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ }
+ }
+
+ return (lwritten);
+}
+
+/* sndwrite calls this function to queue data. */
+int
+midibuf_uiowrite(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ int i, lwrt, lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Find out the number of bytes to write. */
+ lwrt = SPACE_AVAIL(dbuf);
+ if (lwrt > len)
+ lwrt = len;
+ if (lwrt > 0) {
+ /* We can write some now. Queue the data. */
+ queueuiodata(dbuf, buf, lwrt);
+
+ lwritten += lwrt;
+ len -= lwrt;
+ }
+
+ /* Have we got still more data to write? */
+ if (len > 0) {
+ /* Yes, sleep until we have enough space. */
+ i = tsleep(&dbuf->tsleep_out, PRIBIO | PCATCH, "mbuiwt", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ }
+ }
+
+ return (lwritten);
+}
+
+int
+midibuf_output_intr(midi_dbuf *dbuf, u_char *data, int len)
+{
+ int lrd;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ /* Have we got any data in the queue? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0)
+ return (0);
+
+ /* Dequeue the data. */
+ if (lrd > len)
+ lrd = len;
+ dequeuerawdata(dbuf, data, lrd);
+
+ return (lrd);
+}
+
+int
+midibuf_input_intr(midi_dbuf *dbuf, u_char *data, int len)
+{
+ int lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+
+ /* Have we got any data to write? */
+ if (len == 0)
+ return (0);
+ /* Can we write now? */
+ if (SPACE_AVAIL(dbuf) < len)
+ return (-EAGAIN);
+
+ /* We can write some now. Queue the data. */
+ queuerawdata(dbuf, data, len);
+ lwritten = len;
+
+ /* Have we managed to write the whole data? */
+ if (lwritten < len)
+ printf("midibuf_input_intr: queue did not have enough space, discarded %d bytes out of %d bytes.\n", len - lwritten, len);
+
+ return (lwritten);
+}
+
+/* The sequencer calls this function to dequeue data. */
+int
+midibuf_seqread(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbsqrd", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Dequeue the data. */
+ dequeuerawdata(dbuf, data, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ data += lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/* The sequencer calls this function to copy data without dequeueing. */
+int
+midibuf_seqcopy(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbsqrd", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Copy the data. */
+ copyrawdata(dbuf, data, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ data += lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/* sndread calls this function to dequeue data. */
+int
+midibuf_uioread(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ while (len > 0 && lread == 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbuird", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Dequeue the data. */
+ dequeueuiodata(dbuf, buf, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static void
+queuerawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* dbuf->fp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->fp < len) {
+ /* The new data wraps, copy them twice. */
+ memcpy(dbuf->buf + dbuf->fp, data, dbuf->bufsize - dbuf->fp);
+ memcpy(dbuf->buf, data + dbuf->bufsize - dbuf->fp, len - (dbuf->bufsize - dbuf->fp));
+ } else
+ /* The new data do not wrap, once is enough. */
+ memcpy(dbuf->buf + dbuf->fp, data, len);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
+ dbuf->fl -= len;
+ dbuf->rl += len;
+
+ /* Wake up the processes sleeping on input data. */
+ wakeup(&dbuf->tsleep_in);
+ if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ /* dbuf->fp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->fp < len) {
+ /* The new data wraps, copy them twice. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->fp), dbuf->bufsize - dbuf->fp, buf);
+ uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->fp), buf);
+ } else
+ /* The new data do not wrap, once is enough. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->fp), len, buf);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
+ dbuf->fl -= len;
+ dbuf->rl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_in);
+ if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+dequeuerawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* Copy the data. */
+ copyrawdata(dbuf, data, len);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
+ dbuf->rl -= len;
+ dbuf->fl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_out);
+ if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+copyrawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* dbuf->rp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->rp < len) {
+ /* The data to be read wraps, copy them twice. */
+ memcpy(data, dbuf->buf + dbuf->rp, dbuf->bufsize - dbuf->rp);
+ memcpy(data + dbuf->bufsize - dbuf->rp, dbuf->buf, len - (dbuf->bufsize - dbuf->rp));
+ } else
+ /* The new data do not wrap, once is enough. */
+ memcpy(data, dbuf->buf + dbuf->rp, len);
+}
+
+static void
+dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ /* dbuf->rp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->rp < len) {
+ /* The new data wraps, copy them twice. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->rp), dbuf->bufsize - dbuf->rp, buf);
+ uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->rp), buf);
+ } else
+ /* The new data do not wrap, once is enough. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->rp), len, buf);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
+ dbuf->rl -= len;
+ dbuf->fl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_out);
+ if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
diff --git a/sys/dev/sound/midi/midibuf.h b/sys/dev/sound/midi/midibuf.h
new file mode 100644
index 0000000..d40019d
--- /dev/null
+++ b/sys/dev/sound/midi/midibuf.h
@@ -0,0 +1,64 @@
+/*
+ * Include file for midi buffer.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * descriptor of a midi buffer. See midibuf.c for documentation.
+ * (rp,rl) and (fp,fl) identify the READY and FREE regions of the
+ * buffer. dl contains the length used for dma transfer, dl>0 also
+ * means that the channel is busy and there is a DMA transfer in progress.
+ */
+
+typedef struct _midi_dbuf {
+ char *buf;
+ int bufsize ;
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* length of ready and free areas. */
+ int int_count;
+ int chan; /* dma channel */
+ int unit_size ; /* unit size */
+ struct selinfo sel;
+ u_long total; /* total bytes processed */
+ u_long prev_total; /* copy of the above when GETxPTR called */
+ int tsleep_in, tsleep_out; /* pillows to tsleep on */
+ int blocksize; /* block size */
+} midi_dbuf ;
+
+/*
+ * These are the midi buffer methods, used in midi interface devices.
+ */
+int midibuf_init(midi_dbuf *dbuf);
+int midibuf_seqwrite(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_uiowrite(midi_dbuf *dbuf, struct uio *buf, int len);
+int midibuf_output_intr(midi_dbuf *dbuf, u_char *data, int len);
+int midibuf_input_intr(midi_dbuf *dbuf, u_char *data, int len);
+int midibuf_seqread(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_seqcopy(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_uioread(midi_dbuf *dbuf, struct uio *buf, int len);
diff --git a/sys/dev/sound/midi/midisynth.c b/sys/dev/sound/midi/midisynth.c
new file mode 100644
index 0000000..7d668fd
--- /dev/null
+++ b/sys/dev/sound/midi/midisynth.c
@@ -0,0 +1,632 @@
+/*
+ * 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$
+ *
+ */
+
+/*
+ * This is the interface for a sequencer to interact a midi driver.
+ * This interface translates the sequencer operations to the corresponding
+ * midi messages, and vice versa.
+ */
+
+#include "opt_devfs.h"
+
+#include <stddef.h>
+#include <dev/sound/midi/midi.h>
+
+#define TYPEDRANGE(type, x, lower, upper) \
+{ \
+ type tl, tu; \
+ tl = (lower); \
+ tu = (upper); \
+ if (x < tl) { \
+ x = tl; \
+ } else if(x > tu) { \
+ x = tu; \
+ } \
+}
+
+/*
+ * These functions goes into midisynthdev_op_desc.
+ */
+static mdsy_killnote_t synth_killnote;
+static mdsy_setinstr_t synth_setinstr;
+static mdsy_startnote_t synth_startnote;
+static mdsy_reset_t synth_reset;
+static mdsy_hwcontrol_t synth_hwcontrol;
+static mdsy_loadpatch_t synth_loadpatch;
+static mdsy_panning_t synth_panning;
+static mdsy_aftertouch_t synth_aftertouch;
+static mdsy_controller_t synth_controller;
+static mdsy_patchmgr_t synth_patchmgr;
+static mdsy_bender_t synth_bender;
+static mdsy_allocvoice_t synth_allocvoice;
+static mdsy_setupvoice_t synth_setupvoice;
+static mdsy_sendsysex_t synth_sendsysex;
+static mdsy_prefixcmd_t synth_prefixcmd;
+static mdsy_volumemethod_t synth_volumemethod;
+static mdsy_readraw_t synth_readraw;
+static mdsy_writeraw_t synth_writeraw;
+
+/*
+ * This is the synthdev_info for a midi interface device.
+ * You may have to replace a few of functions for an internal
+ * synthesizer.
+ */
+synthdev_info midisynth_op_desc = {
+ synth_killnote,
+ synth_setinstr,
+ synth_startnote,
+ synth_reset,
+ synth_hwcontrol,
+ synth_loadpatch,
+ synth_panning,
+ synth_aftertouch,
+ synth_controller,
+ synth_patchmgr,
+ synth_bender,
+ synth_allocvoice,
+ synth_setupvoice,
+ synth_sendsysex,
+ synth_prefixcmd,
+ synth_volumemethod,
+ synth_readraw,
+ synth_writeraw,
+};
+
+/* The following functions are local. */
+static int synth_leavesysex(mididev_info *md);
+
+/*
+ * Here are the main functions to interact to the midi sequencer.
+ * These are called from the sequencer functions in sys/i386/isa/snd/sequencer.c.
+ */
+
+static int
+synth_killnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && ((msg == 0x90 && vel == 64) || msg == 0x80)) {
+ /* Use running status. */
+ c[0] = (u_char)note;
+ if (msg == 0x90)
+ /* The note was on. */
+ c[1] = 0;
+ else
+ c[1] = (u_char)vel;
+
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ 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;
+ }
+
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return EAGAIN;
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_setinstr(mididev_info *md, int chn, int instr)
+{
+ int unit;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (instr < 0 || instr > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */
+ c[1] = (u_char)instr;
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+
+ return (0);
+}
+
+static int
+synth_startnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0x90) {
+ /* Use running status. */
+ c[0] = (u_char)note;
+ c[1] = (u_char)vel;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+ c[1] = (u_char)note;
+ c[2] = (u_char)vel;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_reset(mididev_info *md)
+{
+ synth_leavesysex(md);
+ return (0);
+}
+
+static int
+synth_hwcontrol(mididev_info *md, u_char *event)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_loadpatch(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag)
+{
+ struct sysex_info sysex;
+ synthdev_info *sd;
+ int unit, i, eox_seen, first_byte, left, src_offs, hdr_size;
+ u_char c[count];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ eox_seen = 0;
+ first_byte = 1;
+ hdr_size = offsetof(struct sysex_info, data);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ if (synth_prefixcmd(md, 0xf0))
+ return (0);
+ if (format != SYSEX_PATCH) {
+ printf("synth_loadpatch: patch format 0x%x is invalid.\n", format);
+ return (EINVAL);
+ }
+ if (count < hdr_size) {
+ printf("synth_loadpatch: patch header is too short.\n");
+ return (EINVAL);
+ }
+ count -= hdr_size;
+
+ /* Copy the patch data. */
+ if (uiomove((caddr_t)&((char *)&sysex)[offs], hdr_size - offs, buf))
+ printf("synth_loadpatch: memory mangled?\n");
+
+ if (count < sysex.len) {
+ sysex.len = (long)count;
+ printf("synth_loadpatch: sysex record of %d bytes is too long, adjusted to %d bytes.\n", (int)sysex.len, count);
+ }
+ left = sysex.len;
+ src_offs = 0;
+
+ for (i = 0 ; i < left ; i++) {
+ uiomove((caddr_t)&c[i], 1, buf);
+ eox_seen = i > 0 && (c[i] & 0x80) != 0;
+ if (eox_seen && c[i] != 0xf7)
+ c[i] = 0xf7;
+ if (i == 0 && c[i] != 0x80) {
+ printf("synth_loadpatch: sysex does not begin with the status.\n");
+ return (EINVAL);
+ }
+ if (!first_byte && (c[i] & 0x80) != 0) {
+ md->synth.writeraw(md, c, i + 1, 0);
+ /* Update the status. */
+ sd->prev_out_status = c[i];
+ return (0);
+ }
+ first_byte = 0;
+ }
+
+ if (!eox_seen) {
+ c[0] = 0xf7;
+ md->synth.writeraw(md, c, 1, 0);
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_panning(mididev_info *md, int chn, int pan)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_aftertouch(mididev_info *md, int chn, int press)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (press < 0 || press > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xd0) {
+ /* Use running status. */
+ c[0] = (u_char)press;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 1, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xd0 | (chn & 0x0f); /* Channel Pressure. */
+ c[1] = (u_char)press;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_controller(mididev_info *md, int chn, int ctrlnum, int val)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (ctrlnum < 1 || ctrlnum > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xb0) {
+ /* Use running status. */
+ c[0] = (u_char)ctrlnum;
+ c[1] = (u_char)val & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
+ c[1] = (u_char)ctrlnum;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_patchmgr(mididev_info *md, struct patmgr_info *rec)
+{
+ return (EINVAL);
+}
+
+static int
+synth_bender(mididev_info *md, int chn, int val)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (val < 0 || val > 16383 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xe0) {
+ /* Use running status. */
+ c[0] = (u_char)val & 0x7f;
+ c[1] = (u_char)(val >> 7) & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */
+ c[1] = (u_char)val & 0x7f;
+ c[2] = (u_char)(val >> 7) & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_allocvoice(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_setupvoice(mididev_info *md, int voice, int chn)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_sendsysex(mididev_info *md, u_char *sysex, int len)
+{
+ int unit, i, j;
+ synthdev_info *sd;
+ u_char c[len];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ for (i = 0 ; i < len ; i++) {
+ switch (sysex[i]) {
+ case 0xf0:
+ /* Sysex begins. */
+ if (synth_prefixcmd(md, 0xf0))
+ return (0);
+ sd->sysex_state = 1;
+ break;
+ case 0xf7:
+ /* Sysex ends. */
+ if (!sd->sysex_state)
+ return (0);
+ sd->sysex_state = 0;
+ break;
+ default:
+ if (!sd->sysex_state)
+ return (0);
+ if ((sysex[i] & 0x80) != 0) {
+ /* A status in a sysex? */
+ sysex[i] = 0xf7;
+ sd->sysex_state = 0;
+ }
+ break;
+ }
+ c[i] = sysex[i];
+ if (!sd->sysex_state)
+ break;
+ }
+ if (md->synth.writeraw(md, c, i, 1) == EAGAIN)
+ return (EAGAIN);
+
+ /* Update the status. */
+ for (j = i - 1 ; j >= 0 ; j--)
+ if ((c[j] & 0x80) != 0) {
+ sd->prev_out_status = c[j];
+ break;
+ }
+
+ return (0);
+}
+
+static int
+synth_prefixcmd(mididev_info *md, int status)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_volumemethod(mididev_info *md, int mode)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_readraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ int unit, ret, s;
+
+ if (md == NULL)
+ return (ENXIO);
+
+ unit = md->unit;
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("synth_readraw: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ if ((md->fflags & FREAD) == 0) {
+ DEB(printf("mpu_readraw: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ s = splmidi();
+
+ /* Begin recording. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_RD);
+
+ if (nonblock) {
+ /* Have we got enough data to read? */
+ if (md->midi_dbuf_in.rl < len)
+ return (EAGAIN);
+ }
+
+ ret = midibuf_seqread(&md->midi_dbuf_in, buf, len);
+
+ splx(s);
+
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ return (ret);
+}
+
+static int
+synth_writeraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ int unit, ret, s;
+
+ if (md == NULL)
+ return (ENXIO);
+
+ unit = md->unit;
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("synth_writeraw: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ if ((md->fflags & FWRITE) == 0) {
+ DEB(printf("synth_writeraw: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* For nonblocking, have we got enough space to write? */
+ if (nonblock && md->midi_dbuf_out.fl < len)
+ return (EAGAIN);
+
+ s = splmidi();
+
+ ret = midibuf_seqwrite(&md->midi_dbuf_out, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ /* Begin playing. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_WR);
+
+ splx(s);
+
+ return (ret);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+synth_leavesysex(mididev_info *md)
+{
+ int unit;
+ synthdev_info *sd;
+ u_char c;
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (!sd->sysex_state)
+ return (0);
+
+ sd->sysex_state = 0;
+ c = 0xf7;
+ if (md->synth.writeraw(md, &c, sizeof(c), 1) == EAGAIN)
+ return (EAGAIN);
+ sd->sysex_state = 0;
+ /* Update the status. */
+ sd->prev_out_status = c;
+
+ return (0);
+}
diff --git a/sys/dev/sound/midi/midisynth.h b/sys/dev/sound/midi/midisynth.h
new file mode 100644
index 0000000..d27e62b
--- /dev/null
+++ b/sys/dev/sound/midi/midisynth.h
@@ -0,0 +1,97 @@
+/*
+ * include file for midi synthesizer interface.
+ *
+ * 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$
+ *
+ */
+
+#define SYNTH_MAX_VOICES 32
+
+/* This is the voice allocation state for a synthesizer. */
+struct voice_alloc_info {
+ int max_voice;
+ int used_voices;
+ int ptr; /* For device specific use */
+ u_short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+ int timestamp;
+ int alloc_times[SYNTH_MAX_VOICES];
+};
+
+/* This is the channel information for a synthesizer. */
+struct channel_info {
+ int pgm_num;
+ int bender_value;
+ u_char controllers[128];
+};
+
+/* These are the function types for a midi synthesizer interface. */
+typedef int (mdsy_killnote_t)(mididev_info *md, int chn, int note, int vel);
+typedef int (mdsy_setinstr_t)(mididev_info *md, int chn, int instr);
+typedef int (mdsy_startnote_t)(mididev_info *md, int chn, int note, int vel);
+typedef int (mdsy_reset_t)(mididev_info *md);
+typedef int (mdsy_hwcontrol_t)(mididev_info *md, u_char *event);
+typedef int (mdsy_loadpatch_t)(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag);
+typedef int (mdsy_panning_t)(mididev_info *md, int chn, int pan);
+typedef int (mdsy_aftertouch_t)(mididev_info *md, int chn, int press);
+typedef int (mdsy_controller_t)(mididev_info *md, int chn, int ctrlnum, int val);
+typedef int (mdsy_patchmgr_t)(mididev_info *md, struct patmgr_info *rec);
+typedef int (mdsy_bender_t)(mididev_info *md, int chn, int val);
+typedef int (mdsy_allocvoice_t)(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc);
+typedef int (mdsy_setupvoice_t)(mididev_info *md, int voice, int chn);
+typedef int (mdsy_sendsysex_t)(mididev_info *md, u_char *sysex, int len);
+typedef int (mdsy_prefixcmd_t)(mididev_info *md, int status);
+typedef int (mdsy_volumemethod_t)(mididev_info *md, int mode);
+typedef int (mdsy_readraw_t)(mididev_info *md, u_char *buf, int len, int nonblock);
+typedef int (mdsy_writeraw_t)(mididev_info *md, u_char *buf, int len, int nonblock);
+
+/* This is a midi synthesizer interface and state. */
+struct _synthdev_info {
+ mdsy_killnote_t *killnote;
+ mdsy_setinstr_t *setinstr;
+ mdsy_startnote_t *startnote;
+ mdsy_reset_t *reset;
+ mdsy_hwcontrol_t *hwcontrol;
+ mdsy_loadpatch_t *loadpatch;
+ mdsy_panning_t *panning;
+ mdsy_aftertouch_t *aftertouch;
+ mdsy_controller_t *controller;
+ mdsy_patchmgr_t *patchmgr;
+ mdsy_bender_t *bender;
+ mdsy_allocvoice_t *allocvoice;
+ mdsy_setupvoice_t *setupvoice;
+ mdsy_sendsysex_t *sendsysex;
+ mdsy_prefixcmd_t *prefixcmd;
+ mdsy_volumemethod_t *volumemethod;
+ mdsy_readraw_t *readraw;
+ mdsy_writeraw_t *writeraw;
+
+ struct voice_alloc_info alloc; /* Voice allocation. */
+ struct channel_info chn_info[16]; /* Channel information. */
+
+ u_char prev_out_status; /* Previous status. */
+ int sysex_state; /* State of sysex transmission. */
+};
+typedef struct _synthdev_info synthdev_info;
diff --git a/sys/dev/sound/midi/miditypes.h b/sys/dev/sound/midi/miditypes.h
new file mode 100644
index 0000000..c0dfdf6
--- /dev/null
+++ b/sys/dev/sound/midi/miditypes.h
@@ -0,0 +1,34 @@
+/*
+ * Include file for type definitions in midi driver.
+ *
+ * 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$
+ *
+ */
+
+typedef struct _mididev_info mididev_info;
+
+typedef int (midi_callback_t)(mididev_info *d, int reason);
+typedef void (midi_intr_t)(void *p, mididev_info *md);
diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c
new file mode 100644
index 0000000..ba6a21d
--- /dev/null
+++ b/sys/dev/sound/midi/sequencer.c
@@ -0,0 +1,2040 @@
+/*
+ * The sequencer personality manager.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * This is the newmidi sequencer driver. This driver handles io against
+ * /dev/sequencer, midi input and output event queues and event transmittion
+ * to and from a midi device or synthesizer.
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/midi/sequencer.h>
+
+#ifndef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+
+#define MIDIDEV_MODE 0x2000
+
+/* Length of a sequencer event. */
+#define EV_SZ 8
+#define IEV_SZ 8
+
+/* Return value from seq_playevent and the helpers. */
+enum {
+ MORE,
+ TIMERARMED,
+ QUEUEFULL
+};
+
+/*
+ * These functions goes into seq_op_desc to get called
+ * from sound.c.
+ */
+
+static midi_intr_t seq_intr;
+static seq_callback_t seq_callback;
+
+/* 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;
+
+/*
+ * This is the device descriptor for the midi sequencer.
+ */
+seqdev_info seq_op_desc = {
+ "midi sequencer",
+
+ 0,
+
+ seq_open,
+ seq_close,
+ seq_read,
+ seq_write,
+ seq_ioctl,
+ seq_poll,
+
+ seq_callback,
+
+ SEQ_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/* Here is the parameter structure per a device. */
+struct seq_softc {
+ seqdev_info *devinfo; /* sequencer device information */
+
+ int fflags; /* Access mode */
+
+ u_long seq_time; /* The beggining time of this sequence */
+ u_long prev_event_time; /* The time of the previous event output */
+ u_long prev_input_time; /* The time of the previous event input */
+ u_long prev_wakeup_time; /* The time of the previous wakeup */
+ struct callout_handle timeout_ch; /* Timer callout handler */
+ long timer_current; /* Current timer value */
+ int timer_running; /* State of timer */
+ int midi_open[NMIDI_MAX]; /* State of midi devices. */
+ int pending_timer; /* Timer change operation */
+ int output_threshould; /* Sequence output threshould */
+ int pre_event_timeout; /* Time to wait event input */
+ int queueout_pending; /* Pending for the output queue */
+ snd_sync_parm sync_parm; /* AIOSYNC parameter set */
+ struct proc *sync_proc; /* AIOSYNCing process */
+};
+
+typedef struct seq_softc *sc_p;
+
+static d_open_t seqopen;
+static d_close_t seqclose;
+static d_ioctl_t seqioctl;
+static d_read_t seqread;
+static d_write_t seqwrite;
+static d_poll_t seqpoll;
+
+#define CDEV_MAJOR SEQ_CDEV_MAJOR
+static struct cdevsw seq_cdevsw = {
+ /* open */ seqopen,
+ /* close */ seqclose,
+ /* read */ seqread,
+ /* write */ seqwrite,
+ /* ioctl */ seqioctl,
+ /* poll */ seqpoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "midi", /* XXX */
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+seqdev_info seq_info[NSEQ_MAX] ;
+static int seq_info_inited;
+u_long nseq = NSEQ_MAX; /* total number of sequencers */
+
+/* The followings are the local function. */
+static int seq_init(void);
+static int seq_queue(sc_p scp, u_char *note);
+static void seq_startplay(sc_p scp);
+static int seq_playevent(sc_p scp, u_char *event);
+static u_long seq_gettime(void);
+static int seq_requesttimer(sc_p scp, int delay);
+static void seq_stoptimer(sc_p scp);
+static void seq_midiinput(sc_p scp, mididev_info *md);
+static int seq_copytoinput(sc_p scp, u_char *event, int len);
+static int seq_extended(sc_p scp, u_char *event);
+static int seq_chnvoice(sc_p scp, u_char *event);
+static int seq_findvoice(mididev_info *md, int chn, int note);
+static int seq_allocvoice(mididev_info *md, int chn, int note);
+static int seq_chncommon(sc_p scp, u_char *event);
+static int seq_timing(sc_p scp, u_char *event);
+static int seq_local(sc_p scp, u_char *event);
+static int seq_sysex(sc_p scp, u_char *event);
+static void seq_timer(void *arg);
+static int seq_reset(sc_p scp);
+static int seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static int seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static void seq_panic(sc_p scp);
+static int seq_sync(sc_p scp);
+
+static seqdev_info * get_seqdev_info(dev_t i_dev, int *unit);
+
+/*
+ * Here are the main functions to interact to the user process.
+ * These are called from snd* functions in sys/i386/isa/snd/sound.c.
+ */
+
+static int
+seq_init(void)
+{
+ int unit;
+ sc_p scp;
+ seqdev_info *devinfo;
+
+ DEB(printf("seq: initing.\n"));
+
+ /* Have we already inited? */
+ if (seq_info_inited)
+ return (1);
+
+ for (unit = 0 ; unit < nseq ; unit++) {
+ /* Allocate the softc. */
+ scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
+ if (scp == (sc_p)NULL) {
+ printf("seq%d: softc allocation failed.\n", unit);
+ return (1);
+ }
+ bzero(scp, sizeof(*scp));
+
+ /* Fill the softc and the seq_info for this unit. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ callout_handle_init(&scp->timeout_ch);
+ scp->timer_current = 0;
+ scp->timer_running = 0;
+ scp->queueout_pending = 0;
+
+ scp->devinfo = devinfo = &seq_info[unit];
+ bcopy(&seq_op_desc, devinfo, sizeof(seq_op_desc));
+ devinfo->unit = unit;
+ devinfo->softc = scp;
+ devinfo->flags = 0;
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = EV_SZ;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ make_dev(&seq_cdevsw, MIDIMKMINOR(unit, SND_DEV_SEQ),
+ UID_ROOT, GID_WHEEL, 0666, "sequencer%d", unit);
+ }
+
+ /* We have inited. */
+ seq_info_inited = 1;
+
+ if (nseq == 1)
+ printf("seq0: Midi sequencer.\n");
+ else
+ printf("seq0-%lu: Midi sequencers.\n", nseq - 1);
+
+ DEB(printf("seq: inited.\n"));
+
+ return (0);
+}
+
+int
+seq_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, midiunit;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: opening.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_open: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_open: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+ /* Mark this device busy. */
+ if ((sd->flags & SEQ_F_BUSY) != 0) {
+ splx(s);
+ DEB(printf("seq_open: unit %d is busy.\n", unit));
+ return (EBUSY);
+ }
+ sd->flags |= SEQ_F_BUSY;
+ sd->flags &= ~(SEQ_F_READING | SEQ_F_WRITING);
+ scp->fflags = flags;
+
+ /* Init the queue. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+ /* Init timestamp. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+
+ splx(s);
+
+ /* Open midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md))
+ seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ DEB(printf("seq%d: opened.\n", unit));
+
+ return (0);
+}
+
+int
+seq_close(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, i;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: closing.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_close: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_close: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+
+ if (!(sd->flags & MIDI_F_NBIO))
+ seq_sync(scp);
+
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+
+ /* Reset the sequencer. */
+ seq_reset(scp);
+ seq_sync(scp);
+
+ /* Clean up the midi device. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ sd->flags &= ~(SEQ_F_BUSY | SEQ_F_READING | SEQ_F_WRITING);
+
+ splx(s);
+
+ DEB(printf("seq%d: closed.\n", unit));
+
+ return (0);
+}
+
+int
+seq_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ int unit, ret, s, len;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: reading.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_read: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_read: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FREAD) == 0) {
+ DEB(printf("seq_read: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ s = splmidi();
+
+ /* Begin recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Have we got the data to read? */
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_in.rl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ ret = midibuf_uioread(&sd->midi_dbuf_in, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+ splx(s);
+
+ return (ret);
+}
+
+int
+seq_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ u_char event[EV_SZ], ev_code;
+ int unit, count, countorg, midiunit, ev_size, p, ret, s;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: writing.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_write: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_write: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FWRITE) == 0) {
+ DEB(printf("seq_write: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ p = 0;
+ countorg = buf->uio_resid;
+ count = countorg;
+
+ s = splmidi();
+ /* Begin playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ /* Pick up an event. */
+ while (count >= 4) {
+ if (uiomove((caddr_t)event, 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ ev_code = event[0];
+
+ /* Have a look at the event code. */
+ if (ev_code == SEQ_FULLSIZE) {
+
+ /* A long event, these are the patches/samples for a synthesizer. */
+ midiunit = *(u_short *)&event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ s = splmidi();
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0) {
+ splx(s);
+ return (ENXIO);
+ }
+
+ DEB(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);
+ splx(s);
+ return (ret);
+ }
+
+ if (ev_code >= 128) {
+
+ /* Some sort of an extended event. The size is eight bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) {
+ printf("seq%d: invalid level two event %x.\n", unit, ev_code);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 8;
+
+ if (count < ev_size) {
+ /* No more data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+ }
+ if (uiomove((caddr_t)&event[4], 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ } else {
+
+ /* Not an extended event. The size is four bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ printf("seq%d: four byte event in level two mode.\n", unit);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 4;
+ }
+ if (ev_code == SEQ_MIDIPUTC) {
+ /* An event passed to the midi device itself. */
+ midiunit = event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && (ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc)) != 0) {
+ seq_reset(scp);
+ return (ret);
+ }
+ }
+
+ /*DEB(printf("seq_write: queueing event %d.\n", event[0]));*/
+ /* Now we queue the event. */
+ switch (seq_queue(scp, event)) {
+ case EAGAIN:
+ s = splmidi();
+ /* The queue is full. Start playing now. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+ return (0);
+ case EINTR:
+ return (EINTR);
+ case ERESTART:
+ return (ERESTART);
+ }
+ p += ev_size;
+ count -= ev_size;
+ }
+
+ /* We have written every single data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+}
+
+int
+seq_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ int unit, midiunit, ret, tmp, s, arg2;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+ struct patmgr_info *patinfo;
+ snd_sync_parm *syncparm;
+ struct seq_event_rec *event;
+ struct snd_size *sndsize;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+
+ switch (cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can be written ? */
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ if (sndsize->play_size <= sd->midi_dbuf_out.unit_size && sndsize->rec_size <= sd->midi_dbuf_in.unit_size) {
+ sd->flags &= ~MIDI_F_HAS_SIZE;
+ sd->midi_dbuf_out.blocksize = sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize = sd->midi_dbuf_in.unit_size;
+ }
+ else {
+ if (sndsize->play_size > sd->midi_dbuf_out.bufsize / 4)
+ sndsize->play_size = sd->midi_dbuf_out.bufsize / 4;
+ if (sndsize->rec_size > sd->midi_dbuf_in.bufsize / 4)
+ sndsize->rec_size = sd->midi_dbuf_in.bufsize / 4;
+ /* Round up the size to the multiple of EV_SZ. */
+ sd->midi_dbuf_out.blocksize =
+ ((sndsize->play_size + sd->midi_dbuf_out.unit_size - 1)
+ / sd->midi_dbuf_out.unit_size) * sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize =
+ ((sndsize->rec_size + sd->midi_dbuf_in.unit_size - 1)
+ / sd->midi_dbuf_in.unit_size) * sd->midi_dbuf_in.unit_size;
+ sd->flags |= MIDI_F_HAS_SIZE;
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ sndsize->play_size = sd->midi_dbuf_out.blocksize;
+ sndsize->rec_size = sd->midi_dbuf_in.blocksize;
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) {
+ s = splmidi();
+
+ /* Stop writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_WR);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_out.rl;
+ splx(s);
+ }
+ else if (*(int *)arg == AIOSYNC_CAPTURE) {
+ s = splmidi();
+
+ /* Stop reading. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ splx(s);
+ }
+
+ ret = 0;
+ break;
+
+ case AIOSYNC:
+ syncparm = (snd_sync_parm *)arg;
+ scp->sync_parm = *syncparm;
+
+ /* XXX Should select(2) against us watch the blocksize, or sync_parm? */
+
+ ret = 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+#endif /* notyet */
+ break;
+ case SNDCTL_TMR_SELECT:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+#endif /* notyet */
+ scp->pending_timer = *(int *)arg;
+ if (scp->pending_timer < 0 || scp->pending_timer >= /*NTIMER*/1) {
+ scp->pending_timer = -1;
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = scp->pending_timer;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_PANIC:
+ seq_panic(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_SYNC:
+ if (mode == O_RDONLY) {
+ ret = 0;
+ break;
+ }
+ ret = seq_sync(scp);
+ break;
+ case SNDCTL_SEQ_RESET:
+ seq_reset(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_TESTMIDI:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && !scp->midi_open[midiunit]) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ break;
+ }
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (mode == O_WRONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETOUTCOUNT:
+ if (mode == O_RDONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_CTRLRATE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+ break;
+ }
+#endif /* notyet */
+ if (*(int *)arg != 0) {
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = hz;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_RESETSAMPLES:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_NRSYNTHS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_NRMIDIS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SYNTH_MEMAVL:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_FM_4OP_ENABLE:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ midiunit = synthinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_OUTOFBAND:
+ event = (struct seq_event_rec *)arg;
+ s = splmidi();
+ ret = seq_playevent(scp, event->arr);
+ splx(s);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ midiunit = midiinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_IFACE:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ ret = EBUSY;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_ACCESS:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_THRESHOLD:
+ tmp = *(int *)arg;
+ RANGE(tmp, 1, sd->midi_dbuf_out.bufsize - 1);
+ scp->output_threshould = tmp;
+ ret = 0;
+ break;
+ case SNDCTL_MIDI_PRETIME:
+ tmp = *(int *)arg;
+ if (tmp < 0)
+ tmp = 0;
+ tmp = (hz * tmp) / 10;
+ scp->pre_event_timeout = tmp;
+ ret = 0;
+ break;
+ default:
+ if (scp->fflags == O_RDONLY) {
+ ret = EIO;
+ break;
+ }
+ if (!scp->midi_open[0]) {
+ md = &midi_info[0];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), 0, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ }
+
+ return (ret);
+}
+
+int
+seq_poll(dev_t i_dev, int events, struct proc *p)
+{
+ int unit, ret, s, lim;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: polling.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_poll: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_poll: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+ s = splmidi();
+
+ /* Look up the apropriate queue and select it. */
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ /* Start playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_out.blocksize;
+ else
+ lim = sd->midi_dbuf_out.unit_size;
+ if (sd->midi_dbuf_out.fl < lim)
+ /* No enough space, record select. */
+ selrecord(p, &sd->midi_dbuf_out.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLOUT | POLLWRNORM);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ /* Start recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_in.blocksize;
+ else
+ lim = sd->midi_dbuf_in.unit_size;
+ if (sd->midi_dbuf_in.rl < lim)
+ /* No data ready, record select. */
+ selrecord(p, &sd->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+ splx(s);
+
+ return (ret);
+}
+
+static void
+seq_intr(void *p, mididev_info *md)
+{
+ sc_p scp;
+ seqdev_info *sd;
+
+ sd = (seqdev_info *)p;
+ scp = sd->softc;
+
+ /* Restart playing if we have the data to output. */
+ if (scp->queueout_pending)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ /* Check the midi device if we are reading. */
+ if ((sd->flags & SEQ_F_READING) != 0)
+ seq_midiinput(scp, md);
+}
+
+static int
+seq_callback(seqdev_info *sd, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ /*DEB(printf("seq_callback: reason 0x%x.\n", reason));*/
+
+ if (sd == NULL) {
+ DEB(printf("seq_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ unit = sd->unit;
+
+ switch (reason & SEQ_CB_REASON_MASK) {
+ case SEQ_CB_START:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) == 0)
+ /* Begin recording. */
+ sd->flags |= SEQ_F_READING;
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) == 0)
+ /* Start playing. */
+ seq_startplay(scp);
+ break;
+ case SEQ_CB_STOP:
+ case SEQ_CB_ABORT:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) != 0) {
+ /* Stop the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+
+ /* Stop recording. */
+ sd->flags &= ~SEQ_F_READING;
+ }
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) != 0) {
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+
+ /* Stop Playing. */
+ sd->flags &= ~SEQ_F_WRITING;
+ scp->queueout_pending = 0;
+ }
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+seq_queue(sc_p scp, u_char *note)
+{
+ int unit, err, s;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /*DEB(printf("seq%d: queueing.\n", unit));*/
+
+ s = splmidi();
+
+ /* Start playing if we have some data in the queue. */
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ if (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Start playing if not yet. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We would block. */
+ splx(s);
+ return (EAGAIN);
+ } else
+ while (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Good night. */
+ err = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqque", 0);
+ if (err == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (err == EINTR || err == ERESTART) {
+ splx(s);
+ return (err);
+ }
+ }
+ }
+
+ /* We now have enough space to write. */
+ err = midibuf_seqwrite(&sd->midi_dbuf_out, note, EV_SZ);
+
+ splx(s);
+
+ if (err < 0)
+ err = -err;
+ else
+ err = 0;
+
+ return (err);
+}
+
+static void
+seq_startplay(sc_p scp)
+{
+ int unit;
+ u_char event[EV_SZ];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Dequeue the events to play. */
+ while (sd->midi_dbuf_out.rl >= EV_SZ) {
+
+ /* We are playing now. */
+ sd->flags |= SEQ_F_WRITING;
+
+ /* We only copy the event, not dequeue. */
+ midibuf_seqcopy(&sd->midi_dbuf_out, event, EV_SZ);
+
+ switch (seq_playevent(scp, event)) {
+ case TIMERARMED:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ /* FALLTHRU */
+ case QUEUEFULL:
+ /* We cannot play further. */
+ return;
+ case MORE:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ break;
+ }
+ }
+
+ /* Played every event in the queue. */
+ sd->flags &= ~SEQ_F_WRITING;
+}
+
+static int
+seq_playevent(sc_p scp, u_char *event)
+{
+ int unit, ret;
+ long *delay;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ md = &midi_info[0];
+ if (!MIDICONFED(md))
+ return (MORE);
+
+ switch(event[0]) {
+ case SEQ_NOTEOFF:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.killnote(md, event[1], 255, event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_NOTEON:
+ if (((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ && (event[4] < 128 || event[4] == 255))
+ if (md->synth.startnote(md, event[1], event[2], event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_WAIT:
+
+ /* Extract the delay. */
+ delay = (long *)event;
+ *delay = (*delay >> 8) & 0xffffff;
+ if (*delay > 0) {
+ /* Arm the timer. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = *delay;
+ if (seq_requesttimer(scp, *delay)) {
+ ret = TIMERARMED;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_PGMCHANGE:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.setinstr(md, event[1], event[2]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_SYNCTIMER:
+ /* Reset the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ ret = MORE;
+ break;
+ case SEQ_MIDIPUTC:
+ /* Pass through to the midi device. */
+ if (event[2] < nmidi + nsynth) {
+ md = &midi_info[event[2]];
+ if (MIDICONFED(md) && ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)) {
+ if (md->synth.writeraw(md, &event[1], sizeof(event[1]), 1) == EAGAIN) {
+ /* The queue was full. Try again later. */
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_ECHO:
+ /* Echo this event back. */
+ if (seq_copytoinput(scp, event, 4) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_PRIVATE:
+ if (event[1] < nmidi + nsynth) {
+ md = &midi_info[event[1]];
+ if (MIDICONFED(md) && md->synth.hwcontrol(md, event) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_EXTENDED:
+ ret = seq_extended(scp, event);
+ break;
+ case EV_CHN_VOICE:
+ ret = seq_chnvoice(scp, event);
+ break;
+ case EV_CHN_COMMON:
+ ret = seq_chncommon(scp, event);
+ break;
+ case EV_TIMING:
+ ret = seq_timing(scp, event);
+ break;
+ case EV_SEQ_LOCAL:
+ ret = seq_local(scp, event);
+ break;
+ case EV_SYSEX:
+ ret = seq_sysex(scp, event);
+ break;
+ default:
+ ret = MORE;
+ break;
+ }
+
+ switch (ret) {
+ case QUEUEFULL:
+ /*DEB(printf("seq_playevent: the queue is full.\n"));*/
+ /* The queue was full. Try again on the interrupt by the midi device. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->queueout_pending = 1;
+ break;
+ case TIMERARMED:
+ sd->flags |= SEQ_F_WRITING;
+ /* FALLTHRU */
+ case MORE:
+ scp->queueout_pending = 0;
+ break;
+ }
+
+ return (ret);
+}
+
+static u_long
+seq_gettime(void)
+{
+ struct timeval timecopy;
+
+ getmicrotime(&timecopy);
+ return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz;
+}
+
+static int
+seq_requesttimer(sc_p scp, int delay)
+{
+ u_long cur_time, rel_base;
+
+ /*DEB(printf("seq%d: requested timer at delay of %d.\n", unit, delay));*/
+
+ cur_time = seq_gettime();
+
+ if (delay < 0)
+ /* Request a new timer. */
+ delay = -delay;
+ else {
+ rel_base = cur_time - scp->seq_time;
+ if (delay <= rel_base)
+ return 0;
+ delay -= rel_base;
+ }
+
+#if notdef
+ /*
+ * Compensate the delay of midi message transmission.
+ * XXX Do we have to consider the accumulation of errors
+ * less than 1/hz second?
+ */
+ delay -= (cur_time - scp->prev_wakeup_time);
+ if (delay < 1) {
+ printf("sequencer: prev = %lu, cur = %lu, delay = %d, skip sleeping.\n",
+ scp->prev_wakeup_time, cur_time, delay);
+ seq_stoptimer(scp);
+ return 0;
+ }
+#endif /* notdef */
+
+ scp->timeout_ch = timeout(seq_timer, (void *)scp, delay);
+ scp->timer_running = 1;
+ return 1;
+}
+
+static void
+seq_stoptimer(sc_p scp)
+{
+ /*DEB(printf("seq%d: stopping timer.\n", unit));*/
+
+ if (scp->timer_running) {
+ untimeout(seq_timer, (void *)scp, scp->timeout_ch);
+ scp->timer_running = 0;
+ }
+}
+
+static void
+seq_midiinput(sc_p scp, mididev_info *md)
+{
+ int unit, midiunit;
+ u_long tstamp;
+ u_char event[4];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Can this midi device interrupt for input? */
+ midiunit = md->unit;
+ if (scp->midi_open[midiunit]
+ && (md->flags & MIDI_F_READING) != 0
+ && md->intrarg == sd)
+ /* Read the input data. */
+ while (md->synth.readraw(md, &event[1], sizeof(event[1]), 1) == 0) {
+ tstamp = seq_gettime() - scp->seq_time;
+ if (tstamp != scp->prev_input_time) {
+ /* Insert a wait between events. */
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+ seq_copytoinput(scp, (u_char *)&tstamp, 4);
+ scp->prev_input_time = tstamp;
+ }
+ event[0] = SEQ_MIDIPUTC;
+ event[2] = midiunit;
+ event[3] = 0;
+ seq_copytoinput(scp, event, sizeof(event));
+ }
+}
+
+static int
+seq_copytoinput(sc_p scp, u_char *event, int len)
+{
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+
+ if (midibuf_input_intr(&sd->midi_dbuf_in, event, len) == -EAGAIN)
+ return (EAGAIN);
+
+ return (0);
+}
+
+static int
+seq_extended(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[2] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[2]];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (event[1]) {
+ case SEQ_NOTEOFF:
+ if (md->synth.killnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_NOTEON:
+ if (event[4] < 128 || event[4] == 255)
+ if (md->synth.startnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_PGMCHANGE:
+ if (md->synth.setinstr(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_AFTERTOUCH:
+ if (md->synth.aftertouch(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_BALANCE:
+ if (md->synth.panning(md, event[3], (char)event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_CONTROLLER:
+ if (md->synth.controller(md, event[3], event[4], *(short *)&event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_VOLMODE:
+ if (md->synth.volumemethod != NULL)
+ if (md->synth.volumemethod(md, event[3]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_chnvoice(sc_p scp, u_char *event)
+{
+ int voice;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char dev, cmd, chn, note, parm;
+
+ voice = -1;
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ note = event[4];
+ parm = event[5];
+
+ sd = scp->devinfo;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (md->synth.allocvoice)
+ voice = seq_allocvoice(md, chn, note);
+ }
+#endif /* notyet */
+ switch (cmd) {
+ case MIDI_NOTEON:
+ if (note < 128 || note == 255) {
+#if notyet
+ if (voice == -1 && scp->seq_mode == SEQ_2 && md->synth.allocvoice)
+ /* This is an internal synthesizer. (FM, GUS, etc) */
+ if ((voice = seq_allocvoice(md, chn, note)) == -EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (voice == -1)
+ voice = chn;
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && dev < nmidi + nsynth && chn == 9) {
+ /* This channel is a percussion. The note number is the patch number. */
+ if (md->synth.setinstr(md, voice, 128 + note) == EAGAIN)
+ return (QUEUEFULL);
+ note = 60; /* Middle C. */
+ }
+ if (scp->seq_mode == SEQ_2)
+ if (md->synth.setupvoice(md, voice, chn) == EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (md->synth.startnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ break;
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.killnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_KEY_PRESSURE:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.aftertouch(md, voice, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_findvoice(mididev_info *md, int chn, int note)
+{
+ int i;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+ if (md->synth.alloc.map[i] == key)
+ return (i);
+
+ return (-1);
+}
+
+static int
+seq_allocvoice(mididev_info *md, int chn, int note)
+{
+ int voice;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ if ((voice = md->synth.allocvoice(md, chn, note, &md->synth.alloc)) == -EAGAIN)
+ return (-EAGAIN);
+ md->synth.alloc.map[voice] = key;
+ md->synth.alloc.alloc_times[voice] = md->synth.alloc.timestamp++;
+
+ return (voice);
+}
+
+static int
+seq_chncommon(sc_p scp, u_char *event)
+{
+ int unit/*, i, val, key*/;
+ u_short w14;
+ u_char dev, cmd, chn, p1;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ p1 = event[4];
+ w14 = *(u_short *)&event[6];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (cmd) {
+ case MIDI_PGM_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].pgm_num = p1;
+ if (dev < nmidi + nsynth)
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_CTL_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (chn < 16 && p1 < 128) {
+ md->synth.chn_info[chn].controllers[p1] = w14 & 0x7f;
+ if (p1 < 32)
+ /* We have set the MSB, clear the LSB. */
+ md->synth.chn_info[chn].controllers[p1 + 32] = 0;
+ if (dev < nmidi + nsynth) {
+ val = w14 & 0x7f;
+ if (p1 < 64) {
+ /* Combine the MSB and the LSB. */
+ val = ((md->synth.chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (md->synth.chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+ /* 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)
+ if (md->synth.controller(md, i, p1, val) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_PITCH_BEND:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].bender_value = w14;
+ if (dev < nmidi + nsynth) {
+ /* 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)
+ if (md->synth.bender(md, i, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_timing(sc_p scp, u_char *event)
+{
+ int unit/*, ret*/;
+ long parm;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ parm = *(long *)&event[4];
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && (ret = tmr->event(tmr_no, event)) == TIMERARMED)
+ return (ret);
+#endif /* notyet */
+ switch (event[1]) {
+ case TMR_WAIT_REL:
+ parm += scp->prev_event_time;
+ /* FALLTHRU */
+ case TMR_WAIT_ABS:
+ if (parm > 0) {
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = parm;
+ if (seq_requesttimer(scp, parm))
+ return (TIMERARMED);
+ }
+ break;
+ case TMR_START:
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ break;
+ case TMR_STOP:
+ break;
+ case TMR_CONTINUE:
+ break;
+ case TMR_TEMPO:
+ break;
+ case TMR_ECHO:
+#if notyet
+ if (scp->seq_mode == SEQ_2)
+ seq_copytoinput(scp, event, 8);
+ else {
+#endif /* notyet */
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copytoinput(scp, (u_char *)&parm, 4);
+#if notyet
+ }
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_local(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ switch (event[1]) {
+ case LOCL_STARTAUDIO:
+#if notyet
+ DMAbuf_start_devices(*(u_int *)&event[4]);
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_sysex(sc_p scp, u_char *event)
+{
+ int unit, i, l;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[1] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[1]];
+ if (!MIDICONFED(md) || md->synth.sendsysex == NULL
+ || ((md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0))
+ return (MORE);
+
+ l = 0;
+ for (i = 0 ; i < 6 && event[i + 2] != 0xff ; i++)
+ l = i + 1;
+ if (l > 0)
+ if (md->synth.sendsysex(md, &event[2], l) == EAGAIN)
+ return (QUEUEFULL);
+
+ return (MORE);
+}
+
+static void
+seq_timer(void *arg)
+{
+ sc_p scp;
+
+ scp = arg;
+
+ /*DEB(printf("seq_timer: timer fired.\n"));*/
+
+ /* Record the current timestamp. */
+ scp->prev_wakeup_time = seq_gettime();
+
+ seq_startplay(scp);
+}
+
+static int
+seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s, err;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_openmidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_openmidi: opening midi unit %d.\n", midiunit));
+
+ if (!scp->midi_open[midiunit]) {
+ err = midi_open(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ if (err != 0) {
+ printf("seq_openmidi: failed to open midi device %d.\n", midiunit);
+ return (err);
+ }
+ s = splmidi();
+ scp->midi_open[midiunit] = 1;
+ md->intr = seq_intr;
+ md->intrarg = scp->devinfo;
+ md->synth.prev_out_status = 0;
+ md->synth.sysex_state = 0;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static int
+seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_closemidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_closemidi: closing midi unit %d.\n", midiunit));
+
+ if (scp->midi_open[midiunit]) {
+ midi_close(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ s = splmidi();
+ scp->midi_open[midiunit] = 0;
+ md->intr = NULL;
+ md->intrarg = NULL;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static void
+seq_panic(sc_p scp)
+{
+ seq_reset(scp);
+}
+
+static int
+seq_reset(sc_p scp)
+{
+ int unit, i, s, chn;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char c[3];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ /* Stop reading and writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD | SEQ_CB_WR);
+
+ /* Clear the queues. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+#if notyet
+ /* Reset the synthesizers. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md) && scp->midi_open[i])
+ md->synth.reset(md);
+ }
+#endif /* notyet */
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ for (chn = 0 ; chn < 16 ; chn++)
+ for (i = 0 ; i < nmidi + nsynth ; i++)
+ if (midi_open[i]) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+ if (md->synth.controller(md, chn, 123, 0) == EAGAIN /* All notes off. */
+ || md->synth.controller(md, chn, 121, 0) == EAGAIN /* Reset all controllers. */
+ || md->synth.bender(md, chn, 1 << 13) == EAGAIN) { /* Reset pitch bend. */
+ splx(s);
+ return (EAGAIN);
+ }
+ }
+ splx(s);
+ } else {
+#endif /* notyet */
+ splx(s);
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+
+ /* Send active sensing. */
+ c[0] = 0xfe; /* Active Sensing. */
+ if (md->synth.writeraw(md, c, 1, 0) == EAGAIN)
+ return (EAGAIN);
+ /*
+ * We need a sleep to reset a midi device using an active sensing.
+ * SC-88 resets after 420ms...
+ */
+ tsleep(md, PRIBIO, "seqrst", 500 * hz / 1000);
+ for (chn = 0 ; chn < 16 ; chn++) {
+ c[0] = 0xb0 | (chn & 0x0f);
+ c[1] = (u_char)0x78; /* All sound off */
+ c[2] = (u_char)0;
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x7b; /* All note off */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x79; /* Reset all controller */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ }
+ }
+ for (i = 0 ; i < nmidi + nsynth ; i++){
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ }
+#if notyet
+ }
+#endif /* notyet */
+
+ return (0);
+}
+
+static int
+seq_sync(sc_p scp)
+{
+ int unit, s, i;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ while ((sd->flags & SEQ_F_WRITING) != 0 && sd->midi_dbuf_out.rl >= EV_SZ) {
+ i = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqsnc", 0);
+ if (i == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (i == EINTR || i == ERESTART) {
+ splx(s);
+ return (i);
+ }
+ }
+ splx(s);
+
+ return (0);
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated seqdev_info struct, and sets the unit
+ * number.
+ */
+static seqdev_info *
+get_seqdev_info(dev_t i_dev, int *unit)
+{
+ int u;
+ seqdev_info *d = NULL ;
+
+ if (MIDIDEV(i_dev) != SND_DEV_SEQ && MIDIDEV(i_dev) != SND_DEV_SEQ2)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u ;
+
+ if (u >= NSEQ_MAX) {
+ DEB(printf("get_seqdev_info: unit %d is not configured.\n", u));
+ return NULL;
+ }
+ d = &seq_info[u];
+
+ return d ;
+}
+
+/* XXX These functions are actually redundant. */
+static int
+seqopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_open(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_close(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqread(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_read(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_write(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_ioctl(i_dev, cmd, arg, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqpoll(dev_t i_dev, int events, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_poll(i_dev, events, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seq_modevent(module_t mod, int type, void *data)
+{
+ int retval;
+
+ retval = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ seq_init();
+ break;
+
+ case MOD_UNLOAD:
+ printf("sequencer: unload not supported yet.\n");
+ retval = EOPNOTSUPP;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+DEV_MODULE(seq, seq_modevent, NULL);
diff --git a/sys/dev/sound/midi/sequencer.h b/sys/dev/sound/midi/sequencer.h
new file mode 100644
index 0000000..7275b77
--- /dev/null
+++ b/sys/dev/sound/midi/sequencer.h
@@ -0,0 +1,234 @@
+/*
+ * Include file for midi sequencer driver.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _SEQUENCER_H_
+#define _SEQUENCER_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#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 <machine/clock.h> /* for DELAY */
+#include <sys/soundcard.h>
+
+#define SEQ_CDEV_MAJOR MIDI_CDEV_MAJOR
+
+/*
+ * the following assumes that FreeBSD 3.X uses poll(2) instead of select(2).
+ * This change dates to late 1997.
+ */
+#include <sys/poll.h>
+#define d_select_t d_poll_t
+
+typedef struct _seqdev_info seqdev_info;
+
+typedef int (seq_callback_t)(seqdev_info *sd, int reason);
+
+/*
+ * descriptor of sequencer operations ...
+ *
+ */
+
+struct _seqdev_info {
+
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[64];
+
+ int type ;
+
+ d_open_t *open;
+ d_close_t *close;
+ d_read_t *read;
+ d_write_t *write;
+ d_ioctl_t *ioctl;
+ d_poll_t *poll;
+ seq_callback_t *callback;
+
+ /*
+ * combinations of the following flags are used as second argument in
+ * the callback from the dma module to the device-specific routines.
+ */
+
+#define SEQ_CB_RD 0x100 /* read callback */
+#define SEQ_CB_WR 0x200 /* write callback */
+#define SEQ_CB_REASON_MASK 0xff
+#define SEQ_CB_START 0x01 /* start dma op */
+#define SEQ_CB_STOP 0x03 /* stop dma op */
+#define SEQ_CB_ABORT 0x04 /* abort dma op */
+#define SEQ_CB_INIT 0x05 /* init board parameters */
+
+ /*
+ * callback extensions
+ */
+#define SEQ_CB_DMADONE 0x10
+#define SEQ_CB_DMAUPDATE 0x11
+#define SEQ_CB_DMASTOP 0x12
+
+ /* init can only be called with int enabled and
+ * no pending DMA activity.
+ */
+
+ /*
+ * whereas from here, parameters are set at runtime.
+ * io_base == 0 means that the board is not configured.
+ */
+
+ int unit; /* unit number of the device */
+ void *softc; /* softc for a device */
+
+ int bd_id ; /* used to hold board-id info, eg. sb version,
+ * mss codec type, etc. etc.
+ */
+
+ midi_dbuf midi_dbuf_in; /* midi input event/message queue */
+ midi_dbuf midi_dbuf_out; /* midi output event/message queue */
+
+ /*
+ * these parameters describe the operation of the board.
+ * Generic things like busy flag, speed, etc are here.
+ */
+
+ volatile u_long flags ; /* 32 bits, used for various purposes. */
+
+ /*
+ * we have separate flags for read and write, although in some
+ * cases this is probably not necessary (e.g. because we cannot
+ * know how many processes are using the device, we cannot
+ * distinguish if open, close, abort are for a write or for a
+ * read).
+ */
+
+ /*
+ * the following flag is used by open-close routines
+ * to mark the status of the device.
+ */
+#define SEQ_F_BUSY 0x0001 /* has been opened */
+ /*
+ * the next two are used to allow only one pending operation of
+ * each type.
+ */
+#define SEQ_F_READING 0x0004 /* have a pending read */
+#define SEQ_F_WRITING 0x0008 /* have a pending write */
+
+ /*
+ * flag used to mark a pending close.
+ */
+#define SEQ_F_CLOSING 0x0040 /* a pending close */
+
+ /*
+ * if user has not set block size, then make it adaptive
+ * (0.25s, or the perhaps last read/write ?)
+ */
+#define SEQ_F_HAS_SIZE 0x0080 /* user set block size */
+ /*
+ * assorted flags related to operating mode.
+ */
+#define SEQ_F_STEREO 0x0100 /* doing stereo */
+#define SEQ_F_NBIO 0x0200 /* do non-blocking i/o */
+
+ /*
+ * these flags mark a pending abort on a r/w operation.
+ */
+#define SEQ_F_ABORTING 0x1000 /* a pending abort */
+
+ /*
+ * this is used to mark that board initialization is needed, e.g.
+ * because of a change in sampling rate, format, etc. -- It will
+ * be done at the next convenient time.
+ */
+#define SEQ_F_INIT 0x4000 /* changed parameters. need init */
+
+ int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
+
+#define swsel midi_dbuf_out.sel
+#define srsel midi_dbuf_in.sel
+ u_long interrupts; /* counter of interrupts */
+ u_long magic;
+#define MAGIC(unit) ( 0xa4d10de0 + unit )
+ void *device_data ; /* just in case it is needed...*/
+} ;
+
+
+/*
+ * then ioctls and other stuff
+ */
+#define NSEQ_MAX 64 /* Number of supported devices */
+
+/*
+ * 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))
+
+/*
+ * finally, all default parameters
+ */
+#define SEQ_BUFFSIZE (4 * 1024) /* XXX */
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+ extern seqdev_info seq_info[NSEQ_MAX] ;
+
+#define MIDI_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+
+#endif /* _SEQUENCER_H_ */
OpenPOWER on IntegriCloud