patch-2.1.117 linux/drivers/sound/wavfront.c

Next file: linux/drivers/sound/wf_midi.c
Previous file: linux/drivers/sound/soundcard.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.116/linux/drivers/sound/wavfront.c linux/drivers/sound/wavfront.c
@@ -1,8 +1,8 @@
-/*
- * sound/wavefront.c
+/*  -*- linux-c -*-
  *
- * A low level driver for Turtle Beach WaveFront Series
- *    (Maui, Tropez, Tropez Plus, and perhaps the Monterey & Rio)
+ * sound/wavfront.c
+ *
+ * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus)
  *
  * This driver supports the onboard wavetable synthesizer (an ICS2115),
  * including patch, sample and program loading and unloading, conversion
@@ -26,18 +26,6 @@
  * This chip also controls the configuration of the card: the wavefront
  * synth is logical unit 4.
  *
- * NOTE: this driver has been written to support multiple WaveFront
- * cards, but without using PnP to configure the CS4232, all of them
- * would end up with the same configuration. Further, the current
- * module loading interface doesn't permit this, since it only allows
- * once instance of a module (which happens to be equivalent to a
- * single hardware configuration) to be installed at one time.In
- * addition, there is a hard limit on the available DMA channels that
- * also makes installing more than 1 card limited to purely MIDI/synth
- * activities on the second card. Still, the coding style gets rid of
- * virtually all globals, which I believe is a better way to code
- * device drivers (or anything else, for that matter).
- *
  **********************************************************************
  *
  * Copyright (C) by Paul Barton-Davis 1998
@@ -61,9 +49,12 @@
  * aspects of configuring a WaveFront soundcard, particularly the
  * effects processor.
  *
+ * $Id: wavfront.c,v 0.4 1998/07/22 02:12:11 pbd Exp $
+ *
  * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.  */
+ * for more info.
+ */
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -78,23 +69,26 @@
 #define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
 #include "midi_synth.h"
 
-#define COPY_FROM_USER(a,b,c) copy_from_user ((a),(b),(c))
-#define COPY_TO_USER(a,b,c)   copy_to_user ((a),(b),(c))
+/* This thing is meant to work as a module */
 
-#if defined(CONFIG_SOUND_WAVEFRONT) || defined(CONFIG_SOUND_WAVEFRONT_MODULE)
+#if defined(CONFIG_SOUND_WAVEFRONT_MODULE) && defined(MODULE)
 
-/* This thing is meant to work as a module */
+/* if WF_DEBUG not defined, no run-time debugging messages will
+   be available via the debug flag setting. Given the current
+   beta state of the driver, this will remain set until a future 
+   version.
+*/
 
-#ifdef MODULE
+#define WF_DEBUG 1
 
 /* bitmasks for WaveFront status port value */
 
 #define STAT_INTR_WRITE		0x40
 #define STAT_CAN_WRITE		0x20
-#define STAT_RINTR_ENABLED	0x10
+#define STAT_WINTR_ENABLED	0x10
 #define STAT_INTR_READ		0x04
 #define STAT_CAN_READ		0x02
-#define STAT_WINTR_ENABLED	0x01
+#define STAT_RINTR_ENABLED	0x01
 
 /*** Module-accessible parameters ***************************************/
 
@@ -111,7 +105,7 @@
 		   operation, whatever that means.
 		*/
 
-int wf_debug_default = 0;  /* you can set this to control debugging
+int debug_default = 0;  /* you can set this to control debugging
 			      during driver loading. it takes any combination
 			      of the WF_DEBUG_* flags defined in
 			      wavefront.h
@@ -119,27 +113,33 @@
 
 /* XXX this needs to be made firmware and hardware version dependent */
 
-char *wf_ospath = "/etc/sound/wavefront.os"; /* where to find a processed
-						version of the WaveFront OS
-					     */
-
-/* These three don't need to be messed with unless you're trying to
-   tweak the driver for optimal I/O performance. Read wavefront_wait()
-   and wavefront_sleep() to see what they do. You may need or want to
-   tweak them for CPU's different than the 486/66Mhz that I run on.
-*/
-
-int wf_short_wait_count = 5000; /* loops, CPU dependent */
-int wf_sleep_interval = 50;     /* HZ/wf_sleep_interval seconds per sleep */
-int wf_sleep_tries = 100;  /*2sec*/ /* number of times we'll try to sleep */
+char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed
+					     version of the WaveFront OS
+					  */
+
+int sleep_interval = 100;     /* HZ/sleep_interval seconds per sleep */
+int sleep_tries = 50;       /* number of times we'll try to sleep */
+
+int wait_usecs = 150; /* This magic number seems to give pretty optimal
+			 throughput based on my limited experimentation.
+			 If you want to play around with it and find a better
+			 value, be my guest. Remember, the idea is to
+			 get a number that causes us to just busy wait
+			 for as many WaveFront commands as possible, without
+			 coming up with a number so large that we hog the
+			 whole CPU.
+
+			 Specifically, with this number, out of about 134,000
+			 status waits, only about 250 result in a sleep.
+		      */
 
 MODULE_PARM(wf_raw,"i");
 MODULE_PARM(fx_raw,"i");
-MODULE_PARM(wf_debug_default,"i");
-MODULE_PARM(wf_ospath,"s");
-MODULE_PARM(wf_short_wait_count,"i");
-MODULE_PARM(wf_sleep_interval,"i");
-MODULE_PARM(wf_sleep_tries,"i");
+MODULE_PARM(debug_default,"i");
+MODULE_PARM(sleep_interval,"i");
+MODULE_PARM(sleep_tries,"i");
+MODULE_PARM(wait_usecs,"i");
+MODULE_PARM(ospath,"s");
 
 /***************************************************************************/
 
@@ -150,9 +150,7 @@
 static int (*midi_load_patch) (int dev, int format, const char *addr,
 			       int offs, int count, int pmgr_flag) = NULL;
 
-
 typedef struct wf_config {
-	int installed;        /* well, is it ? note: doesn't mean "working" */
 	int devno;            /* device number from kernel */
 	int irq;              /* "you were one, one of the few ..." */
 	int base;             /* low i/o port address */
@@ -185,7 +183,7 @@
 	volatile int irq_ok;               /* set by interrupt handler */
 	int opened;                        /* flag, holds open(1) mode */
 	char debug;                        /* debugging flags */
-	unsigned int freemem;              /* installed RAM, in bytes */ 
+	int freemem;                       /* installed RAM, in bytes */ 
 	int synthdev;                      /* OSS minor devnum for synth */
 	int mididev;                       /* OSS minor devno for internal MIDI */
 	int ext_mididev;                   /* OSS minor devno for external MIDI */ 
@@ -198,9 +196,20 @@
 	int samples_used;                  /* how many */
 	char interrupts_on;                /* h/w MPU interrupts enabled ? */
 	char rom_samples_rdonly;           /* can we write on ROM samples */
+	struct wait_queue *interrupt_sleeper; 
+#ifdef  WF_STATS
+	unsigned long status_found_during_loop;
+	unsigned long status_found_during_sleep[4];
+#endif  WF_STATS
+
 } wf_config;
 
-static wf_config wfs[WAVEFRONT_MAX_DEVICES];
+/* Note: because this module doesn't export any symbols, this really isn't
+   a global variable, even if it looks like one. I was quite confused by
+   this when I started writing this as a (newer) module -- pbd.
+*/
+
+static wf_config wavefront_configuration;
 
 #define wavefront_status(hw) (inb (hw->status_port))
 
@@ -210,14 +219,6 @@
 static int wffx_init (struct wf_config *hw);
 static int wavefront_delete_sample (struct wf_config *hw, int sampnum);
 
-static volatile int irq2hw[17] =
-{-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1};
-
-static volatile int dev2hw[17] =
-{-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1};
-
 typedef struct {
 	int cmd;
 	char *action;
@@ -304,25 +305,10 @@
 	{ WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK },
 	{ WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers",
 	  32, 0, 0 },
+	{ WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK },
 	{ 0x00 }
 };
 
