/*
 * sound/dmabuf.c
 * 
 * The DMA buffer manager for digitized voice applications
 * 
 * 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
 * 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 <i386/isa/sound/sound_config.h>

#include <machine/md_var.h>

#if defined(CONFIG_AUDIO) || defined(CONFIG_GUS)
#ifdef ALLOW_POLL

int
DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait);
#endif;

static void
reorganize_buffers(int dev, struct dma_buffparms * dmap);

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 int      ndmaps = 0;

#define MAX_DMAP (MAX_AUDIO_DEV*2)

static struct dma_buffparms dmaps[MAX_DMAP] = {{0}};
/*
 * Primitive way to allocate such a large array. Needs dynamic run-time
 * alloction.
 */

static int      space_in_queue(int dev);

static void     dma_reset_output(int dev);
static void     dma_reset_input(int dev);

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;
    }

    bsz &= ~0x03;		/* Force size which is multiple of 4 bytes */
#ifdef OS_DMA_ALIGN_CHECK
    OS_DMA_ALIGN_CHECK(bsz);
#endif

    n = dsp_dev->buffsize / bsz;

    if (n > MAX_SUB_BUFFERS)
	n = MAX_SUB_BUFFERS;

    if (n > dmap->max_fragments)
	n = dmap->max_fragments;
    dmap->nbufs = n;
    dmap->bytes_in_use = n * bsz;

    for (i = 0; i < dmap->nbufs; i++) {
	dmap->counts[i] = 0;
    }

    if (dmap->raw_buf)
	fillw (dmap->neutral_byte, dmap->raw_buf,
	       dmap->bytes_in_use/2);

    dmap->flags |= DMA_ALLOC_DONE;

}

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;
    }

    dmap->flags = DMA_BUSY;	/* Other flags off */
    dmap->qlen = dmap->qhead = dmap->qtail = 0;
    dmap->nbufs = 1;
    dmap->bytes_in_use = audio_devs[dev]->buffsize;

    dmap->dma_mode = DMODE_NONE;
    dmap->mapping_flags = 0;
    dmap->neutral_byte = 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
    {
	int             err;

	if ((err = sound_alloc_dmap(dev, dmap, chan)) < 0)
	    return err;
    }
#endif

    if (dmap->raw_buf == NULL)
	return -(ENOSPC);	/* Memory allocation failed during boot */

    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->dma_chan = chan;
    dma_init_buffers(dev, dmap);

    return 0;
}

static void
close_dmap(int dev, struct dma_buffparms * dmap, int chan)
{
    if (dmap->flags & DMA_BUSY)
	dmap->dma_mode = DMODE_NONE;
    dmap->flags &= ~DMA_BUSY;
    isa_dma_release(dmap->dma_chan);
#ifdef RUNTIME_DMA_ALLOC
    sound_free_dmap(dev, dmap);
#endif
}

int
DMAbuf_open(int dev, int mode)
{
    int             retval;
    struct dma_buffparms *dmap_in = NULL;
    struct dma_buffparms *dmap_out = NULL;

    if (dev >= num_audiodevs) {
	printf("PCM device %d not installed.\n", dev);
	return -(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_out = audio_devs[dev]->dmap_out;
    dmap_in = audio_devs[dev]->dmap_in;

    if ((retval = open_dmap(dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0) {
	audio_devs[dev]->close(dev);
	return retval;
    }
    audio_devs[dev]->enable_bits = mode;

    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;

    in_sleep_flag[dev].aborting = 0;
    in_sleep_flag[dev].mode = WK_NONE;

    out_sleep_flag[dev].aborting = 0;
    out_sleep_flag[dev].mode = WK_NONE;

    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;
}

static void
dma_reset(int dev)
{
    u_long   flags;

    flags = splhigh();
    audio_devs[dev]->reset(dev);
    splx(flags);

    dma_reset_output(dev);

    if (audio_devs[dev]->flags & DMA_DUPLEX)
	dma_reset_input(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)
{
    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;
	}

#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;

	  }
	}


	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);

	    }
	}
	splx(flags);
    }
    return audio_devs[dev]->dmap_out->qlen;
}

int
DMAbuf_release(int dev, int mode)
{
    u_long   flags;

    if (!((out_sleep_flag[dev].aborting))
	    && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) {
	dma_sync(dev);
    }
    flags = splhigh();

    audio_devs[dev]->close(dev);

    close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);

    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;

    splx(flags);

    return 0;
}

static int
activate_recording(int dev, struct dma_buffparms * dmap)
{
    if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
	return 0;

    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) {
	int             err;

	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;
}

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 = activate_recording(dev, dmap)) < 0) {
	    splx(flags);
	    return err;
	}
	/* Wait for the next block */

	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;

	{
	    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;
    }
    splx(flags);

    if (!dmap->qlen)
	return -(err);

    *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
    *len = dmap->fragment_size - dmap->counts[dmap->qhead];

    return dmap->qhead;
}

int
DMAbuf_rmchars(int dev, int buff_no, int c)
{
    struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;

    int             p = dmap->counts[dmap->qhead] + c;

#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)
{
    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);

    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 (!(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
    {
      pos = dmap->fragment_size - pos;
      if (pos < 0)
        return 0;
      return pos;
    }


}

int
DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local)
{
    struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
    struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;

    switch (cmd) {


    case SNDCTL_DSP_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_out->flags & DMA_ALLOC_DONE))
	    reorganize_buffers(dev, dmap_out);

	return *(int *) arg = dmap_out->fragment_size;
	break;

    case SNDCTL_DSP_SETBLKSIZE:
	{
	    int             size = (*(int *) arg);

	    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 (audio_devs[dev]->flags & DMA_DUPLEX) {
		    dmap_in->fragment_size = size;
		    dmap_in->max_fragments = 8888;
		}
		return 0;

	    } else
		return -(EINVAL);	/* Too late to change */

	}
	break;

    case SNDCTL_DSP_SUBDIVIDE:
	{
	    int             fact = (*(int *) arg);
	    int             ret;

	    ret = dma_subdivide(dev, dmap_out, arg, fact);
	    if (ret < 0)
		return ret;

	    if (audio_devs[dev]->flags & DMA_DUPLEX)
		ret = dma_subdivide(dev, dmap_in, arg, fact);

	    return ret;
	}
	break;

    case SNDCTL_DSP_SETFRAGMENT:
	{
	    int             fact = (*(int *) arg);
	    int             ret;

	    ret = dma_set_fragment(dev, dmap_out, arg, fact);
	    if (ret < 0)
		return ret;

	    if (audio_devs[dev]->flags & DMA_DUPLEX)
		ret = dma_set_fragment(dev, dmap_in, arg, fact);

	    return ret;
	}
	break;

    case SNDCTL_DSP_GETISPACE:
    case SNDCTL_DSP_GETOSPACE:
	if (!local)
	    return -(EINVAL);
	else {
	    struct dma_buffparms *dmap = dmap_out;

	    audio_buf_info *info = (audio_buf_info *) arg;

	    if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
		dmap = dmap_in;

#ifdef ALLOW_BUFFER_MAPPING
	    if (dmap->mapping_flags & DMA_MAP_MAPPED)
		return -(EINVAL);
#endif

	    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 (info->fragments < 0)
		info->fragments = 0;
	    else if (info->fragments > dmap->nbufs)
		info->fragments = dmap->nbufs;

	    info->fragsize = dmap->fragment_size;
	    info->bytes = info->fragments * dmap->fragment_size;

	    if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
		info->bytes -= dmap->counts[dmap->qhead];
	}
	return 0;

    case SNDCTL_DSP_SETTRIGGER:
	{
	    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);
    }
}

