summaryrefslogtreecommitdiffstats
path: root/drivers/sound/audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sound/audio.c')
-rw-r--r--drivers/sound/audio.c661
1 files changed, 559 insertions, 102 deletions
diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c
index 4ce28d0d1..0ce7c0a9d 100644
--- a/drivers/sound/audio.c
+++ b/drivers/sound/audio.c
@@ -5,7 +5,7 @@
*/
/*
- * 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
@@ -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 @@ audio_open (int dev, struct fileinfo *file)
local_conversion[dev] = 0;
- if (audio_devs[dev]->d->set_bits (dev, bits) != bits)
- {
- }
if (dev_type == SND_DEV_AUDIO)
{
@@ -113,23 +113,33 @@ audio_open (int dev, struct fileinfo *file)
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 @@ sync_output (int dev)
*/
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 @@ translate_bytes (const void *table, void *buff, int n)
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 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
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 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
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 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
* 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 @@ audio_read (int dev, struct fileinfo *file, char *buf, int count)
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 @@ audio_read (int dev, struct fileinfo *file, char *buf, int count)
}
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 @@ audio_ioctl (int dev, struct fileinfo *file,
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 @@ audio_ioctl (int dev, struct fileinfo *file,
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 @@ audio_ioctl (int dev, struct fileinfo *file,
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_ioctl (int dev, struct fileinfo *file,
{
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_ioctl (int dev, struct fileinfo *file,
{
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 @@ audio_ioctl (int dev, struct fileinfo *file,
{
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 @@ audio_ioctl (int dev, struct fileinfo *file,
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);
}
}
@@ -543,16 +546,15 @@ audio_init_devices (void)
}
int
-audio_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+audio_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
{
- char *dma_buf;
- int buf_no, buf_ptr, buf_size;
-
dev = dev >> 4;
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 */
@@ -562,16 +564,13 @@ audio_select (int dev, struct fileinfo *file, int sel_type, select_table * 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 */
}
- if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
- {
- return 1; /* There is space in the current buffer */
- }
-
return DMAbuf_select (dev, file, sel_type, wait);
break;
@@ -584,3 +583,461 @@ audio_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
#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;
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return -EINVAL;
+
+ 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);
+ }
+
+}