summaryrefslogtreecommitdiff
path: root/kernel/framework/midi
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/midi')
-rw-r--r--kernel/framework/midi/.config2
-rw-r--r--kernel/framework/midi/oss_default_timer.c204
-rw-r--r--kernel/framework/midi/oss_midi_core.c1733
-rw-r--r--kernel/framework/midi/oss_midi_mapper.c109
-rw-r--r--kernel/framework/midi/oss_midi_parser.c406
-rw-r--r--kernel/framework/midi/oss_midi_queue.c567
-rw-r--r--kernel/framework/midi/oss_midi_timers.c607
7 files changed, 3628 insertions, 0 deletions
diff --git a/kernel/framework/midi/.config b/kernel/framework/midi/.config
new file mode 100644
index 0000000..101096e
--- /dev/null
+++ b/kernel/framework/midi/.config
@@ -0,0 +1,2 @@
+configcheck=MIDI
+targetcpu=any
diff --git a/kernel/framework/midi/oss_default_timer.c b/kernel/framework/midi/oss_default_timer.c
new file mode 100644
index 0000000..ee09601
--- /dev/null
+++ b/kernel/framework/midi/oss_default_timer.c
@@ -0,0 +1,204 @@
+/*
+ * Purpose: Default MIDI timer (using system clock)
+ */
+/*
+ *
+ * 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"
+
+typedef struct
+{
+ unsigned long long start_time;
+ timeout_id_t timeout_id;
+ oss_midi_wait_callback_t timer_callback;
+ void *arg;
+} deftmr_timerc_t;
+
+static unsigned long long tick_scale = 1;
+
+static int
+deftmr_create_instance (int timer_dev, int driver_dev)
+{
+ deftmr_timerc_t *timerc;
+
+ if ((timerc = KERNEL_MALLOC (sizeof (*timerc))) == NULL)
+ return OSS_ENOMEM;
+
+ memset (timerc, 0, sizeof (*timerc));
+
+ oss_timer_devs[timer_dev]->timerc = timerc;
+
+ return 0;
+}
+
+static void
+deftmr_free_instance (int timer_dev, int driver_dev)
+{
+ deftmr_timerc_t *timerc;
+
+ timerc = oss_timer_devs[timer_dev]->timerc;
+ untimeout (timerc->timeout_id);
+
+ if (timerc != NULL)
+ KERNEL_FREE (timerc);
+ oss_timer_devs[timer_dev]->timerc = NULL;
+}
+
+static int
+deftmr_attach_client (int timer_dev, int mididev)
+{
+ return 0;
+}
+
+static void
+deftmr_detach_client (int timer_dev, int mididev)
+{
+}
+
+static int
+deftmr_ioctl (int timer_dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static int
+deftmr_set_source (int timer_dev, int source)
+{
+ return source;
+}
+
+static int
+deftmr_set_tempo (int timer_dev, int tempo)
+{
+ return tempo;
+}
+
+static int
+deftmr_set_timebase (int timer_dev, int timebase)
+{
+ return timebase;
+}
+static int
+deftmr_start (int timer_dev, oss_uint64_t tick)
+{
+ deftmr_timerc_t *timerc;
+
+ timerc = oss_timer_devs[timer_dev]->timerc;
+
+ if (timerc == NULL)
+ {
+ cmn_err (CE_WARN, "deftmr_start: timerc==NULL\n");
+ return OSS_EIO;
+ }
+
+ timerc->start_time = GET_JIFFIES ();
+ return 0;
+}
+
+static int
+deftmr_stop (int timer_dev)
+{
+ deftmr_timerc_t *timerc;
+
+ timerc = oss_timer_devs[timer_dev]->timerc;
+ untimeout (timerc->timeout_id);
+
+ return 0;
+}
+
+static int
+deftmr_cont (int timer_dev)
+{
+ return 0;
+}
+
+static int
+deftmr_wait (int timer_dev, unsigned long long time,
+ oss_midi_wait_callback_t callback, void *arg)
+{
+ deftmr_timerc_t *timerc;
+ unsigned long long t;
+
+ timerc = oss_timer_devs[timer_dev]->timerc;
+ untimeout (timerc->timeout_id);
+
+ t = ((oss_uint64_t) time + (tick_scale / 2)) / tick_scale;
+
+ t += timerc->start_time;
+
+ t -= GET_JIFFIES ();
+
+ if (t < 0)
+ {
+ return 1;
+ }
+
+ timerc->timeout_id = timeout (callback, arg, t);
+
+ return 0;
+}
+
+static unsigned long long
+deftmr_get_current_time (int timer_dev)
+{
+ unsigned long long t;
+ deftmr_timerc_t *timerc;
+
+ timerc = oss_timer_devs[timer_dev]->timerc;
+ /*
+ * Compute time in system ticks since start of the timer
+ */
+ t = GET_JIFFIES ();
+ if (t < timerc->start_time)
+ return 0;
+ t -= timerc->start_time;
+
+ return t * tick_scale; /* In microseconds */
+}
+
+oss_timer_driver_t default_midi_driver = {
+ deftmr_create_instance,
+ deftmr_free_instance,
+ deftmr_attach_client,
+ deftmr_detach_client,
+ deftmr_ioctl,
+ deftmr_wait,
+ deftmr_get_current_time,
+ deftmr_set_source,
+ deftmr_set_tempo,
+ deftmr_set_timebase,
+ deftmr_start,
+ deftmr_stop,
+ deftmr_cont
+};
+
+void
+attach_oss_default_timer (oss_device_t * osdev)
+{
+ int timer_dev;
+
+ tick_scale = 1000000 / OSS_HZ;
+ if ((timer_dev = oss_install_timer (OSS_TIMER_DRIVER_VERSION, "System timer", &default_midi_driver, sizeof (oss_timer_driver_t), 0, /* Flags */
+ 16, /* max_instances */
+ 1000000 / OSS_HZ, /* Resolution */
+ NULL, osdev)) < 0)
+ {
+ cmn_err (CE_WARN, "Failed to install default MIDI timer\n");
+ return;
+ }
+}
+
+void
+detach_oss_default_timer ()
+{
+}
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;
+}
diff --git a/kernel/framework/midi/oss_midi_mapper.c b/kernel/framework/midi/oss_midi_mapper.c
new file mode 100644
index 0000000..e99d579
--- /dev/null
+++ b/kernel/framework/midi/oss_midi_mapper.c
@@ -0,0 +1,109 @@
+/*
+ * Purpose: MIDI device mapper
+ *
+ * This code is used to bind applications using /dev/mixer to the actual
+ * MIDI input and output ports.
+ */
+/*
+ *
+ * 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"
+
+extern oss_mutex_t midi_mutex;
+
+void
+midi_mapper_init (oss_device_t * osdev)
+{
+}
+
+void
+midi_mapper_uninit (void)
+{
+}
+
+int
+midi_mapper_autobind (int client_dev, int mode)
+{
+ int dev;
+ oss_midi_client_t *client = oss_midi_clients[client_dev];
+
+ cmn_err (CE_CONT, "MIDI autobind %d\n", client_dev);
+
+ for (dev = 0; dev < num_mididevs; dev++)
+ {
+ int err;
+
+ if (midi_devs[dev]->open_mode) /* Busy */
+ continue;
+
+ if ((mode & OPEN_WRITE) && !(midi_devs[dev]->flags & MFLAG_OUTPUT))
+ continue;
+
+ if ((mode == OPEN_READ) && !(midi_devs[dev]->flags & MFLAG_INPUT))
+ continue;
+
+ if ((err = midi_devs[dev]->d->open (dev, client->open_mode,
+ oss_midi_input_byte,
+ oss_midi_input_buf,
+ oss_midi_output_intr)) < 0)
+ {
+ /* The device seems to think it's busy */
+ continue;
+ }
+
+ if (midi_devs[dev]->open_mode == 0) /* Not busy */
+ {
+
+ midi_devs[dev]->open_mode = mode;
+ client->mididev = midi_devs[dev];
+
+ if (mode & OPEN_READ)
+ {
+ client->mididev->in_queue =
+ midi_queue_alloc (client->mididev->osdev, "qread");
+ if (client->mididev->in_queue == NULL)
+ {
+ cmn_err (CE_WARN,
+ "Failed to allocate MIDI input queue(2)\n");
+ midi_devs[dev]->d->close (dev, mode);
+ midi_devs[dev]->open_mode = 0;
+ return OSS_ENOMEM;
+
+ }
+ }
+
+ if (mode & OPEN_WRITE)
+ {
+ client->mididev->out_queue =
+ midi_queue_alloc (client->mididev->osdev, "qwrite");
+ if (client->mididev->out_queue == NULL)
+ {
+ cmn_err (CE_WARN,
+ "Failed to allocate MIDI output queue(2)\n");
+ midi_devs[dev]->d->close (dev, mode);
+ midi_devs[dev]->open_mode = 0;
+ return OSS_ENOMEM;
+
+ }
+ }
+
+ oss_midi_set_defaults (client->mididev);
+
+ cmn_err (CE_CONT, "Bound to %d/%s\n", dev, midi_devs[dev]->name);
+ return dev;
+
+ }
+ }
+
+ return OSS_EBUSY;
+}
diff --git a/kernel/framework/midi/oss_midi_parser.c b/kernel/framework/midi/oss_midi_parser.c
new file mode 100644
index 0000000..6681677
--- /dev/null
+++ b/kernel/framework/midi/oss_midi_parser.c
@@ -0,0 +1,406 @@
+/*
+ * Purpose: MIDI mesage parser
+ */
+/*
+ *
+ * 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 "midiparser.h"
+
+struct _mtc_state
+{
+ int prev_ix;
+ int state;
+ int offset;
+ oss_mtc_data_t mtc, mtc0;
+};
+
+typedef struct midi_input_info
+{ /* MIDI input scanner variables */
+#define MI_MAX 64
+ int m_busy;
+ unsigned char m_buf[MI_MAX];
+ unsigned char m_prev_status; /* For running status */
+ int m_ptr;
+#define MST_INIT 0
+#define MST_DATA 1
+#define MST_SYSEX 2
+ int m_state;
+ int m_left;
+ int m_f1_flag;
+} midi_input_info_t;
+
+struct midiparser_common
+{
+ midi_input_info_t inc;
+ midiparser_callback_t callback;
+ midiparser_mtc_callback_t mtc_callback;
+ void *client_context;
+ struct _mtc_state mtc_state;
+};
+
+#define CALLBACK(cat, msg, ch, p1, p2, p3) \
+ { \
+ unsigned char arr[3];arr[0]=p1;arr[1]=p2;arr[2]=p3; \
+ synth->callback(synth->client_context, cat, msg, ch, arr, 3); \
+ }
+#define CALLBACK_A(cat, msg, ch, parms, len) \
+ synth->callback(synth->client_context, cat, msg, ch, parms, len)
+
+static void
+do_system_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
+{
+ CALLBACK_A (CAT_REALTIME, *msg, 0, msg, mlen);
+ return;
+}
+
+static void
+do_sysex_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
+{
+ CALLBACK_A (CAT_SYSEX, 0, 0, msg, mlen);
+ return;
+}
+
+static void
+do_realtime_msg (midiparser_common_p synth, unsigned char data)
+{
+}
+
+static void
+do_midi_msg (midiparser_common_p synth, unsigned char *msg, int mlen)
+{
+ switch (msg[0] & 0xf0)
+ {
+ case 0x90:
+ if (msg[2] != 0)
+ {
+ CALLBACK (CAT_VOICE, 0x90, msg[0] & 0x0f, msg[1], msg[2], 0);
+ break;
+ }
+ msg[2] = 64;
+
+ case 0x80:
+ CALLBACK (CAT_VOICE, 0x80, msg[0] & 0x0f, msg[1], msg[2], 0);
+ break;
+
+ case 0xA0:
+ CALLBACK (CAT_VOICE, 0xA0, msg[0] & 0x0f, msg[1], msg[2], 0);
+ break;
+
+ case 0xB0:
+ CALLBACK (CAT_CHN, 0xB0, msg[0] & 0x0f, msg[1], msg[2], 0);
+ break;
+
+ case 0xC0:
+ CALLBACK (CAT_CHN, 0xC0, msg[0] & 0x0f, msg[1], 0, 0);
+ break;
+
+ case 0xD0:
+ CALLBACK (CAT_CHN, 0xD0, msg[0] & 0x0f, msg[1], 0, 0);
+ break;
+
+ case 0xE0:
+ CALLBACK (CAT_VOICE, 0xE0, msg[0] & 0x0f, msg[1], msg[2], 0);
+ break;
+
+ case 0xf0: /* System common messages */
+ do_system_msg (synth, msg, mlen);
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void
+send_mtc (midiparser_common_p synth, struct _mtc_state *st)
+{
+ oss_mtc_data_t mtc;
+
+ memcpy (&mtc, &st->mtc, sizeof (mtc));
+
+ mtc.qframes += st->offset;
+ mtc.frames += mtc.qframes / 4;
+ mtc.qframes %= 4;
+
+ if (mtc.time_code_type == 0)
+ mtc.time_code_type = 30;
+ mtc.seconds += mtc.frames / mtc.time_code_type; /* TODO: Handle drop frames */
+ mtc.frames %= mtc.time_code_type;
+
+ mtc.minutes += mtc.seconds / 60;
+ mtc.seconds %= 60;
+
+ mtc.hours += mtc.minutes / 60;
+ mtc.minutes %= 60;
+
+ synth->mtc_callback (synth->client_context, &mtc);
+}
+
+static void
+mtc_message (midiparser_common_p synth, struct _mtc_state *st,
+ unsigned char b)
+{
+ static char frame_types[4] = { 24, 25, 29, 30 };
+ int ix, data;
+ int previx;
+
+ ix = b >> 4;
+ data = b & 0x0f;
+
+ previx = (st->prev_ix + 1) % 8;
+
+ if (ix == previx)
+ st->mtc0.direction = st->mtc.direction = MTC_DIR_FORWARD;
+ else if (ix == st->prev_ix)
+ st->mtc0.direction = st->mtc.direction = MTC_DIR_STOPPED;
+ else
+ st->mtc0.direction = st->mtc.direction = MTC_DIR_BACKWARD;
+ st->prev_ix = ix;
+
+ if (st->state == 0)
+ {
+ if (ix != 0) /* Not the beginning of the sequence yet */
+ return;
+ st->state = 1;
+ st->offset = -1;
+ }
+
+ switch (ix)
+ {
+ case 0: /* Frame count LS nibble */
+ st->mtc0.qframes = 0;
+ st->mtc0.frames = data;
+ break;
+
+ case 1: /* Frame count MS nibble */
+ st->mtc0.frames |= data << 4;
+ break;
+
+ case 2: /* Seconds count LS nibble */
+ st->mtc0.seconds = data;
+ break;
+
+ case 3: /* Seconds count MS nibble */
+ st->mtc0.seconds |= data << 4;
+ break;
+
+ case 4: /* Minutes count LS nibble */
+ st->mtc0.minutes = data;
+ break;
+
+ case 5: /* Minutes count MS nibble */
+ st->mtc0.minutes |= data << 4;
+ break;
+
+ case 6: /* Hours count LS nibble */
+ st->mtc0.hours = data;
+ break;
+
+ case 7: /* Hours count MS nibble */
+ st->mtc0.hours |= data << 4;
+ st->mtc0.time_code_type = frame_types[(st->mtc0.hours >> 5) & 0x03];
+ st->mtc0.hours &= 0x1f;
+ memcpy (&st->mtc, &st->mtc0, sizeof (st->mtc));
+ break;
+ }
+
+ if (ix == 7)
+ st->offset = 7;
+ else
+ st->offset++;
+ send_mtc (synth, st);
+}
+
+static void
+handle_midi_input (midiparser_common_p synth, midi_input_info_t * inc,
+ unsigned char data)
+{
+ static unsigned char len_tab[] = /* # of data bytes following a status
+ */
+ {
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
+ };
+
+ if (data == 0xfe) /* Active sensing */
+ {
+ return;
+ }
+
+ if (data >= 0xf8) /* Real time message */
+ {
+ do_realtime_msg (synth, data);
+ CALLBACK (CAT_REALTIME, data, 0, 0, 0, 0);
+ return;
+ }
+
+ if (data == 0xf1) /* MTC quarter frame (1st byte) */
+ {
+ inc->m_f1_flag = 1;
+ return;
+ }
+
+ if (inc->m_f1_flag) /* MTC quarter frame (2nd byte) */
+ {
+ inc->m_f1_flag = 0;
+
+ if (synth->mtc_callback != NULL)
+ {
+ mtc_message (synth, &synth->mtc_state, data);
+ return;
+ }
+ CALLBACK (CAT_MTC, 0xf1, 0, data, 0, 0);
+ return;
+ }
+
+ switch (inc->m_state)
+ {
+ case MST_INIT:
+ if (data & 0x80) /* MIDI status byte */
+ {
+ if ((data & 0xf0) == 0xf0) /* Common message */
+ {
+ switch (data)
+ {
+ case 0xf0: /* Sysex */
+ inc->m_state = MST_SYSEX;
+ inc->m_ptr = 1;
+ inc->m_left = MI_MAX;
+ inc->m_buf[0] = data;
+ break; /* Sysex */
+
+ case 0xf1: /* MTC quarter frame */
+ case 0xf3: /* Song select */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 1;
+ inc->m_buf[0] = data;
+ break;
+
+ case 0xf2: /* Song position pointer */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 2;
+ inc->m_buf[0] = data;
+ break;
+
+ default: /* Other common messages */
+ inc->m_buf[0] = data;
+ inc->m_ptr = 1;
+ do_midi_msg (synth, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ inc->m_left = 0;
+ }
+ }
+ else
+ {
+ /* Channel messages */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = len_tab[(data >> 4) - 8];
+ inc->m_buf[0] = inc->m_prev_status = data;
+ }
+ }
+ else /* Running status */ if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */
+ {
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 2;
+ inc->m_left = len_tab[(inc->m_prev_status >> 4) - 8] - 1;
+ inc->m_buf[0] = inc->m_prev_status;
+ inc->m_buf[1] = data;
+ }
+ break; /* MST_INIT */
+
+ case MST_DATA:
+ inc->m_buf[inc->m_ptr++] = data;
+ if (--inc->m_left <= 0)
+ {
+ inc->m_state = MST_INIT;
+ do_midi_msg (synth, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ }
+ break; /* MST_DATA */
+
+ case MST_SYSEX:
+ inc->m_buf[inc->m_ptr++] = data;
+ if (data == 0xf7) /* Sysex end */
+ {
+ do_sysex_msg (synth, inc->m_buf, inc->m_ptr);
+ inc->m_state = MST_INIT;
+ inc->m_left = 0;
+ inc->m_ptr = 0;
+
+ }
+ else if (inc->m_ptr >= MI_MAX) /* Overflow protection */
+ {
+ do_sysex_msg (synth, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ }
+
+ break; /* MST_SYSEX */
+
+ default:
+ inc->m_state = MST_INIT;
+ }
+}
+
+midiparser_common_p
+midiparser_create (midiparser_callback_t callback, void *context)
+{
+ midiparser_common_p synth;
+
+ if ((synth = KERNEL_MALLOC (sizeof (*synth))) == NULL)
+ return NULL;
+
+ memset (synth, 0, sizeof (*synth));
+
+ synth->callback = callback;
+ synth->client_context = context;
+ synth->mtc_state.prev_ix = -1;
+
+ return synth;
+}
+
+void
+midiparser_unalloc (midiparser_common_p common)
+{
+ KERNEL_FREE (common);
+}
+
+void
+midiparser_mtc_callback (midiparser_common_p common,
+ midiparser_mtc_callback_t callback)
+{
+ common->mtc_callback = callback;
+}
+
+void
+midiparser_input (midiparser_common_p synth, unsigned char data)
+{
+ handle_midi_input (synth, &synth->inc, data);
+}
+
+void
+midiparser_input_buf (midiparser_common_p synth, unsigned char *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ handle_midi_input (synth, &synth->inc, data[i]);
+}
diff --git a/kernel/framework/midi/oss_midi_queue.c b/kernel/framework/midi/oss_midi_queue.c
new file mode 100644
index 0000000..23801ae
--- /dev/null
+++ b/kernel/framework/midi/oss_midi_queue.c
@@ -0,0 +1,567 @@
+/*
+ * Purpose: MIDI message queue management
+ */
+/*
+ *
+ * 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)
+
+typedef struct
+{
+ int len;
+ midi_packet_header_t hdr;
+ int next;
+ int size;
+
+ unsigned char *data;
+ unsigned char buf[1];
+ /* Do _NOT_ add any fields after data[] */
+ /* Do _NOT_ add any fields after data[] */
+ /* Do _NOT_ add any fields after data[] */
+ /* Do _NOT_ add any fields after data[] */
+ /* Do _NOT_ add any fields after data[] */
+} midi_buf_t;
+
+#define BUF_PREFIX_SIZE (sizeof(midi_buf_t)-1)
+#define MAX_QUEUE_DEPTH 31
+#define QUEUE_BYTES ((MAX_QUEUE_DEPTH+1)*(BUF_PREFIX_SIZE+MIDI_PAYLOAD_SIZE))
+
+struct midi_queue_t
+{
+ oss_mutex_t mutex;
+ char name[16]; /* For debugging purposes */
+
+/*
+ * Memeory management variables.
+ */
+ unsigned char buffer[QUEUE_BYTES];
+ int buf_head, buf_tail;
+ int debugging;
+ int q_head, q_tail;
+ int avail;
+ int writecount, readcount;
+
+/*
+ * Message queue
+ */
+
+ midi_buf_t *buffers[MAX_QUEUE_DEPTH];
+ int dummy;
+};
+
+midi_queue_t *
+midi_queue_alloc (oss_device_t * osdev, const char *name)
+{
+ midi_queue_t *q;
+
+ if ((q = KERNEL_MALLOC (sizeof (*q))) == NULL)
+ return NULL;
+
+ memset (q, 0, sizeof (*q));
+
+ MUTEX_INIT (osdev, q->mutex, MH_FRAMEW + 2);
+ if (name != NULL)
+ strcpy (q->name, name);
+
+ q->avail = QUEUE_BYTES;
+ return q;
+}
+
+void
+midi_queue_free (midi_queue_t * queue)
+{
+ if (queue != NULL)
+ {
+ MUTEX_CLEANUP (queue->mutex);
+ KERNEL_FREE (queue);
+ }
+}
+
+static int
+queue_concat (midi_queue_t * queue, unsigned char *data, int len,
+ midi_packet_header_t * hdr)
+{
+/*
+ * Check if the MIDI event can be appended to the previous event (to improve
+ * buffering performance).
+ *
+ * This is possible if:
+ *
+ * 1) The queue is not empty.
+ * 2) The previous event has the same time stamp.
+ * 3) The previous event is not too close to the end of the allocated buffer
+ * area.
+ */
+
+ midi_buf_t *buf;
+ int p, i;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+ if (queue->buf_tail == queue->buf_head) /* Nothing in the queue */
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ if (queue->avail < len)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ if (queue->q_tail == queue->q_head) /* No events in the queue */
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ p = queue->q_head - 1;
+ if (p < 0)
+ p = MAX_QUEUE_DEPTH - 1;
+
+ buf = queue->buffers[p];
+
+ if (hdr == NULL || buf->hdr.time != hdr->time)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ if (buf->len + len > MIDI_PAYLOAD_SIZE) /* Event would become too long */
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ if (buf->next + len >= QUEUE_BYTES - 1) /* No space to grow */
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ /* Check that we are not too close to the buffer tail */
+ if (buf->next < queue->buf_tail && buf->next + len >= queue->buf_tail)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+/*
+ * Ok. All checks passed. Now append the data.
+ */
+
+ queue->avail -= len;
+ queue->writecount += len;
+ buf->next += len;
+ buf->size += len;
+ for (i = 0; i < len; i++)
+ buf->data[buf->len + i] = data[i];
+ buf->len += len;
+ queue->buf_head = buf->next;
+
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 1;
+}
+
+int
+midi_queue_alloc_record (midi_queue_t * queue, unsigned char **data, int len,
+ midi_packet_header_t * hdr)
+{
+ int p;
+ int avail = 0, n, next;
+ void *ptr;
+ midi_buf_t *buf;
+ oss_native_word flags;
+
+ if (queue == NULL)
+ {
+ cmn_err (CE_WARN, "midi_queue_alloc_record: Queue==NULL\n");
+ return OSS_EIO;
+ }
+
+ if (len < 1 && hdr == NULL) /* Nothing was given */
+ {
+ cmn_err (CE_WARN, "midi_queue_alloc_record: No data\n");
+ return OSS_EIO;
+ }
+
+ if (len > MIDI_PAYLOAD_SIZE)
+ {
+ cmn_err (CE_WARN, "Too long MIDI block\n");
+ len = MIDI_PAYLOAD_SIZE;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+
+ next = (queue->q_head + 1) % MAX_QUEUE_DEPTH;
+
+ if (next == queue->q_tail)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_ENOSPC;
+ }
+
+ if (queue->avail < BUF_PREFIX_SIZE + len)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_ENOSPC;
+ }
+
+ n = queue->q_head - queue->q_tail;
+ if (n <= 0)
+ n += MAX_QUEUE_DEPTH;
+
+ if (n < 1)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_ENOSPC;
+ }
+
+ if (queue->buf_tail > queue->buf_head)
+ {
+ avail = queue->buf_tail - queue->buf_head;
+
+ if (avail < BUF_PREFIX_SIZE + len)
+ {
+ queue->buf_head = 0;
+ }
+ }
+
+ if (queue->buf_tail == queue->buf_head)
+ {
+ avail = QUEUE_BYTES;
+ queue->buf_tail = queue->buf_head = 0;
+ queue->avail = QUEUE_BYTES;
+ }
+ else if (queue->buf_tail < queue->buf_head)
+ avail = QUEUE_BYTES - queue->buf_head;
+
+ if (avail < BUF_PREFIX_SIZE + len)
+ {
+#if 1
+ len = avail - BUF_PREFIX_SIZE;
+
+ if (len <= 0)
+#endif
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_ENOSPC;
+ }
+ }
+
+ if (queue->buf_head >= QUEUE_BYTES)
+ {
+ cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/alloc)\n", queue->buf_head,
+ QUEUE_BYTES);
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_EIO;
+ }
+
+ if (queue->buf_head + BUF_PREFIX_SIZE + len > QUEUE_BYTES)
+ {
+ cmn_err (CE_CONT, "Potential MIDI queue overflow detected\n");
+ cmn_err (CE_CONT, "Head=%d (%d)\n", queue->buf_head, QUEUE_BYTES);
+ cmn_err (CE_CONT, "Tail=%d (%d)\n", queue->buf_tail, QUEUE_BYTES);
+ cmn_err (CE_CONT, "Avail=%d\n", avail);
+ cmn_err (CE_CONT, "Required=%d\n", len + BUF_PREFIX_SIZE);
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return OSS_EIO;
+ }
+
+ ptr = queue->buffer + queue->buf_head;
+
+ p = queue->buf_head;
+ queue->buf_head = (queue->buf_head + BUF_PREFIX_SIZE + len) % QUEUE_BYTES;
+
+ buf = ptr;
+ buf->size = BUF_PREFIX_SIZE + len;
+ queue->avail -= buf->size;
+ buf->data = buf->buf;
+ buf->next = queue->buf_head;
+ if (hdr == NULL)
+ memset (&buf->hdr, 0, sizeof (midi_packet_header_t));
+ else
+ memcpy (&buf->hdr, hdr, sizeof (midi_packet_header_t));
+ buf->len = len;
+ *data = buf->data;
+ queue->writecount += len;
+ MDB (cmn_err (CE_CONT, "%s: alloc %d bytes\n", queue->name, len));
+
+ queue->buffers[queue->q_head] = buf;
+
+ queue->q_head = (queue->q_head + 1) % MAX_QUEUE_DEPTH;
+
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return len;
+}
+
+int
+midi_queue_put (midi_queue_t * queue, unsigned char *data, int len,
+ midi_packet_header_t * hdr)
+{
+ int ret;
+ unsigned char *targetbuf;
+
+ if (queue == NULL)
+ {
+ return OSS_EIO;
+ }
+#if 0
+ {
+ char tmp[1024];
+ memcpy (tmp, data, len);
+ tmp[len] = 0;
+ cmn_err (CE_CONT, "Q Put='%s', %d\n", tmp, len);
+ }
+#endif
+
+ if (len < 1)
+ return 0;
+
+ if (len > MIDI_PAYLOAD_SIZE)
+ return OSS_E2BIG;
+
+#if 1
+ if (len == 1)
+ if (queue_concat (queue, data, len, hdr))
+ return len;
+#endif
+
+ queue->debugging = 1;
+ if ((ret = midi_queue_alloc_record (queue, &targetbuf, len, hdr)) <= 0)
+ return ret;
+ queue->debugging = 0;
+
+ if (ret < len) /* All data didn't fit */
+ len = ret;
+
+ memcpy (targetbuf, data, len);
+
+ if (queue->buf_head >= QUEUE_BYTES || queue->buf_tail >= QUEUE_BYTES)
+ {
+ cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/%d/put)\n",
+ queue->buf_head, queue->buf_tail, QUEUE_BYTES);
+ return OSS_EIO;
+ }
+ return len;
+}
+
+int
+midi_queue_get (midi_queue_t * queue, unsigned char **data, int max_len,
+ midi_packet_header_t ** hdr)
+{
+ midi_buf_t *buf;
+ oss_native_word flags;
+
+ *hdr = NULL;
+ *data = NULL;
+
+ if (queue == NULL)
+ return OSS_EIO;
+
+ if (queue->buf_head >= QUEUE_BYTES || queue->buf_tail >= QUEUE_BYTES)
+ {
+ cmn_err (CE_CONT, "MIDI queue damaged (%d/%d/%d/get)\n",
+ queue->buf_head, queue->buf_tail, QUEUE_BYTES);
+ return OSS_EIO;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+
+ if (queue->q_head == queue->q_tail)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ buf = queue->buffers[queue->q_tail];
+ queue->q_tail = (queue->q_tail + 1) % MAX_QUEUE_DEPTH;
+
+ queue->buf_tail = buf->next;
+ queue->avail += buf->size;
+ queue->readcount += buf->size;
+
+ *data = buf->data;
+ *hdr = &buf->hdr;
+
+#if 0
+ if (queue->buf_tail == queue->buf_head) /* Buffer empty */
+ queue->buf_tail = queue->buf_head = 0; /* Rewind */
+
+ if (queue->q_tail == queue->q_head)
+ queue->q_tail = queue->q_head = 0;
+#endif
+
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return buf->len;
+}
+
+int
+midi_queue_find_buffer (midi_queue_t * queue, unsigned char **data,
+ midi_packet_header_t ** hdr)
+{
+ midi_buf_t *buf;
+ oss_native_word flags;
+
+ *hdr = NULL;
+ *data = NULL;
+
+ if (queue == NULL)
+ return OSS_EIO;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+ if (queue->q_head == queue->q_tail)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return 0;
+ }
+
+ buf = queue->buffers[queue->q_tail];
+
+ *data = buf->data;
+ *hdr = &buf->hdr;
+
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return buf->len;
+}
+
+void
+midi_queue_remove_chars (midi_queue_t * queue, int len)
+{
+ midi_buf_t *buf;
+ oss_native_word flags;
+
+ if (queue == NULL)
+ return;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+
+ if (queue->q_head == queue->q_tail)
+ {
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ MDB (cmn_err (CE_CONT, "%s: Q Nothing to remove\n", queue->name));
+ return;
+ }
+
+ buf = queue->buffers[queue->q_tail];
+
+ if (len < buf->len)
+ {
+ /* Buffer not completely used. Just remove len characters from the beginning */
+ /* unsigned char *data = buf->data; */
+
+ MDB (cmn_err
+ (CE_CONT, "%s: Q Remove chars %d (%02x)\n", queue->name, len,
+ buf->data[len]));
+ /* memcpy(data, data+len, buf->len-len); */
+ buf->data += len;
+ buf->len -= len;
+ queue->readcount += len;
+ MDB (cmn_err (CE_CONT, "%s: Q left %d\n", queue->name, buf->len));
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+ return;
+ }
+
+/*
+ * Remove the whole buffer
+ */
+ MDB (cmn_err (CE_CONT, "%s: Q Remove all\n", queue->name));
+ queue->q_tail = (queue->q_tail + 1) % MAX_QUEUE_DEPTH;
+ queue->buf_tail = buf->next;
+ queue->avail += buf->size;
+ queue->readcount += len;
+
+ if (queue->buf_tail == queue->buf_head) /* Buffer empty */
+ queue->buf_tail = queue->buf_head = 0; /* Rewind */
+
+ if (queue->q_tail == queue->q_head)
+ queue->q_tail = queue->q_head = 0;
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+}
+
+void
+midi_queue_removeall (midi_queue_t * queue)
+{
+/*
+ * Make the queue completely empty
+ */
+ oss_native_word flags;
+
+ if (queue == NULL)
+ return;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+
+ queue->buf_tail = queue->buf_head = 0; /* Rewind */
+ queue->q_tail = queue->q_head = 0;
+ queue->avail = QUEUE_BYTES;
+ queue->readcount = 0;
+ queue->writecount = 0;
+
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+}
+
+int
+midi_queue_isempty (midi_queue_t * queue)
+{
+ int is_empty;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+ is_empty = (queue->q_tail == queue->q_head);
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+
+ return is_empty;
+}
+
+int
+midi_queue_spaceleft (midi_queue_t * queue)
+{
+ oss_native_word flags;
+ int space = 1;
+ int next;
+
+ MUTEX_ENTER_IRQDISABLE (queue->mutex, flags);
+ next = (queue->q_head + 1) % MAX_QUEUE_DEPTH;
+ if (next == queue->q_tail)
+ space = 0;
+ else
+ {
+ if (queue->avail < BUF_PREFIX_SIZE + 1)
+ space = 0;
+ else
+ {
+ space = queue->avail;
+ if (space > MIDI_PAYLOAD_SIZE)
+ space = MIDI_PAYLOAD_SIZE;
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (queue->mutex, flags);
+
+ return space;
+}
+
+void
+midi_queue_debugging (midi_queue_t * queue)
+{
+ queue->debugging = 1;
+}
+
+void
+midi_queue_trace (midi_queue_t * queue)
+{
+ MDB (cmn_err
+ (CE_CONT, "Write %d, read %d\n", queue->writecount, queue->readcount));
+}
diff --git a/kernel/framework/midi/oss_midi_timers.c b/kernel/framework/midi/oss_midi_timers.c
new file mode 100644
index 0000000..67cb8c9
--- /dev/null
+++ b/kernel/framework/midi/oss_midi_timers.c
@@ -0,0 +1,607 @@
+/*
+ * Purpose: MIDI timer 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"
+
+tdev_t *oss_timer_devs[MAX_TIMER_DEV] = { NULL };
+int oss_num_timers = 0;
+
+oss_timer_driver_t *oss_timer_drivers[MAX_TIMER_DEV] = { NULL };
+int oss_num_timer_drivers = 0;
+
+int
+oss_install_timer (int driver_version,
+ char *name,
+ oss_timer_driver_t * d, int driver_size,
+ unsigned int flags,
+ int max_instances,
+ int resolution, void *devc, oss_device_t * osdev)
+{
+ oss_timer_driver_t *op;
+ int num;
+
+ if (driver_version != OSS_TIMER_DRIVER_VERSION)
+ {
+ cmn_err (CE_WARN, "Incompatible timer driver version %d\n",
+ driver_version);
+ return OSS_EINVAL;
+ }
+
+ if (driver_size != sizeof (*op))
+ {
+ cmn_err (CE_WARN, "Incompatible timer driver size %d\n", driver_size);
+ return OSS_EINVAL;
+ }
+
+ if (oss_num_timer_drivers >= MAX_TIMER_DEV)
+ {
+ cmn_err (CE_WARN, "Too many timer drivers\n");
+ return OSS_ENOMEM;
+ }
+
+ if ((op = PMALLOC (osdev, sizeof (*op))) == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory (oss_install_timer)\n");
+ return OSS_ENOMEM;
+ }
+
+ memcpy (op, d, driver_size);
+ op->flags = flags;
+ op->devc = devc;
+ op->osdev = osdev;
+ MUTEX_INIT (op->osdev, op->mutex, MH_FRAMEW + 2);
+
+ op->max_instances = max_instances;
+ op->num_instances = 0;
+ strncpy (op->name, name, sizeof (op->name));
+ op->name[sizeof (op->name) - 1] = 0;
+ num = oss_num_timer_drivers++;
+ op->driver_dev = num;
+ op->resolution = resolution;
+ oss_timer_drivers[num] = op;
+
+ return 0;
+}
+
+int
+oss_timer_create_instance (int driver_dev, mididev_t * mididev)
+{
+ oss_timer_driver_t *drv;
+ tdev_t *tmr;
+ oss_native_word flags;
+ int num, i, err;
+
+/* TODO: Create a mutex for all timers */
+
+ if (driver_dev < 0 || driver_dev >= oss_num_timer_drivers)
+ return OSS_ENXIO;
+
+ drv = oss_timer_drivers[driver_dev];
+
+ MUTEX_ENTER_IRQDISABLE (drv->mutex, flags);
+ if (drv->num_instances >= drv->max_instances)
+ {
+ cmn_err (CE_CONT, "All instances of timer %d are in use (%d)\n",
+ driver_dev, drv->max_instances);
+
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+ return OSS_EBUSY;
+ }
+ drv->num_instances++;
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+
+ if ((tmr = KERNEL_MALLOC (sizeof (*tmr))) == NULL)
+ {
+ MUTEX_ENTER_IRQDISABLE (drv->mutex, flags);
+ drv->num_instances--;
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+ return OSS_ENOMEM;
+ }
+ memset (tmr, 0, sizeof (*tmr));
+
+ num = -1;
+
+ for (i = 0; i < oss_num_timers; i++)
+ if (oss_timer_devs[i] == NULL)
+ {
+ num = i;
+ break;
+ }
+
+ if (num == -1)
+ {
+ if (oss_num_timers >= MAX_TIMER_DEV) /* No timers available */
+ {
+ cmn_err (CE_WARN, "No free timer instances\n");
+ KERNEL_FREE (tmr);
+ MUTEX_ENTER_IRQDISABLE (drv->mutex, flags);
+ drv->num_instances--;
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+ return OSS_EBUSY;
+ }
+ num = oss_num_timers++;
+ }
+
+/*
+ * Init the timer instance
+ */
+ tmr->osdev = drv->osdev;
+ MUTEX_INIT (tmr->osdev, tmr->mutex, MH_FRAMEW + 3);
+ tmr->refcount = 0;
+ tmr->d = drv;
+ tmr->driver_dev = driver_dev;
+ tmr->timer_dev = num;
+ tmr->devc = drv->devc;
+ tmr->name = drv->name;
+
+ oss_timer_devs[num] = tmr;
+
+ if (drv->create_instance != NULL)
+ if ((err = drv->create_instance (num, driver_dev)) < 0)
+ {
+ oss_timer_devs[num] = NULL;
+ MUTEX_CLEANUP (tmr->mutex);
+ KERNEL_FREE (tmr);
+ MUTEX_ENTER_IRQDISABLE (drv->mutex, flags);
+ drv->num_instances--;
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+ return err;
+ }
+
+ return num;
+}
+
+#define BITS_PER_ENTRY (sizeof(unsigned int)*8)
+
+int
+oss_timer_attach_client (int timer_dev, mididev_t * mididev)
+{
+ tdev_t *tmr;
+ int err;
+ int mdev = mididev->dev;
+ oss_native_word flags;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return OSS_ENXIO;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ {
+ cmn_err (CE_WARN, "tmr==NULL\n");
+ return OSS_EIO;
+ }
+ if (tmr->d == NULL)
+ {
+ cmn_err (CE_WARN, "tmr->d==NULL\n");
+ return OSS_EIO;
+ }
+
+ if (tmr->d->attach_client != NULL)
+ if ((err = tmr->d->attach_client (timer_dev, mididev->dev)) < 0)
+ {
+ return err;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (tmr->mutex, flags);
+ tmr->refcount++;
+ tmr->midimap[mdev / BITS_PER_ENTRY] |= (1 << (mdev % BITS_PER_ENTRY));
+ tmr->midi_times[mdev] = 0;
+ MUTEX_EXIT_IRQRESTORE (tmr->mutex, flags);
+
+ return 0;
+}
+
+void
+oss_timer_detach_client (int timer_dev, mididev_t * mididev)
+{
+ oss_native_word flags;
+ tdev_t *tmr;
+ oss_timer_driver_t *drv;
+ int refcount;
+ int mdev;
+
+ mdev = mididev->dev;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return;
+
+ if (tmr->d->detach_client != NULL)
+ tmr->d->detach_client (timer_dev, mididev->dev);
+
+ MUTEX_ENTER_IRQDISABLE (tmr->mutex, flags);
+ refcount = --tmr->refcount;
+ tmr->midimap[mdev / BITS_PER_ENTRY] &= ~(1 << (mdev % BITS_PER_ENTRY));
+ tmr->midi_times[mdev] = 0;
+ MUTEX_EXIT_IRQRESTORE (tmr->mutex, flags);
+
+ if (refcount <= 0)
+ {
+ drv = tmr->d;
+
+ oss_timer_stop (timer_dev);
+ if (tmr->d->free_instance != NULL)
+ tmr->d->free_instance (timer_dev, tmr->driver_dev);
+
+ oss_timer_devs[timer_dev] = NULL;
+ MUTEX_CLEANUP (tmr->mutex);
+ KERNEL_FREE (tmr);
+
+ MUTEX_ENTER_IRQDISABLE (drv->mutex, flags);
+ drv->num_instances--;
+ if (drv->num_instances < 0)
+ {
+ cmn_err (CE_WARN, "Driver instances < 0\n");
+ drv->num_instances = 0;
+ }
+ MUTEX_EXIT_IRQRESTORE (drv->mutex, flags);
+ }
+}
+
+static int update_timer_state (tdev_t * tmr);
+
+static void
+timer_callback (void *arg)
+{
+ int i, x;
+ oss_uint64_t t;
+
+ tdev_t *tmr = arg;
+
+ t = tmr->next_time;
+ tmr->prev_time = t;
+ tmr->prev_tick = tmr->next_tick;
+
+ for (x = 0; x < sizeof (tmr->midimap) / sizeof (unsigned int); x++)
+ if (tmr->midimap[x] != 0)
+ for (i = x * BITS_PER_ENTRY;
+ i < (x + 1) * BITS_PER_ENTRY && x < num_mididevs; i++)
+ if (tmr->midimap[i / BITS_PER_ENTRY] & (1 << (i % BITS_PER_ENTRY)))
+ if (tmr->midi_times[i] > 0 && tmr->midi_times[i] <= t)
+ {
+ /*
+ * Timer for this MIDI device has expired. Wake up the device.
+ */
+ tmr->midi_times[i] = 0;
+ if (tmr->callback != NULL)
+ {
+ oss_native_word d = i;
+ tmr->callback ((void *) d);
+ }
+ }
+
+ update_timer_state (tmr);
+}
+
+static int
+update_timer_state (tdev_t * tmr)
+{
+ int i, x;
+ int state;
+ oss_uint64_t t;
+ oss_midi_time_t tick = 0;
+
+ if (tmr == NULL)
+ return 0;
+
+ if (tmr->tempo < 1)
+ tmr->tempo = 60;
+ if (tmr->timebase < 1)
+ tmr->timebase = OSS_HZ;
+
+ t = 0xffffffffffffffffULL; /* Largest possible time */
+ for (x = 0; x < sizeof (tmr->midimap) / sizeof (unsigned int); x++)
+ if (tmr->midimap[x] != 0)
+ for (i = x * BITS_PER_ENTRY;
+ i < (x + 1) * BITS_PER_ENTRY && x < num_mididevs; i++)
+ if (tmr->midimap[i / BITS_PER_ENTRY] & (1 << (i % BITS_PER_ENTRY)))
+ if (tmr->midi_times[i] != 0)
+ {
+ if (tmr->midi_times[i] < t)
+ {
+ t = tmr->midi_times[i];
+ tick = tmr->midi_ticks[i];
+ }
+ }
+
+ if (t == 0xffffffffffffffffULL) /* No events queued */
+ return 1;
+
+ tmr->next_time = t;
+ tmr->next_tick = tick;
+
+ if (tmr->d->wait == NULL)
+ {
+ cmn_err (CE_WARN, "No wait method for timer\n");
+ return 1;
+ }
+
+ state = tmr->d->wait (tmr->timer_dev, t, timer_callback, tmr);
+
+ if (state == 1)
+ {
+ timer_callback (tmr);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+oss_timer_set_tempo (int timer_dev, int tempo)
+{
+ tdev_t *tmr;
+ int err;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return OSS_ENXIO;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return OSS_ENXIO;
+ tmr->pending_tempo = tempo;
+
+ {
+ tmr->tempo = tmr->pending_tempo;
+
+ tmr->bookmark_time = tmr->prev_time;
+ tmr->bookmark_tick = tmr->prev_tick;
+ }
+
+ if (tmr->d->set_tempo != NULL)
+ if ((err = tmr->d->set_tempo (timer_dev, tempo)) < 0)
+ return err;
+
+ return 0;
+}
+
+int
+oss_timer_set_timebase (int timer_dev, int timebase)
+{
+ tdev_t *tmr;
+ int err;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return OSS_ENXIO;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL || tmr->d->set_timebase == NULL)
+ return OSS_ENXIO;
+ tmr->timebase = timebase;
+ if ((err = tmr->d->set_timebase (timer_dev, timebase)) < 0)
+ return err;
+
+ return 0;
+}
+
+int
+oss_timer_wait_time (int timer_dev, int midi_dev, int latency,
+ oss_midi_time_t time, oss_midi_wait_callback_t callback,
+ unsigned short options)
+{
+ tdev_t *tmr;
+ oss_uint64_t t;
+ unsigned int tick;
+
+ if (time == 0)
+ return 1;
+
+ if (latency < 0)
+ latency = 0;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return 0;
+
+ if (midi_dev < 0 || midi_dev >= num_mididevs)
+ return 0;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return 0;
+
+ tmr->callback = callback;
+
+ if (!tmr->run_state)
+ oss_timer_start (timer_dev);
+
+ t = tick = time;
+
+ if (!(options & MIDI_OPT_USECTIME))
+ {
+ /*
+ * Convert MIDI time to absolute (usecs)
+ */
+ time -= tmr->bookmark_tick;
+
+ t =
+ 60000000LL * (oss_uint64_t) time / (oss_uint64_t) (tmr->tempo *
+ tmr->timebase);
+
+ t += tmr->bookmark_time;
+ }
+
+ if (t >= latency)
+ t -= latency;
+
+ if (t <= tmr->prev_time) /* Already expired */
+ {
+ return 1;
+ }
+
+ tmr->midi_times[midi_dev] = t;
+ tmr->midi_ticks[midi_dev] = tick;
+
+ return update_timer_state (tmr);
+}
+
+oss_midi_time_t
+oss_timer_get_current_time (int timer_dev, int latency, int use_abs)
+{
+ tdev_t *tmr;
+ oss_uint64_t t;
+
+ if (latency < 0)
+ latency = 0;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return 0;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL || tmr->d->get_current_time == NULL)
+ return 0;
+
+ if (tmr->run_state == 0)
+ return 0;
+
+ t = tmr->d->get_current_time (timer_dev);
+
+ if (!use_abs)
+ {
+ /*
+ * Convert from absolute (usec) time to relative (MIDI).
+ */
+
+ if (t < tmr->bookmark_time)
+ return 0;
+
+ t -= tmr->bookmark_time;
+ t = 60000000LL * t / (oss_uint64_t) (tmr->tempo * tmr->timebase);
+ t = t * (oss_uint64_t) (tmr->tempo * tmr->timebase);
+
+ t = (t + 30000000LL) / 60000000LL;
+
+ t += tmr->bookmark_tick;
+ }
+
+ return t;
+}
+
+int
+oss_timer_is_running (int timer_dev)
+{
+ tdev_t *tmr;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return 0;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return 0;
+ return tmr->run_state;
+}
+
+void
+oss_timer_start (int timer_dev)
+{
+ tdev_t *tmr;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return;
+ tmr->pending_tempo = tmr->tempo;
+ tmr->next_tick = 0;
+ tmr->prev_tick = 0;
+ tmr->next_time = 0;
+ tmr->prev_time = 0;
+
+ tmr->bookmark_tick = 0;
+ tmr->bookmark_time = 0LL;
+
+ tmr->run_state = 1;
+ if (tmr->d->start != NULL)
+ tmr->d->start (timer_dev, 0LL);
+}
+
+void
+oss_timer_continue (int timer_dev)
+{
+ tdev_t *tmr;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return;
+ tmr->run_state = 1;
+}
+
+void
+oss_timer_stop (int timer_dev)
+{
+ tdev_t *tmr;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return;
+ tmr->run_state = 0;
+ if (tmr->d->stop != NULL)
+ tmr->d->stop (timer_dev);
+ tmr->callback = NULL;
+}
+
+int
+oss_timer_get_timeout (int timer_dev, int midi_dev)
+{
+ tdev_t *tmr;
+ oss_uint64_t t, scale;
+
+ if (timer_dev < 0 || timer_dev >= oss_num_timers)
+ return OSS_HZ;
+
+ tmr = oss_timer_devs[timer_dev];
+ if (tmr == NULL)
+ return OSS_HZ;
+
+ if (tmr->prev_time >= tmr->next_time)
+ return OSS_HZ;
+
+ scale = 1000000 / OSS_HZ;
+ if (scale < 1)
+ return OSS_HZ;
+
+ t = tmr->next_time - tmr->prev_time;
+ t /= scale; /* Usecs to system clock ticks */
+
+ t += 1;
+
+ if (t > 1)
+ return t;
+
+ return OSS_HZ;
+}
+
+int
+oss_init_timers (oss_device_t * osdev)
+{
+ return 0;
+}
+
+int
+oss_uninit_timers (void)
+{
+ /* TODO: Cleanup all mutexes */
+ return 0;
+}