patch-2.1.28 linux/drivers/sound/audio.c

Next file: linux/drivers/sound/configure.c
Previous file: linux/drivers/sound/aedsp16.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.27/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
@@ -5,14 +5,14 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
 #include <linux/config.h>
-#include <linux/poll.h>
+
 
 #include "sound_config.h"
 
@@ -24,6 +24,9 @@
 #define ON		1
 #define OFF		0
 
+#define NEUTRAL8	0x80
+#define NEUTRAL16	0x00
+
 static int      audio_mode[MAX_AUDIO_DEV];
 static int      dev_nblock[MAX_AUDIO_DEV];	/* 1 if in nonblocking mode */
 
@@ -95,9 +98,6 @@
 
   local_conversion[dev] = 0;
 
-  if (audio_devs[dev]->d->set_bits (dev, bits) != bits)
-    {
-    }
 
   if (dev_type == SND_DEV_AUDIO)
     {
@@ -113,23 +113,33 @@
   return ret;
 }
 
-void
+static void
 sync_output (int dev)
 {
-  int             buf_no, buf_ptr, buf_size, p, i;
-  char           *dma_buf;
+  int             p, i;
+  int             l;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
+  if (dmap->fragment_size <= 0)
+    return;
+  dmap->flags |= DMA_POST;
+
+  /* Align the write pointer with fragment boundaries */
+  if ((l = dmap->user_counter % dmap->fragment_size) > 0)
     {
-      int             i, n = buf_size & 3;
+      char           *ptr;
+      int             err, dummylen, len = dmap->fragment_size - l;
 
-      if (n)			/* Not 4 byte aligned */
-	{
-	  for (i = 0; i < n; i++)
-	    dma_buf[buf_ptr++] = dmap->neutral_byte;
-	}
-      DMAbuf_start_output (dev, buf_no, buf_ptr);
+      if ((err = DMAbuf_getwrbuffer (dev, &ptr, &dummylen, 1)) >= 0)
+	if (dummylen >= len && ((long) ptr % dmap->fragment_size) == l)
+	  {
+	    if ((ptr + len) > (dmap->raw_buf + audio_devs[dev]->buffsize))
+	      printk ("audio: Buffer error 1\n");
+	    if (ptr < dmap->raw_buf)
+	      printk ("audio: Buffer error 11\n");
+	    memset (ptr, dmap->neutral_byte, len);
+	    DMAbuf_move_wrpointer (dev, len);
+	  }
     }
 
 /*
@@ -137,16 +147,21 @@
  */
 
   p = dmap->qtail;
+  dmap->flags |= DMA_POST;
 
   for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
     {
       p = (p + 1) % dmap->nbufs;
+      if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+	  (dmap->raw_buf + audio_devs[dev]->buffsize))
+	printk ("audio: Buffer error 2\n");
+
       memset (dmap->raw_buf + p * dmap->fragment_size,
 	      dmap->neutral_byte,
 	      dmap->fragment_size);
     }
 
-  dmap->flags |= DMA_CLEAN;
+  dmap->flags |= DMA_DIRTY;
 }
 
 void
@@ -201,7 +216,7 @@
 int
 audio_write (int dev, struct fileinfo *file, const char *buf, int count)
 {
-  int             c, p, l, buf_no, buf_ptr, buf_size;
+  int             c, p, l, buf_size;
   int             err;
   char           *dma_buf;
 
@@ -210,9 +225,8 @@
   p = 0;
   c = count;
 
-  if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    {				/* Direction change */
-    }
+  if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+    return -EPERM;
 
   if (audio_devs[dev]->flags & DMA_DUPLEX)
     audio_mode[dev] |= AM_WRITE;
@@ -227,34 +241,33 @@
 
   while (c)
     {
-      if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) < 0)
+      if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev]) < 0))
 	{
-	  if ((buf_no = DMAbuf_getwrbuffer (dev, &dma_buf,
-					    &buf_size,
-					    dev_nblock[dev])) < 0)
-	    {
-	      /* Handle nonblocking mode */
-	      if (dev_nblock[dev] && buf_no == -EAGAIN)
-		return p;	/* No more space. Return # of accepted bytes */
-	      return buf_no;
-	    }
-	  buf_ptr = 0;
+	  /* Handle nonblocking mode */
+	  if (dev_nblock[dev] && err == -EAGAIN)
+	    return p;		/* No more space. Return # of accepted bytes */
+	  return err;
 	}
 
       l = c;
-      if (l > (buf_size - buf_ptr))
-	l = (buf_size - buf_ptr);
-
+      if (l > buf_size)
+	l = buf_size;
 
       if (!audio_devs[dev]->d->copy_user)
-	{			/*
-				 * No device specific copy routine
-				 */
-	  copy_from_user (&dma_buf[buf_ptr], &(buf)[p], l);
+	{
+	  if ((dma_buf + l) >
+	   (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->buffsize))
+	    printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n",
+		    (long) dma_buf, l,
+		    (long) audio_devs[dev]->dmap_out->raw_buf,
+		    (int) audio_devs[dev]->buffsize);
+	  if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+	    printk ("audio: Buffer error 13\n");
+	  copy_from_user (dma_buf, &(buf)[p], l);
 	}
       else
 	audio_devs[dev]->d->copy_user (dev,
-				       dma_buf, buf_ptr, buf, p, l);
+				       dma_buf, 0, buf, p, l);
 
       if (local_conversion[dev] & CNV_MU_LAW)
 	{
@@ -262,23 +275,12 @@
 	   * This just allows interrupts while the conversion is running
 	   */
 	  sti ();
-	  translate_bytes (ulaw_dsp, (unsigned char *) &dma_buf[buf_ptr], l);
+	  translate_bytes (ulaw_dsp, (unsigned char *) dma_buf, l);
 	}
 
       c -= l;
       p += l;
-      buf_ptr += l;
-
-      if (buf_ptr >= buf_size)
-	{
-	  if ((err = DMAbuf_start_output (dev, buf_no, buf_ptr)) < 0)
-	    {
-	      return err;
-	    }
-
-	}
-      else
-	DMAbuf_set_count (dev, buf_no, buf_ptr);
+      DMAbuf_move_wrpointer (dev, l);
 
     }
 
@@ -296,6 +298,9 @@
   p = 0;
   c = count;
 
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return -EPERM;
+
   if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
     {
       sync_output (dev);
@@ -352,12 +357,12 @@
 }
 
 int
-audio_ioctl (int dev, struct fileinfo *file,
+audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
 	     unsigned int cmd, caddr_t arg)
 {
   int             val;
 
-/* printk("audio_ioctl(%x, %x)\n", cmd, arg); */
+  /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
 
   dev = dev >> 4;
 
@@ -377,6 +382,8 @@
 	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 	  return 0;
 
+	if (audio_devs[dev]->dmap_out->fragment_size == 0)
+	  return 0;
 	sync_output (dev);
 	DMAbuf_sync (dev);
 	DMAbuf_reset (dev);
@@ -386,9 +393,11 @@
       case SNDCTL_DSP_POST:
 	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 	  return 0;
-	audio_devs[dev]->dmap_out->flags |= DMA_POST;
+	if (audio_devs[dev]->dmap_out->fragment_size == 0)
+	  return 0;
+	audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
 	sync_output (dev);
-	DMAbuf_ioctl (dev, SNDCTL_DSP_POST, 0, 1);
+	dma_ioctl (dev, SNDCTL_DSP_POST, (caddr_t) 0);
 	return 0;
 	break;
 
@@ -399,12 +408,12 @@
 	break;
 
       case SNDCTL_DSP_GETFMTS:
-	return ioctl_out (arg, audio_devs[dev]->format_mask);
+	return (*(int *) arg = audio_devs[dev]->format_mask);
 	break;
 
       case SNDCTL_DSP_SETFMT:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, set_format (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = set_format (dev, val));
 
       case SNDCTL_DSP_GETISPACE:
 	if (!(audio_devs[dev]->open_mode & OPEN_READ))
@@ -415,16 +424,12 @@
 	{
 	  audio_buf_info  info;
 
-	  int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+	  int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
 	  if (err < 0)
 	    return err;
 
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -436,22 +441,13 @@
 
 	{
 	  audio_buf_info  info;
-	  char           *dma_buf;
-	  int             buf_no, buf_ptr, buf_size;
 
-	  int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+	  int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
 	  if (err < 0)
 	    return err;
 
-	  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-	    info.bytes -= buf_ptr;
-
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -464,7 +460,8 @@
 	{
 	  int             info = 1;	/* Revision level of this ioctl() */
 
-	  if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	      audio_devs[dev]->open_mode == OPEN_READWRITE)
 	    info |= DSP_CAP_DUPLEX;
 
 	  if (audio_devs[dev]->coproc)
@@ -478,59 +475,65 @@
 
 	  info |= DSP_CAP_MMAP;
 
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 	break;
 
       case SOUND_PCM_WRITE_RATE:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, val));
 
       case SOUND_PCM_READ_RATE:
-	return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, 0));
 
       case SNDCTL_DSP_STEREO:
 	{
 	  int             n;
 
-	  get_user (n, (int *) arg);
+	  n = *(int *) arg;
 	  if (n > 1)
 	    {
 	      printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n",
 		      n);
-	      return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n));
+	      return -EINVAL;
 	    }
 
 	  if (n < 0)
 	    return -EINVAL;
 