-wf_config *
-hw_from_dev (int dev)
-
-{
-	int i;
-
-	if ((i = dev2hw[dev]) == -1) {
-		printk (KERN_ERR
-			"WaveFront: no hardware associated with device %d.\n",
-			dev);
-		return 0;
-	}
-
-	return &wfs[i];
-}
-
 static const char *
 wavefront_errorstr (int errnum)
 
@@ -370,21 +356,35 @@
 
 {
 	int             i;
+	static int      short_loop_cnt = 0;
 
-	for (i = 0; i < wf_short_wait_count; i++) {
+	if (short_loop_cnt == 0) {
+	    short_loop_cnt = (int) (((double) wait_usecs / 1000000.0) *
+		(double) current_cpu_data.loops_per_sec);
+	}
+
+	for (i = 0; i < short_loop_cnt; i++) {
 		if (wavefront_status(hw) & mask) {
+#ifdef WF_STATS
+		        hw->status_found_during_loop++;
+#endif WF_STATS
 			return 1;
 		}
 	}
 
-	for (i = 0; i < wf_sleep_tries; i++) {
+	for (i = 0; i < sleep_tries; i++) {
 
 		if (wavefront_status(hw) & mask) {
+#ifdef WF_STATS
+    		        if (i < 4) {
+				hw->status_found_during_sleep[i]++;
+			}
+#endif WF_STATS
 			return 1;
 		}
 
-		if (wavefront_sleep (hw, HZ/wf_sleep_interval)) {
-			return 0;
+		if (wavefront_sleep (hw, HZ/sleep_interval)) {
+			return (0);
 		}
 	}
 
@@ -397,23 +397,30 @@
 	if (wavefront_wait (hw, STAT_CAN_READ))
 		return inb (hw->data_port);
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_DATA) {
 		printk (KERN_DEBUG "WaveFront: read timeout.\n");
 	}
+#endif WF_DEBUG
+
 	return -1;
 }
 
 static int
 wavefront_write (wf_config *hw, unsigned char data)
+
 {
 	if (wavefront_wait (hw, STAT_CAN_WRITE)) {
 		outb (data, hw->data_port);
 		return 1;
 	}
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_DATA) {
 		printk (KERN_DEBUG "WaveFront: write timeout.\n");
 	}
+#endif WF_DEBUG
+
 	return 0;
 }
 
@@ -444,60 +451,74 @@
 		rbuf = 0;
 	}
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_CMD) {
 		printk (KERN_DEBUG "Wavefront: 0x%x [%s] (%d,%d,%d)\n",
 			cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt,
 			wfcmd->need_ack);
 	}
+#endif WF_DEBUG
     
 	if (!wavefront_write (hw, cmd)) { 
+#ifdef WF_DEBUG
 		if (hw->debug & (WF_DEBUG_IO|WF_DEBUG_CMD)) {
 			printk (KERN_DEBUG "WaveFront: cannot request "
 				"0x%x [%s].\n",
 				cmd, wfcmd->action);
 		}
+#endif WF_DEBUG
 		return 1;
 	} 
 
 	if (wfcmd->write_cnt > 0) {
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_DATA) {
 			printk (KERN_DEBUG "WaveFront: writing %d bytes "
 				"for 0x%x\n",
 				wfcmd->write_cnt, cmd);
 		}
+#endif WF_DEBUG
 
 		for (i = 0; i < wfcmd->write_cnt; i++) {
 			if (!wavefront_write (hw, wbuf[i])) {
+#ifdef WF_DEBUG
 				if (hw->debug & WF_DEBUG_IO) {
 					printk (KERN_DEBUG
                            "WaveFront: bad write for byte %d of 0x%x [%s].\n",
 						i, cmd, wfcmd->action);
 				}
+#endif WF_DEBUG
 				return 1;
 			}
+#ifdef WF_DEBUG
 			if (hw->debug & WF_DEBUG_DATA) {
 				printk (KERN_DEBUG
                                         "WaveFront: write[%d] = 0x%x\n",
 					i, wbuf[i]);
+#endif WF_DEBUG
 			}
 		}
 	}
 
 	if (wfcmd->read_cnt > 0) {
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_DATA) {
 			printk (KERN_DEBUG "WaveFront: reading %d ints "
 				"for 0x%x\n",
 				wfcmd->read_cnt, cmd);
 		}
+#endif WF_DEBUG
 
 		for (i = 0; i < wfcmd->read_cnt; i++) {
 
 			if ((c = wavefront_read(hw)) == -1) {
+#ifdef WF_DEBUG
 				if (hw->debug & WF_DEBUG_IO) {
 					printk (KERN_DEBUG
                                "WaveFront: bad read for byte %d of 0x%x [%s].\n",
 						i, cmd, wfcmd->action);
 				}
+#endif WF_DEBUG
 				return 1;
 			}
 
@@ -505,12 +526,14 @@
 	    
 			if (c == 0xff) { 
 				if ((c = wavefront_read (hw)) == -1) {
+#ifdef WF_DEBUG
 					if (hw->debug & WF_DEBUG_IO) {
 						printk (KERN_DEBUG
                                  "WaveFront: bad read for error byte at "
                                   "read byte %d of 0x%x [%s].\n",
 							i, cmd, wfcmd->action);
 					}
+#endif WF_DEBUG
 					return 1;
 				}
 
@@ -519,7 +542,7 @@
 				if (c == 1 &&
 				    wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) {
 					rbuf[0] = WF_ST_EMPTY;
-					return 0;
+					return (0);
 
 				} else if (c == 3 &&
 					   wfcmd->cmd == WFC_UPLOAD_PATCH) {
@@ -533,6 +556,7 @@
 
 				} else {
 
+#ifdef WF_DEBUG
 					if (hw->debug & WF_DEBUG_IO) {
 						printk (KERN_DEBUG
                                             "WaveFront: error %d (%s) during "
@@ -542,6 +566,7 @@
 							wavefront_errorstr (c),
 							i, cmd, wfcmd->action);
 					}
+#endif WF_DEBUG
 					return 1;
 
 				}
@@ -549,20 +574,24 @@
 				rbuf[i] = c;
 			}
 
+#ifdef WF_DEBUG
 			if (hw->debug & WF_DEBUG_DATA) {
 				printk (KERN_DEBUG
 					"WaveFront: read[%d] = 0x%x\n",
 					i, rbuf[i]);
 			}
+#endif WF_DEBUG
 		}
 	}
 
 	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
 
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_CMD) {
 			printk (KERN_DEBUG "WaveFront: reading ACK for 0x%x\n",
 				cmd);
 		}
+#endif WF_DEBUG
 
 		/* Some commands need an ACK, but return zero instead
 		   of the standard value.
@@ -574,11 +603,13 @@
 	
 		if (ack != WF_ACK) {
 			if (ack == -1) {
+#ifdef WF_DEBUG
 				if (hw->debug & WF_DEBUG_IO) {
 					printk (KERN_DEBUG
                                "WaveFront: cannot read ack for 0x%x [%s].\n",
 						cmd, wfcmd->action);
 				}
+#endif WF_DEBUG
 				return 1;
 		
 			} else {
@@ -587,14 +618,17 @@
 				if (ack == 0xff) { /* explicit error */
 		    
 					if ((err = wavefront_read (hw)) == -1) {
+#ifdef WF_DEBUG
 						if (hw->debug & WF_DEBUG_DATA) {
 							printk (KERN_DEBUG
                                "WaveFront: cannot read err for 0x%x [%s].\n",
                                                             cmd, wfcmd->action);
 						}
+#endif WF_DEBUG
 					}
 				}
 
+#ifdef WF_DEBUG
 				if (hw->debug & WF_DEBUG_IO) {
 					printk (KERN_DEBUG
 						"WaveFront: 0x%x [%s] "
@@ -602,22 +636,27 @@
 						cmd, wfcmd->action, ack, err,
 						wavefront_errorstr (err));
 				}
+#endif WF_DEBUG
 				return -err;
 			} 
 		}
 
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_DATA) {
 			printk (KERN_DEBUG "WaveFront: ack received "
 				"for 0x%x [%s]\n",
 				cmd, wfcmd->action);
 		}
