patch-1.3.34 linux/drivers/sound/dmabuf.c

Next file: linux/drivers/sound/gus_card.c
Previous file: linux/drivers/sound/dev_table.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.33/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
@@ -31,15 +31,24 @@
 
 #ifdef CONFIGURE_SOUNDCARD
 
-#include "sound_calls.h"
-
 #if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
 
-static int space_in_queue (int dev);
+static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] =
+{NULL};
+static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] =
+{
+  {0}};
+static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] =
+{NULL};
+static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
+{
+  {0}};
+
+static int      ndmaps = 0;
 
-DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);
+#define MAX_DMAP (MAX_AUDIO_DEV*2)
 
-static struct dma_buffparms dmaps[MAX_AUDIO_DEV] =
+static struct dma_buffparms dmaps[MAX_DMAP] =
 {
   {0}};				/*
 
@@ -47,18 +56,21 @@
 				   * such a large array.
 				   * Needs dynamic run-time alloction.
 				 */
+static int      space_in_queue (int dev);
+
+static void     dma_reset_output (int dev);
+static void     dma_reset_input (int dev);
 
 static void
-reorganize_buffers (int dev)
+reorganize_buffers (int dev, struct dma_buffparms *dmap)
 {
   /*
    * This routine breaks the physical device buffers to logical ones.
    */
 
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
   struct audio_operations *dsp_dev = audio_devs[dev];
 
-  unsigned        i, p, n;
+  unsigned        i, n;
   unsigned        sr, nc, sz, bsz;
 
   if (dmap->fragment_size == 0)
@@ -68,6 +80,11 @@
       nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
       sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
 
+      if (sz == 8)
+	dmap->neutral_byte = 0x80;
+      else
+	dmap->neutral_byte = 0x00;
+
       if (sr < 1 || nc < 1 || sz < 1)
 	{
 	  printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
@@ -77,10 +94,10 @@
 	  sz = 8;
 	}
 
-      sz /= 8;			/* #bits -> #bytes */
-
       sz = sr * nc * sz;
 
+      sz /= 8;			/* #bits -> #bytes */
+
       /*
          * Compute a buffer size for time not exeeding 1 second.
          * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
@@ -91,19 +108,16 @@
       while (bsz > sz)
 	bsz /= 2;
 
-      if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize)
+      if (bsz == dsp_dev->buffsize)
 	bsz /= 2;		/* Needs at least 2 buffers */
 
       if (dmap->subdivision == 0)	/* Not already set */
 	dmap->subdivision = 1;	/* Init to default value */
+      else
+	bsz /= dmap->subdivision;
 
-      bsz /= dmap->subdivision;
-
-      if (bsz < 64)
-	bsz = 4096;		/* Just a sanity check */
-
-      while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS)
-	bsz *= 2;
+      if (bsz < 16)
+	bsz = 16;		/* Just a sanity check */
 
       dmap->fragment_size = bsz;
     }
@@ -113,33 +127,18 @@
          * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or
          * the buffer sice computation has already been done.
        */
-      if (dmap->fragment_size > audio_devs[dev]->buffsize)
-	dmap->fragment_size = audio_devs[dev]->buffsize;
+      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
+	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
       bsz = dmap->fragment_size;
     }
 
-  /*
-   * Now computing addresses for the logical buffers
-   */
-
-  n = 0;
-  for (i = 0; i < dmap->raw_count &&
-       n < dmap->max_fragments &&
-       n < MAX_SUB_BUFFERS; i++)
-    {
-      p = 0;
-
-      while ((p + bsz) <= dsp_dev->buffsize &&
-	     n < dmap->max_fragments &&
-	     n < MAX_SUB_BUFFERS)
-	{
-	  dmap->buf[n] = dmap->raw_buf[i] + p;
-	  dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p;
-	  p += bsz;
-	  n++;
-	}
-    }
+  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */
 
+  n = dsp_dev->buffsize / bsz;
+  if (n > MAX_SUB_BUFFERS)
+    n = MAX_SUB_BUFFERS;
+  if (n > dmap->max_fragments)
+    n = dmap->max_fragments;
   dmap->nbufs = n;
   dmap->bytes_in_use = n * bsz;
 
@@ -152,61 +151,127 @@
 }
 
 static void
-dma_init_buffers (int dev)
+dma_init_buffers (int dev, struct dma_buffparms *dmap)
 {
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev];
-
-  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+  if (dmap == audio_devs[dev]->dmap_out)
+    {
+      {
+	out_sleep_flag[dev].aborting = 0;
+	out_sleep_flag[dev].mode = WK_NONE;
+      };
+    }
+  else
+    {
+      in_sleep_flag[dev].aborting = 0;
+      in_sleep_flag[dev].mode = WK_NONE;
+    };
 
   dmap->flags = DMA_BUSY;	/* Other flags off */
   dmap->qlen = dmap->qhead = dmap->qtail = 0;
+  dmap->nbufs = 1;
+  dmap->bytes_in_use = audio_devs[dev]->buffsize;
 
   dmap->dma_mode = DMODE_NONE;