-	  return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
+	  return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
 	}
 
       case SOUND_PCM_WRITE_CHANNELS:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, val));
 
       case SOUND_PCM_READ_CHANNELS:
-	return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, 0));
 
       case SOUND_PCM_READ_BITS:
-	return ioctl_out (arg, audio_devs[dev]->d->set_bits (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_bits (dev, 0));
 
       case SNDCTL_DSP_SETDUPLEX:
+	if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+	  return -EPERM;
 	if (audio_devs[dev]->flags & DMA_DUPLEX)
 	  return 0;
 	else
 	  return -EIO;
 	break;
 
+      case SNDCTL_DSP_PROFILE:
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	  audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
+	if (audio_devs[dev]->open_mode & OPEN_READ)
+	  audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
+	return 0;
+	break;
+
       default:
-	return DMAbuf_ioctl (dev, cmd, arg, 0);
+	return dma_ioctl (dev, cmd, arg);
       }
 }
 
@@ -542,32 +545,499 @@
    */
 }
 
-unsigned int
-audio_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+audio_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  char           *dma_buf;
-  unsigned int	  mask = 0;
-  int             buf_no, buf_ptr, buf_size;
-
   dev = dev >> 4;
 
-  mask = DMAbuf_poll (dev, file, wait);
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	return 0;
+      if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
+	{
+	  return 0;		/* Not recording */
+	}
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_OUT:
+      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	return 0;
+      if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX))
+	{
+	  return 0;		/* Wrong direction */
+	}
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
+}
+
+
+#endif
+
+void
+reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
+{
+  /*
+   * This routine breaks the physical device buffers to logical ones.
+   */
+
+  struct audio_operations *dsp_dev = audio_devs[dev];
+
+  unsigned        i, n;
+  unsigned        sr, nc, sz, bsz;
+
+  if (!dmap->needs_reorg)
+    return;
+
+  sr = dsp_dev->d->set_speed (dev, 0);
+  nc = dsp_dev->d->set_channels (dev, 0);
+  sz = dsp_dev->d->set_bits (dev, 0);
+  dmap->needs_reorg = 0;
+
+  if (sz == 8)
+    dmap->neutral_byte = NEUTRAL8;
+  else
+    dmap->neutral_byte = NEUTRAL16;
+
+  if (sr < 1 || nc < 1 || sz < 1)
+    {
+      printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
+	      dev, sr, nc, sz);
+      sr = DSP_DEFAULT_SPEED;
+      nc = 1;
+      sz = 8;
+    }
+
+  sz = sr * nc * sz;
+
+  sz /= 8;			/* #bits -> #bytes */
+  dmap->data_rate = sz;
+
+  if (dmap->fragment_size == 0)
+    {				/* Compute the fragment size using the default algorithm */
+
+      /*
+         * Compute a buffer size for time not exceeding 1 second.
+         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+         * of sound (using the current speed, sample size and #channels).
+       */
+
+      bsz = dsp_dev->buffsize;
+      while (bsz > sz)
+	bsz /= 2;
+
+      if (bsz == dsp_dev->buffsize)
+	bsz /= 2;		/* Needs at least 2 buffers */
+
+/*
+ *    Split the computed fragment to smaller parts. After 3.5a9
+ *      the default subdivision is 4 which should give better
+ *      results when recording.
+ */
+
+      if (dmap->subdivision == 0)	/* Not already set */
+	{
+	  dmap->subdivision = 4;	/* Init to the default value */
+
+	  if ((bsz / dmap->subdivision) > 4096)
+	    dmap->subdivision *= 2;
+	  if ((bsz / dmap->subdivision) < 4096)
+	    dmap->subdivision = 1;
+	}
+
+      bsz /= dmap->subdivision;
+
+      if (bsz < 16)
+	bsz = 16;		/* Just a sanity check */
+
+      dmap->fragment_size = bsz;
+    }
+  else
+    {
+      /*
+         * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+         * the buffer size computation has already been done.
+       */
+      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
+	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
+      bsz = dmap->fragment_size;
+    }
+
+  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */
+#ifdef OS_DMA_ALIGN_CHECK
+  OS_DMA_ALIGN_CHECK (bsz);
+#endif
+
+  n = dsp_dev->buffsize / bsz;
+  if (n > MAX_SUB_BUFFERS)
+    n = MAX_SUB_BUFFERS;
+  if (n > dmap->max_fragments)
+    n = dmap->max_fragments;
+
+  if (n < 2)
+    {
+      n = 2;
+      bsz /= 2;
+    }
+
+  dmap->nbufs = n;
+  dmap->bytes_in_use = n * bsz;
+  dmap->fragment_size = bsz;
+
+  if (dmap->raw_buf)
+    {
+      memset (dmap->raw_buf,
+	      dmap->neutral_byte,
+	      dmap->bytes_in_use);
+    }
+
+  for (i = 0; i < dmap->nbufs; i++)
+    {
+      dmap->counts[i] = 0;
+    }
+
+  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+}
+
+static int
+dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  if (fact == 0)
+    {
+      fact = dmap->subdivision;
+      if (fact == 0)
+	fact = 1;
+      return (*(int *) arg = fact);
+    }
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Too late to change */
+    return -EINVAL;
 
