summaryrefslogtreecommitdiffstats
path: root/drivers/sound/dmabuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sound/dmabuf.c')
-rw-r--r--drivers/sound/dmabuf.c964
1 files changed, 964 insertions, 0 deletions
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
new file mode 100644
index 000000000..6f0794824
--- /dev/null
+++ b/drivers/sound/dmabuf.c
@@ -0,0 +1,964 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ *
+ * Copyright by Hannu Savolainen 1993, 1994
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "sound_calls.h"
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);
+
+static struct dma_buffparms dmaps[MAX_AUDIO_DEV] =
+{0}; /*
+ * Primitive way to allocate
+ * such a large array.
+ * Needs dynamic run-time allocation.
+ */
+
+static void
+reorganize_buffers (int dev)
+{
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
+
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ struct audio_operations *dsp_dev = audio_devs[dev];
+
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
+
+ if (dmap->fragment_size == 0)
+ { /* Compute the fragment size using the default algorithm */
+
+ sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+ nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+ sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+ 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 /= 8; /* #bits -> #bytes */
+
+ sz = sr * nc * sz;
+
+ /*
+ * 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 (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize)
+ bsz /= 2; /* Needs at least 2 buffers */
+
+ if (dmap->subdivision == 0) /* Not already set */
+ dmap->subdivision = 1; /* Init to default value */
+
+ bsz /= dmap->subdivision;
+
+ if (bsz < 64)
+ bsz = 4096; /* Just a sanity check */
+
+ while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS)
+ bsz *= 2;
+
+ 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)
+ dmap->fragment_size = audio_devs[dev]->buffsize;
+ bsz = dmap->fragment_size;
+ }
+
+ /*
+ * Now computing addresses for the logical buffers
+ */
+
+ n = 0;
+ for (i = 0; i < dmap->raw_count &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS; i++)
+ {
+ p = 0;
+
+ while ((p + bsz) <= dsp_dev->buffsize &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS)
+ {
+ dmap->buf[n] = dmap->raw_buf[i] + p;
+ dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p;
+ p += bsz;
+ n++;
+ }
+ }
+
+ dmap->nbufs = n;
+ dmap->bytes_in_use = n * bsz;
+
+ for (i = 0; i < dmap->nbufs; i++)
+ {
+ dmap->counts[i] = 0;
+ }
+
+ dmap->flags |= DMA_ALLOC_DONE;
+}
+
+static void
+dma_init_buffers (int dev)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
+ dmap->flags = DMA_BUSY; /* Other flags off */
+ dmap->qlen = dmap->qhead = dmap->qtail = 0;
+
+ dmap->qlen = dmap->qtail = dmap->qhead = 0;
+ dmap->dma_mode = DMODE_NONE;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ int retval;
+ struct dma_buffparms *dmap = NULL;
+
+ if (dev >= num_audiodevs)
+ {
+ printk ("PCM device %d not installed.\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!audio_devs[dev])
+ {
+ printk ("PCM device %d not initialized\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ if (dmap->flags & DMA_BUSY)
+ return RET_ERROR (EBUSY);
+
+#ifdef USE_RUNTIME_DMAMEM
+ dmap->raw_buf[0] = NULL;
+ sound_dma_malloc (dev);
+#endif
+
+ if (dmap->raw_buf[0] == NULL)
+ return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
+
+ if ((retval = audio_devs[dev]->open (dev, mode)) < 0)
+ return retval;
+
+ dmap->open_mode = mode;
+ dmap->subdivision = dmap->underrun_count = 0;
+ dmap->fragment_size = 0;
+ dmap->max_fragments = 65536; /* Just a large value */
+
+ dma_init_buffers (dev);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
+
+ return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+ int retval;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ audio_devs[dev]->reset (dev);
+ audio_devs[dev]->close (dev);
+
+ if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0)
+ printk ("Sound: Reset failed - Can't reopen device\n");
+ RESTORE_INTR (flags);
+
+ dma_init_buffers (dev);
+ reorganize_buffers (dev);
+}
+
+static int
+dma_sync (int dev)
+{
+ unsigned long flags;
+
+ if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)
+ {
+ DISABLE_INTR (flags);
+
+ while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])
+ && audio_devs[dev]->dmap->qlen)
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ RESTORE_INTR (flags);
+ return audio_devs[dev]->dmap->qlen;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ /*
+ * Some devices such as GUS have huge amount of on board RAM for the
+ * audio data. We have to wait util the device has finished playing.
+ */
+
+ DISABLE_INTR (flags);
+ if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */
+ {
+ while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && audio_devs[dev]->local_qlen (dev))
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ);
+ }
+ }
+ RESTORE_INTR (flags);
+ }
+ return audio_devs[dev]->dmap->qlen;
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ unsigned long flags;
+
+ if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT))
+ {
+ dma_sync (dev);
+ }
+
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free (dev);
+#endif
+
+ DISABLE_INTR (flags);
+ audio_devs[dev]->reset (dev);
+
+ audio_devs[dev]->close (dev);
+
+ audio_devs[dev]->dmap->dma_mode = DMODE_NONE;
+ audio_devs[dev]->dmap->flags &= ~DMA_BUSY;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ unsigned long flags;
+ int err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ DISABLE_INTR (flags);
+ if (!dmap->qlen)
+ {
+ if (dmap->flags & DMA_RESTART)
+ {
+ dma_reset (dev);
+ dmap->flags &= ~DMA_RESTART;
+ }
+
+ if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (!dmap->dma_mode)
+ {
+ int err;
+
+ if ((err = audio_devs[dev]->prepare_for_input (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+ dmap->dma_mode = DMODE_INPUT;
+ }
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+ }
+
+ /* Wait for the next block */
+
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else
+ err = EINTR;
+ }
+ RESTORE_INTR (flags);
+
+ if (!dmap->qlen)
+ return RET_ERROR (err);
+
+ *buf = &dmap->buf[dmap->qhead][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;
+
+ int p = dmap->counts[dmap->qhead] + c;
+
+ 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;
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ dma_sync (dev);
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ return IOCTL_OUT (arg, dmap->fragment_size);
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact = IOCTL_IN (arg);
+
+ if (fact == 0)
+ {
+ fact = dmap->subdivision;
+ if (fact == 0)
+ fact = 1;
+ return IOCTL_OUT (arg, fact);
+ }
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size)/* Too late to change */
+ return RET_ERROR (EINVAL);
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return RET_ERROR (EINVAL);
+
+ if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+ return RET_ERROR (EINVAL);
+
+ dmap->subdivision = fact;
+ return IOCTL_OUT (arg, fact);
+ }
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ {
+ int fact = IOCTL_IN (arg);
+ int bytes, count;
+
+ if (fact == 0)
+ return RET_ERROR (EIO);
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size)/* Too late to change */
+ return RET_ERROR (EINVAL);
+
+ bytes = fact & 0xffff;
+ count = (fact >> 16) & 0xffff;
+
+ if (count == 0)
+ count = MAX_SUB_BUFFERS;
+
+ if (bytes < 7 || bytes > 17) /* <64 || > 128k */
+ return RET_ERROR (EINVAL);
+
+ if (count < 2)
+ return RET_ERROR (EINVAL);
+
+ dmap->fragment_size = (1 << bytes);
+ dmap->max_fragments = count;
+
+ if (dmap->fragment_size > audio_devs[dev]->buffsize)
+ dmap->fragment_size = audio_devs[dev]->buffsize;
+
+ if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+ audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->fragment_size /= 2; /* Needs at least 2 buffers */
+
+ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
+ return IOCTL_OUT (arg, bytes | (count << 16));
+ }
+ break;
+
+ default:
+ return audio_devs[dev]->ioctl (dev, cmd, arg, local);
+ }
+
+ return RET_ERROR (EIO);
+}
+
+static int
+space_in_queue (int dev)
+{
+ int len, max, tmp;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->qlen == dmap->nbufs)/* 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]->local_qlen)
+ {
+ tmp = audio_devs[dev]->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)
+{
+ unsigned long flags;
+ int abort, err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
+ {
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+ else if (dmap->flags & DMA_RESTART) /* Restart buffering */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ }
+
+ dmap->flags &= ~DMA_RESTART;
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (!dmap->dma_mode)
+ {
+ int err;
+
+ dmap->dma_mode = DMODE_OUTPUT;
+ if ((err = audio_devs[dev]->prepare_for_output (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ return err;
+ }
+
+
+ DISABLE_INTR (flags);
+
+ abort = 0;
+ while (!space_in_queue (dev) &&
+ !abort)
+ {
+ /*
+ * Wait for free space
+ */
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ err = EIO;
+ abort = 1;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ err = EINTR;
+ abort = 1;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ if (!space_in_queue (dev))
+ {
+ return RET_ERROR (err); /* Caught a signal ? */
+ }
+
+ *buf = dmap->buf[dmap->qtail];
+ *size = dmap->fragment_size;
+ dmap->counts[dmap->qtail] = 0;
+
+ return dmap->qtail;
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (buff_no != dmap->qtail)
+ printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);
+
+ dmap->qlen++;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+
+ dmap->counts[dmap->qtail] = l;
+
+ if ((l != dmap->fragment_size) &&
+ ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART))
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ dmap->flags |= DMA_ACTIVE;
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_STARTED;
+ }
+
+ return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan = audio_devs[dev]->dmachan;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ unsigned long flags;
+
+ /*
+ * This function is not as portable as it should be.
+ */
+
+ /*
+ * 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
+ */
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
+ set_dma_addr (chan, dmap->raw_buf_phys[0]);
+ set_dma_count (chan, dmap->bytes_in_use);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+
+#ifdef __386BSD__
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ dmap->raw_buf_phys[0],
+ dmap->bytes_in_use,
+ chan);
+#else
+#if defined(GENERIC_SYSV)
+#ifndef DMAMODE_AUTO
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+#endif
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef DMAMODE_AUTO
+ | DMAMODE_AUTO
+#endif
+ ,
+ dmap->raw_buf_phys[0], dmap->bytes_in_use);
+ dma_enable (chan);
+#else
+#error This routine is not valid for this OS.
+#endif
+#endif
+
+#endif
+ }
+ else
+ {
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode);
+ set_dma_addr (chan, physaddr);
+ set_dma_count (chan, count);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+#ifdef __386BSD__
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+ physaddr,
+ count,
+ chan);
+#else
+
+#if defined(GENERIC_SYSV)
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+ physaddr, count);
+ dma_enable (chan);
+#else
+#error This routine is not valid for this OS.
+#endif /* GENERIC_SYSV */
+#endif
+
+#endif
+ }
+
+ return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ int dev;
+
+ /*
+ * NOTE! This routine could be called several times.
+ */
+
+ for (dev = 0; dev < num_audiodevs; dev++)
+ audio_devs[dev]->dmap = &dmaps[dev];
+ return mem_start;
+}
+
+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;
+
+ 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);
+ return;
+ }
+
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ dmap->flags &= ~DMA_ACTIVE;
+
+ if (dmap->qlen)
+ {
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+ else if (event_type == 1)
+ {
+ dmap->underrun_count++;
+ audio_devs[dev]->halt_xfer (dev);
+ if ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART)
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+ }
+ } /* event_type != 2 */
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->qlen == (dmap->nbufs - 1))
+ {
+ printk ("Sound: Recording overrun\n");
+ dmap->underrun_count++;
+ audio_devs[dev]->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;
+
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ unsigned long flags;
+ int chan = audio_devs[dev]->dmachan;
+
+ if (ALLOC_DMA_CHN (chan,"audio"))
+ {
+ printk ("Unable to grab DMA%d for the audio driver\n", chan);
+ return RET_ERROR (EBUSY);
+ }
+
+ DISABLE_INTR (flags);
+#ifdef linux
+ disable_dma (chan);
+ clear_dma_ff (chan);
+#endif
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ int chan = audio_devs[dev]->dmachan;
+
+ DMAbuf_reset_dma (chan);
+ RELEASE_DMA_CHN (chan);
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+}
+
+/*
+ * The sound_mem_init() is called by mem_init() immediately after mem_map is
+ * initialized and before free_page_list is created.
+ *
+ * This routine allocates DMA buffers at the end of available physical memory (
+ * <16M) and marks pages reserved at mem_map.
+ */
+
+#else
+/*
+ * Stub versions if audio services not included
+ */
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ return RET_ERROR (ENXIO);
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ return 0;
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_open_dma (int chan)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+DMAbuf_close_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_reset_dma (int chan)
+{
+ return;
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_outputintr (int dev, int underrun_flag)
+{
+ return;
+}
+
+#endif
+
+#endif