+#endif WF_DEBUG
 	} else {
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_CMD) {
 			printk (KERN_DEBUG 
 				"Wavefront: 0x%x [%s] does not need "
 				"ACK (%d,%d,%d)\n",
 				cmd, wfcmd->action, wfcmd->read_cnt,
 				wfcmd->write_cnt, wfcmd->need_ack);
+#endif WF_DEBUG
 		}
 	}
 
@@ -795,7 +834,7 @@
 		WF_MAX_SAMPLE - hw->samples_used);
 
 
-	return 0;
+	return (0);
 
 }
 
@@ -826,6 +865,7 @@
 			printk (KERN_ERR "WaveFront: upload patch "
 				"error 0x%x\n", x);
 			hw->patch_status[i] = 0;
+			return 1;
 		}
 	}
 
@@ -843,7 +883,7 @@
 	printk (KERN_INFO
 		"WaveFront: %d patch slots filled, %d in use\n", cnt, cnt2);
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -889,7 +929,7 @@
 
 	printk (KERN_INFO "WaveFront: %d programs slots in use\n", cnt);
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -900,10 +940,12 @@
 	unsigned char buf[WF_PATCH_BYTES+2];
 	unsigned char *bptr;
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: downloading patch %d\n",
 			header->number);
 	}
+#endif WF_DEBUG
 
 	hw->patch_status[header->number] |= WF_SLOT_FILLED;
 
@@ -913,10 +955,10 @@
     
 	if (wavefront_cmd (hw, WFC_DOWNLOAD_PATCH, 0, buf)) {
 		printk (KERN_ERR "WaveFront: download patch failed\n");
-		return -EIO;
+		return -(EIO);
 	}
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -927,10 +969,12 @@
 	unsigned char buf[WF_PROGRAM_BYTES+1];
 	int i;
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG
 			"WaveFront: downloading program %d\n", header->number);
 	}
+#endif WF_DEBUG
 
 	hw->prog_status[header->number] = WF_SLOT_USED;
 
@@ -954,10 +998,10 @@
     
 	if (wavefront_cmd (hw, WFC_DOWNLOAD_PROGRAM, 0, buf)) {
 		printk (KERN_WARNING "WaveFront: download patch failed\n");	
-		return -EIO;
+		return -(EIO);
 	}
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -1004,6 +1048,7 @@
 	int skip = 0;
 	int initial_skip = 0;
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: sample %sdownload for slot %d, "
 			"type %d, %d bytes from 0x%x\n",
@@ -1011,6 +1056,7 @@
 			header->number, header->subkey, header->size,
 			(int) header->dataptr);
 	}
+#endif WF_DEBUG
 
 	if (header->size) {
 
@@ -1032,10 +1078,9 @@
 		   or theoretically some other configuration) is the
 		   responsibility of the user level library. 
 
-		   To try to do this in the kernel would be a little crazy:
-		   we'd need 24 * 512 bytes (12K) of kernel space just to
-		   hold copies of the original sample headers; the whole
-		   patch/program/sample header data is about 158K!!!
+		   To try to do this in the kernel would be a little
+		   crazy: we'd need 158K of kernel space just to hold
+		   a copy of the patch/program/sample header data.
 		*/
 
 		if (hw->rom_samples_rdonly) {
@@ -1073,7 +1118,7 @@
 			printk (KERN_ERR
 				"WaveFront: channel selection only possible "
 				"on 16-bit samples");
-			return -EINVAL;
+			return -(EINVAL);
 		}
 	}
 
@@ -1108,11 +1153,13 @@
 		break;
 	}
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: channel selection: %d => "
 			"initial skip = %d, skip = %d\n",
 			WF_GET_CHANNEL (&header->hdr.s), initial_skip, skip);
 	}
+#endif WF_DEBUG
     
 	/* Be safe, and zero the "Unused" bits ... */
 
@@ -1153,7 +1200,7 @@
 	
 	/* This one is truly weird.  What kind of weirdo decided that in
 	   a system dominated by 16- and 32-bit integers, they would use
-	   a 12-bit transfer size ?
+	   just 12 bits ?
 	*/
 	
 	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
@@ -1171,7 +1218,7 @@
 			   0, sample_hdr)) {
 		printk (KERN_WARNING "WaveFront: sample %sdownload refused.\n",
 			header->size ? "" : "header ");
-		return -EIO;
+		return -(EIO);
 	}
 
 	if (header->size == 0) {
@@ -1197,7 +1244,7 @@
 		if (wavefront_cmd (hw, WFC_DOWNLOAD_BLOCK, 0, 0)) {
 			printk (KERN_WARNING "WaveFront: download block "
 				"request refused.\n");
-			return -EIO;
+			return -(EIO);
 		}
 
 		for (i = 0; i < blocksize; i++) {
@@ -1207,7 +1254,7 @@
 				get_user (sample_short, dataptr);
 				dataptr += skip;
 		
-				if (data_is_unsigned) {
+				if (data_is_unsigned) { /* GUS ? */
 
 					if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) {
 			
@@ -1255,12 +1302,12 @@
 			if (dma_ack == -1) {
 				printk (KERN_ERR "WaveFront: upload sample "
 					"DMA ack timeout\n");
-				return -EIO;
+				return -(EIO);
 			} else {
 				printk (KERN_ERR "WaveFront: upload sample "
 					"DMA ack error 0x%x\n",
 					dma_ack);
-				return -EIO;
+				return -(EIO);
 			}
 		}
 	}
@@ -1272,7 +1319,7 @@
 	*/
 
  sent:
-	return 0;
+	return (0);
 }
 
 static int
@@ -1282,12 +1329,14 @@
 {
 	unsigned char alias_hdr[WF_ALIAS_BYTES];
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: download alias, %d is "
 			"alias for %d\n",
 			header->number,
 			header->hdr.a.OriginalSample);
 	}
+#endif WF_DEBUG
     
 	munge_int32 (header->number, &alias_hdr[0], 2);
 	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
@@ -1304,12 +1353,12 @@
 
 	if (wavefront_cmd (hw, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
 		printk (KERN_ERR "WaveFront: download alias failed.\n");
-		return -EIO;
+		return -(EIO);
 	}
 
 	hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -1330,17 +1379,21 @@
 	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
 	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: multi %d with %d=%d samples\n",
 			header->number, header->hdr.ms.NumberOfSamples, num_samples);
 	}
+#endif WF_DEBUG
 
 	for (i = 0; i < num_samples; i++) {
+#ifdef WF_DEBUG
 		if ((hw->debug & (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) ==
 		    (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) {
 			printk (KERN_DEBUG "WaveFront: sample[%d] = %d\n",
 				i, header->hdr.ms.SampleNumber[i]);
 		}
+#endif WF_DEBUG
 		munge_int32 (header->hdr.ms.SampleNumber[i],
 			     &msample_hdr[3+(i*2)], 2);
 	}
@@ -1354,12 +1407,12 @@
 			   (unsigned char *) ((num_samples*2)+3),
 			   msample_hdr)) {
 		printk (KERN_ERR "WaveFront: download of multisample failed.\n");
-		return -EIO;
+		return -(EIO);
 	}
 
 	hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
 
-	return 0;
+	return (0);
 }
 
 static int
@@ -1375,13 +1428,15 @@
     
 	if (wavefront_cmd (hw, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
 		printk (KERN_ERR "WaveFront: upload multisample failed.\n");
-		return -EIO;
+		return -(EIO);
 	}
     
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_DATA) {
 		printk (KERN_DEBUG "WaveFront: msample %d has %d samples\n",
 			header->number, log_ns[0]);
 	}
+#endif WF_DEBUG
 
 	header->hdr.ms.NumberOfSamples = log_ns[0];
 
@@ -1395,26 +1450,28 @@
 		if ((d[0] = wavefront_read (hw)) == -1) {
 			printk (KERN_ERR "WaveFront: upload multisample failed "
 				"during sample loop.\n");
-			return -EIO;
+			return -(EIO);
 		}
 
 		if ((d[1] = wavefront_read (hw)) == -1) {
 			printk (KERN_ERR "WaveFront: upload multisample failed "
 				"during sample loop.\n");
-			return -EIO;
+			return -(EIO);
 		}
 	
 		header->hdr.ms.SampleNumber[i] =
 			demunge_int32 ((unsigned char *) d, 2);
 	
