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!

mixer/oss_mixer_core.c

OSS mixer core.

Description

Copyright (C) 4Front Technologies, 2002-2004. All rights reserved.



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>
#include <stdarg.h>
oss_mutex_t oss_timing_mutex;

char *oss_license_string = OSS_LICENSE;

#ifdef DO_TIMINGS
static int timing_is_active = 0; /* 1=The readtimings utility has been active */
#endif

void *mixer_devs_p = NULL;

Mixer device list

mixer_operations_t **mixer_devs = NULL;
int num_mixers = 0;
int mixer_port_number = 0;
int current_mixer_card = -1;


mix_cvt is used for scaling mixer levels by various drivers.

char mix_cvt[101] = {
  0, 0, 3, 7, 10, 13, 16, 19, 21, 23, 26, 28, 30, 32, 34, 35, 37, 39, 40, 42,
  43, 45, 46, 47, 49, 50, 51, 52, 53, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
  65,
  65, 66, 67, 68, 69, 70, 70, 71, 72, 73, 73, 74, 75, 75, 76, 77, 77, 78, 79,
  79,
  80, 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 89, 90,
  90,
  91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96, 96, 96, 97, 97, 98, 98, 98, 99,
  99,
  100
};

static int
get_mixer_info (int dev, ioctl_arg arg)
{
  mixer_info *info = (mixer_info *) arg;

  memset(info, 0, sizeof(*info));

  if (dev < 0 || dev >= num_mixers)
    return OSS_ENXIO;

  strcpy (info->id, mixer_devs[dev]->id);
  strncpy (info->name, mixer_devs[dev]->name, 32);
  info->name[31] = '\0';
  info->modify_counter = mixer_devs[dev]->modify_counter;

  return 0;
}


Legacy mixer handling


int
oss_legacy_mixer_ioctl (int mixdev, int audiodev, unsigned int cmd,
			ioctl_arg arg)
{
  int ret;

#ifdef DO_TIMINGS
    oss_timing_printf ("oss_legacy_mixer_ioctl(%d/%d, %08x) entered", mixdev,
	     audiodev, cmd);
#endif

  if (!(cmd & SIOC_OUT) && !(cmd & SIOC_IN))
    return OSS_EINVAL;

  if (arg == 0)
    return OSS_EINVAL;

  if (((cmd >> 8) & 0xff) != 'M')	/* Not a mixer ioctl */
    return OSS_EINVAL;

  if (mixdev < 0 || mixdev >= num_mixers)
    {
      cmn_err (CE_WARN, "Bad mixer device %d\n", mixdev);
      return OSS_EIO;
    }

  if (cmd == SOUND_MIXER_INFO)
    return get_mixer_info (mixdev, arg);

  if (!mixer_devs[mixdev]->enabled || mixer_devs[mixdev]->unloaded)
    return OSS_ENXIO;

  if (mixer_devs[mixdev]->d->ioctl == NULL)
    return OSS_EINVAL;

  if (IOC_IS_OUTPUT (cmd))
    mixer_devs[mixdev]->modify_counter++;

  ret = mixer_devs[mixdev]->d->ioctl (mixdev, audiodev, cmd, arg);

  return ret;
}


Handling of initial mixer volumes


static int muted_mixer_levels[32] = {0};

static int default_mixer_levels[32] = {
  0x3232,			/* Master Volume */
  0x3232,			/* Bass */
  0x3232,			/* Treble */
  0x4b4b,			/* FM */
  0x3232,			/* PCM */
  0x1515,			/* PC Speaker */
  0x2020,			/* Ext Line */
  0x2020,			/* Mic */
  0x4b4b,			/* CD */
  0x0000,			/* Recording monitor */
  0x4b4b,			/* Second PCM */
  0x4b4b,			/* Recording level */
  0x4b4b,			/* Input gain */
  0x4b4b,			/* Output gain */
  0x2020,			/* Line1 */
  0x2020,			/* Line2 */
  0x1515			/* Line3 (usually line in) */
};


Table for configurable mixer volume handling

static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
static int num_mixer_volumes = 0;

int *
load_mixer_volumes (char *name, int *levels, int present)
{
  int i, n;
  extern int mixer_muted;

  if (mixer_muted)		/* Config setting from osscore.conf */
     return muted_mixer_levels;

  if (levels == NULL)
    levels = default_mixer_levels;

  for (i = 0; i < num_mixer_volumes; i++)
    if (strcmp (name, mixer_vols[i].name) == 0)
      {
	if (present)
	  mixer_vols[i].num = i;
	return mixer_vols[i].levels;
      }

  if (num_mixer_volumes >= MAX_MIXER_DEV)
    {
      cmn_err (CE_WARN, "Too many mixers (%s/%d/%d)\n",
	       name, num_mixer_volumes, MAX_MIXER_DEV);
      return levels;
    }

  n = num_mixer_volumes++;

  strcpy (mixer_vols[n].name, name);

  if (present)
    mixer_vols[n].num = n;
  else
    mixer_vols[n].num = -1;

  for (i = 0; i < 32; i++)
    mixer_vols[n].levels[i] = levels[i];
  return mixer_vols[n].levels;
}


Mixer "extension" handling


static int
oss_mixer_ext_info (oss_mixext * ent)
{

  int dev, ctrl;
  int extnr;

  if (ent == NULL)
    return OSS_EFAULT;

  if (ent->dev < 0 || ent->dev >= num_mixers)
    return OSS_ENXIO;

  dev = ent->dev;
  if (!mixer_devs[dev]->enabled || mixer_devs[dev]->unloaded)
    return OSS_ENXIO;
  touch_mixer (dev);

  ctrl = ent->ctrl;
  if (ent->ctrl < 0 || ent->ctrl >= mixer_devs[dev]->nr_ext)
    return OSS_EIDRM;
  extnr = ent->ctrl;

  memcpy ((char *) ent, (char *) &mixer_devs[dev]->extensions[extnr].ext,
	  sizeof (*ent));

  switch (ent->type)
    {
    case MIXT_MONOPEAK:
    case MIXT_STEREOPEAK:
    case MIXT_MONOVU:
    case MIXT_STEREOVU:
      /* Peak meters will need to be polled */
      ent->flags |= MIXF_POLL;
      break;
    }


Read-only controls are likely to change their value spontaneously so they should be polled.

  if (!(ent->flags & MIXF_WRITEABLE))
     ent->flags |= MIXF_POLL;

  ent->ctrl = ctrl;
  return 0;
}

static int
mixer_ext_get_enuminfo (oss_mixer_enuminfo * ent)
{

  int dev, ctrl;

  if (ent == NULL)
    return OSS_EFAULT;

  dev = ent->dev;
  ctrl = ent->ctrl;
  memset (ent, 0, sizeof (*ent));

  if (dev < 0 || dev >= num_mixers)
    {
      return OSS_ENXIO;
    }

  touch_mixer (dev);

  if (ctrl < 0 || ctrl >= mixer_devs[dev]->nr_ext)
    {
      return OSS_EIDRM;
    }

  if (mixer_devs[dev]->extensions[ctrl].enum_info == NULL)
    {
      return OSS_EIO;
    }

  memcpy ((char *) ent,
	  (char *) mixer_devs[dev]->extensions[ctrl].enum_info,
	  sizeof (*ent));
  ent->ctrl = ctrl;
  return 0;
}

static int
mixer_ext_get_description (oss_mixer_enuminfo * ent)
{

  int dev, ctrl;
  char *s;

  if (ent == NULL)
    return OSS_EFAULT;

  dev = ent->dev;
  ctrl = ent->ctrl;
  memset (ent, 0, sizeof (*ent));

  if (dev < 0 || dev >= num_mixers)
    {
      return OSS_ENXIO;
    }

  touch_mixer (dev);

  if (ctrl < 0 || ctrl >= mixer_devs[dev]->nr_ext)
    {
      return OSS_EIDRM;
    }

  if (mixer_devs[dev]->extensions[ctrl].description == NULL)
    {
      return OSS_EIO;
    }

  s = mixer_devs[dev]->extensions[ctrl].description;

  strncpy (ent->strings, s, sizeof (ent->strings));
  ent->strings [sizeof (ent->strings) - 1] = '\0';
  ent->ctrl = ctrl;
  return 0;
}

int
mixer_ext_set_strings (int dev, int ctl, const char *s, int version)
{

Note! The version parameter should usually be set to 0. However if the value set can change dynamically then it must be initially set to 1 and later incremented every time the value set changes. This tells the applications to poll for updated values.

  static oss_mixer_enuminfo ent;

  memset (&ent, 0, sizeof (ent));
  ent.dev = dev;
  ent.ctrl = ctl;
  ent.version = version;
  ent.nvalues = 0;
  strcpy (ent.strings, s);
  return mixer_ext_set_enum (&ent);
}

static int
rebuild_list (oss_mixer_enuminfo * ent)
{
  int i, n, l;

  n = 1;
  ent->nvalues = 0;
  ent->strindex[0] = 0;
  l = strlen (ent->strings);

  for (i = 0; i < l; i++)
    if (n < OSS_ENUM_MAXVALUE)
      {
	if (ent->strings[i] == ' ')
	  {
	    ent->strindex[n++] = i + 1;
	    ent->strings[i] = 0;
	  }
      }

  ent->nvalues = n;
  return 0;
}

int
mixer_ext_set_enum (oss_mixer_enuminfo * ent)
{
  int dev;
  int extnr;

  if (ent == NULL)
    return OSS_EFAULT;

  if (ent->dev < 0 || ent->dev >= num_mixers)
    return OSS_ENXIO;

  dev = ent->dev;
  touch_mixer (dev);

  if (ent->ctrl < 0 || ent->ctrl >= mixer_devs[dev]->nr_ext)
    return OSS_EIDRM;
  extnr = ent->ctrl;

  if (mixer_devs[dev]->extensions[extnr].enum_info == NULL)
    mixer_devs[dev]->extensions[extnr].enum_info =
      PMALLOC (mixer_devs[dev]->osdev, sizeof (*ent));

  if (mixer_devs[dev]->extensions[extnr].enum_info == NULL)
    return OSS_EIO;

  memcpy ((char *) mixer_devs[dev]->extensions[extnr].enum_info,
	  (char *) ent, sizeof (*ent));

  ent = mixer_devs[dev]->extensions[extnr].enum_info;

  if (ent->nvalues <= 0)
    return rebuild_list (ent);

  if (ent->nvalues >= OSS_ENUM_MAXVALUE)
    {
      mixer_devs[dev]->extensions[extnr].enum_info = NULL;
      return OSS_EIO;
    }

  return 0;
}

