diff options
Diffstat (limited to 'kernel/framework/midi')
-rw-r--r-- | kernel/framework/midi/.config | 2 | ||||
-rw-r--r-- | kernel/framework/midi/oss_default_timer.c | 204 | ||||
-rw-r--r-- | kernel/framework/midi/oss_midi_core.c | 1733 | ||||
-rw-r--r-- | kernel/framework/midi/oss_midi_mapper.c | 109 | ||||
-rw-r--r-- | kernel/framework/midi/oss_midi_parser.c | 406 | ||||
-rw-r--r-- | kernel/framework/midi/oss_midi_queue.c | 567 | ||||
-rw-r--r-- | kernel/framework/midi/oss_midi_timers.c | 607 |
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; +} |