+  dmap->mapping_flags = 0;
+  dmap->neutral_byte = 0x00;
+}
+
+static int
+open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
+{
+  if (dmap->flags & DMA_BUSY)
+    return -EBUSY;
+
+  {
+    int             err;
+
+    if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0)
+      return err;
+  }
+
+  if (dmap->raw_buf == NULL)
+    return -ENOSPC;		/* Memory allocation failed during boot */
+
+  if (sound_open_dma (chan, audio_devs[dev]->name))
+    {
+      printk ("Unable to grab(2) DMA%d for the audio driver\n", chan);
+      return -EBUSY;
+    }
+
+  dmap->open_mode = mode;
+  dmap->subdivision = dmap->underrun_count = 0;
+  dmap->fragment_size = 0;
+  dmap->max_fragments = 65536;	/* Just a large value */
+  dmap->byte_counter = 0;
+
+  dma_init_buffers (dev, dmap);
+
+  return 0;
+}
+
+static void
+close_dmap (int dev, struct dma_buffparms *dmap, int chan)
+{
+  sound_close_dma (chan);
+
+  if (dmap->flags & DMA_BUSY)
+    dmap->dma_mode = DMODE_NONE;
+  dmap->flags &= ~DMA_BUSY;
+
+  sound_free_dmap (dev, dmap);
 }
 
 int
 DMAbuf_open (int dev, int mode)
 {
   int             retval;
-  struct dma_buffparms *dmap = NULL;
+  struct dma_buffparms *dmap_in = NULL;
+  struct dma_buffparms *dmap_out = NULL;
 
   if (dev >= num_audiodevs)
     {
       printk ("PCM device %d not installed.\n", dev);
-      return RET_ERROR (ENXIO);
+      return -ENXIO;
     }
 
   if (!audio_devs[dev])
     {
       printk ("PCM device %d not initialized\n", dev);
-      return RET_ERROR (ENXIO);
+      return -ENXIO;
     }
 
-  dmap = audio_devs[dev]->dmap = &dmaps[dev];
-
-  if (dmap->flags & DMA_BUSY)
-    return RET_ERROR (EBUSY);
-
-#ifdef USE_RUNTIME_DMAMEM
-  dmap->raw_buf[0] = NULL;
-  sound_dma_malloc (dev);
-#endif
-
-  if (dmap->raw_buf[0] == NULL)
-    return RET_ERROR (ENOSPC);	/* Memory allocation failed during boot */
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+    {
+      audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
+      audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1;
+    }
 
   if ((retval = audio_devs[dev]->open (dev, mode)) < 0)
     return retval;
 
-  dmap->open_mode = mode;
-  dmap->subdivision = dmap->underrun_count = 0;
-  dmap->fragment_size = 0;
-  dmap->max_fragments = 65536;	/* Just a large value */
+  dmap_out = audio_devs[dev]->dmap_out;
+  dmap_in = audio_devs[dev]->dmap_in;
 
-  dma_init_buffers (dev);
-  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
-  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
-  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
+  if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0)
+    {
+      audio_devs[dev]->close (dev);
+      return retval;
+    }
+
+  audio_devs[dev]->enable_bits = mode;
+  if (audio_devs[dev]->flags & DMA_DUPLEX && dmap_out != dmap_in)
+    if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0)
+      {
+	audio_devs[dev]->close (dev);
+	close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1);
+	return retval;
+      }
+  audio_devs[dev]->open_mode = mode;
+  audio_devs[dev]->go = 1;
+
+  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, (ioctl_arg) 8, 1);
+  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, (ioctl_arg) 1, 1);
+  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, (ioctl_arg) DSP_DEFAULT_SPEED, 1);
 
   return 0;
 }
@@ -214,20 +279,53 @@
 static void
 dma_reset (int dev)
 {
-  int             retval;
   unsigned long   flags;
 
-  DISABLE_INTR (flags);
-
+  save_flags (flags);
+  cli ();
   audio_devs[dev]->reset (dev);
-  audio_devs[dev]->close (dev);
+  restore_flags (flags);
+
+  dma_reset_output (dev);
+
+  if (audio_devs[dev]->flags & DMA_DUPLEX)
+    dma_reset_input (dev);
+}
+
+static void
+dma_reset_output (int dev)
+{
+  unsigned long   flags;
 
-  if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0)
-    printk ("Sound: Reset failed - Can't reopen device\n");
-  RESTORE_INTR (flags);
+  save_flags (flags);
+  cli ();
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+      !audio_devs[dev]->halt_output)
+    audio_devs[dev]->reset (dev);
+  else
+    audio_devs[dev]->halt_output (dev);
+  restore_flags (flags);
 
-  dma_init_buffers (dev);
-  reorganize_buffers (dev);
+  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_out);
+}
+
+static void
+dma_reset_input (int dev)
+{
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+      !audio_devs[dev]->halt_input)
+    audio_devs[dev]->reset (dev);
+  else
+    audio_devs[dev]->halt_input (dev);
+  restore_flags (flags);
+
+  dma_init_buffers (dev, audio_devs[dev]->dmap_in);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_in);
 }
 
 static int
@@ -235,39 +333,80 @@
 {
   unsigned long   flags;
 
-  if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)
+  if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+    return 0;
+
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
     {
-      DISABLE_INTR (flags);
+      save_flags (flags);
+      cli ();
 
-      while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])
-	     && audio_devs[dev]->dmap->qlen)
+      while (!((current->signal & ~current->blocked))
+	     && audio_devs[dev]->dmap_out->qlen)
 	{
-	  DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
-	  if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+
+	  {
+	    unsigned long   tl;
+
+	    if (10 * HZ)
+	      tl = current->timeout = jiffies + (10 * HZ);
+	    else
+	      tl = 0xffffffff;
+	    out_sleep_flag[dev].mode = WK_SLEEP;
+	    interruptible_sleep_on (&out_sleeper[dev]);
+	    if (!(out_sleep_flag[dev].mode & WK_WAKEUP))
+	      {
+		if (current->signal & ~current->blocked)
+		  out_sleep_flag[dev].aborting = 1;
+		else if (jiffies >= tl)
+		  out_sleep_flag[dev].mode |= WK_TIMEOUT;
+	      }
+	    out_sleep_flag[dev].mode &= ~WK_SLEEP;
+	  };
+	  if ((out_sleep_flag[dev].mode & WK_TIMEOUT))
 	    {
-	      RESTORE_INTR (flags);
-	      return audio_devs[dev]->dmap->qlen;
+	      restore_flags (flags);
+	      return audio_devs[dev]->dmap_out->qlen;
 	    }
 	}
-      RESTORE_INTR (flags);
+      restore_flags (flags);
 
       /*
        * Some devices such as GUS have huge amount of on board RAM for the
        * audio data. We have to wait until the device has finished playing.
        */
 
-      DISABLE_INTR (flags);
+      save_flags (flags);
+      cli ();
       if (audio_devs[dev]->local_qlen)	/* Device has hidden buffers */
 	{
-	  while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+	  while (!(((current->signal & ~current->blocked)))
 		 && audio_devs[dev]->local_qlen (dev))
 	    {
-	      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ);
+
+	      {
+		unsigned long   tl;
+
+		if (HZ)
+		  tl = current->timeout = jiffies + (HZ);
+		else
+		  tl = 0xffffffff;
+		out_sleep_flag[dev].mode = WK_SLEEP;
+		interruptible_sleep_on (&out_sleeper[dev]);
+		if (!(out_sleep_flag[dev].mode & WK_WAKEUP))
+		  {
+		    if (current->signal & ~current->blocked)
+		      out_sleep_flag[dev].aborting = 1;
+		    else if (jiffies >= tl)
+		      out_sleep_flag[dev].mode |= WK_TIMEOUT;
+		  }
+		out_sleep_flag[dev].mode &= ~WK_SLEEP;
+	      };
 	    }
 	}
-      RESTORE_INTR (flags);
+      restore_flags (flags);
     }
-  return audio_devs[dev]->dmap->qlen;
+  return audio_devs[dev]->dmap_out->qlen;
 }
 
 int
@@ -275,25 +414,75 @@
 {
   unsigned long   flags;
 
-  if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
-      && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT))
+  if (!(((current->signal & ~current->blocked)))
+      && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
     {
       dma_sync (dev);
     }
 
-#ifdef USE_RUNTIME_DMAMEM
-  sound_dma_free (dev);
-#endif
-
-  DISABLE_INTR (flags);
+  save_flags (flags);
+  cli ();
   audio_devs[dev]->reset (dev);
 
   audio_devs[dev]->close (dev);
 
-  audio_devs[dev]->dmap->dma_mode = DMODE_NONE;
-  audio_devs[dev]->dmap->flags &= ~DMA_BUSY;
-  RESTORE_INTR (flags);
+  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+
+  if (audio_devs[dev]->flags & DMA_DUPLEX)
+    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+  audio_devs[dev]->open_mode = 0;
+
+  restore_flags (flags);
+
+  return 0;
+}
+
+static int
+activate_recording (int dev, struct dma_buffparms *dmap)
+{
+  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
+    return 0;
+
+  if (dmap->flags & DMA_RESTART)
+    {
+      dma_reset_input (dev);
+      dmap->flags &= ~DMA_RESTART;
+    }
+
+  if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
+    {
+      dma_sync (dev);
+      dma_reset (dev);
+      dmap->dma_mode = DMODE_NONE;
+    }
+
+  if (!(dmap->flags & DMA_ALLOC_DONE))
+    reorganize_buffers (dev, dmap);
+
+  if (!dmap->dma_mode)
+    {
+      int             err;
+
+      if ((err = audio_devs[dev]->prepare_for_input (dev,
+				     dmap->fragment_size, dmap->nbufs)) < 0)
+	{
+	  return err;
+	}
+      dmap->dma_mode = DMODE_INPUT;
+    }
 
+  if (!(dmap->flags & DMA_ACTIVE))
+    {
+      audio_devs[dev]->start_input (dev, dmap->raw_buf_phys +
+				    dmap->qtail * dmap->fragment_size,
+				    dmap->fragment_size, 0,
+				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+				    !(dmap->flags & DMA_STARTED));
+      dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+      if (audio_devs[dev]->trigger)
+	audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+    }
   return 0;
 }
 
@@ -302,73 +491,79 @@
 {
   unsigned long   flags;
   int             err = EIO;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
-  DISABLE_INTR (flags);
-  if (!dmap->qlen)
+  save_flags (flags);
+  cli ();
+  if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
     {
-      if (dmap->flags & DMA_RESTART)
-	{
-	  dma_reset (dev);
-	  dmap->flags &= ~DMA_RESTART;
-	}
+      printk ("Sound: Can't read from mmapped device (1)\n");
+      return -EINVAL;
+    }
+  else if (!dmap->qlen)
+    {
+      int             timeout;
 
-      if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
+      if ((err = activate_recording (dev, dmap)) < 0)
 	{
-	  dma_sync (dev);
-	  dma_reset (dev);
-	  dmap->dma_mode = DMODE_NONE;
+	  restore_flags (flags);
+	  return err;
 	}
 
-      if (!(dmap->flags & DMA_ALLOC_DONE))
-	reorganize_buffers (dev);
+      /* Wait for the next block */
 
-      if (!dmap->dma_mode)
+      if (dontblock)
 	{
-	  int             err;
-
-	  if ((err = audio_devs[dev]->prepare_for_input (dev,
-				     dmap->fragment_size, dmap->nbufs)) < 0)
-	    {
-	      RESTORE_INTR (flags);
-	      return err;
-	    }
-	  dmap->dma_mode = DMODE_INPUT;
+	  restore_flags (flags);
+	  return -EAGAIN;
 	}
 
-      if (!(dmap->flags & DMA_ACTIVE))
+      if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) &
+	  audio_devs[dev]->go)
 	{
-	  audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
-					dmap->fragment_size, 0,
-				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
-					!(dmap->flags & DMA_STARTED));
-	  dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+	  restore_flags (flags);
+	  return -EAGAIN;
 	}
 
-      if (dontblock)
-	{
-	  RESTORE_INTR (flags);
-	  return RET_ERROR (EAGAIN);
-	}
+      if (!audio_devs[dev]->go)
+	timeout = 0;
+      else
+	timeout = 2 * HZ;
 
