diff options
Diffstat (limited to 'drivers/sound/dmabuf.c')
-rw-r--r-- | drivers/sound/dmabuf.c | 964 |
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 |