patch-2.4.8 linux/drivers/sound/emu10k1/audio.c

Next file: linux/drivers/sound/emu10k1/cardmi.c
Previous file: linux/drivers/sound/emu10k1/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.7/linux/drivers/sound/emu10k1/audio.c linux/drivers/sound/emu10k1/audio.c
@@ -48,6 +48,8 @@
 #include "recmgr.h"
 #include "irqmgr.h"
 #include "audio.h"
+#include "8010.h"
+#include "passthrough.h"
 
 static void calculate_ofrag(struct woinst *);
 static void calculate_ifrag(struct wiinst *);
@@ -167,6 +169,18 @@
 		return -ENXIO;
 	}
 
+	if (woinst->format.passthrough) {
+		int r;
+		
+		woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2;
+		woinst->buffer.numfrags = PT_BLOCKCOUNT;
+		calculate_ofrag(woinst);
+		
+		r = emu10k1_pt_write(file, buffer, count);
+		spin_unlock_irqrestore(&woinst->lock, flags);
+		return r;
+	}
+
 	if (woinst->state == WAVE_STATE_CLOSED) {
 		calculate_ofrag(woinst);
 
@@ -188,6 +202,11 @@
 	spin_unlock_irqrestore(&woinst->lock, flags);
 
 	ret = 0;
+	if (count % woinst->format.bytespersample)
+                return -EINVAL;
+
+	count /= woinst->num_voices;
+
 	while (count > 0) {
 		u32 bytestocopy;
 
@@ -206,8 +225,8 @@
 			emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy);
 
 			count -= bytestocopy;
-			buffer += bytestocopy;
-			ret += bytestocopy;
+			buffer += bytestocopy * woinst->num_voices;
+			ret += bytestocopy * woinst->num_voices;
 
 			spin_lock_irqsave(&woinst->lock, flags);
 			woinst->total_copied += bytestocopy;
@@ -267,14 +286,6 @@
 			spin_lock_irqsave(&woinst->lock, flags);
 
 			if (woinst->state & WAVE_STATE_OPEN) {
-				if (woinst->mmapped) {
-					int i;
-
-					/* Undo marking the pages as reserved */
-					for (i = 0; i < woinst->buffer.pages; i++)
-						mem_map_unreserve(virt_to_page(woinst->buffer.addr[i]));
-				}
-
 				emu10k1_waveout_close(wave_dev);
 			}
 
@@ -289,8 +300,9 @@
 		if (file->f_mode & FMODE_READ) {
 			spin_lock_irqsave(&wiinst->lock, flags);
 
-			if (wiinst->state & WAVE_STATE_OPEN)
+			if (wiinst->state & WAVE_STATE_OPEN) {
 				emu10k1_wavein_close(wave_dev);
+			}
 
 			wiinst->mmapped = 0;
 			wiinst->total_recorded = 0;
@@ -316,15 +328,6 @@
 						interruptible_sleep_on(&woinst->wait_queue);
 						spin_lock_irqsave(&woinst->lock, flags);
 					}
-
-				if (woinst->mmapped) {
-					int i;
-
-					/* Undo marking the pages as reserved */
-					for (i = 0; i < woinst->buffer.pages; i++)
-						mem_map_unreserve(virt_to_page(woinst->buffer.addr[i]));
-				}
-
 				emu10k1_waveout_close(wave_dev);
 			}
 
@@ -339,8 +342,9 @@
 		if (file->f_mode & FMODE_READ) {
 			spin_lock_irqsave(&wiinst->lock, flags);
 
-			if (wiinst->state & WAVE_STATE_OPEN)
+			if (wiinst->state & WAVE_STATE_OPEN) {
 				emu10k1_wavein_close(wave_dev);
+			}
 
 			wiinst->mmapped = 0;
 			wiinst->total_recorded = 0;
@@ -490,7 +494,6 @@
 					spin_unlock_irqrestore(&wiinst->lock, flags);
 					return -EINVAL;
 				}
-
 				val = wiinst->format.channels;
 
 				spin_unlock_irqrestore(&wiinst->lock, flags);
@@ -532,9 +535,13 @@
 
 		if (file->f_mode & FMODE_READ)
 			val = AFMT_S16_LE;
-		else if (file->f_mode & FMODE_WRITE)
+		else if (file->f_mode & FMODE_WRITE) {
 			val = AFMT_S16_LE | AFMT_U8;
-
+			if (emu10k1_find_control_gpr(&wave_dev->card->mgr,
+			    			     wave_dev->card->pt.patch_name, 
+			    			     wave_dev->card->pt.enable_gpr_name) >= 0)
+				val |= AFMT_AC3;
+		}
 		return put_user(val, (int *) arg);
 
 	case SNDCTL_DSP_SETFMT:	/* Same as SNDCTL_DSP_SAMPLESIZE */
@@ -552,17 +559,17 @@
 				spin_lock_irqsave(&wiinst->lock, flags);
 
 				format = wiinst->format;
-				format.bitsperchannel = val;
+				format.id = val;
 
 				if (emu10k1_wavein_setformat(wave_dev, &format) < 0) {
 					spin_unlock_irqrestore(&wiinst->lock, flags);
 					return -EINVAL;
 				}
 
-				val = wiinst->format.bitsperchannel;
+				val = wiinst->format.id;
 
 				spin_unlock_irqrestore(&wiinst->lock, flags);
-				DPD(2, "set recording sample size -> %d\n", val);
+				DPD(2, "set recording format -> %d\n", val);
 			}
 
 			if (file->f_mode & FMODE_WRITE) {
@@ -571,27 +578,27 @@
 				spin_lock_irqsave(&woinst->lock, flags);
 
 				format = woinst->format;
-				format.bitsperchannel = val;
+				format.id = val;
 
 				if (emu10k1_waveout_setformat(wave_dev, &format) < 0) {
 					spin_unlock_irqrestore(&woinst->lock, flags);
 					return -EINVAL;
 				}
 
-				val = woinst->format.bitsperchannel;
+				val = woinst->format.id;
 
 				spin_unlock_irqrestore(&woinst->lock, flags);
-				DPD(2, "set playback sample size -> %d\n", val);
+				DPD(2, "set playback format -> %d\n", val);
 			}
 
-			return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg);
+			return put_user(val, (int *) arg);
 		} else {
 			if (file->f_mode & FMODE_READ)
-				val = wiinst->format.bitsperchannel;
+				val = wiinst->format.id;
 			else if (file->f_mode & FMODE_WRITE)
-				val = woinst->format.bitsperchannel;
+				val = woinst->format.id;
 
-			return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg);
+			return put_user(val, (int *) arg);
 		}
 		break;
 
@@ -602,7 +609,7 @@
 		else if (file->f_mode & FMODE_WRITE)
 			val = woinst->format.bitsperchannel;
 
-		return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg);
+		return put_user(val, (int *) arg);
 
 	case SOUND_PCM_READ_RATE:
 
@@ -705,9 +712,10 @@
 			}
 			spin_unlock_irqrestore(&woinst->lock, flags);
 
-			info.fragstotal = woinst->buffer.numfrags;
-			info.fragments = info.bytes / woinst->buffer.fragment_size;
-			info.fragsize = woinst->buffer.fragment_size;
+			info.bytes *= woinst->num_voices;
+			info.fragsize = woinst->buffer.fragment_size * woinst->num_voices;
+			info.fragstotal = woinst->buffer.numfrags * woinst->num_voices;
+			info.fragments = info.bytes / info.fragsize;
 
 			if (copy_to_user((int *) arg, &info, sizeof(info)))
 				return -EFAULT;
@@ -763,6 +771,7 @@
 		} else
 			val = 0;
 
+		val *= woinst->num_voices;
 		spin_unlock_irqrestore(&woinst->lock, flags);
 
 		return put_user(val, (int *) arg);
@@ -790,6 +799,9 @@
 				cinfo.blocks = 0;
 			}
 
+			if(wiinst->mmapped)
+				wiinst->buffer.bytestocopy %= wiinst->buffer.fragment_size;
+
 			spin_unlock_irqrestore(&wiinst->lock, flags);
 
 			if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo)))
@@ -808,19 +820,31 @@
 
 			spin_lock_irqsave(&woinst->lock, flags);
 
-			if (woinst->state & WAVE_STATE_OPEN) {
-				emu10k1_waveout_update(woinst);
+			if (woinst->state & WAVE_STATE_OPEN || 
+			    (woinst->format.passthrough && wave_dev->card->pt.state)) {
+				int num_fragments;
+				if (woinst->format.passthrough) {
+					emu10k1_pt_waveout_update(wave_dev);
+					cinfo.bytes = woinst->total_played;
+				} else {
+					emu10k1_waveout_update(woinst);
+					cinfo.bytes = woinst->total_played;
+				}
 				cinfo.ptr = woinst->buffer.hw_pos;
-				cinfo.bytes = cinfo.ptr + woinst->total_played - woinst->total_played % woinst->buffer.size;
-				cinfo.blocks = cinfo.bytes / woinst->buffer.fragment_size - woinst->blocks;
-				woinst->blocks = cinfo.bytes / woinst->buffer.fragment_size;
+				num_fragments = cinfo.bytes / woinst->buffer.fragment_size;
+				cinfo.blocks = num_fragments - woinst->blocks;
+				woinst->blocks = num_fragments;
+
+				cinfo.bytes *= woinst->num_voices;
+				cinfo.ptr *= woinst->num_voices;
 			} else {
 				cinfo.ptr = 0;
 				cinfo.bytes = 0;
 				cinfo.blocks = 0;
 			}
-			if(woinst->mmapped)
-				woinst->buffer.bytestocopy %= woinst->buffer.fragment_size;
+
+			if (woinst->mmapped)
+				woinst->buffer.free_bytes %= woinst->buffer.fragment_size;
 
 			spin_unlock_irqrestore(&woinst->lock, flags);
 
@@ -836,7 +860,7 @@
 			spin_lock_irqsave(&woinst->lock, flags);
 
 			calculate_ofrag(woinst);
-			val = woinst->buffer.fragment_size;
+			val = woinst->buffer.fragment_size * woinst->num_voices;
 
 			spin_unlock_irqrestore(&woinst->lock, flags);
 		}
@@ -878,13 +902,14 @@
 		if (get_user(val, (int *) arg))
 			return -EFAULT;
 
-		DPD(2, "val is 0x%x\n", val);
+		DPD(2, "val is %#x\n", val);
 
 		if (val == 0)
 			return -EIO;
 
 		if (file->f_mode & FMODE_WRITE) {
-			if (woinst->state & WAVE_STATE_OPEN)
+			/* digital pass-through fragment count and size are fixed values */
+			if (woinst->state & WAVE_STATE_OPEN || woinst->format.passthrough)
 				return -EINVAL;	/* too late to change */
 
 			woinst->buffer.ossfragshift = val & 0xffff;
@@ -905,126 +930,175 @@
 		{
 			copr_buffer *buf;
 			u32 i;
-			int ret = -EFAULT;
 
-			DPF(2, "SNDCTL_COPR_LOAD:\n");
-			
+			DPF(4, "SNDCTL_COPR_LOAD:\n");
+
 			buf = kmalloc(sizeof(copr_buffer), GFP_KERNEL);
-			if(buf == NULL)
+			if (!buf)
 				return -ENOMEM;
 
-			if (copy_from_user(buf, (copr_buffer *) arg, sizeof(buf)))
-				goto fail;
+			if (copy_from_user(buf, (copr_buffer *) arg, sizeof(copr_buffer))) {
+				kfree (buf);
+				return -EFAULT;
+			}
 
-			if ((buf->command != 1) && (buf->command != 2))
-			{
-				ret = -EINVAL;
-				goto fail;
-			}
-
-			if ((buf->offs < 0x100)
-			    || (buf->offs < 0x000)
-			    || (buf->offs + buf->len > 0x800) || (buf->len > 1000))
-			{
-				ret = -EINVAL;
-				goto fail;
+			if ((buf->command != CMD_READ) && (buf->command != CMD_WRITE)) {
+				kfree (buf);
+				return -EINVAL;
+			}
+#ifdef DBGEMU
+			if ( (buf->offs < 0) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) {
+#else
+			if ( ((buf->offs < 0x100 ) || (buf->offs + buf->len > 0x800) || (buf->len > 1000))
+			     && !( ( buf->offs == DBG) && (buf->len ==1) )){
+#endif	
+				kfree(buf);
+				return -EINVAL;
 			}
-			if (buf->command == 1) {
+
+			if (buf->command == CMD_READ) {
 				for (i = 0; i < buf->len; i++)
 					((u32 *) buf->data)[i] = sblive_readptr(wave_dev->card, buf->offs + i, 0);
 
-				if (copy_to_user((copr_buffer *) arg, buf, sizeof(buf)))
-					goto fail;
+				if (copy_to_user((copr_buffer *) arg, buf, sizeof(copr_buffer))) {
+					kfree(buf);
+					return -EFAULT;
+				}
 			} else {
 				for (i = 0; i < buf->len; i++)
 					sblive_writeptr(wave_dev->card, buf->offs + i, 0, ((u32 *) buf->data)[i]);
 			}
-			ret = 0;
-		fail:			
-			kfree(buf);
-			return ret;
+
+			kfree (buf);
+			break;
 		}
 
 	default:		/* Default is unrecognized command */
-		DPD(2, "default: 0x%x\n", cmd);
+		DPD(2, "default: %#x\n", cmd);
 		return -EINVAL;
 	}
 	return 0;
 }
 
+static struct page *emu10k1_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access)
+{
+	struct emu10k1_wavedevice *wave_dev = vma->vm_private_data;
+	struct woinst *woinst = wave_dev->woinst;
+	struct wiinst *wiinst = wave_dev->wiinst;
+	struct page *dmapage;
+	unsigned long pgoff;
+	int rd, wr;
+
+	if (address > vma->vm_end) {
+		DPF(2, "EXIT, returning NOPAGE_SIGBUS\n");
+		return NOPAGE_SIGBUS; /* Disallow mremap */
+	}
+
+	pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT);
+	if (woinst != NULL)
+		wr = woinst->mmapped;
+	else
+		wr = 0;
+
+	if (wiinst != NULL)
+		rd = wiinst->mmapped;
+	else
+		rd = 0;
+
+	/* if full-duplex (read+write) and we have two sets of bufs,
+	* then the playback buffers come first, sez soundcard.c */
+	if (wr) {
+		if (pgoff >= woinst->buffer.pages) {
+			pgoff -= woinst->buffer.pages;
+			dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
+		} else
+			dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]);
+	} else {
+		dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
+	}
+
+	get_page (dmapage);
+	return dmapage;
+}
+
+struct vm_operations_struct emu10k1_mm_ops = {
+	nopage:         emu10k1_mm_nopage,
+};
+
 static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
+	unsigned long maxsize, size, offset, pgoffset;
+	struct woinst *woinst = NULL;
+	struct wiinst *wiinst = NULL;
+	unsigned long flags;
 
 	DPF(2, "emu10k1_audio_mmap()\n");
 
-	if (vma_get_pgoff(vma) != 0)
-		return -ENXIO;
-
-	lock_kernel();
-
+	maxsize = 0;
 	if (vma->vm_flags & VM_WRITE) {
-		struct woinst *woinst = wave_dev->woinst;
-		u32 size;
-		unsigned long flags;
-		int i;
+		woinst = wave_dev->woinst;
 
 		spin_lock_irqsave(&woinst->lock, flags);
 
+		/* No m'mapping possible for multichannel */
+		if (woinst->num_voices > 1) {
+			spin_unlock_irqrestore(&woinst->lock, flags);
+                	return -EINVAL;
+		}
+
 		if (woinst->state == WAVE_STATE_CLOSED) {
 			calculate_ofrag(woinst);
 
 			if (emu10k1_waveout_open(wave_dev) < 0) {
 				spin_unlock_irqrestore(&woinst->lock, flags);
 				ERROR();
-				unlock_kernel();
 				return -EINVAL;
 			}
-
-			/* Now mark the pages as reserved, otherwise remap_page_range doesn't do what we want */
-			for (i = 0; i < woinst->buffer.pages; i++)
-				mem_map_reserve(virt_to_page(woinst->buffer.addr[i]));
-		}
-
-		size = vma->vm_end - vma->vm_start;
-
-		if (size > (PAGE_SIZE * woinst->buffer.pages)) {
-			spin_unlock_irqrestore(&woinst->lock, flags);
-			unlock_kernel();
-			return -EINVAL;
-		}
-
-		for (i = 0; i < woinst->buffer.pages; i++) {
-			if (remap_page_range(vma->vm_start + (i * PAGE_SIZE), virt_to_phys(woinst->buffer.addr[i]), PAGE_SIZE, vma->vm_page_prot)) {
-				spin_unlock_irqrestore(&woinst->lock, flags);
-				unlock_kernel();
-				return -EAGAIN;
-			}
 		}
 
 		woinst->mmapped = 1;
-
+		maxsize += woinst->buffer.pages * PAGE_SIZE;
 		spin_unlock_irqrestore(&woinst->lock, flags);
 	}
 
 	if (vma->vm_flags & VM_READ) {
-		struct wiinst *wiinst = wave_dev->wiinst;
-		unsigned long flags;
+		wiinst = wave_dev->wiinst;
 
 		spin_lock_irqsave(&wiinst->lock, flags);
+		if (wiinst->state == WAVE_STATE_CLOSED) {
+			calculate_ifrag(wiinst);
+
+			if (emu10k1_wavein_open(wave_dev) < 0) {
+				spin_unlock_irqrestore(&wiinst->lock, flags);
+				ERROR();
+				return -EINVAL;
+			}
+		}
+
 		wiinst->mmapped = 1;
+		maxsize += wiinst->buffer.pages * PAGE_SIZE;
 		spin_unlock_irqrestore(&wiinst->lock, flags);
 	}
 
-	unlock_kernel();
+	size = vma->vm_end - vma->vm_start;
+	pgoffset = vma->vm_pgoff;
+	offset = pgoffset << PAGE_SHIFT;
 
+	if (offset + size > maxsize)
+		return -EINVAL;
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_ops = &emu10k1_mm_ops;
+	vma->vm_private_data = wave_dev;
+	
 	return 0;
 }
 
 static int emu10k1_audio_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
-	struct emu10k1_card *card=NULL;
+	struct emu10k1_card *card = NULL;
 	struct list_head *entry;
 	struct emu10k1_wavedevice *wave_dev;
 
@@ -1035,17 +1109,19 @@
 	list_for_each(entry, &emu10k1_devs) {
 		card = list_entry(entry, struct emu10k1_card, list);
 
-		if (!((card->audio_num ^ minor) & ~0xf) || !((card->audio1_num ^ minor) & ~0xf))
-			break;
+		if (!((card->audio_dev ^ minor) & ~0xf) || !((card->audio_dev1 ^ minor) & ~0xf))
+			goto match;
 	}
 
-	if (entry == &emu10k1_devs)
-		return -ENODEV;
+	return -ENODEV;
+
+match:
 
-	if ((wave_dev = (struct emu10k1_wavedevice *)
-	     kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL)) == NULL) {
+	wave_dev = (struct emu10k1_wavedevice *) kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL);
+
+	if (wave_dev == NULL) { 
 		ERROR();
-		return -EINVAL;
+		return -ENOMEM;
 	}
 
 	wave_dev->card = card;
@@ -1067,16 +1143,19 @@
 
 		switch (wiinst->recsrc) {
 		case WAVERECORD_AC97:
+			wiinst->format.id = AFMT_S16_LE;
 			wiinst->format.samplingrate = 8000;
 			wiinst->format.bitsperchannel = 16;
 			wiinst->format.channels = 1;
 			break;
 		case WAVERECORD_MIC:
+			wiinst->format.id = AFMT_S16_LE;
 			wiinst->format.samplingrate = 8000;
 			wiinst->format.bitsperchannel = 16;
 			wiinst->format.channels = 1;
 			break;
 		case WAVERECORD_FX:
+			wiinst->format.id = AFMT_S16_LE;
 			wiinst->format.samplingrate = 48000;
 			wiinst->format.bitsperchannel = 16;
 			wiinst->format.channels = hweight32(wiinst->fxwc);
@@ -1105,6 +1184,7 @@
 
 	if (file->f_mode & FMODE_WRITE) {
 		struct woinst *woinst;
+		int i;
 
 		if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) {
 			ERROR();
@@ -1114,6 +1194,7 @@
 		if (wave_dev->wiinst != NULL) {
 			woinst->format = wave_dev->wiinst->format;
 		} else {
+			woinst->format.id = AFMT_U8;
 			woinst->format.samplingrate = 8000;
 			woinst->format.bitsperchannel = 8;
 			woinst->format.channels = 1;
@@ -1124,7 +1205,13 @@
 		woinst->buffer.fragment_size = 0;
 		woinst->buffer.ossfragshift = 0;
 		woinst->buffer.numfrags = 0;
-		woinst->device = (card->audio1_num == minor);
+		woinst->device = (card->audio_dev1 == minor);
+		woinst->timer.state = TIMER_STATE_UNINSTALLED;
+		woinst->num_voices = 1;
+		for (i = 0; i < WAVEOUT_MAXVOICES; i++) {
+			woinst->voice[i].usage = VOICE_USAGE_FREE;
+			woinst->buffer.mem[i].emupageindex = -1;
+		}
 
 		init_waitqueue_head(&woinst->wait_queue);
 
@@ -1137,45 +1224,6 @@
 		wave_dev->woinst = woinst;
 		emu10k1_waveout_setformat(wave_dev, &woinst->format);
 
-#ifdef PRIVATE_PCM_VOLUME
-		{
-			int i;
-			int j = -1;
-
-			/*
-			 * find out if we've already been in this table
-			 * xmms reopens dsp on every move of slider
-			 * this way we keep the same local pcm for such
-			 * process
-			 */
-			for (i = 0; i < MAX_PCM_CHANNELS; i++) {
-				if (sblive_pcm_volume[i].files == current->files)
-					break;
-				// here we should select last used memeber
-				// improve me in case its not sufficient
-				if (j < 0 && !sblive_pcm_volume[i].opened)
-					j = i;
-			}
-			// current task not found
-			if (i == MAX_PCM_CHANNELS) {
-				// add new entry
-				if (j < 0)
-					printk(KERN_WARNING "emu10k1: too many writters!\n");
-				i = (j >= 0) ? j : 0;
-				DPD(2, "new pcm private %p\n", current->files);
-				sblive_pcm_volume[i].files = current->files;
-				sblive_pcm_volume[i].mixer = pcm_last_mixer;
-				sblive_pcm_volume[i].attn_l = 0;
-				sblive_pcm_volume[i].attn_r = 0;
-				sblive_pcm_volume[i].channel_l = NUM_G;
-				sblive_pcm_volume[i].channel_r = NUM_G;
-			} else
-				DPD(2, "old pcm private %p  0x%x\n", current->files,
-				    sblive_pcm_volume[i].mixer);
-
-			sblive_pcm_volume[i].opened++;
-		}
-#endif
 	}
 
 	file->private_data = (void *) wave_dev;
@@ -1189,7 +1237,6 @@
 	struct emu10k1_card *card;
 	unsigned long flags;
 
-	lock_kernel();
 	card = wave_dev->card;
 
 	DPF(2, "emu10k1_audio_release()\n");
@@ -1199,6 +1246,9 @@
 
 		spin_lock_irqsave(&woinst->lock, flags);
 
+		if (woinst->format.passthrough && card->pt.state != PT_STATE_INACTIVE)
+                        emu10k1_pt_stop(card);
+
 		if (woinst->state & WAVE_STATE_OPEN) {
 			if (woinst->state & WAVE_STATE_STARTED) {
 				if (!(file->f_flags & O_NONBLOCK)) {
@@ -1211,31 +1261,9 @@
 					}
 				}
 			}
-
-			if (woinst->mmapped) {
-				int i;
-
-				/* Undo marking the pages as reserved */
-				for (i = 0; i < woinst->buffer.pages; i++)
-					mem_map_unreserve(virt_to_page(woinst->buffer.addr[i]));
-			}
-
 			emu10k1_waveout_close(wave_dev);
 		}
-#ifdef PRIVATE_PCM_VOLUME
-		{
-			int i;
 
-			/* mark as closed
-			 * NOTE: structure remains unchanged for next reopen */
-			for (i = 0; i < MAX_PCM_CHANNELS; i++) {
-				if (sblive_pcm_volume[i].files == current->files) {
-					sblive_pcm_volume[i].opened--;
-					break;
-				}
-			}
-		}
-#endif
 		spin_unlock_irqrestore(&woinst->lock, flags);
 		/* wait for the tasklet (bottom-half) to finish */
 		tasklet_unlock_wait(&woinst->timer.tasklet);
@@ -1247,8 +1275,9 @@
 
 		spin_lock_irqsave(&wiinst->lock, flags);
 
-		if (wiinst->state & WAVE_STATE_OPEN)
+		if (wiinst->state & WAVE_STATE_OPEN) {
 			emu10k1_wavein_close(wave_dev);
+		}
 
 		spin_unlock_irqrestore(&wiinst->lock, flags);
 		tasklet_unlock_wait(&wiinst->timer.tasklet);
@@ -1257,12 +1286,13 @@
 
 	kfree(wave_dev);
 
-	wake_up_interruptible(&card->open_wait);
-	unlock_kernel();
+	if (waitqueue_active(&card->open_wait))
+		wake_up_interruptible(&card->open_wait);
 
 	return 0;
 }
 
+/* FIXME sort out poll() + mmap() */
 static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
@@ -1292,11 +1322,6 @@
 		} else
 			mask |= POLLOUT | POLLWRNORM;
 
-		if(woinst->mmapped) {
-			spin_unlock_irqrestore(&woinst->lock, flags);
-			return mask;
-		}
-
 		spin_unlock_irqrestore(&woinst->lock, flags);
 	}
 
@@ -1336,7 +1361,7 @@
 		return;
 
 	if (!buffer->ossfragshift) {
-		fragsize = (woinst->format.bytespersec * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1;
+		fragsize = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1;
 
 		while (fragsize) {
 			fragsize >>= 1;
@@ -1352,7 +1377,8 @@
 	if (!buffer->numfrags) {
 		u32 numfrags;
 
-		numfrags = (woinst->format.bytespersec * WAVEOUT_DEFAULTBUFLEN) / (buffer->fragment_size * 1000) - 1;
+		numfrags = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTBUFLEN) /
+			   (buffer->fragment_size * 1000) - 1;
 
 		buffer->numfrags = 1;
 
@@ -1452,13 +1478,13 @@
 	}
 
 	buffer->numfrags = buffer->size / buffer->fragment_size;
-
+	buffer->pages =  buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0);
 	if (buffer->size % buffer->fragment_size)
 		BUG();
 
 	DPD(2, " calculated recording fragment_size -> %d\n", buffer->fragment_size);
 	DPD(2, " calculated recording numfrags -> %d\n", buffer->numfrags);
-	DPD(2, " buffer size register -> 0x%2x\n", buffer->sizeregval);
+	DPD(2, " buffer size register -> %#04x\n", buffer->sizeregval);
 
 	return;
 }
@@ -1478,17 +1504,11 @@
 	}
 
 	emu10k1_wavein_update(wave_dev->card, wiinst);
-
-	if (wiinst->mmapped) {
-		spin_unlock_irqrestore(&wiinst->lock, flags);
-		return;
-	}
-
 	emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
 
 	spin_unlock_irqrestore(&wiinst->lock, flags);
 
-	if (bytestocopy >= wiinst->buffer.fragment_size)
+	if (bytestocopy >= wiinst->buffer.fragment_size && waitqueue_active(&wiinst->wait_queue))
 		wake_up_interruptible(&wiinst->wait_queue);
 	else
 		DPD(3, "Not enough transfer size, %d\n", bytestocopy);
@@ -1519,7 +1539,7 @@
 	} else
 		spin_unlock_irqrestore(&woinst->lock, flags);
 
-	if (bytestocopy >= woinst->buffer.fragment_size)
+	if (bytestocopy >= woinst->buffer.fragment_size && waitqueue_active(&woinst->wait_queue))
 		wake_up_interruptible(&woinst->wait_queue);
 	else
 		DPD(3, "Not enough transfer size -> %d\n", bytestocopy);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)