+#ifdef WF_DEBUG
 		if (hw->debug & WF_DEBUG_DATA) {
 			printk (KERN_DEBUG "WaveFront: msample "
 				"sample[%d] = %d\n",
 				i, header->hdr.ms.SampleNumber[i]);
 		}
+#endif WF_DEBUG
 	}
 
-	return 0;
+	return (0);
 }
 
 
@@ -1426,12 +1483,14 @@
 	wavefront_drum *drum = &header->hdr.d;
 	int i;
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG
 			"WaveFront: downloading edrum for MIDI "
 			"note %d, patch = %d\n", 
 			header->number, drum->PatchNumber);
 	}
+#endif WF_DEBUG
 
 	drumbuf[0] = header->number & 0x7f;
 
@@ -1441,10 +1500,10 @@
 
 	if (wavefront_cmd (hw, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
 		printk (KERN_ERR "WaveFront: download drum failed.\n");
-		return -EIO;
+		return -(EIO);
 	}
 
-	return 0;
+	return (0);
 }
 
 static int 
@@ -1468,7 +1527,7 @@
 {
 	int i;
 
-	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+	for (i = 0; i < WF_MAX_PATCH; i++) {
 		if (!(hw->patch_status[i] & WF_SLOT_FILLED)) {
 			return i;
 		}
@@ -1522,7 +1581,7 @@
 	/* Copy in the header of the GUS patch */
 
 	sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; 
-	COPY_FROM_USER (&((char *) &guspatch)[offs],
+	copy_from_user (&((char *) &guspatch)[offs],
 			&(addr)[offs], sizeof_patch - offs);
 
 	if ((i = wavefront_find_free_patch (hw)) == -1) {
@@ -1656,19 +1715,15 @@
 		      int offs, int count, int pmgr_flag)
 {
 
-	struct wf_config *hw;
+	struct wf_config *hw = &wavefront_configuration;
 	wavefront_patch_info header;
 
-	if ((hw = hw_from_dev (dev)) == 0) {
-		return -EINVAL;
-	}
-
 	if (format == SYSEX_PATCH) {	/* Handled by midi_synth.c */
 		if (midi_load_patch == NULL) {
 			printk (KERN_ERR
 				"WaveFront: SYSEX not loadable: "
 				"no midi patch loader!\n");
-			return -EINVAL;
+			return -(EINVAL);
 		}
 		return midi_load_patch (dev, format, addr,
 					offs, count, pmgr_flag);
@@ -1679,12 +1734,12 @@
 
 	} else if (format != WAVEFRONT_PATCH) {
 		printk (KERN_ERR "WaveFront: unknown patch format %d\n", format);
-		return -EINVAL;
+		return -(EINVAL);
 	}
 
 	if (count < sizeof (wavefront_patch_info)) {
 		printk (KERN_ERR "WaveFront: sample header too short\n");
-		return -EINVAL;
+		return -(EINVAL);
 	}
 
 	/* copied in so far: `offs' bytes from `addr'. We shouldn't copy
@@ -1694,10 +1749,11 @@
 	   through the 'hdrptr' field.
 	*/
 
-	COPY_FROM_USER (&((char *) &header)[offs], &(addr)[offs],
+	copy_from_user (&((char *) &header)[offs], &(addr)[offs],
 			sizeof(wavefront_patch_info) -
 			sizeof(wavefront_any) - offs);
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
 		printk (KERN_DEBUG "WaveFront: download "
 			"Sample type: %d "
@@ -1707,11 +1763,12 @@
 			header.number,
 			header.size);
 	}
+#endif WF_DEBUG
 
 	switch (header.subkey) {
 	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
 
-		COPY_FROM_USER ((unsigned char *) &header.hdr.s,
+		copy_from_user ((unsigned char *) &header.hdr.s,
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_sample));
 
@@ -1719,7 +1776,7 @@
 
 	case WF_ST_MULTISAMPLE:
 
-		COPY_FROM_USER ((unsigned char *) &header.hdr.s,
+		copy_from_user ((unsigned char *) &header.hdr.s,
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_multisample));
 
@@ -1728,28 +1785,28 @@
 
 	case WF_ST_ALIAS:
 
-		COPY_FROM_USER ((unsigned char *) &header.hdr.a,
+		copy_from_user ((unsigned char *) &header.hdr.a,
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_alias));
 
 		return wavefront_send_alias (hw, &header);
 
 	case WF_ST_DRUM:
-		COPY_FROM_USER ((unsigned char *) &header.hdr.d, 
+		copy_from_user ((unsigned char *) &header.hdr.d, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_drum));
 
 		return wavefront_send_drum (hw, &header);
 
 	case WF_ST_PATCH:
-		COPY_FROM_USER ((unsigned char *) &header.hdr.p, 
+		copy_from_user ((unsigned char *) &header.hdr.p, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_patch));
 
 		return wavefront_send_patch (hw, &header);
 
 	case WF_ST_PROGRAM:
-		COPY_FROM_USER ((unsigned char *) &header.hdr.pr, 
+		copy_from_user ((unsigned char *) &header.hdr.pr, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_program));
 
@@ -1758,7 +1815,7 @@
 	default:
 		printk (KERN_ERR "WaveFront: unknown patch type %d.\n",
 			header.subkey);
-		return -EINVAL;
+		return -(EINVAL);
 	}
 
 	return 0;
@@ -1804,24 +1861,19 @@
 wavefront_synth_control (int dev, int cmd, caddr_t arg)
 
 {
-	struct wf_config *hw;
+	struct wf_config *hw = &wavefront_configuration;
 	wavefront_control wc;
 	unsigned char patchnumbuf[2];
 	int i;
 
-	if ((hw = hw_from_dev (dev)) == 0) {
-		printk (KERN_ERR
-			"WaveFront: synth_control with unknown "
-			"device number %d\n", dev);
-		return -EINVAL;
-	}
-
-	COPY_FROM_USER (&wc, arg, sizeof (wc));
+	copy_from_user (&wc, arg, sizeof (wc));
 
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_CMD) {
 		printk (KERN_DEBUG "WaveFront: synth control with "
 			"cmd 0x%x\n", wc.cmd);
 	}
+#endif WF_DEBUG
 
 	/* special case handling of or for various commands */
 
@@ -1940,7 +1992,7 @@
 	   is a low priority fix.
 	*/
 
-	COPY_TO_USER (arg, &wc, sizeof (wc));
+	copy_to_user (arg, &wc, sizeof (wc));
 	return 0;
 }
 
@@ -1953,13 +2005,9 @@
 static int
 wavefront_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
-	wf_config *hw;
+	wf_config *hw = &wavefront_configuration;
 	unsigned char rbuf[4];
 
-	if ((hw = hw_from_dev (dev)) == 0) {
-		return -EINVAL;
-	}
-
 	switch (cmd) {
 	case SNDCTL_SYNTH_INFO:
 		memcpy (&((char *) arg)[0], &wavefront_info,
@@ -1992,7 +2040,7 @@
 		return wavefront_synth_control (dev, cmd, arg);
 
 	default:
-		return -EINVAL;
+		return -(EINVAL);
 	}
 }
 
@@ -2000,32 +2048,30 @@
 wavefront_open (int dev, int mode)
 
 {
-	struct wf_config *hw;
-
-	if ((hw = hw_from_dev (dev)) == 0) {
-		return -EINVAL;
-	}
+	struct wf_config *hw = &wavefront_configuration;
 
 	if (hw->opened) {
-		printk (KERN_ERR "WaveFront: warning: device in use\n");
+		printk (KERN_WARNING "WaveFront: warning: device in use\n");
 	}
 
 	hw->opened = mode;
-	return 0;
+	return (0);
 }
 
 static void
 wavefront_close (int dev)
 
 {
-	struct wf_config *hw;
+	struct wf_config *hw = &wavefront_configuration;
+	int i;
 
-	if ((hw = hw_from_dev (dev)) == 0) {
-		printk (KERN_ERR
-			"WaveFront: close() called on non-existent dev %d", dev);
-		return;
+#ifdef WF_STATS
+	printk ("Status during loop: %ld\n", hw->status_found_during_loop);
+	for (i = 0; i < 4; i++) {
+		printk ("Status during sleep[%d]: %ld\n",
+			i, hw->status_found_during_sleep[i]);
 	}
-
+#endif WF_STATS
 	hw->opened = 0;
 	hw->debug = 0;
 
@@ -2035,70 +2081,65 @@
 static void
 wavefront_aftertouch (int dev, int channel, int pressure)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return;
-	midi_synth_aftertouch (hw->mididev,channel,pressure);
+	midi_synth_aftertouch (wavefront_configuration.mididev,channel,pressure);
 };
 
 static void
 wavefront_bender (int dev, int chn, int value)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return;
-	midi_synth_bender (hw->mididev, chn, value);
+	midi_synth_bender (wavefront_configuration.mididev, chn, value);
 };
 
 static void
 wavefront_controller (int dev, int channel, int ctrl_num, int value)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return;
 	if(ctrl_num==CTRL_PITCH_BENDER) wavefront_bender(0,channel,value);
-	midi_synth_controller (hw->mididev, channel,ctrl_num,value);
+	midi_synth_controller (wavefront_configuration.mididev,
+			       channel,ctrl_num,value);
 };
 
 static void
 wavefront_panning(int dev, int channel, int pressure)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return;
-	midi_synth_controller(hw->mididev,channel,CTL_PAN,pressure);
+	midi_synth_controller (wavefront_configuration.mididev,
+			       channel,CTL_PAN,pressure);
 };
 
 static int
 wavefront_set_instr (int dev, int channel, int instr_no)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return 1;
-	return(midi_synth_set_instr(hw->mididev,channel,instr_no));
+	return(midi_synth_set_instr (wavefront_configuration.mididev,
+				     channel,instr_no));
 };
 
 static int
 wavefront_kill_note (int dev, int channel, int note, int volume)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return 1;
 	if (note==255)