/*
 * 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)
{
    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;
}

int
DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
{
    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

    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, dmap);

    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;
    }
    flags = splhigh();

    abort = 0;
    while (!space_in_queue(dev) && !abort) {
	int             timeout;

	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;

	{
	    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;
	}
	}
    }
    splx(flags);

    if (!space_in_queue(dev)) {
	return -(err);	/* Caught a signal ? */
    }
    *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
    *size = dmap->fragment_size;
    dmap->counts[dmap->qtail] = 0;
    return dmap->qtail;
}

int
DMAbuf_start_output(int dev, int buff_no, int l)
{
    struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;

    /*
     * Bypass buffering if using mmaped access
     */

#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
    {

	if (buff_no != dmap->qtail)
	    printf("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);

	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->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->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, u_long physaddr, int count, int dma_mode)
{
    int             chan;
    struct dma_buffparms *dmap;

    if (dma_mode == 1) {
	chan = audio_devs[dev]->dmachan1;
	dmap = audio_devs[dev]->dmap_out;

    } else {
	chan = audio_devs[dev]->dmachan2;
	dmap = audio_devs[dev]->dmap_in;
    }

    /*
     * The count must be one less than the actual size. This is handled
     * by set_dma_addr()
     */

#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) (void *) (uintptr_t) dmap->raw_buf_phys,
	     dmap->bytes_in_use, chan);

    } else
#endif
    {
	isa_dmastart((dma_mode == 0) ? B_READ : B_WRITE,
		 (caddr_t) (void *) (uintptr_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 its 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
    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);

	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
	    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();

    if ((out_sleep_flag[dev].mode & WK_SLEEP)) {
	out_sleep_flag[dev].mode = WK_WAKEUP;
	wakeup(out_sleeper[dev]);
    }

    if(selinfo[dev].si_pid) {
	selwakeup(&selinfo[dev]);
    }

    splx(flags);
}

void
DMAbuf_inputintr(int dev)
{
    u_long   flags;
    struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;

    dmap->byte_counter += dmap->fragment_size;

#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->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]->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->flags |= DMA_ACTIVE;
    }

    flags = splhigh();
    if ((in_sleep_flag[dev].mode & WK_SLEEP)) {
	in_sleep_flag[dev].mode = WK_WAKEUP;
	wakeup(in_sleeper[dev]);
    }
    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_close_dma(int dev)
{
    DMAbuf_reset_dma(dev);
    close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);

    if (audio_devs[dev]->flags & DMA_DUPLEX)
	close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);

}

void
DMAbuf_reset_dma(int dev)
{
}

#ifdef ALLOW_POLL

int
DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait)
{
    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 (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);

	} else 
	  revents |= events & (POLLOUT | POLLWRNORM);


    }

    return (revents);
}


#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;
}

#endif				/* ALLOW_POLL */
#endif

#else				/* CONFIG_AUDIO */
/*
 * Stub versions if audio services not included
 */

int
DMAbuf_open(int dev, int mode)
{
    return -(ENXIO);
}

int
DMAbuf_release(int dev, int mode)
{
    return 0;
}

int
DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
{
    return -(EIO);
}

int
DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
{
    return -(EIO);
}

int
DMAbuf_rmchars(int dev, int buff_no, int c)
{
    return -(EIO);
}

int
DMAbuf_start_output(int dev, int buff_no, int l)
{
    return -(EIO);
}

int
DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local)
{
    return -(EIO);
}

void
DMAbuf_init()
{
}

int
DMAbuf_start_dma(int dev, u_long physaddr, int count, int dma_mode)
{
    return -(EIO);
}

int
DMAbuf_open_dma(int dev)
{
    return -(ENXIO);
}

void
DMAbuf_close_dma(int dev)
{
    return;
}

void
DMAbuf_reset_dma(int dev)
{
    return;
}

void
DMAbuf_inputintr(int dev)
{
    return;
}

void
DMAbuf_outputintr(int dev, int underrun_flag)
{
    return;
}
#endif	/* CONFIG_AUDIO */