int
mixer_ext_set_description (int dev, int ctrl, const char *desc)
{
  int l = strlen(desc);

  if (dev < 0 || dev >= num_mixers)
    return OSS_ENXIO;

  touch_mixer (dev);

  if (ctrl < 0 || ctrl >= mixer_devs[dev]->nr_ext)
    return OSS_EIDRM;

  if (l > OSS_ENUM_STRINGSIZE) l = OSS_ENUM_STRINGSIZE;

    mixer_devs[dev]->extensions[ctrl].description =
      PMALLOC (mixer_devs[dev]->osdev, l);

  if (mixer_devs[dev]->extensions[ctrl].description == NULL)
    return OSS_EIO;

  strncpy (mixer_devs[dev]->extensions[ctrl].description, desc, l);
  mixer_devs[dev]->extensions[ctrl].description[l-1] = '\0';

  mixer_devs[dev]->extensions[ctrl].ext.flags |= MIXF_DESCR;

  return 0;
}

static int
mixer_ext_read (oss_mixer_value * val)
{

  int dev;
  int extnr;
  oss_mixext *ext;
  oss_mixext_desc *ext_desc;
  mixer_ext_fn func;

  if (val == NULL)
    return OSS_EFAULT;

  if (val->dev < 0 || val->dev >= num_mixers)
    return OSS_ENXIO;

  dev = val->dev;

  if (!mixer_devs[dev]->enabled || mixer_devs[dev]->unloaded)
    return OSS_ENXIO;
  touch_mixer (dev);

  if (val->ctrl < 0 || val->ctrl >= mixer_devs[dev]->nr_ext)
    return OSS_EIDRM;
  extnr = val->ctrl;

  ext_desc = &mixer_devs[dev]->extensions[extnr];
  ext = &ext_desc->ext;

  if (val->timestamp != ext->timestamp)
    {
      return OSS_EIDRM;
    }

  if (ext_desc->handler == NULL || !(ext->flags & MIXF_READABLE))
    return OSS_EFAULT;

  func = (mixer_ext_fn) ext_desc->handler;
  return (val->value = func (dev, ext->ctrl, SNDCTL_MIX_READ, val->value));
}

static int
mixer_ext_write (oss_mixer_value * val)
{

  int dev;
  int extnr;
  int err;
  oss_mixext *ext;
  oss_mixext_desc *ext_desc;
  mixer_ext_fn func;

  if (val == NULL)
    {
      cmn_err (CE_WARN, "NULL argument in mixer call\n");
      return OSS_EFAULT;
    }

  if (val->dev < 0 || val->dev >= num_mixers)
    return OSS_ENXIO;

  dev = val->dev;
  if (!mixer_devs[dev]->enabled || mixer_devs[dev]->unloaded)
    return OSS_ENXIO;
  touch_mixer (dev);

  if (val->ctrl < 0 || val->ctrl >= mixer_devs[dev]->nr_ext)
    return OSS_EIDRM;
  extnr = val->ctrl;

  ext_desc = &mixer_devs[dev]->extensions[extnr];
  ext = &ext_desc->ext;

  if (ext_desc->handler == NULL || !(ext->flags & MIXF_WRITEABLE))
    {
      cmn_err (CE_WARN, "NULL handler or control not writeable\n");
      return OSS_EFAULT;
    }

  if (val->timestamp != ext->timestamp)
    {
      return OSS_EIDRM;
    }

  func = (mixer_ext_fn) ext_desc->handler;
  mixer_devs[dev]->modify_counter++;
  err = val->value = func (dev, ext->ctrl, SNDCTL_MIX_WRITE, val->value);

  if (err >= 0)
    ext->update_counter++;
  return err;
}

int
mixer_ext_create_device (int dev, int maxentries)
{
  oss_mixext_root *mixroot;
  oss_mixext *mixext;
  oss_mixext_desc *mixext_desc;
  char *name = mixer_devs[dev]->name;
  char *id = mixer_devs[dev]->id;
  int i;

  maxentries++;			/* Needs space for the device root node */

  if (mixer_devs[dev]->max_ext == 0)
     {
        mixext_desc =
          PMALLOC (mixer_devs[dev]->osdev, sizeof (*mixext_desc) * maxentries);
        mixer_devs[dev]->max_ext = maxentries;
  	mixer_devs[dev]->extensions = mixext_desc;
     }
  else
     {
  	mixext_desc = mixer_devs[dev]->extensions;
     }

  if (mixext_desc == NULL)
    {
      cmn_err (CE_CONT, "Not enough memory for mixer%d (ext)\n", dev);
      return OSS_EIO;
    }

  mixer_devs[dev]->nr_ext = 1;
  mixer_devs[dev]->timestamp = GET_JIFFIES ();

  mixext = &mixext_desc->ext;
  mixext->dev = dev;
  mixext->ctrl = -1;		/* Undefined */
  mixext->type = MIXT_DEVROOT;
  mixext->maxvalue = 0;
  mixext->minvalue = 0;
  mixext->flags = 0;
  strcpy (mixext->id, "DEVROOT");
  mixext->parent = 0;		/* Link to itself */
  mixext->timestamp = mixer_devs[dev]->timestamp;
  mixext_desc->handler = NULL;
  memset (mixext->data, 0, sizeof (mixext->data));

  mixroot = (oss_mixext_root *) & mixext->data;

  for (i = 0; i < 15 && id[i]; i++)
    mixroot->id[i] = id[i];
  mixroot->id[15] = 0;

  for (i = 0; i < 47 && name[i]; i++)
    mixroot->name[i] = name[i];
  mixroot->name[47] = 0;

  return 0;
}

int
mixer_ext_create_group_flags (int dev, int parent, const char *id,
			      unsigned int flags)
{
  oss_mixext *mixext;
  oss_mixext_desc *mixext_desc, *parent_desc;
  int enumber;

  flags &= ~MIXF_DESCR;

  if (mixer_devs[dev]->extensions == NULL)
    {
      cmn_err (CE_WARN, "Mixer extensions not initialized for device %d\n",
	       dev);
      return OSS_EFAULT;
    }


Ensure that the parent node number is valid.

  if (parent < 0 || parent >= mixer_devs[dev]->nr_ext)
     parent = 0;

  parent_desc =
    &mixer_devs[dev]->extensions[parent];
  mixext = &parent_desc->ext;

  if (mixext->type != MIXT_DEVROOT && mixext->type != MIXT_GROUP)
     parent = 0; /* Point to the root group */

  if (mixer_devs[dev]->nr_ext >= mixer_devs[dev]->max_ext)
    {
      cmn_err (CE_WARN, "Out of mixer controls for device %d/%s (%d)\n", dev,
	       mixer_devs[dev]->name, mixer_devs[dev]->max_ext);
      return OSS_ENOSPC;
    }

  mixext_desc =
    &mixer_devs[dev]->extensions[(enumber = mixer_devs[dev]->nr_ext++)];
  mixext = &mixext_desc->ext;
  mixext->dev = dev;
  mixext->ctrl = -1;		/* Undefined */
  mixext->type = MIXT_GROUP;
  mixext->maxvalue = 0;
  mixext->minvalue = 0;
  mixext->flags = flags | MIXF_FLAT;	/* Will be unflattened later if required */
  strcpy (mixext->id, id);
  mixext->parent = parent;
  mixext_desc->handler = NULL;
  mixext_desc->enum_info = NULL;
  memset (mixext->enum_present, 0xff, sizeof (mixext->enum_present));
  mixext->timestamp = mixer_devs[dev]->timestamp;
  mixext->control_no = -1;
  mixext->desc = 0;
  memset (mixext->data, 0, sizeof (mixext->data));

  return enumber;
}

int
mixer_ext_create_group (int dev, int parent, const char *id)
{
  return mixer_ext_create_group_flags (dev, parent, id, 0);
}

int
mixer_ext_truncate (int dev, int index)
{
  if (index < mixer_devs[dev]->nr_ext)
    {
      mixer_devs[dev]->nr_ext = index;
      mixer_devs[dev]->modify_counter++;
      mixer_devs[dev]->timestamp++;
    }
  return 0;
}

static void expand_names (int dev);
static void unflatten_group (int dev, int group);
static void touch_parents (int dev, int group);

