/*
 * Purpose: OSS DDK routines
 *
 * OSSDDK is a driver development interface for 3rd party drivers. It's
 * designed to be OSS version independent so that drivers using them can
 * be compiled and distributed outside the actual OSS package. See the
 * OSSDDK manual pages and documentation for further info.
 *
 * For the time being the OSSDDK library is only available for Solaris.
 */

/*
 * This file is part of Open Sound System
 *
 * Copyright (C) 4Front Technologies 1996-2007.
 *
 * This software is released under CDDL 1.0 source license for operating systems
 * that have their full source code available under the BSD or CDDL licenses.
 *
 * See the COPYING file included in the main directory of this source
 * distribution. Please contact sales@opensound.com for further info.
 */

/*
 * NOTE! This is not the development version of this file. Please download the
 * sources of the latest Open Sound System version from 
 * http://developer.opensound.com/sources if you plan to make any changes
 * to the software.
 */

#include "oss_config.h"

oss_device_t *
ossddk_register_device (int ddkvers,
			dev_info_t * dip,
			int drvtype, int instance, const char *nick,
#ifdef sun
			ddi_iblock_cookie_t iblock_cookie,
#endif
			void *devc, const char *longname)
{
#ifdef sun
  oss_device_t *osdev;

  if (ddkvers != OSSDDK_VERSION)
    {
      cmn_err (CE_CONT, "Incompatible OSS DDK version for this driver\n");
      return NULL;
    }

  if ((osdev = osdev_create (dip, drvtype, instance, nick, NULL)) == NULL)
    {
      cmn_err (CE_WARN, "Creating osdev failed\n");
      return NULL;
    }

  osdev->iblock_cookie = iblock_cookie;
  osdev->devc = devc;

  oss_register_device (osdev, longname);

  return osdev;
#else
  return NULL;
#endif
}

int
ossddk_disable_device (oss_device_t * osdev)
{
  return oss_disable_device (osdev);
}

void *
ossddk_osdev_get_devc (oss_device_t * osdev)
{
  return osdev->devc;
}

void
ossddk_unregister_device (oss_device_t * osdev)
{
  oss_unregister_device (osdev);
}

/*
 * ossddk_install_audiodev is just a wrapper for oss_install_audiodev
 */
int
ossddk_install_audiodev (int vers,
			 oss_device_t * osdev,
			 oss_device_t * master_osdev,
			 char *name,
			 const audiodrv_t * driver,
			 int driver_size,
			 int flags,
			 unsigned int format_mask, void *devc, int parent)
{
  flags |= ADEV_AUTOMODE;

  return oss_install_audiodev (vers, osdev, master_osdev, name, driver,
			       driver_size, flags, format_mask, devc, parent);
}

/*
 * Audio device access routines
 */
void
ossddk_adev_set_devc (int dev, void *devc)
{
  audio_engines[dev]->devc = devc;
}

void
ossddk_adev_set_portc (int dev, void *portc)
{
  audio_engines[dev]->portc = portc;
}

void
ossddk_adev_set_portc_play (int dev, void *portc)
{
  audio_engines[dev]->portc_play = portc;
}

void
ossddk_adev_set_portc_record (int dev, void *portc)
{
  audio_engines[dev]->portc_record = portc;
}

void
ossddk_adev_set_portnum (int dev, int portnum)
{
  audio_engines[dev]->port_number = portnum;
}

void
ossddk_adev_set_mixer (int dev, int mixer_dev)
{
  audio_engines[dev]->mixer_dev = mixer_dev;
}

void
ossddk_adev_set_rates (int dev, int min_rate, int max_rate, int nrates,
		       int rates[20])
{
  adev_p adev = audio_engines[dev];

  adev->min_rate = min_rate;
  adev->max_rate = max_rate;

  if (nrates <= 0)
    {
      adev->caps |= PCM_CAP_FREERATE;
      adev->nrates = 0;

      return;
    }

  adev->caps &= ~PCM_CAP_FREERATE;
  if (nrates > OSS_MAX_SAMPLE_RATES)
    nrates = OSS_MAX_SAMPLE_RATES;

  memcpy (adev->rates, rates, sizeof (int) * nrates);
  adev->nrates = nrates;
}

void
ossddk_adev_set_formats (int dev, unsigned int oformats,
			 unsigned int iformats)
{
  adev_p adev = audio_engines[dev];

  adev->iformat_mask = iformats;
  adev->oformat_mask = oformats;
}

void
ossddk_adev_set_caps (int dev, unsigned int caps)
{
  adev_p adev = audio_engines[dev];

  adev->caps &= ~PCM_CAP_FREERATE;
  adev->caps |= caps & ~PCM_CAP_FREERATE;
}

void
ossddk_adev_set_flags (int dev, unsigned int flags)
{
  adev_p adev = audio_engines[dev];

  adev->flags = flags;
}

int
ossddk_adev_get_flags (int dev)
{
  adev_p adev = audio_engines[dev];

  return adev->flags;
}

void
ossddk_adev_set_channels (int dev, int min_channels, int max_channels)
{
  adev_p adev = audio_engines[dev];

  adev->min_channels = min_channels;
  adev->max_channels = max_channels;
}

void
ossddk_adev_set_buflimits (int dev, int min_fragment, int max_fragment)
{
}

void
ossddk_adev_set_enable_flag (int dev, int state)
{
  audio_engines[dev]->enabled = state;
}

void
ossddk_adev_set_unloaded_flag (int dev, int state)
{
  audio_engines[dev]->unloaded = state;
}

void
ossddk_adev_set_magic (int dev, int magic)
{
  audio_engines[dev]->magic = magic;
}

void
ossddk_adev_set_ratesource (int dev, int rate_source)
{
  audio_engines[dev]->rate_source = rate_source;
}

void
ossddk_adev_set_songname (int dev, char *song)
{
  adev_p adev = audio_engines[dev];

  strncpy (adev->song_name, song, sizeof (adev->song_name) - 1);
  adev->song_name[sizeof (adev->song_name) - 1] = 0;
}

void
ossddk_adev_set_label (int dev, char *label)
{
  adev_p adev = audio_engines[dev];

  strncpy (adev->label, label, sizeof (adev->label) - 1);
  adev->label[sizeof (adev->label) - 1] = 0;
}

void *
ossddk_adev_get_devc (int dev)
{
  return audio_engines[dev]->devc;
}

void *
ossddk_adev_get_portc (int dev)
{
  return audio_engines[dev]->portc;
}

void *
ossddk_adev_get_portc_play (int dev)
{
  return audio_engines[dev]->portc_play;
}

void *
ossddk_adev_get_portc_record (int dev)
{
  return audio_engines[dev]->portc_record;
}

dmap_t *
ossddk_adev_get_dmapout (int dev)
{
  return audio_engines[dev]->dmap_out;
}

dmap_t *
ossddk_adev_get_dmapin (int dev)
{
  return audio_engines[dev]->dmap_in;
}

char *
ossddk_adev_get_songname (int dev)
{
  return audio_engines[dev]->song_name;
}

char *
ossddk_adev_get_label (int dev)
{
  return audio_engines[dev]->label;
}

void
ossddk_audio_outputintr (int dev, int intr_flags)
{
  oss_audio_outputintr (dev, intr_flags);
}

void
ossddk_audio_inputintr (int dev, int intr_flags)
{
  oss_audio_inputintr (dev, intr_flags);
}

/*
 * dmap access routines
 */
unsigned char *
ossddk_dmap_get_dmabuf (dmap_t * dmap)
{
  return dmap->dmabuf;
}

unsigned long
ossddk_dmap_get_phys (dmap_t * dmap)
{
  return dmap->dmabuf_phys;
}

int
ossddk_dmap_get_buffsize (dmap_t * dmap)
{
  return dmap->buffsize;
}

void
ossddk_dmap_set_dmabuf (dmap_t * dmap, unsigned char *buf)
{
  dmap->dmabuf = buf;
}

void
ossddk_dmap_set_phys (dmap_t * dmap, unsigned long addr)
{
  dmap->dmabuf_phys = addr;
}

void
ossddk_dmap_set_buffsize (dmap_t * dmap, int size)
{
  dmap->buffsize = size;
}

void
ossddk_dmap_set_private (dmap_t * dmap, void *ptr)
{
  dmap->driver_use_ptr = ptr;
}

void
ossddk_dmap_set_callback (dmap_t * dmap, oss_audio_callback_t cb, int arg)
{
  dmap->audio_callback = cb;
  dmap->callback_parm = arg;
}

