diff options
Diffstat (limited to 'sys/i386/isa/sound/dmabuf.c')
-rw-r--r-- | sys/i386/isa/sound/dmabuf.c | 1944 |
1 files changed, 1291 insertions, 653 deletions
diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c index 0e275b6a3aea..db1824efab15 100644 --- a/sys/i386/isa/sound/dmabuf.c +++ b/sys/i386/isa/sound/dmabuf.c @@ -1,10 +1,10 @@ /* * sound/dmabuf.c - * + * * The DMA buffer manager for digitized voice applications - * - * Copyright by Hannu Savolainen 1993, 1994 - * + * + * Copyright by Hannu Savolainen 1993, 1994, 1995 + * * 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 @@ -12,7 +12,7 @@ * 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 @@ -24,941 +24,1579 @@ * 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" +#include <i386/isa/sound/sound_config.h> +#include <i386/include/md_var.h> -#ifdef CONFIGURE_SOUNDCARD +#if defined(CONFIG_AUDIO) || defined(CONFIG_GUS) +#ifdef ALLOW_POLL -#include "sound_calls.h" - -#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) +int +DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait); +#endif; -DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]); +static void +reorganize_buffers(int dev, struct dma_buffparms * dmap); -static struct dma_buffparms dmaps[MAX_AUDIO_DEV] = -{0}; /* - * Primitive way to allocate - * such a large array. - * Needs dynamic run-time alloction. - */ +static int *in_sleeper[MAX_AUDIO_DEV] = {NULL}; +static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] = {{0}}; +static int *out_sleeper[MAX_AUDIO_DEV] = {NULL}; +static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = {{0}}; -static void -reorganize_buffers (int dev) -{ - /* - * This routine breaks the physical device buffers to logical ones. - */ +static int ndmaps = 0; - struct dma_buffparms *dmap = audio_devs[dev]->dmap; - struct audio_operations *dsp_dev = audio_devs[dev]; +#define MAX_DMAP (MAX_AUDIO_DEV*2) - unsigned i, p, n; - unsigned sr, nc, sz, bsz; +static struct dma_buffparms dmaps[MAX_DMAP] = {{0}}; +/* + * Primitive way to allocate such a large array. Needs dynamic run-time + * alloction. + */ - if (dmap->fragment_size == 0) - { /* Compute the fragment size using the default algorithm */ +static int space_in_queue(int dev); - 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); +static void dma_reset_output(int dev); +static void dma_reset_input(int dev); - 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; +static void +reorganize_buffers(int dev, struct dma_buffparms * dmap) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + struct audio_operations *dsp_dev = audio_devs[dev]; + u_int sr, nc; + int bsz, sz, n, i; + + 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 (sz == 8) + dmap->neutral_byte = 254; + else + dmap->neutral_byte = 0x00; + + if (sr < 1 || nc < 1 || sz < 1) { + printf("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 */ + + /* + * Compute a buffer size for time not exeeding 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 */ + + if (dmap->subdivision == 0) /* Not already set */ + dmap->subdivision = 1; /* Init to default value */ + else + bsz /= dmap->subdivision; + + if (bsz < 16) + bsz = 16; /* Just a sanity check */ + + dmap->fragment_size = bsz; + } else { + /* + * The process has specified the buffer sice with + * SNDCTL_DSP_SETFRAGMENT or the buffer sice 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; + } - sz /= 8; /* #bits -> #bytes */ - - sz = sr * nc * sz; + bsz &= ~0x03; /* Force size which is multiple of 4 bytes */ +#ifdef OS_DMA_ALIGN_CHECK + OS_DMA_ALIGN_CHECK(bsz); +#endif - /* - * Compute a buffer size for time not exeeding 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). - */ + n = dsp_dev->buffsize / bsz; - bsz = dsp_dev->buffsize; - while (bsz > sz) - bsz /= 2; + if (n > MAX_SUB_BUFFERS) + n = MAX_SUB_BUFFERS; - if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize) - bsz /= 2; /* Needs at least 2 buffers */ + if (n > dmap->max_fragments) + n = dmap->max_fragments; + dmap->nbufs = n; + dmap->bytes_in_use = n * bsz; - if (dmap->subdivision == 0) /* Not already set */ - dmap->subdivision = 1; /* Init to default value */ + for (i = 0; i < dmap->nbufs; i++) { + dmap->counts[i] = 0; + } - bsz /= dmap->subdivision; + if (dmap->raw_buf) + fillw (dmap->neutral_byte, dmap->raw_buf, + dmap->bytes_in_use/2); - if (bsz < 64) - bsz = 4096; /* Just a sanity check */ + dmap->flags |= DMA_ALLOC_DONE; - while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS) - bsz *= 2; +} - dmap->fragment_size = bsz; - } - else - { - /* - * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or - * the buffer sice computation has already been done. - */ - if (dmap->fragment_size > audio_devs[dev]->buffsize) - dmap->fragment_size = audio_devs[dev]->buffsize; - bsz = dmap->fragment_size; +static void +dma_init_buffers(int dev, struct dma_buffparms * dmap) +{ + if (dmap == audio_devs[dev]->dmap_out) { + out_sleep_flag[dev].aborting = 0; + out_sleep_flag[dev].mode = WK_NONE; + } else { + in_sleep_flag[dev].aborting = 0; + in_sleep_flag[dev].mode = WK_NONE; } - /* - * Now computing addresses for the logical buffers - */ + 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; - n = 0; - for (i = 0; i < dmap->raw_count && - n < dmap->max_fragments && - n < MAX_SUB_BUFFERS; i++) + dmap->dma_mode = DMODE_NONE; + dmap->mapping_flags = 0; + dmap->neutral_byte = 0x00; +} + +static int +open_dmap(int dev, int mode, struct dma_buffparms * dmap, int chan) +{ + if (dmap->flags & DMA_BUSY) + return -(EBUSY); + +#ifdef RUNTIME_DMA_ALLOC { - p = 0; + int err; - 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++; - } + if ((err = sound_alloc_dmap(dev, dmap, chan)) < 0) + return err; } +#endif - dmap->nbufs = n; - dmap->bytes_in_use = n * bsz; + if (dmap->raw_buf == NULL) + return -(ENOSPC); /* Memory allocation failed during boot */ - for (i = 0; i < dmap->nbufs; i++) - { - dmap->counts[i] = 0; + if (0) { + printf("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; + isa_dma_acquire(chan); - dmap->flags |= DMA_ALLOC_DONE; + dma_init_buffers(dev, dmap); + + return 0; } static void -dma_init_buffers (int dev) +close_dmap(int dev, struct dma_buffparms * dmap, int chan) { - 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; + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + isa_dma_release(chan); +#ifdef RUNTIME_DMA_ALLOC + sound_free_dmap(dev, dmap); +#endif } int -DMAbuf_open (int dev, int mode) +DMAbuf_open(int dev, int mode) { - int retval; - struct dma_buffparms *dmap = NULL; + 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 RET_ERROR (ENXIO); + if (dev >= num_audiodevs) { + printf("PCM device %d not installed.\n", dev); + return -(ENXIO); } - - if (!audio_devs[dev]) - { - printk ("PCM device %d not initialized\n", dev); - return RET_ERROR (ENXIO); + if (!audio_devs[dev]) { + printf("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]->open(dev, mode)) < 0) + return retval; - dmap = audio_devs[dev]->dmap = &dmaps[dev]; - - if (dmap->flags & DMA_BUSY) - return RET_ERROR (EBUSY); + dmap_out = audio_devs[dev]->dmap_out; + dmap_in = audio_devs[dev]->dmap_in; -#ifdef USE_RUNTIME_DMAMEM - dmap->raw_buf[0] = NULL; - sound_dma_malloc (dev); -#endif + if ((retval = open_dmap(dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0) { + audio_devs[dev]->close(dev); + return retval; + } + audio_devs[dev]->enable_bits = mode; - if (dmap->raw_buf[0] == NULL) - return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ + if (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]->close(dev); + close_dmap(dev, dmap_out, audio_devs[dev]->dmachan1); + return retval; + } + } + audio_devs[dev]->open_mode = mode; + audio_devs[dev]->go = 1; - if ((retval = audio_devs[dev]->open (dev, mode)) < 0) - return retval; + in_sleep_flag[dev].aborting = 0; + in_sleep_flag[dev].mode = WK_NONE; - dmap->open_mode = mode; - dmap->subdivision = dmap->underrun_count = 0; - dmap->fragment_size = 0; - dmap->max_fragments = 65536; /* Just a large value */ + out_sleep_flag[dev].aborting = 0; + out_sleep_flag[dev].mode = WK_NONE; - 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); + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_BITS, (ioctl_arg) 8, 1); + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_CHANNELS, (ioctl_arg) 1, 1); + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_RATE, (ioctl_arg) DSP_DEFAULT_SPEED, 1); - return 0; + return 0; } static void -dma_reset (int dev) +dma_reset(int dev) { - int retval; - unsigned long flags; + u_long flags; - DISABLE_INTR (flags); + flags = splhigh(); + audio_devs[dev]->reset(dev); + splx(flags); - audio_devs[dev]->reset (dev); - audio_devs[dev]->close (dev); + dma_reset_output(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); + if (audio_devs[dev]->flags & DMA_DUPLEX) + dma_reset_input(dev); +} - dma_init_buffers (dev); - reorganize_buffers (dev); +static void +dma_reset_output(int dev) +{ + u_long flags; + + flags = splhigh(); + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->halt_output) + audio_devs[dev]->reset(dev); + else + audio_devs[dev]->halt_output(dev); + splx(flags); + + dma_init_buffers(dev, audio_devs[dev]->dmap_out); + reorganize_buffers(dev, audio_devs[dev]->dmap_out); +} + +static void +dma_reset_input(int dev) +{ + u_long flags; + + flags = splhigh(); + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->halt_input) + audio_devs[dev]->reset(dev); + else + audio_devs[dev]->halt_input(dev); + splx(flags); + + dma_init_buffers(dev, audio_devs[dev]->dmap_in); + reorganize_buffers(dev, audio_devs[dev]->dmap_in); } static int -dma_sync (int dev) +dma_sync(int dev) { - unsigned long flags; + u_long flags; + int i = 0; + + 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) { + flags = splhigh(); + + out_sleep_flag[dev].aborting = 0; +#ifdef ALLOW_BUFFER_MAPPING + if(audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED && + audio_devs[dev]->dmap_out->qlen) { + splx(flags); + + return audio_devs[dev]->dmap_out->qlen; + } - if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT) - { - DISABLE_INTR (flags); +#endif + while (!PROCESS_ABORTING (out_sleep_flag[dev]) + && audio_devs[dev]->dmap_out->qlen){ + int chn; + + out_sleeper[dev] = &chn; + DO_SLEEP1(chn, out_sleep_flag[dev], 10 * hz); + if (TIMED_OUT (out_sleep_flag[dev]) ) { + + splx(flags); + + return audio_devs[dev]->dmap_out->qlen; - 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); + splx(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. + */ + + flags = splhigh(); + if (audio_devs[dev]->local_qlen) { /* Device has hidden buffers */ + while (!(PROCESS_ABORTING (out_sleep_flag[dev])) + && audio_devs[dev]->local_qlen(dev)) { + int chn; + out_sleeper[dev] = &chn; + DO_SLEEP(chn, out_sleep_flag[dev], 10 * hz); + } } - RESTORE_INTR (flags); + splx(flags); } - return audio_devs[dev]->dmap->qlen; + return audio_devs[dev]->dmap_out->qlen; } int -DMAbuf_release (int dev, int mode) +DMAbuf_release(int dev, int mode) { - unsigned long flags; + u_long flags; - if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) - && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)) - { - dma_sync (dev); + if (!((out_sleep_flag[dev].aborting)) + && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) { + dma_sync(dev); } + flags = splhigh(); -#ifdef USE_RUNTIME_DMAMEM - sound_dma_free (dev); -#endif + audio_devs[dev]->close(dev); - DISABLE_INTR (flags); - audio_devs[dev]->reset (dev); + close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); - audio_devs[dev]->close (dev); + if (audio_devs[dev]->flags & DMA_DUPLEX) + close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); + audio_devs[dev]->open_mode = 0; - audio_devs[dev]->dmap->dma_mode = DMODE_NONE; - audio_devs[dev]->dmap->flags &= ~DMA_BUSY; - RESTORE_INTR (flags); + splx(flags); - return 0; + return 0; } -int -DMAbuf_getrdbuffer (int dev, char **buf, int *len) +static int +activate_recording(int dev, struct dma_buffparms * dmap) { - unsigned long flags; - int err = EIO; - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) + return 0; - DISABLE_INTR (flags); - if (!dmap->qlen) - { - if (dmap->flags & DMA_RESTART) - { - dma_reset (dev); - dmap->flags &= ~DMA_RESTART; - } + if (dmap->flags & DMA_RESTART) { + dma_reset_input(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, dmap); - if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ - { - dma_sync (dev); - dma_reset (dev); - dmap->dma_mode = DMODE_NONE; - } + if (!dmap->dma_mode) { + int err; - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev); + if ((err = audio_devs[dev]->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]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + return 0; +} - if (dmap->dma_mode) - { - int err; +int +DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) +{ + u_long flags; + int err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + flags = splhigh(); +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't read from mmapped device (1)\n"); + return -(EINVAL); + } else +#endif + if (!dmap->qlen) { + int timeout; - 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 ((err = activate_recording(dev, dmap)) < 0) { + splx(flags); + return err; } + /* Wait for the next block */ - 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; + if (dontblock) { + splx(flags); + return -(EAGAIN); } + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) & + audio_devs[dev]->go) { + splx(flags); + return -(EAGAIN); + } + if (!audio_devs[dev]->go) + timeout = 0; + else + timeout = 2 * hz; - /* 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; + int chn; + + in_sleeper[dev] = &chn; + DO_SLEEP(chn, in_sleep_flag[dev], timeout); + + }; + /* XXX note -- nobody seems to set the mode to WK_TIMEOUT - lr */ + if ((in_sleep_flag[dev].mode & WK_TIMEOUT)) { + /* XXX hey, we are in splhigh here ? lr 970705 */ + printf("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + err = EIO; + audio_devs[dev]->reset(dev); + in_sleep_flag[dev].aborting = 1; + } else + err = EINTR; } - RESTORE_INTR (flags); + splx(flags); - if (!dmap->qlen) - return RET_ERROR (err); + if (!dmap->qlen) + return -(err); - *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]]; - *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; - return dmap->qhead; + return dmap->qhead; } int -DMAbuf_rmchars (int dev, int buff_no, int c) +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; + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't read from mmapped device (2)\n"); + return -(EINVAL); + } else +#endif + if (p >= dmap->fragment_size) { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printf("\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, ioctl_arg arg, int fact) { - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + if (fact == 0) { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return *(int *) arg = fact; + } + if (dmap->subdivision != 0 || dmap->fragment_size)/* Loo late to change */ + return -(EINVAL); - int p = dmap->counts[dmap->qhead] + c; + if (fact > MAX_REALTIME_FACTOR) + return -(EINVAL); + + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return -(EINVAL); + + dmap->subdivision = fact; + return *(int *) arg = fact; +} + +static int +dma_set_fragment(int dev, struct dma_buffparms * dmap, ioctl_arg arg, int fact) +{ + int bytes, count; + + if (fact == 0) + return -(EIO); + + if (dmap->subdivision != 0 || dmap->fragment_size)/* Loo late to change */ + return -(EINVAL); + + bytes = fact & 0xffff; + count = (fact >> 16) & 0xffff; + + if (count == 0) + count = MAX_SUB_BUFFERS; + +#if amancio + if (bytes < 4 || bytes > 17) /* <16 || > 128k */ + return -(EINVAL); +#endif + + if (count < 2) + return -(EINVAL); + +#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 */ + return *(int *) arg = bytes | (count << 16); +} + +static int +get_buffer_pointer(int dev, int chan, struct dma_buffparms * dmap) +{ + int pos; + u_long flags; + + flags = splhigh(); - 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; + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else + { + pos = isa_dmastatus(chan); } + + splx(flags); + + pos = dmap->bytes_in_use - pos ; + if (audio_devs[dev]->flags & DMA_AUTOMODE) + return pos; else - dmap->counts[dmap->qhead] = p; + { + pos = dmap->fragment_size - pos; + if (pos < 0) + return 0; + return pos; + } + - return 0; } int -DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) { - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; + + switch (cmd) { + - switch (cmd) - { case SNDCTL_DSP_RESET: - dma_reset (dev); - return 0; - break; + dma_reset(dev); + return 0; + break; case SNDCTL_DSP_SYNC: - dma_sync (dev); - dma_reset (dev); - return 0; - break; + dma_sync(dev); + dma_reset(dev); + return 0; + break; case SNDCTL_DSP_GETBLKSIZE: - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev); + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_out); - return IOCTL_OUT (arg, dmap->fragment_size); - break; + return *(int *) arg = dmap_out->fragment_size; + break; - case SNDCTL_DSP_SUBDIVIDE: - { - int fact = IOCTL_IN (arg); + case SNDCTL_DSP_SETBLKSIZE: + { + int size = (*(int *) arg); - if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return IOCTL_OUT (arg, fact); - } + if (!(dmap_out->flags & DMA_ALLOC_DONE) && size) { + if ((size >> 16) > 0 ) + dmap_out->fragment_size = size >> 16; + else { + dmap_out->fragment_size = size; + } + dmap_out->max_fragments = 8888; + + size &= 0xffff; - if (dmap->subdivision != 0 || - dmap->fragment_size)/* Loo late to change */ - return RET_ERROR (EINVAL); + if (audio_devs[dev]->flags & DMA_DUPLEX) { + dmap_in->fragment_size = size; + dmap_in->max_fragments = 8888; + } + return 0; - if (fact > MAX_REALTIME_FACTOR) - return RET_ERROR (EINVAL); + } else + return -(EINVAL); /* Too late to change */ + + } + break; + + case SNDCTL_DSP_SUBDIVIDE: + { + int fact = (*(int *) arg); + int ret; - if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) - return RET_ERROR (EINVAL); + ret = dma_subdivide(dev, dmap_out, arg, fact); + if (ret < 0) + return ret; - dmap->subdivision = fact; - return IOCTL_OUT (arg, fact); - } - break; + if (audio_devs[dev]->flags & DMA_DUPLEX) + ret = dma_subdivide(dev, dmap_in, arg, fact); + + return ret; + } + break; case SNDCTL_DSP_SETFRAGMENT: - { - int fact = IOCTL_IN (arg); - int bytes, count; + { + int fact = (*(int *) arg); + int ret; - if (fact == 0) - return RET_ERROR (EIO); + ret = dma_set_fragment(dev, dmap_out, arg, fact); + if (ret < 0) + return ret; - if (dmap->subdivision != 0 || - dmap->fragment_size)/* Loo late to change */ - return RET_ERROR (EINVAL); + if (audio_devs[dev]->flags & DMA_DUPLEX) + ret = dma_set_fragment(dev, dmap_in, arg, fact); - bytes = fact & 0xffff; - count = (fact >> 16) & 0xffff; + return ret; + } + break; - if (count == 0) - count = MAX_SUB_BUFFERS; + case SNDCTL_DSP_GETISPACE: + case SNDCTL_DSP_GETOSPACE: + if (!local) + return -(EINVAL); + else { + struct dma_buffparms *dmap = dmap_out; - if (bytes < 7 || bytes > 17) /* <64 || > 128k */ - return RET_ERROR (EINVAL); + audio_buf_info *info = (audio_buf_info *) arg; - if (count < 2) - return RET_ERROR (EINVAL); + if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) + dmap = dmap_in; - dmap->fragment_size = (1 << bytes); - dmap->max_fragments = count; +#ifdef ALLOW_BUFFER_MAPPING + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return -(EINVAL); +#endif - if (dmap->fragment_size > audio_devs[dev]->buffsize) - dmap->fragment_size = audio_devs[dev]->buffsize; + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap); + + 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]->local_qlen) { + int tmp = audio_devs[dev]->local_qlen(dev); + + if (tmp & info->fragments) + tmp--; /* This buffer has been counted twice */ + info->fragments -= tmp; + } + } + } - if (dmap->fragment_size == audio_devs[dev]->buffsize && - audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->fragment_size /= 2; /* Needs at least 2 buffers */ + if (info->fragments < 0) + info->fragments = 0; + else if (info->fragments > dmap->nbufs) + info->fragments = dmap->nbufs; - dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ - return IOCTL_OUT (arg, bytes | (count << 16)); - } - break; + 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: + { + u_long flags; + + int bits = (*(int *) arg) & audio_devs[dev]->open_mode; + int changed; + + if (audio_devs[dev]->trigger == NULL) + return -(EINVAL); + + if (!(audio_devs[dev]->flags & DMA_DUPLEX)) + if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) { + printf("Sound: Device doesn't have full duplex capability\n"); + return -(EINVAL); + } + flags = splhigh(); + changed = audio_devs[dev]->enable_bits ^ bits; + + if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) { + if (!(dmap_in->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_in); + activate_recording(dev, dmap_in); + } +#ifdef ALLOW_BUFFER_MAPPING + 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); + + audio_devs[dev]->prepare_for_output (dev, + dmap_out->fragment_size, dmap_out->nbufs); + + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_start_output(dev, 0, dmap_out->fragment_size); + dmap_out->dma_mode = DMODE_OUTPUT; + } +#endif + + audio_devs[dev]->enable_bits = bits; + if (changed && audio_devs[dev]->trigger) + audio_devs[dev]->trigger(dev, bits * audio_devs[dev]->go); + splx(flags); + } + case SNDCTL_DSP_GETTRIGGER: + return *(int *) arg = audio_devs[dev]->enable_bits; + break; + + case SNDCTL_DSP_SETSYNCRO: + + if (!audio_devs[dev]->trigger) + return -(EINVAL); + + audio_devs[dev]->trigger(dev, 0); + audio_devs[dev]->go = 0; + return 0; + break; + + case SNDCTL_DSP_GETIPTR: + { + count_info info; + u_long flags; + + flags = splhigh(); + 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; + + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) + audio_devs[dev]->dmap_in->qlen = 0; /* Ack interrupts */ +#endif + splx(flags); + return 0; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info info; + u_long flags; + + flags = splhigh(); + 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; + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) + audio_devs[dev]->dmap_out->qlen = 0; /* Ack interrupts */ +#endif + splx(flags); + return 0; + } + break; default: - return audio_devs[dev]->ioctl (dev, cmd, arg, local); + return audio_devs[dev]->ioctl(dev, cmd, arg, local); } +} - return RET_ERROR (EIO); +/* + * DMAbuf_start_devices() is called by the /dev/music driver to start one or + * more audio devices at desired moment. + */ + +void +DMAbuf_start_devices(u_int devmask) +{ + int dev; + + for (dev = 0; dev < num_audiodevs; dev++) + if (devmask & (1 << 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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } } static int -space_in_queue (int dev) +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; + int len, max, tmp; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + 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; + if (len >= max) + return 0; + return 1; } int -DMAbuf_getwrbuffer (int dev, char **buf, int *size) +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; - - 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); + u_long flags; + int abort, err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't write to mmapped device (3)\n"); + return -(EINVAL); } +#endif - dmap->flags &= ~DMA_RESTART; + 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_output(dev); + } + dmap->flags &= ~DMA_RESTART; - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev); + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap); - if (!dmap->dma_mode) - { - int err; + 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; + dmap->dma_mode = DMODE_OUTPUT; + if ((err = audio_devs[dev]->prepare_for_output(dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; } + flags = splhigh(); + abort = 0; + while (!space_in_queue(dev) && !abort) { + int timeout; - DISABLE_INTR (flags); + if (dontblock) { + splx(flags); + return -(EAGAIN); + } + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT) && + audio_devs[dev]->go) { + splx(flags); + return -(EAGAIN); + } + /* + * Wait for free space + */ + if (!audio_devs[dev]->go) + timeout = 0; + else + timeout = 2 * hz; - 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]); + int chn; + + out_sleep_flag[dev].mode = WK_SLEEP; + out_sleeper[dev] = &chn; + DO_SLEEP2(chn, out_sleep_flag[dev], timeout); + + if ((out_sleep_flag[dev].mode & WK_TIMEOUT)) { + printf("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + err = EIO; + abort = 1; + out_sleep_flag[dev].aborting = 1; + audio_devs[dev]->reset(dev); + } else if ((out_sleep_flag[dev].aborting) || + CURSIG(curproc)) { + err = EINTR; + abort = 1; } - else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) - { - err = EINTR; - abort = 1; } } - RESTORE_INTR (flags); + splx(flags); - if (!space_in_queue (dev)) - { - return RET_ERROR (err); /* Caught a signal ? */ + if (!space_in_queue(dev)) { + return -(err); /* Caught a signal ? */ } - - *buf = dmap->buf[dmap->qtail]; - *size = dmap->fragment_size; - dmap->counts[dmap->qtail] = 0; - - return dmap->qtail; + *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size; + *size = dmap->fragment_size; + dmap->counts[dmap->qtail] = 0; + return dmap->qtail; } int -DMAbuf_start_output (int dev, int buff_no, int l) +DMAbuf_start_output(int dev, int buff_no, int l) { - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - if (buff_no != dmap->qtail) - printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); + /* + * Bypass buffering if using mmaped access + */ - 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); +#ifdef ALLOW_BUFFER_MAPPING + 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 +#else + if (dmap != NULL) +#endif + { - dmap->counts[dmap->qtail] = l; + if (buff_no != dmap->qtail) + printf("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); - 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->qlen++; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printf("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->counts[dmap->qtail] = l; - 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; - } + 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; - return 0; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } + if (!(dmap->flags & DMA_ACTIVE)) { + dmap->flags |= DMA_ACTIVE; + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + return 0; } int -DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +DMAbuf_start_dma(int dev, u_long physaddr, int count, int dma_mode) { - int chan = audio_devs[dev]->dmachan; - struct dma_buffparms *dmap = audio_devs[dev]->dmap; - unsigned long flags; + int chan; + struct dma_buffparms *dmap; - /* - * This function is not as portable as it should be. - */ + if (dma_mode == 1) { + chan = audio_devs[dev]->dmachan1; + dmap = audio_devs[dev]->dmap_out; - /* - * The count must be one less than the actual size. This is handled by - * set_dma_addr() - */ + } else { + chan = audio_devs[dev]->dmachan2; + dmap = audio_devs[dev]->dmap_in; + } - 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 + /* + * The count must be one less than the actual size. This is handled + * by set_dma_addr() + */ -#ifdef __386BSD__ - printk ("sound: Invalid DMA mode for device %d\n", dev); +#ifndef PSEUDO_DMA_AUTOINIT + if (audio_devs[dev]->flags & DMA_AUTOMODE) { + /* Auto restart mode. Transfer the whole buffer */ + isa_dmastart(B_RAW | ((dma_mode == 0) ? B_READ : B_WRITE), + (caddr_t) dmap->raw_buf_phys, dmap->bytes_in_use, chan); - 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 + } else #endif - , - dmap->raw_buf_phys[0], dmap->bytes_in_use); - dma_enable (chan); -#else -#error This routine is not valid for this OS. + { + isa_dmastart((dma_mode == 0) ? B_READ : B_WRITE, + (caddr_t) physaddr, count, chan); + } + return count; +} + +void +DMAbuf_init() +{ + int dev; + + /* + * NOTE! This routine could be called several times. + * XXX is it ok to make it run only the first time ? -- lr970710 + */ + + 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++]; + } +} + +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. + */ + + u_long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + dmap->byte_counter += dmap->counts[dmap->qhead]; +#ifdef OS_DMA_INTR + sound_dma_intr(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); #endif +#ifdef ALLOW_BUFFER_MAPPING + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + /* mmapped access */ + + int p = dmap->fragment_size * dmap->qhead; + + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + 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)) { + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +#ifdef PSEUDO_DMA_AUTOINIT + else { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + } #endif + dmap->flags |= DMA_ACTIVE; + } else #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 (event_type != 2) { + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) { + printf("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + return; + } + isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan1); -#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 + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->flags &= ~DMA_ACTIVE; + if (dmap->qlen) { + /* if (!(audio_devs[dev]->flags & NEEDS_RESTART)) */ + { + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +#ifdef PSEUDO_DMA_AUTOINIT + /* else */ + { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + } #endif - } - - return count; -} + dmap->flags |= DMA_ACTIVE; + } else if (event_type == 1) { + dmap->underrun_count++; + if ((audio_devs[dev]->flags & DMA_DUPLEX) && + audio_devs[dev]->halt_output) + audio_devs[dev]->halt_output(dev); + else + 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 */ + flags = splhigh(); -long -DMAbuf_init (long mem_start) -{ - int dev; + if ((out_sleep_flag[dev].mode & WK_SLEEP)) { + out_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(out_sleeper[dev]); + } - /* - * NOTE! This routine could be called several times. - */ + if(selinfo[dev].si_pid) { + selwakeup(&selinfo[dev]); + } - for (dev = 0; dev < num_audiodevs; dev++) - audio_devs[dev]->dmap = &dmaps[dev]; - return mem_start; + splx(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. - */ +DMAbuf_inputintr(int dev) +{ + u_long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; - unsigned long flags; - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + dmap->byte_counter += dmap->fragment_size; - 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; +#ifdef OS_DMA_INTR + sound_dma_intr(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); +#endif + isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan2); + +#ifdef ALLOW_BUFFER_MAPPING + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + + if (!(audio_devs[dev]->flags & NEEDS_RESTART)) { + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); } +#ifdef PSEUDO_DMA_AUTOINIT + else { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->counts[dmap->qtail], 0); + } +#endif - dmap->qlen--; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->flags &= ~DMA_ACTIVE; - - if (dmap->qlen) + dmap->flags |= DMA_ACTIVE; + } else +#endif + if (dmap->qlen == (dmap->nbufs - 1)) { + /* printf ("Sound: Recording overrun\n"); */ + dmap->underrun_count++; + if ((audio_devs[dev]->flags & DMA_DUPLEX) && + audio_devs[dev]->halt_input) + audio_devs[dev]->halt_input(dev); + else + 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) + printf("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + + /* if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) */ { - 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; + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); } - else if (event_type == 1) +#ifdef PSEUDO_DMA_AUTOINIT + /* else */ { - 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; + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->counts[dmap->qtail], 0); } - } /* event_type != 2 */ +#endif - DISABLE_INTR (flags); - if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) - { - WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); + dmap->flags |= DMA_ACTIVE; + } + + flags = splhigh(); + if ((in_sleep_flag[dev].mode & WK_SLEEP)) { + in_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(in_sleeper[dev]); } - RESTORE_INTR (flags); + if (selinfo[dev].si_pid) + selwakeup(&selinfo[dev]); + splx(flags); +} + +int +DMAbuf_open_dma(int dev) +{ + int err; + u_long flags; + flags = splhigh(); + + if ((err = open_dmap(dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, + audio_devs[dev]->dmachan1)) < 0) { + splx(flags); + 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; + /* reorganize_buffers (dev, audio_devs[dev]->dmap_out); */ + + if (audio_devs[dev]->flags & DMA_DUPLEX) { + if ((err = open_dmap(dev, OPEN_READWRITE, + audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2)) < 0) { + printf("Unable to grab DMA%d for the audio driver\n", + audio_devs[dev]->dmachan2); + close_dmap(dev, audio_devs[dev]->dmap_out, + audio_devs[dev]->dmachan1); + splx(flags); + return -(EBUSY); + } + dma_init_buffers(dev, audio_devs[dev]->dmap_in); + /* audio_devs[dev]->dmap_in->flags |= DMA_ALLOC_DONE; */ + audio_devs[dev]->dmap_in->fragment_size = audio_devs[dev]->buffsize; + /* reorganize_buffers (dev, audio_devs[dev]->dmap_in); */ + } else { + audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; + audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1; + } + + splx(flags); + return 0; } void -DMAbuf_inputintr (int dev) +DMAbuf_close_dma(int dev) { - unsigned long flags; - struct dma_buffparms *dmap = audio_devs[dev]->dmap; + DMAbuf_reset_dma(dev); + close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); - 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; + if (audio_devs[dev]->flags & DMA_DUPLEX) + close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); - 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); +void +DMAbuf_reset_dma(int dev) +{ } +#ifdef ALLOW_POLL + int -DMAbuf_open_dma (int dev) +DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait) { - unsigned long flags; - int chan = audio_devs[dev]->dmachan; + struct dma_buffparms *dmap; + u_long flags; + int revents = 0; + + dmap = audio_devs[dev]->dmap_in; + + if (events & (POLLIN | POLLRDNORM)) { + if (dmap->dma_mode != DMODE_INPUT) { + if ((audio_devs[dev]->flags & DMA_DUPLEX) && !dmap->qlen && + audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT && + audio_devs[dev]->go) { + u_long flags; + + flags = splhigh(); + + activate_recording(dev, dmap); + splx(flags); + + } + return 0; + } + if (!dmap->qlen) { + flags = splhigh(); + + selrecord(wait, &selinfo[dev]); + + splx(flags); + + return 0; + } else + revents |= events & (POLLIN | POLLRDNORM); - if (ALLOC_DMA_CHN (chan)) - { - 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); + if (events & (POLLOUT | POLLWRNORM)) { + + dmap = audio_devs[dev]->dmap_out; + if (dmap->dma_mode == DMODE_INPUT) + return 0; + + if (dmap->dma_mode == DMODE_NONE) + return ( events & (POLLOUT | POLLWRNORM)); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + + if(dmap->qlen) + return 1; + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + + splx(flags); + + return 0; + + } + if (!space_in_queue(dev)) { + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + splx(flags); - return 0; -} + } else + revents |= events & (POLLOUT | POLLWRNORM); -void -DMAbuf_close_dma (int dev) -{ - int chan = audio_devs[dev]->dmachan; - DMAbuf_reset_dma (chan); - RELEASE_DMA_CHN (chan); + } + + return (revents); } -void -DMAbuf_reset_dma (int chan) + +#ifdef amancio +int +DMAbuf_select(int dev, struct fileinfo * file, int sel_type, select_table * wait) { + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmapin = audio_devs[dev]->dmap_in; + u_long flags; + + switch (sel_type) { + case FREAD: + if (dmapin->dma_mode != DMODE_INPUT) + return 0; + + if (!dmap->qlen) { + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + splx(flags); + + return 0; + } + return 1; + break; + + case FWRITE: + if (dmap->dma_mode == DMODE_INPUT) + return 0; + + if (dmap->dma_mode == DMODE_NONE) + return 1; + + if (!space_in_queue(dev)) { + flags = splhigh(); + + selrecord(wait, &selinfo[dev]); + splx(flags); + + return 0; + } + return 1; + break; + + } + + return 0; } -/* - * 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. - */ +#endif /* ALLOW_POLL */ +#endif -#else +#else /* CONFIG_AUDIO */ /* * Stub versions if audio services not included */ int -DMAbuf_open (int dev, int mode) +DMAbuf_open(int dev, int mode) { - return RET_ERROR (ENXIO); + return -(ENXIO); } int -DMAbuf_release (int dev, int mode) +DMAbuf_release(int dev, int mode) { - return 0; + return 0; } int -DMAbuf_getwrbuffer (int dev, char **buf, int *size) +DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) { - return RET_ERROR (EIO); + return -(EIO); } int -DMAbuf_getrdbuffer (int dev, char **buf, int *len) +DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) { - return RET_ERROR (EIO); + return -(EIO); } int -DMAbuf_rmchars (int dev, int buff_no, int c) +DMAbuf_rmchars(int dev, int buff_no, int c) { - return RET_ERROR (EIO); + return -(EIO); } int -DMAbuf_start_output (int dev, int buff_no, int l) +DMAbuf_start_output(int dev, int buff_no, int l) { - return RET_ERROR (EIO); + return -(EIO); } int -DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) +DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) { - return RET_ERROR (EIO); + return -(EIO); } -long -DMAbuf_init (long mem_start) +void +DMAbuf_init() { - return mem_start; } int -DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +DMAbuf_start_dma(int dev, u_long physaddr, int count, int dma_mode) { - return RET_ERROR (EIO); + return -(EIO); } int -DMAbuf_open_dma (int chan) +DMAbuf_open_dma(int dev) { - return RET_ERROR (ENXIO); + return -(ENXIO); } void -DMAbuf_close_dma (int chan) +DMAbuf_close_dma(int dev) { - return; + return; } void -DMAbuf_reset_dma (int chan) +DMAbuf_reset_dma(int dev) { - return; + return; } void -DMAbuf_inputintr (int dev) +DMAbuf_inputintr(int dev) { - return; + return; } void -DMAbuf_outputintr (int dev, int underrun_flag) +DMAbuf_outputintr(int dev, int underrun_flag) { - return; + return; } - -#endif - -#endif +#endif /* CONFIG_AUDIO */ |