/*
* sound/dmabuf.c
*
* The DMA buffer manager for digitized voice applications
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1996
*
* 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"
#if defined(CONFIG_AUDIO) || defined(CONFIG_GUSHW)
static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] =
{NULL};
static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] =
{
{0}};
static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] =
{NULL};
static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
{
{0}};
#define NEUTRAL8 0x80
#define NEUTRAL16 0x00
static int ndmaps = 0;
#define MAX_DMAP (MAX_AUDIO_DEV*2)
static struct dma_buffparms dmaps[MAX_DMAP] =
{
{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 void
dma_init_buffers (int dev, struct dma_buffparms *dmap)
{
if (dmap == audio_devs[dev]->dmap_out)
{
out_sleep_flag[dev].opts = WK_NONE;
}
else
{
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->bytes_in_use = audio_devs[dev]->buffsize;
dmap->dma_mode = DMODE_NONE;
dmap->mapping_flags = 0;
dmap->neutral_byte = NEUTRAL8;
dmap->data_rate = 8000;
dmap->cfrag = -1;
dmap->closing = 0;
}
static int
open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
{
if (dmap->flags & DMA_BUSY)
return -EBUSY;
{
int err;
if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0)
return err;
}
if (dmap->raw_buf == NULL)
{
printk ("Sound: DMA buffers not available\n");
return -ENOSPC; /* Memory allocation failed during boot */
}
if (sound_open_dma (chan, audio_devs[dev]->name))
{
printk ("Unable to grab(2) DMA%d for the audio driver\n", chan);
return -EBUSY;
}
dmap->open_mode = mode;
dmap->subdivision = dmap->underrun_count = 0;
dmap->fragment_size = 0;
dmap->max_fragments = 65536; /* Just a large value */
dmap->byte_counter = 0;
dma_init_buffers (dev, dmap);
return 0;
}
static void
close_dmap (int dev, struct dma_buffparms *dmap, int chan)
{
sound_close_dma (chan);
if (dmap->flags & DMA_BUSY)
dmap->dma_mode = DMODE_NONE;
dmap->flags &= ~DMA_BUSY;
disable_dma (chan);
sound_free_dmap (dev, dmap, chan);
}
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);
}
static int
default_set_speed (int dev, int speed)
{
return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) speed, 1);
}
static short
default_set_channels (int dev, short channels)
{
int c = channels;
return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) c, 1);
}
static void
check_driver (struct audio_driver *d)
{
if (d->set_speed == NULL)
d->set_speed = default_set_speed;
if (d->set_bits == NULL)
d->set_bits = default_set_bits;
if (d->set_channels == NULL)
d->set_channels = default_set_channels;
}
int
DMAbuf_open (int dev, int mode)
{
int retval;
struct dma_buffparms *dmap_in = NULL;
struct dma_buffparms *dmap_out = NULL;
if (dev >= num_audiodevs)
{
/* printk ("PCM device %d not installed.\n", dev); */
return -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;
}
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)
{
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;
}
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;
audio_devs[dev]->d->set_bits (dev, 8);
audio_devs[dev]->d->set_channels (dev, 1);
audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED);
return 0;
}
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]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ)
dma_reset_input (dev);
}
static void
dma_reset_output (int dev)
{
unsigned long flags;
int tmout;
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;
/*
* First wait until the current fragment has been played completely
*/
save_flags (flags);
cli ();
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;
audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
audio_devs[dev]->dmap_out->underrun_count = 0;
if (!(current->signal & ~current->blocked)
&& audio_devs[dev]->dmap_out->qlen
&& audio_devs[dev]->dmap_out->underrun_count == 0)
{
{
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;
};
}
audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
/*
* Finally shut the device off
*/
if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
!audio_devs[dev]->d->halt_output)
audio_devs[dev]->d->reset (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);
reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
}
static void
dma_reset_input (int dev)
{
unsigned long flags;
save_flags (flags);
cli ();
if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
!audio_devs[dev]->d->halt_input)
audio_devs[dev]->d->reset (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);
reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
}
static void
launch_output (int dev, struct dma_buffparms *dmap)
{
dmap->flags |= DMA_ACTIVE;
if (dmap->dma_mode == DMODE_NONE || dmap->flags & DMA_RESTART)
{
reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
audio_devs[dev]->d->prepare_for_output (dev,
dmap->fragment_size, dmap->nbufs);
}
dmap->dma_mode |= DMODE_OUTPUT;
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]->enable_bits * audio_devs[dev]->go);
}
int
DMAbuf_sync (int dev)
{
unsigned long flags;
int tmout;
if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
return 0;
if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
{
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
save_flags (flags);
cli ();
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;
;
if (dmap->qlen > 0)
if (!(dmap->flags & DMA_ACTIVE))
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)
&& audio_devs[dev]->dmap_out->qlen
&& audio_devs[dev]->dmap_out->underrun_count == 0)
{
{
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))
{
audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING;
restore_flags (flags);
return audio_devs[dev]->dmap_out->qlen;
}
}
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
* audio data. We have to wait until the device has finished playing.
*/
save_flags (flags);
cli ();
if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
{
while (!((current->signal & ~current->blocked))
&& audio_devs[dev]->d->local_qlen (dev))
{
{
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;
};
}
}
restore_flags (flags);
}
audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
return audio_devs[dev]->dmap_out->qlen;
}
int
DMAbuf_release (int dev, int mode)
{
unsigned long flags;
audio_devs[dev]->dmap_out->closing = 1;
audio_devs[dev]->dmap_in->closing = 1;
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_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);
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_READ &&
audio_devs[dev]->flags & DMA_DUPLEX)
close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
audio_devs[dev]->open_mode = 0;
restore_flags (flags);
return 0;
}
static int
activate_recording (int dev, struct dma_buffparms *dmap)
{
int prepare = 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);
DMAbuf_reset (dev);
dmap->dma_mode = DMODE_NONE;
}
if (prepare || !dmap->dma_mode)
{
int err;
reorganize_buffers (dev, dmap, 1);
if ((err = audio_devs[dev]->d->prepare_for_input (dev,
dmap->fragment_size, dmap->nbufs)) < 0)
{
return err;
}
dmap->dma_mode = DMODE_INPUT;
}
if (!(dmap->flags & DMA_ACTIVE))
{
audio_devs[dev]->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;
if (audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger (dev,
audio_devs[dev]->enable_bits * audio_devs[dev]->go);
}
return 0;
}
int
DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
{
unsigned long flags;
int err = EIO;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
save_flags (flags);
cli ();
if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
{
printk ("Sound: Can't read from mmapped device (1)\n");
return -EINVAL;
}
else if (!dmap->qlen)
{
int tmout;
if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
!audio_devs[dev]->go)
{
restore_flags (flags);
return -EAGAIN;
}
if ((err = activate_recording (dev, dmap)) < 0)
{
restore_flags (flags);
return err;
}
/* Wait for the next block */
if (dontblock)
{
restore_flags (flags);
return -EAGAIN;
}
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;
in_sleep_flag[dev].opts = WK_SLEEP;
interruptible_sleep_on (&in_sleeper[dev]);
if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
{
if (jiffies >= tlimit)
in_sleep_flag[dev].opts |= WK_TIMEOUT;
}
in_sleep_flag[dev].opts &= ~WK_SLEEP;
};
if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
{
printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
err = EIO;
audio_devs[dev]->d->reset (dev);
;
}
else
err = EINTR;
}
restore_flags (flags);
if (!dmap->qlen)
return -err;
*buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
*len = dmap->fragment_size - dmap->counts[dmap->qhead];
return dmap->qhead;
}
int
DMAbuf_rmchars (int dev, int buff_no, int c)
{
struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
int p = dmap->counts[dmap->qhead] + c;
if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
{
printk ("Sound: Can't read from mmapped device (2)\n");
return -EINVAL;
}
else if (p >= dmap->fragment_size)
{ /* This buffer is completely empty */
dmap->counts[dmap->qhead] = 0;
if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n",
dev, dmap->qlen, dmap->nbufs);
dmap->qlen--;
dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
}
else
dmap->counts[dmap->qhead] = p;
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)
{
/*
* Try to approximate the active byte position of the DMA pointer within the
* buffer area as well as possible.
*/
int pos;
unsigned long flags;
save_flags (flags);
cli ();
if (!(dmap->flags & DMA_ACTIVE))
pos = 0;
else
{
clear_dma_ff (chan);
disable_dma (chan);
pos = get_dma_residue (chan);
if (chan > 3) /* Word count */
pos *= 2;
pos = dmap->bytes_in_use - pos - 1;
if (pos < 0)
pos = 0;
if (pos > dmap->bytes_in_use)
pos = dmap->bytes_in_use;
enable_dma (chan);
}
restore_flags (flags);
/* printk ("%04x ", pos); */
return pos;
}
int
DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
{
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 (local)
return dmap_out->fragment_size;
else
return ioctl_out (arg, dmap_out->fragment_size);
break;
case SNDCTL_DSP_SUBDIVIDE:
{
int fact;
int ret;
get_user (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_SETFRAGMENT:
{
int fact;
int ret;
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);
return ret;
}
break;
case SNDCTL_DSP_GETISPACE:
case SNDCTL_DSP_GETOSPACE:
if (!local)
return -EINVAL;
else
{
struct dma_buffparms *dmap = dmap_out;
audio_buf_info *info = (audio_buf_info *) arg;
if (cmd == SNDCTL_DSP_GETISPACE &&
!(audio_devs[dev]->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 (!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;
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;
}
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);
;
return 0;
break;
default:
return audio_devs[dev]->d->ioctl (dev, cmd, arg, local);
}
}
/*
* DMAbuf_start_devices() is called by the /dev/music driver to start
* one or more audio devices at desired moment.
*/
void
DMAbuf_start_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);
}
static int
space_in_queue (int dev)
{
int len, max, tmp;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
/* Don't allow touching pages too close to the playing ones */
int lim = dmap->nbufs - 1;
if (lim < 2)
lim = 2;
if (dmap->qlen >= lim) /* No space at all */
return 0;
/*
* Verify that there are no more pending buffers than the limit
* defined by the process.
*/
max = dmap->max_fragments;
len = dmap->qlen;
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;
}
if (len >= max)
return 0;
return 1;
}
int
DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
{
unsigned long flags;
int abort, err = EIO;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
dmap->flags &= ~DMA_CLEAN;
if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
{
printk ("Sound: Can't write to mmapped device (3)\n");
return -EINVAL;
}
if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
{
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;
}
save_flags (flags);
cli ();
abort = 0;
while (!space_in_queue (dev) &&
!abort)
{
int tmout;
if (dontblock)
{
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;
}
}
restore_flags (flags);
if (!space_in_queue (dev))
{
return -err; /* Caught a signal ? */
}
*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;
return 0;
}
int
DMAbuf_start_output (int dev, int buff_no, int l)
{
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
int restart = 0;
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
*/
if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
{
l = dmap->fragment_size;
dmap->counts[dmap->qtail] = l;
dmap->flags &= ~DMA_RESTART;
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
}
else
{
dmap->qlen++;
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];
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
}
/* 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))
{
launch_output (dev, dmap);
}
;
return 0;
}
int
DMAbuf_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]->dmachan1;
dmap = audio_devs[dev]->dmap_out;
}
else
{
chan = audio_devs[dev]->dmachan2;
dmap = audio_devs[dev]->dmap_in;
}
if (dmap->raw_buf == NULL)
{
printk ("sound: DMA buffer == NULL\n");
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;
/*
* The count must be one less than the actual size. This is handled by
* set_dma_addr()
*/
if (audio_devs[dev]->flags & DMA_AUTOMODE)
{ /*
* Auto restart mode. Transfer the whole *
* buffer
*/
unsigned long flags;
save_flags (flags);
cli ();
disable_dma (chan);
clear_dma_ff (chan);
set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
set_dma_addr (chan, dmap->raw_buf_phys);
set_dma_count (chan, dmap->bytes_in_use);
enable_dma (chan);
restore_flags (flags);
}
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);
}
return count;
}
void
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)
{
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++];
}
}
static void
force_restart (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))
{
{
out_sleep_flag[dev].opts = WK_WAKEUP;
wake_up (&out_sleeper[dev]);
};
}
restore_flags (flags);
}
void
DMAbuf_outputintr (int dev, int event_type)
{
/*
* Event types:
* 0 = DMA transfer done. Device still has more data in the local
* buffer.
* 1 = DMA transfer done. Device doesn't have local buffer or it's
* empty now.
* 2 = No DMA transfer but the device has now more space in it's local
* buffer.
*/
unsigned long flags;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
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);
#endif
if (dmap->raw_buf == NULL)
{
printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
return;
}
if (dmap->mapping_flags & DMA_MAP_MAPPED)
{
/* 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;
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
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], 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;
}
else 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;
}
save_flags (flags);
cli ();
dmap->qlen--;
this_fragment = dmap->qhead;
dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
if (dmap->qhead == 0) /* Wrapped */
dmap->byte_counter += dmap->bytes_in_use;
dmap->flags &= ~DMA_ACTIVE;
if (event_type == 1 && dmap->qlen < 1)
{
dmap->underrun_count++;
dmap->qlen = 0;
force_restart (dev, dmap);
}
if (dmap->qlen)
{
if (dmap->flags & DMA_CLEAN)
{
int p = dmap->fragment_size * this_fragment;
memset (dmap->raw_buf + p,
dmap->neutral_byte,
dmap->fragment_size);
}
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
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], 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;
}
restore_flags (flags);
} /* event_type != 2 */
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);
}
void
DMAbuf_inputintr (int dev)
{
unsigned long flags;
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);
#endif
if (dmap->raw_buf == NULL)
{
printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
return;
}
if (dmap->mapping_flags & DMA_MAP_MAPPED)
{
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
if (dmap->qtail == 0) /* Wrapped */
dmap->byte_counter += dmap->bytes_in_use;
dmap->qlen++;
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
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));
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;
}
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;
}
}
else
{
dmap->qlen++;
if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n",
dev, dmap->qlen, dmap->nbufs);
dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
if (dmap->qtail == 0) /* Wrapped */
dmap->byte_counter += dmap->bytes_in_use;
}
if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
{
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));
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;
save_flags (flags);
cli ();
if ((in_sleep_flag[dev].opts & WK_SLEEP))
{
{
in_sleep_flag[dev].opts = WK_WAKEUP;
wake_up (&in_sleeper[dev]);
};
}
restore_flags (flags);
}
int
DMAbuf_open_dma (int dev)
{
/*
* NOTE! This routine opens only the primary DMA channel (output).
*/
int chan = audio_devs[dev]->dmachan1;
int err;
unsigned long flags;
if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
{
return -EBUSY;
}
dma_init_buffers (dev, audio_devs[dev]->dmap_out);
audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->buffsize;
if (chan >= 0)
{
save_flags (flags);
cli ();
disable_dma (chan);
clear_dma_ff (chan);
restore_flags (flags);
}
return 0;
}
void
DMAbuf_close_dma (int dev)
{
DMAbuf_reset_dma (dev);
close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
}
void
DMAbuf_reset_dma (int dev)
{
}
unsigned int
DMAbuf_poll (kdev_t dev, struct fileinfo *file, 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;
save_flags (flags);
cli ();
activate_recording (dev, dmap);
restore_flags (flags);
}
goto sel_out;
}
if (!dmap->qlen)
goto sel_out;
mask |= POLLIN | POLLRDNORM;
sel_out:
dmap = audio_devs[dev]->dmap_out;
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;
sel_ex:
return mask;
}
#else /* CONFIG_AUDIO */
/*
* Stub versions if audio services not included
*/
int
DMAbuf_open (int dev, int mode)
{
return -ENXIO;
}
int
DMAbuf_release (int dev, int mode)
{
return 0;
}
int
DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
{
return -EIO;
}
int
DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
{
return -EIO;
}
int
DMAbuf_rmchars (int dev, int buff_no, int c)
{
return -EIO;
}
int
DMAbuf_start_output (int dev, int buff_no, int l)
{
return -EIO;
}
int
DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
{
return -EIO;
}
void
DMAbuf_init (void)
{
}
int
DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
{
return -EIO;
}
int
DMAbuf_open_dma (int dev)
{
return -ENXIO;
}
void
DMAbuf_close_dma (int dev)
{
return;
}
void
DMAbuf_reset_dma (int dev)
{
return;
}
void
DMAbuf_inputintr (int dev)
{
return;
}
void
DMAbuf_outputintr (int dev, int underrun_flag)
{
return;
}
#endif