/*
 * sound/dmabuf.c
 *
 * The DMA buffer manager for digitized voice applications
 *
 * Copyright by Hannu Savolainen 1993
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. 2.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "sound_config.h"

#ifdef CONFIGURE_SOUNDCARD

#include "sound_calls.h"

#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)

#define MAX_SUB_BUFFERS		(32*MAX_REALTIME_FACTOR)

/*
 * The DSP channel can be used either for input or output. Variable
 * 'dma_mode' will be set when the program calls read or write first time
 * after open. Current version doesn't support mode changes without closing
 * and reopening the device. Support for this feature may be implemented in a
 * future version of this driver.
 */

#define DMODE_NONE		0
#define DMODE_OUTPUT		1
#define DMODE_INPUT		2

DEFINE_WAIT_QUEUES (dev_sleeper[MAX_DSP_DEV], dev_sleep_flag[MAX_DSP_DEV]);

static int      dma_mode[MAX_DSP_DEV] =
{0};				/* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */

static volatile int dmabuf_interrupted[MAX_DSP_DEV] =
{0};

/*
 * Pointers to raw buffers
 */

char           *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT] =
{
  {NULL}};
unsigned long   snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
int             snd_raw_count[MAX_DSP_DEV];

/*
 * Device state tables
 */

static int      dev_busy[MAX_DSP_DEV];
static int      dev_needs_restart[MAX_DSP_DEV];
static int      dev_modes[MAX_DSP_DEV];
static int      dev_active[MAX_DSP_DEV];
static int      dev_started[MAX_DSP_DEV];
static int      dev_qlen[MAX_DSP_DEV];
static int      dev_qhead[MAX_DSP_DEV];
static int      dev_qtail[MAX_DSP_DEV];
static int      dev_underrun[MAX_DSP_DEV];
static int      bufferalloc_done[MAX_DSP_DEV] =
{0};

/*
 * Logical buffers for each devices
 */

static int      dev_nbufs[MAX_DSP_DEV];	/* # of logical buffers ( >=

						   * sound_buffcounts[dev] */
static int      dev_counts[MAX_DSP_DEV][MAX_SUB_BUFFERS];
static int      dev_subdivision[MAX_DSP_DEV];
static unsigned long dev_buf_phys[MAX_DSP_DEV][MAX_SUB_BUFFERS];
static char    *dev_buf[MAX_DSP_DEV][MAX_SUB_BUFFERS] =
{
  {NULL}};
static int      dev_buffsize[MAX_DSP_DEV];

static void
reorganize_buffers (int dev)
{
  /*
   * This routine breaks the physical device buffers to logical ones.
   */

  unsigned        i, p, n;
  unsigned        sr, nc, sz, bsz;

  sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
  nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
  sz = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);

  if (sr < 1 || nc < 1 || sz < 1)
    {
      printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
      sr = DSP_DEFAULT_SPEED;
      nc = 1;
      sz = 8;
    }

  sz /= 8;			/* Convert # of bits -> # of bytes */

  sz = sr * nc * sz;

  /*
   * Compute a buffer size not exeeding 1 second.
   */

  bsz = sound_buffsizes[dev];

  while (bsz > sz)
    bsz >>= 1;			/* Divide by 2 */

  if (sound_buffcounts[dev] == 1 && bsz == sound_buffsizes[dev])
    bsz >>= 1;			/* Need at least 2 buffers */

  if (dev_subdivision[dev] == 0)
    dev_subdivision[dev] = 1;	/* Default value */

  bsz /= dev_subdivision[dev];	/* Use smaller buffers */

  if (bsz == 0)
    bsz = 4096;			/* Just a sanity check */

  while ((sound_buffsizes[dev] * sound_buffcounts[dev]) / bsz > MAX_SUB_BUFFERS)
    bsz <<= 1;			/* Too much buffers */

  dev_buffsize[dev] = bsz;
  n = 0;

  /*
   * Now computing addresses for the logical buffers
   */

  for (i = 0; i < snd_raw_count[dev]; i++)
    {
      p = 0;

      while ((p + bsz) <= sound_buffsizes[dev])
	{
	  dev_buf[dev][n] = snd_raw_buf[dev][i] + p;
	  dev_buf_phys[dev][n] = snd_raw_buf_phys[dev][i] + p;
	  p += bsz;
	  n++;
	}
    }

  dev_nbufs[dev] = n;

  for (i = 0; i < dev_nbufs[dev]; i++)
    {
      dev_counts[dev][i] = 0;
    }

  bufferalloc_done[dev] = 1;
}