-      /* 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]))
+      {
+	unsigned long   tl;
+
+	if (timeout)
+	  tl = current->timeout = jiffies + (timeout);
+	else
+	  tl = 0xffffffff;
+	in_sleep_flag[dev].mode = WK_SLEEP;
+	interruptible_sleep_on (&in_sleeper[dev]);
+	if (!(in_sleep_flag[dev].mode & WK_WAKEUP))
+	  {
+	    if (current->signal & ~current->blocked)
+	      in_sleep_flag[dev].aborting = 1;
+	    else if (jiffies >= tl)
+	      in_sleep_flag[dev].mode |= WK_TIMEOUT;
+	  }
+	in_sleep_flag[dev].mode &= ~WK_SLEEP;
+      };
+      if ((in_sleep_flag[dev].mode & WK_TIMEOUT))
 	{
-	  printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+	  printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
 	  err = EIO;
-	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+	  in_sleep_flag[dev].aborting = 1;
 	}
       else
 	err = EINTR;
     }
-  RESTORE_INTR (flags);
+  restore_flags (flags);
 
   if (!dmap->qlen)
-    return RET_ERROR (err);
+    return -err;
 
-  *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]];
+  *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
   *len = dmap->fragment_size - dmap->counts[dmap->qhead];
 
   return dmap->qhead;
@@ -377,11 +572,16 @@
 int
 DMAbuf_rmchars (int dev, int buff_no, int c)
 {
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
   int             p = dmap->counts[dmap->qhead] + c;
 
-  if (p >= dmap->fragment_size)
+  if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+    {
+      printk ("Sound: Can't read from mmapped device (2)\n");
+      return -EINVAL;
+    }
+  else if (p >= dmap->fragment_size)
     {				/* This buffer is completely empty */
       dmap->counts[dmap->qhead] = 0;
       if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
@@ -396,10 +596,105 @@
   return 0;
 }
 
+static int
+dma_subdivide (int dev, struct dma_buffparms *dmap, ioctl_arg arg, int fact)
+{
+  if (fact == 0)
+    {
+      fact = dmap->subdivision;
+      if (fact == 0)
+	fact = 1;
+      return snd_ioctl_return ((int *) arg, fact);
+    }
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Loo late to change */
+    return -EINVAL;
+
+  if (fact > MAX_REALTIME_FACTOR)
+    return -EINVAL;
+
+  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+    return -EINVAL;
+
+  dmap->subdivision = fact;
+  return snd_ioctl_return ((int *) arg, fact);
+}
+
+static int
+dma_set_fragment (int dev, struct dma_buffparms *dmap, ioctl_arg arg, int fact)
+{
+  int             bytes, count;
+
+  if (fact == 0)
+    return -EIO;
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Loo late to change */
+    return -EINVAL;
+
+  bytes = fact & 0xffff;
+  count = (fact >> 16) & 0xffff;
+
+  if (count == 0)
+    count = MAX_SUB_BUFFERS;
+
+  if (bytes < 4 || bytes > 17)	/* <16 || > 128k */
+    return -EINVAL;
+
+  if (count < 2)
+    return -EINVAL;
+
+  dmap->fragment_size = (1 << bytes);
+  dmap->max_fragments = count;
+
+  if (dmap->fragment_size > audio_devs[dev]->buffsize)
+    dmap->fragment_size = audio_devs[dev]->buffsize;
+
+  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+      audio_devs[dev]->flags & DMA_AUTOMODE)
+    dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
+
+  dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
+  return snd_ioctl_return ((int *) arg, bytes | (count << 16));
+}
+
+static int
+get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
+{
+  int             pos;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  if (!(dmap->flags & DMA_ACTIVE))
+    pos = 0;
+  else
+    {
+      clear_dma_ff (chan);
+      disable_dma (chan);
+      pos = get_dma_residue (chan);
+      enable_dma (chan);
+    }
+  restore_flags (flags);
+  /* printk ("%04x ", pos); */
+
+  if (audio_devs[dev]->flags & DMA_AUTOMODE)
+    return dmap->bytes_in_use - pos;
+  else
+    {
+      pos = dmap->fragment_size - pos;
+      if (pos < 0)
+	return 0;
+      return pos;
+    }
+}
+
 int
-DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+DMAbuf_ioctl (int dev, unsigned int cmd, ioctl_arg arg, int local)
 {
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
 
   switch (cmd)
     {
@@ -415,88 +710,64 @@
       break;
 
     case SNDCTL_DSP_GETBLKSIZE:
-      if (!(dmap->flags & DMA_ALLOC_DONE))
-	reorganize_buffers (dev);
+      if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	reorganize_buffers (dev, dmap_out);
 
-      return IOCTL_OUT (arg, dmap->fragment_size);
+      return snd_ioctl_return ((int *) arg, dmap_out->fragment_size);
       break;
 
     case SNDCTL_DSP_SUBDIVIDE:
       {
-	int             fact = IOCTL_IN (arg);
-
-	if (fact == 0)
-	  {
-	    fact = dmap->subdivision;
-	    if (fact == 0)
-	      fact = 1;
-	    return IOCTL_OUT (arg, fact);
-	  }
+	int             fact = get_fs_long ((long *) arg);
+	int             ret;
 
-	if (dmap->subdivision != 0 ||
-	    dmap->fragment_size)	/* Loo late to change */
-	  return RET_ERROR (EINVAL);
+	ret = dma_subdivide (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
 
-	if (fact > MAX_REALTIME_FACTOR)
-	  return RET_ERROR (EINVAL);
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  ret = dma_subdivide (dev, dmap_in, arg, fact);
 
-	if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
-	  return RET_ERROR (EINVAL);
-
-	dmap->subdivision = fact;
-	return IOCTL_OUT (arg, fact);
+	return ret;
       }
       break;
 
     case SNDCTL_DSP_SETFRAGMENT:
       {
-	int             fact = IOCTL_IN (arg);
-	int             bytes, count;
-
-	if (fact == 0)
-	  return RET_ERROR (EIO);
-
-	if (dmap->subdivision != 0 ||
-	    dmap->fragment_size)	/* Loo late to change */
-	  return RET_ERROR (EINVAL);
-
-	bytes = fact & 0xffff;
-	count = (fact >> 16) & 0xffff;
-
-	if (count == 0)
-	  count = MAX_SUB_BUFFERS;
-
-	if (bytes < 7 || bytes > 17)	/* <64 || > 128k */
-	  return RET_ERROR (EINVAL);
+	int             fact = get_fs_long ((long *) arg);
+	int             ret;
 
-	if (count < 2)
-	  return RET_ERROR (EINVAL);
+	ret = dma_set_fragment (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
 
-	dmap->fragment_size = (1 << bytes);
-	dmap->max_fragments = count;
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  ret = dma_set_fragment (dev, dmap_in, arg, fact);
 
-	if (dmap->fragment_size > audio_devs[dev]->buffsize)
-	  dmap->fragment_size = audio_devs[dev]->buffsize;
-
-	if (dmap->fragment_size == audio_devs[dev]->buffsize &&
-	    audio_devs[dev]->flags & DMA_AUTOMODE)
-	  dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
-
-	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
-	return IOCTL_OUT (arg, bytes | (count << 16));
+	return ret;
       }
       break;
 
     case SNDCTL_DSP_GETISPACE:
     case SNDCTL_DSP_GETOSPACE:
       if (!local)
-	return RET_ERROR (EINVAL);
+	return -EINVAL;
       else
 	{
+	  struct dma_buffparms *dmap = dmap_out;
+
 	  audio_buf_info *info = (audio_buf_info *) arg;
 
+	  if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+	    dmap = dmap_in;
+
+	  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	    return -EINVAL;
+
 	  if (!(dmap->flags & DMA_ALLOC_DONE))
-	    reorganize_buffers (dev);
+	    reorganize_buffers (dev, dmap);
+
+	  info->fragstotal = dmap->nbufs;
 
 	  if (cmd == SNDCTL_DSP_GETISPACE)
 	    info->fragments = dmap->qlen;
@@ -533,17 +804,127 @@
 	}
       return 0;
 
+    case SNDCTL_DSP_SETTRIGGER:
+      {
+	unsigned long   flags;
+
+	int             bits = get_fs_long ((long *) arg) & audio_devs[dev]->open_mode;
+	int             changed;
+
+	if (audio_devs[dev]->trigger == NULL)
+	  return -EINVAL;
+
+	if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+	  if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
+	    {
+	      printk ("Sound: Device doesn't have full duplex capability\n");
+	      return -EINVAL;
+	    }
+
+	save_flags (flags);
+	cli ();
+	changed = audio_devs[dev]->enable_bits ^ bits;
+
+	if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
+	  {
+	    if (!(dmap_in->flags & DMA_ALLOC_DONE))
+	      reorganize_buffers (dev, dmap_in);
+
+	    activate_recording (dev, dmap_in);
+	  }
+
+	if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+	    dmap_out->mapping_flags & DMA_MAP_MAPPED &&
+	    audio_devs[dev]->go)
+	  {
+	    if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	      reorganize_buffers (dev, dmap_out);
+
+	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+	    DMAbuf_start_output (dev, 0, dmap_out->fragment_size);
+	  }
+
+	audio_devs[dev]->enable_bits = bits;
+	if (changed && audio_devs[dev]->trigger)
+	  audio_devs[dev]->trigger (dev, bits * audio_devs[dev]->go);
+	restore_flags (flags);
+      }
+    case SNDCTL_DSP_GETTRIGGER:
+      return snd_ioctl_return ((int *) arg, audio_devs[dev]->enable_bits);
+      break;
+
+    case SNDCTL_DSP_GETIPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_in->byte_counter;
+	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in);
+	info.blocks = audio_devs[dev]->dmap_in->qlen;
+	info.bytes += info.ptr;
+	memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+
+	if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+	  audio_devs[dev]->dmap_in->qlen = 0;	/* Acknowledge interrupts */
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_DSP_GETOPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_out->byte_counter;
+	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out);
+	info.blocks = audio_devs[dev]->dmap_out->qlen;
+	info.bytes += info.ptr;
+	memcpy_tofs (&(((char *) arg)[0]), ((char *) &info), (sizeof (info)));
+
+	if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+	  audio_devs[dev]->dmap_out->qlen = 0;	/* Acknowledge interrupts */
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+
     default:
       return audio_devs[dev]->ioctl (dev, cmd, arg, local);
     }
 
 }
 