-/* sel_in */
-  if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    mask &= ~(POLLIN | POLLRDNORM); /* Wrong direction */
-
-/* sel_out */
-  if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX)) {
-    mask &= ~(POLLOUT | POLLWRNORM); /* Wrong direction */
-    goto sel_ex;
-  }
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-    mask |= POLLOUT | POLLWRNORM;
+  if (fact > MAX_REALTIME_FACTOR)
+    return -EINVAL;
 
- sel_ex:
-  return mask;
+  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+    return -EINVAL;
+
+  dmap->subdivision = fact;
+  return (*(int *) arg = fact);
 }
 
+static int
+dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  int             bytes, count;
+
+  if (fact == 0)
+    return -EIO;
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Too late to change */
+    return -EINVAL;
+
+  bytes = fact & 0xffff;
+  count = (fact >> 16) & 0x7fff;
+
+  if (count == 0)
+    count = MAX_SUB_BUFFERS;
 
+  if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
+    return -EINVAL;
+
+  if (count < 2)
+    return -EINVAL;
+
+  if (audio_devs[dev]->min_fragment > 0)
+    if (bytes < audio_devs[dev]->min_fragment)
+      bytes = audio_devs[dev]->min_fragment;
+
+#ifdef OS_DMA_MINBITS
+  if (bytes < OS_DMA_MINBITS)
+    bytes = OS_DMA_MINBITS;
 #endif