int
mixer_ext_create_control (int dev, int parent, int ctrl, mixer_ext_fn func,
			  int type, const char *id, int maxvalue, int flags)
{
  oss_mixext *mixext;
  oss_mixext_desc *mixext_desc, *parent_desc;
  int enumber;

  flags &= ~MIXF_DESCR;

  if (mixer_devs[dev]->extensions == NULL)
    {
      cmn_err (CE_WARN, "Mixer extensions not initialized for device %d\n",
	       dev);
      return OSS_EFAULT;
    }

  if (mixer_devs[dev]->nr_ext >= mixer_devs[dev]->max_ext)
    {
      cmn_err (CE_WARN, "Out of mixer controls for device %d/%s (%d)\n", dev,
	       mixer_devs[dev]->name, mixer_devs[dev]->max_ext);
      return OSS_ENOSPC;
    }

  if (func == NULL)		/* No access function */
    flags &= ~(MIXF_READABLE | MIXF_WRITEABLE);


Ensure that the parent node number is valid.

  if (parent < 0 || parent >= mixer_devs[dev]->nr_ext)
     parent = 0;

  parent_desc =
    &mixer_devs[dev]->extensions[parent];
  mixext = &parent_desc->ext;

  if (mixext->type != MIXT_DEVROOT && mixext->type != MIXT_GROUP)
     parent = 0; /* Point to the root group */


  mixext_desc =
    &mixer_devs[dev]->extensions[(enumber = mixer_devs[dev]->nr_ext++)];
  mixext = &mixext_desc->ext;
  mixext->dev = dev;
  mixext->ctrl = ctrl;
  mixext->type = type;
  mixext->maxvalue = maxvalue;
  mixext->minvalue = 0;
  mixext->flags = flags;
  strncpy (mixext->id, id, sizeof (mixext->id));
  mixext->id[sizeof (mixext->id) - 1] = 0;
  mixext->parent = parent;
  mixext->timestamp = mixer_devs[dev]->timestamp;
  mixext_desc->handler = (mixer_ext_fn) func;
  mixext_desc->enum_info = NULL;
  memset (mixext->data, 0, sizeof (mixext->data));
  memset (mixext->enum_present, 0xff, sizeof (mixext->enum_present));
  mixext->control_no = -1;
  mixext->desc = 0;


Perform name expansion too if it has already been done for the earlier controls. Note that this only gets done with rare devices that add/remove controls on fly.

TODO: Optimize this to expand only the current control. Scanning through all the controls may be bit time consuming with future devices having 100s of controls.

  if (mixer_devs[dev]->names_checked)
    expand_names (dev);
  else
    strcpy(mixext->extname, mixext->id);


Mark groups with tall controls such as peak meters and sliders as non-flat


  switch (type)
    {
    case MIXT_SLIDER:
    case MIXT_MONOSLIDER:
    case MIXT_STEREOSLIDER:
    case MIXT_MONOSLIDER16:
    case MIXT_STEREOSLIDER16:
    case MIXT_MONOVU:
    case MIXT_STEREOVU:
    case MIXT_MONOPEAK:
    case MIXT_STEREOPEAK:
    case MIXT_MONODB:
    case MIXT_STEREODB:
    case MIXT_3D:
      unflatten_group (dev, parent);
      break;
    }

  touch_parents(dev, parent);

  return enumber;
}


oss_mixext *
mixer_find_ext (int dev, int enumber)
{
  oss_mixext_desc *mixext;

  if (dev < 0 || dev >= num_mixers)
    {
      return NULL;
    }
  touch_mixer (dev);

  if (enumber < 0 || enumber >= mixer_devs[dev]->nr_ext)
    {
      return NULL;
    }

  mixext = &mixer_devs[dev]->extensions[enumber];

  return &mixext->ext;
}


Default read/write access functions

int
mixer_ext_rw (int dev, int ctrl, unsigned int cmd, int value)
{
  int err;

  if (cmd == SNDCTL_MIX_READ)
    {
      if ((err =
	   oss_legacy_mixer_ioctl (dev, -1, MIXER_READ (ctrl),
				   (ioctl_arg) & value)) < 0)
	return err;
      return value;
    }

  if (cmd == SNDCTL_MIX_WRITE)
    {
      if ((err =
	   oss_legacy_mixer_ioctl (dev, -1, MIXER_WRITE (ctrl),
				   (ioctl_arg) & value)) < 0)
	return err;
      return value;
    }

  return OSS_EINVAL;
}

int
mixer_ext_recrw (int dev, int ctrl, unsigned int cmd, int value)
{
  int caps, recmask, err;

  if ((err =
       oss_legacy_mixer_ioctl (dev, -1, SOUND_MIXER_READ_CAPS,
			       (ioctl_arg) & caps)) < 0)
    caps = SOUND_CAP_EXCL_INPUT;	/* Default */

  if ((err =
       oss_legacy_mixer_ioctl (dev, -1, SOUND_MIXER_READ_RECSRC,
			       (ioctl_arg) & recmask)) < 0)
    return err;

  if (cmd == SNDCTL_MIX_READ)
    return (recmask & (1 << ctrl)) ? 1 : 0;

  if (caps & SOUND_CAP_EXCL_INPUT)	/* Single recording source */
    recmask = 0;

  if (value)
    recmask |= (1 << ctrl);
  else
    recmask &= ~(1 << ctrl);

  if (recmask == 0)
    return 1;			/* Can't remove the only recording source */

  if ((err =
       oss_legacy_mixer_ioctl (dev, -1, SOUND_MIXER_WRITE_RECSRC,
			       (ioctl_arg) & recmask)) < 0)
    return err;


  return (recmask & (1 << ctrl)) ? 1 : 0;
}


Mixer extension initialization


static void
store_name (oss_mixext * thisrec, char *name)
{
  int i;

  while (*name == '.')
    name++;
  strncpy (thisrec->extname, name, 32);
  thisrec->extname[31] = '\0';

  name = thisrec->extname;
  for (i = 0; i < strlen (name); i++)
    if (name[i] >= 'A' && name[i] <= 'Z')
      name[i] += 32;
}

static char *
cut_name (char *name)
{
  char *s = name;
  while (*s)
    if (*s++ == '_')
      return s;

  if (name[0] == '@')
    return &name[1];

  return name;
}

#include "mixerdefs.h"

static void
find_enum_defs (oss_mixext * thisrec, int dev, int ctl)
{
  int i;

  for (i = 0; mixer_defs[i].name != NULL; i++)
    if (strcmp (thisrec->extname, mixer_defs[i].name) == 0)
      {
	mixer_ext_set_strings (dev, ctl, mixer_defs[i].strings, 0);
	return;
      }
}

static void
expand_names (int dev)
{
  int i, n;
  oss_mixext_desc *mixext_desc;

  n = mixer_devs[dev]->nr_ext;
  mixer_devs[dev]->names_checked = 1;

  if (n < 1)
    return;

  for (i = 0; i < n; i++)
    {
      char tmp[100], *name;
      int parent = 0;
      oss_mixext *thisrec = NULL, *parentrec = NULL;

      mixext_desc = &mixer_devs[dev]->extensions[i];
      thisrec = &mixext_desc->ext;

      switch (thisrec->type)
	{
	case MIXT_DEVROOT:
	  thisrec->extname[0] = 0;
	  break;

	case MIXT_GROUP:
	  parent = thisrec->parent;
	  mixext_desc = &mixer_devs[dev]->extensions[parent];
	  parentrec = &mixext_desc->ext;
	  name = cut_name (thisrec->id);
	  if (parentrec->extname[0] == 0)
	    strcpy (tmp, name);
	  else
	    sprintf (tmp, "%s.%s", parentrec->extname, name);
	  store_name (thisrec, tmp);
	  break;

	case MIXT_STEREOSLIDER:
	case MIXT_STEREOSLIDER16:
	case MIXT_STEREODB:
	case MIXT_STEREOVU:
	case MIXT_MONODB:
	case MIXT_MONOSLIDER:
	case MIXT_MONOSLIDER16:
	case MIXT_SLIDER:
	case MIXT_MONOVU:
	case MIXT_MONOPEAK:
	case MIXT_STEREOPEAK:
	case MIXT_ONOFF:
	case MIXT_MUTE:
	case MIXT_ENUM:
	case MIXT_VALUE:
	case MIXT_HEXVALUE:
	case MIXT_3D:
	  parent = thisrec->parent;
	  mixext_desc = &mixer_devs[dev]->extensions[parent];
	  parentrec = &mixext_desc->ext;
	  name = cut_name (thisrec->id);
	  if (*thisrec->id == 0 || *thisrec->id == '-')	/* Special (hidden) names */
	    strcpy (thisrec->extname, parentrec->extname);
	  else
	    {
	      sprintf (tmp, "%s.%s", parentrec->extname, name);
	      store_name (thisrec, tmp);

	      if (thisrec->type == MIXT_ENUM)
		find_enum_defs (thisrec, dev, i);
	    }
	  break;

	case MIXT_MARKER:
	  break;

	default:;
	}
    }

Fix duplicate names.


  for (i = 0; i < n; i++)
    {
      char tmp[100];
      int j, dupes = 0;
      oss_mixext *thisrec = NULL;

      mixext_desc = &mixer_devs[dev]->extensions[i];
      thisrec = &mixext_desc->ext;

      if (thisrec->type == MIXT_GROUP)
	continue;

      strcpy (tmp, thisrec->extname);

      for (j = i + 1; j < n; j++)
	{
	  oss_mixext_desc *mixext_desc2;
	  oss_mixext *thisrec2 = NULL;
	  mixext_desc2 = &mixer_devs[dev]->extensions[j];
	  thisrec2 = &mixext_desc2->ext;

	  if (thisrec2->type == MIXT_GROUP)
	    continue;

	  if (strcmp (thisrec2->extname, tmp) == 0)
	    dupes++;
	}

      if (dupes > 0)		/* Need to fix duplicates */
	{
	  int count = 1, len;
	  char tmp2[32];

	  for (j = i; j < n; j++)
	    {
	      oss_mixext_desc *mixext_desc2;
	      oss_mixext *thisrec2 = NULL;
	      mixext_desc2 = &mixer_devs[dev]->extensions[j];
	      thisrec2 = &mixext_desc2->ext;

	      if (thisrec2->type != MIXT_GROUP)
		if (strcmp (thisrec2->extname, tmp) == 0)
		  {
		    sprintf (tmp2, "%d", count++);
		    tmp2[31] = '\0';
		    len = strlen (thisrec2->extname);
		    if (len >= sizeof (thisrec2->extname) - strlen (tmp2))
		      len = sizeof (thisrec2->extname) - strlen (tmp2) - 1;
		    strcpy (thisrec2->extname + len, tmp2);
		  }
	    }
	}
    }
}