+/*
+ * DMAbuf_start_devices() is called by the /dev/music driver to start
+ * one or more audio devices at desired moment.
+ */
+
+void
+DMAbuf_start_devices (unsigned int devmask)
+{
+  int             dev;
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (devmask & (1 << dev))
+      if (audio_devs[dev]->open_mode != 0)
+	if (!audio_devs[dev]->go)
+	  {
+	    /* OK to start the device */
+	    audio_devs[dev]->go = 1;
+	  }
+}
+
 static int
 space_in_queue (int dev)
 {
   int             len, max, tmp;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
   if (dmap->qlen >= dmap->nbufs)	/* No space at all */
     return 0;
@@ -576,7 +957,13 @@
 {
   unsigned long   flags;
   int             abort, err = EIO;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+    {
+      printk ("Sound: Can't write to mmapped device (3)\n");
+      return -EINVAL;
+    }
 
   if (dmap->dma_mode == DMODE_INPUT)	/* Direction change */
     {
@@ -586,13 +973,13 @@
   else if (dmap->flags & DMA_RESTART)	/* Restart buffering */
     {
       dma_sync (dev);
-      dma_reset (dev);
+      dma_reset_output (dev);
     }
 
   dmap->flags &= ~DMA_RESTART;
 
   if (!(dmap->flags & DMA_ALLOC_DONE))
-    reorganize_buffers (dev);
+    reorganize_buffers (dev, dmap);
 
   if (!dmap->dma_mode)
     {
@@ -604,41 +991,76 @@
 	return err;
     }
 
-
-  if (dontblock)
-    return RET_ERROR (EAGAIN);
-
-  DISABLE_INTR (flags);
+  save_flags (flags);
+  cli ();
 
   abort = 0;
   while (!space_in_queue (dev) &&
 	 !abort)
     {
+      int             timeout;
+
+      if (dontblock)
+	{
+	  restore_flags (flags);
+	  return -EAGAIN;
+	}
+
+      if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT) &&
+	  audio_devs[dev]->go)
+	{
+	  restore_flags (flags);
+	  return -EAGAIN;
+	}
+
       /*
        * 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]))
+      if (!audio_devs[dev]->go)
+	timeout = 0;
+      else
+	timeout = 2 * HZ;
+
+
+      {
+	unsigned long   tl;
+
+	if (timeout)
+	  tl = current->timeout = jiffies + (timeout);
+	else
+	  tl = 0xffffffff;
+	out_sleep_flag[dev].mode = WK_SLEEP;
+	interruptible_sleep_on (&out_sleeper[dev]);
+	if (!(out_sleep_flag[dev].mode & WK_WAKEUP))
+	  {
+	    if (current->signal & ~current->blocked)
+	      out_sleep_flag[dev].aborting = 1;
+	    else if (jiffies >= tl)
+	      out_sleep_flag[dev].mode |= WK_TIMEOUT;
+	  }
+	out_sleep_flag[dev].mode &= ~WK_SLEEP;
+      };
+      if ((out_sleep_flag[dev].mode & WK_TIMEOUT))
 	{
-	  printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+	  printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
 	  err = EIO;
 	  abort = 1;
-	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+	  out_sleep_flag[dev].aborting = 1;
 	}
-      else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+      else if (((current->signal & ~current->blocked)))
 	{
 	  err = EINTR;
 	  abort = 1;
 	}
     }
-  RESTORE_INTR (flags);
+  restore_flags (flags);
 
   if (!space_in_queue (dev))
     {
-      return RET_ERROR (err);	/* Caught a signal ? */
+      return -err;		/* Caught a signal ? */
     }
 
-  *buf = dmap->buf[dmap->qtail];
+  *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
   *size = dmap->fragment_size;
   dmap->counts[dmap->qtail] = 0;
 
@@ -648,35 +1070,54 @@
 int
 DMAbuf_start_output (int dev, int buff_no, int l)
 {
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (buff_no != dmap->qtail)
-    printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);
+/*
+ * Bypass buffering if using mmaped access
+ */
 
-  dmap->qlen++;
-  if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
-    printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n",
-	    dev, dmap->qlen, dmap->nbufs);
-
-  dmap->counts[dmap->qtail] = l;
-
-  if ((l != dmap->fragment_size) &&
-      ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
-       audio_devs[dev]->flags & NEEDS_RESTART))
-    dmap->flags |= DMA_RESTART;
+  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+    {
+      l = dmap->fragment_size;
+      dmap->counts[dmap->qtail] = l;
+      dmap->flags &= ~DMA_RESTART;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+    }
   else
-    dmap->flags &= ~DMA_RESTART;
+    {
+
+      if (buff_no != dmap->qtail)
+	printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);
+
+      dmap->qlen++;
+      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+	printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n",
+		dev, dmap->qlen, dmap->nbufs);
+
+      dmap->counts[dmap->qtail] = l;
 
-  dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      if ((l != dmap->fragment_size) &&
+	  ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+	   audio_devs[dev]->flags & NEEDS_RESTART))
+	dmap->flags |= DMA_RESTART;
+      else
+	dmap->flags &= ~DMA_RESTART;
+
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+    }
 
   if (!(dmap->flags & DMA_ACTIVE))
     {
       dmap->flags |= DMA_ACTIVE;
-      audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+      audio_devs[dev]->output_block (dev, dmap->raw_buf_phys +
+				     dmap->qhead * dmap->fragment_size,
 				     dmap->counts[dmap->qhead], 0,
 				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
 				     !(dmap->flags & DMA_STARTED));
       dmap->flags |= DMA_STARTED;
+      if (audio_devs[dev]->trigger)
+	audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
     }
 
   return 0;
@@ -685,13 +1126,20 @@
 int
 DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
 {
-  int             chan = audio_devs[dev]->dmachan;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  int             chan;
+  struct dma_buffparms *dmap;
   unsigned long   flags;
 
-  /*
-   * This function is not as portable as it should be.
-   */
+  if (dma_mode == DMA_MODE_WRITE)
+    {
+      chan = audio_devs[dev]->dmachan1;
+      dmap = audio_devs[dev]->dmap_out;
+    }
+  else
+    {
+      chan = audio_devs[dev]->dmachan2;
+      dmap = audio_devs[dev]->dmap_in;
+    }
 
   /*
    * The count must be one less than the actual size. This is handled by
@@ -703,89 +1151,27 @@
 				 * Auto restart mode. Transfer the whole *
 				 * buffer
 				 */
-#ifdef linux
-      DISABLE_INTR (flags);
+      save_flags (flags);
+      cli ();
       disable_dma (chan);
       clear_dma_ff (chan);
       set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
-      set_dma_addr (chan, dmap->raw_buf_phys[0]);
+      set_dma_addr (chan, dmap->raw_buf_phys);
       set_dma_count (chan, dmap->bytes_in_use);
       enable_dma (chan);
-      RESTORE_INTR (flags);
-#else
-
-#ifdef __386BSD__
-      printk ("sound: Invalid DMA mode for device %d\n", dev);
-
-      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
-		    dmap->raw_buf_phys[0],
-		    dmap->bytes_in_use,
-		    chan);
-#else
-#if defined(GENERIC_SYSV)
-#ifndef DMAMODE_AUTO
-      printk ("sound: Invalid DMA mode for device %d\n", dev);
-#endif
-#if defined(SVR42)
-
-      /*
-         ** send full count to snd_dma_prog, it will take care of subtracting
-         ** one if it is required.
-       */
-      snd_dma_prog (chan, dmap->raw_buf_phys[0], dmap->bytes_in_use,
-		    dma_mode, TRUE);
-
-#else /* !SVR42 */
-      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
-#ifdef DMAMODE_AUTO
-		 | DMAMODE_AUTO
-#endif
-		 ,
-		 dmap->raw_buf_phys[0], dmap->bytes_in_use - 1);
-      dma_enable (chan);
-#endif /*  ! SVR42 */
-#else
-#error This routine is not valid for this OS.
-#endif
-#endif
-
-#endif
+      restore_flags (flags);
     }
   else
     {
-#ifdef linux
-      DISABLE_INTR (flags);
+      save_flags (flags);
+      cli ();
       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
-#ifdef __386BSD__
-      isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
-		    physaddr,
-		    count,
-		    chan);
-#else
-
-#if defined(GENERIC_SYSV)
-#if defined(SVR42)
-
-      snd_dma_prog (chan, physaddr, count, dma_mode, FALSE);
-
-#else /* ! SVR42 */
-      dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
-		 physaddr, count);
-      dma_enable (chan);
-#endif /* SVR42 */
-#else
-#error This routine is not valid for this OS.
-#endif /* GENERIC_SYSV */
-#endif
-
-#endif
+      restore_flags (flags);
     }
 
   return count;