-		return(midi_synth_start_note(hw->mididev, channel, 0, 0));
-	return(midi_synth_kill_note(hw->mididev, channel, note, volume));
+		return (midi_synth_start_note (wavefront_configuration.mididev,
+					       channel, 0, 0));
+	return(midi_synth_kill_note (wavefront_configuration.mididev,
+				     channel, note, volume));
 };
 
 static int
 wavefront_start_note (int dev, int channel, int note, int volume)
 {
-	struct wf_config *hw = hw_from_dev (dev);
-	if (!hw) return 1;
-
 	if (note==255) {
-		/*midi_synth_controller(hw->mididev,channel,7,volume);*/
-		midi_synth_aftertouch(hw->mididev,channel,volume); 
+		midi_synth_aftertouch (wavefront_configuration.mididev,
+				       channel,volume); 
 		return(0);
 	};
+
 	if (volume==0) {
 		volume=127;
-		midi_synth_aftertouch(hw->mididev,channel,0);
+		midi_synth_aftertouch
+			(wavefront_configuration.mididev,
+			 channel,0);
 	};
-	midi_synth_start_note (hw->mididev, channel, note, volume);
+
+	midi_synth_start_note (wavefront_configuration.mididev,
+			       channel, note, volume);
 	return(0);
 };
 
@@ -2111,7 +2152,8 @@
 
 {
 	int i;
-	for(i=0;i<16;i++) {
+
+	for (i = 0; i < 16; i++) {
 		midi_synth_kill_note (dev,i,0,0);
 	};
 };
@@ -2149,16 +2191,18 @@
 void
 wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
-	int i;
+        /* We don't use this handler except during device
+	   configuration. While the module is installed, the 
+	   interrupt is used to signal MIDI interrupts, and is 
+	   handled by the interrupt routine in wf_midi.c
+	 */
+	   
+	wf_config *hw = (wf_config *) dev_id;
+	hw->irq_ok = 1;
 
-	if (irq < 0 || irq > 16) {
-		printk (KERN_WARNING "WaveFront: bogus interrupt %d recv'd\n",
-			irq);
-	} else if ((i = irq2hw[irq]) == -1) {
-		printk (KERN_ALERT
-			"WaveFront: interrupt from unknown hw (irq=%d).\n", irq);
-	} else {
-		wfs[i].irq_ok = 1;
+	if ((wavefront_status(hw) & STAT_INTR_WRITE) ||
+	    (wavefront_status(hw) & STAT_INTR_READ)) {
+		wake_up (&hw->interrupt_sleeper);
 	}
 }
 
@@ -2173,106 +2217,24 @@
 6 Host Tx Interrupt Pending (1=Interrupt)
 7 Unused
 
-*/
+11111001 
+  Rx Intr enable
+  nothing to read from board
+  no rx interrupt pending
+  unused
+  tx interrupt enabled
+  space to transmit
+  tx interrupt pending
 
-/* CONTROL REGISTER
-0 Host Rx Interrupt Enable (1=Enabled)      0x1
-1 Unused                                    0x2
-2 Unused                                    0x4
-3 Unused                                    0x8
-4 Host Tx Interrupt Enable                 0x10
-5 Mute (0=Mute; 1=Play)                    0x20
-6 Master Interrupt Enable (1=Enabled)      0x40
-7 Master Reset (0=Reset; 1=Run)            0x80
 */
 
 int
-probe_wavefront (struct address_info *hw_config)
+wavefront_interrupt_bits (int irq)
 
 {
-	int             i;
-	int             tmp1, tmp2;
-	unsigned char   bits;
-	unsigned char   rbuf[32], wbuf[32];
-	wf_config       *hw;
-
-	if (hw_config->irq < 0 || hw_config->irq > 16) {
-	    printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", 
-		    hw_config->irq);
-	    return 0;
-	}
-  
-	/* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus
-	   takes up another 8 ...
-	*/
-
-	if (check_region (hw_config->io_base, 16)) {
-		printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x "
-			"already in use - ignored\n", hw_config->io_base,
-			hw_config->io_base+15);
-		return 0;
-	}
-  
-	for (i = 0; i < WAVEFRONT_MAX_DEVICES; i++) {
-		if (!wfs[i].installed) {
-			wfs[i].installed = 1;
-			break;
-		}
-	}
-
-	if (i == WAVEFRONT_MAX_DEVICES) {
-		printk (KERN_WARNING "WaveFront: no device slots available (max = %d).\n",
-			WAVEFRONT_MAX_DEVICES);
-		return 0;
-	}
-  
-	hw = &wfs[i];
+	int bits;
 
-	hw->irq = hw_config->irq;
-	hw->base = hw_config->io_base;
-
-	hw->israw = 0;
-	hw->debug = wf_debug_default;
-	hw->interrupts_on = 0;
-	hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */
-
-	hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1;
-	hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1;
-	hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1;
-
-	irq2hw[hw_config->irq] = i;
-  
-	if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
-		hw->fw_version[0] = rbuf[0];
-		hw->fw_version[1] = rbuf[1];
-		printk (KERN_INFO "WaveFront: firmware %d.%d already loaded.\n",
-			rbuf[0], rbuf[1]);
-      
-		if (wavefront_cmd (hw, WFC_HARDWARE_VERSION, rbuf, wbuf) == 0) {
-			hw->hw_version[0] = rbuf[0];
-			hw->hw_version[1] = rbuf[1];
-		} else {
-			printk (KERN_INFO "WaveFront: not raw, but no hardware version!\n");
-			return 0;
-		}
-		if (!wf_raw) {
-			return 1;
-		}
-	} else {
-		hw->israw = 1;
-		printk (KERN_INFO
-			"WaveFront: no response to firmware probe, "
-			"assume raw.\n");
-	}
-
-	if (request_irq (hw_config->irq, wavefrontintr,
-			 0, "WaveFront", NULL) < 0) {
-		printk (KERN_WARNING "WaveFront: IRQ %d not available!\n",
-			hw_config->irq);
-		return 0;
-	}
-
-	switch (hw_config->irq) {
+	switch (irq) {
 	case 9:
 		bits = 0x00;
 		break;
@@ -2287,11 +2249,42 @@
 		break;
 	
 	default:
-		printk (KERN_WARNING "WaveFront: invalid IRQ %d\n",
-			hw_config->irq);
-		return 0;
+		printk (KERN_WARNING "WaveFront: invalid IRQ %d\n", irq);
+		bits = -1;
 	}
-  
+
+	return bits;
+}
+
+void
+wavefront_should_cause_interrupt (wf_config *hw, int val, int port, int timeout)
+
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+	hw->irq_ok = 0;
+	outb (val,port);
+	current->timeout = jiffies + timeout;
+	interruptible_sleep_on (&hw->interrupt_sleeper);
+	restore_flags (flags);
+}
+
+static int
+wavefront_hw_reset (wf_config *hw)
+
+{
+	int bits;
+	int hwv[2];
+
+	if (request_irq (hw->irq, wavefrontintr,
+			 0, "WaveFront", (void *) hw) < 0) {
+		printk (KERN_WARNING "WaveFront: IRQ %d not available!\n",
+			hw->irq);
+		return 1;
+	}
+
 	/* try reset of port */
       
 	outb (0x0, hw->control_port); 
@@ -2305,9 +2298,6 @@
      
 	   Bit 6 - MIDI Interface Select
 
-	   XXX PBD: I think this documentation is backwards. I leave bit
-	   6 unset, and get MIDI data from the 9 pin D connector.
-
 	   0 - Use the MIDI Input from the 26-pin WaveBlaster
 	   compatible header as the serial MIDI source
 	   1 - Use the MIDI Input from the 9-pin D connector as the serial MIDI 
@@ -2331,101 +2321,270 @@
      
 	*/
 
-	/* configure hardware: IRQ, plus external MIDI interface selected */
-  
-	outb (bits | 0x80, hw->data_port);	
-  
-	/* take us out of reset, unmute, master + TX + RX interrupts on */
-  
-	outb (0x80|0x20|0x40|0x10|0x1, hw->control_port);
-  
-	for (i = 0; i < 1000000 && !hw->irq_ok; i++);
-  
-	/* Data port is now the data port, not the h/w initialization port 
-     
-	   The boot ROM will check the OSRAM, and will then
-	   wait for the either the "download OS" or
-	   "report h/w version" commands.
-
-	   Any other command will supposedly be ignored.
+	/* configure hardware: IRQ, enable interrupts, 
+	   plus external 9-pin MIDI interface selected
 	*/
+
+	if ((bits = wavefront_interrupt_bits (hw->irq)) < 0) {
+		return 1;
+	}
+
+	outb (0x80 | 0x40 | bits, hw->data_port);	
   
+	/* CONTROL REGISTER
+
+	   0 Host Rx Interrupt Enable (1=Enabled)      0x1
+	   1 Unused                                    0x2
+	   2 Unused                                    0x4
+	   3 Unused                                    0x8
+	   4 Host Tx Interrupt Enable                 0x10
+	   5 Mute (0=Mute; 1=Play)                    0x20
+	   6 Master Interrupt Enable (1=Enabled)      0x40
+	   7 Master Reset (0=Reset; 1=Run)            0x80
+
+	   Take us out of reset, unmute, master + TX + RX interrupts on.
+	   
+	   We'll get an interrupt presumably to tell us that the TX
+	   register is clear. However, this doesn't mean that the
+	   board is ready. We actually have to send it a command, and
+	   wait till it gets back to use. After a cold boot, this can
+	   take some time.
+	   
+	   
+	   I think this is because its only after a cold boot that the
+	   onboard ROM does its memory check, which can take "up to 4
+	   seconds" according to the WaveFront SDK. So, since sleeping
+	   doesn't cost us much, we'll give it *plenty* of time. It
+	   turns out that with 12MB of RAM, it can take up to 16
+	   seconds or so!! See the code after "ABOUT INTERRUPTS"
+	*/
+
+	wavefront_should_cause_interrupt(hw,
+					 0x80|0x40|0x10|0x1,
+					 hw->control_port,
+					 (2*HZ)/100);
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+	 */
+
 	if (!hw->irq_ok) {
 		printk (KERN_WARNING
 			"WaveFront: intr not received after h/w un-reset.\n");
-		free_irq (hw_config->irq, NULL);
-		return 0;
-	} else {
-		hw->irq_ok = 0;
-	}
+		goto gone_bad;
+	} 
 
 	hw->interrupts_on = 1;
+	
+	/* ABOUT INTERRUPTS:
+	   -----------------
+	   
+	   When we talk about interrupts, there are two kinds
+	   generated by the ICS2115. The first is to signal MPU data
+	   ready to read, and the second is to signal RX or TX status
+	   changes. We *always* want interrupts for MPU stuff but we 
+	   generally avoid using RX/TX interrupts.
+
+	   In theory, we could use the TX and RX interrupts for all
+	   communication with the card. However, there are 2 good
+	   reasons not to do this.
+
+	   First of all, the MIDI interface is going to use the same
+	   interrupt. This presents no practical problem since Linux
+	   allows us to share IRQ's. However, there are times when it
+	   makes sense for a user to ask the driver to disable
+	   interrupts, to avoid bothering Linux with a stream of MIDI
+	   interrupts that aren't going to be used because nothing
+	   cares about them. If we rely on them for communication with
+	   the WaveFront synth as well, this disabling would be
+	   crippling. Since being able to disable them can save quite
+	   a bit of overhead (consider the interrupt frequency of a
+	   physical MIDI controller like a modwheel being shunted back
+	   and forth - its higher than the mouse, and much of
+	   the time is of absolutely no interest to the kernel or any
+	   user space processes whatsoever), we don't want to do this.
+
+	   Secondly, much of the time, there's no reason to go to
+	   sleep on a TX or RX status: the WaveFront gets back to us
+	   quickly enough that its a lot more efficient to just busy
+	   wait on the relevant status. Once we go to sleep, all is
+	   lost anyway, and so interrupts don't really help us much anyway.
+
+	   Therefore, we don't use interrupts for communication with
+	   the WaveFront synth. We just poll the relevant RX/TX status.
+
+	   However, there is one broad exception to this. During module
+	   loading, to deal with several situations where timing would
+	   be an issue, we use TX/RX interrupts to help us avoid busy
+	   waiting for indeterminate and hard to manage periods of
+	   time. So, TX/RX interrupts are enabled until the end of 
+	   wavefront_init(), and not used again after that.
+
+	 */
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+
+	   At this point, only "HW VERSION" or "DOWNLOAD OS" commands
+	   will work. So, issue one of them, and wait for TX
+	   interrupt. This can take a *long* time after a cold boot,
+	   while the ISC ROM does its RAM test. The SDK says up to 4
+	   seconds - with 12MB of RAM on a Tropez+, it takes a lot
+	   longer than that (~16secs). Note that the card understands
+	   the difference between a warm and a cold boot, so
+	   subsequent ISC2115 reboots (say, caused by module
+	   reloading) will get through this much faster.
 
-	/* WaveFront SDK says:
-     
-	   "When the Master Reset is set to zero (0), the audio board is held
-	   in reset, which is the power-up condition. Setting Master Reset to one
-	   (1) allows the on-board processor to run. It takes approximately two
-	   to four seconds, depending on the memory configuration, for the
-	   on-board processor to complete it's initialization routine before it
-	   will respond to commands after a reset."
-
-	   Actually, it seems that most of the time, even with 8MB of RAM,
-	   its actually ready immediately.
-	*/
-
-	if (!wavefront_wait (hw, STAT_CAN_WRITE)) {
-		if (!wavefront_wait (hw, STAT_CAN_WRITE)) {
-			if (!wavefront_wait (hw, STAT_CAN_WRITE)) {
-				printk (KERN_WARNING
-					"WaveFront: OS not ready after "
-					"memory check.\n");
-				free_irq (hw_config->irq, NULL);
-				return 0;
-			}
-		}
+	   Interesting question: why is no RX interrupt received first ?
+	*/
+	
+	wavefront_should_cause_interrupt(hw, WFC_HARDWARE_VERSION, 
+					 hw->data_port, 20*HZ);
+
+	if (!hw->irq_ok) {
+		printk (KERN_WARNING
+			"WaveFront: post-RAM-check interrupt not received.\n");
+		goto gone_bad;
+	} 
+
+	if (!(wavefront_status(hw) & STAT_CAN_READ)) {
+		printk (KERN_WARNING
+			"WaveFront: no response to HW version cmd.\n");
+		goto gone_bad;
+	}
+	
+	if ((hwv[0] = wavefront_read (hw)) == -1) {
+		printk (KERN_WARNING
+			"WaveFront: board not responding correctly.\n");
+		goto gone_bad;
 	}
 
-	/* get H/W version, and check we get an interrupt */
-  
-	outb (WFC_HARDWARE_VERSION, hw->data_port);
-  
-	for (i = 0; i < 1000000 && !hw->irq_ok; i++);
-  
-	/* We don't need the IRQ anymore for the WaveFront code,
-	   and to allow an MPU-401 driver to attach to it later, lets
-	   give it back ....
+	if (hwv[0] == 0xFF) { /* NAK */
 
-	   DO NOT alter irq2hw[], since we'll still use this to lookup
-	   the config struct from an address_info struct.
-	*/
+		/* Board's RAM test failed. Try to read error code,
+		   and tell us about it either way.
+		*/
+		
+		if ((hwv[0] = wavefront_read (hw)) == -1) {
+			printk (KERN_WARNING 
+				"WaveFront: on-board RAM test failed "
+				"(bad error code).\n");
+		} else {
+			printk (KERN_WARNING 
+				"WaveFront: on-board RAM test failed "
+				"(error code: 0x%x).\n",
+				hwv[0]);
+		}
+		goto gone_bad;
+	}
 
-	free_irq (hw_config->irq, NULL);
+	/* We're OK, just get the next byte of the HW version response */
 
-	if (!hw->irq_ok) {
+	if ((hwv[1] = wavefront_read (hw)) == -1) {
 		printk (KERN_WARNING
-			"WaveFront: interrupt not received after "
-			"h/w version cmd.\n");
-		return 0;
-	} else {
-		hw->irq_ok = 0;
+			"WaveFront: board not responding correctly(2).\n");
+		goto gone_bad;
+	}
+
+	printk (KERN_INFO "WaveFront: hardware version %d.%d\n",
+		hwv[0], hwv[1]);
+
+	return 0;
+
+
+     gone_bad:
+	free_irq (hw->irq, hw);
+	return (1);
+	}
+
+int
+probe_wavefront (struct address_info *hw_config)
+
+{
+	unsigned char   rbuf[4], wbuf[4];
+	wf_config       *hw;
+
+	if (hw_config->irq < 0 || hw_config->irq > 16) {
+	    printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", 
+		    hw_config->irq);
+	    return 0;
 	}
   
-	if ((tmp1 = wavefront_read(hw)) == -1) {
-		printk (KERN_WARNING
-			"WaveFront @ 0x%x not ready, ignoring\n", hw->base);
+	/* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus
+	   takes up another 8 ...
+	*/
+
+	if (check_region (hw_config->io_base, 16)) {
+		printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x "
+			"already in use - ignored\n", hw_config->io_base,
+			hw_config->io_base+15);
 		return 0;
 	}
   
-	if ((tmp2 = wavefront_read(hw)) == -1) {
-		printk (KERN_WARNING
-			"WaveFront @ 0x%x not responding correctly, ignoring\n",
-			hw->base);
+	hw = &wavefront_configuration;
+
+	hw->irq = hw_config->irq;
+	hw->base = hw_config->io_base;
+
+	hw->israw = 0;
+	hw->debug = debug_default;
+	hw->interrupts_on = 0;
+	hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */
+
+#ifdef WF_STATS
+	hw->status_found_during_sleep[0] = 0;
+	hw->status_found_during_sleep[1] = 0;
+	hw->status_found_during_sleep[2] = 0;
+	hw->status_found_during_sleep[3] = 0;
+	hw->status_found_during_loop = 0;
+#endif WF_STATS
+
+	hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1;
+	hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1;
+	hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1;
+
+	if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
+
+		hw->fw_version[0] = rbuf[0];
+		hw->fw_version[1] = rbuf[1];
+		printk (KERN_INFO
+			"WaveFront: firmware %d.%d already loaded.\n",
+			rbuf[0], rbuf[1]);
+
+		/* check that a command actually works */
+      
+		if (wavefront_cmd (hw, WFC_HARDWARE_VERSION,
+				   rbuf, wbuf) == 0) {
+			hw->hw_version[0] = rbuf[0];
+			hw->hw_version[1] = rbuf[1];
+		} else {
+			printk (KERN_INFO "WaveFront: not raw, but no "
+				"hardware version!\n");
+			return 0;
+		}
+
+		if (!wf_raw) {
+			return 1;
+		} else {
+			printk (KERN_INFO
+				"WaveFront: reloading firmware anyway.\n");
+		}
+
+	} else {
+
+		hw->israw = 1;
+		printk (KERN_INFO "WaveFront: no response to firmware probe, "
+			"assume raw.\n");
+
+	}
+
+	init_waitqueue (&hw->interrupt_sleeper);
+
+	if (wavefront_hw_reset (hw)) {
+		printk (KERN_WARNING "WaveFront: hardware reset failed\n");
 		return 0;
 	}
-  
-	printk (KERN_INFO "WaveFront: hardware version %d.%d\n", tmp1, tmp2);
-  
+
 	return 1;
 }
 
@@ -2465,7 +2624,8 @@
 	set_fs (get_ds());
 
 	if ((fd = open (path, 0, 0)) < 0) {
-		printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n", path);
+		printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n",
+			path);
 		return 1;
 	}
 
@@ -2512,9 +2672,13 @@
 					section_cnt_downloaded + 1, c);
 				goto failure;
 		
-			} else if ((hw->debug & WF_DEBUG_IO) &&
+			} else {
+#ifdef WF_DEBUG
+			    if ((hw->debug & WF_DEBUG_IO) &&
 				   !(++section_cnt_downloaded % 10)) {
 				printk (KERN_DEBUG ".");
+			    }
+#endif WF_DEBUG
 			}
 
 		} else {
@@ -2526,9 +2690,11 @@
 
 	close (fd);
 	set_fs (fs);
+#ifdef WF_DEBUG
 	if (hw->debug & WF_DEBUG_IO) {
 		printk (KERN_DEBUG "\n");
 	}
+#endif WF_DEBUG
 	return 0;
 
  failure:
@@ -2618,81 +2784,137 @@
 }
 
 static int
