Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

midi/oss_midi_timers.c

MIDI timer support.

Description




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;
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site