static void
dma_init_buffers (int dev)
{
  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
  dev_underrun[dev] = 0;

  dev_busy[dev] = 1;

  bufferalloc_done[dev] = 0;

  dev_active[dev] = dev_qlen[dev] = dev_qtail[dev] = dev_qhead[dev] = 0;
  dev_needs_restart[dev] = dev_started[dev] = 0;
  dma_mode[dev] = DMODE_NONE;
}

int
DMAbuf_open (int dev, int mode)
{
  int             retval;

  if (dev >= num_dspdevs)
    {
      printk ("PCM device %d not installed.\n", dev);
      return RET_ERROR (ENXIO);
    }

  if (dev_busy[dev])
    return RET_ERROR (EBUSY);

  if (!dsp_devs[dev])
    {
      printk ("DSP device %d not initialized\n", dev);
      return RET_ERROR (ENXIO);
    }

#ifdef USE_RUNTIME_DMAMEM
  sound_dma_malloc (dev);
#endif

  if (snd_raw_buf[dev][0] == NULL)
    return RET_ERROR (ENOSPC);	/* Memory allocation failed during boot */

  if ((retval = dsp_devs[dev]->open (dev, mode)) < 0)
    return retval;

  dev_modes[dev] = mode;
  dev_subdivision[dev] = 0;

  dma_init_buffers (dev);
  dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
  dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
  dsp_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);

  return 0;
}

static void
dma_reset (int dev)
{
  int             retval;
  unsigned long   flags;

  DISABLE_INTR (flags);
  dsp_devs[dev]->reset (dev);
  dsp_devs[dev]->close (dev);

  if ((retval = dsp_devs[dev]->open (dev, dev_modes[dev])) < 0)
    printk ("Sound: Reset failed - Can't reopen device\n");
  RESTORE_INTR (flags);

  dma_init_buffers (dev);
  reorganize_buffers (dev);
}

static int
dma_sync (int dev)
{
  unsigned long   flags;

  if (dma_mode[dev] == DMODE_OUTPUT)
    {
      DISABLE_INTR (flags);

      while ((!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
		dmabuf_interrupted[dev]))
	     && dev_qlen[dev])
	{
	  DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
	  if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
	    return dev_qlen[dev];
	}
      RESTORE_INTR (flags);

      /*
       * Some devices such as GUS have huge amount of on board RAM for the
       * audio data. We have to wait util the device has finished playing.
       */

      DISABLE_INTR (flags);
      if (dsp_devs[dev]->has_output_drained)	/* Device has hidden buffers */
	{
	  while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
		   dmabuf_interrupted[dev])
		 && !dsp_devs[dev]->has_output_drained (dev))
	    {
	      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ / 4);
	    }
	}
      RESTORE_INTR (flags);
    }
  return dev_qlen[dev];
}

int
DMAbuf_release (int dev, int mode)
{

  if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) ||
	dmabuf_interrupted[dev])
      && (dma_mode[dev] == DMODE_OUTPUT))
    {
      dma_sync (dev);
    }

#ifdef USE_RUNTIME_DMAMEM
  sound_dma_free (dev);
#endif

  dsp_devs[dev]->reset (dev);

  dsp_devs[dev]->close (dev);

  dma_mode[dev] = DMODE_NONE;
  dev_busy[dev] = 0;

  return 0;
}

