summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/midi
diff options
context:
space:
mode:
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