diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/framework/midi/oss_midi_timers.c | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/framework/midi/oss_midi_timers.c')
-rw-r--r-- | kernel/framework/midi/oss_midi_timers.c | 607 |
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; +} |