int
DMAbuf_getrdbuffer (int dev, char **buf, int *len)
{
  unsigned long   flags;
  int             err = EIO;

  DISABLE_INTR (flags);
  if (!dev_qlen[dev])
    {
      if (dev_needs_restart[dev])
	{
	  dma_reset (dev);
	  dev_needs_restart[dev] = 0;
	}

      if (dma_mode[dev] == DMODE_OUTPUT)	/* Was output -> direction change */
	{
	  dma_sync (dev);
	  dma_reset (dev);
	  dma_mode[dev] = DMODE_NONE;
	}

      if (!bufferalloc_done[dev])
	reorganize_buffers (dev);

      if (!dma_mode[dev])
	{
	  int             err;

	  if ((err = dsp_devs[dev]->prepare_for_input (dev,
				    dev_buffsize[dev], dev_nbufs[dev])) < 0)
	    {
	      RESTORE_INTR (flags);
	      return err;
	    }
	  dma_mode[dev] = DMODE_INPUT;
	}

      if (!dev_active[dev])
	{
	  dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
				      dev_buffsize[dev], 0,
				      !sound_dma_automode[dev] ||
				      !dev_started[dev]);
	  dev_active[dev] = 1;
	  dev_started[dev] = 1;
	}

      /* Wait for the next block */
      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
      if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
	{
	  printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
	  err = EIO;
	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
	}
      else
	err = EINTR;
    }
  RESTORE_INTR (flags);

  if (!dev_qlen[dev])
    return RET_ERROR (err);

  *buf = &dev_buf[dev][dev_qhead[dev]][dev_counts[dev][dev_qhead[dev]]];
  *len = dev_buffsize[dev] - dev_counts[dev][dev_qhead[dev]];

  return dev_qhead[dev];
}

int
DMAbuf_rmchars (int dev, int buff_no, int c)
{
  int             p = dev_counts[dev][dev_qhead[dev]] + c;

  if (p >= dev_buffsize[dev])
    {				/* This buffer is now empty */
      dev_counts[dev][dev_qhead[dev]] = 0;
      dev_qlen[dev]--;
      dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
    }
  else
    dev_counts[dev][dev_qhead[dev]] = p;

  return 0;
}

int
DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
{
  char           *dmabuf;
  int             buff_no, c, err;

  /*
   * This routine returns at most 'count' bytes from the dsp input buffers.
   * Returns negative value if there is an error.
   */

  if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &c)) < 0)
    return buff_no;

  if (c > count)
    c = count;

  COPY_TO_USER (user_buf, 0, dmabuf, c);

  if ((err = DMAbuf_rmchars (dev, buff_no, c)) < 0)
    return err;
  return c;

}

int
DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
{
  switch (cmd)
    {
    case SNDCTL_DSP_RESET:
      dma_reset (dev);
      return 0;
      break;

    case SNDCTL_DSP_SYNC:
      dma_sync (dev);
      dma_reset (dev);
      return 0;
      break;

    case SNDCTL_DSP_GETBLKSIZE:
      if (!bufferalloc_done[dev])
	reorganize_buffers (dev);

      return IOCTL_OUT (arg, dev_buffsize[dev]);
      break;

    case SNDCTL_DSP_SUBDIVIDE:
      {
	int             fact = IOCTL_IN (arg);

	if (fact == 0)
	  {
	    fact = dev_subdivision[dev];
	    if (fact == 0)
	      fact = 1;
	    return IOCTL_OUT (arg, fact);
	  }

	if (dev_subdivision[dev] != 0)	/* Too late to change */
	  return RET_ERROR (EINVAL);

	if (fact > MAX_REALTIME_FACTOR)
	  return RET_ERROR (EINVAL);

	if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
	  return RET_ERROR (EINVAL);

	dev_subdivision[dev] = fact;
	return IOCTL_OUT (arg, fact);
      }
      break;

    default:
      return dsp_devs[dev]->ioctl (dev, cmd, arg, local);
    }

  /* NOTREACHED */
  return RET_ERROR (EIO);
}

int
DMAbuf_getwrbuffer (int dev, char **buf, int *size)
{
  unsigned long   flags;
  int             err = EIO;

  if (dma_mode[dev] == DMODE_INPUT)	/* Was input -> Direction change */
    {
      dma_reset (dev);
      dma_mode[dev] = DMODE_NONE;
    }
  else if (dev_needs_restart[dev])	/* Restart buffering */
    {
      dma_sync (dev);
      dma_reset (dev);
    }

  dev_needs_restart[dev] = 0;

  if (!bufferalloc_done[dev])
    reorganize_buffers (dev);

  if (!dma_mode[dev])
    {
      int             err;

      dma_mode[dev] = DMODE_OUTPUT;
      if ((err = dsp_devs[dev]->prepare_for_output (dev,
				    dev_buffsize[dev], dev_nbufs[dev])) < 0)
	return err;
    }


  DISABLE_INTR (flags);

  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);

  if (dev_qlen[dev] == dev_nbufs[dev])
    {
      if (!dev_active[dev])
	{
	  printk ("Soundcard warning: DMA not activated %d/%d\n",
		  dev_qlen[dev], dev_nbufs[dev]);
	  return RET_ERROR (EIO);
	}

      /* Wait for free space */
      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
      if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
	{
	  printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
	  err = EIO;
	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
	}
      else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
	err = EINTR;
    }
  RESTORE_INTR (flags);

  if (dev_qlen[dev] == dev_nbufs[dev])
    return RET_ERROR (err);	/* We have got signal (?) */

  *buf = dev_buf[dev][dev_qtail[dev]];
  *size = dev_buffsize[dev];
  dev_counts[dev][dev_qtail[dev]] = 0;

  return dev_qtail[dev];
}