+
+  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 */
+  if (arg)
+    return (*(int *) arg = bytes | (count << 16));
+  else
+    return 0;
+}
+
+static int
+dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+
+  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
+
+  switch (cmd)
+    {
+
+    case SNDCTL_DSP_SUBDIVIDE:
+      {
+	int             fact;
+	int             ret;
+
+	fact = *(int *) arg;
+
+	ret = dma_subdivide (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	    audio_devs[dev]->open_mode & OPEN_READ)
+	  ret = dma_subdivide (dev, dmap_in, arg, fact);
+
+	return ret;
+      }
+      break;
+
+    case SNDCTL_DSP_GETISPACE:
+    case SNDCTL_DSP_GETOSPACE:
+      {
+	struct dma_buffparms *dmap = dmap_out;
+
+	audio_buf_info *info = (audio_buf_info *) arg;
+
+	if (cmd == SNDCTL_DSP_GETISPACE &&
+	    !(audio_devs[dev]->open_mode & OPEN_READ))
+	  return -EINVAL;
+
+	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, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+
+	info->fragstotal = dmap->nbufs;
+
+	if (cmd == SNDCTL_DSP_GETISPACE)
+	  info->fragments = dmap->qlen;
+	else
+	  {
+	    if (!DMAbuf_space_in_queue (dev))
+	      info->fragments = 0;
+	    else
+	      {
+		info->fragments = dmap->nbufs - dmap->qlen;
+		if (audio_devs[dev]->d->local_qlen)
+		  {
+		    int             tmp = audio_devs[dev]->d->local_qlen (dev);
+
+		    if (tmp && info->fragments)
+		      tmp--;	/*
+				   * This buffer has been counted twice
+				 */
+		    info->fragments -= tmp;
+		  }
+	      }
+	  }
+
+	if (info->fragments < 0)
+	  info->fragments = 0;
+	else if (info->fragments > dmap->nbufs)
+	  info->fragments = dmap->nbufs;
+
+	info->fragsize = dmap->fragment_size;
+	info->bytes = info->fragments * dmap->fragment_size;
+
+	if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+	  info->bytes -= dmap->counts[dmap->qhead];
+      }
+      return 0;
+
+    case SNDCTL_DSP_SETTRIGGER:
+      {
+	unsigned long   flags;
+
+	int             bits;
+	int             changed;
+
+	bits = *(int *) arg;
+	bits &= audio_devs[dev]->open_mode;
+
+	if (audio_devs[dev]->d->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)
+	  {
+	    int             err;
+
+	    reorganize_buffers (dev, dmap_in, 1);
+
+	    if ((err = audio_devs[dev]->d->prepare_for_input (dev,
+			       dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+	      return -err;
+
+	    audio_devs[dev]->enable_bits = bits;
+	    DMAbuf_activate_recording (dev, dmap_in);
+	  }
+
+
+	if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+	 (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+	    audio_devs[dev]->go)
+	  {
+
+	    if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	      {
+		reorganize_buffers (dev, dmap_out, 0);
+	      }
+
+	    ;
+	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+	    DMAbuf_launch_output (dev, dmap_out);
+	    ;
+	  }
+
+	audio_devs[dev]->enable_bits = bits;
+	if (changed && audio_devs[dev]->d->trigger)
+	  {
+	    audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
+	  }
+	restore_flags (flags);
+      }
+    case SNDCTL_DSP_GETTRIGGER:
+      return (*(int *) arg = audio_devs[dev]->enable_bits);
+      break;
+
+    case SNDCTL_DSP_SETSYNCRO:
+
+      if (!audio_devs[dev]->d->trigger)
+	return -EINVAL;
+
+      audio_devs[dev]->d->trigger (dev, 0);
+      audio_devs[dev]->go = 0;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETIPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	  return -EINVAL;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_in->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_in) & ~3;
+	info.blocks = audio_devs[dev]->dmap_in->qlen;
+	info.bytes += info.ptr;
+	memcpy ((&((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;	/* Reset interrupt counter */
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_DSP_GETOPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	  return -EINVAL;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_out->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_out) & ~3;
+	info.blocks = audio_devs[dev]->dmap_out->qlen;
+	info.bytes += info.ptr;
+	memcpy ((&((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;	/* Reset interrupt counter */
+
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+
+    case SNDCTL_DSP_POST:
+      ;
+      if (audio_devs[dev]->dmap_out->qlen > 0)
+	if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+	  DMAbuf_launch_output (dev, audio_devs[dev]->dmap_out);
+      ;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETBLKSIZE:
+      if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	{
+	  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	    reorganize_buffers (dev, dmap_out,
+				(audio_devs[dev]->open_mode == OPEN_READ));
+	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	      audio_devs[dev]->open_mode & OPEN_READ)
+	    reorganize_buffers (dev, dmap_in,
+				(audio_devs[dev]->open_mode == OPEN_READ));
+	}
+
+      return (*(int *) arg = dmap_out->fragment_size);
+      break;
+
+    case SNDCTL_DSP_SETFRAGMENT:
+      {
+	int             fact;
+	int             ret;
+
+	fact = *(int *) arg;
+	ret = dma_set_fragment (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	    audio_devs[dev]->open_mode & OPEN_READ)
+	  ret = dma_set_fragment (dev, dmap_in, arg, fact);
+
+	return ret;
+      }
+      break;
+
+    default:
+      return audio_devs[dev]->d->ioctl (dev, cmd, arg);
+    }
+
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov