patch-2.1.28 linux/drivers/sound/dmabuf.c
Next file: linux/drivers/sound/finetune.h
Previous file: linux/drivers/sound/dev_table.h
Back to the patch index
Back to the overall index
- Lines: 2197
- Date:
Wed Feb 26 02:35:10 1997
- Orig file:
v2.1.27/linux/drivers/sound/dmabuf.c
- Orig date:
Tue Jan 28 00:02:45 1997
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
@@ -4,14 +4,15 @@
* The DMA buffer manager for digitized voice applications
*/
/*
- * 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>
+
+#undef BE_CONSERVATIVE
#include "sound_config.h"
@@ -28,9 +29,6 @@
{
{0}};
-#define NEUTRAL8 0x80
-#define NEUTRAL16 0x00
-
static int ndmaps = 0;
#define MAX_DMAP (MAX_AUDIO_DEV*2)
@@ -39,130 +37,9 @@
{
{0}};
-static int space_in_queue (int dev);
-
static void dma_reset_output (int dev);
static void dma_reset_input (int dev);
-static int dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact);
-
-static 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;
-
- sr = dsp_dev->d->set_speed (dev, 0);
- nc = dsp_dev->d->set_channels (dev, 0);
- sz = dsp_dev->d->set_bits (dev, 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 local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
static void
dma_init_buffers (int dev, struct dma_buffparms *dmap)
@@ -176,17 +53,18 @@
in_sleep_flag[dev].opts = WK_NONE;
}
- dmap->flags = DMA_BUSY; /* Other flags off */
- dmap->qlen = dmap->qhead = dmap->qtail = 0;
- dmap->nbufs = 1;
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+ dmap->byte_counter = 0;
dmap->bytes_in_use = audio_devs[dev]->buffsize;
dmap->dma_mode = DMODE_NONE;
dmap->mapping_flags = 0;
- dmap->neutral_byte = NEUTRAL8;
+ dmap->neutral_byte = 0x80;
dmap->data_rate = 8000;
dmap->cfrag = -1;
dmap->closing = 0;
+ dmap->nbufs = 1;
+ dmap->flags = DMA_BUSY; /* Other flags off */
}
static int
@@ -219,6 +97,8 @@
dmap->fragment_size = 0;
dmap->max_fragments = 65536; /* Just a large value */
dmap->byte_counter = 0;
+ dmap->applic_profile = APF_NORMAL;
+ dmap->needs_reorg = 1;
dma_init_buffers (dev, dmap);
@@ -235,20 +115,20 @@
dmap->dma_mode = DMODE_NONE;
dmap->flags &= ~DMA_BUSY;
- disable_dma (chan);
- sound_free_dmap (dev, dmap, chan);
+ disable_dma (dmap->dma);
}
+
static unsigned int
default_set_bits (int dev, unsigned int bits)
{
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) bits, 1);
+ return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
}
static int
default_set_speed (int dev, int speed)
{
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) speed, 1);
+ return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
}
static short
@@ -256,7 +136,7 @@
{
int c = channels;
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) c, 1);
+ return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
}
static void
@@ -279,50 +159,66 @@
if (dev >= num_audiodevs)
{
- /* printk ("PCM device %d not installed.\n", dev); */
return -ENXIO;
}
if (!audio_devs[dev])
{
- /* printk ("PCM device %d not initialized\n", dev); */
return -ENXIO;
}
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;
+ audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
}
+ check_driver (audio_devs[dev]->d);
+
if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0)
return retval;
- check_driver (audio_devs[dev]->d);
-
dmap_out = audio_devs[dev]->dmap_out;
dmap_in = audio_devs[dev]->dmap_in;
- if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0)
+ if (dmap_in == dmap_out)
+ audio_devs[dev]->flags &= ~DMA_DUPLEX;
+
+ if (mode & OPEN_WRITE)
{
- audio_devs[dev]->d->close (dev);
- return retval;
+ if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
+ {
+ audio_devs[dev]->d->close (dev);
+ return retval;
+ }
}
audio_devs[dev]->enable_bits = mode;
- if (mode & OPEN_READ &&
- 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]->d->close (dev);
- close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1);
- return retval;
- }
+
+ if (mode == OPEN_READ || (mode != OPEN_WRITE &&
+ audio_devs[dev]->flags & DMA_DUPLEX))
+ {
+ if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
+ {
+ audio_devs[dev]->d->close (dev);
+
+ if (mode & OPEN_WRITE)
+ {
+ close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma);
+ }
+
+ return retval;
+ }
+ }
+
audio_devs[dev]->open_mode = mode;
audio_devs[dev]->go = 1;
- in_sleep_flag[dev].opts = WK_NONE;
- out_sleep_flag[dev].opts = WK_NONE;
+
+ if (mode & OPEN_READ)
+ in_sleep_flag[dev].opts = WK_NONE;
+
+ if (mode & OPEN_WRITE)
+ out_sleep_flag[dev].opts = WK_NONE;
audio_devs[dev]->d->set_bits (dev, 8);
audio_devs[dev]->d->set_channels (dev, 1);
@@ -334,17 +230,10 @@
void
DMAbuf_reset (int dev)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- audio_devs[dev]->d->reset (dev);
- restore_flags (flags);
-
- dma_reset_output (dev);
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ dma_reset_output (dev);
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ)
+ if (audio_devs[dev]->open_mode & OPEN_READ)
dma_reset_input (dev);
}
@@ -356,8 +245,6 @@
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- if (dmap->dma_mode != DMODE_OUTPUT)
- return;
if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */
return;
@@ -404,7 +291,6 @@
};
}
audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
- audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
/*
* Finally shut the device off
@@ -412,68 +298,74 @@
if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
!audio_devs[dev]->d->halt_output)
- audio_devs[dev]->d->reset (dev);
+ audio_devs[dev]->d->halt_io (dev);
else
audio_devs[dev]->d->halt_output (dev);
audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
restore_flags (flags);
- dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+ clear_dma_ff (dmap->dma);
+ disable_dma (dmap->dma);
+ dmap->byte_counter = 0;
reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
}
static void
dma_reset_input (int dev)
{
unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
save_flags (flags);
cli ();
if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
!audio_devs[dev]->d->halt_input)
- audio_devs[dev]->d->reset (dev);
+ audio_devs[dev]->d->halt_io (dev);
else
audio_devs[dev]->d->halt_input (dev);
audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
restore_flags (flags);
- dma_init_buffers (dev, audio_devs[dev]->dmap_in);
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+ dmap->byte_counter = 0;
reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
}
-static void
-launch_output (int dev, struct dma_buffparms *dmap)
+void
+DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
{
- dmap->flags |= DMA_ACTIVE;
-
- if (dmap->dma_mode == DMODE_NONE || dmap->flags & DMA_RESTART)
+ if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE))
{
- reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
- audio_devs[dev]->d->prepare_for_output (dev,
- dmap->fragment_size, dmap->nbufs);
- }
+ if (!(dmap->flags & DMA_STARTED))
+ {
+ reorganize_buffers (dev, dmap, 0);
- dmap->dma_mode |= DMODE_OUTPUT;
+ if (audio_devs[dev]->d->prepare_for_output (dev,
+ dmap->fragment_size, dmap->nbufs))
+ return;
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
+ local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_WRITE);
+ }
+ if (dmap->counts[dmap->qhead] == 0)
+ dmap->counts[dmap->qhead] = dmap->fragment_size;
- audio_devs[dev]->d->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]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
+ audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
+ dmap->qhead * dmap->fragment_size,
+ dmap->counts[dmap->qhead], 1);
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger (dev,
audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
+ dmap->flags |= DMA_ACTIVE;
}
int
DMAbuf_sync (int dev)
{
unsigned long flags;
- int tmout;
+ int tmout, n = 0;
if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
return 0;
@@ -499,13 +391,14 @@
;
if (dmap->qlen > 0)
if (!(dmap->flags & DMA_ACTIVE))
- launch_output (dev, dmap);
+ DMAbuf_launch_output (dev, dmap);
;
audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
audio_devs[dev]->dmap_out->underrun_count = 0;
while (!(current->signal & ~current->blocked)
+ && n++ <= audio_devs[dev]->dmap_out->nbufs
&& audio_devs[dev]->dmap_out->qlen
&& audio_devs[dev]->dmap_out->underrun_count == 0)
{
@@ -534,7 +427,6 @@
}
}
audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
- audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
restore_flags (flags);
/*
* Some devices such as GUS have huge amount of on board RAM for the
@@ -578,32 +470,39 @@
{
unsigned long flags;
- audio_devs[dev]->dmap_out->closing = 1;
- audio_devs[dev]->dmap_in->closing = 1;
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ audio_devs[dev]->dmap_out->closing = 1;
+ if (audio_devs[dev]->open_mode & OPEN_READ)
+ audio_devs[dev]->dmap_in->closing = 1;
+
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
+ if (!((current->signal & ~current->blocked))
+ && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+ {
+ DMAbuf_sync (dev);
+ }
- if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
- if (!((current->signal & ~current->blocked))
- && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+ if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
{
- DMAbuf_sync (dev);
+ memset (audio_devs[dev]->dmap_out->raw_buf,
+ audio_devs[dev]->dmap_out->neutral_byte,
+ audio_devs[dev]->dmap_out->bytes_in_use);
}
- if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
- memset (audio_devs[dev]->dmap_out->raw_buf,
- audio_devs[dev]->dmap_out->neutral_byte,
- audio_devs[dev]->dmap_out->bytes_in_use);
-
save_flags (flags);
cli ();
- audio_devs[dev]->d->halt_xfer (dev);
+ DMAbuf_reset (dev);
audio_devs[dev]->d->close (dev);
- close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
- if (audio_devs[dev]->open_mode & OPEN_READ &&
- audio_devs[dev]->flags & DMA_DUPLEX)
- close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+ if (audio_devs[dev]->open_mode == OPEN_READ ||
+ (audio_devs[dev]->open_mode != OPEN_WRITE &&
+ audio_devs[dev]->flags & DMA_DUPLEX))
+ close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
audio_devs[dev]->open_mode = 0;
restore_flags (flags);
@@ -611,21 +510,15 @@
return 0;
}
-static int
-activate_recording (int dev, struct dma_buffparms *dmap)
+int
+DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
{
- int prepare = 0;
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return 0;
if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
return 0;
- if (dmap->flags & DMA_RESTART)
- {
- dma_reset_input (dev);
- dmap->flags &= ~DMA_RESTART;
- prepare = 1;
- }
-
if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
{
DMAbuf_sync (dev);
@@ -633,8 +526,7 @@
dmap->dma_mode = DMODE_NONE;
}
-
- if (prepare || !dmap->dma_mode)
+ if (!dmap->dma_mode)
{
int err;
@@ -649,12 +541,14 @@
if (!(dmap->flags & DMA_ACTIVE))
{
+ if (dmap->needs_reorg)
+ reorganize_buffers (dev, dmap, 0);
+ local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
audio_devs[dev]->d->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;
+ dmap->fragment_size, 0);
+ dmap->flags |= DMA_ACTIVE;
if (audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger (dev,
audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -669,6 +563,11 @@
int err = EIO;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return -EIO;
+ if (dmap->needs_reorg)
+ reorganize_buffers (dev, dmap, 0);
+
save_flags (flags);
cli ();
if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
@@ -687,7 +586,7 @@
return -EAGAIN;
}
- if ((err = activate_recording (dev, dmap)) < 0)
+ if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
{
restore_flags (flags);
return err;
@@ -737,7 +636,7 @@
{
printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
err = EIO;
- audio_devs[dev]->d->reset (dev);
+ dma_reset_input (dev);
;
}
else
@@ -781,83 +680,8 @@
return 0;
}
-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 ioctl_out (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 ioctl_out (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 ioctl_out (arg, bytes | (count << 16));
- else
- return 0;
-}
-
-static int
-get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
+int
+DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap)
{
/*
* Try to approximate the active byte position of the DMA pointer within the
@@ -865,6 +689,7 @@
*/
int pos;
unsigned long flags;
+ int chan = dmap->dma;
save_flags (flags);
cli ();
@@ -873,16 +698,26 @@
else
{
clear_dma_ff (chan);
- disable_dma (chan);
+ disable_dma (dmap->dma);
pos = get_dma_residue (chan);
- if (chan > 3) /* Word count */
- pos *= 2;
- pos = dmap->bytes_in_use - pos - 1;
+ pos = dmap->bytes_in_use - pos;
+
+ if (dmap->flags & DMODE_OUTPUT)
+ {
+ if (dmap->qhead == 0)
+ pos %= dmap->bytes_in_use;
+ }
+ else
+ {
+ if (dmap->qtail == 0)
+ pos %= dmap->bytes_in_use;
+ }
+
if (pos < 0)
pos = 0;
if (pos > dmap->bytes_in_use)
pos = dmap->bytes_in_use;
- enable_dma (chan);
+ enable_dma (dmap->dma);
}
restore_flags (flags);
/* printk ("%04x ", pos); */
@@ -890,353 +725,200 @@
return pos;
}
-
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+/*
+ * DMAbuf_start_devices() is called by the /dev/music driver to start
+ * one or more audio devices at desired moment.
+ */
+static void
+DMAbuf_start_device (int dev)
{
- 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_GETBLKSIZE:
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- {
- 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));
- }
+ if (audio_devs[dev]->open_mode != 0)
+ if (!audio_devs[dev]->go)
+ {
+ /* OK to start the device */
+ audio_devs[dev]->go = 1;
- if (local)
- return dmap_out->fragment_size;
- else
- return ioctl_out (arg, dmap_out->fragment_size);
- break;
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger (dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
+}
- case SNDCTL_DSP_SUBDIVIDE:
- {
- int fact;
- int ret;
+void
+DMAbuf_start_devices (unsigned int devmask)
+{
+ int dev;
- get_user (fact, (int *) arg);
+ for (dev = 0; dev < num_audiodevs; dev++)
+ if (devmask & (1 << dev))
+ DMAbuf_start_device (dev);
+}
- ret = dma_subdivide (dev, dmap_out, arg, fact);
- if (ret < 0)
- return ret;
+int
+DMAbuf_space_in_queue (int dev)
+{
+ int len, max, tmp;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ)
- ret = dma_subdivide (dev, dmap_in, arg, fact);
+ /* Don't allow touching pages too close to the playing ones */
+ int lim = dmap->nbufs - 1;
- return ret;
- }
- break;
+ if (lim < 2)
+ lim = 2;
- case SNDCTL_DSP_SETFRAGMENT:
- {
- int fact;
- int ret;
+ if (dmap->qlen >= lim) /* No space at all */
+ return 0;
- get_user (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);
+ /*
+ * Verify that there are no more pending buffers than the limit
+ * defined by the process.
+ */
- return ret;
- }
- break;
+ max = dmap->max_fragments;
+ len = dmap->qlen;
- case SNDCTL_DSP_GETISPACE:
- case SNDCTL_DSP_GETOSPACE:
- if (!local)
- return -EINVAL;
- else
- {
- struct dma_buffparms *dmap = dmap_out;
+ if (audio_devs[dev]->d->local_qlen)
+ {
+ tmp = audio_devs[dev]->d->local_qlen (dev);
+ if (tmp && len)
+ tmp--; /*
+ * This buffer has been counted twice
+ */
+ len += tmp;
+ }
- audio_buf_info *info = (audio_buf_info *) arg;
+ if (len >= max)
+ return 0;
+ return 1;
+}
- if (cmd == SNDCTL_DSP_GETISPACE &&
- !(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
+static int
+output_sleep (int dev, int dontblock)
+{
+ int tmout;
+ int err = 0;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
- dmap = dmap_in;
+ if (dontblock)
+ {
+ return -EAGAIN;
+ }
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return -EINVAL;
+ if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+ {
+ return -EAGAIN;
+ }
- if (!(dmap->flags & DMA_ALLOC_DONE))
- reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+ /*
+ * Wait for free space
+ */
+ if (!audio_devs[dev]->go)
+ tmout = 0;
+ else
+ {
+ tmout =
+ (dmap->fragment_size * HZ) / dmap->data_rate;
- info->fragstotal = dmap->nbufs;
+ tmout += HZ / 10; /* Some safety distance */
- if (cmd == SNDCTL_DSP_GETISPACE)
- info->fragments = dmap->qlen;
- else
- {
- if (!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 (tmout < (HZ / 2))
+ tmout = HZ / 2;
+ if (tmout > 20 * HZ)
+ tmout = 20 * HZ;
+ }
- if (info->fragments < 0)
- info->fragments = 0;
- else if (info->fragments > dmap->nbufs)
- info->fragments = dmap->nbufs;
+ if ((current->signal & ~current->blocked))
+ return -EIO;
- 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;
+ {
+ unsigned long tlimit;
- case SNDCTL_DSP_SETTRIGGER:
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on (&out_sleeper[dev]);
+ if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
{
- unsigned long flags;
-
- int bits;
- int changed;
-
- get_user (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;
- 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, 0);
- }
-
- ;
- dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
- 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 ioctl_out (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 = 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;
- {
- char *fixit = (char *) &info;
-
- copy_to_user (&((char *) arg)[0], fixit, 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;
+ if (jiffies >= tlimit)
+ out_sleep_flag[dev].opts |= WK_TIMEOUT;
}
- 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 = 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;
- {
- char *fixit = (char *) &info;
-
- copy_to_user (&((char *) arg)[0], fixit, 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;
-
-
- case SNDCTL_DSP_POST:
- ;
- if (audio_devs[dev]->dmap_out->qlen > 0)
- if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
- launch_output (dev, audio_devs[dev]->dmap_out);
+ out_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+ {
+ printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+ err = EIO;
;
- return 0;
- break;
-
- default:
- return audio_devs[dev]->d->ioctl (dev, cmd, arg, local);
+ dma_reset_output (dev);
+ }
+ else if ((current->signal & ~current->blocked))
+ {
+ err = EINTR;
}
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-
-void
-DMAbuf_start_device (int dev)
-{
- if (audio_devs[dev]->open_mode != 0)
- if (!audio_devs[dev]->go)
- {
- /* OK to start the device */
- audio_devs[dev]->go = 1;
-
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
-}
-
-void
-DMAbuf_start_devices (unsigned int devmask)
-{
- int dev;
-
- for (dev = 0; dev < num_audiodevs; dev++)
- if (devmask & (1 << dev))
- DMAbuf_start_device (dev);
+ return err;
}
static int
-space_in_queue (int dev)
+find_output_space (int dev, char **buf, int *size)
{
- int len, max, tmp;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ unsigned long flags;
+ unsigned long offs, active_offs;
+ long len;
- /* Don't allow touching pages too close to the playing ones */
- int lim = dmap->nbufs - 1;
+ if (!DMAbuf_space_in_queue (dev))
+ return 0;
- if (lim < 2)
- lim = 2;
+ save_flags (flags);
+ cli ();
- if (dmap->qlen >= lim) /* No space at all */
- return 0;
+#ifdef BE_CONSERVATIVE
+ active_offs = dmap->byte_counter + (dmap->qhead + 1) * dmap->fragment_size;
+#else
+ active_offs = ((dmap->byte_counter + DMAbuf_get_buffer_pointer (dev, dmap)));
+ /* / dmap->fragment_size) * dmap->fragment_size; */
- /*
- * Verify that there are no more pending buffers than the limit
- * defined by the process.
- */
+#endif
- max = dmap->max_fragments;
- len = dmap->qlen;
+ offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+ *buf = dmap->raw_buf + offs;
- if (audio_devs[dev]->d->local_qlen)
+ len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */
+
+ if ((offs + len) > dmap->bytes_in_use)
+ len = dmap->bytes_in_use - offs;
+
+ if (len < 0)
{
- tmp = audio_devs[dev]->d->local_qlen (dev);
- if (tmp && len)
- tmp--; /*
- * This buffer has been counted twice
- */
- len += tmp;
+ restore_flags (flags);
+ return 0;
}
- if (len >= max)
- return 0;
- return 1;
+ if ((offs + len) > dmap->bytes_in_use)
+ len = dmap->bytes_in_use - offs;
+
+ *size = len & ~3;
+
+ restore_flags (flags);
+
+ return (len > 0);
}
int
DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
{
unsigned long flags;
- int abort, err = EIO;
+ int err = EIO;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- dmap->flags &= ~DMA_CLEAN;
+ if (dmap->needs_reorg)
+ reorganize_buffers (dev, dmap, 0);
- if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
{
printk ("Sound: Can't write to mmapped device (3)\n");
return -EINVAL;
@@ -1247,204 +929,77 @@
DMAbuf_reset (dev);
dmap->dma_mode = DMODE_NONE;
}
- else if (dmap->flags & DMA_RESTART) /* Restart buffering */
- {
- DMAbuf_sync (dev);
- dma_reset_output (dev);
- dmap->dma_mode = DMODE_NONE;
- }
-
- dmap->flags &= ~(DMA_RESTART | DMA_EMPTY);
- if (!dmap->dma_mode)
- {
- int err;
-
- reorganize_buffers (dev, dmap, 0);
- dmap->dma_mode = DMODE_OUTPUT;
- if ((err = audio_devs[dev]->d->prepare_for_output (dev,
- dmap->fragment_size, dmap->nbufs)) < 0)
- return err;
- }
+ dmap->dma_mode = DMODE_OUTPUT;
save_flags (flags);
cli ();
- abort = 0;
- while (!space_in_queue (dev) &&
- !abort)
+ while (!find_output_space (dev, buf, size))
{
- int tmout;
-
- if (dontblock)
+ if ((err = output_sleep (dev, dontblock)) < 0)
{
restore_flags (flags);
- return -EAGAIN;
- }
-
- if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
- {
- restore_flags (flags);
- return -EAGAIN;
- }
-
- /*
- * Wait for free space
- */
- if (!audio_devs[dev]->go)
- tmout = 0;
- else
- {
- tmout =
- (dmap->fragment_size * HZ) / dmap->data_rate;
-
- tmout += HZ / 10; /* Some safety distance */
-
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
- }
-
-
- {
- unsigned long tlimit;
-
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- out_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&out_sleeper[dev]);
- if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- out_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- out_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
- {
- printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
- err = EIO;
- abort = 1;
- ;
- if (audio_devs[dev]->flags & DMA_AUTOMODE)
- dmap->flags |= DMA_RESTART;
- else
- dmap->flags &= ~DMA_RESTART;
- audio_devs[dev]->d->reset (dev);
- }
- else if ((current->signal & ~current->blocked))
- {
- err = EINTR;
- abort = 1;
+ return err;
}
}
- restore_flags (flags);
- if (!space_in_queue (dev))
+ if (!find_output_space (dev, buf, size))
{
- return -err; /* Caught a signal ? */
+ restore_flags (flags);
+ return -EIO; /* Caught a signal ? */
}
+ restore_flags (flags);
- *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
- *size = dmap->fragment_size;
- dmap->counts[dmap->qtail] = 0;
-
- return dmap->qtail;
-}
-
-int
-DMAbuf_get_curr_buffer (int dev, int *buf_no, char **dma_buf, int *buf_ptr, int *buf_size)
-{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dmap->cfrag < 0)
- return -1;
-
- *dma_buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
- *buf_ptr = dmap->counts[dmap->qtail];
- *buf_size = dmap->fragment_size;
- return *buf_no = dmap->cfrag;
-}
-
-int
-DMAbuf_set_count (int dev, int buff_no, int l)
-{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (buff_no == dmap->qtail)
- {
- dmap->cfrag = buff_no;
- dmap->counts[buff_no] = l;
- }
- else
- dmap->cfrag = -1;
+ dmap->flags |= DMA_DIRTY;
return 0;
}
int
-DMAbuf_start_output (int dev, int buff_no, int l)
+DMAbuf_move_wrpointer (int dev, int l)
{
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- int restart = 0;
- int post = dmap->flags & DMA_POST;
+ unsigned long ptr = (dmap->user_counter / dmap->fragment_size)
+ * dmap->fragment_size;
+
+ unsigned long end_ptr, p, prev_count;
+ int post = (dmap->flags & DMA_POST);
;
dmap->flags &= ~DMA_POST;
dmap->cfrag = -1;
- if (dmap->flags & DMA_RESTART)
- restart = 1;
-/*
- * Bypass buffering if using mmapped access
- */
+ prev_count = dmap->user_counter;
+ dmap->user_counter += l;
- 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;
+ if (dmap->user_counter < prev_count) /* Wrap? */
+ { /* Wrap the device counter too */
+ dmap->byte_counter %= dmap->bytes_in_use;
}
- else
- {
- dmap->qlen++;
+ end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
- dmap->counts[dmap->qtail] = l;
- if (l < dmap->fragment_size)
- {
- int p = dmap->fragment_size * dmap->qtail;
-
-#if defined(PPC) || defined(sparc) || defined(HPPA)
- dmap->neutral_byte = dmap->raw_buf[p + l - 2];
-#else
- dmap->neutral_byte = dmap->raw_buf[p + l - 1];
-#endif
-
- memset (dmap->raw_buf + p + l,
- dmap->neutral_byte,
- dmap->fragment_size - l);
- }
- else
- dmap->neutral_byte =
- dmap->raw_buf[dmap->fragment_size * dmap->qtail - 1];
+ p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+ dmap->neutral_byte = dmap->raw_buf[p];
+ /* Update the fragment based bookkeeping too */
+ while (ptr < end_ptr)
+ {
+ dmap->counts[dmap->qtail] = dmap->fragment_size;
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ dmap->qlen++;
+ ptr += dmap->fragment_size;
}
- /* Delay playback until there are at least two fragments (to prevent hiccup) */
- if (dmap->qlen > 1 || post ||
- (dmap->qlen > 0 && dmap->nbufs <= 2) ||
- (dmap->qlen > 0 && dmap->flags & DMA_SYNCING) ||
- restart || l != dmap->fragment_size)
- if (!(dmap->flags & DMA_ACTIVE))
+ dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ if (dmap->qlen > 1 ||
+ (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
{
- launch_output (dev, dmap);
+ DMAbuf_launch_output (dev, dmap);
}
;
@@ -1459,26 +1014,23 @@
if (dma_mode == DMA_MODE_WRITE)
{
- chan = audio_devs[dev]->dmachan1;
+ chan = audio_devs[dev]->dmap_out->dma;
dmap = audio_devs[dev]->dmap_out;
}
else
{
- chan = audio_devs[dev]->dmachan2;
+ chan = audio_devs[dev]->dmap_in->dma;
dmap = audio_devs[dev]->dmap_in;
}
if (dmap->raw_buf == NULL)
{
- printk ("sound: DMA buffer == NULL\n");
+ printk ("sound: DMA buffer(1) == NULL\n");
+ printk ("Device %d, chn=%s\n", dev,
+ (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
return 0;
}
- /* Handle cards with non automode DMA in new way */
- if (physaddr != dmap->raw_buf_phys) /* Not fragment 0 */
- return count;
- count = dmap->bytes_in_use;
-
if (chan < 0)
return 0;
@@ -1487,79 +1039,55 @@
* set_dma_addr()
*/
- if (audio_devs[dev]->flags & DMA_AUTOMODE)
- { /*
- * Auto restart mode. Transfer the whole *
- * buffer
- */
- unsigned long flags;
+ sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
- 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);
- set_dma_count (chan, dmap->bytes_in_use);
- enable_dma (chan);
- restore_flags (flags);
+ return count;
+}
+
+static int
+local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan;
+ struct dma_buffparms *dmap;
+
+ if (dma_mode == DMA_MODE_WRITE)
+ {
+ chan = audio_devs[dev]->dmap_out->dma;
+ dmap = audio_devs[dev]->dmap_out;
}
else
{
- unsigned long 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_flags (flags);
+ chan = audio_devs[dev]->dmap_in->dma;
+ dmap = audio_devs[dev]->dmap_in;
}
- return count;
-}
-
-void
-DMAbuf_init (void)
-{
- int dev;
+ if (dmap->raw_buf == NULL)
+ {
+ printk ("sound: DMA buffer(2) == NULL\n");
+ printk ("Device %d, chn=%s\n", dev,
+ (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+ return 0;
+ }
+ if (chan < 0)
+ return 0;
/*
- * NOTE! This routine could be called several times.
+ * The count must be one less than the actual size. This is handled by
+ * set_dma_addr()
*/
- for (dev = 0; dev < num_audiodevs; 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++];
- }
+ sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+ dmap->flags |= DMA_STARTED;
+
+ return count;
}
static void
-force_restart (int dev, struct dma_buffparms *dmap)
+finish_output_interrupt (int dev, struct dma_buffparms *dmap)
{
unsigned long flags;
- if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
- audio_devs[dev]->d->halt_output)
- audio_devs[dev]->d->halt_output (dev);
- else
- audio_devs[dev]->d->halt_xfer (dev);
-
- dmap->flags &= ~(DMA_ACTIVE | DMA_STARTED);
- dmap->flags |= DMA_RESTART;
- dmap->qlen = dmap->qhead = dmap->qtail = 0;
-
save_flags (flags);
cli ();
if ((out_sleep_flag[dev].opts & WK_SLEEP))
@@ -1590,111 +1118,98 @@
int this_fragment;
#ifdef OS_DMA_INTR
- if (audio_devs[dev]->dmachan1 >= 0)
- sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+ if (audio_devs[dev]->dmap_out->dma >= 0)
+ sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
#endif
if (dmap->raw_buf == NULL)
{
- printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+ printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
return;
}
if (dmap->mapping_flags & DMA_MAP_MAPPED)
{
+ unsigned long prev_counter = dmap->byte_counter;
+
/* mmapped access */
dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
if (dmap->qhead == 0) /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- dmap->qlen++; /* Yes increment it (don't decrement) */
- dmap->flags &= ~DMA_ACTIVE;
- dmap->counts[dmap->qhead] = dmap->fragment_size;
+ {
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter < prev_counter) /* Overflow */
+ {
+ dmap->user_counter %= dmap->bytes_in_use;
+ }
+ }
+ dmap->qlen++; /* Yes increment it (don't decrement) */
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
- {
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
+ dmap->flags &= ~DMA_ACTIVE;
+ dmap->counts[dmap->qhead] = dmap->fragment_size;
- audio_devs[dev]->d->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]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
- dmap->flags |= DMA_ACTIVE;
+ DMAbuf_launch_output (dev, dmap);
+ finish_output_interrupt (dev, dmap);
+ return;
}
- else if (event_type != 2)
+
+ if (event_type == 2)
{
- if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
- {
- printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n",
- dev, dmap->qlen, dmap->nbufs);
- force_restart (dev, dmap);
- return;
- }
+ finish_output_interrupt (dev, dmap);
+ return;
+ }
- save_flags (flags);
- cli ();
+ if (dmap->qlen > dmap->nbufs)
+ dmap->qlen = dmap->nbufs;
- dmap->qlen--;
- this_fragment = dmap->qhead;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ if (dmap->qlen <= 0)
+ {
+ finish_output_interrupt (dev, dmap);
+ return;
+ }
- if (dmap->qhead == 0) /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- dmap->flags &= ~DMA_ACTIVE;
+ save_flags (flags);
+ cli ();
- if (event_type == 1 && dmap->qlen < 1)
- {
- dmap->underrun_count++;
+ dmap->qlen--;
+ this_fragment = dmap->qhead;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- dmap->qlen = 0;
- force_restart (dev, dmap);
- }
+ if (dmap->qhead == 0) /* Wrapped */
+ {
+ unsigned long prev_counter = dmap->byte_counter;
- if (dmap->qlen)
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter < prev_counter) /* Overflow */
{
- if (dmap->flags & DMA_CLEAN)
- {
- int p = dmap->fragment_size * this_fragment;
-
- memset (dmap->raw_buf + p,
- dmap->neutral_byte,
- dmap->fragment_size);
- }
+ dmap->user_counter %= dmap->bytes_in_use;
+ }
+ }
- if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
- {
+ if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+ dmap->flags &= ~DMA_ACTIVE;
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
+ if (event_type == 1 && dmap->qlen < 1)
+ {
+ dmap->underrun_count++;
- audio_devs[dev]->d->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]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
- dmap->flags |= DMA_ACTIVE;
+ dmap->qlen = 0;
+ if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
+ {
+ dmap->flags &= ~DMA_DIRTY;
+ memset (audio_devs[dev]->dmap_out->raw_buf,
+ audio_devs[dev]->dmap_out->neutral_byte,
+ audio_devs[dev]->dmap_out->bytes_in_use);
}
+ dmap->user_counter += dmap->fragment_size;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ }
- restore_flags (flags);
- } /* event_type != 2 */
+ if (dmap->qlen > 0)
+ DMAbuf_launch_output (dev, dmap);
- save_flags (flags);
- cli ();
- if ((out_sleep_flag[dev].opts & WK_SLEEP))
- {
- {
- out_sleep_flag[dev].opts = WK_WAKEUP;
- wake_up (&out_sleeper[dev]);
- };
- }
restore_flags (flags);
+ finish_output_interrupt (dev, dmap);
}
void
@@ -1704,8 +1219,8 @@
struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
#ifdef OS_DMA_INTR
- if (audio_devs[dev]->dmachan2 >= 0)
- sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+ if (audio_devs[dev]->dmap_in->dma >= 0)
+ sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
#endif
if (dmap->raw_buf == NULL)
@@ -1718,15 +1233,26 @@
{
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
if (dmap->qtail == 0) /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
+ {
+ unsigned long prev_counter = dmap->byte_counter;
+
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter < prev_counter) /* Overflow */
+ {
+ dmap->user_counter %= dmap->bytes_in_use;
+ }
+ }
dmap->qlen++;
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
+ if (dmap->needs_reorg)
+ reorganize_buffers (dev, dmap, 0);
+ local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1,
- !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->fragment_size, 1);
if (audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger (dev,
audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -1734,26 +1260,13 @@
dmap->flags |= DMA_ACTIVE;
}
- else if (dmap->qlen == (dmap->nbufs - 1))
+ else if (dmap->qlen >= (dmap->nbufs - 1))
{
/* printk ("Sound: Recording overrun\n"); */
dmap->underrun_count++;
- if (audio_devs[dev]->flags & DMA_AUTOMODE)
- {
- /* Force restart on next read */
- if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
- audio_devs[dev]->d->halt_input)
- audio_devs[dev]->d->halt_input (dev);
- else
- audio_devs[dev]->d->halt_xfer (dev);
-
- dmap->flags &= ~DMA_ACTIVE;
- if (audio_devs[dev]->flags & DMA_AUTOMODE)
- dmap->flags |= DMA_RESTART;
- else
- dmap->flags &= ~DMA_RESTART;
- }
+ /* Just throw away the oldest fragment but keep the engine running */
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
}
else
{
@@ -1763,15 +1276,26 @@
dev, dmap->qlen, dmap->nbufs);
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
if (dmap->qtail == 0) /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
+ {
+ unsigned long prev_counter = dmap->byte_counter;
+
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter < prev_counter) /* Overflow */
+ {
+ dmap->user_counter %= dmap->bytes_in_use;
+ }
+ }
}
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
+ if (dmap->needs_reorg)
+ reorganize_buffers (dev, dmap, 0);
+ local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1,
- !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->fragment_size, 1);
if (audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger (dev,
audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -1798,9 +1322,8 @@
* NOTE! This routine opens only the primary DMA channel (output).
*/
- int chan = audio_devs[dev]->dmachan1;
+ int chan = audio_devs[dev]->dmap_out->dma;
int err;
- unsigned long flags;
if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
{
@@ -1812,6 +1335,8 @@
if (chan >= 0)
{
+ unsigned long flags;
+
save_flags (flags);
cli ();
disable_dma (chan);
@@ -1825,167 +1350,147 @@
void
DMAbuf_close_dma (int dev)
{
- DMAbuf_reset_dma (dev);
- close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+ close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
}
void
-DMAbuf_reset_dma (int dev)
+DMAbuf_init (void)
{
+ int dev;
+
+ /*
+ * NOTE! This routine could be called several times.
+ */
+
+ for (dev = 0; dev < num_audiodevs; dev++)
+ if (audio_devs[dev]->dmap_out == NULL)
+ {
+ if (audio_devs[dev]->d == NULL)
+ panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
+ if (audio_devs[dev]->parent_dev)
+ { /* Use DMA map of the parent dev */
+ int parent = audio_devs[dev]->parent_dev - 1;
+
+ audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
+ audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
+ }
+ else
+ {
+ 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++];
+ }
+ }
}
-unsigned int
-DMAbuf_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
{
- unsigned int mask = 0;
struct dma_buffparms *dmap;
unsigned long flags;
- save_flags (flags);
- cli ();
-
- in_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&in_sleeper[dev], wait);
- out_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&out_sleeper[dev], wait);
-
- restore_flags (flags);
-
-/* sel_in */
- dmap = audio_devs[dev]->dmap_in;
- if (!(audio_devs[dev]->open_mode))
- goto sel_out;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- mask |= POLLIN | POLLRDNORM;
- goto sel_out;
- }
- if (dmap->dma_mode != DMODE_INPUT) {
- if (dmap->dma_mode == DMODE_NONE &&
- audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
- !dmap->qlen &&
- audio_devs[dev]->go) {
- unsigned long flags;
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!(audio_devs[dev]->open_mode))
+ return 0;
- save_flags (flags);
- cli ();
- activate_recording (dev, dmap);
- restore_flags (flags);
- }
- goto sel_out;
- }
- if (!dmap->qlen)
- goto sel_out;
- mask |= POLLIN | POLLRDNORM;
+ dmap = audio_devs[dev]->dmap_in;
- sel_out:
- dmap = audio_devs[dev]->dmap_out;
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ if (dmap->qlen)
+ return 1;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- mask |= POLLOUT | POLLWRNORM;
- goto sel_ex;
- }
- if (dmap->dma_mode == DMODE_INPUT)
- goto sel_ex;
- if (dmap->dma_mode == DMODE_NONE) {
- mask |= POLLOUT | POLLWRNORM;
- goto sel_ex;
- }
- if (!space_in_queue (dev))
- goto sel_ex;
- mask |= POLLOUT | POLLWRNORM;
+ save_flags (flags);
+ cli ();
-sel_ex:
- return mask;
-}
+ in_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait (&in_sleeper[dev], wait);
+ restore_flags (flags);
+ return 0;
+ }
+ if (dmap->dma_mode != DMODE_INPUT)
+ {
+ if (dmap->dma_mode == DMODE_NONE &&
+ audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
+ !dmap->qlen &&
+ audio_devs[dev]->go)
+ {
+ unsigned long flags;
-#else /* CONFIG_AUDIO */
-/*
- * Stub versions if audio services not included
- */
+ save_flags (flags);
+ cli ();
+ DMAbuf_activate_recording (dev, dmap);
+ restore_flags (flags);
+ }
+ return 0;
+ }
-int
-DMAbuf_open (int dev, int mode)
-{
- return -ENXIO;
-}
+ if (!dmap->qlen)
+ {
+ save_flags (flags);
+ cli ();
-int
-DMAbuf_release (int dev, int mode)
-{
- return 0;
-}
+ in_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait (&in_sleeper[dev], wait);
+ restore_flags (flags);
+ return 0;
+ }
+ return 1;
+ break;
-int
-DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
-{
- return -EIO;
-}
+ case SEL_OUT:
+ dmap = audio_devs[dev]->dmap_out;
-int
-DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
-{
- return -EIO;
-}
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ if (dmap->qlen)
+ return 1;
-int
-DMAbuf_rmchars (int dev, int buff_no, int c)
-{
- return -EIO;
-}
+ save_flags (flags);
+ cli ();
-int
-DMAbuf_start_output (int dev, int buff_no, int l)
-{
- return -EIO;
-}
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait (&out_sleeper[dev], wait);
+ restore_flags (flags);
+ return 0;
+ }
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
-{
- return -EIO;
-}
+ if (dmap->dma_mode == DMODE_INPUT)
+ {
+ return 0;
+ }
-void
-DMAbuf_init (void)
-{
-}
+ if (dmap->dma_mode == DMODE_NONE)
+ {
+ return 1;
+ }
-int
-DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
-{
- return -EIO;
-}
+ if (!DMAbuf_space_in_queue (dev))
+ {
+ save_flags (flags);
+ cli ();
-int
-DMAbuf_open_dma (int dev)
-{
- return -ENXIO;
-}
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait (&out_sleeper[dev], wait);
+ restore_flags (flags);
+ return 0;
+ }
+ return 1;
+ break;
-void
-DMAbuf_close_dma (int dev)
-{
- return;
-}
+ case SEL_EX:
+ return 0;
+ }
-void
-DMAbuf_reset_dma (int dev)
-{
- return;
+ return 0;
}
-void
-DMAbuf_inputintr (int dev)
-{
- return;
-}
-void
-DMAbuf_outputintr (int dev, int underrun_flag)
-{
- return;
-}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov