Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
new file mode 100644
index 0000000..baf4244
--- /dev/null
+++ b/sound/oss/dmabuf.c
@@ -0,0 +1,1298 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ *
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : remove {in,out}_sleep_flag. It was used for the sleeper to
+ *                   determine if it was woken up by the expiring timeout or by
+ *                   an explicit wake_up. The return value from schedule_timeout
+ *		     can be used instead; if 0, the wakeup was due to the timeout.
+ *
+ * Rob Riggs		Added persistent DMA buffers (1998/10/17)
+ */
+
+#define BE_CONSERVATIVE
+#define SAMPLE_ROUNDUP 0
+
+#include "sound_config.h"
+
+#define DMAP_FREE_ON_CLOSE      0
+#define DMAP_KEEP_ON_CLOSE      1
+extern int sound_dmap_flag;
+
+static void dma_reset_output(int dev);
+static void dma_reset_input(int dev);
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
+
+
+
+static int debugmem;    	/* switched off by default */
+static int dma_buffsize = DSP_BUFFSIZE;
+
+static long dmabuf_timeout(struct dma_buffparms *dmap)
+{
+	long tmout;
+
+	tmout = (dmap->fragment_size * HZ) / dmap->data_rate;
+	tmout += HZ / 5;	/* Some safety distance */
+	if (tmout < (HZ / 2))
+		tmout = HZ / 2;
+	if (tmout > 20 * HZ)
+		tmout = 20 * HZ;
+	return tmout;
+}
+
+static int sound_alloc_dmap(struct dma_buffparms *dmap)
+{
+	char *start_addr, *end_addr;
+	int dma_pagesize;
+	int sz, size;
+	struct page *page;
+
+	dmap->mapping_flags &= ~DMA_MAP_MAPPED;
+
+	if (dmap->raw_buf != NULL)
+		return 0;	/* Already done */
+	if (dma_buffsize < 4096)
+		dma_buffsize = 4096;
+	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	
+	/*
+	 *	Now check for the Cyrix problem.
+	 */
+	 
+	if(isa_dma_bridge_buggy==2)
+		dma_pagesize=32768;
+	 
+	dmap->raw_buf = NULL;
+	dmap->buffsize = dma_buffsize;
+	if (dmap->buffsize > dma_pagesize)
+		dmap->buffsize = dma_pagesize;
+	start_addr = NULL;
+	/*
+	 * Now loop until we get a free buffer. Try to get smaller buffer if
+	 * it fails. Don't accept smaller than 8k buffer for performance
+	 * reasons.
+	 */
+	while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
+		for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+		dmap->buffsize = PAGE_SIZE * (1 << sz);
+		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA|__GFP_NOWARN, sz);
+		if (start_addr == NULL)
+			dmap->buffsize /= 2;
+	}
+
+	if (start_addr == NULL) {
+		printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
+		return -ENOMEM;
+	} else {
+		/* make some checks */
+		end_addr = start_addr + dmap->buffsize - 1;
+
+		if (debugmem)
+			printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
+		
+		/* now check if it fits into the same dma-pagesize */
+
+		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
+			printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
+			return -EFAULT;
+		}
+	}
+	dmap->raw_buf = start_addr;
+	dmap->raw_buf_phys = virt_to_bus(start_addr);
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		SetPageReserved(page);
+	return 0;
+}
+
+static void sound_free_dmap(struct dma_buffparms *dmap)
+{
+	int sz, size;
+	struct page *page;
+	unsigned long start_addr, end_addr;
+
+	if (dmap->raw_buf == NULL)
+		return;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+		return;		/* Don't free mmapped buffer. Will use it next time */
+	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+
+	start_addr = (unsigned long) dmap->raw_buf;
+	end_addr = start_addr + dmap->buffsize;
+
+	for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
+		ClearPageReserved(page);
+
+	free_pages((unsigned long) dmap->raw_buf, sz);
+	dmap->raw_buf = NULL;
+}
+
+
+/* Intel version !!!!!!!!! */
+
+static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
+{
+	unsigned long flags;
+	int chan = dmap->dma;
+
+	/* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
+
+	flags = claim_dma_lock();
+	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);
+	release_dma_lock(flags);
+
+	return 0;
+}
+
+static void dma_init_buffers(struct dma_buffparms *dmap)
+{
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->bytes_in_use = dmap->buffsize;
+
+	dmap->dma_mode = DMODE_NONE;
+	dmap->mapping_flags = 0;
+	dmap->neutral_byte = 0x80;
+	dmap->data_rate = 8000;
+	dmap->cfrag = -1;
+	dmap->closing = 0;
+	dmap->nbufs = 1;
+	dmap->flags = DMA_BUSY;	/* Other flags off */
+}
+
+static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
+{
+	int err;
+	
+	if (dmap->flags & DMA_BUSY)
+		return -EBUSY;
+	if ((err = sound_alloc_dmap(dmap)) < 0)
+		return err;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_WARNING "Sound: DMA buffers not available\n");
+		return -ENOSPC;	/* Memory allocation failed during boot */
+	}
+	if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) {
+		printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
+		return -EBUSY;
+	}
+	dma_init_buffers(dmap);
+	spin_lock_init(&dmap->lock);
+	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;
+	dmap->max_byte_counter = 8000 * 60 * 60;
+	dmap->applic_profile = APF_NORMAL;
+	dmap->needs_reorg = 1;
+	dmap->audio_callback = NULL;
+	dmap->callback_parm = 0;
+	return 0;
+}
+
+static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
+{
+	unsigned long flags;
+	
+	if (dmap->dma >= 0) {
+		sound_close_dma(dmap->dma);
+		flags=claim_dma_lock();
+		disable_dma(dmap->dma);
+		release_dma_lock(flags);
+	}
+	if (dmap->flags & DMA_BUSY)
+		dmap->dma_mode = DMODE_NONE;
+	dmap->flags &= ~DMA_BUSY;
+	
+	if (sound_dmap_flag == DMAP_FREE_ON_CLOSE)
+		sound_free_dmap(dmap);
+}
+
+
+static unsigned int default_set_bits(int dev, unsigned int bits)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (void __user *)&bits);
+	set_fs(fs);
+	return bits;
+}
+
+static int default_set_speed(int dev, int speed)
+{
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (void __user *)&speed);
+	set_fs(fs);
+	return speed;
+}
+
+static short default_set_channels(int dev, short channels)
+{
+	int c = channels;
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+	audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (void __user *)&c);
+	set_fs(fs);
+	return c;
+}
+
+static void check_driver(struct audio_driver *d)
+{
+	if (d->set_speed == NULL)
+		d->set_speed = default_set_speed;
+	if (d->set_bits == NULL)
+		d->set_bits = default_set_bits;
+	if (d->set_channels == NULL)
+		d->set_channels = default_set_channels;
+}
+
+int DMAbuf_open(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int retval;
+	struct dma_buffparms *dmap_in = NULL;
+	struct dma_buffparms *dmap_out = NULL;
+
+	if (!adev)
+		  return -ENXIO;
+	if (!(adev->flags & DMA_DUPLEX))
+		adev->dmap_in = adev->dmap_out;
+	check_driver(adev->d);
+
+	if ((retval = adev->d->open(dev, mode)) < 0)
+		return retval;
+	dmap_out = adev->dmap_out;
+	dmap_in = adev->dmap_in;
+	if (dmap_in == dmap_out)
+		adev->flags &= ~DMA_DUPLEX;
+
+	if (mode & OPEN_WRITE) {
+		if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
+			adev->d->close(dev);
+			return retval;
+		}
+	}
+	adev->enable_bits = mode;
+
+	if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) {
+		if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
+			adev->d->close(dev);
+			if (mode & OPEN_WRITE)
+				close_dmap(adev, dmap_out);
+			return retval;
+		}
+	}
+	adev->open_mode = mode;
+	adev->go = 1;
+
+	adev->d->set_bits(dev, 8);
+	adev->d->set_channels(dev, 1);
+	adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) 
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+		       adev->dmap_out->bytes_in_use);
+	return 0;
+}
+/* MUST not hold the spinlock */
+void DMAbuf_reset(int dev)
+{
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+		dma_reset_output(dev);
+
+	if (audio_devs[dev]->open_mode & OPEN_READ)
+		dma_reset_input(dev);
+}
+
+static void dma_reset_output(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags,f ;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
+		return;
+
+	/*
+	 *	First wait until the current fragment has been played completely
+	 */
+	spin_lock_irqsave(&dmap->lock,flags);
+	adev->dmap_out->flags |= DMA_SYNCING;
+
+	adev->dmap_out->underrun_count = 0;
+	if (!signal_pending(current) && adev->dmap_out->qlen && 
+	    adev->dmap_out->underrun_count == 0){
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		interruptible_sleep_on_timeout(&adev->out_sleeper,
+					       dmabuf_timeout(dmap));
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+
+	/*
+	 *	Finally shut the device off
+	 */
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_output(dev);
+	adev->dmap_out->flags &= ~DMA_STARTED;
+	
+	f=claim_dma_lock();
+	clear_dma_ff(dmap->dma);
+	disable_dma(dmap->dma);
+	release_dma_lock(f);
+	
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_out, 0);
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+
+static void dma_reset_input(int dev)
+{
+        struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
+		adev->d->halt_io(dev);
+	else
+		adev->d->halt_input(dev);
+	adev->dmap_in->flags &= ~DMA_STARTED;
+
+	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+	dmap->byte_counter = 0;
+	reorganize_buffers(dev, adev->dmap_in, 1);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+/* MUST be called with holding the dmap->lock */
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT))
+		return;		/* Don't start DMA yet */
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		if (!(dmap->flags & DMA_STARTED)) {
+			reorganize_buffers(dev, dmap, 0);
+			if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs))
+				return;
+			if (!(dmap->flags & DMA_NODMA))
+				local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE);
+			dmap->flags |= DMA_STARTED;
+		}
+		if (dmap->counts[dmap->qhead] == 0)
+			dmap->counts[dmap->qhead] = dmap->fragment_size;
+		dmap->dma_mode = DMODE_OUTPUT;
+		adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
+				      dmap->counts[dmap->qhead], 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+}
+
+int DMAbuf_sync(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int n = 0;
+	struct dma_buffparms *dmap;
+
+	if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return 0;
+
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
+		dmap = adev->dmap_out;
+		spin_lock_irqsave(&dmap->lock,flags);
+		if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
+			DMAbuf_launch_output(dev, dmap);
+		adev->dmap_out->flags |= DMA_SYNCING;
+		adev->dmap_out->underrun_count = 0;
+		while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && 
+		       adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
+			long t = dmabuf_timeout(dmap);
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			/* FIXME: not safe may miss events */
+			t = interruptible_sleep_on_timeout(&adev->out_sleeper, t);
+			spin_lock_irqsave(&dmap->lock,flags);
+			if (!t) {
+				adev->dmap_out->flags &= ~DMA_SYNCING;
+				spin_unlock_irqrestore(&dmap->lock,flags);
+				return adev->dmap_out->qlen;
+			}
+		}
+		adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+		
+		/*
+		 * 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.
+		 */
+
+		/* still holding the lock */
+		if (adev->d->local_qlen) {   /* Device has hidden buffers */
+			while (!signal_pending(current) &&
+			       adev->d->local_qlen(dev)){
+				spin_unlock_irqrestore(&dmap->lock,flags);
+				interruptible_sleep_on_timeout(&adev->out_sleeper,
+							       dmabuf_timeout(dmap));
+				spin_lock_irqsave(&dmap->lock,flags);
+			}
+		}
+		spin_unlock_irqrestore(&dmap->lock,flags);
+	}
+	adev->dmap_out->dma_mode = DMODE_NONE;
+	return adev->dmap_out->qlen;
+}
+
+int DMAbuf_release(int dev, int mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap;
+	unsigned long flags;
+
+	dmap = adev->dmap_out;
+	if (adev->open_mode & OPEN_WRITE)
+		adev->dmap_out->closing = 1;
+
+	if (adev->open_mode & OPEN_READ){
+		adev->dmap_in->closing = 1;
+		dmap = adev->dmap_in;
+	}
+	if (adev->open_mode & OPEN_WRITE)
+		if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED))
+			if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT))
+				DMAbuf_sync(dev);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
+
+	DMAbuf_reset(dev);
+	spin_lock_irqsave(&dmap->lock,flags);
+	adev->d->close(dev);
+
+	if (adev->open_mode & OPEN_WRITE)
+		close_dmap(adev, adev->dmap_out);
+
+	if (adev->open_mode == OPEN_READ ||
+	    (adev->open_mode != OPEN_WRITE &&
+	     (adev->flags & DMA_DUPLEX)))
+		close_dmap(adev, adev->dmap_in);
+	adev->open_mode = 0;
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+/* called with dmap->lock dold */
+int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int  err;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (!(adev->enable_bits & PCM_ENABLE_INPUT))
+		return 0;
+	if (dmap->dma_mode == DMODE_OUTPUT) {	/* Direction change */
+		/* release lock - it's not recursive */
+		spin_unlock_irq(&dmap->lock);
+		DMAbuf_sync(dev);
+		DMAbuf_reset(dev);
+		spin_lock_irq(&dmap->lock);
+		dmap->dma_mode = DMODE_NONE;
+	}
+	if (!dmap->dma_mode) {
+		reorganize_buffers(dev, dmap, 1);
+		if ((err = adev->d->prepare_for_input(dev,
+				dmap->fragment_size, dmap->nbufs)) < 0)
+			return err;
+		dmap->dma_mode = DMODE_INPUT;
+	}
+	if (!(dmap->flags & DMA_ACTIVE)) {
+		if (dmap->needs_reorg)
+			reorganize_buffers(dev, dmap, 0);
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+				     dmap->fragment_size, 0);
+		dmap->flags |= DMA_ACTIVE;
+		if (adev->d->trigger)
+			adev->d->trigger(dev, adev->enable_bits * adev->go);
+	}
+	return 0;
+}
+/* aquires lock */
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = 0, n = 0;
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int go;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return -EIO;
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+	if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
+/*		  printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
+		  spin_unlock_irqrestore(&dmap->lock,flags);
+		  return -EINVAL;
+	} else while (dmap->qlen <= 0 && n++ < 10) {
+		long timeout = MAX_SCHEDULE_TIMEOUT;
+		if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return -EAGAIN;
+		}
+		if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return err;
+		}
+		/* Wait for the next block */
+
+		if (dontblock) {
+			spin_unlock_irqrestore(&dmap->lock,flags);
+			return -EAGAIN;
+		}
+		if ((go = adev->go))
+			timeout = dmabuf_timeout(dmap);
+
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		timeout = interruptible_sleep_on_timeout(&adev->in_sleeper,
+							 timeout);
+		if (!timeout) {
+			/* FIXME: include device name */
+			err = -EIO;
+			printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+			dma_reset_input(dev);
+		} else
+			err = -EINTR;
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	spin_unlock_irqrestore(&dmap->lock,flags);
+
+	if (dmap->qlen <= 0)
+		return err ? err : -EINTR;
+	*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 audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int p = dmap->counts[dmap->qhead] + c;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+/*		  printk("Sound: Can't read from mmapped device (2)\n");*/
+		return -EINVAL;
+	}
+	else if (dmap->qlen <= 0)
+		return -EIO;
+	else if (p >= dmap->fragment_size) {  /* This buffer is completely empty */
+		dmap->counts[dmap->qhead] = 0;
+		dmap->qlen--;
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+	}
+	else dmap->counts[dmap->qhead] = p;
+
+	return 0;
+}
+/* MUST be called with dmap->lock hold */
+int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
+{
+	/*
+	 *	Try to approximate the active byte position of the DMA pointer within the
+	 *	buffer area as well as possible.
+	 */
+
+	int pos;
+	unsigned long f;
+
+	if (!(dmap->flags & DMA_ACTIVE))
+		pos = 0;
+	else {
+		int chan = dmap->dma;
+		
+		f=claim_dma_lock();
+		clear_dma_ff(chan);
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		
+		pos = get_dma_residue(chan);
+		
+		pos = dmap->bytes_in_use - pos;
+
+		if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) {
+			if (direction == DMODE_OUTPUT) {
+				if (dmap->qhead == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			} else {
+				if (dmap->qtail == 0)
+					if (pos > dmap->fragment_size)
+						pos = 0;
+			}
+		}
+		if (pos < 0)
+			pos = 0;
+		if (pos >= dmap->bytes_in_use)
+			pos = 0;
+		
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+			
+		release_dma_lock(f);
+	}
+	/* printk( "%04x ",  pos); */
+
+	return pos;
+}
+
+/*
+ *	DMAbuf_start_devices() is called by the /dev/music driver to start
+ *	one or more audio devices at desired moment.
+ */
+
+void DMAbuf_start_devices(unsigned int devmask)
+{
+	struct audio_operations *adev;
+	int dev;
+
+	for (dev = 0; dev < num_audiodevs; dev++) {
+		if (!(devmask & (1 << dev)))
+			continue;
+		if (!(adev = audio_devs[dev]))
+			continue;
+		if (adev->open_mode == 0)
+			continue;
+		if (adev->go)
+			continue;
+		/* OK to start the device */
+		adev->go = 1;
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+}
+/* via poll called without a lock ?*/
+int DMAbuf_space_in_queue(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int len, max, tmp;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int lim = dmap->nbufs;
+
+	if (lim < 2)
+		lim = 2;
+
+	if (dmap->qlen >= lim)	/* No space at all */
+		return 0;
+
+	/*
+	 *	Verify that there are no more pending buffers than the limit
+	 *	defined by the process.
+	 */
+
+	max = dmap->max_fragments;
+	if (max > lim)
+		max = lim;
+	len = dmap->qlen;
+
+	if (adev->d->local_qlen) {
+		tmp = adev->d->local_qlen(dev);
+		if (tmp && len)
+			tmp--;	/* This buffer has been counted twice */
+		len += tmp;
+	}
+	if (dmap->byte_counter % dmap->fragment_size)	/* There is a partial fragment */
+		len = len + 1;
+
+	if (len >= max)
+		return 0;
+	return max - len;
+}
+/* MUST not hold the spinlock  - this function may sleep */
+static int output_sleep(int dev, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	int err = 0;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	long timeout;
+	long timeout_value;
+
+	if (dontblock)
+		return -EAGAIN;
+	if (!(adev->enable_bits & PCM_ENABLE_OUTPUT))
+		return -EAGAIN;
+
+	/*
+	 * Wait for free space
+	 */
+	if (signal_pending(current))
+		return -EINTR;
+	timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
+	if (timeout) 
+		timeout_value = dmabuf_timeout(dmap);
+	else
+		timeout_value = MAX_SCHEDULE_TIMEOUT;
+	timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper,
+						       timeout_value);
+	if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) {
+		printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+		dma_reset_output(dev);
+	} else {
+		if (signal_pending(current))
+			err = -EINTR;
+	}
+	return err;
+}
+/* called with the lock held */
+static int find_output_space(int dev, char **buf, int *size)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long active_offs;
+	long len, offs;
+	int maxfrags;
+	int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
+
+	*buf = dmap->raw_buf;
+	if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
+		return 0;
+
+#ifdef BE_CONSERVATIVE
+	active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
+#else
+	active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT);
+	/* Check for pointer wrapping situation */
+	if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
+		active_offs = 0;
+	active_offs += dmap->byte_counter;
+#endif
+
+	offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
+	if (offs < 0 || offs >= dmap->bytes_in_use) {
+		printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
+		printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+		return 0;
+	}
+	*buf = dmap->raw_buf + offs;
+
+	len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
+
+	if ((offs + len) > dmap->bytes_in_use)
+		len = dmap->bytes_in_use - offs;
+	if (len < 0) {
+		return 0;
+	}
+	if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+		len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+	*size = len & ~SAMPLE_ROUNDUP;
+	return (*size > 0);
+}
+/* aquires lock  */
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	int err = -EIO;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+/*		printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (dmap->needs_reorg)
+		reorganize_buffers(dev, dmap, 0);
+
+	if (dmap->dma_mode == DMODE_INPUT) {	/* Direction change */
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		DMAbuf_reset(dev);
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+	dmap->dma_mode = DMODE_OUTPUT;
+
+	while (find_output_space(dev, buf, size) <= 0) {
+		spin_unlock_irqrestore(&dmap->lock,flags);
+		if ((err = output_sleep(dev, dontblock)) < 0) {
+			return err;
+		}
+		spin_lock_irqsave(&dmap->lock,flags);
+	}
+
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+/* has to aquire dmap->lock */
+int DMAbuf_move_wrpointer(int dev, int l)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	unsigned long ptr;
+	unsigned long end_ptr, p;
+	int post;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	post= (dmap->flags & DMA_POST);
+	ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+	dmap->flags &= ~DMA_POST;
+	dmap->cfrag = -1;
+	dmap->user_counter += l;
+	dmap->flags |= DMA_DIRTY;
+
+	if (dmap->byte_counter >= dmap->max_byte_counter) {
+		/* Wrap the byte counters */
+		long decr = dmap->byte_counter;
+		dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+		decr -= dmap->byte_counter;
+		dmap->user_counter -= decr;
+	}
+	end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+	p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+	dmap->neutral_byte = dmap->raw_buf[p];
+
+	/* Update the fragment based bookkeeping too */
+	while (ptr < end_ptr) {
+		dmap->counts[dmap->qtail] = dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		dmap->qlen++;
+		ptr += dmap->fragment_size;
+	}
+
+	dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+	/*
+	 *	Let the low level driver perform some postprocessing to
+	 *	the written data.
+	 */
+	if (adev->d->postprocess_write)
+		adev->d->postprocess_write(dev);
+
+	if (!(dmap->flags & DMA_ACTIVE))
+		if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
+			DMAbuf_launch_output(dev, dmap);
+
+	spin_unlock_irqrestore(&dmap->lock,flags);
+	return 0;
+}
+
+int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(1) == NULL\n");
+		printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, physaddr, count, dma_mode);
+	return count;
+}
+
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
+{
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
+		printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
+		return 0;
+	}
+	if (dmap->flags & DMA_NODMA)
+		return 1;
+	if (dmap->dma < 0)
+		return 0;
+	sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
+	dmap->flags |= DMA_STARTED;
+	return count;
+}
+
+static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
+{
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (dmap->audio_callback != NULL)
+		dmap->audio_callback(dev, dmap->callback_parm);
+	wake_up(&adev->out_sleeper);
+	wake_up(&adev->poll_sleeper);
+}
+/* called with dmap->lock held in irq context*/
+static void do_outputintr(int dev, int dummy)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int this_fragment;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {	/* Virtual memory mapped access */
+		/* mmapped access */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		if (dmap->qhead == 0) {	    /* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;	/* Yes increment it (don't decrement) */
+		if (!(adev->flags & DMA_AUTOMODE))
+			dmap->flags &= ~DMA_ACTIVE;
+		dmap->counts[dmap->qhead] = dmap->fragment_size;
+		DMAbuf_launch_output(dev, dmap);
+		finish_output_interrupt(dev, dmap);
+		return;
+	}
+
+	dmap->qlen--;
+	this_fragment = dmap->qhead;
+	dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+
+	if (dmap->qhead == 0) {	/* Wrapped */
+		dmap->byte_counter += dmap->bytes_in_use;
+		if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+			long decr = dmap->byte_counter;
+			dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+			decr -= dmap->byte_counter;
+			dmap->user_counter -= decr;
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE))
+		dmap->flags &= ~DMA_ACTIVE;
+		
+	/*
+	 *	This is  dmap->qlen <= 0 except when closing when
+	 *	dmap->qlen < 0
+	 */
+	 
+	while (dmap->qlen <= -dmap->closing) {
+		dmap->underrun_count++;
+		dmap->qlen++;
+		if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) {
+			dmap->flags &= ~DMA_DIRTY;
+			memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+			       adev->dmap_out->buffsize);
+		}
+		dmap->user_counter += dmap->fragment_size;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	}
+	if (dmap->qlen > 0)
+		DMAbuf_launch_output(dev, dmap);
+	finish_output_interrupt(dev, dmap);
+}
+/* called in irq context */
+void DMAbuf_outputintr(int dev, int notify_only)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
+	struct dma_buffparms *dmap = adev->dmap_out;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+		
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+		n = 0;
+		while (dmap->qhead != pos && n++ < dmap->nbufs)
+			do_outputintr(dev, notify_only);
+	}
+	else
+		do_outputintr(dev, notify_only);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+/* called with dmap->lock held in irq context */
+static void do_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (dmap->raw_buf == NULL) {
+		printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+		return;
+	}
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+		dmap->qlen++;
+
+		if (!(adev->flags & DMA_AUTOMODE)) {
+			if (dmap->needs_reorg)
+				reorganize_buffers(dev, dmap, 0);
+			local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
+			adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+					     dmap->fragment_size, 1);
+			if (adev->d->trigger)
+				adev->d->trigger(dev, adev->enable_bits * adev->go);
+		}
+		dmap->flags |= DMA_ACTIVE;
+	} else if (dmap->qlen >= (dmap->nbufs - 1)) {
+		printk(KERN_WARNING "Sound: Recording overrun\n");
+		dmap->underrun_count++;
+
+		/* Just throw away the oldest fragment but keep the engine running */
+		dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+	} else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
+		dmap->qlen++;
+		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+		if (dmap->qtail == 0) {		/* Wrapped */
+			dmap->byte_counter += dmap->bytes_in_use;
+			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
+				long decr = dmap->byte_counter;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				decr -= dmap->byte_counter;
+				dmap->user_counter -= decr;
+			}
+		}
+	}
+	if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
+	dmap->flags |= DMA_ACTIVE;
+	if (dmap->qlen > 0)
+	{
+		wake_up(&adev->in_sleeper);
+		wake_up(&adev->poll_sleeper);
+	}
+}
+/* called in irq context */
+void DMAbuf_inputintr(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmap->lock,flags);
+
+	if (!(dmap->flags & DMA_NODMA)) {
+		int chan = dmap->dma, pos, n;
+		unsigned long f;
+		
+		f=claim_dma_lock();
+		if(!isa_dma_bridge_buggy)
+			disable_dma(dmap->dma);
+		clear_dma_ff(chan);
+		pos = dmap->bytes_in_use - get_dma_residue(chan);
+		if(!isa_dma_bridge_buggy)
+			enable_dma(dmap->dma);
+		release_dma_lock(f);
+
+		pos = pos / dmap->fragment_size;	/* Actual qhead */
+		if (pos < 0 || pos >= dmap->nbufs)
+			pos = 0;
+
+		n = 0;
+		while (dmap->qtail != pos && ++n < dmap->nbufs)
+			do_inputintr(dev);
+	} else
+		do_inputintr(dev);
+	spin_unlock_irqrestore(&dmap->lock,flags);
+}
+
+int DMAbuf_open_dma(int dev)
+{
+	/*
+	 *    NOTE!  This routine opens only the primary DMA channel (output).
+	 */
+	struct audio_operations *adev = audio_devs[dev];
+	int err;
+
+	if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0)
+		return -EBUSY;
+	dma_init_buffers(adev->dmap_out);
+	adev->dmap_out->flags |= DMA_ALLOC_DONE;
+	adev->dmap_out->fragment_size = adev->dmap_out->buffsize;
+
+	if (adev->dmap_out->dma >= 0) {
+		unsigned long flags;
+
+		flags=claim_dma_lock();
+		clear_dma_ff(adev->dmap_out->dma);
+		disable_dma(adev->dmap_out->dma);
+		release_dma_lock(flags);
+	}
+	return 0;
+}
+
+void DMAbuf_close_dma(int dev)
+{
+	close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out);
+}
+
+void DMAbuf_init(int dev, int dma1, int dma2)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/*
+	 * NOTE! This routine could be called several times.
+	 */
+
+	/* drag in audio_syms.o */
+	{
+		extern char audio_syms_symbol;
+		audio_syms_symbol = 0;
+	}
+
+	if (adev && adev->dmap_out == NULL) {
+		if (adev->d == NULL)
+			panic("OSS: audio_devs[%d]->d == NULL\n", dev);
+
+		if (adev->parent_dev) {	 /* Use DMA map of the parent dev */
+			int parent = adev->parent_dev - 1;
+			adev->dmap_out = audio_devs[parent]->dmap_out;
+			adev->dmap_in = audio_devs[parent]->dmap_in;
+		} else {
+			adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
+			adev->dmap_out->dma = dma1;
+			if (adev->flags & DMA_DUPLEX) {
+				adev->dmap_in = &adev->dmaps[1];
+				adev->dmap_in->dma = dma2;
+			}
+		}
+		/* Persistent DMA buffers allocated here */
+		if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+			if (adev->dmap_in->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_in);
+			if (adev->dmap_out->raw_buf == NULL)
+				sound_alloc_dmap(adev->dmap_out);
+		}
+	}
+}
+
+/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */
+static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
+
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLIN | POLLRDNORM;
+		return 0;
+	}
+	if (dmap->dma_mode != DMODE_INPUT) {
+		if (dmap->dma_mode == DMODE_NONE &&
+		    adev->enable_bits & PCM_ENABLE_INPUT &&
+		    !dmap->qlen && adev->go) {
+			unsigned long flags;
+			
+			spin_lock_irqsave(&dmap->lock,flags);
+			DMAbuf_activate_recording(dev, dmap);
+			spin_unlock_irqrestore(&dmap->lock,flags);
+		}
+		return 0;
+	}
+	if (!dmap->qlen)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
+
+static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	
+	if (!(adev->open_mode & OPEN_WRITE))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		if (dmap->qlen)
+			return POLLOUT | POLLWRNORM;
+		return 0;
+	}
+	if (dmap->dma_mode == DMODE_INPUT)
+		return 0;
+	if (dmap->dma_mode == DMODE_NONE)
+		return POLLOUT | POLLWRNORM;
+	if (!DMAbuf_space_in_queue(dev))
+		return 0;
+	return POLLOUT | POLLWRNORM;
+}
+
+unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	poll_wait(file, &adev->poll_sleeper, wait);
+	return poll_input(file, dev, wait) | poll_output(file, dev, wait);
+}
+
+void DMAbuf_deinit(int dev)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	/* This routine is called when driver is being unloaded */
+	if (!adev)
+		return;
+
+	/* Persistent DMA buffers deallocated here */
+	if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
+		sound_free_dmap(adev->dmap_out);
+		if (adev->flags & DMA_DUPLEX)
+			sound_free_dmap(adev->dmap_in);
+	}
+}