summaryrefslogtreecommitdiff
path: root/kernel/framework/midi/oss_midi_timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/midi/oss_midi_timers.c')
-rw-r--r--kernel/framework/midi/oss_midi_timers.c607
1 files changed, 607 insertions, 0 deletions
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;
+}