void
ossddk_dmap_set_fragsize (dmap_t * dmap, int size)
{
  dmap->fragment_size = size;
  dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;

  if (dmap->bytes_in_use > dmap->fragment_size)
    cmn_err (CE_PANIC,
	     "ossddk_dmap_set_fragsize: Too large fragment allocation\n");
}

void
ossddk_dmap_set_numfrags (dmap_t * dmap, int nfrags)
{
  dmap->nfrags = nfrags;
  dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;

  if (dmap->bytes_in_use > dmap->fragment_size)
    cmn_err (CE_PANIC,
	     "ossddk_dmap_set_numfrags: Too large fragment allocation\n");
}

void
ossddk_dmap_set_playerror (dmap_t * dmap, int err, int parm)
{
  /* TODO: For the time being this is unimplemented. */
}

void
ossddk_dmap_set_recerror (dmap_t * dmap, int err, int parm)
{
  /* TODO: For the time being this is unimplemented. */
}

void *
ossddk_dmap_get_private (dmap_t * dmap)
{
  return dmap->driver_use_ptr;
}

int
ossddk_dmap_get_fragsize (dmap_t * dmap)
{
  return dmap->fragment_size;
}

int
ossddk_dmap_get_numfrags (dmap_t * dmap)
{
  return dmap->fragment_size;
}

int
ossddk_dmap_get_buffused (dmap_t * dmap)
{
  return dmap->bytes_in_use;
}

int
ossddk_dmap_get_qhead (dmap_t * dmap)
{
  return dmap_get_qhead (dmap);
}

int
ossddk_dmap_get_qtail (dmap_t * dmap)
{
  return dmap_get_qtail (dmap);
}

/*
 * AC97 codec/mixer interface
 */
#include "ac97.h"

struct _ac97_handle_t
{
  ac97_devc devc;
};

int
ossddk_ac97_install (ac97_handle_t ** handle, char *name,
		     ac97_readfunc_t readfn, ac97_writefunc_t writefn,
		     void *hostparms, oss_device_t * osdev)
{
  ac97_handle_t *h;
  int err;

  *handle = NULL;

  if ((h = KERNEL_MALLOC (sizeof (*h))) == NULL)
    return -ENOMEM;

  if ((err =
       ac97_install (&h->devc, name, readfn, writefn, hostparms, osdev)) < 0)
    {
      KERNEL_FREE (h);
      return err;
    }

  *handle = h;

  return h->devc.mixer_dev;
}

void
ossddk_ac97_remove (ac97_handle_t * handle)
{
  KERNEL_FREE (handle);
}

int
ossddk_ac97_set_ext_init (ac97_handle_t * handle,
			  mixer_create_controls_t func, int nextra)
{
  return ac97_init_ext (handle->devc.mixer_dev, &handle->devc, func, nextra);
}

int
ossddk_ac97_is_varrate (ac97_handle_t * handle)
{
  return ac97_varrate (&handle->devc);
}

int
ossddk_ac97_set_recrate (ac97_handle_t * handle, int srate)
{
  return ac97_recrate (&handle->devc, srate);
}

int
ossddk_ac97_set_playrate (ac97_handle_t * handle, int srate)
{
  return ac97_playrate (&handle->devc, srate);
}

/*
 * Mixer interface
 */
int
ossddk_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)
{
  return oss_install_mixer (vers, osdev, master_osdev, name,
			    driver, driver_size, devc);
}

void *
ossddk_mixer_get_devc (int dev)
{
  return mixer_devs[dev]->devc;
}

void
ossddk_mixer_touch (int dev)
{
  touch_mixer (dev);
}

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

int
ossddk_mixer_create_control (int dev, int parent, int ctrl,
			     mixer_ext_fn func, int type, const char *id,
			     int maxvalue, int flags)
{
  return mixer_ext_create_control (dev, parent, ctrl,
				   func, type, id, maxvalue, flags);
}

int
ossddk_mixer_truncate (int dev, int index)
{
  return mixer_ext_truncate (dev, index);
}

int
ossddk_mixer_set_strings (int dev, int ctl, const char *s, int version)
{
  return mixer_ext_set_strings (dev, ctl, s, version);
}