static void
unflatten_group (int dev, int group)
{

Clear the MIXF_FLAT flags from all parent groups (recursively):

  int n;
  oss_mixext_desc *mixext_desc;
  oss_mixext *thisrec = NULL;

  n = mixer_devs[dev]->nr_ext;

  if (n < 1)
    return;

  if (group <= 0 || group >= n)
    return;

  mixext_desc = &mixer_devs[dev]->extensions[group];
  thisrec = &mixext_desc->ext;

  if (thisrec->type != MIXT_GROUP)	/* Not a group */
    return;

  if (!(thisrec->flags & MIXF_FLAT))	/* Already unflattened */
    return;

  thisrec->flags &= ~MIXF_FLAT;

  if (thisrec->parent >= group)	/* Broken link */
    return;

  unflatten_group (dev, thisrec->parent);	/* Unflatten the parent */
}

static void
touch_parents (int dev, int group)
{
  int n;
  oss_mixext_desc *mixext_desc;
  oss_mixext *thisrec = NULL;

  n = mixer_devs[dev]->nr_ext;

  if (n < 1)
    return;

  if (group <= 0 || group >= n)
    return;

  mixext_desc = &mixer_devs[dev]->extensions[group];
  thisrec = &mixext_desc->ext;

  while (thisrec->type != MIXT_DEVROOT)
    {
      if (thisrec->type != MIXT_GROUP)	/* Not a group */
        return;

      thisrec->update_counter++;

      if (thisrec->parent >= group)	/* Broken link */
        return;

      unflatten_group (dev, thisrec->parent);	/* Unflatten the parent */

      mixext_desc = &mixer_devs[dev]->extensions[thisrec->parent];
      thisrec = &mixext_desc->ext;
    }

  thisrec->update_counter++;
}

#define INPUT_MASK (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN)
#define OUTPUT_MASK (SOUND_MASK_PCM|SOUND_MASK_ALTPCM|SOUND_MASK_VOLUME| \
		SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_SYNTH| \
		SOUND_MASK_IMIX|SOUND_MASK_REARVOL|SOUND_MASK_CENTERVOL| \
		SOUND_MASK_SIDEVOL)
#define MONITOR_MASK (SOUND_MASK_SPEAKER|SOUND_MASK_LINE|SOUND_MASK_LINE| \
		SOUND_MASK_MIC|SOUND_MASK_CD|SOUND_MASK_LINE1|SOUND_MASK_LINE2| \
		SOUND_MASK_LINE3|SOUND_MASK_DIGITAL1|SOUND_MASK_DIGITAL2| \
		SOUND_MASK_DIGITAL3|SOUND_MASK_MONO|SOUND_MASK_PHONE| \
		SOUND_MASK_RADIO|SOUND_MASK_VIDEO)

void
touch_mixer (int dev)
{
  int i, n, devmask, recmask, stereomask, grp, root = 0, caps = 0;
  static char *id[] = SOUND_DEVICE_NAMES;
  int created = 0;

  if (mixer_devs[dev] == NULL || mixer_devs[dev]->unloaded
      || !mixer_devs[dev]->enabled)
    {
      return;
    }


Create default mixer extension records if required.

  if (mixer_devs[dev]->nr_ext > 0)	/* Already initialized */
    return;


Compute number of required mixer extension entries


  n = mixer_devs[dev]->nr_extra_ext;	/* Reserve space for the actual driver */
  if (n < 20)
    n = 20;

  if (oss_legacy_mixer_ioctl
      (dev, -1, SOUND_MIXER_READ_CAPS, (ioctl_arg) & caps) < 0)
    caps = 0;			/* Error */

  if (oss_legacy_mixer_ioctl
      (dev, -1, SOUND_MIXER_READ_DEVMASK, (ioctl_arg) & devmask) < 0)
    goto skip;			/* Error */

  /* Remove devices that are handled otherwise */
  devmask &= ~mixer_devs[dev]->ignore_mask;

  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    if (devmask & (1 << i))	/* This control is supported */
      n++;

  n = n * 2;

  if (oss_legacy_mixer_ioctl
      (dev, -1, SOUND_MIXER_READ_RECMASK, (ioctl_arg) & recmask) < 0)
    goto skip;			/* Error */

  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    if (recmask & (1 << i))	/* This control is also recording device */
      n++;

  root = 0;

#ifdef CONFIG_OSSD
  n += 20;			/* Space for OSSD use */
#endif

  n = n + 5;			/* The marker entry and some spare space */
  if ((root = mixer_ext_create_device (dev, n)) < 0)
    return;			/* Error */
  created = 1;

  if (oss_legacy_mixer_ioctl (dev, -1, SOUND_MIXER_READ_STEREODEVS,
			      (ioctl_arg) & stereomask) < 0)
    stereomask = -1;		/* Assume all stereo */

  if (!(caps & SOUND_CAP_NOLEGACY))	/* Don't (re)export the legacy mixer */
    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
      if (devmask & (1 << i))
	{
	  if ((grp =
	       mixer_ext_create_group_flags (dev, root, id[i],
					     MIXF_LEGACY)) > 0)
	    {
	      int cnum;
	      oss_mixext *ent;
	      int flags = 0;


Set the type hints for main and PCM volume controls


	      switch (i)
		{
		case SOUND_MIXER_VOLUME:
		case SOUND_MIXER_MONO:
		case SOUND_MIXER_REARVOL:
		case SOUND_MIXER_CENTERVOL:
		case SOUND_MIXER_SIDEVOL:
		  flags |= MIXF_MAINVOL;
		  break;

		case SOUND_MIXER_PCM:
		case SOUND_MIXER_ALTPCM:
		  flags |= MIXF_PCMVOL;
		  break;

		case SOUND_MIXER_RECLEV:
		case SOUND_MIXER_IGAIN:
		  flags |= MIXF_RECVOL;
		  break;

		case SOUND_MIXER_SYNTH:
		case SOUND_MIXER_SPEAKER:
		case SOUND_MIXER_LINE:
		case SOUND_MIXER_LINE1:
		case SOUND_MIXER_LINE2:
		case SOUND_MIXER_LINE3:
		case SOUND_MIXER_MIC:
		case SOUND_MIXER_CD:
		case SOUND_MIXER_DIGITAL1:
		case SOUND_MIXER_DIGITAL2:
		case SOUND_MIXER_DIGITAL3:
		case SOUND_MIXER_PHONE:
		case SOUND_MIXER_VIDEO:
		case SOUND_MIXER_RADIO:
		  flags |= MIXF_MONVOL;
		  break;
		}

	      if (stereomask & (1 << i))
		cnum = mixer_ext_create_control (dev, grp, i, mixer_ext_rw,
						 MIXT_STEREOSLIDER,
						 "", 100,
						 flags | MIXF_READABLE |
						 MIXF_WRITEABLE);
	      else
		cnum = mixer_ext_create_control (dev, grp, i, mixer_ext_rw,
						 MIXT_MONOSLIDER,
						 "", 100,
						 flags | MIXF_READABLE |
						 MIXF_WRITEABLE);

	      if ((ent = mixer_find_ext (dev, cnum)) != NULL)
		{
		  ent->control_no = i;

		  if ((1 << i) & INPUT_MASK)
		    ent->desc &= MIXEXT_SCOPE_INPUT;
		  if ((1 << i) & OUTPUT_MASK)
		    ent->desc &= MIXEXT_SCOPE_OUTPUT;
		  if ((1 << i) & MONITOR_MASK)
		    ent->desc &= MIXEXT_SCOPE_MONITOR;


Set the RGB color for some of the controls to match the usual jack color.


		      switch (i)
		      {
		      case SOUND_MIXER_MIC: ent->rgbcolor=OSS_RGB_PINK; break;
		      case SOUND_MIXER_LINE: ent->rgbcolor=OSS_RGB_BLUE; break;
		      case SOUND_MIXER_VOLUME: ent->rgbcolor=OSS_RGB_GREEN; break;
		      case SOUND_MIXER_REARVOL: ent->rgbcolor=OSS_RGB_BLACK; break;
		      case SOUND_MIXER_SIDEVOL: ent->rgbcolor=OSS_RGB_GRAY; break;
		      case SOUND_MIXER_CENTERVOL: ent->rgbcolor=OSS_RGB_ORANGE; break;
		      }
		 }

	      if (recmask & (1 << i))
		{
		  cnum =
		    mixer_ext_create_control (dev, grp, i, mixer_ext_recrw,
					      MIXT_ONOFF, "REC", 1,
					      MIXF_READABLE | MIXF_WRITEABLE | MIXF_RECVOL);
		  if ((ent = mixer_find_ext (dev, cnum)) != NULL)
		    {
		      ent->desc &= MIXEXT_SCOPE_RECSWITCH;
		    }
		}
	    }
	}

skip:
  if (!created)
    if ((root = mixer_ext_create_device (dev, n)) < 0)
      return;			/* Error */
  mixer_ext_create_control (dev, root, 0, NULL, MIXT_MARKER, "", 0, 0);

  if (mixer_devs[dev]->create_controls != NULL)
    mixer_devs[dev]->create_controls (dev);
  expand_names (dev);
}

int
mixer_ext_set_init_fn (int dev, mixer_create_controls_t func, int nextra)
{

Set device dependent mixer extension initialization function and reserve some extension entries for device dependent use.

This initialization function will be called later when/if the extended mixer is actually used.

  if (dev < 0 || dev >= num_mixers)
    return OSS_ENXIO;

  mixer_devs[dev]->nr_extra_ext = nextra;
  mixer_devs[dev]->create_controls = func;
  return 0;
}

int
mixer_ext_rebuild_all (int dev, mixer_create_controls_t func, int nextra)
{

Throw away all existing mixer controls and recreate the mixer.

  if (dev < 0 || dev >= num_mixers)
    return OSS_ENXIO;
  mixer_devs[dev]->nr_ext = 0;

  mixer_devs[dev]->nr_extra_ext = nextra;
  mixer_devs[dev]->create_controls = func;

  touch_mixer (dev);
  if (mixer_devs[dev]->create_vmix_controls != NULL)
     {
        mixer_devs[dev]->create_vmix_controls(dev);
     }

  return 0;
}

int
mixer_ext_set_vmix_init_fn (int dev, mixer_create_controls_t func, int nextra,
			    void *devc)
{

Set device dependent mixer extension initialization function and reserve some extension entries for device dependent use.

This initialization function will be called later when/if the extended mixer is actually used.

  if (dev < 0 || dev >= num_mixers)
    return OSS_ENXIO;

  mixer_devs[dev]->nr_extra_ext += nextra;
  mixer_devs[dev]->create_vmix_controls = func;
  mixer_devs[dev]->vmix_devc = devc;
  touch_mixer (dev);
  func (dev);
  expand_names (dev);
  return 0;
}

#ifdef VDEV_SUPPORT
static void
ainfo_combine_caps (oss_audioinfo * ainfo, adev_p adev)
{
  if (!(adev->flags & ADEV_NOOUTPUT))
    ainfo->caps |= DSP_CAP_OUTPUT;
  else
    ainfo->caps &= ~DSP_CAP_OUTPUT;

  if (!(adev->flags & ADEV_NOINPUT))
    ainfo->caps |= DSP_CAP_INPUT;
  else
    ainfo->caps &= ~DSP_CAP_INPUT;

  if (adev->flags & ADEV_DUPLEX)
    ainfo->caps |= DSP_CAP_DUPLEX;
  else
    ainfo->caps &= ~DSP_CAP_DUPLEX;

#ifdef ALLOW_BUFFER_MAPPING
  if (!(adev->flags & ADEV_NOMMAP))
    ainfo->caps |= DSP_CAP_MMAP;
#endif
}
#endif

#if 0

Device list support is currently not used

static int
check_list (oss_devlist_t * oldlist, oss_devlist_t * newlist)
{

Check that the same devices are present in both lists. Any difference indicates that the device configuration has changed (invalidates the list).

#if MAX_AUDIO_DEVFILES > 64
#error Too many audio devices - fix this algorithm
#endif
  unsigned long long mask1, mask2;
  int i;

  if (newlist->ndevs != oldlist->ndevs)
    return 0;

  mask1 = 0LL;
  mask2 = 0LL;

  for (i = 0; i < oldlist->ndevs; i++)
    mask1 |= 1LL << oldlist->devices[i];
  for (i = 0; i < newlist->ndevs; i++)
    mask1 |= 1LL << newlist->devices[i];

  if (mask1 != mask2)
    return 0;

  return 1;
}
#endif

static int
get_engineinfo (int dev, oss_audioinfo * info, int combine_slaves)
{
  int dev_present = 0;
  int i;
  oss_native_word flags;

  adev_p adev, next;

  flags = 0;
  memset ((char *) info, 0, sizeof (*info));

  if (dev < 0 || dev >= num_audio_engines)
    return OSS_ENXIO;

  adev = audio_engines[dev];
  if (adev == NULL)
    {
      cmn_err (CE_WARN, "Internal error - adev==NULL (%d)\n", dev);
      return OSS_ENXIO;
    }

  if (!adev->unloaded && adev->enabled)
    dev_present = 1;

  if (dev_present)
    {
      MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
    }
  info->dev = dev;
  strcpy (info->name, adev->name);
  strcpy (info->handle, adev->handle);
  info->busy = adev->open_mode;
  info->caps = adev->caps;
  if (!(adev->flags & ADEV_NOINPUT))
    info->caps |= PCM_CAP_INPUT;
  if (!(adev->flags & ADEV_NOOUTPUT))
    info->caps |= PCM_CAP_OUTPUT;
  if (adev->flags & ADEV_SPECIAL)
    info->caps |= PCM_CAP_SPECIAL;
  if (adev->flags & ADEV_VIRTUAL)
    info->caps |= PCM_CAP_VIRTUAL;

  if (adev->flags & (ADEV_HIDDEN | ADEV_SHADOW))
    {
      info->caps |= PCM_CAP_HIDDEN;
    }

  if (adev->flags & ADEV_DUPLEX)
    {
      info->caps |= PCM_CAP_DUPLEX;
    }
  if (adev->d->adrv_trigger)	/* Supports SETTRIGGER */
    info->caps |= PCM_CAP_TRIGGER;
#ifdef ALLOW_BUFFER_MAPPING
  if (!(adev->flags & ADEV_NOMMAP))
    info->caps |= PCM_CAP_MMAP;
#endif

  info->oformats = adev->oformat_mask;
  info->iformats = adev->iformat_mask;
  info->pid = adev->pid;
  info->latency = adev->latency;
  *info->cmd = 0;
  strncpy (info->cmd, adev->cmd, sizeof (info->cmd));
  info->cmd[sizeof (info->cmd) - 1] = 0;

  strcpy (info->devnode, adev->devnode);

  if (!adev->unloaded && adev->enabled)
    {
      if (audio_engines[dev]->d->adrv_ioctl (dev, SNDCTL_GETSONG,
					     (ioctl_arg) info->song_name) ==
	  OSS_EINVAL)
	strcpy (info->song_name, adev->song_name);
      if (audio_engines[dev]->d->adrv_ioctl (dev, SNDCTL_GETLABEL,
					     (ioctl_arg) info->label) ==
	  OSS_EINVAL)
	strcpy (info->label, adev->label);
    }

  if (*info->label == 0)
    {
      strncpy (info->label, info->cmd, sizeof (info->label));
      info->label[sizeof (info->label) - 1] = 0;
    }

  info->magic = adev->magic;
  info->card_number = adev->card_number;
  info->port_number = adev->port_number;
  info->mixer_dev = adev->mixer_dev;
  info->legacy_device = adev->real_dev;
  info->rate_source = adev->rate_source;
  info->enabled = (adev->enabled && !adev->unloaded);
  info->flags = adev->flags;
  info->min_rate = adev->min_rate;
  info->max_rate = adev->max_rate;
  info->min_channels = adev->min_channels;
  info->max_channels = adev->max_channels;
  info->binding = adev->binding;
  info->nrates = adev->nrates;
  for (i = 0; i < info->nrates; i++)
    info->rates[i] = adev->rates[i];

  if (adev->next_out == NULL || !dev_present)
    info->next_play_engine = 0;
  else
    {
      info->next_play_engine = adev->next_out->engine_num;
      next = adev->next_out;

#ifdef VDEV_SUPPORT
      i = 0;
      while (combine_slaves && next != NULL && i++ < num_audio_engines)
	{
	  ainfo_combine_caps (info, next);
	  next = next->next_out;
	}
#endif
    }

  if (adev->next_in == NULL || !dev_present)
    info->next_rec_engine = 0;
  else
    {
      info->next_rec_engine = adev->next_in->engine_num;
      next = adev->next_in;

#ifdef VDEV_SUPPORT
      i=0;
      while (combine_slaves && next != NULL && i++ < num_audio_engines)
	{
	  ainfo_combine_caps (info, next);
	  next = next->next_in;
	}
#endif
    }

  if (dev_present)
    {
      MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
    }
  return 0;
}

#ifdef CONFIG_OSS_VMIX
static int
vmixctl_attach(vmixctl_attach_t *att)
{
	int err;
	oss_device_t *osdev;

	if (att->masterdev<0 || att->masterdev >= num_audio_engines)
	   return OSS_ENXIO;

	if (att->inputdev != -1)
	if (att->inputdev<0 || att->inputdev >= num_audio_engines)
	   return OSS_ENXIO;

	osdev=audio_engines[att->masterdev]->master_osdev;

	if ((err=vmix_attach_audiodev(osdev, att->masterdev, att->inputdev, att->attach_flags))<0)
	   return err;

	return 0;
}

static int
vmixctl_detach(vmixctl_attach_t *att)
{
	int err;
	oss_device_t *osdev;

	if (att->masterdev<0 || att->masterdev >= num_audio_engines)
	   return OSS_ENXIO;

	osdev=audio_engines[att->masterdev]->master_osdev;

	if ((err=vmix_detach_audiodev(att->masterdev))<0)
	   return err;

	return 0;
}

static int
vmixctl_rate(vmixctl_rate_t *rate)
{
	int err;

	if (rate->masterdev<0 || rate->masterdev >= num_audio_engines)
	   return OSS_ENXIO;

	if ((err=vmix_set_master_rate(rate->masterdev, rate->rate))<0)
	   return err;

	return 0;
}
#endif

int
oss_mixer_ext (int orig_dev, int class, unsigned int cmd, ioctl_arg arg)
{
  int val;
  int combine_slaves = 0;
#ifdef MANAGE_DEV_DSP
#ifdef VDEV_SUPPORT
  extern void oss_combine_write_lists (void);
#endif
#endif

  switch (cmd)
    {
    case SNDCTL_SYSINFO:	/* Formerly OSS_SYSINFO */
      {
	oss_sysinfo *info = (oss_sysinfo *) arg;
	int i;

	memset (info, 0, sizeof (*info));
	strcpy (info->product, "OSS");
	strncpy (info->version, OSS_VERSION_STRING, sizeof (info->version));
	info->version [sizeof (info->version) - 1] = '\0';
	strcpy (info->license, oss_license_string);
	info->versionnum = OSS_VERSION;

#ifdef OSS_HG_INFO
	/* Detailed Mercurial version */
	strncpy (info->revision_info, OSS_HG_INFO, sizeof(info->revision_info));
	info->revision_info[sizeof(info->revision_info)-1]=0;
#endif

	memset (info->options, 0, sizeof (info->options));

	info->numaudios = num_audio_devfiles;
	info->numaudioengines = num_audio_engines;
	for (i = 0; i < 8; i++)
	  info->openedaudio[i] = 0;
	for (i = 0; i < num_audio_engines; i++)
	  if (audio_engines[i] != NULL)
	    {
	      if (audio_engines[i]->flags & ADEV_OPENED)
		if (audio_engines[i]->next_out != NULL)
		  {
		    int x = audio_engines[i]->real_dev;
		    info->openedaudio[x / 32] |= 1 << (x % 32);
		  }
	    }
	for (i = 0; i < 8; i++)
	  info->openedmidi[i] = 0;
	for (i = 0; i < num_mididevs; i++)
	  if (midi_devs[i]->open_mode != 0)
	    info->openedmidi[i / 32] |= 1 << (i % 32);

	info->numsynths = 0;
#ifdef CONFIG_OSS_MIDI
	info->nummidis = num_mididevs;
#endif
	info->numtimers = oss_num_timers;
	info->nummixers = num_mixers;
	info->numcards = oss_num_cards;

	return 0;
      }
      break;

    case SNDCTL_MIX_NRMIX:
      return *arg = num_mixers;
      break;

    case SNDCTL_MIX_NREXT:	/* Return # of mixer extensions for device */
      val = *arg;
      *arg = 0;
      if (val==-1)
	 val=orig_dev;
      if (val < 0 || val >= num_mixers)
	return OSS_ENXIO;
      if (mixer_devs[val] == NULL || mixer_devs[val]->unloaded
	  || !mixer_devs[val]->enabled)
	{
	  return OSS_ENXIO;
	}


      touch_mixer (val);
      return *arg = mixer_devs[val]->nr_ext;
      break;

    case SNDCTL_MIX_EXTINFO:
      return oss_mixer_ext_info ((oss_mixext *) arg);
      break;

    case SNDCTL_MIX_ENUMINFO:
      return mixer_ext_get_enuminfo ((oss_mixer_enuminfo *) arg);
      break;

    case SNDCTL_MIX_DESCRIPTION:
      return mixer_ext_get_description ((oss_mixer_enuminfo *) arg);
      break;

    case SNDCTL_MIX_READ:
      return mixer_ext_read ((oss_mixer_value *) arg);
      break;

    case SNDCTL_MIX_WRITE:
      return mixer_ext_write ((oss_mixer_value *) arg);
      break;

    case OSS_GETVERSION:
      return *arg = OSS_VERSION;
      break;

    case SNDCTL_AUDIOINFO:
    case SNDCTL_AUDIOINFO_EX:
      {
	int dev;
	oss_audioinfo *info = (oss_audioinfo *) arg;

	if (info == NULL)
	  return OSS_EFAULT;

	dev = info->dev;

	if (dev == -1)		/* Request for the current device */
	   {
	      oss_audio_set_error (orig_dev, E_PLAY,
				   OSSERR (1022,
					   "SNDCTL_AUDIOINFO called with dev=-1.."),
				   0);

Errordesc: Applications that try to obtain audio device information about the current device should call SNDCTL_ENGINEINFO instead of SNDCTL_AUDIOINFO.

Audio file descriptors returned by open(2) are bound directly to specific audio engine instead of the device file.

	      return OSS_EINVAL;
	  }

	if (dev < 0 || dev >= num_audio_devfiles)
	  {
	    return OSS_EINVAL;
	  }

	if (audio_devfiles[dev] == NULL)
	  {
	    return OSS_EIO;
	  }

	if (dev >= 0)
	  {
	    dev = audio_devfiles[dev]->engine_num;	/* Get the engine number */
	    if (cmd == SNDCTL_AUDIOINFO && info->dev != -1)
	      combine_slaves = 1;
	  }
	return get_engineinfo (dev, info, combine_slaves);
      }
      break;

    case SNDCTL_ENGINEINFO:
      {
	int dev;
	oss_audioinfo *info = (oss_audioinfo *) arg;

	if (info == NULL)
	  return OSS_EFAULT;

	dev = info->dev;

	if (dev == -1)		/* Request for the current device */
	  switch (class)
	    {
	    case OSS_DEV_DSP:
	    case OSS_DEV_DSP_ENGINE:
	      dev = orig_dev;
	      break;

	    default:
	      cmn_err(CE_WARN, "Unrecognized device class %d for dev %d\n", class, orig_dev);
	      return OSS_EINVAL;
	    }

	if (dev < 0 || dev >= num_audio_engines)
	  {
	    return OSS_EINVAL;
	  }

	return get_engineinfo (dev, info, 0);
      }
      break;

#ifdef CONFIG_OSS_MIDI
    case SNDCTL_MIDIINFO:
      {
	int dev;
	oss_native_word flags;
	extern int oss_num_midi_clients;	/* midi.c */

	oss_midi_info *info = (oss_midi_info *) arg;
	mididev_t *mdev;

	dev = info->dev;

	if (dev == -1)		/* Request for the current device */
	  switch (class)
	    {
	    case OSS_DEV_MIDI:

Figure out the HW device connected to this client (orig_dev).

	      dev = orig_dev;
	      if (dev < 0 || dev >= oss_num_midi_clients)
		return OSS_ENXIO;
	      if (oss_midi_clients[dev]->mididev == NULL)
		return OSS_EBUSY;	/* No binding established (yet) */
	      dev = oss_midi_clients[dev]->mididev->dev;
	      break;

	    default:
	      return OSS_EINVAL;
	    }

	if (dev < 0 || dev >= num_mididevs)
	  {
	    return OSS_EINVAL;
	  }

	memset ((char *) info, 0, sizeof (*info));

	mdev = midi_devs[dev];
	MUTEX_ENTER_IRQDISABLE (mdev->mutex, flags);
	info->dev = dev;
	strcpy (info->name, mdev->name);
	strcpy (info->handle, mdev->handle);
	info->pid = mdev->pid;
	info->busy = mdev->open_mode;
	*info->cmd = 0;
	strncpy (info->cmd, mdev->cmd, sizeof (info->cmd));
	info->cmd[sizeof (info->cmd) - 1] = 0;
	info->magic = mdev->magic;
	info->card_number = mdev->card_number;
	strcpy (info->devnode, mdev->devnode);
	info->legacy_device = mdev->real_dev;
	info->port_number = mdev->port_number;
	info->enabled = mdev->enabled;
	info->flags = mdev->flags;
	info->caps = mdev->caps;
	info->latency = mdev->latency;
	if (!(info->caps & MIDI_CAP_INOUT))
	  info->caps |= MIDI_CAP_INOUT;
	if (mdev->flags & MFLAG_VIRTUAL)
	  info->caps |= MIDI_CAP_VIRTUAL;
	if (mdev->flags & MFLAG_CLIENT)
	  info->caps |= MIDI_CAP_CLIENT;
	if (mdev->flags & MFLAG_SERVER)
	  info->caps |= MIDI_CAP_SERVER;
	if (mdev->flags & MFLAG_INTERNAL)
	  info->caps |= MIDI_CAP_INTERNAL;
	if (mdev->flags & MFLAG_EXTERNAL)
	  info->caps |= MIDI_CAP_EXTERNAL;
	if (mdev->flags & MFLAG_MTC)
	  info->caps |= MIDI_CAP_MTC;
	if (midi_devs[dev]->enabled && !midi_devs[dev]->unloaded)
	  if (midi_devs[dev]->d->ioctl)
	    {
	      midi_devs[dev]->d->ioctl (dev, SNDCTL_GETSONG,
					(ioctl_arg) info->song_name);
	      midi_devs[dev]->d->ioctl (dev, SNDCTL_GETLABEL,
					(ioctl_arg) info->label);
	    }
	if (*info->label == 0)
	  {
	    strncpy (info->label, info->cmd, sizeof (info->label));
	    info->label[sizeof (info->label) - 1] = 0;
	  }
	MUTEX_EXIT_IRQRESTORE (mdev->mutex, flags);
      }
      return 0;
      break;
#endif

    case SNDCTL_CARDINFO:
      {
	int card, err;

	oss_card_info *info = (oss_card_info *) arg;
	card = info->card;

	if (card < 0 || card >= oss_num_cards)
	  {
	    return OSS_ENXIO;
	  }

	memset ((char *) info, 0, sizeof (*info));
	if ((err = oss_get_cardinfo (card, info)) < 0)
	  return err;
	info->card = card;
      }
      return 0;
      break;

    case SNDCTL_MIXERINFO:
      {
	int dev;

	oss_mixerinfo *info = (oss_mixerinfo *) arg;
	mixer_operations_t *mdev;

	dev = info->dev;

	if (dev == -1)		/* Request for the current device */
	  switch (class)
	    {
	    case OSS_DEV_MIXER:
	      dev = orig_dev;
	      break;

	    default:
	      return OSS_EINVAL;
	    }

	if (dev < 0 || dev >= num_mixers)
	  {
	    return OSS_ENXIO;
	  }

	if (mixer_devs[dev] == NULL)
	  return OSS_ENXIO;

	memset ((char *) info, 0, sizeof (*info));
	touch_mixer (dev);

	mdev = mixer_devs[dev];
	info->dev = dev;
	strncpy (info->name, mdev->name, sizeof (info->name));
	info->name[sizeof (info->name) - 1] = '\0';
	strcpy (info->id, mdev->id);
	strcpy (info->handle, mdev->handle);
	info->card_number = mdev->card_number;
	info->port_number = mdev->port_number;
	info->enabled = (mdev->enabled && !mdev->unloaded);
	info->magic = mdev->magic;
	info->caps = mdev->caps;
	info->flags = mdev->flags;
	info->modify_counter = mdev->modify_counter;
	info->nrext = mdev->nr_ext;
	info->priority = mdev->priority;
	strcpy (info->devnode, mdev->devnode);
	info->legacy_device = mdev->real_dev;
      }
      return 0;
      break;

    case OSSCTL_RENUM_AUDIODEVS:
      {
	oss_renumber_t *r = (oss_renumber_t *) arg;
	int i;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif

	if (r->n != num_audio_devfiles)	/* Wrong map size? */
	  {
	    cmn_err (CE_NOTE, "Legacy audio map size mismatch %d/%d\n",
		     r->n, num_audio_devfiles);
	    return OSS_EINVAL;
	  }

	for (i = 0; i < r->n; i++)
	  {
	    adev_p adev = audio_devfiles[i];

	    if (r->map[i] >= HARD_MAX_AUDIO_DEVFILES)	/* May be unnecessary check */
	      return OSS_EINVAL;

	    if (r->map[i] < -1)
	      r->map[i] = -1;

	    adev->real_dev = r->map[i];
	  }
      }
      return 0;
      break;

    case OSSCTL_RENUM_MIXERDEVS:
      {
	oss_renumber_t *r = (oss_renumber_t *) arg;
	int i;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif

	if (r->n != num_mixers)	/* Wrong map size? */
	  return OSS_EINVAL;

	for (i = 0; i < r->n; i++)
	  {
	    mixdev_p mdev = mixer_devs[i];

	    if (r->map[i] >= HARD_MAX_AUDIO_DEVFILES)	/* May be unnecessary check */
	      return OSS_EINVAL;

	    mdev->real_dev = r->map[i];
	  }
      }
      return 0;
      break;

    case OSSCTL_RENUM_MIDIDEVS:
      {
	oss_renumber_t *r = (oss_renumber_t *) arg;
	int i;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif

	if (r->n != num_mididevs)	/* Wrong map size? */
	  return OSS_EINVAL;

	for (i = 0; i < r->n; i++)
	  {
	    mididev_p mdev = midi_devs[i];

	    if (r->map[i] >= HARD_MAX_AUDIO_DEVFILES)	/* May be unnecessary check */
	      return OSS_EINVAL;

	    mdev->real_dev = r->map[i];
	  }
      }
      return 0;
      break;

#ifdef CONFIG_OSS_VMIX
    case VMIXCTL_ATTACH:
#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif
      return vmixctl_attach((vmixctl_attach_t*)arg);
      break;

    case VMIXCTL_DETACH:
#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif
      return vmixctl_detach((vmixctl_attach_t*)arg);
      break;

    case VMIXCTL_RATE:
#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif
      return vmixctl_rate((vmixctl_rate_t*)arg);
      break;
#endif

#if 0

These calls are obsolete and disabled in current OSS version.

    case OSSCTL_GET_REROUTE:
      {
	oss_reroute_t *r = (oss_reroute_t *) arg;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  {
	    return OSS_EINVAL;
	  }
#endif

	switch (r->mode)
	  {
	  case OPEN_READ:
	    memcpy (&r->devlist, &dspinlist, sizeof (oss_devlist_t));
	    break;

	  case OPEN_WRITE:
#ifdef MANAGE_DEV_DSP
#ifdef VDEV_SUPPORT
	    oss_combine_write_lists ();
#endif
#endif
	    memcpy (&r->devlist, &dspoutlist, sizeof (oss_devlist_t));
	    break;

	  case OPEN_WRITE | OPEN_READ:
	    memcpy (&r->devlist, &dspinoutlist, sizeof (oss_devlist_t));
	    break;

	  default:
	    return OSS_EINVAL;
	  }
      }
      return 0;
      break;

#if 0
    case OSSCTL_SET_REROUTE:
      {
	oss_reroute_t *r = (oss_reroute_t *) arg;
	int i, d;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif

	for (i = 0; i < r->devlist.ndevs; i++)
	  {
	    if ((d = r->devlist.devices[i]) < 0 || d >= num_audio_devfiles)
	      return OSS_EINVAL;
	  }

	switch (r->mode)
	  {
	  case OPEN_READ:
	    /* Refuse if number of devices has changed */
	    if (!check_list (&r->devlist, &dspinlist))
	      {
		return OSS_EINVAL;
	      }
	    memcpy (&dspinlist, &r->devlist, sizeof (oss_devlist_t));
	    break;

	  case OPEN_WRITE:
#ifdef MANAGE_DEV_DSP
#ifdef VDEV_SUPPORT
	    oss_combine_write_lists ();
#endif
#endif
	    /* Refuse if number of devices has changed */
	    if (!check_list (&r->devlist, &dspoutlist))
	      {
		return OSS_EINVAL;
	      }
	    memcpy (&dspoutlist, &r->devlist, sizeof (oss_devlist_t));
	    dspoutlist2.ndevs = 0;
	    break;

	  case OPEN_WRITE | OPEN_READ:
	    /* Refuse if number of devices has changed */
	    if (!check_list (&r->devlist, &dspinoutlist))
	      {
		return OSS_EINVAL;
	      }
	    memcpy (&dspinoutlist, &r->devlist, sizeof (oss_devlist_t));
	    break;

	  default:
	    return OSS_EINVAL;
	  }
      }
      return 0;
      break;
#endif

#ifdef APPLIST_SUPPORT
    case OSSCTL_RESET_APPLIST:
#ifdef GET_PROCESS_UID
      if (GET_PROCESS_UID () != 0)	/* Not root */
	return OSS_EINVAL;
#endif

      oss_applist_size = 0;
      return 0;
      break;

    case OSSCTL_ADD_APPLIST:
      {
	app_routing_t *def, *parm = (app_routing_t *) arg;

#ifdef GET_PROCESS_UID
	if (GET_PROCESS_UID () != 0)	/* Not root */
	  return OSS_EINVAL;
#endif

	if (oss_applist_size >= APPLIST_SIZE)
	  return OSS_ENOSPC;

	if (parm->dev < -1 || parm->dev >= num_audio_devfiles)
	  return OSS_ENXIO;

	def = &oss_applist[oss_applist_size];

	memset (def, 0, sizeof (*def));
	strcpy (def->name, parm->name);
	def->mode = parm->mode & (OPEN_READ | OPEN_WRITE);
	def->dev = parm->dev;
	def->open_flags = parm->open_flags;
	oss_applist_size++;
	return 0;
      }
      break;

#endif
#endif
    default:
#if 0
      if (mixer_devs[orig_dev]->d->ioctl != NULL)
         return mixer_devs[orig_dev]->d->ioctl (orig_dev, -1, cmd, arg);
#endif

      return OSS_EINVAL;
    }
}

/*ARGSUSED*/
static int
oss_mixer_open (int dev, int dev_type, struct fileinfo *file, int recursive,
		int open_flags, int *newdev)
{

Permit opening nonexistent mixer so that certain mixer ioctl calls can be called. Other code must check that the devices really exist before permitting the calls.

  if (dev >= 0 && dev < num_mixers)
    return 0;

  if (mixer_devs == NULL)
    return 0;

  if (mixer_devs[dev]->unloaded)
    return OSS_ENODEV;

  if (!mixer_devs[dev]->enabled)
    return OSS_ENXIO;

  return 0;
}

/*ARGSUSED*/
static void
oss_mixer_release (int dev, struct fileinfo *file)
{
}

/*ARGSUSED*/
int
oss_mixer_ioctl (int dev, struct fileinfo *bogus,
		 unsigned int cmd, ioctl_arg arg)
{
  int ret;

  if (cmd == OSS_GETVERSION)
    return *arg = OSS_VERSION;


Handle SNDCTL_SYSINFO/CARDINFO/etc even if there are no mixer devices in the system.

  switch (cmd)
  {
      case OSS_GETVERSION:
      case SNDCTL_SYSINFO:
      case SNDCTL_CARDINFO:
      case SNDCTL_MIXERINFO:
      case SNDCTL_MIDIINFO:
      case SNDCTL_AUDIOINFO:
      case SNDCTL_AUDIOINFO_EX:
      case SNDCTL_ENGINEINFO:
      case OSSCTL_RENUM_AUDIODEVS:
      case OSSCTL_RENUM_MIXERDEVS:
      case OSSCTL_RENUM_MIDIDEVS:
      case SNDCTL_MIX_EXTINFO:
      case SNDCTL_MIX_ENUMINFO:
      case SNDCTL_MIX_READ:
      case SNDCTL_MIX_WRITE:
      case SNDCTL_MIX_NRMIX:
      case SNDCTL_MIX_MATRIX_WRITE:
      case SNDCTL_MIX_MATRIX_READ:
      case VMIXCTL_ATTACH:
      case VMIXCTL_DETACH:
      case VMIXCTL_RATE:
         return oss_mixer_ext (dev, OSS_DEV_MIXER, cmd, arg);
         break;
  }

  if (dev < 0 || dev >= num_mixers)
    {
      return OSS_ENXIO;
    }

  if (mixer_devs == NULL)
    {
      return OSS_ENXIO;
    }

  if (!mixer_devs[dev]->enabled || mixer_devs[dev]->unloaded)
    {
      return OSS_ENODEV;
    }

  if ((ret = oss_legacy_mixer_ioctl (dev, -1, cmd, arg)) != OSS_EINVAL)
    {
      return ret;
    }

  return oss_mixer_ext (dev, OSS_DEV_MIXER, cmd, arg);
}

#ifdef DO_TIMINGS

static char timing_buf[256 * 1024] = { 0 }, *timing_ptr = timing_buf;
static int timing_prev_time = 0;
int timing_flags = 0x7fffffff;

#define TM_SCALE 256
static oss_timing_timer_func tmfunc = NULL;
static void *tmfunc_arg = NULL;

void
timing_install_timer (oss_timing_timer_func f, void *x)
{
  tmfunc = f;
  tmfunc_arg = x;
}

static void
oss_do_timing_ (char *txt)
{
  int l = strlen (txt) + 20;
  oss_native_word flags;
  oss_native_word this_time;

  if (!timing_is_active) /* Nobody is listening */
     return;

  MUTEX_ENTER_IRQDISABLE (oss_timing_mutex, flags);
  if ((long) (&timing_buf[sizeof (timing_buf)] - timing_ptr - 8) <= l)
    {
      MUTEX_EXIT_IRQRESTORE (oss_timing_mutex, flags);
      return;
    }

  if (tmfunc != NULL)
    {
      this_time = tmfunc (tmfunc_arg);
    }
  else
    {
      this_time = 0;		/* TODO: Get the actual audio pointer */

      if (this_time == 0)
	this_time = GET_JIFFIES ();
    }

  sprintf (timing_ptr, "%ld/%d: %s\n", this_time,
	   this_time - timing_prev_time, txt);
  l = strlen (timing_ptr);
  timing_ptr += l;
  timing_prev_time = this_time;
  MUTEX_EXIT_IRQRESTORE (oss_timing_mutex, flags);
}