@@ -796,16 +1182,22 @@
 {
   int             dev;
 
-#if defined(SVR42)
-  snd_dma_init ();
-#endif /* SVR42 */
 
   /*
      * NOTE! This routine could be called several times.
    */
 
   for (dev = 0; dev < num_audiodevs; dev++)
-    audio_devs[dev]->dmap = &dmaps[dev];
+    if (audio_devs[dev]->dmap_out == NULL)
+      {
+	audio_devs[dev]->dmap_out =
+	  audio_devs[dev]->dmap_in =
+	  &dmaps[ndmaps++];
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  audio_devs[dev]->dmap_in =
+	    &dmaps[ndmaps++];
+      }
   return mem_start;
 }
 
@@ -823,13 +1215,39 @@
    */
 
   unsigned long   flags;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-#if defined(SVR42)
-  snd_dma_intr (audio_devs[dev]->dmachan);
-#endif /* SVR42 */
+  dmap->byte_counter += dmap->counts[dmap->qhead];
+
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+    {
+      /* mmapped access */
 
-  if (event_type != 2)
+      int             p = dmap->fragment_size * dmap->qhead;
+
+      memset (dmap->raw_buf + p,
+	      dmap->neutral_byte,
+	      dmap->fragment_size);
+
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+      dmap->qlen++;		/* Yes increment it (don't decrement) */
+      dmap->flags &= ~DMA_ACTIVE;
+      dmap->counts[dmap->qhead] = dmap->fragment_size;
+
+      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	{
+	  audio_devs[dev]->output_block (dev, dmap->raw_buf_phys +
+					 dmap->qhead * dmap->fragment_size,
+					 dmap->counts[dmap->qhead], 1,
+				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+	  if (audio_devs[dev]->trigger)
+	    audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+	}
+      dmap->flags |= DMA_ACTIVE;
+    }
+  else if (event_type != 2)
     {
       if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
 	{
@@ -844,15 +1262,27 @@
 
       if (dmap->qlen)
 	{
-	  audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
-					 dmap->counts[dmap->qhead], 1,
+	  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	    {
+	      audio_devs[dev]->output_block (dev, dmap->raw_buf_phys +
+					  dmap->qhead * dmap->fragment_size,
+					     dmap->counts[dmap->qhead], 1,
 				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+	      if (audio_devs[dev]->trigger)
+		audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+	    }
 	  dmap->flags |= DMA_ACTIVE;
 	}
       else if (event_type == 1)
 	{
 	  dmap->underrun_count++;
-	  audio_devs[dev]->halt_xfer (dev);
+	  if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
+	      audio_devs[dev]->halt_output)
+	    audio_devs[dev]->halt_output (dev);
+	  else
+	    audio_devs[dev]->halt_xfer (dev);
+
 	  if ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
 	      audio_devs[dev]->flags & NEEDS_RESTART)
 	    dmap->flags |= DMA_RESTART;
@@ -861,29 +1291,55 @@
 	}
     }				/* event_type != 2 */
 
-  DISABLE_INTR (flags);
-  if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+  save_flags (flags);
+  cli ();
+  if ((out_sleep_flag[dev].mode & WK_SLEEP))
     {
-      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+      {
+	out_sleep_flag[dev].mode = WK_WAKEUP;
+	wake_up (&out_sleeper[dev]);
+      };
     }
-  RESTORE_INTR (flags);
+  restore_flags (flags);
 }
 
 void
 DMAbuf_inputintr (int dev)
 {
   unsigned long   flags;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
-#if defined(SVR42)
-  snd_dma_intr (audio_devs[dev]->dmachan);
-#endif /* SVR42 */
+  dmap->byte_counter += dmap->fragment_size;
 
-  if (dmap->qlen == (dmap->nbufs - 1))
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+    {
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      dmap->qlen++;
+
+      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	{
+	  audio_devs[dev]->start_input (dev, dmap->raw_buf_phys +
+					dmap->qtail * dmap->fragment_size,
+					dmap->fragment_size, 1,
+				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+	  if (audio_devs[dev]->trigger)
+	    audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+	}
+
+      dmap->flags |= DMA_ACTIVE;
+    }
+  else if (dmap->qlen == (dmap->nbufs - 1))
     {
       printk ("Sound: Recording overrun\n");
       dmap->underrun_count++;
-      audio_devs[dev]->halt_xfer (dev);
+      if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
+	  audio_devs[dev]->halt_input)
+	audio_devs[dev]->halt_input (dev);
+      else
+	audio_devs[dev]->halt_xfer (dev);
+
       dmap->flags &= ~DMA_ACTIVE;
       if (audio_devs[dev]->flags & DMA_AUTOMODE)
 	dmap->flags |= DMA_RESTART;
@@ -898,38 +1354,74 @@
 		dev, dmap->qlen, dmap->nbufs);
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
 
-      audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
-				    dmap->fragment_size, 1,
+      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	{
+	  audio_devs[dev]->start_input (dev, dmap->raw_buf_phys +
+					dmap->qtail * dmap->fragment_size,
+					dmap->fragment_size, 1,
 				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+	  if (audio_devs[dev]->trigger)
+	    audio_devs[dev]->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+	}
+
       dmap->flags |= DMA_ACTIVE;
     }
 
-  DISABLE_INTR (flags);
-  if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+  save_flags (flags);
+  cli ();
+  if ((in_sleep_flag[dev].mode & WK_SLEEP))
     {
-      WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+      {
+	in_sleep_flag[dev].mode = WK_WAKEUP;
+	wake_up (&in_sleeper[dev]);
+      };
     }
-  RESTORE_INTR (flags);
+  restore_flags (flags);
 }
 
 int
 DMAbuf_open_dma (int dev)
 {
+  int             chan = audio_devs[dev]->dmachan1;
+  int             err;
   unsigned long   flags;
-  int             chan = audio_devs[dev]->dmachan;
 
-  if (ALLOC_DMA_CHN (chan, audio_devs[dev]->name))
+  if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1)) < 0)
     {
-      printk ("Unable to grab DMA%d for the audio driver\n", chan);
-      return RET_ERROR (EBUSY);
+      return -EBUSY;
     }