-wavefront_init (wf_config *hw, struct address_info *hw_config)
+wavefront_do_reset (wf_config *hw, int atboot)
 
 {
-	int samples_are_from_rom = 0;
+	char voices[1];
 
-	/* XXX what state does the board need to be in before
-	   I can download the firmware ? I think any state
-	   after it acknowledges a hardware version command
-	   post-booting.
-	*/
+	if (!atboot && wavefront_hw_reset (hw)) {
+		printk (KERN_WARNING "WaveFront: hw reset failed.\n");
+		goto gone_bad;
+	}
 
 	if (hw->israw || wf_raw) {
-		samples_are_from_rom = 1;
-
-		if (wavefront_download_firmware (hw, wf_ospath)) {
+		if (wavefront_download_firmware (hw, ospath)) {
+			goto gone_bad;
 			return 1;
 		}
-    
-		/* enter normal operation:
-		   bit 7: (on) reset  0x80
-		   bit 6: interrupts enabled 0x40
-		   bit 5: (on) mute (i.e. in play mode) 0x20
-		   bits 4-0: zero
+	}
+
+	if (fx_raw) {
+		wffx_init (hw);
+	}
+
+	/* If we loaded the OS, we now have to wait for it to be ready
+	   to roll. We can't guarantee that interrupts are enabled,
+	   because we might be reloading the module without forcing a
+	   reset/reload of the firmware.
 	   
-		   note: tx and rx interrupts turned off
-		*/
+	   Rather than busy-wait, lets just turn interrupts on.
+	*/
+
+	outb (0x80|0x40|0x10|0x1, hw->control_port);
+
+	wavefront_should_cause_interrupt (hw, WFC_NOOP,
+					  hw->data_port, (10*HZ));
 	
-		outb (0x80|0x40|0x20, hw->control_port);
+	if (!hw->irq_ok) {
+		printk (KERN_WARNING "WaveFront: no post-OS interrupt.\n");
+		goto gone_bad;
+	}
+
+	/* Now, do it again ! */
+
+	wavefront_should_cause_interrupt (hw, WFC_NOOP,
+					  hw->data_port, (10*HZ));
 	
-		/* Set up MPU-401 emulation mode.  For some reason, this always
-		   fails the first time, and generates no ACK, so we'll
-		   treat it as a special case by sleeping for a while, and then
-		   trying again.
-		*/
+	if (!hw->irq_ok) {
+		printk (KERN_WARNING "WaveFront: no post-OS interrupt(2).\n");
+		goto gone_bad;
+	}
 	
-		wavefront_write (hw, 0xf0);
-		wavefront_write (hw, 1);
-		wavefront_sleep (hw, (HZ/wf_sleep_interval) * wf_sleep_tries);
-		wavefront_write (hw, 0xf0);
-		wavefront_write (hw, 1);
-		if (wavefront_read (hw) != 0x80) {
-			printk (KERN_ERR
-				"WaveFront: set MPU emulation mode "
-				"command failed.\n");
-			return (1);
+	/* OK, no (RX/TX) interrupts any more, but leave mute
+	   on. Master interrupts get enabled when we're done here.
+	*/
+
+	outb (0x80, hw->control_port); 
+
+	/* No need for the IRQ anymore */
+
+	free_irq (hw->irq, hw);
+
+	/* SETUPSND.EXE asks for sample memory config here, but since i
+	   have no idea how to interpret the result, we'll forget
+	   about it.
+	*/
+	
+	if ((hw->freemem = wavefront_freemem (hw)) < 0) {
+		goto gone_bad;
+	}
+		
+	printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024);
+
+	if (!wavefront_write (hw, 0xf0) ||
+	    !wavefront_write (hw, 1) ||
+	    (wavefront_read (hw) < 0)) {
+		hw->debug = 0;
+		printk (KERN_WARNING "WaveFront: MPU emulation mode not set.\n");
+		goto gone_bad;
+	}
+
+	voices[0] = 32;
+
+	if (wavefront_cmd (hw, WFC_SET_NVOICES, 0, voices)) {
+		printk (KERN_WARNING
+			"WaveFront: cannot set number of voices to 32.\n");
+	}
+
+	return 0;
+
+ gone_bad:
+	/* reset that sucker so that it doesn't bother us. */
+
+	outb (0x0, hw->control_port);
+	free_irq (hw->irq, hw);
+	return 1;
+}
+
+static int
+wavefront_init (wf_config *hw, int atboot)
+
+{
+	int samples_are_from_rom;
+
+	if (hw->israw || wf_raw) {
+		samples_are_from_rom = 1;
+	} else {
+		samples_are_from_rom = 0;
+	}
+
+	if (hw->israw || wf_raw || fx_raw) {
+		if (wavefront_do_reset (hw, atboot)) {
+			return 1;
 		}
 	}
 
-	hw->freemem = wavefront_freemem (hw);
-	printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024);
-    
 	wavefront_get_sample_status (hw, samples_are_from_rom);
 	wavefront_get_program_status (hw);
 	wavefront_get_patch_status (hw);
 
-	if (fx_raw) {
-		wffx_init (hw);
-	}
-    
-	return 0;
+	/* Start normal operation: unreset, master interrupt enable
+	   (for MPU interrupts) no mute
+	*/
+
+	outb (0x80|0x40|0x20, hw->control_port); 
+
+	return (0);
 }
 
 void
 attach_wavefront (struct address_info *hw_config)
 {
 	int i;
-	struct wf_config *hw;
-
-	if ((i = irq2hw[hw_config->irq]) == -1) {
-		printk (KERN_ERR "WaveFront: cannot attach unknown irq 0x%x.\n",
-			hw_config->irq);
-		return;
-	}
-
-	hw = &wfs[i];
+	struct wf_config *hw = &wavefront_configuration;
 
 	if ((i = sound_alloc_synthdev()) == -1) {
 		printk (KERN_ERR "WaveFront: Too many synthesizers\n");
@@ -2700,15 +2922,13 @@
 	} else {
 		hw_config->slots[WF_SYNTH_SLOT] = i;
 		hw->synthdev = i;
-		dev2hw[hw->synthdev] = i;
 		synth_devs[hw->synthdev] = &wavefront_operations;
 	}
 
-	if (wavefront_init (hw, hw_config)) {
+	if (wavefront_init (hw, 1)) {
 		printk (KERN_WARNING "WaveFront: board could not "
 			"be initialized.\n");
 		sound_unload_synthdev (i);
-		hw->installed = 0;
 		return;
 	}
     
@@ -2731,18 +2951,11 @@
 void
 unload_wavefront (struct address_info *hw_config)
 {
-	struct wf_config *hw;
-	int i;
-
-	if ((i = irq2hw[hw_config->irq]) == -1) {
-	    printk (KERN_ERR "WaveFront: unloading unrecognized device!\n");
-	    return;
-	}
-	hw = &wfs[i];
+	struct wf_config *hw = &wavefront_configuration;
 
 	/* the first two are freed by the wf_mpu code */
-	release_region (hw_config->io_base+2, 6);
-	release_region (hw_config->io_base+8, 8);
+	release_region (hw->base+2, 6);
+	release_region (hw->base+8, 8);
 	sound_unload_synthdev (hw->synthdev);
 #if defined(CONFIG_MIDI)
 	unload_wf_mpu (hw_config);
@@ -2802,13 +3015,13 @@
 	if (page < 0 || page > 7) {
 		printk (KERN_ERR "WaveFront: FX memset: "
 			"page must be >= 0 and <= 7\n");
-		return -EINVAL;
+		return -(EINVAL);
 	}
 
 	if (addr < 0 || addr > 0x7f) {
 		printk (KERN_ERR "WaveFront: FX memset: "
 			"addr must be >= 0 and <= 7f\n");
-		return -EINVAL;
+		return -(EINVAL);
 	}
 
 	if (cnt == 1) {
@@ -2842,7 +3055,7 @@
 				"WaveFront: FX memset "
 				"(0x%x, 0x%x, 0x%x, %d) incomplete\n",
 				page, addr, (int) data, cnt);
-			return -EIO;
+			return -(EIO);
 		}
 	}
 
@@ -2866,18 +3079,17 @@
 		if (r->data[2] <= 0) {
 			printk (KERN_ERR "WaveFront: cannot write "
 				"<= 0 bytes to FX\n");
-			return -EINVAL;
+			return -(EINVAL);
 		} else if (r->data[2] == 1) {
 			pd = (unsigned short *) &r->data[3];
 		} else {
 			if (r->data[2] > sizeof (page_data)) {
-				printk (KERN_ERR
-					"WaveFront: cannot write "
+				printk (KERN_ERR "WaveFront: cannot write "
 					"> 255 bytes to FX\n");
-				return -EINVAL;
+				return -(EINVAL);
 			}
-			COPY_FROM_USER(page_data, (unsigned char *) r->data[3],
-				       r->data[2]);
+			copy_from_user (page_data, (unsigned char *) r->data[3],
+					r->data[2]);
 			pd = page_data;
 		}
 
@@ -2891,7 +3103,7 @@
 		printk (KERN_WARNING
 			"WaveFront: FX: ioctl %d not yet supported\n",
 			r->request);
-		return -EINVAL;
+		return -(EINVAL);
 	}
 }
 
@@ -3364,7 +3576,7 @@
 
 	outb (0x00, hw->fx_op); /* mute off */
 
-	return 0;
+	return (0);
 }
 
 EXPORT_NO_SYMBOLS;
@@ -3388,6 +3600,7 @@
 			"options must be set.\n");
 		return -EINVAL;
 	}
+
 	cfg.io_base = io;
 	cfg.irq = irq;
 
@@ -3406,7 +3619,6 @@
 	SOUND_LOCK_END;
 }
 
-#endif MODULE   
-#endif CONFIG_SOUND_WAVEFRONT 
+#endif CONFIG_SOUND_WAVEFRONT_MODULE_AND_MODULE
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov