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!

audio/oss_spdif.c

S/PDIF (IEC958) control bit and mixer extension management

Description



This code is used by some drivers to handle S/PDIF specific control functions (see spdif_control for more info.


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 "spdif.h"

#define MAX_DEV 10
static int ndevs = 0;
static spdif_devc *devc_list[MAX_DEV] = { 0 };

static void
init_ctl (spdif_devc * devc, oss_digital_control * ctl)
{
  memset (ctl, 0, sizeof (*ctl));

  ctl->caps = devc->caps;
}

static void
block_mixer (spdif_devc * devc)
{
  devc->mixer_blocked = 1;
  memcpy (&devc->cold_ctl, &devc->hot_ctl, sizeof (devc->hot_ctl));
  devc->ctl = &devc->cold_ctl;

}

static void
unblock_mixer (spdif_devc * devc)
{
  memcpy (&devc->hot_ctl, &devc->cold_ctl, sizeof (devc->hot_ctl));
  devc->ctl = &devc->hot_ctl;
  devc->mixer_blocked = 0;
}

int
oss_spdif_install (spdif_devc * devc, oss_device_t * osdev,
		   spdif_driver_t * d, int driver_size, void *host_devc,
		   void *host_portc, int mixer_dev, int flags,
		   unsigned int caps)
{
  int i, pos;

  if (devc->is_ok)
    {
      /* cmn_err (CE_WARN, "spdif: Bad usage - double init\n"); */
      return 0;
    }

  if (d == NULL || driver_size != sizeof (spdif_driver_t))
    {
      cmn_err (CE_WARN, "spdif: Bad driver\n");
      return OSS_EIO;
    }

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

  devc->open_mode = 0;
  devc->osdev = osdev;
  MUTEX_INIT (devc->osdev, devc->mutex, MH_FRAMEW + 2);
  devc->d = d;
  devc->flags = flags;
  devc->host_devc = host_devc;
  devc->host_portc = host_portc;
  devc->mixer_dev = mixer_dev;
  devc->ctl = &devc->hot_ctl;
  devc->caps = caps;
  devc->is_ok = 1;

  pos=-1;

  for (i=0;pos==-1 && i<ndevs;i++)
  if (devc_list[i]==NULL)
  {
	  pos = i;
  }

  if (pos==-1)
  {
     if (ndevs >= MAX_DEV)
     {
	     cmn_err(CE_WARN, "Too many S/PDIF devices\n");
	     return OSS_EBUSY;
     }

     pos=ndevs++;
  }

  devc_list[pos] = devc;

  init_ctl (devc, &devc->hot_ctl);
  init_ctl (devc, &devc->cold_ctl);

  return 0;
}

void
oss_spdif_uninstall (spdif_devc * devc)
{
	int i;

	for (i=0;i<ndevs;i++)
	    if (devc_list[i]==devc)
	       devc_list[i]=NULL;
}

int
oss_spdif_open (spdif_devc * devc, int open_mode)
{
  oss_native_word flags;
  flags = 0;
  if (!devc->is_ok)
    {
      cmn_err (CE_WARN, "spdif: Bad usage - open\n");
      return OSS_EIO;
    }
  MUTEX_ENTER (devc->mutex, flags);
  devc->open_mode |= open_mode;
  MUTEX_EXIT (devc->mutex, flags);
  return 0;
}

void
oss_spdif_close (spdif_devc * devc, int open_mode)
{
  oss_native_word flags;
  flags = 0;
  if (!devc->is_ok)
    {
      cmn_err (CE_WARN, "spdif: Bad usage - close\n");
      return;
    }
  MUTEX_ENTER (devc->mutex, flags);
  devc->open_mode &= ~open_mode;

  if (devc->mixer_blocked && (open_mode & OPEN_WRITE))
    {
      unblock_mixer (devc);
    }
  MUTEX_EXIT (devc->mutex, flags);
}

static void
update_rate_bits (spdif_devc * devc, int rate)
{
  unsigned char spd;

Set S/PDIF rate control (TODO: Pro mode)


  devc->hot_ctl.srate_out = rate;

  switch (rate)
    {
    case 48000:
      spd = 2;
      break;
    case 32000:
      spd = 3;
      break;
    case 88200:
      spd = 4;
      break;
    case 96000:
      spd = 5;
      break;
    case 176400:
      spd = 7;
      break;
    case 192000:
      spd = 6;
      break;
    default:
      spd = 0;
      rate = 44100;
    }

  devc->hot_ctl.rate_bits = spd;

  devc->hot_ctl.cbitout[3] &= ~0x0f;
  devc->hot_ctl.cbitout[3] |= spd & 0x0f;
  devc->hot_ctl.srate_out = rate;
}

void
oss_spdif_setrate (spdif_devc * devc, int rate)
{
  oss_native_word flags;
  flags = 0;
  if (!devc->is_ok)
    {
      cmn_err (CE_WARN, "spdif: Bad usage - setrate\n");
      return;
    }
  MUTEX_ENTER (devc->mutex, flags);

  update_rate_bits (devc, rate);

  if (devc->d->reprogram_device)
    devc->d->reprogram_device (devc->host_devc, devc->host_portc,
			       &devc->hot_ctl, REPGM_RATE | REPGM_CBIT3);
  MUTEX_EXIT (devc->mutex, flags);
}

static int spdif_mix_init (spdif_devc * devc);

/*ARGSUSED*/
static void
switch_mode (spdif_devc * devc, oss_digital_control * ctl, int smurf)
{
  ctl->pro_mode = ctl->cbitout[0] & 0x01;

  if (ctl == devc->ctl)		/* Visible to mixer */
    spdif_mix_init (devc);	/* Re-create mixer extensions */
}

static void
update_hardware (spdif_devc * devc, int repgm)
{
  if (repgm & REPGM_RATE)
    update_rate_bits (devc, devc->hot_ctl.srate_out);
  if (devc->d->reprogram_device)
    devc->d->reprogram_device (devc->host_devc, devc->host_portc,
			       &devc->hot_ctl, repgm);
}

static int
readctl (spdif_devc * devc, oss_digital_control * c, int open_mode)
{
  int valid = c->valid & ~(VAL_OUTMASK);
  int ret = 0;
  oss_native_word flags;
  flags = 0;

  MUTEX_ENTER (devc->mutex, flags);

  memcpy (c, &devc->hot_ctl, sizeof (devc->hot_ctl));
  memset (c->filler, 0, sizeof (c->filler));	/* Hide internal variables */
  c->valid &= (VAL_OUTMASK);

  if (open_mode & OPEN_READ)
    {
      if (devc->d->get_status)
	ret = devc->d->get_status (devc->host_devc, devc->host_portc,
				   c, valid);
      else
	{
	  if (valid != 0)	/* Can't get input status */
	    ret = OSS_EIO;
	}
    }

  MUTEX_EXIT (devc->mutex, flags);
  return ret;
}

/*ARGSUSED*/
static int
handle_request (spdif_devc * devc, oss_digital_control * c)
{
  return OSS_EIO;
}

static int
writectl (spdif_devc * devc, oss_digital_control * c)
{
  int valid = c->valid;
  int repgm = 0;
  int err = 0;
  oss_native_word flags;
  flags = 0;

  MUTEX_ENTER (devc->mutex, flags);
  if (!devc->mixer_blocked)
    block_mixer (devc);
  c->valid = 0;
  c->caps = devc->caps;

  if ((valid & VAL_CBITOUT) &&
      ((devc->caps & DIG_CBITOUT_MASK) != DIG_CBITOUT_NONE))
    {
      int smurf;

      smurf = (devc->hot_ctl.cbitout[0] ^ c->cbitout[0]) & 1;
      c->valid |= VAL_CBITOUT;
      repgm |= REPGM_CBITALL;

      if (smurf)		/* Mode changet CONSUMER<->PRO */
	{
	  repgm |= REPGM_ALL;
	  switch_mode (devc, &devc->hot_ctl, 0);
	}

      memcpy (&devc->hot_ctl.cbitout, &c->cbitout, sizeof (c->cbitout));
    }

  if ((valid & VAL_UBITOUT) && (devc->caps & DIG_UBITOUT))
    {
      c->valid |= VAL_UBITOUT;
      repgm |= REPGM_UBITALL;
      memcpy (&devc->hot_ctl.ubitout, &c->ubitout, sizeof (c->ubitout));
    }

  if ((c->out_vbit != VBIT_NOT_INDICATED) && (devc->caps & DIG_VBITOUT))
    {
      devc->hot_ctl.out_vbit = c->out_vbit;
      repgm |= REPGM_VBIT;
    }
  else
    c->out_vbit = VBIT_NOT_INDICATED;
  c->in_vbit = VBIT_NOT_INDICATED;

  devc->hot_ctl.valid = valid;

  if ((valid & VAL_ORATE))
    {
      /* TODO: Check the rate */
      update_rate_bits (devc, c->srate_out);
      c->valid |= VAL_ORATE;
      c->srate_out = devc->hot_ctl.srate_out;
    }

  if (valid & VAL_REQUEST)
    {
      err = handle_request (devc, c);
      if (err >= 0)
	c->valid |= VAL_REQUEST;
    }

  MUTEX_EXIT (devc->mutex, flags);
  return err;
}

int
oss_spdif_ioctl (spdif_devc * devc, int open_mode, signed int cmd,
		 ioctl_arg arg)
{
  if (!devc->is_ok)
    {
      cmn_err (CE_WARN, "spdif: Bad usage - ioctl\n");
      return SPDIF_NOIOCTL;	/* Not for me */
    }

  switch (cmd)
    {
    case SNDCTL_DSP_WRITECTL:
      if (!(open_mode & OPEN_WRITE))	/* Not opened for output */
	return OSS_ENOTSUP;
      if (!(devc->flags & SPDF_OUT))
	return OSS_ENOTSUP;
      return writectl (devc, (oss_digital_control *) arg);
      break;

    case SNDCTL_DSP_READCTL:
      if (!(devc->flags & (SPDF_OUT | SPDF_IN)))
	return OSS_ENOTSUP;
      return readctl (devc, (oss_digital_control *) arg, open_mode);
      break;
    }

  return SPDIF_NOIOCTL;		/* Not for me too */
}

static int
spdif_set_control (spdif_devc * devc, int ctrl, unsigned int cmd, int value)
{
  int val;

  if (cmd == SNDCTL_MIX_READ)
    {
      switch (ctrl)
	{
	case 1:		/* spdif.enable (OFF/ON) */
	  return !!(devc->ctl->outsel & OUTSEL_DIGITAL);
	  break;

	case 2:		/* spdif.mode (CONSUMER|PRO) */
	  return !!(devc->ctl->cbitout[0] & 1);
	  break;

	case 3:		/* spdif.audio (AUDIO/DATA) */
	  return !!(devc->ctl->cbitout[0] & 2);
	  break;

	case 4:		/* spdif.vbit (OFF/ON) */
	  return (devc->ctl->out_vbit == VBIT_ON);
	  break;

	case 5:		/* spdif.copyright (YES/NO) */
	  return get_cbit (devc->ctl->cbitout, 0, 2);
	  break;

	case 6:		/* spdif.generation (COPY/ORIGINAL) */
	  return get_cbit (devc->ctl->cbitout, 1, 0);
	  break;

	case 7:		/* spdif.preemph (OFF 50/16usec) */
	  return devc->ctl->emphasis_type;
	}

      return OSS_EINVAL;
    }

  if (cmd == SNDCTL_MIX_WRITE)
    {
      switch (ctrl)
	{
	case 1:		/* spdif.enable */
	  if (value)
	    devc->ctl->outsel |= OUTSEL_DIGITAL;
	  else
	    devc->ctl->outsel &= ~OUTSEL_DIGITAL;
	  update_hardware (devc, REPGM_OUTSEL);
	  return !!(devc->ctl->outsel & OUTSEL_DIGITAL);
	  break;

	case 2:		/* spdif.mode (CONSUMER|PRO) */
	  val = devc->ctl->cbitout[0] & 1;
	  value = !!value;

	  set_cbit (devc->ctl->cbitout, 0, 0, value);
	  if (val != value)	/* Mode change */
	    switch_mode (devc, devc->ctl, 1);
	  update_hardware (devc, REPGM_ALL);
	  return get_cbit (devc->ctl->cbitout, 0, 0);
	  break;

	case 3:		/* spdif.audio (AUDIO|DATA) */
	  value = !!value;

	  set_cbit (devc->ctl->cbitout, 0, 1, value);
	  update_hardware (devc, REPGM_CBIT0);
	  return get_cbit (devc->ctl->cbitout, 0, 1);
	  break;

	case 4:		/* spdif.vbit (OFF/ON) */
	  devc->ctl->out_vbit = (value) ? VBIT_ON : VBIT_OFF;
	  update_hardware (devc, REPGM_VBIT);
	  return (devc->ctl->out_vbit == VBIT_ON);
	  break;

	case 5:		/* spdif.copyright (YES|NO) */
	  value = !!value;

	  set_cbit (devc->ctl->cbitout, 0, 2, value);
	  update_hardware (devc, REPGM_CBIT0);
	  return get_cbit (devc->ctl->cbitout, 0, 2);
	  break;

	case 6:		/* spdif.generation (COPY|ORIGINAL) */
	  value = !!value;

	  set_cbit (devc->ctl->cbitout, 1, 0, value);
	  update_hardware (devc, REPGM_CBIT1);
	  return get_cbit (devc->ctl->cbitout, 1, 0);
	  break;

	case 7:		/* spdif.preemph (OFF 50/15usec) */
	  value = !!value;
	  devc->ctl->emphasis_type = value;
	  if (devc->ctl->pro_mode)
	    {
	      devc->ctl->cbitout[0] &= ~0x1c;
	      if (value)
		devc->ctl->cbitout[0] |= 0x0c;
	    }
	  else
	    {
	      devc->ctl->cbitout[0] &= ~0x28;
	      if (value)
		devc->ctl->cbitout[0] |= 0x08;
	    }
	  update_hardware (devc, REPGM_CBIT0);
	  return value;

	  break;
	}

      return OSS_EINVAL;
    }
  return OSS_EINVAL;
}

static int
spdif_set_mixer_control (int dev, int ctrl, unsigned int cmd, int value)
{
  int i, ret;
  spdif_devc *devc = NULL;
  oss_native_word flags;
  flags = 0;

  for (i = 0; i < ndevs && devc == NULL; i++)
    if (devc_list[i]->mixer_dev == dev)
      devc = devc_list[i];

  if (devc == NULL)
    return OSS_EINVAL;
  MUTEX_ENTER (devc->mutex, flags);
  ret = spdif_set_control (devc, ctrl, cmd, value);
  MUTEX_EXIT (devc->mutex, flags);
  return ret;
}

static int
spdif_mix_init (spdif_devc * devc)
{
  int group, err, dev;

  dev = devc->mixer_dev;

  if (!devc->is_ok)
    {
      cmn_err (CE_WARN, "spdif: Bad usage - mix_init\n");
      return OSS_EIO;
    }

  if (devc->group > 0)
    mixer_ext_truncate (devc->mixer_dev, devc->group);
  devc->group = 0;

  if ((group = mixer_ext_create_group_flags (dev, 0, "SPDIF", MIXF_FLAT)) < 0)
    return group;

  devc->group = group;

  if (devc->flags & SPDF_ENABLE)
    if ((err = mixer_ext_create_control (dev, group,
					 1, spdif_set_mixer_control,
					 MIXT_ONOFF,
					 "SPDIF_ENABLE", 2,
					 MIXF_READABLE | MIXF_WRITEABLE)) < 0)
      return err;

  if ((devc->caps & DIG_CBITOUT_MASK))
    {
      if ((err = mixer_ext_create_control (dev, group,
					   3, spdif_set_mixer_control,
					   MIXT_ENUM,
					   "SPDIF_AUDIO", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;
    }

  if (devc->caps & DIG_VBITOUT)
    {
      if ((err = mixer_ext_create_control (dev, group,
					   4, spdif_set_mixer_control,
					   MIXT_ONOFF,
					   "SPDIF_VBIT", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;
    }

  if ((devc->caps & DIG_CBITOUT_MASK))
    {
      if ((err = mixer_ext_create_control (dev, group,
					   7, spdif_set_mixer_control,
					   MIXT_ENUM,
					   "SPDIF_PREEMPH", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;
    }

  if ((devc->caps & DIG_PRO) && (devc->caps & DIG_CONSUMER))
    {
      /* Device supports both consumer and pro */
      if ((err = mixer_ext_create_control (dev, group,
					   2, spdif_set_mixer_control,
					   MIXT_ENUM,
					   "SPDIF_MODE", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;
    }

  /* Copyright management */
  if ((devc->caps & DIG_CBITOUT_MASK) && !devc->ctl->pro_mode)
    {
      if ((err = mixer_ext_create_control (dev, group,
					   5, spdif_set_mixer_control,
					   MIXT_ENUM,
					   "SPDIF_COPYRIGHT", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;

      if ((err = mixer_ext_create_control (dev, group,
					   6, spdif_set_mixer_control,
					   MIXT_ENUM,
					   "SPDIF_GENERAT", 2,
					   MIXF_READABLE | MIXF_WRITEABLE)) <
	  0)
	return err;
    }

  return 0;
}

int
oss_spdif_mix_init (spdif_devc * devc)
{
  int ret;
  oss_native_word flags;
  flags = 0;
  MUTEX_ENTER (devc->mutex, flags);
  ret = spdif_mix_init (devc);
  MUTEX_EXIT (devc->mutex, flags);
  return ret;
}

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