summaryrefslogtreecommitdiff
path: root/kernel/framework/midi/oss_midi_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/midi/oss_midi_core.c')
-rw-r--r--kernel/framework/midi/oss_midi_core.c1733
1 files changed, 1733 insertions, 0 deletions
diff --git a/kernel/framework/midi/oss_midi_core.c b/kernel/framework/midi/oss_midi_core.c
new file mode 100644
index 0000000..fdca69b
--- /dev/null
+++ b/kernel/framework/midi/oss_midi_core.c
@@ -0,0 +1,1733 @@
+/*
+ * Purpose: MIDI support.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_config.h"
+#include "midi_core.h"
+
+#define MDB(x)
+
+oss_mutex_t midi_mutex;
+static oss_device_t *osscore_osdev = NULL;
+
+/*
+ * List of MIDI devices.
+ */
+mididev_t **midi_devs = NULL;
+int num_mididevs = 0;
+
+/*
+ * List of MIDI clients (/dev/midi* device files).
+ */
+int oss_num_midi_clients = 0;
+oss_midi_client_t *oss_midi_clients[MAX_MIDI_CLIENTS] = { NULL };
+
+static int
+queue_midi_input (mididev_t * mididev, unsigned char *data, int len,
+ oss_native_word * wait_flags)
+{
+ int ret;
+
+ midi_packet_header_t hdr, *hdrp = NULL;
+
+ if (mididev->working_mode != MIDI_MODE_TRADITIONAL)
+ {
+ int use_abs = 0;
+
+ hdrp = &hdr;
+
+ memset (&hdr, 0, sizeof (hdr));
+ hdr.magic = MIDI_HDR_MAGIC;
+ hdr.event_type = MIDI_EV_WRITE;
+ hdr.options |= MIDI_OPT_TIMED;
+
+ if (mididev->working_mode == MIDI_MODE_TIMED_ABS)
+ {
+ use_abs = 1;
+ hdr.options |= MIDI_OPT_USECTIME;
+ }
+
+ hdr.time =
+ oss_timer_get_current_time (mididev->timer_dev, mididev->latency,
+ use_abs);
+ }
+
+ if ((ret = midi_queue_put (mididev->in_queue, data, len, hdrp)) < 0)
+ {
+ if (len == 1)
+ cmn_err (CE_CONT, "/dev/midi%02d: Input buffer overflow\n",
+ mididev->dev);
+ ret = 0;
+ }
+
+ oss_wakeup (mididev->in_wq, &mididev->mutex, wait_flags, POLLIN);
+
+ return ret;
+}
+
+static void
+mtc_generator (void *d)
+{
+#if 1
+ oss_native_word dev = (oss_native_word) d;
+ oss_native_word flags;
+ unsigned char data, buf[2];
+ mididev_p mididev = midi_devs[dev];
+ int t;
+ int step;
+ int h, m, s, f;
+
+ t = (int) (GET_JIFFIES () - mididev->mtc_t0);
+
+ if (mididev->mtc_timebase < 1)
+ return;
+ mididev->mtc_timeout_id =
+ timeout (mtc_generator, (void *) dev, OSS_HZ / 100);
+
+ step = OSS_HZ / (mididev->mtc_timebase * 4); /* System ticks per quarter frame */
+ if (step < 1)
+ {
+ /* System timer has too low resolution so do nothing */
+ return;
+ }
+
+ t /= step;
+ if (t <= mididev->mtc_prev_t) /* Not enough time elapsed yet (for some reason) */
+ {
+ return;
+ }
+
+ mididev->mtc_prev_t = t;
+
+ if (mididev->mtc_phase == 0)
+ mididev->mtc_current = t;
+ else
+ t = mididev->mtc_current;
+
+ t /= 4; /* Ignore quarter frames */
+ f = t % mididev->mtc_timebase;
+ t /= mididev->mtc_timebase;
+
+ s = t % 60;
+ t /= 60;
+
+ m = t % 60;
+ t /= 60;
+
+ h = t % 0x24;
+
+ data = 0x00;
+
+ switch (mididev->mtc_phase)
+ {
+ case 0:
+ data = 0x00 | (f % 0x0f);
+ break;
+ case 1:
+ data = 0x10 | ((f >> 4) % 0x0f);
+ break;
+ case 2:
+ data = 0x20 | (s % 0x0f);
+ break;
+ case 3:
+ data = 0x30 | ((s >> 4) % 0x0f);
+ break;
+ case 4:
+ data = 0x40 | (m % 0x0f);
+ break;
+ case 5:
+ data = 0x50 | ((m >> 4) % 0x0f);
+ break;
+ case 6:
+ data = 0x60 | (h % 0x0f);
+ break;
+ case 7:
+ data = 0x70 | ((h >> 4) % 0x03) | (mididev->mtc_codetype << 1);
+ break;
+ }
+
+ mididev->mtc_phase = (mididev->mtc_phase + 1) % 8;
+
+#if 0
+ cmn_err (CE_CONT, "MTC = %02d:%02d:%02d %02d.x -> %02x\n",
+ h, m, s, f, data);
+#endif
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ buf[0] = 0xf1;
+ buf[1] = data;
+ queue_midi_input (mididev, buf, 2, &flags);
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+#endif
+}
+
+int
+oss_midi_input_byte (int dev, unsigned char data)
+{
+ oss_native_word flags;
+ mididev_t *mididev;
+ int ret;
+
+ if (dev < 0 || dev >= num_mididevs)
+ cmn_err (CE_PANIC, "MIDI driver bug - bad MIDI device %d\n", dev);
+
+ mididev = midi_devs[dev];
+
+ if (data == 0xf1)
+ {
+ mididev->mtc_timebase = -1;
+ }
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ ret = queue_midi_input (mididev, &data, 1, &flags);
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ return ret;
+}
+
+int
+oss_midi_input_buf (int dev, unsigned char *data, int len)
+{
+ oss_native_word flags;
+ mididev_t *mididev;
+ int ret, n = 0;
+
+ if (dev < 0 || dev >= num_mididevs)
+ cmn_err (CE_PANIC, "MIDI driver bug - bad MIDI device %d\n", dev);
+
+ mididev = midi_devs[dev];
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+
+ while (len > 0)
+ {
+ int i, l = len;
+
+ if (l > MIDI_PAYLOAD_SIZE)
+ l = MIDI_PAYLOAD_SIZE;
+
+
+ for (i = 0; i < l; i++)
+ {
+ /* Turn off MTC generation if MTC data is received */
+ if (data[i] == 0xf1)
+ {
+ mididev->mtc_timebase = -1;
+ }
+ }
+ ret = queue_midi_input (mididev, data, l, &flags);
+ if (ret < 0)
+ {
+ ret = 0;
+ cmn_err (CE_WARN, "MIDI input bytes dropped\n");
+ }
+
+ if (ret == 0) /* Cannot queue more bytes */
+ break;
+
+ len -= ret;
+ data += ret;
+ n += ret;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ return n;
+}
+
+static void
+oss_midi_input_event (int dev, unsigned char *data, int len)
+{
+ oss_native_word flags;
+ mididev_t *mididev;
+ int ret, n = 0, i;
+
+ if (dev < 0 || dev >= num_mididevs)
+ cmn_err (CE_PANIC, "MIDI driver bug - bad MIDI device %d\n", dev);
+
+ mididev = midi_devs[dev];
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+
+ for (i = 0; i < len; i += 4)
+ {
+ int l;
+ unsigned char *d = data + i;
+
+ switch (d[0] & 0x0f)
+ {
+ case 0x0: /* End marker */
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return;
+ break;
+
+ case 0x2: /* Two byte real time messages TODO? */
+ l = 2;
+ break;
+
+ case 0x4: /* Sysex start/continuation */
+ l = 3;
+ break;
+
+ case 0x5: /* Sysex termination records */
+ case 0x6:
+ case 0x7:
+ l = (d[0] & 0x0f) - 4;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xe:
+ l = 3;
+ break;
+
+ case 0x0c:
+ case 0x0d:
+ l = 2;
+ break;
+
+ case 0xf: /* System real time messages */
+ l = 1;
+ break;
+
+ default:
+ l = 3;
+ }
+
+ if (l == 0) /* Nothing to send */
+ continue;
+
+ if (l > MIDI_PAYLOAD_SIZE)
+ l = MIDI_PAYLOAD_SIZE;
+
+ /*
+ * Ignore active sensing
+ */
+ if (d[1] == 0xfe)
+ continue;
+
+ ret = queue_midi_input (mididev, d + 1, l, &flags);
+ if (ret < 0)
+ {
+ ret = 0;
+ cmn_err (CE_WARN, "MIDI input bytes dropped\n");
+ }
+
+ if (ret == 0) /* Cannot queue more bytes */
+ break;
+
+ len -= ret;
+ data += ret;
+ n += ret;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ return;
+}
+
+static void oss_midi_callback (void *arg);
+static int setup_tempo (mididev_t * mididev);
+
+void
+oss_midi_copy_timer (int dev, int source_dev)
+{
+ mididev_t *mididev;
+ int err;
+
+ if (dev < 0 || dev >= num_mididevs)
+ return;
+ mididev = midi_devs[dev];
+
+ if (mididev->timer_dev >= 0) /* Detach the old client */
+ oss_timer_detach_client (mididev->timer_dev, mididev);
+
+ mididev->timer_dev = -1;
+
+ if (source_dev < 0 || source_dev >= num_mididevs)
+ return;
+
+ if (midi_devs[source_dev]->timer_dev < 0)
+ return;
+
+ if ((err =
+ oss_timer_attach_client (midi_devs[source_dev]->timer_dev,
+ mididev)) < 0)
+ {
+ cmn_err (CE_CONT, "Cannot attach timer %d to MIDI dev %d, err=%d\n",
+ midi_devs[source_dev]->timer_dev, mididev->dev, err);
+ return;
+ }
+
+ mididev->timer_dev = midi_devs[source_dev]->timer_dev;
+ mididev->timer_driver = midi_devs[source_dev]->timer_driver;
+}
+
+int
+oss_midi_output_intr_handler (int dev, int callback_flag)
+{
+ mididev_t *mididev;
+ oss_native_word flags;
+
+ unsigned char *buf;
+ int len;
+ midi_packet_header_t *hdr;
+ MDB (cmn_err (CE_CONT, "oss_midi_output_intr_handler()\n"));
+
+ if (dev < 0 || dev >= num_mididevs)
+ cmn_err (CE_PANIC, "oss_midi_output_intr: Bad device %d\n", dev);
+
+ mididev = midi_devs[dev];
+
+ if (callback_flag)
+ {
+ mididev->out_timeout = 0;
+ }
+
+ if (!(mididev->open_mode & OPEN_WRITE)) /* Device may have been closed */
+ {
+ return 0;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+
+ while ((len = midi_queue_find_buffer (mididev->out_queue, &buf, &hdr)) >= 0)
+/* TODO: Check time */
+ {
+ int i, n = 0;
+
+ if (!callback_flag) /* Called from write() handler */
+ if (hdr != NULL && (hdr->options & MIDI_OPT_BUSY))
+ {
+ /*
+ * This buffer is already playing
+ */
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return 0;
+ }
+
+ MDB (cmn_err (CE_CONT, "Bytes in buffer=%d\n", len));
+#if 0
+ if (buf != NULL)
+ MDB (cmn_err (CE_CONT, "First=%02x\n", buf[0]));
+#endif
+
+ if (hdr == NULL)
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return 0;
+ }
+
+ hdr->options |= MIDI_OPT_BUSY;
+ if (mididev->timer_dev >= 0)
+ if (hdr->options & MIDI_OPT_TIMED)
+ {
+ if (mididev->d->flush_output)
+ mididev->d->flush_output (mididev->dev);
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ /* TODO: Does releasing the mutex here cause any race conditions? */
+ if (oss_timer_wait_time
+ (mididev->timer_dev, mididev->dev, mididev->latency,
+ hdr->time, oss_midi_callback, hdr->options) <= 0)
+ {
+ /*
+ * Not the right time yet. Timer is armed
+ * and the callback function will be called
+ * at the right time. We don't remove the event
+ * so it can be retried later.
+ */
+ return 0;
+ }
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ }
+
+ switch (hdr->event_type)
+ {
+ case MIDI_EV_TEMPO:
+ mididev->tempo = hdr->parm;
+ setup_tempo (mididev);
+ break;
+
+ case MIDI_EV_START:
+ oss_timer_start (mididev->timer_dev);
+ if (mididev->d->timer_setup)
+ mididev->d->timer_setup (mididev->dev);
+ break;
+
+ case MIDI_EV_WRITE:
+ break;
+ }
+
+ if (mididev->d->outputc == NULL
+ || (len > 1 && mididev->d->bulk_write != NULL))
+ {
+ n = mididev->d->bulk_write (mididev->dev, buf, len);
+ if (n <= 0) /* Error */
+ {
+ MDB (cmn_err
+ (CE_NOTE, "MIDI device %d - output stalled (%d).\n",
+ mididev->dev, n));
+ n = 0;
+ }
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ MDB (cmn_err (CE_CONT, "Out %d %02x\n", mididev->dev, buf[i]));
+ if (!mididev->d->outputc (mididev->dev, buf[i]))
+ {
+ MDB (cmn_err
+ (CE_NOTE, "MIDI device %d - output stalled (%d).\n",
+ mididev->dev, n));
+ break; /* device full */
+ }
+ n++;
+ }
+ }
+
+ /* Remove the bytes sent out */
+ if (n > 0 || len == 0)
+ {
+ MDB (cmn_err (CE_CONT, "Remove %d/%d\n", n, len));
+ midi_queue_remove_chars (mididev->out_queue, n);
+ oss_wakeup (mididev->out_wq, &mididev->mutex, &flags, POLLOUT);
+ }
+
+ if (n == len)
+ {
+ if (mididev->d->flush_output)
+ mididev->d->flush_output (mididev->dev);
+ }
+
+ if (n < len)
+ {
+ /* Not everything consumed - arm a timeout */
+
+ MDB (cmn_err (CE_CONT, "Not all consumed %d/%d\n", n, len));
+ if (mididev->out_timeout == 0) /* Not armed yet */
+ {
+ oss_native_word d = dev;
+ mididev->out_timeout =
+ timeout (oss_midi_callback, (void *) d, 1);
+ }
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ return 0; /* Not all buffered bytes were sent */
+ }
+ MDB (cmn_err (CE_CONT, "Everything consumed\n"));
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ return 1; /* Everything was done */
+}
+
+int
+oss_midi_output_intr (int dev)
+{
+ MDB (cmn_err (CE_CONT, "oss_midi_output_intr\n"));
+ return oss_midi_output_intr_handler (dev, 0);
+}
+
+static void
+oss_midi_callback (void *arg)
+{
+ int dev;
+
+ dev = (oss_native_word) arg;
+
+ MDB (cmn_err (CE_CONT, "MIDI timer callback\n"));
+ oss_midi_output_intr_handler (dev, 1);
+}
+
+void
+oss_midi_set_defaults (mididev_t * mididev)
+{
+ mididev->prech_timeout = 0; /* Infinite wait */
+
+ mididev->event_input = oss_midi_input_event;
+ mididev->tempo = 120;
+ mididev->timebase = OSS_HZ;
+}
+
+static void put_output (mididev_t * mididev, unsigned char *buf, int len);
+
+int
+oss_midi_open (int dev, int no_worries, struct fileinfo *file, int recursive,
+ int open_flags, int *newdev)
+{
+/*
+ * oss_midi_open opens a MIDI client (/dev/midi##) and binds it directly to
+ * the MIDI port with the same number (dev).
+ */
+ oss_native_word flags;
+ oss_midi_client_t *client;
+ int ok, err = OSS_EBUSY;
+ int mode = file->mode & O_ACCMODE;
+
+ char *cmd;
+
+ if (dev < 0 || dev >= num_mididevs)
+ return OSS_ENXIO;
+
+ /*
+ * Don't allow read only access on playback only devices.
+ * However R/W access should be allowed so that the application can be
+ * informed about (possible) status changes.
+ */
+ if ((mode == OPEN_READ) && !(midi_devs[dev]->flags & MFLAG_INPUT))
+ {
+ return OSS_EACCES;
+ }
+
+ /*
+ * Input only devices cannot do output so don't allow it.
+ */
+ if ((mode & OPEN_WRITE) && !(midi_devs[dev]->flags & MFLAG_OUTPUT))
+ {
+ return OSS_EACCES;
+ }
+
+ if (!midi_devs[dev]->enabled)
+ return OSS_ENXIO;
+
+ if (midi_devs[dev]->unloaded)
+ return OSS_ENXIO;
+
+ client = oss_midi_clients[dev];
+ if (client == NULL)
+ return OSS_ENXIO;
+
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+
+ ok = 1;
+ if (client->open_mode != 0) /* Client busy */
+ ok = 0;
+
+ if (midi_devs[dev]->open_mode != 0) /* Device busy */
+ ok = 0;
+
+ if (ok) /* Still OK */
+ {
+ client->mididev = midi_devs[dev];
+ if (client->mididev == NULL)
+ {
+ cmn_err(CE_WARN, "client->mididev == NULL\n");
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ return OSS_EIO;
+ }
+
+ client->open_mode = mode;
+ midi_devs[dev]->open_mode = mode;
+
+ if ((cmd = GET_PROCESS_NAME (file)) != NULL)
+ strncpy (client->mididev->cmd, cmd, 15);
+ client->mididev->pid = GET_PROCESS_PID (file);
+
+ /* Release locks before calling device open method */
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ if ((err = midi_devs[dev]->d->open (dev, mode,
+ oss_midi_input_byte,
+ oss_midi_input_buf,
+ oss_midi_output_intr)) < 0)
+ ok = 0;
+ /* Reacquire locks */
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+ if (!ok)
+ {
+ client->open_mode = 0;
+ midi_devs[dev]->open_mode = 0;
+ }
+ }
+
+ if (!ok)
+ {
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ return err;
+ }
+
+ oss_midi_set_defaults (client->mididev);
+
+ client->mididev->is_timing_master = 0;
+
+ client->num = dev;
+
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+
+ if (mode & OPEN_READ)
+ {
+ char name[16];
+ sprintf (name, "read%d", dev);
+ client->mididev->in_queue =
+ midi_queue_alloc (client->mididev->osdev, name);
+ if (client->mididev->in_queue == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to allocate MIDI input queue\n");
+ midi_devs[dev]->open_mode = 0;
+ client->open_mode = 0;
+ client->mididev = NULL;
+ return OSS_ENOMEM;
+ }
+ }
+
+ if (mode & OPEN_WRITE)
+ {
+ char name[16];
+ int i;
+ unsigned char tmpbuf[4];
+ sprintf (name, "write%d", dev);
+ client->mididev->out_queue =
+ midi_queue_alloc (client->mididev->osdev, name);
+/* if (dev==0)midi_queue_debugging(client->mididev->out_queue); */
+ if (client->mididev->out_queue == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to allocate MIDI output queue\n");
+ midi_devs[dev]->open_mode = 0;
+ client->open_mode = 0;
+ client->mididev = NULL;
+ return OSS_ENOMEM;
+ }
+
+/*
+ * Reset all MIDI controllers to their default values
+ */
+ if (!(client->mididev->flags & MFLAG_QUIET))
+ for (i = 0; i < 16; i++)
+ {
+ tmpbuf[0] = 0xb0 | i; /* Control change */
+ tmpbuf[1] = 121; /* Reset all controllers */
+ tmpbuf[2] = 127; /* Reset vol/exp/pan too */
+ put_output (client->mididev, tmpbuf, 3);
+ }
+ if (client->mididev->d->flush_output)
+ client->mididev->d->flush_output (client->mididev->dev);
+ }
+
+ if (client->mididev->d->init_device)
+ client->mididev->d->init_device (dev);
+
+ return 0;
+}
+
+int
+oss_vmidi_open (int dev, int dev_type, struct fileinfo *file, int recursive,
+ int open_flags, int *newdev);
+
+static void
+sync_output (mididev_t * mididev)
+{
+ int n = 0;
+ unsigned int status;
+ oss_native_word flags;
+
+ if (mididev->is_killed)
+ {
+ midi_queue_removeall (mididev->out_queue);
+ return;
+ }
+
+ if (mididev->timer_dev < 0 || !oss_timer_is_running (mididev->timer_dev))
+ {
+ midi_queue_removeall (mididev->out_queue);
+ return;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ if (mididev->d->flush_output)
+ mididev->d->flush_output (mididev->dev);
+
+ while (!midi_queue_isempty (mididev->out_queue))
+ {
+ int tmout;
+
+ if (n++ > 100)
+ {
+ cmn_err (CE_NOTE, "MIDI output didn't get drained (sync).\n");
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return;
+ }
+ tmout = oss_timer_get_timeout (mididev->timer_dev, mididev->dev);
+ if (oss_sleep (mididev->out_wq, &mididev->mutex,
+ tmout, &flags, &status))
+ n = 0; /* Not timeout */
+ if (status & WK_SIGNAL)
+ {
+ mididev->is_killed = 1;
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return;
+ }
+ }
+
+ n = 0;
+ if (mididev->d->wait_output)
+ while (mididev->d->wait_output (mididev->dev))
+ {
+ MDB (cmn_err (CE_CONT, "Wait output\n"));
+ if (n++ > 10)
+ {
+ cmn_err (CE_NOTE, "MIDI output doesn't get emptied (sync).\n");
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return;
+ }
+ oss_sleep (mididev->out_wq, &mididev->mutex, OSS_HZ, &flags, &status);
+ if (status & WK_SIGNAL)
+ {
+ mididev->is_killed = 1;
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return;
+ }
+ }
+/*
+ * Give the device few moments of extra time to flush all buffers.
+ */
+ oss_sleep (mididev->out_wq, &mididev->mutex, OSS_HZ / 2, &flags, &status);
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+}
+
+static void
+put_output (mididev_t * mididev, unsigned char *buf, int len)
+{
+ int i, n;
+ int tmout = 1000; /* Spend here at most 1000 msec */
+
+ if (mididev->d->outputc == NULL
+ || (len > 1 && mididev->d->bulk_write != NULL))
+ {
+ int p = 0;
+
+ while (p < len && tmout-- > 0)
+ {
+ n = mididev->d->bulk_write (mididev->dev, buf + p, len - p);
+ if (n < 0) /* Error */
+ {
+ return;
+ }
+
+ if (n < (len - p)) /* Not all consumed yet */
+ oss_udelay (1000);
+
+ p += n;
+ }
+
+ return;
+ }
+
+ if (mididev->d->outputc)
+ for (i = 0; i < len; i++)
+ {
+ while (!mididev->d->outputc (mididev->dev, buf[i]) && tmout-- > 0)
+ {
+ oss_udelay (1000);
+ }
+ }
+}
+
+void
+oss_midi_release (int dev, struct fileinfo *file)
+{
+ oss_native_word flags;
+ oss_midi_client_t *client;
+ int midi_dev;
+ mididev_t *mididev;
+ int mode;
+
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+ mode = file->mode & O_ACCMODE;
+ client = oss_midi_clients[dev];
+
+ if (client->mididev == NULL) /* Not bound to a port device */
+ {
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ return;
+ }
+
+ mididev = client->mididev;
+ midi_dev = mididev->dev;
+
+ untimeout (mididev->mtc_timeout_id);
+
+ client->open_mode = 0;
+
+ if (mididev->open_mode & OPEN_WRITE) /* Needs to sync */
+ {
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ sync_output (mididev);
+
+ if (mididev->is_timing_master)
+ oss_timer_stop (mididev->timer_dev);
+ mididev->is_killed = 0; /* Too young to die */
+
+ /* Shut up possible hanging notes (if any) */
+ if (!(mididev->flags & MFLAG_QUIET))
+ {
+ unsigned char tmpbuf[4];
+ int i;
+
+ /* Sending one active sensing message should stop most devices */
+ tmpbuf[0] = 0xfe;
+ put_output (mididev, tmpbuf, 1);
+
+#if 1
+ /*
+ * To make sure we will also send a "all notes off" message to
+ * all MIDI channels.
+ */
+ for (i = 0; i < 16; i++)
+ {
+ tmpbuf[0] = 0xb0 | i; /* Control change */
+ tmpbuf[1] = 123; /* All notes off */
+ tmpbuf[2] = 0;
+ put_output (mididev, tmpbuf, 3);
+ }
+#endif
+ }
+
+ sync_output (mididev);
+
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+ }
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+
+ mididev->d->ioctl (midi_dev, SNDCTL_SETSONG, (ioctl_arg) "");
+ mididev->d->close (midi_dev, mode);
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+ mididev->open_mode &= ~mode;
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+
+ if (mididev->timer_dev >= 0)
+ oss_timer_detach_client (mididev->timer_dev, mididev);
+ mididev->timer_dev = -1;
+ mididev->timer_driver = 0;
+
+ if (mididev->in_queue != NULL)
+ midi_queue_free (mididev->in_queue);
+ mididev->in_queue = NULL;
+ mididev->pid = -1;
+ mididev->cmd[0] = 0;
+
+ if (mididev->out_queue != NULL)
+ midi_queue_free (mididev->out_queue);
+ mididev->out_queue = NULL;
+
+ client->mididev = NULL;
+
+ if (mididev->out_timeout != 0)
+ untimeout (mididev->out_timeout);
+ mididev->out_timeout = 0;
+}
+
+int
+oss_midi_write (int dev, struct fileinfo *file, uio_t * buf, int count)
+{
+ oss_midi_client_t *client = oss_midi_clients[dev];
+ mididev_t *mididev;
+ int ret;
+ unsigned int status;
+ int c = 0, l, loops;
+ oss_native_word flags;
+ midi_packet_header_t hdr;
+ int save_header = 0;
+ int tmout = OSS_HZ;
+
+ unsigned char *targetbuf;
+
+ MDB (cmn_err (CE_CONT, "MIDI write %d\n", count));
+ if (client->mididev == NULL)
+ if ((ret = midi_mapper_autobind (dev, client->open_mode)) < 0)
+ return ret;
+ mididev = client->mididev;
+
+ if (mididev == NULL)
+ return OSS_EBUSY;
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+
+ /*
+ * Since the application called write again we can ignore possible
+ * earlier received kill signals.
+ */
+ mididev->is_killed = 0;
+
+ memset (&hdr, 0, sizeof (hdr));
+ if (mididev->working_mode != MIDI_MODE_TRADITIONAL)
+ {
+ if (count < sizeof (midi_packet_header_t))
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ cmn_err (CE_WARN, "Too short MIDI write (no header)\n");
+ return OSS_EINVAL;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ if (uiomove (&hdr, sizeof (midi_packet_header_t), UIO_WRITE, buf) != 0)
+ {
+ cmn_err (CE_WARN, "uiomove (header) failed\n");
+ return OSS_EFAULT;
+ }
+
+ if (hdr.magic != MIDI_HDR_MAGIC)
+ {
+ cmn_err (CE_WARN, "Bad MIDI write packet header (%04x)\n",
+ hdr.magic);
+ return OSS_EINVAL;
+ }
+ count -= sizeof (midi_packet_header_t);
+ c += sizeof (midi_packet_header_t);
+
+ /* Force save of the header if no other data was written */
+ if (count <= 0)
+ save_header = 1;
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ }
+
+ loops = 0;
+ while (save_header || count > 0)
+ {
+ MDB (cmn_err (CE_CONT, "Write bytes left %d\n", count));
+ l = count;
+ save_header = 0;
+
+ if (loops++ > 10)
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ cmn_err (CE_CONT, "MIDI Output buffer %d doesn't drain %d/%d\n",
+ mididev->dev, c, count);
+ return OSS_EIO;
+ }
+
+ if (l > MIDI_PAYLOAD_SIZE)
+ l = MIDI_PAYLOAD_SIZE;
+
+ if ((ret =
+ midi_queue_alloc_record (mididev->out_queue, &targetbuf, l,
+ &hdr)) < 0)
+ {
+ if (ret == OSS_ENOSPC) /* Buffers full */
+ {
+ MDB (cmn_err (CE_CONT, "*** Buffers full ***\n"));
+ tmout =
+ oss_timer_get_timeout (mididev->timer_dev, mididev->dev);
+ if (!oss_sleep
+ (mididev->out_wq, &mididev->mutex, tmout, &flags, &status))
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ if (c > 0)
+ return c;
+ return OSS_EIO; /* Timeout - why */
+ }
+ else
+ loops = 0; /* Restart loop limiter */
+
+ if (status & WK_SIGNAL)
+ {
+ mididev->is_killed = 1;
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return OSS_EINTR;
+ }
+
+ if (loops > 95)
+ cmn_err (CE_NOTE, "MIDI Output buffers full\n");
+ continue; /* Try again later */
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return ret;
+ }
+
+ if (l > ret)
+ l = ret;
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+/* cmn_err(CE_CONT, "Copy %08x (%d)\n", targetbuf, l); */
+
+ loops = 0;
+
+ if (l > 0)
+ {
+ MDB (cmn_err (CE_CONT, "Store %d bytes\n", l));
+ if (uiomove (targetbuf, l, UIO_WRITE, buf) != 0)
+ {
+ cmn_err (CE_WARN, "uiomove (write) failed\n");
+ return OSS_EFAULT;
+ }
+
+ count -= l;
+ c += l;
+ }
+
+ MDB (cmn_err (CE_CONT, "From oss_midi_write\n"));
+ oss_midi_output_intr_handler (mididev->dev, 0);
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+
+ /* midi_queue_trace(mididev->out_queue); */
+ MDB (cmn_err (CE_CONT, "MIDI write done %d\n\n", c));
+
+ return c;
+}
+
+static int
+do_read (mididev_t * mididev, uio_t * buf, unsigned char *data, int c,
+ int max_bytes, midi_packet_header_t * hdr)
+{
+ int l = 0;
+
+ if (mididev->working_mode != MIDI_MODE_TRADITIONAL)
+ {
+ if (max_bytes <= sizeof (*hdr))
+ {
+ cmn_err (CE_WARN, "MIDI read too small for packet header\n");
+ return OSS_E2BIG;
+ }
+
+ hdr->magic = MIDI_HDR_MAGIC;
+ if (uiomove (hdr, sizeof (*hdr), UIO_READ, buf) != 0)
+ {
+ cmn_err (CE_WARN, "uiomove (read) failed\n");
+ return OSS_EFAULT;
+ }
+
+ l += sizeof (*hdr);
+ }
+
+ if (l + c > max_bytes)
+ c = max_bytes - l;
+
+ if (c <= 0)
+ return OSS_E2BIG;
+
+ if (uiomove (data, c, UIO_READ, buf) != 0)
+ {
+ cmn_err (CE_WARN, "uiomove (read) failed\n");
+ return OSS_EFAULT;
+ }
+
+ midi_queue_remove_chars (mididev->in_queue, c);
+ l += c;
+
+ return l;
+}
+
+int
+oss_midi_read (int dev, struct fileinfo *file, uio_t * buf, int count)
+{
+ oss_midi_client_t *client = oss_midi_clients[dev];
+ mididev_t *mididev;
+ oss_native_word flags;
+ unsigned char *data;
+
+ unsigned int status;
+ int c = 0;
+ midi_packet_header_t *hdr;
+
+ int err;
+
+ if (client->mididev == NULL)
+ if ((err = midi_mapper_autobind (dev, client->open_mode)) < 0)
+ return err;
+ mididev = client->mididev;
+
+ if (mididev == NULL)
+ return OSS_EBUSY;
+
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+
+ if ((c = midi_queue_find_buffer (mididev->in_queue, &data, &hdr)) < 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return c;
+ }
+
+ if (c > 0
+ || (mididev->working_mode != MIDI_MODE_TRADITIONAL && c >= 0
+ && hdr != NULL))
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return do_read (mididev, buf, data, c, count, hdr);
+ }
+
+ if (mididev->prech_timeout < 0) /* Non blocking mode */
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return OSS_EWOULDBLOCK;
+ }
+
+ if (!oss_sleep
+ (mididev->in_wq, &mididev->mutex, mididev->prech_timeout, &flags,
+ &status))
+ {
+ /* Timeout */
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return 0;
+ }
+
+ if (status & WK_SIGNAL)
+ {
+ mididev->is_killed = 1;
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return OSS_EINTR;
+ }
+
+ if ((c = midi_queue_get (mididev->in_queue, &data, count, &hdr)) < 0
+ || (c == 0 && hdr == NULL))
+ {
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ return c;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ if (c > 0)
+ return do_read (mididev, buf, data, c, count, hdr);
+
+ return c;
+}
+
+static int
+setup_working_mode (mididev_t * mididev)
+{
+ int timer_dev;
+ int err;
+
+ if (mididev->timer_dev < 0) /* No timer yet */
+ {
+ /*
+ * Setup a timer
+ */
+
+ if (mididev->timer_driver < 0
+ || mididev->timer_driver >= oss_num_timer_drivers)
+ {
+ cmn_err (CE_WARN, "Invalid MIDI timer %d selected\n",
+ mididev->timer_driver);
+ return OSS_ENXIO;
+ }
+
+ if ((timer_dev =
+ oss_timer_create_instance (mididev->timer_driver, mididev)) < 0)
+ return timer_dev;
+ mididev->is_timing_master = 1;
+
+ if ((err = oss_timer_attach_client (timer_dev, mididev)) < 0)
+ {
+ oss_timer_detach_client (timer_dev, mididev);
+ return err;
+ }
+
+ mididev->timer_dev = timer_dev;
+ if (mididev->d->timer_setup)
+ mididev->d->timer_setup (mididev->dev);
+ }
+ else
+ timer_dev = mididev->timer_dev;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers ||
+ oss_timer_devs[timer_dev] == NULL)
+ {
+ cmn_err (CE_WARN, "Failed to allocate a timer instance\n");
+ return OSS_ENXIO;
+ }
+
+
+ return 0;
+}
+
+static int
+setup_timebase (mididev_t * mididev)
+{
+ if (mididev->timer_dev < 0) /* No timer yet */
+ return 0;
+
+ return oss_timer_set_timebase (mididev->timer_dev, mididev->timebase);
+}
+
+static int
+setup_tempo (mididev_t * mididev)
+{
+ if (mididev->timer_dev < 0) /* No timer yet */
+ return 0;
+
+ return oss_timer_set_tempo (mididev->timer_dev, mididev->tempo);
+}
+
+int
+oss_midi_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, ioctl_arg arg)
+{
+ oss_midi_client_t *client = oss_midi_clients[dev];
+ mididev_t *mididev;
+ int err, val;
+ oss_native_word d;
+
+ if (client->mididev == NULL)
+ if ((err = midi_mapper_autobind (dev, client->open_mode)) < 0)
+ return err;
+ mididev = client->mididev;
+ if (mididev == NULL)
+ return OSS_EBUSY;
+
+ dev = mididev->dev;
+
+ switch (cmd)
+ {
+ case SNDCTL_MIDI_PRETIME:
+ val = *arg;
+ if (val < -1)
+ return OSS_EINVAL;
+
+ if (val != -1)
+ val = (OSS_HZ * val) / 100;
+ mididev->prech_timeout = val;
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MTCINPUT:
+ val = *arg;
+ switch (val)
+ {
+ case 0:
+ case -1:
+ break;
+ case 24:
+ mididev->mtc_codetype = 0;
+ break;
+ case 25:
+ mididev->mtc_codetype = 1;
+ break;
+ case 29:
+ mididev->mtc_codetype = 2;
+ break;
+ case 30:
+ mididev->mtc_codetype = 3;
+ break;
+ break;
+ default:
+ return OSS_EINVAL;
+ }
+ mididev->mtc_timebase = val;
+ mididev->mtc_t0 = GET_JIFFIES ();
+ mididev->mtc_current = 0;
+ mididev->mtc_prev_t = -1;
+ mididev->mtc_phase = 0;
+ d = dev;
+ if (mididev->mtc_timebase != -1)
+ mididev->mtc_timeout_id =
+ timeout (mtc_generator, (void *) d, OSS_HZ / 100);
+ return *arg = val;
+ break;
+
+ case SNDCTL_MIDI_SETMODE:
+ val = *arg;
+ if (val != MIDI_MODE_TRADITIONAL && val != MIDI_MODE_TIMED
+ && val != MIDI_MODE_TIMED_ABS)
+ return OSS_EINVAL;
+ mididev->working_mode = val;
+ *arg = val;
+ if ((err = setup_working_mode (mididev)) < 0)
+ return err;
+ if ((err = setup_tempo (mididev)) < 0)
+ return err;
+ if ((err = setup_timebase (mididev)) < 0)
+ return err;
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_TIMEBASE:
+ val = *arg;
+ if (val < 1 || val > 1000)
+ return OSS_EINVAL;
+ mididev->timebase = val;
+ return setup_timebase (mididev);
+ break;
+
+ case SNDCTL_MIDI_TEMPO:
+ val = *arg;
+ if (val < 1 || val > 200)
+ return OSS_EINVAL;
+ mididev->tempo = val;
+ return setup_tempo (mididev);
+ break;
+
+ default:
+ return mididev->d->ioctl (mididev->dev, cmd, arg);
+ }
+}
+
+#ifdef ALLOW_SELECT
+int
+oss_midi_chpoll (int dev, struct fileinfo *file, oss_poll_event_t * ev)
+{
+ short events = ev->events;
+ oss_midi_client_t *client;
+ mididev_t *mididev;
+ oss_native_word flags;
+ int err;
+
+ if (dev < 0 || dev >= oss_num_midi_clients)
+ return OSS_ENXIO;
+ client = oss_midi_clients[dev];
+ if (client->mididev == NULL)
+ if ((err = midi_mapper_autobind (dev, client->open_mode)) < 0)
+ return err;
+ mididev = client->mididev;
+ if (mididev == NULL)
+ return OSS_EIO;
+
+ if ((events & (POLLOUT | POLLWRNORM)) && (mididev->open_mode & OPEN_WRITE))
+ {
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ if (mididev->out_queue == NULL
+ || midi_queue_spaceleft (mididev->out_queue) < 10)
+ { /* No space yet */
+ oss_register_poll (mididev->out_wq, &mididev->mutex, &flags, ev);
+ }
+ else
+ {
+ ev->revents |= (POLLOUT | POLLWRNORM) & events;
+ }
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ }
+
+ if ((events & (POLLIN | POLLRDNORM)) && (mididev->open_mode & OPEN_READ))
+ {
+ MUTEX_ENTER_IRQDISABLE (mididev->mutex, flags);
+ if (mididev->in_queue == NULL || midi_queue_isempty (mididev->in_queue))
+ { /* No space yet */
+ oss_register_poll (mididev->in_wq, &mididev->mutex, &flags, ev);
+ }
+ else
+ {
+ ev->revents |= (POLLIN | POLLRDNORM) & events;
+ }
+ MUTEX_EXIT_IRQRESTORE (mididev->mutex, flags);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef VDEV_SUPPORT
+static oss_cdev_drv_t vmidi_cdev_drv = {
+ oss_vmidi_open,
+ oss_midi_release,
+ oss_midi_read,
+ oss_midi_write,
+ oss_midi_ioctl,
+#ifdef ALLOW_SELECT
+ oss_midi_chpoll
+#else
+ NULL
+#endif
+};
+#endif
+
+static oss_cdev_drv_t midi_cdev_drv = {
+ oss_midi_open,
+ oss_midi_release,
+ oss_midi_read,
+ oss_midi_write,
+ oss_midi_ioctl,
+#ifdef ALLOW_SELECT
+ oss_midi_chpoll
+#else
+ NULL
+#endif
+};
+
+int
+oss_vmidi_open (int dev, int dev_type, struct fileinfo *file, int recursive,
+ int open_flags, int *newdev)
+{
+/*
+ * oss_vmidi_open is another version of oss_midi_open. Instead of binding
+ * the client directly with the device port this routine creates just a
+ * client (device file). The binding operation is left to be done later
+ * using SNDCTL_MIDI_BIND. If no binding is made then the first
+ * read/write/ioctl/etc operation will automatically bind the client
+ * with the first available (lowest number) MIDI port.
+ *
+ * oss_vmidi_open() drives the /dev/midi device file.
+ */
+
+ oss_native_word flags;
+ oss_midi_client_t *client = NULL;
+ int d;
+ int mode = file->mode & O_ACCMODE;
+
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+
+/*
+ * Find the first midi client number that is free. Start from the client
+ * numbers after the last existing MIDI port.
+ */
+ for (dev = num_mididevs; dev < oss_num_midi_clients; dev++)
+ {
+ if (oss_midi_clients[dev] != NULL &&
+ oss_midi_clients[dev]->open_mode == 0)
+ {
+ client = oss_midi_clients[dev];
+ break;
+ }
+ }
+
+ if (client == NULL)
+ {
+ /*
+ * No earlier allocated clients were free so create a new entry.
+ * Also create an anonymous character device.
+ */
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ client = PMALLOC (osdev, sizeof (*client));
+ MUTEX_ENTER_IRQDISABLE (midi_mutex, flags);
+
+ if (oss_num_midi_clients >= MAX_MIDI_CLIENTS)
+ {
+ cmn_err (CE_WARN, "Too many MIDI clients\n");
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ return OSS_ENXIO;
+ }
+
+ if (client == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+ return OSS_ENOMEM;
+ }
+
+ memset (client, 0, sizeof (*client));
+ oss_midi_clients[dev = oss_num_midi_clients++] = client;
+
+#ifdef CONFIG_OSS_MIDI
+ MDB (cmn_err (CE_CONT, "Installing /dev/midi%02d\n", dev));
+ oss_install_chrdev (osscore_osdev, NULL, OSS_DEV_MIDI, dev,
+ &midi_cdev_drv, 0);
+#endif
+ }
+
+ client->open_mode = mode;
+ client->num = dev;
+ client->mididev = NULL; /* Not bound yet */
+ MUTEX_EXIT_IRQRESTORE (midi_mutex, flags);
+
+ if ((d = oss_find_minor (OSS_DEV_MIDI, dev)) < 0)
+ {
+ oss_midi_release (dev, file);
+ return d;
+ }
+ *newdev = d;
+
+ return dev;
+}
+
+void
+oss_midi_init (oss_device_t * osdev)
+{
+ static int already_done = 0;
+
+ if (already_done)
+ return;
+ already_done = 1;
+
+ if (sizeof (midi_packet_header_t) != 32)
+ {
+ cmn_err (CE_WARN, "sizeof(midi_packet_header_t) != 32 (%d)\n",
+ sizeof (midi_packet_header_t));
+ cmn_err (CE_CONT, "MIDI subsystem not activated\n");
+ return;
+ }
+
+ osscore_osdev = osdev;
+
+ MUTEX_INIT (osdev, midi_mutex, MH_FRAMEW);
+
+ midi_mapper_init (osdev);
+ oss_init_timers (osdev);
+ attach_oss_default_timer (osdev);
+}
+
+void
+oss_midi_uninit (void)
+{
+ int dev;
+
+ midi_mapper_uninit ();
+ detach_oss_default_timer ();
+ oss_uninit_timers ();
+
+ for (dev = 0; dev < num_mididevs; dev++)
+ {
+ oss_remove_wait_queue (midi_devs[dev]->out_wq);
+ oss_remove_wait_queue (midi_devs[dev]->in_wq);
+ MUTEX_CLEANUP (midi_devs[dev]->mutex);
+ }
+ MUTEX_CLEANUP (midi_mutex);
+}
+
+void
+install_vmidi (oss_device_t * osdev)
+{
+#ifdef CONFIG_OSS_MIDI
+#ifdef VDEV_SUPPORT
+ oss_install_chrdev (osdev, "midi", OSS_DEV_VMIDI, 0, &vmidi_cdev_drv,
+ CHDEV_VIRTUAL);
+#endif
+#endif
+}
+
+int
+oss_install_mididev (int version,
+ char *id, char *name,
+ midi_driver_t * d, int driver_size,
+ unsigned int flags, void *devc, oss_device_t * osdev)
+{
+ int curr_midi_dev;
+ mididev_t *op;
+ oss_midi_client_t *client;
+ int i;
+ int old = 0;
+
+/*
+ * Old style drivers are always input&output even they don't report that.
+ * Fix the permissions automatically.
+ */
+ if (!(flags & (MFLAG_INPUT | MFLAG_OUTPUT)))
+ flags |= MFLAG_INPUT | MFLAG_OUTPUT;
+
+ if (driver_size > sizeof (midi_driver_t))
+ driver_size = sizeof (midi_driver_t);
+
+ if (midi_devs == NULL)
+ {
+ midi_devs = PMALLOC (osdev, sizeof (mididev_p) * MAX_MIDI_DEV);
+ }
+
+ if (midi_devs == NULL)
+ {
+ cmn_err (CE_WARN, "oss_install_mididev: Out of memory\n");
+ return OSS_ENOMEM;
+ }
+
+ for (i = 0; i < num_mididevs; i++)
+ if (midi_devs[i]->unloaded && midi_devs[i]->os_id == oss_get_osid (osdev))
+ {
+ old = 1;
+ op = midi_devs[i];
+ curr_midi_dev = i;
+ MDB (cmn_err (CE_CONT, "Reincarnation of MIDI device %d\n", i));
+ break;
+ }
+
+ if (!old)
+ {
+ if (num_mididevs >= MAX_MIDI_DEV - 1)
+ {
+ cmn_err (CE_WARN, "Too many MIDI devices in the system\n");
+ return OSS_EIO;
+ }
+
+ op = midi_devs[num_mididevs] = PMALLOC (osdev, sizeof (mididev_t));
+ if (op == NULL)
+ {
+ cmn_err (CE_WARN,
+ "oss_install_mididev: Failed to allocate memory\n");
+ return OSS_ENOMEM;
+ }
+ memset (op, 0, sizeof (*op));
+ curr_midi_dev = num_mididevs++;
+ }
+
+ op->devc = devc;
+ op->dev = op->real_dev = curr_midi_dev;
+ op->pid = -1;
+ op->osdev = osdev;
+ op->os_id = oss_get_osid (osdev);
+ op->latency = -1; /* Not known */
+
+ if (!old)
+ {
+ MUTEX_INIT (op->osdev, op->mutex, MH_FRAMEW + 1);
+
+ if ((op->out_wq =
+ oss_create_wait_queue (op->osdev, "midi_out")) == NULL)
+ cmn_err (CE_PANIC, "Cannot create MIDI output wait queue\n");
+
+ if ((op->in_wq = oss_create_wait_queue (op->osdev, "midi_in")) == NULL)
+ cmn_err (CE_PANIC, "Cannot create MIDI input wait queue\n");
+
+ op->d = PMALLOC (osdev, sizeof (midi_driver_t));
+ memset (op->d, 0, sizeof (op->d));
+ memcpy (op->d, d, driver_size);
+ sprintf (op->handle, "%s-md%02d", osdev->handle, ++osdev->num_mididevs);
+ op->port_number = osdev->num_mididevs;
+ }
+
+ op->flags = flags;
+ op->enabled = 1;
+ op->unloaded = 0;
+ op->mtc_timebase = -1;
+ op->card_number = osdev->cardnum;
+ op->timer_driver = 0; /* Point to the default timer */
+ op->timer_dev = -1; /* No timer instance created yet */
+
+ strncpy (op->name, name, sizeof (op->name) - 1);
+ op->name[sizeof (op->name) - 1] = 0;
+
+ op->caps = 0;
+ if (flags & MFLAG_INPUT)
+ op->caps |= MIDI_CAP_INPUT;
+ if (flags & MFLAG_OUTPUT)
+ op->caps |= MIDI_CAP_OUTPUT;
+
+
+ if (oss_midi_clients[curr_midi_dev] == NULL)
+ {
+ client = PMALLOC (osdev, sizeof (*client));
+ if (client == NULL)
+ cmn_err (CE_PANIC, "OSS install MIDI: Out of memory.\n");
+ memset (client, 0, sizeof (*client));
+ oss_midi_clients[curr_midi_dev] = client;
+ oss_num_midi_clients = num_mididevs;
+ }
+
+
+#ifdef CONFIG_OSS_MIDI
+/*
+ * Create the device node.
+ */
+
+ {
+ char name[32];
+#ifdef NEW_DEVICE_NAMING
+# ifdef USE_DEVICE_SUBDIRS
+ sprintf (name, "oss/%s/mid%d", osdev->nick, osdev->num_mididevs - 1);
+# else
+ sprintf (name, "%s_mid%d", osdev->nick, osdev->num_mididevs - 1);
+# endif
+#else
+ sprintf (name, "midi%02d", curr_midi_dev);
+#endif
+ oss_install_chrdev (osdev, name, OSS_DEV_MIDI, curr_midi_dev,
+ &midi_cdev_drv, 0);
+ sprintf (op->devnode, "/dev/%s", name);
+ }
+#endif
+
+ return curr_midi_dev;
+}