typedef struct
{
  oss_native_word sum;
  oss_native_word open_time;
}
timing_entry;

static timing_entry timing_bins[DF_NRBINS];
static oss_native_word timing_start = 0;

static char *bin_names[DF_NRBINS] = {
  "Iddle",
  "Write",
  "Read",
  "Interrupt",
  "Write sleep",
  "Read sleep",
  "Write SRC",
  "Read SRC"
};

void
timing_open (void)
{
  int i;

  if (tmfunc == NULL)
    return;
  timing_start = tmfunc (tmfunc_arg) / TM_SCALE;

  for (i = 0; i < DF_NRBINS; i++)
    {
      timing_bins[i].sum = 0;
      timing_bins[i].open_time = 0xffffffff;
    }

}

void
timing_close (void)
{
  char tmp[64];
  int i;
  oss_native_word t, sum, pc;
  if (tmfunc == NULL)
    return;

  t = tmfunc (tmfunc_arg) / TM_SCALE - timing_start;

  sprintf (tmp, "Timing close, elapsed=%d", t);
  oss_do_timing2 (DFLAG_PROFILE, tmp);

  sum = 0;

  for (i = 0; i < DF_NRBINS; i++)
    {
      sum += timing_bins[i].sum;
      pc = (timing_bins[i].sum * 1000) / t;
      sprintf (tmp, "Bin %s: %d/%d (%d.%d%%)", bin_names[i],
	       timing_bins[i].sum, pc, pc / 10, pc % 10);
      oss_do_timing2 (DFLAG_PROFILE, tmp);
    }

  /* sum = sum-timing_bins[DF_SLEEPWRITE].sum-timing_bins[DF_SLEEPREAD].sum; */
  pc = (sum * 10000) / t;

  sprintf (tmp, "OSS Total: %d (%d.%d%%)", sum, pc / 100, pc % 100);
  oss_do_timing2 (DFLAG_PROFILE, tmp);
}

void
oss_timing_enter (int bin)
{
  if (tmfunc == NULL)
    return;

  timing_bins[bin].open_time = tmfunc (tmfunc_arg) / TM_SCALE;
}

void
oss_timing_leave (int bin)
{
  oss_native_word t;

  if (tmfunc == NULL)
    return;

  if (timing_bins[bin].open_time >= 0xfffffffe)
    return;

  t = tmfunc (tmfunc_arg) / TM_SCALE - timing_bins[bin].open_time;
  timing_bins[bin].sum += t;
  timing_bins[bin].open_time = 0xfffffffe;
}

void
oss_do_timing (char *txt)
{
  if (!timing_is_active) /* Nobody is listening */
     return;

  if (timing_flags & DFLAG_ALL)
    oss_do_timing_ (txt);
}

void
oss_do_timing2 (int mask, char *txt)
{
  if (!timing_is_active) /* Nobody is listening */
     return;

  if ((timing_flags & DFLAG_ALL) || (timing_flags & mask))
    oss_do_timing_ (txt);
}

void
oss_timing_printf (char *s, ...)
{
  char tmp[1024], *a[6];
  va_list ap;
  int i, n = 0;

  if (!timing_is_active) /* Nobody is listening */
     return;

  va_start (ap, s);

  for (i = 0; i < strlen (s); i++)
    if (s[i] == '%')
      n++;

  for (i = 0; i < n && i < 6; i++)
    a[i] = va_arg (ap, char *);

  for (i = n; i < 6; i++)
    a[i] = NULL;

      sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL,
	       NULL, NULL, NULL);
      oss_do_timing(tmp);

  va_end (ap);
}

static int
timing_read (int dev, struct fileinfo *file, uio_t * buf, int count)
{

Return at most 'count' bytes from the status_buf.

  int l;
  oss_native_word flags;

  timing_is_active = 1;

  MUTEX_ENTER_IRQDISABLE (oss_timing_mutex, flags);

  l = timing_ptr - timing_buf;
  if (l <= 0)
    {
      MUTEX_EXIT_IRQRESTORE (oss_timing_mutex, flags);
      return 0;
    }

  if (l > count)
    l = count;

  timing_ptr = timing_buf;

  MUTEX_EXIT_IRQRESTORE (oss_timing_mutex, flags);

  if (uiomove (timing_buf, l, UIO_READ, buf) != 0)
    cmn_err (CE_WARN, "audio: uiomove(UIO_READ) failed\n");

  return l;
}
#else

Dummy wrappers


/*ARGSUSED*/
void
oss_timing_enter (int bin)
{
}

/*ARGSUSED*/
void
oss_timing_leave (int bin)
{
}

/*ARGSUSED*/
void
oss_do_timing (char *txt)
{
}

/*ARGSUSED*/
void
oss_do_timing2 (int mask, char *txt)
{
}

/*ARGSUSED*/
void
oss_timing_printf (char *s, ...)
{
}
#endif

static oss_cdev_drv_t mixer_cdev_drv = {
  oss_mixer_open,
  oss_mixer_release,
#ifdef DO_TIMINGS
  timing_read,
#else
  NULL,				/* read */
#endif
  NULL,				/* write */
  oss_mixer_ioctl
};

int
oss_install_mixer (int vers,
		   oss_device_t * osdev,
		   oss_device_t * master_osdev,
		   const char *name,
		   mixer_driver_t * driver, int driver_size, void *devc)
{
  mixer_operations_t *op = NULL;
  mixer_driver_t *d;

  int i, num;
  char handle[32];

  if (master_osdev == NULL)
    master_osdev = osdev;

  if (mixer_devs == NULL)
    {
      mixer_devs = PMALLOC (osdev, sizeof (mixdev_p) * MAX_MIXER_DEV);
      memset (mixer_devs, 0, sizeof (mixdev_p) * MAX_MIXER_DEV);
      mixer_devs_p = mixer_devs;
    }

  if (num_mixers >= MAX_MIXER_DEV - 1)
    {
      static int nnn = 0;
      cmn_err (CE_WARN, "Too many mixer devices %d/%d (%s)\n",
	       num_mixers, MAX_MIXER_DEV, name);

In some special situations a driver may keep trying to install a mixer in infinite loop if the request fails. Stop this by panicking after this has continued for more than 50 times. In this case we can get an error message instead of having the system to lock up foreever.

      if (nnn++ > 50)
	cmn_err (CE_PANIC, "Killing runaway system.\n");
      return OSS_EIO;
    }

  if (vers != OSS_MIXER_DRIVER_VERSION)
    {
      cmn_err (CE_WARN, "Incompatible mixer driver for %s\n", name);
      return OSS_EIO;
    }

  if (driver_size > sizeof (mixer_driver_t))
    driver_size = sizeof (mixer_driver_t);


Check if this device was earlier unloaded and now returning back.

  num = -1;
  for (i = 0; i < num_mixers; i++)
    {
      if (mixer_devs[i]->unloaded
	  && mixer_devs[i]->os_id == oss_get_osid (osdev))
	{
	  op = mixer_devs[i];
	  num = i;
	  break;
	}
    }

  if ((d = PMALLOC (osdev, sizeof (*d))) == NULL)
    {
      cmn_err (CE_WARN, "Can't allocate mixer driver for (%s)\n", name);
      return OSS_ENOSPC;
    }

  if (num == -1)
    {
      op = PMALLOC (osdev, sizeof (mixer_operations_t));
      if (op == NULL)
	{
	  cmn_err (CE_WARN, "Can't allocate mixer driver for (%s)\n", name);
	  return OSS_ENOSPC;
	}

      memset ((char *) op, 0, sizeof (mixer_operations_t));
      num = num_mixers++;
      sprintf (handle, "%s-mx%02d", osdev->handle, osdev->num_mixerdevs+1);
      op->port_number = osdev->num_mixerdevs++;
    }
  else
    {
      strcpy (handle, op->handle);	/* Preserve the previous handle */
    }

  memset ((char *) d, 0, sizeof (mixer_driver_t));
  memcpy ((char *) d, (char *) driver, driver_size);
  strcpy (op->handle, handle);
  strcpy (op->id, osdev->nick);
  op->d = d;

  strncpy (op->name, name, sizeof (op->name));
  op->name[sizeof (op->name) - 1] = 0;
  op->devc = devc;
  op->osdev = osdev;
  op->os_id = oss_get_osid (osdev);
  op->master_osdev = master_osdev;
  op->hw_devc = NULL;
  op->max_ext = op->nr_ext = 0;
  op->names_checked = 0;
  op->extensions = NULL;
  op->timestamp = GET_JIFFIES ();
  op->ignore_mask = 0;
  op->card_number = osdev->cardnum;
  op->enabled = 1;
  op->unloaded = 0;
  op->flags = 0;
  op->caps = 0;
  op->priority = 0;		/* Normal (low) priority */
  op->real_dev = num;

  if (osdev->first_mixer == -1) /* Not defined yet */
     osdev->first_mixer = num;

  mixer_devs[num] = op;

Create the device node


  {
    oss_devnode_t name;

#ifdef NEW_DEVICE_NAMING
# ifdef USE_DEVICE_SUBDIRS
    sprintf (name, "oss/%s/mix%d", osdev->nick, osdev->num_mixerdevs - 1);
# else
    sprintf (name, "%s_mix%d", osdev->nick, osdev->num_mixerdevs - 1);
# endif
#else
    sprintf (name, "mixer%d", num);
#endif
    oss_install_chrdev (osdev, name, OSS_DEV_MIXER, num, &mixer_cdev_drv, 0);
    sprintf (op->devnode, "/dev/%s", name);

#if 0

Moved to install_dev_mixer()

    if (num == 0)
      {
	oss_install_chrdev (osdev, "mixer", OSS_DEV_MIXER, num,
			    &mixer_cdev_drv, 0);
      }
#endif
  }

  return num;
}

void
install_dev_mixer (oss_device_t * osdev)
{

Install the default mixer node if necessary

  oss_install_chrdev (osdev, "mixer", OSS_DEV_MIXER, 0, &mixer_cdev_drv, 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