+  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
+  audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->buffsize;
+  /* reorganize_buffers (dev, audio_devs[dev]->dmap_out); */
 
-  DISABLE_INTR (flags);
-#ifdef linux
+  if (audio_devs[dev]->flags & DMA_DUPLEX)
+    {
+      if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2)) < 0)
+	{
+	  printk ("Unable to grab DMA%d for the audio driver\n",
+		  audio_devs[dev]->dmachan2);
+	  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+	  return -EBUSY;
+	}
+      dma_init_buffers (dev, audio_devs[dev]->dmap_in);
+      audio_devs[dev]->dmap_in->flags |= DMA_ALLOC_DONE;
+      audio_devs[dev]->dmap_in->fragment_size = audio_devs[dev]->buffsize;
+      /* reorganize_buffers (dev, audio_devs[dev]->dmap_in); */
+    }
+  else
+    {
+      audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
+      audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1;
+    }
+
+
+  save_flags (flags);
+  cli ();
   disable_dma (chan);
   clear_dma_ff (chan);
-#endif
-  RESTORE_INTR (flags);
+  restore_flags (flags);
 
   return 0;
 }
@@ -937,48 +1429,60 @@
 void
 DMAbuf_close_dma (int dev)
 {
-  int             chan = audio_devs[dev]->dmachan;
-
   DMAbuf_reset_dma (dev);
-  RELEASE_DMA_CHN (chan);
+  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+
+  if (audio_devs[dev]->flags & DMA_DUPLEX)
+    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+
 }
 
 void
 DMAbuf_reset_dma (int dev)
 {
-#if 0
-  int             chan = audio_devs[dev]->dmachan;
-
-  disable_dma (chan);
-#endif
 }
 
-#ifdef ALLOW_SELECT
 int
 DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
 {
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+  struct dma_buffparms *dmap;
   unsigned long   flags;
 
   switch (sel_type)
     {
     case SEL_IN:
+      dmap = audio_devs[dev]->dmap_in;
 
       if (dmap->dma_mode != DMODE_INPUT)
-	return 0;
+	{
+	  if ((audio_devs[dev]->flags & DMA_DUPLEX) && !dmap->qlen &&
+	      audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
+	      audio_devs[dev]->go)
+	    {
+	      unsigned long   flags;
+
+	      save_flags (flags);
+	      cli ();
+	      activate_recording (dev, dmap);
+	      restore_flags (flags);
+	    }
+	  return 0;
+	}
 
       if (!dmap->qlen)
 	{
-	  DISABLE_INTR (flags);
-	  dev_sleep_flag[dev].mode = WK_SLEEP;
-	  select_wait (&dev_sleeper[dev], wait);
-	  RESTORE_INTR (flags);
+	  save_flags (flags);
+	  cli ();
+	  in_sleep_flag[dev].mode = WK_SLEEP;
+	  select_wait (&in_sleeper[dev], wait);
+	  restore_flags (flags);
 	  return 0;
 	}
       return 1;
       break;
 
     case SEL_OUT:
+      dmap = audio_devs[dev]->dmap_out;
       if (dmap->dma_mode == DMODE_INPUT)
 	{
 	  return 0;
@@ -991,10 +1495,11 @@
 
       if (!space_in_queue (dev))
 	{
-	  DISABLE_INTR (flags);
-	  dev_sleep_flag[dev].mode = WK_SLEEP;
-	  select_wait (&dev_sleeper[dev], wait);
-	  RESTORE_INTR (flags);
+	  save_flags (flags);
+	  cli ();
+	  out_sleep_flag[dev].mode = WK_SLEEP;
+	  select_wait (&out_sleeper[dev], wait);
+	  restore_flags (flags);
 	  return 0;
 	}
       return 1;
@@ -1007,7 +1512,6 @@
   return 0;
 }
 
-#endif /* ALLOW_SELECT */
 
 #else /* EXCLUDE_AUDIO */
 /*
@@ -1017,7 +1521,7 @@
 int
 DMAbuf_open (int dev, int mode)
 {
-  return RET_ERROR (ENXIO);
+  return -ENXIO;
 }
 
 int
@@ -1029,31 +1533,31 @@
 int
 DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 int
 DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 int
 DMAbuf_rmchars (int dev, int buff_no, int c)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 int
 DMAbuf_start_output (int dev, int buff_no, int l)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 int
-DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+DMAbuf_ioctl (int dev, unsigned int cmd, ioctl_arg arg, int local)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 long
@@ -1065,13 +1569,13 @@
 int
 DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
 {
-  return RET_ERROR (EIO);
+  return -EIO;
 }
 
 int
 DMAbuf_open_dma (int dev)
 {
-  return RET_ERROR (ENXIO);
+  return -ENXIO;
 }
 
 void
@@ -1097,7 +1601,6 @@
 {
   return;
 }
-
 #endif
 
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this