summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/midi/midi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/midi/midi.c')
-rw-r--r--sys/dev/sound/midi/midi.c1057
1 files changed, 1057 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..07d2660
--- /dev/null
+++ b/sys/dev/sound/midi/midi.c
@@ -0,0 +1,1057 @@
+/*
+ * 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 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 <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,
+};
+
+/*
+ * descriptors for active devices. also used as the public softc
+ * of a device.
+ */
+static TAILQ_HEAD(,_mididev_info) midi_info;
+static int nmidi, nsynth;
+/* Mutex to protect midi_info, nmidi and nsynth. */
+static struct mtx midiinfo_mtx;
+static int midiinfo_mtx_init;
+
+/* These make the buffer for /dev/midistat */
+static int midistatbusy;
+static char midistatbuf[4096];
+static int midistatptr;
+
+SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver");
+
+int midi_debug;
+SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
+
+midi_cmdtab cmdtab_midiioctl[] = {
+ {SNDCTL_MIDI_PRETIME, "SNDCTL_MIDI_PRETIME"},
+ {SNDCTL_MIDI_MPUMODE, "SNDCTL_MIDI_MPUMODE"},
+ {SNDCTL_MIDI_MPUCMD, "SNDCTL_MIDI_MPUCMD"},
+ {SNDCTL_SYNTH_INFO, "SNDCTL_SYNTH_INFO"},
+ {SNDCTL_MIDI_INFO, "SNDCTL_MIDI_INFO"},
+ {SNDCTL_SYNTH_MEMAVL, "SNDCTL_SYNTH_MEMAVL"},
+ {SNDCTL_FM_LOAD_INSTR, "SNDCTL_FM_LOAD_INSTR"},
+ {SNDCTL_FM_4OP_ENABLE, "SNDCTL_FM_4OP_ENABLE"},
+ {MIOSPASSTHRU, "MIOSPASSTHRU"},
+ {MIOGPASSTHRU, "MIOGPASSTHRU"},
+ {AIONWRITE, "AIONWRITE"},
+ {AIOGSIZE, "AIOGSIZE"},
+ {AIOSSIZE, "AIOSSIZE"},
+ {AIOGFMT, "AIOGFMT"},
+ {AIOSFMT, "AIOSFMT"},
+ {AIOGMIX, "AIOGMIX"},
+ {AIOSMIX, "AIOSMIX"},
+ {AIOSTOP, "AIOSTOP"},
+ {AIOSYNC, "AIOSYNC"},
+ {AIOGCAP, "AIOGCAP"},
+ {-1, NULL},
+};
+
+/*
+ * This is the generic init routine.
+ * Must be called after device-specific init.
+ */
+int
+midiinit(mididev_info *d, device_t dev)
+{
+ int unit;
+
+ /*
+ * initialize standard parameters for the device. This can be
+ * overridden by device-specific configurations but better do
+ * here the generic things.
+ */
+
+ MIDI_DEBUG(printf("midiinit: unit %d.\n", d->unit));
+
+ unit = d->unit;
+ d->softc = device_get_softc(dev);
+ d->dev = dev;
+ d->magic = MAGIC(d->unit); /* debugging... */
+ d->flags = 0;
+ d->fflags = 0;
+ d->midi_dbuf_in.unit_size = 1;
+ d->midi_dbuf_out.unit_size = 1;
+ d->midi_dbuf_passthru.unit_size = 1;
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ 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");
+ }
+ make_dev(&midi_cdevsw, MIDIMKMINOR(unit, MIDI_DEV_MIDIN),
+ UID_ROOT, GID_WHEEL, 0666, "midi%d", unit);
+
+ 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;
+
+ if (MIDIDEV(i_dev) != MIDI_DEV_MIDIN)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u;
+
+ return get_mididev_info_unit(u);
+}
+
+/*
+ * a small utility function which, given a unit number, returns
+ * a pointer to the associated mididev_info struct.
+ */
+mididev_info *
+get_mididev_info_unit(int unit)
+{
+ mididev_info *md;
+
+ /* XXX */
+ if (!midiinfo_mtx_init) {
+ midiinfo_mtx_init = 1;
+ mtx_init(&midiinfo_mtx, "midinf", NULL, MTX_DEF);
+ TAILQ_INIT(&midi_info);
+ }
+
+ mtx_lock(&midiinfo_mtx);
+ TAILQ_FOREACH(md, &midi_info, md_link) {
+ if (md->unit == unit)
+ break;
+ }
+ mtx_unlock(&midiinfo_mtx);
+
+ return md;
+}
+
+/*
+ * a small utility function which, given a unit number, returns
+ * a pointer to the associated mididev_info struct with MDT_MIDI.
+ */
+mididev_info *
+get_mididev_midi_unit(int unit)
+{
+ mididev_info *md;
+
+ /* XXX */
+ if (!midiinfo_mtx_init) {
+ midiinfo_mtx_init = 1;
+ mtx_init(&midiinfo_mtx, "midinf", NULL, MTX_DEF);
+ TAILQ_INIT(&midi_info);
+ }
+
+ mtx_lock(&midiinfo_mtx);
+ TAILQ_FOREACH(md, &midi_info, md_link) {
+ if (md->midiunit == unit)
+ break;
+ }
+ mtx_unlock(&midiinfo_mtx);
+
+ return md;
+}
+
+/*
+ * a small utility function which, given a unit number, returns
+ * a pointer to the associated mididev_info struct with MDT_SYNTH.
+ */
+mididev_info *
+get_mididev_synth_unit(int unit)
+{
+ mididev_info *md;
+
+ /* XXX */
+ if (!midiinfo_mtx_init) {
+ midiinfo_mtx_init = 1;
+ mtx_init(&midiinfo_mtx, "midinf", NULL, MTX_DEF);
+ TAILQ_INIT(&midi_info);
+ }
+
+ mtx_lock(&midiinfo_mtx);
+ TAILQ_FOREACH(md, &midi_info, md_link) {
+ if (md->synthunit == unit)
+ break;
+ }
+ mtx_unlock(&midiinfo_mtx);
+
+ return md;
+}
+
+/* Create a new midi device info structure. */
+/* TODO: lock md, then exit. */
+mididev_info *
+create_mididev_info_unit(int type, mididev_info *mdinf, synthdev_info *syninf)
+{
+ int unit;
+ mididev_info *md, *mdnew;
+
+ /* XXX */
+ if (!midiinfo_mtx_init) {
+ midiinfo_mtx_init = 1;
+ mtx_init(&midiinfo_mtx, "midinf", NULL, MTX_DEF);
+ TAILQ_INIT(&midi_info);
+ }
+
+ /* As malloc(9) might block, allocate mididev_info now. */
+ mdnew = malloc(sizeof(mididev_info), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (mdnew == NULL)
+ return NULL;
+ bcopy(mdinf, mdnew, sizeof(mididev_info));
+ bcopy(syninf, &mdnew->synth, sizeof(synthdev_info));
+ midibuf_init(&mdnew->midi_dbuf_in);
+ midibuf_init(&mdnew->midi_dbuf_out);
+ midibuf_init(&mdnew->midi_dbuf_passthru);
+ mtx_init(&mdnew->flagqueue_mtx, "midflq", NULL, MTX_DEF);
+ mtx_init(&mdnew->synth.vc_mtx, "synsvc", NULL, MTX_DEF);
+ mtx_init(&mdnew->synth.status_mtx, "synsst", NULL, MTX_DEF);
+
+ mtx_lock(&midiinfo_mtx);
+
+ switch (type) {
+ case MDT_MIDI:
+ mdnew->midiunit = nmidi;
+ mdnew->synthunit = nmidi;
+ nmidi++;
+ break;
+ case MDT_SYNTH:
+ mdnew->midiunit = -1;
+ mdnew->synthunit = nsynth;
+ nsynth++;
+ break;
+ default:
+ mtx_unlock(&midiinfo_mtx);
+ midibuf_destroy(&mdnew->midi_dbuf_in);
+ midibuf_destroy(&mdnew->midi_dbuf_out);
+ midibuf_destroy(&mdnew->midi_dbuf_passthru);
+ mtx_destroy(&mdnew->flagqueue_mtx);
+ mtx_destroy(&mdnew->synth.vc_mtx);
+ mtx_destroy(&mdnew->synth.status_mtx);
+ free(mdnew, M_DEVBUF);
+ panic("unsupported device type");
+ return NULL;
+ }
+ mdnew->mdtype = type;
+
+ for (unit = 0 ; ; unit++) {
+ TAILQ_FOREACH(md, &midi_info, md_link) {
+ if (md->unit == unit)
+ break;
+ }
+ if (md == NULL)
+ break;
+ }
+
+ mdnew->unit = unit;
+ mtx_lock(&mdnew->flagqueue_mtx);
+ TAILQ_INSERT_TAIL(&midi_info, mdnew, md_link);
+
+ mtx_unlock(&midiinfo_mtx);
+
+ return mdnew;
+}
+
+/* Return the number of configured devices. */
+int
+mididev_info_number(void)
+{
+ return nmidi + nsynth;
+}
+
+/* Return the number of configured midi devices. */
+int
+mididev_midi_number(void)
+{
+ return nmidi;
+}
+
+/* Return the number of configured synth devices. */
+int
+mididev_synth_number(void)
+{
+ return nsynth;
+}
+
+/*
+ * 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 thread *td)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_open(i_dev, flags, mode, td);
+ break;
+ case MIDI_DEV_STATUS:
+ ret = midistat_open(i_dev, flags, mode, td);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+midiclose(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_close(i_dev, flags, mode, td);
+ break;
+ case MIDI_DEV_STATUS:
+ ret = midistat_close(i_dev, flags, mode, td);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+midiread(dev_t i_dev, struct uio * buf, int flag)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_read(i_dev, buf, flag);
+ break;
+ case MIDI_DEV_STATUS:
+ ret = midistat_read(i_dev, buf, flag);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+midiwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_write(i_dev, buf, flag);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+midiioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_ioctl(i_dev, cmd, arg, mode, td);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+midipoll(dev_t i_dev, int events, struct thread *td)
+{
+ int ret;
+
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ ret = midi_poll(i_dev, events, td);
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * Followings are the generic methods in midi drivers.
+ */
+
+int
+midi_open(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ int dev, unit, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ MIDI_DEBUG(printf("midi_open: unit %d, flags 0x%x.\n", unit, flags));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ /* Mark this device busy. */
+ mtx_lock(&d->flagqueue_mtx);
+ device_busy(d->dev);
+ if ((d->flags & MIDI_F_BUSY) != 0) {
+ mtx_unlock(&d->flagqueue_mtx);
+ printf("midi_open: unit %d is busy.\n", unit);
+ return (EBUSY);
+ }
+ d->fflags = flags;
+ d->flags |= MIDI_F_BUSY;
+ d->flags &= ~(MIDI_F_READING | MIDI_F_WRITING);
+ if ((d->fflags & O_NONBLOCK) != 0)
+ d->flags |= MIDI_F_NBIO;
+
+ /* Init the queue. */
+ if ((flags & FREAD) != 0)
+ midibuf_clear(&d->midi_dbuf_in);
+ if ((flags & FWRITE) != 0) {
+ midibuf_clear(&d->midi_dbuf_out);
+ midibuf_clear(&d->midi_dbuf_passthru);
+ }
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ if (d->open == NULL)
+ ret = 0;
+ else
+ ret = d->open(i_dev, flags, mode, td);
+
+ mtx_lock(&d->flagqueue_mtx);
+
+ /* Begin recording if nonblocking. */
+ if ((d->flags & (MIDI_F_READING | MIDI_F_NBIO)) == MIDI_F_NBIO && (d->fflags & FREAD) != 0)
+ d->callback(d, MIDI_CB_START | MIDI_CB_RD);
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ MIDI_DEBUG(printf("midi_open: opened.\n"));
+
+ return (ret);
+}
+
+int
+midi_close(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ int dev, unit, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ MIDI_DEBUG(printf("midi_close: unit %d.\n", unit));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ mtx_lock(&d->flagqueue_mtx);
+
+ /* Stop recording and playing. */
+ if ((d->flags & MIDI_F_READING) != 0)
+ d->callback(d, MIDI_CB_ABORT | MIDI_CB_RD);
+ if ((d->flags & MIDI_F_WRITING) != 0)
+ d->callback(d, MIDI_CB_ABORT | MIDI_CB_WR);
+
+ /* Clear the queues. */
+ if ((d->fflags & FREAD) != 0)
+ midibuf_clear(&d->midi_dbuf_in);
+ if ((d->fflags & FWRITE) != 0) {
+ midibuf_clear(&d->midi_dbuf_out);
+ midibuf_clear(&d->midi_dbuf_passthru);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ d->flags &= ~MIDI_F_BUSY;
+ d->fflags = 0;
+
+ device_unbusy(d->dev);
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ if (d->close == NULL)
+ ret = 0;
+ else
+ ret = d->close(i_dev, flags, mode, td);
+
+ MIDI_DEBUG(printf("midi_close: closed.\n"));
+
+ return (ret);
+}
+
+int
+midi_read(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, len, lenr, ret;
+ mididev_info *d ;
+ u_char *uiobuf;
+
+ dev = minor(i_dev);
+
+ d = get_mididev_info(i_dev, &unit);
+ MIDI_DEBUG(printf("midi_read: unit %d, resid %d.\n", unit, buf->uio_resid));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+
+ len = buf->uio_resid;
+ lenr = 0;
+
+ uiobuf = (u_char *)malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (uiobuf == NULL)
+ return (ENOMEM);
+
+ mtx_lock(&d->flagqueue_mtx);
+
+ /* 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 {
+ if ((d->flags & MIDI_F_NBIO) != 0 && len > d->midi_dbuf_in.rl)
+ len = d->midi_dbuf_in.rl;
+ ret = midibuf_seqread(&d->midi_dbuf_in, uiobuf, len, &lenr,
+ d->callback, d, MIDI_CB_START | MIDI_CB_RD,
+ &d->flagqueue_mtx);
+ }
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ if (lenr > 0)
+ ret = uiomove(uiobuf, lenr, buf);
+
+ free(uiobuf, M_DEVBUF);
+
+ MIDI_DEBUG(printf("midi_read: ret %d, resid %d.\n", ret, buf->uio_resid));
+
+ return (ret);
+}
+
+int
+midi_write(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, len, len2, lenw, ret;
+ mididev_info *d;
+ u_char *uiobuf;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ MIDI_DEBUG(printf("midi_write: unit %d.\n", unit));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+
+ len = buf->uio_resid;
+ lenw = 0;
+
+ uiobuf = (u_char *)malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (uiobuf == NULL)
+ return (ENOMEM);
+
+ ret = uiomove(uiobuf, len, buf);
+ if (ret != 0) {
+ free(uiobuf, M_DEVBUF);
+ return (ret);
+ }
+
+ mtx_lock(&d->flagqueue_mtx);
+
+ /* Have we got the data to write? */
+ if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_out.fl == 0) {
+ /* Begin playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+ ret = EAGAIN;
+ } else {
+ len2 = len;
+ if ((d->flags & MIDI_F_NBIO) != 0 && len2 > d->midi_dbuf_out.fl)
+ len2 = d->midi_dbuf_out.fl;
+ ret = midibuf_seqwrite(&d->midi_dbuf_out, uiobuf, len2, &lenw,
+ d->callback, d, MIDI_CB_START | MIDI_CB_WR,
+ &d->flagqueue_mtx);
+ }
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ free(uiobuf, M_DEVBUF);
+ buf->uio_resid = len - lenw;
+
+ 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 thread *td)
+{
+ int ret = ENOSYS, dev, unit;
+ mididev_info *d;
+ struct snd_size *sndsize;
+ snd_sync_parm *sp;
+
+ 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, td);
+ if (ret != ENOSYS)
+ return ret;
+
+ /*
+ * pass control to the default ioctl handler. Set ret to 0 now.
+ */
+ ret = 0;
+
+ MIDI_DEBUG(printf("midi_ioctl: unit %d, cmd %s.\n", unit, midi_cmdname(cmd, cmdtab_midiioctl)));
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+ mtx_lock(&d->flagqueue_mtx);
+ *(int *)arg = d->midi_dbuf_out.fl;
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: fl %d.\n", *(int *)arg));
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ MIDI_DEBUG(printf("midi_ioctl: play %d, rec %d.\n", sndsize->play_size, sndsize->rec_size));
+ mtx_lock(&d->flagqueue_mtx);
+ if (sndsize->play_size <= d->midi_dbuf_out.unit_size && sndsize->rec_size <= d->midi_dbuf_in.unit_size) {
+ d->midi_dbuf_out.blocksize = d->midi_dbuf_out.unit_size;
+ d->midi_dbuf_in.blocksize = d->midi_dbuf_in.unit_size;
+ sndsize->play_size = d->midi_dbuf_out.blocksize;
+ sndsize->rec_size = d->midi_dbuf_in.blocksize;
+ d->flags &= ~MIDI_F_HAS_SIZE;
+ mtx_unlock(&d->flagqueue_mtx);
+ }
+ 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;
+ sndsize->play_size = d->midi_dbuf_out.blocksize;
+ sndsize->rec_size = d->midi_dbuf_in.blocksize;
+ d->flags |= MIDI_F_HAS_SIZE;
+ mtx_unlock(&d->flagqueue_mtx);
+ }
+
+ ret = 0;
+ break;
+
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ mtx_lock(&d->flagqueue_mtx);
+ sndsize->play_size = d->midi_dbuf_out.blocksize;
+ sndsize->rec_size = d->midi_dbuf_in.blocksize;
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: play %d, rec %d.\n", sndsize->play_size, sndsize->rec_size));
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ mtx_lock(&d->flagqueue_mtx);
+ 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 {
+ MIDI_DEBUG(printf("midi_ioctl: bad channel 0x%x.\n", *(int *)arg));
+ *(int *)arg = 0 ;
+ }
+ mtx_unlock(&d->flagqueue_mtx);
+ break ;
+
+ case AIOSYNC:
+ sp = (snd_sync_parm *)arg;
+ MIDI_DEBUG(printf("midi_ioctl: unimplemented, chan 0x%03lx pos %lu.\n",
+ sp->chan,
+ sp->pos));
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+ mtx_lock(&d->flagqueue_mtx);
+ *(int *)arg = d->midi_dbuf_in.rl;
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: rl %d.\n", *(int *)arg));
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ MIDI_DEBUG(printf("FIOASYNC\n"));
+ break;
+
+ case FIONBIO: /* set/clear non-blocking i/o */
+ mtx_lock(&d->flagqueue_mtx);
+ if (*(int *)arg == 0)
+ d->flags &= ~MIDI_F_NBIO ;
+ else
+ d->flags |= MIDI_F_NBIO ;
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: arg %d.\n", *(int *)arg));
+ break ;
+
+ case MIOSPASSTHRU: /* set/clear passthru */
+ mtx_lock(&d->flagqueue_mtx);
+ if (*(int *)arg == 0)
+ d->flags &= ~MIDI_F_PASSTHRU ;
+ else
+ d->flags |= MIDI_F_PASSTHRU ;
+
+ /* Init the queue. */
+ midibuf_clear(&d->midi_dbuf_passthru);
+
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: passthru %d.\n", *(int *)arg));
+
+ /* FALLTHROUGH */
+ case MIOGPASSTHRU: /* get passthru */
+ mtx_lock(&d->flagqueue_mtx);
+ if ((d->flags & MIDI_F_PASSTHRU) != 0)
+ *(int *)arg = 1;
+ else
+ *(int *)arg = 0;
+ mtx_unlock(&d->flagqueue_mtx);
+ MIDI_DEBUG(printf("midi_ioctl: passthru %d.\n", *(int *)arg));
+ break;
+
+ default:
+ MIDI_DEBUG(printf("midi_ioctl: default ioctl midi%d subdev %d fn 0x%08lx fail\n",
+ unit, dev & 0xf, cmd));
+ ret = EINVAL;
+ break ;
+ }
+ return ret ;
+}
+
+int
+midi_poll(dev_t i_dev, int events, struct thread *td)
+{
+ int unit, dev, ret, lim;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ MIDI_DEBUG(printf("midi_poll: unit %d.\n", unit));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+
+ mtx_lock(&d->flagqueue_mtx);
+
+ /* 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(td, &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(td, &d->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+
+ mtx_unlock(&d->flagqueue_mtx);
+
+ return (ret);
+}
+
+void
+midi_intr(mididev_info *d)
+{
+ if (d->intr != NULL)
+ d->intr(d->intrarg, d);
+}
+
+/* Flush the output queue. */
+#define MIDI_SYNC_TIMEOUT 1
+int
+midi_sync(mididev_info *d)
+{
+ int i, rl;
+
+ mtx_assert(&d->flagqueue_mtx, MA_OWNED);
+
+ MIDI_DEBUG(printf("midi_sync: unit %d.\n", d->unit));
+
+ while (d->midi_dbuf_out.rl > 0) {
+ if ((d->flags & MIDI_F_WRITING) == 0)
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+ rl = d->midi_dbuf_out.rl;
+ i = cv_timedwait_sig(&d->midi_dbuf_out.cv_out,
+ &d->flagqueue_mtx,
+ (d->midi_dbuf_out.bufsize * 10 * hz / 38400)
+ + MIDI_SYNC_TIMEOUT * hz);
+ if (i == EINTR || i == ERESTART) {
+ if (i == EINTR)
+ d->callback(d, MIDI_CB_STOP | MIDI_CB_WR);
+ return (i);
+ }
+ if (i == EWOULDBLOCK && rl == d->midi_dbuf_out.rl) {
+ /* A queue seems to be stuck up. Give up and clear the queue. */
+ d->callback(d, MIDI_CB_STOP | MIDI_CB_WR);
+ midibuf_clear(&d->midi_dbuf_out);
+ return (0);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * These handle the status message of the midi drivers.
+ */
+
+int
+midistat_open(dev_t i_dev, int flags, int mode, struct thread *td)
+{
+ 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 thread *td)
+{
+ 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 < mididev_info_number() ; i++) {
+ md = get_mididev_info_unit(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 len;
+
+ len = min(uio->uio_resid, strlen(&buf[*ptr]));
+ if (len > 0) {
+ uiomove(&buf[*ptr], len, uio);
+ *ptr += len;
+ }
+
+ return (0);
+}
+
+char
+*midi_cmdname(int cmd, midi_cmdtab *tab)
+{
+ while (tab->name != NULL) {
+ if (cmd == tab->cmd)
+ return (tab->name);
+ tab++;
+ }
+
+ return ("unknown");
+}
OpenPOWER on IntegriCloud