int
DMAbuf_start_output (int dev, int buff_no, int l)
{
  if (buff_no != dev_qtail[dev])
    printk ("Soundcard warning: DMA buffers out of sync %d != %d\n", buff_no, dev_qtail[dev]);

  dev_qlen[dev]++;

  dev_counts[dev][dev_qtail[dev]] = l;

  dev_needs_restart[dev] = (l != dev_buffsize[dev]) &&
    (sound_dma_automode[dev] || dsp_devs[dev]->flags & NEEDS_RESTART);

  dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];

  if (!dev_active[dev])
    {
      dev_active[dev] = 1;
      dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
				   dev_counts[dev][dev_qhead[dev]], 0,
			     !sound_dma_automode[dev] || !dev_started[dev]);
      dev_started[dev] = 1;
    }

  return 0;
}

int
DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
{
  int             chan = sound_dsp_dmachan[dev];
  unsigned long   flags;

  /*
   * This function is not as portable as it should be.
   */

  /*
   * The count must be one less than the actual size. This is handled by
   * set_dma_addr()
   */

  if (sound_dma_automode[dev])
    {				/* Auto restart mode. Transfer the whole
				 * buffer */
#ifdef linux
      DISABLE_INTR (flags);
      disable_dma (chan);
      clear_dma_ff (chan);
      set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
      set_dma_addr (chan, snd_raw_buf_phys[dev][0]);
      set_dma_count (chan, sound_buffsizes[dev]);
      enable_dma (chan);
      RESTORE_INTR (flags);
#else /* linux */

#ifdef __386BSD__
      printk ("sound: Invalid DMA mode for device %d\n", dev);

      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
		    snd_raw_buf_phys[dev][0],
		    sound_buffsizes[dev],
		    chan);
#else /* __386BSD__ */
#if defined(ISC) || defined(SCO) || defined(SVR42)
#ifndef DMAMODE_AUTO
      printk ("sound: Invalid DMA mode for device %d\n", dev);
#endif /* DMAMODE_AUTO */
      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
#ifdef DMAMODE_AUTO
		 | DMAMODE_AUTO
#endif /* DMAMODE_AUTO */
		 ,
		 snd_raw_buf_phys[dev][0], count);
      dma_enable (chan);
#else /* SYSV */
#error This routine is not valid for this OS.
#endif /* SYSV */
#endif /* __386BSD__ */

#endif /* linux */
    }
  else
    {
#ifdef linux
      DISABLE_INTR (flags);
      disable_dma (chan);
      clear_dma_ff (chan);
      set_dma_mode (chan, dma_mode);
      set_dma_addr (chan, physaddr);
      set_dma_count (chan, count);
      enable_dma (chan);
      RESTORE_INTR (flags);
#else /* linux */
#ifdef __386BSD__
      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
		    physaddr,
		    count,
		    chan);
#else /* __386BSD__ */

#if defined(ISC) || defined(SCO) || defined(SVR42)
      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
		 physaddr, count);
      dma_enable (chan);
#else /* SYSV */
#error This routine is not valid for this OS.
#endif /* SYSV */
#endif /* __386BSD__ */

#endif /* linux */
    }

  return count;
}

long
DMAbuf_init (long mem_start)
{
  int             i;

  /*
   * In this version the DMA buffer allocation is done by sound_mem_init()
   * which is called by init/main.c
   */

  for (i = 0; i < MAX_DSP_DEV; i++)
    {
      dev_qlen[i] = 0;
      dev_qhead[i] = 0;
      dev_qtail[i] = 0;
      dev_active[i] = 0;
      dev_busy[i] = 0;
      bufferalloc_done[i] = 0;
    }

  return mem_start;
}

void
DMAbuf_outputintr (int dev, int underrun_flag)
{
  unsigned long   flags;

  dev_qlen[dev]--;
  dev_qhead[dev] = (dev_qhead[dev] + 1) % dev_nbufs[dev];
  dev_active[dev] = 0;

  if (dev_qlen[dev])
    {
      dsp_devs[dev]->output_block (dev, dev_buf_phys[dev][dev_qhead[dev]],
				   dev_counts[dev][dev_qhead[dev]], 1,
				   !sound_dma_automode[dev]);
      dev_active[dev] = 1;
    }
  else if (underrun_flag)
    {
      dev_underrun[dev]++;
      dsp_devs[dev]->halt_xfer (dev);
      dev_needs_restart[dev] = (sound_dma_automode[dev] ||
				dsp_devs[dev]->flags & NEEDS_RESTART);
    }

  DISABLE_INTR (flags);
  if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
    {
      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
    }
  RESTORE_INTR (flags);
}

void
DMAbuf_inputintr (int dev)
{
  unsigned long   flags;

  if (!dev_busy[dev])
    {
      dsp_devs[dev]->close (dev);
    }
  else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
    {
      printk ("Sound: Recording overrun\n");
      dev_underrun[dev]++;
      dsp_devs[dev]->halt_xfer (dev);
      dev_active[dev] = 0;
      dev_needs_restart[dev] = sound_dma_automode[dev];
    }
  else
    {
      dev_qlen[dev]++;
      dev_qtail[dev] = (dev_qtail[dev] + 1) % dev_nbufs[dev];

      dsp_devs[dev]->start_input (dev, dev_buf_phys[dev][dev_qtail[dev]],
				  dev_buffsize[dev], 1,
				  !sound_dma_automode[dev]);
      dev_active[dev] = 1;
    }

  DISABLE_INTR (flags);
  if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
    {
      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
    }
  RESTORE_INTR (flags);
}

int
DMAbuf_open_dma (int dev)
{
  unsigned long   flags;
  int             chan = sound_dsp_dmachan[dev];

  if (ALLOC_DMA_CHN (chan))
    {
      printk ("Unable to grab DMA%d for the audio driver\n", chan);
      return 0;
    }

  DISABLE_INTR (flags);
#ifdef linux
  disable_dma (chan);
  clear_dma_ff (chan);
#endif
  RESTORE_INTR (flags);

  return 1;
}

void
DMAbuf_close_dma (int dev)
{
  int             chan = sound_dsp_dmachan[dev];

  DMAbuf_reset_dma (chan);
  RELEASE_DMA_CHN (chan);
}

void
DMAbuf_reset_dma (int chan)
{
}

/*
 * The sound_mem_init() is called by mem_init() immediately after mem_map is
 * initialized and before free_page_list is created.
 *
 * This routine allocates DMA buffers at the end of available physical memory (
 * <16M) and marks pages reserved at mem_map.
 */

#else
/* Stub versions if audio services not included  */

int
DMAbuf_open (int dev, int mode)
{
  return RET_ERROR (ENXIO);
}

int
DMAbuf_release (int dev, int mode)
{
  return 0;
}

int
DMAbuf_read (int dev, snd_rw_buf * user_buf, int count)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_getwrbuffer (int dev, char **buf, int *size)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_getrdbuffer (int dev, char **buf, int *len)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_rmchars (int dev, int buff_no, int c)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_start_output (int dev, int buff_no, int l)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
{
  return RET_ERROR (EIO);
}

long
DMAbuf_init (long mem_start)
{
  return mem_start;
}

int
DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
{
  return RET_ERROR (EIO);
}

int
DMAbuf_open_dma (int chan)
{
  return RET_ERROR (ENXIO);
}

void
DMAbuf_close_dma (int chan)
{
  return;
}

void
DMAbuf_reset_dma (int chan)
{
  return;
}

void
DMAbuf_inputintr (int dev)
{
  return;
}

void
DMAbuf_outputintr (int dev, int underrun_flag)
{
  return;
}

#endif

#endif
