diff -u --recursive --new-file v2.1.40/linux/Makefile linux/Makefile
--- v2.1.40/linux/Makefile	Sat May 24 09:10:22 1997
+++ linux/Makefile	Sat May 24 09:09:51 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 40
+SUBLEVEL = 41
 
 ARCH := $(shell uname -m | sed s/i.86/i386/ | sed s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.40/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.1.40/linux/drivers/isdn/isdn_net.c	Tue May 13 22:41:08 1997
+++ linux/drivers/isdn/isdn_net.c	Tue May 27 11:34:02 1997
@@ -374,7 +374,7 @@
 							return 1;
 						}
 					}
-					if (clear_bit(0, (void *) &(p->dev.tbusy)))
+					if (test_and_clear_bit(0, (void *) &(p->dev.tbusy)))
 						mark_bh(NET_BH);
 				}
 				return 1;
diff -u --recursive --new-file v2.1.40/linux/drivers/pnp/parport_procfs.c linux/drivers/pnp/parport_procfs.c
--- v2.1.40/linux/drivers/pnp/parport_procfs.c	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/parport_procfs.c	Sat May 24 09:07:57 1997
@@ -16,10 +16,10 @@
 #include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
-#include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>
 
 #include <linux/proc_fs.h>
 
diff -u --recursive --new-file v2.1.40/linux/drivers/pnp/parport_share.c linux/drivers/pnp/parport_share.c
--- v2.1.40/linux/drivers/pnp/parport_share.c	Wed Apr 23 19:01:21 1997
+++ linux/drivers/pnp/parport_share.c	Sat May 24 09:06:34 1997
@@ -220,6 +220,7 @@
 	tmp->preempt = pf;
 	tmp->wakeup = kf;
 	tmp->private = handle;
+	tmp->flags = flags;
 	tmp->irq_func = irq_func;
 	tmp->ctr = port->ctr;
 	tmp->ecr = port->ecr;
diff -u --recursive --new-file v2.1.40/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v2.1.40/linux/drivers/scsi/README.st	Wed Jan  1 08:54:02 1997
+++ linux/drivers/scsi/README.st	Tue May 27 13:20:09 1997
@@ -2,7 +2,7 @@
 The driver is currently maintained by Kai M{kisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Wed Jan  1 15:44:49 1997 by makisara@kai.makisara.fi
+Last modified: Tue May 27 22:29:24 1997 by makisara@home
 
 
 BASICS
@@ -159,6 +159,15 @@
         buffers is bounded also by the number of drives detected)
 
 
+MODULE PARAMETERS
+
+The same parameters can be also set when the driver is loaded as a
+module. The keywords are:
+
+buffer_kbs=xxx             the buffer size in kilobytes is set to xxx
+write_threshold_kbs=xxx    the write threshold in kilobytes set to xxx
+max_buffers=xxx            the maximum number of tape buffer set to xxx
+
 IOCTLS
 
 The tape is positioned and the drive parameters are set with ioctls
@@ -253,6 +262,7 @@
 		the device dependent address. It is recommended to set
 		this flag unless there are tapes using the device
 		dependent (from the old times) (global)
+	     MT_ST_SYSV sets the SYSV sematics (mode)
 	     MT_ST_DEBUGGING debugging (global; debugging must be
 		compiled into the driver)
 	MT_ST_SETBOOLEANS
@@ -272,6 +282,16 @@
 	   MT_ST_CLEAR_DEFAULT (0xfffff), the default will not be used
 	   any more. Otherwise the lower-most bits of the value contain
 	   the new value of the parameter.
+	MT_ST_SET_TIMEOUT
+	   Set the normal timeout in seconds for this device. The
+	   default is 900 seconds (15 minutes). The timeout should be
+	   long enough for the retries done by the device while
+	   reading/writing.
+	MT_ST_SET_LONG_TIMEOUT
+	   Set the long timeout that is used for operations that are
+	   known to take a long time. The default is 14000 seconds
+	   (3.9 hours). For erase this value is further multiplied by
+	   eight.
 
 The following ioctl uses the structure mtpos:
 MTIOCPOS Reads the current position from the drive. Uses
@@ -331,3 +351,23 @@
 time or the MT_ST_CAN_BSR bit is set for the drive with an ioctl.
 (The driver always backs over a filemark crossed by read ahead if the
 user does not request data that far.)
+
+
+DEBUGGING HINTS
+
+To enable debugging messages, edit st.c and #define DEBUG 1. As seen
+above, debugging can be switched off with an ioctl if debugging is
+compiled into the driver. The debugging output is not not voluminuous.
+
+If the tape seems to hang, I would be very interested to hear where
+the driver is waiting. With the command 'ps -l' you can see the state
+of the process using the tape. If the state is D, the process is
+waiting for something. The field WCHAN tells where the driver is
+waiting. If you have the current System.map in the correct place (in
+/boot for the procps I use) or have updated /etc/psdatabase (for kmem
+ps), ps writes the function name in the WCHAN field. If not, you have
+to look up the function from System.map.
+
+Note also that the timeouts are very long compared to most other
+drivers. This means that the Linux driver may appear hung although the
+real reason is that the tape firmware has got confused.
diff -u --recursive --new-file v2.1.40/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.1.40/linux/drivers/scsi/st.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/st.c	Tue May 27 13:20:09 1997
@@ -11,7 +11,7 @@
   Copyright 1992 - 1997 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Wed Jan  1 15:26:54 1997 by makisara@kai.makisara.fi
+  Last modified: Tue May 27 22:29:00 1997 by makisara@home
   Some small formal changes - aeb, 950809
 */
 
@@ -21,6 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/init.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/mtio.h>
@@ -48,14 +49,23 @@
 #include "st.h"
 #include "constants.h"
 
+#ifdef MODULE
+MODULE_PARM(buffer_kbs, "i");
+MODULE_PARM(write_threshold_kbs, "i");
+MODULE_PARM(max_buffers, "i");
+static int buffer_kbs = 0;
+static int write_threshold_kbs = 0;
+static int max_buffers = 0;
+#endif
+
 /* The default definitions have been moved to st_options.h */
 
-#define ST_BLOCK_SIZE 1024
+#define ST_KILOBYTE 1024
 
 #include "st_options.h"
 
-#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)
-#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)
+#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_KILOBYTE)
+#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
 
 /* The buffer size should fit into the 24 bits for length in the
    6-byte SCSI read and write commands. */
@@ -220,6 +230,7 @@
     }
     else
       (STp->buffer)->last_result = SCpnt->result;
+#if 0
     if ((STp->buffer)->writing) {
       /* Process errors before releasing request */
       (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
@@ -227,6 +238,10 @@
     }
     else
       SCpnt->request.rq_status = RQ_SCSI_DONE;
+#else
+    SCpnt->request.rq_status = RQ_SCSI_DONE;
+    (STp->buffer)->last_SCpnt = SCpnt;
+#endif
 
 #if DEBUG
     STp->write_pending = 0;
@@ -286,6 +301,9 @@
 
   down(&(STp->sem));
 
+  (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt);
+  ((STp->buffer)->last_SCpnt)->request.rq_status = RQ_INACTIVE;
+
   if (STbuffer->writing < STbuffer->buffer_bytes)
     memcpy(STbuffer->b_data,
 	   STbuffer->b_data + STbuffer->writing,
@@ -327,7 +345,7 @@
 	   TAPE_NR(STp->devt), forward ? "forward" : "backward");
 #endif
 
-  SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_TIMEOUT, MAX_RETRIES);
+  SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES);
   if (!SCpnt)
     return (-EBUSY);
 
@@ -387,7 +405,7 @@
     cmd[3] = blks >> 8;
     cmd[4] = blks;
 
-    SCpnt = st_do_scsi(NULL, STp, cmd, transfer, ST_TIMEOUT, MAX_WRITE_RETRIES);
+    SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES);
     if (!SCpnt)
       return (-EBUSY);
 
@@ -594,7 +612,7 @@
     memset ((void *) &cmd[0], 0, 10);
     cmd[0] = TEST_UNIT_READY;
 
-    SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_LONG_TIMEOUT, MAX_READY_RETRIES);
+    SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES);
     if (!SCpnt) {
 	if (scsi_tapes[dev].device->host->hostt->module)
 	    __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
@@ -608,7 +626,7 @@
       memset ((void *) &cmd[0], 0, 10);
       cmd[0] = TEST_UNIT_READY;
 
-      SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, ST_LONG_TIMEOUT, MAX_READY_RETRIES);
+      SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES);
 
       (STp->device)->was_reset = 0;
       STp->partition = STp->new_partition = 0;
@@ -649,7 +667,7 @@
       memset ((void *) &cmd[0], 0, 10);
       cmd[0] = READ_BLOCK_LIMITS;
 
-      SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, ST_TIMEOUT, MAX_READY_RETRIES);
+      SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES);
 
       if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
 	STp->max_block = ((STp->buffer)->b_data[1] << 16) |
@@ -675,7 +693,7 @@
     cmd[0] = MODE_SENSE;
     cmd[4] = 12;
 
-    SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, ST_TIMEOUT, MAX_READY_RETRIES);
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES);
 
     if ((STp->buffer)->last_result_fatal != 0) {
 #if DEBUG
@@ -807,7 +825,7 @@
 	static int
 scsi_tape_close(struct inode * inode, struct file * filp)
 {
-    int result;
+    int result = 0, result2;
     static unsigned char cmd[10];
     Scsi_Cmnd * SCpnt;
     Scsi_Tape * STp;
@@ -823,7 +841,7 @@
     STps = &(STp->ps[STp->partition]);
 
     if (STp->can_partitions &&
-	update_partition(inode) < 0) {
+	(result = update_partition(inode)) < 0) {
 #if DEBUG
       if (debugging)
 	printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev);
@@ -850,7 +868,7 @@
 	cmd[0] = WRITE_FILEMARKS;
 	cmd[4] = 1 + STp->two_fm;
 
-	SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_TIMEOUT, MAX_WRITE_RETRIES);
+	SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES);
 	if (!SCpnt)
 	  goto out;
 
@@ -862,8 +880,12 @@
 	     ((SCpnt->sense_buffer[0] & 0x80) != 0 &&
 	      (SCpnt->sense_buffer[3] | SCpnt->sense_buffer[4] |
 	       SCpnt->sense_buffer[5] |
-	       SCpnt->sense_buffer[6]) == 0)))  /* Filter out successful write at EOM */
-	  printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
+	       SCpnt->sense_buffer[6]) == 0))) {
+	    /* Filter out successful write at EOM */
+	    printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
+	    if (result == 0)
+		result = (-EIO);
+	}
 	else {
 	  if (STps->drv_file >= 0)
 	      STps->drv_file++ ;
@@ -884,9 +906,10 @@
 	STps = &(STp->ps[STp->partition]);
 	if (!STm->sysv || STps->rw != ST_READING) {
 	    if (STp->can_bsr)
-		flush_buffer(inode, filp, 0);
+		result = flush_buffer(inode, filp, 0);
 	    else if (STps->eof == ST_FM_HIT) {
-		if (cross_eof(STp, FALSE)) {
+		result = cross_eof(STp, FALSE);
+		if (result) {
 		    if (STps->drv_file >= 0)
 			STps->drv_file++;
 		    STps->drv_block = 0;
@@ -896,7 +919,8 @@
 		    STps->eof = ST_NOEOF;
 	    }
 	}
-	else if ((STps->eof == ST_NOEOF && !cross_eof(STp, TRUE)) ||
+	else if ((STps->eof == ST_NOEOF &&
+		  !(result = cross_eof(STp, TRUE))) ||
 		 STps->eof == ST_FM_HIT) {
 	    if (STps->drv_file >= 0)
 		STps->drv_file++;
@@ -906,8 +930,11 @@
     }
 
 out:
-    if (STp->rew_at_close)
-      st_int_ioctl(inode, MTREW, 1);
+    if (STp->rew_at_close) {
+      result2 = st_int_ioctl(inode, MTREW, 1);
+      if (result == 0)
+	  result = result2;
+    }
 
     if (STp->door_locked == ST_LOCKED_AUTO)
       st_int_ioctl(inode, MTUNLOCK, 0);
@@ -923,7 +950,7 @@
     if(st_template.module)
 	__MOD_DEC_USE_COUNT(st_template.module);
 
-    return 0;
+    return result;
 }
 
 
@@ -1083,7 +1110,7 @@
       cmd[3] = blks >> 8;
       cmd[4] = blks;
 
-      SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, ST_TIMEOUT, MAX_WRITE_RETRIES);
+      SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES);
       if (!SCpnt)
 	return (-EBUSY);
 
@@ -1177,7 +1204,8 @@
     }
 
     if (STm->do_async_writes &&
-	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
+	(((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+	  (STp->buffer)->buffer_bytes >= STp->block_size) ||
 	 STp->block_size == 0) ) {
       /* Schedule an asynchronous write */
       if (!SCpnt) {
@@ -1211,7 +1239,7 @@
       scsi_do_cmd (SCpnt,
 		   (void *) cmd, (STp->buffer)->b_data,
 		   (STp->buffer)->writing,
-		   st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+		   st_sleep_done, STp->timeout, MAX_WRITE_RETRIES);
     }
     else if (SCpnt != NULL)
       SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
@@ -1270,7 +1298,7 @@
     cmd[4] = blks;
 
     SCpnt = *aSCpnt;
-    SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, ST_TIMEOUT, MAX_RETRIES);
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES);
     *aSCpnt = SCpnt;
     if (!SCpnt)
 	return (-EBUSY);
@@ -1572,6 +1600,8 @@
 "st%d:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
 	 dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
 	 STp->scsi2_logical);
+  printk(KERN_INFO
+"st%d:    sysv: %d\n", dev, STm->sysv);
 #if DEBUG
   printk(KERN_INFO
 	 "st%d:    debugging: %d\n",
@@ -1615,6 +1645,7 @@
     if ((STp->device)->scsi_level >= SCSI_2)
       STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
     STp->scsi2_logical    = (options & MT_ST_SCSI2LOGICAL) != 0;
+    STm->sysv		  = (options & MT_ST_SYSV) != 0;
 #if DEBUG
     debugging = (options & MT_ST_DEBUGGING) != 0;
 #endif
@@ -1645,6 +1676,8 @@
       STp->can_partitions = value;
     if ((options & MT_ST_SCSI2LOGICAL) != 0)
       STp->scsi2_logical = value;
+    if ((options & MT_ST_SYSV) != 0)
+      STm->sysv = value;
 #if DEBUG
     if ((options & MT_ST_DEBUGGING) != 0)
       debugging = value;
@@ -1652,7 +1685,7 @@
     st_log_options(STp, STm, dev);
   }
   else if (code == MT_ST_WRITE_THRESHOLD) {
-    value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE;
+    value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
     if (value < 1 || value > st_buffer_size) {
       printk(KERN_WARNING "st%d: Write threshold %d too small or too large.\n",
 	     dev, value);
@@ -1674,6 +1707,19 @@
 	     dev, STm->default_blksize);
     }
   }
+  else if (code == MT_ST_TIMEOUTS) {
+    value = (options & ~MT_ST_OPTIONS);
+    if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+      STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+      printk(KERN_INFO "st%d: Long timeout set to %d seconds.\n", dev,
+	     (value & ~MT_ST_SET_LONG_TIMEOUT));
+    }
+    else {
+      STp->timeout = value * HZ;
+      printk(KERN_INFO "st%d: Normal timeout set to %d seconds.\n", dev,
+	     value);
+    }
+  }
   else if (code == MT_ST_DEF_OPTIONS) {
     code = (options & ~MT_ST_CLEAR_DEFAULT);
     value = (options & MT_ST_CLEAR_DEFAULT);
@@ -1746,7 +1792,7 @@
   cmd[2] = COMPRESSION_PAGE;
   cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
 
-  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_TIMEOUT, 0);
+  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0);
   if (SCpnt == NULL)
     return (-EBUSY);
   dev = TAPE_NR(SCpnt->request.rq_dev);
@@ -1789,7 +1835,7 @@
   (STp->buffer)->b_data[0] = 0;  /* Reserved data length */
   (STp->buffer)->b_data[1] = 0;  /* Reserved media type byte */
   (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
-  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_TIMEOUT, 0);
+  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0);
 
   if ((STp->buffer)->last_result_fatal != 0) {
 #if DEBUG
@@ -1817,7 +1863,7 @@
 st_int_ioctl(struct inode * inode,
 	     unsigned int cmd_in, unsigned long arg)
 {
-   int timeout = ST_LONG_TIMEOUT;
+   int timeout;
    long ltmp;
    int i, ioctl_result;
    int chg_eof = TRUE;
@@ -1831,6 +1877,7 @@
    STp = &(scsi_tapes[dev]);
    if (STp->ready != ST_READY && cmd_in != MTLOAD)
      return (-EIO);
+   timeout = STp->long_timeout;
    STps = &(STp->ps[STp->partition]);
    fileno = STps->drv_file;
    blkno = STps->drv_block;
@@ -1961,7 +2008,7 @@
        cmd[2] = (arg >> 16);
        cmd[3] = (arg >> 8);
        cmd[4] = arg;
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #if DEBUG
        if (debugging) {
 	 if (cmd_in == MTWEOF)
@@ -1981,7 +2028,7 @@
        cmd[0] = REZERO_UNIT;
 #if ST_NOWAIT
        cmd[1] = 1;  /* Don't wait for completion */
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #endif
 #if DEBUG
        if (debugging)
@@ -2012,9 +2059,9 @@
        }
 #if ST_NOWAIT
        cmd[1] = 1;  /* Don't wait for completion */
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #else
-       timeout = ST_LONG_TIMEOUT * 8;
+       timeout = STp->long_timeout;
 #endif
 #if DEBUG
        if (debugging) {
@@ -2037,7 +2084,7 @@
        cmd[0] = START_STOP;
 #if ST_NOWAIT
        cmd[1] = 1;  /* Don't wait for completion */
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #endif
        cmd[4] = 3;
 #if DEBUG
@@ -2076,9 +2123,9 @@
        cmd[1] = 1;  /* To the end of tape */
 #if ST_NOWAIT
        cmd[1] |= 2;  /* Don't wait for completion */
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #else
-       timeout = ST_LONG_TIMEOUT * 8;
+       timeout = STp->long_timeout * 8;
 #endif
 #if DEBUG
        if (debugging)
@@ -2147,7 +2194,7 @@
        (STp->buffer)->b_data[9] = (ltmp >> 16);
        (STp->buffer)->b_data[10] = (ltmp >> 8);
        (STp->buffer)->b_data[11] = ltmp;
-       timeout = ST_TIMEOUT;
+       timeout = STp->timeout;
 #if DEBUG
        if (debugging) {
 	 if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
@@ -2338,7 +2385,7 @@
       if (!logical && !STp->scsi2_logical)
 	scmd[1] = 1;
     }
-    SCpnt = st_do_scsi(NULL, STp, scmd, 20, ST_TIMEOUT, MAX_READY_RETRIES);
+    SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES);
     if (!SCpnt)
       return (-EBUSY);
 
@@ -2394,13 +2441,14 @@
     int dev = TAPE_NR(inode->i_rdev);
     int result, p;
     unsigned int blk;
-    int timeout = ST_LONG_TIMEOUT;
+    int timeout;
     unsigned char scmd[10];
     Scsi_Cmnd *SCpnt;
 
     STp = &(scsi_tapes[dev]);
     if (STp->ready != ST_READY)
       return (-EIO);
+    timeout = STp->long_timeout;
     STps = &(STp->ps[STp->partition]);
 
 #if DEBUG
@@ -2457,7 +2505,7 @@
      }
 #if ST_NOWAIT
     scmd[1] |= 1;  /* Don't wait for completion */
-    timeout = ST_TIMEOUT;
+    timeout = STp->timeout;
 #endif
 
     SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES);
@@ -2554,7 +2602,7 @@
     cmd[2] = PART_PAGE;
     cmd[4] = 200;
 
-    SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, ST_TIMEOUT, MAX_READY_RETRIES);
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES);
     if (SCpnt == NULL)
       return (-EBUSY);
     SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
@@ -2628,7 +2676,7 @@
     cmd[1] = 0x10;
     cmd[4] = length + MODE_HEADER_LENGTH;
 
-    SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_LONG_TIMEOUT, MAX_READY_RETRIES);
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout, MAX_READY_RETRIES);
     if (SCpnt == NULL)
       return (-EBUSY);
     SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
@@ -2979,13 +3027,13 @@
 /* Set the boot options. Syntax: st=xxx,yyy
    where xxx is buffer size in 1024 byte blocks and yyy is write threshold
    in 1024 byte blocks. */
-	void
-st_setup(char *str, int *ints)
+	__initfunc( void
+st_setup(char *str, int *ints))
 {
   if (ints[0] > 0 && ints[1] > 0)
-    st_buffer_size = ints[1] * ST_BLOCK_SIZE;
+    st_buffer_size = ints[1] * ST_KILOBYTE;
   if (ints[0] > 1 && ints[2] > 0) {
-    st_write_threshold = ints[2] * ST_BLOCK_SIZE;
+    st_write_threshold = ints[2] * ST_KILOBYTE;
     if (st_write_threshold > st_buffer_size)
       st_write_threshold = st_buffer_size;
   }
@@ -3045,12 +3093,14 @@
    tpnt->can_partitions = 0;
    tpnt->two_fm = ST_TWO_FM;
    tpnt->fast_mteom = ST_FAST_MTEOM;
-   tpnt->scsi2_logical = 0;
+   tpnt->scsi2_logical = ST_SCSI2LOGICAL;
    tpnt->write_threshold = st_write_threshold;
    tpnt->default_drvbuffer = 0xff; /* No forced buffering */
    tpnt->partition = 0;
    tpnt->new_partition = 0;
    tpnt->nbr_partitions = 0;
+   tpnt->timeout = ST_TIMEOUT;
+   tpnt->long_timeout = ST_LONG_TIMEOUT;
 
    for (i=0; i < ST_NBR_MODES; i++) {
      STm = &(tpnt->modes[i]);
@@ -3099,7 +3149,7 @@
 
 static int st_registered = 0;
 
-/* Driver initialization */
+/* Driver initialization (not __initfunc because may be called later) */
 static int st_init()
 {
   int i;
@@ -3213,8 +3263,25 @@
 #ifdef MODULE
 
 int init_module(void) {
+  int result;
+
   st_template.module = &__this_module;
-  return scsi_register_module(MODULE_SCSI_DEV, &st_template);
+  result = scsi_register_module(MODULE_SCSI_DEV, &st_template);
+  if (result)
+      return result;
+
+  if (buffer_kbs > 0)
+      st_buffer_size = buffer_kbs * ST_KILOBYTE;
+  if (write_threshold_kbs > 0)
+      st_write_threshold = write_threshold_kbs * ST_KILOBYTE;
+  if (st_write_threshold > st_buffer_size)
+      st_write_threshold = st_buffer_size;
+  if (max_buffers > 0)
+      st_max_buffers = max_buffers;
+printk(KERN_INFO "st: bufsize %d, wrt %d, max buffers %d.\n",
+st_buffer_size, st_write_threshold, st_max_buffers);
+
+  return 0;
 }
 
 void cleanup_module( void)
diff -u --recursive --new-file v2.1.40/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v2.1.40/linux/drivers/scsi/st.h	Wed Jan  1 08:54:02 1997
+++ linux/drivers/scsi/st.h	Tue May 27 13:20:09 1997
@@ -20,6 +20,7 @@
   int writing;
   int last_result;
   int last_result_fatal;
+  Scsi_Cmnd *last_SCpnt;
   unsigned char *b_data;
   int orig_size;
   unsigned char *orig_b_data;
@@ -78,6 +79,8 @@
   unsigned char scsi2_logical;
   unsigned char default_drvbuffer;  /* 0xff = don't touch, value 3 bits */
   int write_threshold;
+  int timeout;			/* timeout for normal commands */
+  int long_timeout;		/* timeout for commands known to take long time*/
 
   /* Mode characteristics */
   ST_mode modes[ST_NBR_MODES];
diff -u --recursive --new-file v2.1.40/linux/drivers/scsi/st_options.h linux/drivers/scsi/st_options.h
--- v2.1.40/linux/drivers/scsi/st_options.h	Thu Jan  2 10:27:46 1997
+++ linux/drivers/scsi/st_options.h	Tue May 27 13:20:09 1997
@@ -3,7 +3,7 @@
 
    Copyright 1995 Kai Makisara.
 
-   Last modified: Thu Dec 14 21:51:27 1995 by root@kai.makisara.fi
+   Last modified: Tue May 27 22:29:15 1997 by makisara@home
 */
 
 #ifndef _ST_OPTIONS_H
@@ -88,8 +88,14 @@
    files and the file number status is retained. */
 #define ST_FAST_MTEOM 0
 
+/* If ST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
+   MTIOCPOS and MTSEEK by default. Vendor addresses are used if ST_SCSI2LOGICAL
+   is zero. */
+#define ST_SCSI2LOGICAL 0
+
 /* If ST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
    The default is BSD semantics. */
 #define ST_SYSV 0
+
 
 #endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/.version linux/drivers/sound/.version
--- v2.1.40/linux/drivers/sound/.version	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/.version	Sun May 11 02:58:40 1997
@@ -1,2 +1,2 @@
-3.8-beta9
-0x030803
+3.8a
+0x030804
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v2.1.40/linux/drivers/sound/CHANGELOG	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/CHANGELOG	Sun May 11 02:58:43 1997
@@ -1,5 +1,11 @@
-Changelog for version 3.8-beta8
--------------------------------
+Changelog for version 3.8
+--------------------------
+
+Since 3.8-beta21
+- Fixed all known bugs (I think).
+
+Since 3.8-beta8
+- Lot of fixes to audio playback code in dmabuf.c
 
 Since 3.8-beta6
 - Fixed the famous Quake delay bug.
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.40/linux/drivers/sound/Config.in	Tue May 13 22:41:13 1997
+++ linux/drivers/sound/Config.in	Sun May 11 02:59:35 1997
@@ -1,277 +1,15 @@
-bool 'ProAudioSpectrum 16 support' CONFIG_PAS
-bool '_TRUE_ Sound Blaster (SB, SBPro, SB16/32/64, ESS, Jazz16) support' CONFIG_SB
-bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB
-bool 'Gravis Ultrasound support' CONFIG_GUS
-bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401
-bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS
-bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16
-bool 'GUS MAX support' CONFIG_GUSMAX
-bool 'Microsoft Sound System support' CONFIG_MSS
-bool 'Ensoniq SoundScape support' CONFIG_SSCAPE
-bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX
-bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16
-bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232
-bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI
-bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812
-
-if [ "$CONFIG_AEDSP16" = "y" ]; then
-hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'I/O base for SB Check from manual of the card' SBC_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'Enter -1 to the following question if you have something else such as SB16/32.'
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS DMA 0, 1 or 3' GUS16_DMA 3
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9
-fi
-
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with Maui.'
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-int 'UART6850 IRQ (Unknown)' U6850_IRQ -1
-fi
-
-
-if [ "$CONFIG_PSS" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with PSS cards.'
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS I/O base 220 or 240' PSS_BASE 220
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11
-fi
-
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with AudioTrix.'
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9
-fi
 #
-$MAKE -C drivers/sound kernelconfig || exit 1
+# Sound driver configuration
+#
+#--------
+# There is another confic script which is compatible with rest of
+# the kernel. It can be activated by running 'make mkscript' in this
+# directory. Please note that this is an _experimental_ feature which
+# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
+#--------
+#
+$MAKE -C drivers/sound config || exit 1
+
 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
 
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
@@ -306,7 +44,7 @@
          fi
       fi
 
-      if [ "$CONFIG_MIDI" = "y" ]; then
+      if [ "$CONFIG_MPU401" = "y" ]; then
          bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
          if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
 	    comment 'Audio Excel DSP 16 [MPU-401]'
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Config.std linux/drivers/sound/Config.std
--- v2.1.40/linux/drivers/sound/Config.std	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/Config.std	Sun May 11 02:59:35 1997
@@ -44,7 +44,7 @@
          fi
       fi
 
-      if [ "$CONFIG_MIDI" = "y" ]; then
+      if [ "$CONFIG_MPU401" = "y" ]; then
          bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
          if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
 	    comment 'Audio Excel DSP 16 [MPU-401]'
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.40/linux/drivers/sound/Makefile	Sun Nov 17 14:33:33 1996
+++ linux/drivers/sound/Makefile	Sun May 11 02:58:39 1997
@@ -4,7 +4,33 @@
 # parent makes. (hopefully)
 #
 #
+#
+#
+ifeq ($(ARCH),m68k)
+  L_TARGET := sound.a
+  L_OBJS   :=
+  M_OBJS   :=
+  ifeq ($(CONFIG_DMASOUND),y)
+    L_OBJS += dmasound.o
+  else
+    ifeq ($(CONFIG_DMASOUND),m)
+    M_OBJS += dmasound.o
+    endif
+  endif
+
+  include $(TOPDIR)/Rules.make
+
+  clean:
+	rm -f core *.o *.a *.s
+
+  # dummy rule to keep 'make xconfig' happy
+  mkscript:
+
+# More dummy targets for make [menu]config
+mkscript:
+kernelconfig:
 
+else
 .PHONY: dummy
 SUB_DIRS	= lowlevel
 VERSION		= `head -1 .version`
@@ -36,6 +62,7 @@
 
 install:	sound.o
 	cp sound.o $(MODULEDIR)
+else
 endif
 
 .c.o:
@@ -159,5 +186,6 @@
 #
 ifeq (.depend,$(wildcard .depend))
 include .depend
+endif
 endif
 endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.1.40/linux/drivers/sound/Readme	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/Readme	Sun May 11 02:58:41 1997
@@ -1,5 +1,5 @@
-OSS Lite version 3.8-beta release notes
----------------------------------------
+OSS Lite version 3.8 release notes
+----------------------------------
 
 Most up to date information about this driver is available from 
 http://www.4front-tech.com/ossfree or http://personal.eunet.fi/pp/voxware
@@ -16,10 +16,10 @@
 with OSS.
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 2.1.26 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 2.1.36 OR LATER.
    ====================================================
 
-Packages "snd-util-3.7.tar.gz" and "snd-data-0.1.tar.Z"
+Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z"
 contain useful utilities to be used with this driver.
 See http://www.4front-tech.com/ossfree/getting.html for
 download instructions.
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.1.40/linux/drivers/sound/Readme.cards	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/Readme.cards	Sun May 11 02:58:42 1997
@@ -43,8 +43,8 @@
 
 
 
-THE BIGGEST MISTAKE YOU CAN DO
-==============================
+THE BIGGEST MISTAKES YOU CAN DO
+===============================
 
 1. Assuming that the card is Sound Blaster compatible when it's not.
 --------------------------------------------------------------------
@@ -106,21 +106,24 @@
 Many PnP cards are simply too much different than their original non PnP
 ancestors which are covered by this document.
 
+
 Cards that are not (fully) supported by this driver
 ===================================================
 
 See http://www.4front-tech.com/ossfree for information about soundcards 
 to be supported in future. 
 
+
 How to use sound without recompiling kernel and/or sound driver
----------------------------------------------------------------
+===============================================================
 
 There is commercial sound driver which should be released during Apr 96.
 It comes in precompiled form and doesn't require recompiling of kernel. See
 http://www.4Front-tech.com/oss.html for more info.
 
+
 Configuring PnP cards
----------------------
+=====================
 
 New versions of most soundcards use so called ISA PnP protocol for
 soft configuring their I/O, IRQ, DMA and shared memory resources.
@@ -170,8 +173,9 @@
 you probably like to do it if you don't waste hours of time in recompiling
 kernel and the required tools.
 
+
 Read this before trying to configure the driver
------------------------------------------------
+===============================================
 
 There are currently many cards that work with this driver. Some of the cards
 have native support while others work since they emulate some other
@@ -196,12 +200,14 @@
 	SB 1.0 to 2.0
 	SB Pro
 	SB 16
-	SB32/AWE
-		Configure SB32/AWE just like SB16. See lowlevel/README.awe
+	SB32/64/AWE
+		Configure SB32/64/AWE just like SB16. See lowlevel/README.awe
 		for information about using the wave table synth.
+	        NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated
+		      using isapnptools before they work with OSS/Free.
 	SB16 compatible cards by other manufacturers than Creative.
 		You have been fooled since there are _no_ SB16 compatible
-		cards on the market (Feb 96). It's likely that your card
+		cards on the market (May 97). It's likely that your card
 		is compatible just with SB Pro but there is also a non-SB-
 		compatible 16 bit mode. Usually it's MSS/WSS but it could also
 		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
@@ -237,7 +243,7 @@
 	GUS + the 16 bit option
 	GUS MAX
 	GUS ACE (No MIDI port and audio recording)
-	GUS PnP (in GUS MAX compatible mode)
+	GUS PnP (with RAM)
 
 MPU-401	and compatibles
 	The driver works both with the full (intelligent mode) MPU-401
@@ -267,10 +273,6 @@
 	cause a conflict. So check if your card is listed in this file before
 	enabling the MSS support.
 
-6850 UART MIDI
-	This UART chip is used in the MIDI interface of some (rare)
-	soundcards. It's supported by the driver in case you need it.
-
 Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
 	Most soundcards have a FM synthesizer chip. The OPL2 is a 2
 	operator chip used in the original AdLib card. Currently it's used
@@ -321,7 +323,9 @@
 	Several companies (including Ensoniq, Reveal and Spea) are selling
 	cards based on this architecture.
 
-	NOTE! The new PnP SoundScape is not supported yet.
+	NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and
+	VIVO90 cards are not compatible with Soundscapes so the Soundscape driver
+	will not work with them. You may want to use OSS/Linux with these cards.
 
 MAD16 and Mozart based cards
 	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
@@ -332,20 +336,32 @@
 	interface chip performs address decoding for the other chips.
 	NOTE! Tropez Plus is not MAD16 but CS4232 based.
 	NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible
-	in the PnP mode. You will have to use them in MAD16 mode after having
-	initialized them using isapnptools or DOS.
+	in the PnP mode. You will have to use them in MSS mode after having
+	initialized them using isapnptools or DOS. 82C931 probably requires
+	initialization using DOS/Windows (running isapnptools is not enough).
+	It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP
+	mode (provided that the card has a jumper for this). In non-PnP mode
+	82C931 is compatible with 82C930 and should work with the MAD16 driver
+	(without need to use isapnptools or DOS to initialize it). All OPTi
+	chips are supported by OSS/Linux (both in PnP and non-PnP modes).
 
 Audio Excel DSP16 
 	Support for this card was written by Riccardo Faccetti
 	(riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in
 	the lowlevel/ directory. To use it you should use the "new" config
 	script and to enable the "Additional low level drivers" option.
-Crystal CS4232 and 4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
+
+Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
 	many PC motherboards (Compaq, HP, Intel, ...)
 	CS4232 is a PnP multimedia chip which contains a CS3231A codec,
 	SB and MPU401 emulations. There is support for OPL3 too.
 	Unfortunately the MPU401 mode doesn't work (I don't know how to
-	initialize it). CS4236 is an enhanced (compatible) version of 4232.
+	initialize it). CS4236 is an enhanced (compatible) version of CS4232.
+	NOTE! Don't ever try to use isapnptools with CS4232 since this just
+	freezes your machine (due to chip bugs). If you have problems in getting
+	CS4232 working you could try initializing it with DOS (CS4232C.EXE) and
+	then booting Linux using loadlin. CS4232C.EXE loads a secret firmware
+	patch which is not documented by Crystal.
 
 Turtle Beach Maui and Tropez "classic"
 	This driver version supports sample, patch and program loading commands
@@ -354,9 +370,13 @@
 	the Tropez is based on the MAD16 chip (see above).
 	NOTE! You will have to use the "old" config script when configuring
 	Maui or Tropez.
+	NOTE! Tropez Plus is different card than Tropez "classic" and will not
+	work fully in Linux. You can get audio features working by configuring
+	the card as a CS4232 based card (above).
+
 
 Jumpers and software configuration
-----------------------------------
+==================================
 
 Some of the earliest soundcards were jumper configurable. You have to
 configure the driver use I/O, IRQ and DMA settings
@@ -388,8 +408,9 @@
 even if they are otherwise compatible with some other cards (like SB,
 MPU401 or Windows Sound System).
 
+
 What if your card was not listed above?
----------------------------------------
+=======================================
 
 The first thing to do is to look at the major IC chips on the card.
 Many of the latest soundcards are based on some standard chips. If you
@@ -518,8 +539,19 @@
 	  that doesn't really have a MPU401 could cause some trouble. If your
 	  card was in the list of supported cards (above), please look at
 	  the card specific instructions later in this file.
+
+	  In MOST cases this MPU401 driver should only be used with "true"
+	  MIDI-only MPU401 professional cards. In most other cases there
+	  is another way to get the MPU401 compatible interface of a
+	  soundcard to work.
+	  Support for the MPU401 compatible MIDI port of SB16, ESS1688
+	  and MV Jazz16 cards is included in the SB driver. Use it instead
+	  of this separate MPU401 driver with these cards. As well 
+	  Soundscape, PSS and Maui drivers include their own MPU401
+	  options.
+
 	  It's safe to answer 'y' if you have a true MPU401 MIDI interface
-	  card.
+	  card. 
   "6850 UART Midi support",
 	- It's safe to answer 'n' to this question in all cases. The 6850
 	  UART interface is so rarely used.
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.1.40/linux/drivers/sound/Readme.linux	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/Readme.linux	Sun May 11 02:58:40 1997
@@ -72,171 +72,14 @@
 Hannu Savolainen
 hannu@voxware.pp.fi
 
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-NOTE!
-
-Running the script enclosed below is usually not required. All known Linux
-distributions build them automaticly during installation. You need to run
-this script only if "ls /dev/sndstat" displays "No such file or directory".
-In case of any other error message you should start looking for the reason
-from somewhere else (see above).
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 ----------------- cut here ------------------------------
-#!/bin/sh
-# *****************************************
-# * NOTICE!
-# *
-# * For security reasons read access to /dev/dsp* and /dev/audio* has been
-# * disabled from other than root. Otherwise any user may be able to spy
-# * what is being talked about near the microphone.
-# * This effectively disables audio recording by other than root. In case
-# * this capability is required, you should change AUDIOPERMS (below) to 666
-# * before executing this script.
-# *****************************************
-AUDIOPERMS=622
-#
-#
-#
-#
-# Create the devices
-#
-#	Mixer devices
-#
-if [ -e /dev/mixer ]; then 
-	rm -f /dev/mixer 
-fi
-
-if [ -e /dev/mixer0 ]; then 
-	rm -f /dev/mixer0 
-fi
- 
-mknod -m 666 /dev/mixer0 c 14 0
-ln -sf /dev/mixer0 /dev/mixer
-
-if [ -e /dev/mixer1 ]; then 
-	rm -f /dev/mixer1 
-fi
-mknod -m 666 /dev/mixer1 c 14 16
-
-
-#	Sequencer	(14, 1)
-#
-if [ -e /dev/sequencer ]; then
-	rm -f /dev/sequencer
-fi
-mknod -m 666 /dev/sequencer c 14 1
-
-if [ -e /dev/patmgr0 ]; then
-	rm -f /dev/patmgr0
-fi
-mknod -m 666 /dev/patmgr0 c 14 17
-if [ -e /dev/patmgr1 ]; then
-	rm -f /dev/patmgr1
-fi
-mknod -m 666 /dev/patmgr1 c 14 33
-
-	#	music	(14, 8)
-	#
-	if [ -e /dev/music ]; then
-		rm -f /dev/music
-	fi
-
-	mknod -m 666 /dev/music c 14 8
-	if [ -e /dev/sequencer2 ]; then
-		rm -f /dev/sequencer2
-	fi
-	ln -s /dev/music /dev/sequencer2
-
-#	Midi devices
-#
-if [ -e /dev/midi ]; then
-	rm -f /dev/midi		# Old name. Don't use it
-fi
-	if [ -e /dev/midi00 ]; then
-		rm -f /dev/midi00
-	fi
-	mknod -m 666 /dev/midi00 c 14 2
-	ln -sf /dev/midi00 /dev/midi
-	
-	if [ -e /dev/midi01 ]; then
-		rm -f /dev/midi01
-	fi
-	mknod -m 666 /dev/midi01 c 14 18
-	
-	if [ -e /dev/midi02 ]; then
-		rm -f /dev/midi02
-	fi
-	mknod -m 666 /dev/midi02 c 14 34
-	
-	if [ -e /dev/midi03 ]; then
-		rm -f /dev/midi03
-	fi
-	mknod -m 666 /dev/midi03 c 14 50
-#
-#	DSP		(14, 3)
-#
-if [ -e /dev/dsp ]; then
-	rm -f /dev/dsp
-fi
-if [ -e /dev/dsp0 ]; then
-	rm -f /dev/dsp0
-fi
-mknod -m $AUDIOPERMS /dev/dsp0 c 14 3
-ln -s /dev/dsp0 /dev/dsp
-
-#
-#	DSPW		(14, 5)
-#
-if [ -e /dev/dspW ]; then
-	rm -f /dev/dspW
-fi
-if [ -e /dev/dspW0 ]; then
-	rm -f /dev/dspW0
-fi
-mknod -m $AUDIOPERMS /dev/dspW0 c 14 5
-ln -s /dev/dspW0 /dev/dspW
+SURPRISE SURPRISE!!!
 
-if [ -e /dev/dspW1 ]; then
-	rm -f /dev/dspW1
-fi
-mknod -m $AUDIOPERMS /dev/dspW1 c 14 37
+The device file creation script that used to be here earlier is 
+obviously not here any more.
 
-#
-#	SPARC compatible /dev/audio	(14, 4)	
-#
-if [ -e /dev/audio ]; then
-	rm -f /dev/audio
-fi
-if [ -e /dev/audio0 ]; then
-	rm -f /dev/audio0
-fi
-mknod -m $AUDIOPERMS /dev/audio0 c 14 4
-ln -s /dev/audio0 /dev/audio
+Why?
 
-#
-#	DSP1		(14, 19) /dev/dsp for the second soundcard.
-#				Also the SB emulation part of the
-#				PAS16 card.
-#
-if [ -e /dev/dsp1 ]; then
-	rm -f /dev/dsp1
-fi
-mknod -m $AUDIOPERMS /dev/dsp1 c 14 19
-#
-#	SPARC audio1	(14, 20) 
-#				/dev/audio for the second soundcard.
-#				Also the SB emulation part of the
-#				PAS16 card.
-#
-if [ -e /dev/audio1 ]; then
-	rm -f /dev/audio1
-fi
-mknod -m $AUDIOPERMS /dev/audio1 c 14 20
-#
-#	/dev/sndstat	(14,6)	For debugging purposes
-#
-if [ -e /dev/sndstat ]; then
-	rm -f /dev/sndstat
-fi
-mknod -m 666 /dev/sndstat c 14 6
-exit 0
+Because you do not need it. All Linux distributions have the
+device files properly created (yes they are) so you should not
+try to run any scripts which create them.
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/Readme.v30 linux/drivers/sound/Readme.v30
--- v2.1.40/linux/drivers/sound/Readme.v30	Fri Nov 15 00:15:00 1996
+++ linux/drivers/sound/Readme.v30	Wed Dec 31 16:00:00 1969
@@ -1,140 +0,0 @@
-Sound driver version v3.0 (and later)
--------------------------------------
-
-All features of v2.90-2 should work as earlier. There could be some
-omissions but they are unintentional. I started this version thread
-after v2.3 so all features implemented before it are there.
-
-New features
-============
-
-There are now two new device interfaces. The /dev/midi## is a raw
-tty like interface to MIDI ports. There is a device file for each MIDI
-port on your system. They are named (/dev/midi00 to /dev/midiNN).
-The second addition is the /dev/music which is higher level interface 
-than the old /dev/sequencer. It's intended for writing device independent
-applications like sequencers.
-
-/dev/midi##
------------
-
-This interface should be useful for applications like MIDI sysex librarians.
-There are (currently) no timing features so making music could be impossible.
-
-There are as many /dev/midi## devices as there are MIDI ports in the system. 
-The /dev/midi00 is connected to the first one, /dev/midi01 to the second etc.
-
-These devices work like tty devices in raw mode. Everything written to them is
-sent out to the MIDI port. There is currently an extra delay of at most 
-1/100th of sec but it will be removed later.
-
-The reading algorithm is little bit more complicated. There are two different
-cases:
-
-1)	There is at least one byte in the input buffer.
-
-The read returns as many bytes as it can without waiting for more bytes.
-For example when a process reads 100 bytes and there are 10 bytes in the 
-buffer, the read returns just 10 bytes.
-
-2)	The input buffer is empty when the process calls read.
-
-The read waits for the first byte and then continues as in case 1. By
-default it waits infinitely but there is an ioctl for setting a timeout
-for this. The ioctl(fd, SNDCTL_MIDI_PRETIME, &time) changes the timeout.
-The time is given in 1/10th of seconds (10 means one second).
-
-Other ioctl calls:
-
-ioctl(fd, SNDCTL_MIDI_MPUMODE, &mode) is available for full MPU-401
-compatible devices such as MPU-IPC-T, MQ PC Midi Card or MQX-32.
-It's not available for the so called MPU UART ports of some soundcards
-(PAS16, SB16 etc). By default the MIDI port is in UART mode after open. 
-If this ioctl is called with mode=1, the interface is put to the intelligent
-(coprocessor) mode. NOTE! The MIDI port will be reset when this ioctl is called.
-It could have some strange effects if not called immediately after open. This
-call returns EINVAL if the midi port doesn't support the MPU-401 intelligent
-mode.
-
-ioctl(fd, SNDCTL_MIDI_MPUCMD, &cmdstruct) is valid only if the MIDI port
-is put to the coprocessor mode using ioctl(SNDCTL_MIDI_MPUMODE). It's used to
-send commands to a MPU-401 compatible MIDI cards. Please refer to the
-MPU-401 Technical Reference Manual (or Music Quest Technical Reference
-Manual) for descriptions of the commands.
-
-The argument of SNDCTL_MIDI_MPUCOMMAND is of type mpu_command_rec. It
-has the following fields:
-
-typedef struct {
-		unsigned char cmd;
-
-		char nr_args, nr_returns;
-		unsigned char data[30];
-	} mpu_command_rec;
-
-where:
-	cmd		Contains the command number.
-	nr_args		Number of arguments of the command.
-			MUST BE INITIALIZED BEFORE CALL
-	nr_returns	Number of bytes returned by the command.
-			MUST BE INITIALIZED BEFORE CALL
-	data		Buffer for the command arguments and returned
-			data.
-
-Be extremely careful with the nr_args and nr_returns fields. They
-must match the command. An incorrect value will put the card and
-the driver out of sync. Refer to the MPU-401/MQX-32M documentation for further
-details.
-
-
-
-/dev/music (/dev/sequencer2)
-----------------------------
-
-This device file works much like the /dev/sequencer which has been present
-since the beginning. The main differences are the following:
-
-- /dev/sequencer makes the MIDI ports to look like the synth devices. In fact
-the result is somewhere between the MIDI specification and the synth devices of
-/dev/sequencer. Both kind of devices are accessed using the SEQ_START_NOTE()
-like macros. The voice number parameters of the API macros have been redefined
-to denote MIDI channels. This means that the driver allocates voices for
-the channels automatically (this is a responsibility/right of an application
-with /dev/sequencer). The result is that a SEQ_START_NOTE() macro has
-similar effects for a synth channel than on a MIDI port. This kind of
-solution provides better device independence than the /dev/sequencer. The
-drawback is that the new interface doesn't permit so low level access to the
-device as the /dev/sequencer does. An application developer must choose between
-these two interfaces. I think the old /dev/sequencer is better for applications
-like module players while the new one is better for making generic sequencer
-programs.
-
-- There are no separate MIDI devices with the /dev/sequencer2. The
-ioctl(SNDCTL_SEQ_NRMIDIS) returns always zero. Instead the MIDI ports are
-shown as synth devices. ioctl(SNDCTL_SEQ_NRSYNTHS) on /dev/sequencer2 will 
-return sum of internal synthesizers (GUS, OPL3) and MIDI ports in the systems.
-
-- The new interface is used much like the ordinary /dev/sequencer. The
-event format is new so you have to use the API macros defined in the 
-sys/soundcard.h. The interface is will probably change before the final 3.0
-release but using the API macros should ensure compatibility in source level.
-The new event format is not recognized by version 2.X so don't try to 
-distribute binaries compiled with soundcard.h of v3.X.
-
-- The basic API usage is similar to the current one. There are some new
-macros but the older ones should work as earlier. The most important
-incompatibility is that the /dev/sequencer2 driver allocates voices itself.
-The other one is that the application must send SEQ_START_TIMER() as its
-first event. Otherwise the timer is not started and the application waits
-infinitely.
-
-
-There are several new features but I don't document them here. There are
-some info in the soundcard.h (near the end). I have also included some
-sample code in the directory v30. Full documentation will
-appear in the Hacker's Guide later.
-
-Don't hesitate to contact me in case you have questions or comments.
-
-Hannu Savolainen
-hannu@voxware.pp.fi
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.40/linux/drivers/sound/ad1848.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/ad1848.c	Sun May 11 02:58:54 1997
@@ -71,6 +71,7 @@
     int             irq_ok;
     mixer_ents     *mix_devices;
     int             mixer_output_port;
+    int             c930_password_port;
   }
 ad1848_info;
 
@@ -107,7 +108,7 @@
   AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
 };
 
-static ad1848_info dev_info[MAX_AUDIO_DEV];
+static ad1848_info adev_info[MAX_AUDIO_DEV];
 
 #define io_Index_Addr(d)	((d)->base)
 #define io_Indexed_Data(d)	((d)->base+1)
@@ -200,7 +201,6 @@
   if (ad_read (devc, 11) & 0x20)
     if (devc->model != MD_1845)
       printk ("ad1848: Auto calibration timed out(3).\n");
-  ad_write (devc, 9, ad_read (devc, 9) & ~0x18);	/* Disable autocalibration */
 }
 
 static void
@@ -215,7 +215,6 @@
   for (i = 6; i < 8; i++)
     {
       prev = devc->saved_regs[i] = ad_read (devc, i);
-      ad_write (devc, i, prev | 0x80);
     }
 
 }
@@ -223,21 +222,6 @@
 static void
 ad_unmute (ad1848_info * devc)
 {
-  int             i, dummy;
-
-/*
- * Let's have some delay
- */
-  for (i = 0; i < 1000; i++)
-    dummy = inb (devc->base);
-
-  /*
-   * Restore back old volume registers (unmute)
-   */
-  for (i = 6; i < 8; i++)
-    {
-      ad_write (devc, i, devc->saved_regs[i] & ~0x80);
-    }
 }
 
 static void
@@ -296,7 +280,6 @@
   restore_flags (flags);
 }
 
-
 static int
 ad1848_set_recmask (ad1848_info * devc, int mask)
 {
@@ -381,16 +364,32 @@
 {
   unsigned char   mask;
   int             shift;
+  int             mute;
+  int             mutemask;
+  int             set_mute_bit;
+
+  set_mute_bit = (newval == 0);
 
   if (devc->mix_devices[dev][chn].polarity == 1)	/* Reverse */
     newval = 100 - newval;
 
   mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
   shift = devc->mix_devices[dev][chn].bitpos;
-  newval = (int) ((newval * mask) + 50) / 100;	/* Scale it */
 
-  *regval &= ~(mask << shift);	/* Clear bits */
-  *regval |= (newval & mask) << shift;	/* Set new value */
+  if (devc->mix_devices[dev][chn].mutepos == 8)
+    {				/* if there is no mute bit */
+      mute = 0;			/* No mute bit; do nothing special */
+      mutemask = ~0;		/* No mute bit; do nothing special */
+    }
+  else
+    {
+      mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
+      mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
+    }
+
+  newval = (int) ((newval * mask) + 50) / 100;	/* Scale it */
+  *regval &= (~(mask << shift)) & (mutemask);	/* Clear bits */
+  *regval |= ((newval & mask) << shift) | mute;		/* Set new value */
 }
 
 static int
@@ -969,13 +968,12 @@
   save_flags (flags);
   cli ();
 
-  if (devc->model == MD_1848 || !devc->dual_dma)	/* Single DMA channel mode */
+  if (devc->model == MD_1848)
     {
       ad_write (devc, 15, (unsigned char) (cnt & 0xff));
       ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
     }
   else
-    /* Dual DMA channel mode */
     {
       ad_write (devc, 31, (unsigned char) (cnt & 0xff));
       ad_write (devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
@@ -1007,6 +1005,8 @@
   if (portc->channels > 1)
     fs |= 0x10;
 
+  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
+
   if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
       fs &= 0xf0;		/* Mask off the rate select bits */
@@ -1017,14 +1017,15 @@
 
   old_fs = ad_read (devc, 8);
 
-  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
-
   if (devc->model == MD_4232)
     {
       tmp = ad_read (devc, 16);
       ad_write (devc, 16, tmp | 0x30);
     }
 
+  if (devc->model == MD_IWAVE)
+    ad_write (devc, 17, 0xc2);	/* Disable variable frequency select */
+
   ad_write (devc, 8, fs);
   /*
    * Write to I8 starts resynchronization. Wait until it completes.
@@ -1075,6 +1076,8 @@
   if (portc->channels > 1)
     fs |= 0x10;
 
+  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
+
   if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
       fs &= 0xf0;		/* Mask off the rate select bits */
@@ -1083,32 +1086,21 @@
       ad_write (devc, 23, portc->speed & 0xff);		/* Speed LSB */
     }
 
-  old_fs = ad_read (devc, 8);
-
-  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
-
   if (devc->model == MD_4232)
     {
       tmp = ad_read (devc, 16);
       ad_write (devc, 16, tmp | 0x30);
     }
 
-  ad_write (devc, 8, fs);
-  /*
-   * Write to I8 starts resynchronization. Wait until it completes.
-   */
-  timeout = 0;
-  while (timeout < 100 && inb (devc->base) != 0x80)
-    timeout++;
-  timeout = 0;
-  while (timeout < 10000 && inb (devc->base) == 0x80)
-    timeout++;
+  if (devc->model == MD_IWAVE)
+    ad_write (devc, 17, 0xc2);	/* Disable variable frequency select */
 
   /*
-     * If mode >= 2 (CS4231), set I28 also. It's the capture format register.
+   * If mode >= 2 (CS4231), set I28. It's the capture format register.
    */
   if (devc->model != MD_1848)
     {
+      old_fs = ad_read (devc, 28);
       ad_write (devc, 28, fs);
 
       /*
@@ -1121,6 +1113,43 @@
       timeout = 0;
       while (timeout < 10000 && inb (devc->base) == 0x80)
 	timeout++;
+
+      if (devc->model != MD_1848 && devc->model != MD_1845)
+	{
+	  /*
+	   * CS4231 compatible devices don't have separate sampling rate selection
+	   * register for recording an playback. The I8 register is shared so we have to
+	   * set the speed encoding bits of it too.
+	   */
+	  unsigned char   tmp = portc->speed_bits | (ad_read (devc, 8) & 0xf0);
+
+	  ad_write (devc, 8, tmp);
+	  /*
+	   * Write to I8 starts resynchronization. Wait until it completes.
+	   */
+	  timeout = 0;
+	  while (timeout < 100 && inb (devc->base) != 0x80)
+	    timeout++;
+
+	  timeout = 0;
+	  while (timeout < 10000 && inb (devc->base) == 0x80)
+	    timeout++;
+	}
+    }
+  else
+    {				/* For AD1848 set I8. */
+
+      old_fs = ad_read (devc, 8);
+      ad_write (devc, 8, fs);
+      /*
+       * Write to I8 starts resynchronization. Wait until it completes.
+       */
+      timeout = 0;
+      while (timeout < 100 && inb (devc->base) != 0x80)
+	timeout++;
+      timeout = 0;
+      while (timeout < 10000 && inb (devc->base) == 0x80)
+	timeout++;
     }
 
   if (devc->model == MD_4232)
@@ -1176,14 +1205,14 @@
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmap_out->dma);
+    disable_dma (audio_devs[dev]->dmap_in->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
 	break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x02);	/* Stop capture */
 
-    enable_dma (audio_devs[dev]->dmap_out->dma);
+    enable_dma (audio_devs[dev]->dmap_in->dma);
     devc->audio_mode &= ~PCM_ENABLE_INPUT;
   }
 
@@ -1306,6 +1335,9 @@
       for (i = 16; i < 32; i++)
 	ad_write (devc, i, init_values[i]);
 
+      if (devc->model == MD_IWAVE)
+	ad_write (devc, 16, 0x30);	/* Playback and capture counters enabled */
+
     }
 
   if (devc->model > MD_1848)
@@ -1321,6 +1353,7 @@
       if (devc->model == MD_IWAVE)
 	{			/* Some magic Interwave specific initialization */
 	  ad_write (devc, 12, 0x6c);	/* Select codec mode 3 */
+	  ad_write (devc, 16, 0x30);	/* Playback and capture counters enabled */
 	  ad_write (devc, 17, 0xc2);	/* Alternate feature enable */
 	}
     }
@@ -1347,11 +1380,12 @@
 {
 
   unsigned char   tmp;
-  ad1848_info    *devc = &dev_info[nr_ad1848_devs];
+  ad1848_info    *devc = &adev_info[nr_ad1848_devs];
   unsigned char   tmp1 = 0xff, tmp2 = 0xff;
   int             optiC930 = 0;	/* OPTi 82C930 flag */
   int             interwave = 0;
   int             ad1847_flag = 0;
+  int             cs4248_flag = 0;
 
   int             i;
 
@@ -1364,16 +1398,22 @@
 	  interwave = 1;
 	  *ad_flags = 0;
 	}
+
+      if (*ad_flags == 0x12345677)
+	{
+	  cs4248_flag = 1;
+	  *ad_flags = 0;
+	}
     }
 
   if (nr_ad1848_devs >= MAX_AUDIO_DEV)
     {
-      DDB (printk ("ad1848 detect error - step 0\n"));
+      printk ("ad1848 - Too many audio devices\n");
       return 0;
     }
   if (check_region (io_base, 4))
     {
-      printk ("\n\nad1848.c: Port %x not free.\n\n", io_base);
+      printk ("ad1848.c: Port %x not free.\n", io_base);
       return 0;
     }
 
@@ -1386,6 +1426,7 @@
   devc->chip_name = "AD1848";
   devc->model = MD_1848;	/* AD1848 or CS4248 */
   devc->levels = NULL;
+  devc->c930_password_port = 0;
   devc->debug_flag = 0;
 
   /*
@@ -1413,6 +1454,9 @@
 
   DDB (printk ("ad1848_detect() - step A\n"));
 
+  if (inb (devc->base) == 0x80)	/* Not ready. Let's wait */
+    ad_leave_MCE (devc);
+
   if ((inb (devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
     {
       DDB (printk ("ad1848 detect error - step A (%02x)\n",
@@ -1420,11 +1464,6 @@
       return 0;
     }
 
-  DDB (printk ("ad1848: regs: "));
-  for (i = 0; i < 32; i++)
-    DDB (printk ("%02x ", ad_read (devc, i)));
-  DDB (printk ("\n"));
-
   /*
      * Test if it's possible to change contents of the indirect registers.
      * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
@@ -1480,6 +1519,8 @@
   /*
      * The original AD1848/CS4248 has just 15 indirect registers. This means
      * that I0 and I16 should return the same value (etc.).
+     * However this doesn't work with CS4248. Actually it seems to be impossible
+     * to detect if the chip is a CS4231 or CS4248.
      * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
      * with CS4231.
    */
@@ -1513,6 +1554,7 @@
   else
     ad_write (devc, 12, 0x40);	/* Set mode2, clear 0x80 */
 
+
   if (ad_flags)
     *ad_flags = 0;
 
@@ -1630,6 +1672,15 @@
 			devc->chip_name = "AD1845";
 			devc->model = MD_1845;
 		      }
+		    else if (cs4248_flag)
+		      {
+			if (ad_flags)
+			  *ad_flags |= AD_F_CS4248;
+
+			devc->chip_name = "CS4248";
+			devc->model = MD_1848;
+			ad_write (devc, 12, ad_read (devc, 12) & ~0x40);	/* Mode2 off */
+		      }
 
 		    ad_write (devc, 23, tmp);	/* Restore */
 		  }
@@ -1686,7 +1737,7 @@
   int             my_dev;
   char            dev_name[100];
 
-  ad1848_info    *devc = &dev_info[nr_ad1848_devs];
+  ad1848_info    *devc = &adev_info[nr_ad1848_devs];
 
   ad1848_port_info *portc = NULL;
 
@@ -1749,7 +1800,7 @@
   if (irq > 0)
     {
       irq2dev[irq] = devc->dev_no = my_dev;
-      if (snd_set_irq_handler (devc->irq, ad1848_interrupt,
+      if (snd_set_irq_handler (devc->irq, adintr,
 			       "SoundPort",
 			       NULL) < 0)
 	{
@@ -1819,7 +1870,7 @@
   if (nr_ad1848_devs < 1)
     return;
 
-  devc = &dev_info[nr_ad1848_devs - 1];
+  devc = &adev_info[nr_ad1848_devs - 1];
 
   switch (cmd)
     {
@@ -1871,9 +1922,9 @@
   ad1848_info    *devc = NULL;
 
   for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
-    if (dev_info[i].base == io_base)
+    if (adev_info[i].base == io_base)
       {
-	devc = &dev_info[i];
+	devc = &adev_info[i];
 	dev = devc->dev_no;
       }
 
@@ -1898,7 +1949,7 @@
 }
 
 void
-ad1848_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
+adintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   unsigned char   status;
   ad1848_info    *devc;
@@ -1937,7 +1988,7 @@
   status = inb (io_Status (devc));
 
   if (status == 0x80)
-    printk ("ad1848_interrupt: Why?\n");
+    printk ("adintr: Why?\n");
   if (devc->model == MD_1848)
     outb ((0), io_Status (devc));	/* Clear interrupt status */
 
@@ -1952,6 +2003,8 @@
 
 	  alt_stat = 0;
 
+	  if (devc->c930_password_port)
+	    outb ((0xe4), devc->c930_password_port);	/* Password */
 	  outb ((11), 0xe0e);
 	  c930_stat = inb (0xe0f);
 
@@ -1972,6 +2025,8 @@
 	  save_flags (flags);
 	  cli ();
 
+	  if (devc->c930_password_port)
+	    outb ((0xe4), devc->c930_password_port);	/* Password */
 	  outb ((11), 0xe0e);
 	  outb ((~c930_stat), 0xe0f);
 	  restore_flags (flags);
@@ -2319,7 +2374,6 @@
     -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
   };
   char            bits, dma2_bit = 0;
-  int             ad_flags = 0;
 
   static char     dma_bits[4] =
   {
@@ -2347,7 +2401,10 @@
 
   bits = interrupt_bits[hw_config->irq];
   if (bits == -1)
-    return;
+    {
+      printk ("MSS: Bad IRQ %d\n", hw_config->irq);
+      return;
+    }
 
   outb ((bits | 0x40), config_port);
   if ((inb (version_port) & 0x40) == 0)
@@ -2357,7 +2414,7 @@
  * Handle the capture DMA channel
  */
 
-  if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma)
+  if (dma2 != -1 && dma2 != dma)
     {
       if (!((dma == 0 && dma2 == 1) ||
 	    (dma == 1 && dma2 == 0) ||
@@ -2382,7 +2439,12 @@
 	}
     }
   else
-    dma2 = dma;
+    {
+      dma2 = dma;
+    }
+
+  hw_config->dma = dma;
+  hw_config->dma2 = dma2;
 
   outb ((bits | dma_bits[dma] | dma2_bit), config_port);	/* Write IRQ+DMA setup */
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/ad1848_mixer.h linux/drivers/sound/ad1848_mixer.h
--- v2.1.40/linux/drivers/sound/ad1848_mixer.h	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/ad1848_mixer.h	Sun May 11 02:58:33 1997
@@ -48,10 +48,11 @@
 				 SOUND_MASK_IGAIN | SOUND_MASK_PCM)
 
 struct mixer_def {
-	unsigned int regno: 7;
+	unsigned int regno: 5;
 	unsigned int polarity:1;	/* 0=normal, 1=reversed */
-	unsigned int bitpos:4;
-	unsigned int nbits:4;
+	unsigned int bitpos:3;
+	unsigned int nbits:3;
+	unsigned int mutepos:4;
 };
 
 static char mix_cvt[101] = {
@@ -75,47 +76,47 @@
  * The current version doesn't try to compensate this.
  */
 
-#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)	\
-	{{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit)	\
+	{{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r, mute_bit}}
 
 static mixer_ents ad1848_mix_devices[32] = {
-MIX_ENT(SOUND_MIXER_VOLUME,	27, 1, 0, 4,	29, 1, 0, 4),
-MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6),
-MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1),
-MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 0, 5,	 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_IMIX,	13, 1, 2, 6,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 0, 5,	 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5)
+MIX_ENT(SOUND_MIXER_VOLUME,	27, 1, 0, 4,	29, 1, 0, 4,  8),
+MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1,  8),
+MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_IMIX,	13, 1, 2, 6,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5,  7)
 };
 
 static mixer_ents iwave_mix_devices[32] = {
-MIX_ENT(SOUND_MIXER_VOLUME,	25, 1, 0, 5,	27, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6),
-MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1),
-MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 0, 5,	 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_IMIX,	16, 1, 0, 5,	17, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 0, 5,	 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5)
+MIX_ENT(SOUND_MIXER_VOLUME,	25, 1, 0, 5,	27, 1, 0, 5,  8),
+MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+MIX_ENT(SOUND_MIXER_SPEAKER,	26, 1, 0, 4,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1,  8),
+MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_IMIX,	16, 1, 0, 5,	17, 1, 0, 5,  8),
+MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 0, 5,	 3, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5,  7)
 };
 
 /* OPTi 82C930 has somewhat different port addresses.
@@ -124,23 +125,23 @@
  * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
  */
 static mixer_ents c930_mix_devices[32] = {
-MIX_ENT(SOUND_MIXER_VOLUME,	22, 1, 0, 5,	23, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6),
-MIX_ENT(SOUND_MIXER_SPEAKER,	22, 1, 0, 5,	 23, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_LINE,	18, 1, 1, 4,	19, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_MIC,	 20, 1, 0, 4,	 21, 1, 0, 4),
-MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 1, 4,	 3, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4),
-MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 1, 4,	 3, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 1, 4,	 5, 1, 1, 4),
-MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 1, 4,	19, 1, 1, 4)
+MIX_ENT(SOUND_MIXER_VOLUME,	22, 1, 0, 5,	23, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_SYNTH,	 4, 1, 0, 5,	 5, 1, 0, 5,  7),
+MIX_ENT(SOUND_MIXER_PCM,	 6, 1, 0, 6,	 7, 1, 0, 6,  7),
+MIX_ENT(SOUND_MIXER_SPEAKER,	22, 1, 0, 5,	 23, 1, 0, 5,  8),
+MIX_ENT(SOUND_MIXER_LINE,	18, 1, 1, 4,	19, 1, 1, 4,  7),
+MIX_ENT(SOUND_MIXER_MIC,	 20, 1, 0, 4,	 21, 1, 0, 4,  8),
+MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 1, 4,	 3, 1, 1, 4,  7),
+MIX_ENT(SOUND_MIXER_IMIX,	0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4,  8),
+MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0,  8),
+MIX_ENT(SOUND_MIXER_LINE1, 	 2, 1, 1, 4,	 3, 1, 1, 4,  7),
+MIX_ENT(SOUND_MIXER_LINE2,	 4, 1, 1, 4,	 5, 1, 1, 4,  7),
+MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 1, 4,	19, 1, 1, 4,  7)
 };
 
 static int default_mixer_levels[32] =
@@ -155,12 +156,12 @@
   0x1010,			/* Mic */
   0x4b4b,			/* CD */
   0x0000,			/* Recording monitor */
-  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Second PCM */
   0x4b4b,			/* Recording level */
   0x4b4b,			/* Input gain */
   0x4b4b,			/* Output gain */
-  0x4040,			/* Line1 */
-  0x4040,			/* Line2 */
+  0x2020,			/* Line1 */
+  0x2020,			/* Line2 */
   0x1515			/* Line3 (usually line in)*/
 };
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.40/linux/drivers/sound/audio.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/audio.c	Sun May 11 02:58:55 1997
@@ -21,9 +21,6 @@
 #include "ulaw.h"
 #include "coproc.h"
 
-#define ON		1
-#define OFF		0
-
 #define NEUTRAL8	0x80
 #define NEUTRAL16	0x00
 
@@ -31,11 +28,12 @@
 static int      dev_nblock[MAX_AUDIO_DEV];	/* 1 if in nonblocking mode */
 
 #define		AM_NONE		0
-#define		AM_WRITE	1
-#define 	AM_READ		2
+#define		AM_WRITE	OPEN_WRITE
+#define 	AM_READ		OPEN_READ
+static int      dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
 
 
-static int      audio_format[MAX_AUDIO_DEV];
+static int      local_format[MAX_AUDIO_DEV], audio_format[MAX_AUDIO_DEV];
 static int      local_conversion[MAX_AUDIO_DEV];
 
 #define CNV_MU_LAW	0x00000001
@@ -44,7 +42,6 @@
 {
   if (fmt != AFMT_QUERY)
     {
-
       local_conversion[dev] = 0;
 
       if (!(audio_devs[dev]->format_mask & fmt))	/* Not supported */
@@ -57,10 +54,10 @@
 	  fmt = AFMT_U8;	/* This is always supported */
 
       audio_format[dev] = audio_devs[dev]->d->set_bits (dev, fmt);
+      local_format[dev] = fmt;
     }
-
-  if (local_conversion[dev])	/* This shadows the HW format */
-    return local_conversion[dev];
+  else
+    return local_format[dev];
 
   return audio_format[dev];
 }
@@ -98,7 +95,6 @@
 
   local_conversion[dev] = 0;
 
-
   if (dev_type == SND_DEV_AUDIO)
     {
       set_format (dev, AFMT_MU_LAW);
@@ -127,19 +123,12 @@
   /* Align the write pointer with fragment boundaries */
   if ((l = dmap->user_counter % dmap->fragment_size) > 0)
     {
-      char           *ptr;
-      int             err, dummylen, len = dmap->fragment_size - l;
+      int             len;
+      unsigned long   offs = dmap->user_counter % dmap->bytes_in_use;
 
-      if ((err = DMAbuf_getwrbuffer (dev, &ptr, &dummylen, 1)) >= 0)
-	if (dummylen >= len && ((long) ptr % dmap->fragment_size) == l)
-	  {
-	    if ((ptr + len) > (dmap->raw_buf + audio_devs[dev]->buffsize))
-	      printk ("audio: Buffer error 1\n");
-	    if (ptr < dmap->raw_buf)
-	      printk ("audio: Buffer error 11\n");
-	    memset (ptr, dmap->neutral_byte, len);
-	    DMAbuf_move_wrpointer (dev, len);
-	  }
+      len = dmap->fragment_size - l;
+      memset (dmap->raw_buf + offs, dmap->neutral_byte, len);
+      DMAbuf_move_wrpointer (dev, len);
     }
 
 /*
@@ -153,7 +142,7 @@
     {
       p = (p + 1) % dmap->nbufs;
       if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
-	  (dmap->raw_buf + audio_devs[dev]->buffsize))
+	  (dmap->raw_buf + dmap->buffsize))
 	printk ("audio: Buffer error 2\n");
 
       memset (dmap->raw_buf + p * dmap->fragment_size,
@@ -241,7 +230,7 @@
 
   while (c)
     {
-      if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev]) < 0))
+      if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0)
 	{
 	  /* Handle nonblocking mode */
 	  if (dev_nblock[dev] && err == -EAGAIN)
@@ -250,17 +239,18 @@
 	}
 
       l = c;
+
       if (l > buf_size)
 	l = buf_size;
 
       if (!audio_devs[dev]->d->copy_user)
 	{
 	  if ((dma_buf + l) >
-	   (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->buffsize))
+	      (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
 	    printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n",
 		    (long) dma_buf, l,
 		    (long) audio_devs[dev]->dmap_out->raw_buf,
-		    (int) audio_devs[dev]->buffsize);
+		    (int) audio_devs[dev]->dmap_out->buffsize);
 	  if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
 	    printk ("audio: Buffer error 13\n");
 	  copy_from_user (dma_buf, &(buf)[p], l);
@@ -541,46 +531,10 @@
 audio_init_devices (void)
 {
   /*
-     * NOTE! This routine could be called several times during boot.
+   * NOTE! This routine could be called several times during boot.
    */
 }
 
-int
-audio_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
-{
-  dev = dev >> 4;
-
-  switch (sel_type)
-    {
-    case SEL_IN:
-      if (!(audio_devs[dev]->open_mode & OPEN_READ))
-	return 0;
-      if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
-	{
-	  return 0;		/* Not recording */
-	}
-
-      return DMAbuf_select (dev, file, sel_type, wait);
-      break;
-
-    case SEL_OUT:
-      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-	return 0;
-      if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX))
-	{
-	  return 0;		/* Wrong direction */
-	}
-
-      return DMAbuf_select (dev, file, sel_type, wait);
-      break;
-
-    case SEL_EX:
-      return 0;
-    }
-
-  return 0;
-}
-
 
 #endif
 
@@ -632,11 +586,11 @@
          * of sound (using the current speed, sample size and #channels).
        */
 
-      bsz = dsp_dev->buffsize;
+      bsz = dmap->buffsize;
       while (bsz > sz)
 	bsz /= 2;
 
-      if (bsz == dsp_dev->buffsize)
+      if (bsz == dmap->buffsize)
 	bsz /= 2;		/* Needs at least 2 buffers */
 
 /*
@@ -668,17 +622,23 @@
          * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
          * the buffer size computation has already been done.
        */
-      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
-	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
+      if (dmap->fragment_size > (dmap->buffsize / 2))
+	dmap->fragment_size = (dmap->buffsize / 2);
       bsz = dmap->fragment_size;
     }
 
-  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */
+  if (audio_devs[dev]->min_fragment)
+    if (bsz < (1 << audio_devs[dev]->min_fragment))
+      bsz = 1 << audio_devs[dev]->min_fragment;
+  if (audio_devs[dev]->max_fragment)
+    if (bsz > (1 << audio_devs[dev]->max_fragment))
+      bsz = 1 << audio_devs[dev]->max_fragment;
+  bsz &= ~0x07;			/* Force size which is multiple of 8 bytes */
 #ifdef OS_DMA_ALIGN_CHECK
   OS_DMA_ALIGN_CHECK (bsz);
 #endif
 
-  n = dsp_dev->buffsize / bsz;
+  n = dmap->buffsize / bsz;
   if (n > MAX_SUB_BUFFERS)
     n = MAX_SUB_BUFFERS;
   if (n > dmap->max_fragments)
@@ -693,6 +653,8 @@
   dmap->nbufs = n;
   dmap->bytes_in_use = n * bsz;
   dmap->fragment_size = bsz;
+  dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
+    dmap->bytes_in_use;		/* Approximately one hour */
 
   if (dmap->raw_buf)
     {
@@ -751,6 +713,8 @@
 
   if (count == 0)
     count = MAX_SUB_BUFFERS;
+  else if (count < MAX_SUB_BUFFERS)
+    count++;
 
   if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
     return -EINVAL;
@@ -762,6 +726,10 @@
     if (bytes < audio_devs[dev]->min_fragment)
       bytes = audio_devs[dev]->min_fragment;
 
+  if (audio_devs[dev]->max_fragment > 0)
+    if (bytes > audio_devs[dev]->max_fragment)
+      bytes = audio_devs[dev]->max_fragment;
+
 #ifdef OS_DMA_MINBITS
   if (bytes < OS_DMA_MINBITS)
     bytes = OS_DMA_MINBITS;
@@ -770,16 +738,16 @@
   dmap->fragment_size = (1 << bytes);
   dmap->max_fragments = count;
 
-  if (dmap->fragment_size > audio_devs[dev]->buffsize)
-    dmap->fragment_size = audio_devs[dev]->buffsize;
+  if (dmap->fragment_size > dmap->buffsize)
+    dmap->fragment_size = dmap->buffsize;
 
-  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+  if (dmap->fragment_size == dmap->buffsize &&
       audio_devs[dev]->flags & DMA_AUTOMODE)
     dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
 
   dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
   if (arg)
-    return (*(int *) arg = bytes | (count << 16));
+    return (*(int *) arg = bytes | ((count - 1) << 16));
   else
     return 0;
 }
@@ -797,16 +765,18 @@
     case SNDCTL_DSP_SUBDIVIDE:
       {
 	int             fact;
-	int             ret;
+	int             ret = 0;
 
 	fact = *(int *) arg;
 
-	ret = dma_subdivide (dev, dmap_out, arg, fact);
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	  ret = dma_subdivide (dev, dmap_out, arg, fact);
 	if (ret < 0)
 	  return ret;
 
-	if (audio_devs[dev]->flags & DMA_DUPLEX &&
-	    audio_devs[dev]->open_mode & OPEN_READ)
+	if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+	    (audio_devs[dev]->flags & DMA_DUPLEX &&
+	     audio_devs[dev]->open_mode & OPEN_READ))
 	  ret = dma_subdivide (dev, dmap_in, arg, fact);
 
 	return ret;
@@ -824,6 +794,10 @@
 	    !(audio_devs[dev]->open_mode & OPEN_READ))
 	  return -EINVAL;
 
+	if (cmd == SNDCTL_DSP_GETOSPACE &&
+	    !(audio_devs[dev]->open_mode & OPEN_WRITE))
+	  return -EINVAL;
+
 	if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
 	  dmap = dmap_in;
 
@@ -843,14 +817,14 @@
 	      info->fragments = 0;
 	    else
 	      {
-		info->fragments = dmap->nbufs - dmap->qlen;
+		info->fragments = DMAbuf_space_in_queue (dev);
 		if (audio_devs[dev]->d->local_qlen)
 		  {
 		    int             tmp = audio_devs[dev]->d->local_qlen (dev);
 
 		    if (tmp && info->fragments)
 		      tmp--;	/*
-				   * This buffer has been counted twice
+				 * This buffer has been counted twice
 				 */
 		    info->fragments -= tmp;
 		  }
@@ -904,6 +878,7 @@
 			       dmap_in->fragment_size, dmap_in->nbufs)) < 0)
 	      return -err;
 
+	    dmap_in->dma_mode = DMODE_INPUT;
 	    audio_devs[dev]->enable_bits = bits;
 	    DMAbuf_activate_recording (dev, dmap_in);
 	  }
@@ -919,7 +894,9 @@
 		reorganize_buffers (dev, dmap_out, 0);
 	      }
 
+	    dmap_out->dma_mode = DMODE_OUTPUT;
 	    ;
+	    audio_devs[dev]->enable_bits = bits;
 	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
 	    DMAbuf_launch_output (dev, dmap_out);
 	    ;
@@ -950,20 +927,24 @@
       {
 	count_info      info;
 	unsigned long   flags;
+	struct dma_buffparms *dmap = dmap_in;
 
 	if (!(audio_devs[dev]->open_mode & OPEN_READ))
 	  return -EINVAL;
 
 	save_flags (flags);
 	cli ();
-	info.bytes = audio_devs[dev]->dmap_in->byte_counter;
-	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_in) & ~3;
-	info.blocks = audio_devs[dev]->dmap_in->qlen;
+	info.bytes = dmap->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_INPUT) & ~3;
+	if (info.ptr < dmap->fragment_size && dmap->qtail != 0)
+	  info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+
+	info.blocks = dmap->qlen;
 	info.bytes += info.ptr;
 	memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 
-	if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
-	  audio_devs[dev]->dmap_in->qlen = 0;	/* Reset interrupt counter */
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	  dmap->qlen = 0;	/* Reset interrupt counter */
 	restore_flags (flags);
 	return 0;
       }
@@ -973,20 +954,23 @@
       {
 	count_info      info;
 	unsigned long   flags;
+	struct dma_buffparms *dmap = dmap_out;
 
 	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 	  return -EINVAL;
 
 	save_flags (flags);
 	cli ();
-	info.bytes = audio_devs[dev]->dmap_out->byte_counter;
-	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_out) & ~3;
-	info.blocks = audio_devs[dev]->dmap_out->qlen;
+	info.bytes = dmap->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT) & ~3;
+	if (info.ptr < dmap->fragment_size && dmap->qhead != 0)
+	  info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+	info.blocks = dmap->qlen;
 	info.bytes += info.ptr;
 	memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 
-	if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
-	  audio_devs[dev]->dmap_out->qlen = 0;	/* Reset interrupt counter */
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	  dmap->qlen = 0;	/* Reset interrupt counter */
 
 	restore_flags (flags);
 	return 0;
@@ -1004,18 +988,23 @@
       break;
 
     case SNDCTL_DSP_GETBLKSIZE:
-      if (!(dmap_out->flags & DMA_ALLOC_DONE))
-	{
-	  if (audio_devs[dev]->open_mode & OPEN_WRITE)
-	    reorganize_buffers (dev, dmap_out,
-				(audio_devs[dev]->open_mode == OPEN_READ));
-	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
-	      audio_devs[dev]->open_mode & OPEN_READ)
-	    reorganize_buffers (dev, dmap_in,
-				(audio_devs[dev]->open_mode == OPEN_READ));
-	}
+      {
+	int             fragment_size;
+	struct dma_buffparms *dmap = dmap_out;
 
-      return (*(int *) arg = dmap_out->fragment_size);
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	  reorganize_buffers (dev, dmap_out,
+			      (audio_devs[dev]->open_mode == OPEN_READ));
+	if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+	    (audio_devs[dev]->flags & DMA_DUPLEX &&
+	     audio_devs[dev]->open_mode & OPEN_READ))
+	  reorganize_buffers (dev, dmap_in,
+			      (audio_devs[dev]->open_mode == OPEN_READ));
+	if (audio_devs[dev]->open_mode == OPEN_READ)
+	  dmap = dmap_in;
+	fragment_size = dmap->fragment_size;
+	return (*(int *) arg = fragment_size);
+      }
       break;
 
     case SNDCTL_DSP_SETFRAGMENT:
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v2.1.40/linux/drivers/sound/configure.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/configure.c	Sun May 11 02:58:58 1997
@@ -145,7 +145,7 @@
 char           *questions[] =
 {
   "ProAudioSpectrum 16 support",
-  "_TRUE_ Sound Blaster (SB, SBPro, SB16/32/64, ESS, Jazz16) support",
+  "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support",
   "Generic OPL2/OPL3 FM synthesizer support",
   "Gravis Ultrasound support",
   "MPU-401 support (NOT for SB16)",
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.40/linux/drivers/sound/dev_table.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/dev_table.c	Sun May 11 02:58:59 1997
@@ -15,6 +15,7 @@
 
 #define _DEV_TABLE_C_
 #include "sound_config.h"
+int             sb_be_quiet = 0;
 
 int             sound_started = 0;
 
@@ -43,20 +44,15 @@
 #ifdef CONFIG_AUDIO
   if (num_audiodevs)		/* Audio devices present */
     {
-      DMAbuf_init ();
+      int             dev;
+
+      for (dev = 0; dev < num_audiodevs; dev++)
+	{
+	}
       audio_init_devices ();
     }
 #endif
 
-#ifdef CONFIG_MIDI
-  if (num_midis)
-    MIDIbuf_init ();
-#endif
-
-#ifdef CONFIG_SEQUENCER
-  if (num_midis + num_synths)
-    sequencer_init ();
-#endif
   return;
 }
 
@@ -493,17 +489,11 @@
 /*
  *    Hardcoded defaults
  */
-  op->buffsize = DSP_BUFFSIZE;
-
   audio_devs[num_audiodevs] = op;
   num = num_audiodevs++;
 
-  DMAbuf_init ();
-
-  op->dmap_out->dma = dma1;
-  op->dmap_in->dma = dma2;
+  DMAbuf_init (num, dma1, dma2);
 
-  DMAbuf_init ();
   audio_init_devices ();
   return num;
 #else
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.40/linux/drivers/sound/dev_table.h	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/dev_table.h	Sun May 11 02:58:34 1997
@@ -70,6 +70,7 @@
 
   	char     *raw_buf;
     	unsigned long   raw_buf_phys;
+	int buffsize;
 
      	/*
          * Device state tables
@@ -85,6 +86,8 @@
 #define DMA_SYNCING	0x00000040
 #define DMA_DIRTY	0x00000080
 #define DMA_POST	0x00000100
+#define DMA_NODMA	0x00000200
+#define DMA_NOTIMEOUT	0x00000400
 
 	int      open_mode;
 
@@ -109,6 +112,7 @@
 	int	 underrun_count;
 	unsigned long	 byte_counter;
 	unsigned long	 user_counter;
+	unsigned long	 max_byte_counter;
 	int	 data_rate; /* Bytes/second */
 
 	int	 mapping_flags;
@@ -120,6 +124,9 @@
 	OS_DMA_PARMS
 #endif
 	int     applic_profile;	/* Application profile (APF_*) */
+	int	 buf_flags[MAX_SUB_BUFFERS];
+#define		 BUFF_EOF		0x00000001 /* Increment eof count */
+#define		 BUFF_DIRTY		0x00000002 /* Buffer written */
 };
 
 /*
@@ -156,6 +163,8 @@
 	int (*set_speed)(int dev, int speed);
 	unsigned int (*set_bits)(int dev, unsigned int bits);
 	short (*set_channels)(int dev, short channels);
+	void (*postprocess_write)(int dev); 	/* Device spesific postprocessing for written data */
+	void (*preprocess_read)(int dev); 	/* Device spesific preprocessing for read data */
 };
 
 struct audio_operations {
@@ -167,13 +176,12 @@
 #define DMA_DUPLEX		0x04
 #define DMA_PSEUDO_AUTOMODE	0x08
 #define DMA_HARDSTOP		0x10
-#define DMA_NODMA		0x20
 #define DMA_EXACT		0x40
+#define DMA_NORESET		0x80
 	int  format_mask;	/* Bitmask for supported audio formats */
 	void *devc;		/* Driver specific info */
 	struct audio_driver *d;
 	void *portc;		/* Driver spesific info */
-	long buffsize;
 	struct dma_buffparms *dmap_in, *dmap_out;
 	struct coproc_operations *coproc;
 	int mixer_dev;
@@ -181,6 +189,7 @@
  	int open_mode;
 	int go;
 	int min_fragment;	/* 0 == unlimited */
+	int max_fragment;	/* 0 == unlimited */
 	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
 };
 
@@ -292,7 +301,7 @@
 	struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
 	struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
 
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) && !defined(VMIDI)
 	extern struct sound_timer_operations default_sound_timer;
 	struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = 
 		{&default_sound_timer, NULL}; 
@@ -313,6 +322,15 @@
 	  {"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu},
 	  {"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss},
 #endif
+
+#ifdef CONFIG_GUS16
+		{"GUS16", 0, SNDCARD_GUS16,	"Ultrasound 16-bit opt.",	attach_gus_db16, probe_gus_db16, unload_gus_db16},
+#endif
+#ifdef CONFIG_GUSHW
+		{"GUS", 0, SNDCARD_GUS,	"Gravis Ultrasound",	attach_gus_card, probe_gus, unload_gus},
+		{"GUSPNP", 1, SNDCARD_GUSPNP,	"GUS PnP",	attach_gus_card, probe_gus, unload_gus},
+#endif
+
 #ifdef CONFIG_MSS
 		{"MSS", 0, SNDCARD_MSS,	"MS Sound System",	attach_ms_sound, probe_ms_sound, unload_ms_sound},
 	/* Compaq Deskpro XL */
@@ -358,15 +376,6 @@
 #	endif
 #endif
 
-
-
-#ifdef CONFIG_GUS16
-		{"GUS16", 0, SNDCARD_GUS16,	"Ultrasound 16-bit opt.",	attach_gus_db16, probe_gus_db16, unload_gus_db16},
-#endif
-#ifdef CONFIG_GUSHW
-		{"GUS", 0, SNDCARD_GUS,	"Gravis Ultrasound",	attach_gus_card, probe_gus, unload_gus},
-		{"GUSPNP", 1, SNDCARD_GUSPNP,	"GUS PnP",	attach_gus_card, probe_gus, unload_gus},
-#endif
 #ifdef CONFIG_SSCAPEHW
 		{"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape",	attach_sscape, probe_sscape, unload_sscape},
 		{"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS,	"MS Sound System (SoundScape)",	attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound},
@@ -377,6 +386,10 @@
 		{"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI",	attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
 #endif
 
+
+
+
+
 		{NULL, 0, 0,		"*?*",			NULL, NULL, NULL}
 	};
 
@@ -439,6 +452,10 @@
 
 
 #ifdef CONFIG_MSS
+#	ifndef MSS_DMA2
+#	define MSS_DMA2 -1
+#	endif
+
 #	ifdef DESKPROXL
 		{SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, MSS_DMA2}, SND_DEFAULT_ENABLE},
 #	else
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.40/linux/drivers/sound/dmabuf.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/dmabuf.c	Sun May 11 02:59:01 1997
@@ -12,7 +12,7 @@
  */
 #include <linux/config.h>
 
-#undef  BE_CONSERVATIVE
+#define BE_CONSERVATIVE
 
 #include "sound_config.h"
 
@@ -44,18 +44,11 @@
 static void
 dma_init_buffers (int dev, struct dma_buffparms *dmap)
 {
-  if (dmap == audio_devs[dev]->dmap_out)
-    {
-      out_sleep_flag[dev].opts = WK_NONE;
-    }
-  else
-    {
-      in_sleep_flag[dev].opts = WK_NONE;
-    }
 
   dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
   dmap->byte_counter = 0;
-  dmap->bytes_in_use = audio_devs[dev]->buffsize;
+  dmap->max_byte_counter = 8000 * 60 * 60;
+  dmap->bytes_in_use = dmap->buffsize;
 
   dmap->dma_mode = DMODE_NONE;
   dmap->mapping_flags = 0;
@@ -92,16 +85,25 @@
       return -EBUSY;
     }
 
+  dma_init_buffers (dev, dmap);
   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;
 
 
-  dma_init_buffers (dev, dmap);
+  if (dmap->dma_mode & DMODE_OUTPUT)
+    {
+      out_sleep_flag[dev].opts = WK_NONE;
+    }
+  else
+    {
+      in_sleep_flag[dev].opts = WK_NONE;
+    }
 
   return 0;
 }
@@ -224,6 +226,13 @@
   audio_devs[dev]->d->set_channels (dev, 1);
   audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED);
 
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+    {
+      memset (audio_devs[dev]->dmap_out->raw_buf,
+	      audio_devs[dev]->dmap_out->neutral_byte,
+	      audio_devs[dev]->dmap_out->bytes_in_use);
+    }
+
   return 0;
 }
 
@@ -335,7 +344,12 @@
 void
 DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
 {
-  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE))
+  if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT))
+    return;			/* Don't start DMA yet */
+
+  dmap->dma_mode = DMODE_OUTPUT;
+
+  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
     {
       if (!(dmap->flags & DMA_STARTED))
 	{
@@ -345,12 +359,17 @@
 					  dmap->fragment_size, dmap->nbufs))
 	    return;
 
-	  local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
-			   DMA_MODE_WRITE);
+	  if (!(dmap->flags & DMA_NODMA))
+	    {
+	      local_start_dma (dev, 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;
       audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
 					dmap->qhead * dmap->fragment_size,
 					dmap->counts[dmap->qhead], 1);
@@ -560,7 +579,7 @@
 DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
 {
   unsigned long   flags;
-  int             err = EIO;
+  int             err = 0, n = 0;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
   if (!(audio_devs[dev]->open_mode & OPEN_READ))
@@ -575,77 +594,82 @@
       printk ("Sound: Can't read from mmapped device (1)\n");
       return -EINVAL;
     }
-  else if (!dmap->qlen)
-    {
-      int             tmout;
+  else
+    while (dmap->qlen <= 0 && n++ < 10)
+      {
+	int             tmout;
 
-      if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
-	  !audio_devs[dev]->go)
-	{
-	  restore_flags (flags);
-	  return -EAGAIN;
-	}
+	if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
+	    !audio_devs[dev]->go)
+	  {
+	    restore_flags (flags);
+	    return -EAGAIN;
+	  }
 
-      if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
-	{
-	  restore_flags (flags);
-	  return err;
-	}
+	if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
+	  {
+	    restore_flags (flags);
+	    return err;
+	  }
 
-      /* Wait for the next block */
+	/* Wait for the next block */
 
-      if (dontblock)
-	{
-	  restore_flags (flags);
-	  return -EAGAIN;
-	}
+	if (dontblock)
+	  {
+	    restore_flags (flags);
+	    return -EAGAIN;
+	  }
 
-      if (!audio_devs[dev]->go)
-	tmout = 0;
-      else
-	{
-	  tmout =
-	    (dmap->fragment_size * HZ) / dmap->data_rate;
+	if (!audio_devs[dev]->go)
+	  tmout = 0;
+	else
+	  {
+	    tmout =
+	      (dmap->fragment_size * HZ) / dmap->data_rate;
 
-	  tmout += HZ / 10;	/* Some safety distance */
+	    tmout += HZ / 10;	/* Some safety distance */
 
-	  if (tmout < (HZ / 2))
-	    tmout = HZ / 2;
-	  if (tmout > 20 * HZ)
-	    tmout = 20 * HZ;
-	}
+	    if (tmout < (HZ / 2))
+	      tmout = HZ / 2;
+	    if (tmout > 20 * HZ)
+	      tmout = 20 * HZ;
+	  }
 
 
-      {
-	unsigned long   tlimit;
+	{
+	  unsigned long   tlimit;
 
-	if (tmout)
-	  current->timeout = tlimit = jiffies + (tmout);
-	else
-	  tlimit = (unsigned long) -1;
-	in_sleep_flag[dev].opts = WK_SLEEP;
-	interruptible_sleep_on (&in_sleeper[dev]);
-	if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
+	  if (tmout)
+	    current->timeout = tlimit = jiffies + (tmout);
+	  else
+	    tlimit = (unsigned long) -1;
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  interruptible_sleep_on (&in_sleeper[dev]);
+	  if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
+	    {
+	      if (jiffies >= tlimit)
+		in_sleep_flag[dev].opts |= WK_TIMEOUT;
+	    }
+	  in_sleep_flag[dev].opts &= ~WK_SLEEP;
+	};
+	if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
 	  {
-	    if (jiffies >= tlimit)
-	      in_sleep_flag[dev].opts |= WK_TIMEOUT;
+	    err = -EIO;
+	    printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+	    dma_reset_input (dev);
+	    ;
 	  }
-	in_sleep_flag[dev].opts &= ~WK_SLEEP;
-      };
-      if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
-	{
-	  printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
-	  err = EIO;
-	  dma_reset_input (dev);
-	  ;
-	}
-      else
-	err = EINTR;
-    }
+	else
+	  err = -EINTR;
+      }
   restore_flags (flags);
 
-  if (!dmap->qlen)
-    return -err;
+  if (dmap->qlen <= 0)
+    {
+      if (err == 0)
+	err = -EINTR;
+      return err;
+    }
 
   *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
   *len = dmap->fragment_size - dmap->counts[dmap->qhead];
@@ -660,17 +684,16 @@
 
   int             p = dmap->counts[dmap->qhead] + c;
 
-  if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+  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;
-      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
-	printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n",
-		dev, dmap->qlen, dmap->nbufs);
       dmap->qlen--;
       dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
     }
@@ -681,7 +704,7 @@
 }
 
 int
-DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap)
+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
@@ -689,7 +712,6 @@
  */
   int             pos;
   unsigned long   flags;
-  int             chan = dmap->dma;
 
   save_flags (flags);
   cli ();
@@ -697,26 +719,31 @@
     pos = 0;
   else
     {
+      int             chan = dmap->dma;
+
       clear_dma_ff (chan);
       disable_dma (dmap->dma);
       pos = get_dma_residue (chan);
       pos = dmap->bytes_in_use - pos;
 
-      if (dmap->flags & DMODE_OUTPUT)
-	{
-	  if (dmap->qhead == 0)
-	    pos %= dmap->bytes_in_use;
-	}
-      else
-	{
-	  if (dmap->qtail == 0)
-	    pos %= dmap->bytes_in_use;
-	}
+      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 = dmap->bytes_in_use;
+      if (pos >= dmap->bytes_in_use)
+	pos = 0;
       enable_dma (dmap->dma);
     }
   restore_flags (flags);
@@ -760,8 +787,8 @@
   int             len, max, tmp;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  /* Don't allow touching pages too close to the playing ones */
-  int             lim = dmap->nbufs - 1;
+  int             lim = dmap->nbufs;
+
 
   if (lim < 2)
     lim = 2;
@@ -789,7 +816,7 @@
 
   if (len >= max)
     return 0;
-  return 1;
+  return max - len;
 }
 
 static int
@@ -812,7 +839,7 @@
   /*
    * Wait for free space
    */
-  if (!audio_devs[dev]->go)
+  if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT)
     tmout = 0;
   else
     {
@@ -850,13 +877,12 @@
   if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
     {
       printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
-      err = EIO;
       ;
       dma_reset_output (dev);
     }
   else if ((current->signal & ~current->blocked))
     {
-      err = EINTR;
+      err = -EINTR;
     }
 
   return err;
@@ -869,19 +895,24 @@
   unsigned long   flags;
   unsigned long   offs, active_offs;
   long            len;
+  int             maxfrags;
 
-  if (!DMAbuf_space_in_queue (dev))
-    return 0;
+  if (!(maxfrags = DMAbuf_space_in_queue (dev)))
+    {
+      return 0;
+    }
 
   save_flags (flags);
   cli ();
 
 #ifdef BE_CONSERVATIVE
-  active_offs = dmap->byte_counter + (dmap->qhead + 1) * dmap->fragment_size;
+  active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
 #else
-  active_offs = ((dmap->byte_counter + DMAbuf_get_buffer_pointer (dev, dmap)));
-  /* / dmap->fragment_size) * dmap->fragment_size; */
-
+  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) & ~3;
@@ -898,13 +929,12 @@
       return 0;
     }
 
-  if ((offs + len) > dmap->bytes_in_use)
-    len = dmap->bytes_in_use - offs;
+  if (len > maxfrags * dmap->fragment_size)
+    len = maxfrags * dmap->fragment_size;
 
   *size = len & ~3;
 
   restore_flags (flags);
-
   return (len > 0);
 }
 
@@ -912,7 +942,7 @@
 DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
 {
   unsigned long   flags;
-  int             err = EIO;
+  int             err = -EIO;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
   if (dmap->needs_reorg)
@@ -944,14 +974,8 @@
 	}
     }
 
-  if (!find_output_space (dev, buf, size))
-    {
-      restore_flags (flags);
-      return -EIO;		/* Caught a signal ? */
-    }
   restore_flags (flags);
 
-  dmap->flags |= DMA_DIRTY;
   return 0;
 }
 
@@ -962,7 +986,7 @@
   unsigned long   ptr = (dmap->user_counter / dmap->fragment_size)
   * dmap->fragment_size;
 
-  unsigned long   end_ptr, p, prev_count;
+  unsigned long   end_ptr, p;
   int             post = (dmap->flags & DMA_POST);
 
   ;
@@ -971,12 +995,16 @@
 
   dmap->cfrag = -1;
 
-  prev_count = dmap->user_counter;
   dmap->user_counter += l;
+  dmap->flags |= DMA_DIRTY;
 
-  if (dmap->user_counter < prev_count)	/* Wrap? */
-    {				/* Wrap the device counter too */
-      dmap->byte_counter %= dmap->bytes_in_use;
+  if (dmap->user_counter >= dmap->max_byte_counter)
+    {				/* Wrap the byte counters */
+      long            decr = dmap->user_counter;
+
+      dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+      decr -= dmap->user_counter;
+      dmap->byte_counter -= decr;
     }
 
   end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
@@ -995,6 +1023,13 @@
 
   dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
 
+/*
+ * Let the low level driver to perform some postprocessing to
+ * the written data.
+ */
+  if (audio_devs[dev]->d->postprocess_write)
+    audio_devs[dev]->d->postprocess_write (dev);
+
   if (!(dmap->flags & DMA_ACTIVE))
     if (dmap->qlen > 1 ||
 	(dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
@@ -1034,11 +1069,6 @@
   if (chan < 0)
     return 0;
 
-  /*
-   * The count must be one less than the actual size. This is handled by
-   * set_dma_addr()
-   */
-
   sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
 
   return count;
@@ -1069,14 +1099,14 @@
       return 0;
     }
 
+  if (dmap->flags & DMA_NODMA)
+    {
+      return 1;
+    }
+
   if (chan < 0)
     return 0;
 
-  /*
-   * The count must be one less than the actual size. This is handled by
-   * set_dma_addr()
-   */
-
   sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
   dmap->flags |= DMA_STARTED;
 
@@ -1100,19 +1130,9 @@
   restore_flags (flags);
 }
 
-void
-DMAbuf_outputintr (int dev, int event_type)
+static void
+do_outputintr (int dev, int dummy)
 {
-  /*
-     * Event types:
-     *  0 = DMA transfer done. Device still has more data in the local
-     *      buffer.
-     *  1 = DMA transfer done. Device doesn't have local buffer or it's
-     *      empty now.
-     *  2 = No DMA transfer but the device has now more space in it's local
-     *      buffer.
-   */
-
   unsigned long   flags;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
   int             this_fragment;
@@ -1128,18 +1148,20 @@
       return;
     }
 
-  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)	/* Virtual memory mapped access */
     {
-      unsigned long   prev_counter = dmap->byte_counter;
-
       /* 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 < prev_counter)	/* Overflow */
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
 	    {
-	      dmap->user_counter %= dmap->bytes_in_use;
+	      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;
 	    }
 	}
 
@@ -1153,21 +1175,6 @@
       return;
     }
 
-  if (event_type == 2)
-    {
-      finish_output_interrupt (dev, dmap);
-      return;
-    }
-
-  if (dmap->qlen > dmap->nbufs)
-    dmap->qlen = dmap->nbufs;
-
-  if (dmap->qlen <= 0)
-    {
-      finish_output_interrupt (dev, dmap);
-      return;
-    }
-
   save_flags (flags);
   cli ();
 
@@ -1177,32 +1184,34 @@
 
   if (dmap->qhead == 0)		/* Wrapped */
     {
-      unsigned long   prev_counter = dmap->byte_counter;
-
       dmap->byte_counter += dmap->bytes_in_use;
-      if (dmap->byte_counter < prev_counter)	/* Overflow */
+      if (dmap->byte_counter >= dmap->max_byte_counter)		/* Overflow */
 	{
-	  dmap->user_counter %= dmap->bytes_in_use;
+	  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 (!(audio_devs[dev]->flags & DMA_AUTOMODE))
     dmap->flags &= ~DMA_ACTIVE;
 
-  if (event_type == 1 && dmap->qlen < 1)
+  while (dmap->qlen < 0)
     {
       dmap->underrun_count++;
 
-      dmap->qlen = 0;
+      dmap->qlen++;
       if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
 	{
 	  dmap->flags &= ~DMA_DIRTY;
 	  memset (audio_devs[dev]->dmap_out->raw_buf,
 		  audio_devs[dev]->dmap_out->neutral_byte,
-		  audio_devs[dev]->dmap_out->bytes_in_use);
+		  audio_devs[dev]->dmap_out->buffsize);
 	}
       dmap->user_counter += dmap->fragment_size;
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
     }
 
   if (dmap->qlen > 0)
@@ -1213,10 +1222,43 @@
 }
 
 void
-DMAbuf_inputintr (int dev)
+DMAbuf_outputintr (int dev, int notify_only)
 {
   unsigned long   flags;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  save_flags (flags);
+  cli ();
+
+  if (!(dmap->flags & DMA_NODMA))
+    {
+      int             chan = dmap->dma, pos, n;
+
+      clear_dma_ff (chan);
+      disable_dma (dmap->dma);
+      pos = dmap->bytes_in_use - get_dma_residue (chan);
+      enable_dma (dmap->dma);
+
+      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);
+  restore_flags (flags);
+}
+
+static void
+do_inputintr (int dev)
+{
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+  unsigned long   flags;
 
 #ifdef OS_DMA_INTR
   if (audio_devs[dev]->dmap_in->dma >= 0)
@@ -1234,12 +1276,14 @@
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)	/* Wrapped */
 	{
-	  unsigned long   prev_counter = dmap->byte_counter;
-
 	  dmap->byte_counter += dmap->bytes_in_use;
-	  if (dmap->byte_counter < prev_counter)	/* Overflow */
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
 	    {
-	      dmap->user_counter %= dmap->bytes_in_use;
+	      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++;
@@ -1267,30 +1311,28 @@
 
       /* 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
+  else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs)
     {
       dmap->qlen++;
-      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
-	printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n",
-		dev, dmap->qlen, dmap->nbufs);
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)	/* Wrapped */
 	{
-	  unsigned long   prev_counter = dmap->byte_counter;
-
 	  dmap->byte_counter += dmap->bytes_in_use;
-	  if (dmap->byte_counter < prev_counter)	/* Overflow */
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
 	    {
-	      dmap->user_counter %= dmap->bytes_in_use;
+	      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 (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+  if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
     {
-      if (dmap->needs_reorg)
-	reorganize_buffers (dev, dmap, 0);
       local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
 		       DMA_MODE_READ);
       audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
@@ -1305,13 +1347,47 @@
 
   save_flags (flags);
   cli ();
-  if ((in_sleep_flag[dev].opts & WK_SLEEP))
-    {
+  if (dmap->qlen > 0)
+    if ((in_sleep_flag[dev].opts & WK_SLEEP))
       {
-	in_sleep_flag[dev].opts = WK_WAKEUP;
-	wake_up (&in_sleeper[dev]);
-      };
+	{
+	  in_sleep_flag[dev].opts = WK_WAKEUP;
+	  wake_up (&in_sleeper[dev]);
+	};
+      }
+  restore_flags (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+
+  if (!(dmap->flags & DMA_NODMA))
+    {
+      int             chan = dmap->dma, pos, n;
+
+      clear_dma_ff (chan);
+      disable_dma (dmap->dma);
+      pos = dmap->bytes_in_use - get_dma_residue (chan);
+      enable_dma (dmap->dma);
+
+      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);
   restore_flags (flags);
 }
 
@@ -1330,8 +1406,9 @@
       return -EBUSY;
     }
   dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  out_sleep_flag[dev].opts = WK_NONE;
   audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
-  audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->buffsize;
+  audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize;
 
   if (chan >= 0)
     {
@@ -1339,7 +1416,7 @@
 
       save_flags (flags);
       cli ();
-      disable_dma (chan);
+      disable_dma (audio_devs[dev]->dmap_out->dma);
       clear_dma_ff (chan);
       restore_flags (flags);
     }
@@ -1354,37 +1431,39 @@
 }
 
 void
-DMAbuf_init (void)
+DMAbuf_init (int dev, int dma1, int dma2)
 {
-  int             dev;
-
   /*
      * NOTE! This routine could be called several times.
    */
 
-  for (dev = 0; dev < num_audiodevs; dev++)
-    if (audio_devs[dev]->dmap_out == NULL)
-      {
-	if (audio_devs[dev]->d == NULL)
-	  panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
-	if (audio_devs[dev]->parent_dev)
-	  {			/* Use DMA map of the parent dev */
-	    int             parent = audio_devs[dev]->parent_dev - 1;
+  if (audio_devs[dev]->dmap_out == NULL)
+    {
+      if (audio_devs[dev]->d == NULL)
+	panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
 
-	    audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
-	    audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
-	  }
-	else
-	  {
-	    audio_devs[dev]->dmap_out =
-	      audio_devs[dev]->dmap_in =
-	      &dmaps[ndmaps++];
+      if (audio_devs[dev]->parent_dev)
+	{			/* Use DMA map of the parent dev */
+	  int             parent = audio_devs[dev]->parent_dev - 1;
 
-	    if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
+	  audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
+	}
+      else
+	{
+	  audio_devs[dev]->dmap_out =
+	    audio_devs[dev]->dmap_in =
+	    &dmaps[ndmaps++];
+	  audio_devs[dev]->dmap_out->dma = dma1;
+
+	  if (audio_devs[dev]->flags & DMA_DUPLEX)
+	    {
 	      audio_devs[dev]->dmap_in =
 		&dmaps[ndmaps++];
-	  }
-      }
+	      audio_devs[dev]->dmap_in->dma = dma2;
+	    }
+	}
+    }
 }
 
 int
@@ -1396,7 +1475,7 @@
   switch (sel_type)
     {
     case SEL_IN:
-      if (!(audio_devs[dev]->open_mode))
+      if (!(audio_devs[dev]->open_mode & OPEN_READ))
 	return 0;
 
       dmap = audio_devs[dev]->dmap_in;
@@ -1447,6 +1526,9 @@
 
     case SEL_OUT:
       dmap = audio_devs[dev]->dmap_out;
+
+      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	return 0;
 
       if (dmap->mapping_flags & DMA_MAP_MAPPED)
 	{
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.1.40/linux/drivers/sound/dmasound.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/dmasound.c	Sun May 11 02:59:34 1997
@@ -0,0 +1,3435 @@
+
+/* linux/drivers/sound/dmasound.c */
+
+/*
+
+OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k
+
+(c) 1995 by Michael Schlueter & Michael Marte
+
+Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
+interface and the u-law to signed byte conversion.
+
+Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
+/dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
+to thank:
+Michael Schlueter for initial ideas and documentation on the MFP and
+the DMA sound hardware.
+Therapy? for their CD 'Troublegum' which really made me rock.
+
+/dev/sndstat is based on code by Hannu Savolainen, the author of the
+VoxWare family of drivers.
+
+This file is subject to the terms and conditions of the GNU General Public
+License.  See the file COPYING in the main directory of this archive
+for more details.
+
+History:
+1995/8/25	first release
+
+1995/9/02	++roman: fixed atari_stram_alloc() call, the timer programming
+			and several race conditions
+
+1995/9/14	++roman: After some discussion with Michael Schlueter, revised
+			the interrupt disabling
+			Slightly speeded up U8->S8 translation by using long
+			operations where possible
+			Added 4:3 interpolation for /dev/audio
+
+1995/9/20	++TeSche: Fixed a bug in sq_write and changed /dev/audio
+			converting to play at 12517Hz instead of 6258Hz.
+
+1995/9/23	++TeSche: Changed sq_interrupt() and sq_play() to pre-program
+			the DMA for another frame while there's still one
+			running. This allows the IRQ response to be
+			arbitrarily delayed and playing will still continue.
+
+1995/10/14	++Guenther_Kelleter@ac3.maus.de, ++TeSche: better support for
+			Falcon audio (the Falcon doesn't raise an IRQ at the
+			end of a frame, but at the beginning instead!). uses
+			'if (codec_dma)' in lots of places to simply switch
+			between Falcon and TT code.
+
+1995/11/06	++TeSche: started introducing a hardware abstraction scheme
+			(may perhaps also serve for Amigas?), can now play
+			samples at almost all frequencies by means of a more
+			generalized expand routine, takes a good deal of care
+			to cut data only at sample sizes, buffer size is now
+			a kernel runtime option, implemented fsync() & several
+			minor improvements
+		++Guenther: useful hints and bug fixes, cross-checked it for
+			Falcons
+
+1996/3/9	++geert: support added for Amiga, A-law, 16-bit little endian.
+			Unification to drivers/sound/dmasound.c.
+
+1996/4/6	++Martin Mitchell: updated to 1.3 kernel.
+
+1996/6/13       ++topi: fixed things that were broken (mainly the amiga
+                        14-bit routines), /dev/sndstat shows now the real
+                        hardware frequency, the lowpass filter is disabled
+			by default now.
+
+1996/9/25	++geert: modularization
+
+*/
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/config.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_ATARI
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif /* CONFIG_AMIGA */
+
+#include "dmasound.h"
+#include <linux/soundcard.h>
+
+
+#ifdef MODULE
+static int chrdev_registered = 0;
+static int irq_installed = 0;
+#endif /* MODULE */
+static char **sound_buffers = NULL;
+
+
+#ifdef CONFIG_ATARI
+extern void atari_microwire_cmd(int cmd);
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+   /*
+    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern volatile u_short amiga_audio_min_period;
+
+
+   /*
+    *	amiga_mksound() should be able to restore the period after beeping
+    *	(Imported from arch/m68k/amiga/amisound.c)
+    */
+
+extern u_short amiga_audio_period;
+
+
+   /*
+    *	Audio DMA masks
+    */
+
+#define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
+#define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
+#define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
+
+#endif /* CONFIG_AMIGA */
+
+
+/*** Some declarations *******************************************************/
+
+
+#define DMASND_TT		1
+#define DMASND_FALCON		2
+#define DMASND_AMIGA		3
+
+#define MAX_CATCH_RADIUS	10
+#define MIN_BUFFERS		4
+#define MIN_BUFSIZE 		4
+#define MAX_BUFSIZE		128	/* Limit for Amiga */
+
+static int catchRadius = 0, numBufs = 4, bufSize = 32;
+
+
+#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
+#define min(x, y)	((x) < (y) ? (x) : (y))
+#define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
+#define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
+
+#define IOCTL_IN(arg, ret) \
+ do { int error = get_user(ret, (int *)(arg)); \
+      if (error) return error; \
+ } while (0)
+#define IOCTL_OUT(arg, ret)	ioctl_return((int *)(arg), ret)
+
+
+/*** Some low level helpers **************************************************/
+
+
+/* 8 bit mu-law */
+
+static char ulaw2dma8[] = {
+    -126,   -122,   -118,   -114,   -110,   -106,   -102,    -98,
+     -94,    -90,    -86,    -82,    -78,    -74,    -70,    -66,
+     -63,    -61,    -59,    -57,    -55,    -53,    -51,    -49,
+     -47,    -45,    -43,    -41,    -39,    -37,    -35,    -33,
+     -31,    -30,    -29,    -28,    -27,    -26,    -25,    -24,
+     -23,    -22,    -21,    -20,    -19,    -18,    -17,    -16,
+     -16,    -15,    -15,    -14,    -14,    -13,    -13,    -12,
+     -12,    -11,    -11,    -10,    -10,     -9,     -9,     -8,
+      -8,     -8,     -7,     -7,     -7,     -7,     -6,     -6,
+      -6,     -6,     -5,     -5,     -5,     -5,     -4,     -4,
+      -4,     -4,     -4,     -4,     -3,     -3,     -3,     -3,
+      -3,     -3,     -3,     -3,     -2,     -2,     -2,     -2,
+      -2,     -2,     -2,     -2,     -2,     -2,     -2,     -2,
+      -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,
+      -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,
+      -1,     -1,     -1,     -1,     -1,     -1,     -1,      0,
+     125,    121,    117,    113,    109,    105,    101,     97,
+      93,     89,     85,     81,     77,     73,     69,     65,
+      62,     60,     58,     56,     54,     52,     50,     48,
+      46,     44,     42,     40,     38,     36,     34,     32,
+      30,     29,     28,     27,     26,     25,     24,     23,
+      22,     21,     20,     19,     18,     17,     16,     15,
+      15,     14,     14,     13,     13,     12,     12,     11,
+      11,     10,     10,      9,      9,      8,      8,      7,
+       7,      7,      6,      6,      6,      6,      5,      5,
+       5,      5,      4,      4,      4,      4,      3,      3,
+       3,      3,      3,      3,      2,      2,      2,      2,
+       2,      2,      2,      2,      1,      1,      1,      1,
+       1,      1,      1,      1,      1,      1,      1,      1,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0
+};
+
+/* 8 bit A-law */
+
+static char alaw2dma8[] = {
+     -22,    -21,    -24,    -23,    -18,    -17,    -20,    -19,
+     -30,    -29,    -32,    -31,    -26,    -25,    -28,    -27,
+     -11,    -11,    -12,    -12,     -9,     -9,    -10,    -10,
+     -15,    -15,    -16,    -16,    -13,    -13,    -14,    -14,
+     -86,    -82,    -94,    -90,    -70,    -66,    -78,    -74,
+    -118,   -114,   -126,   -122,   -102,    -98,   -110,   -106,
+     -43,    -41,    -47,    -45,    -35,    -33,    -39,    -37,
+     -59,    -57,    -63,    -61,    -51,    -49,    -55,    -53,
+      -2,     -2,     -2,     -2,     -2,     -2,     -2,     -2,
+      -2,     -2,     -2,     -2,     -2,     -2,     -2,     -2,
+      -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,
+      -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,
+      -6,     -6,     -6,     -6,     -5,     -5,     -5,     -5,
+      -8,     -8,     -8,     -8,     -7,     -7,     -7,     -7,
+      -3,     -3,     -3,     -3,     -3,     -3,     -3,     -3,
+      -4,     -4,     -4,     -4,     -4,     -4,     -4,     -4,
+      21,     20,     23,     22,     17,     16,     19,     18,
+      29,     28,     31,     30,     25,     24,     27,     26,
+      10,     10,     11,     11,      8,      8,      9,      9,
+      14,     14,     15,     15,     12,     12,     13,     13,
+      86,     82,     94,     90,     70,     66,     78,     74,
+     118,    114,    126,    122,    102,     98,    110,    106,
+      43,     41,     47,     45,     35,     33,     39,     37,
+      59,     57,     63,     61,     51,     49,     55,     53,
+       1,      1,      1,      1,      1,      1,      1,      1,
+       1,      1,      1,      1,      1,      1,      1,      1,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       5,      5,      5,      5,      4,      4,      4,      4,
+       7,      7,      7,      7,      6,      6,      6,      6,
+       2,      2,      2,      2,      2,      2,      2,      2,
+       3,      3,      3,      3,      3,      3,      3,      3
+};
+
+
+#ifdef HAS_16BIT_TABLES
+
+/* 16 bit mu-law */
+
+static char ulaw2dma16[] = {
+    -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
+    -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
+    -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
+    -11900, -11388, -10876, -10364,  -9852,  -9340,  -8828,  -8316,
+     -7932,  -7676,  -7420,  -7164,  -6908,  -6652,  -6396,  -6140,
+     -5884,  -5628,  -5372,  -5116,  -4860,  -4604,  -4348,  -4092,
+     -3900,  -3772,  -3644,  -3516,  -3388,  -3260,  -3132,  -3004,
+     -2876,  -2748,  -2620,  -2492,  -2364,  -2236,  -2108,  -1980,
+     -1884,  -1820,  -1756,  -1692,  -1628,  -1564,  -1500,  -1436,
+     -1372,  -1308,  -1244,  -1180,  -1116,  -1052,   -988,   -924,
+      -876,   -844,   -812,   -780,   -748,   -716,   -684,   -652,
+      -620,   -588,   -556,   -524,   -492,   -460,   -428,   -396,
+      -372,   -356,   -340,   -324,   -308,   -292,   -276,   -260,
+      -244,   -228,   -212,   -196,   -180,   -164,   -148,   -132,
+      -120,   -112,   -104,    -96,    -88,    -80,    -72,    -64,
+       -56,    -48,    -40,    -32,    -24,    -16,     -8,      0,
+     32124,  31100,  30076,  29052,  28028,  27004,  25980,  24956,
+     23932,  22908,  21884,  20860,  19836,  18812,  17788,  16764,
+     15996,  15484,  14972,  14460,  13948,  13436,  12924,  12412,
+     11900,  11388,  10876,  10364,   9852,   9340,   8828,   8316,
+      7932,   7676,   7420,   7164,   6908,   6652,   6396,   6140,
+      5884,   5628,   5372,   5116,   4860,   4604,   4348,   4092,
+      3900,   3772,   3644,   3516,   3388,   3260,   3132,   3004,
+      2876,   2748,   2620,   2492,   2364,   2236,   2108,   1980,
+      1884,   1820,   1756,   1692,   1628,   1564,   1500,   1436,
+      1372,   1308,   1244,   1180,   1116,   1052,    988,    924,
+       876,    844,    812,    780,    748,    716,    684,    652,
+       620,    588,    556,    524,    492,    460,    428,    396,
+       372,    356,    340,    324,    308,    292,    276,    260,
+       244,    228,    212,    196,    180,    164,    148,    132,
+       120,    112,    104,     96,     88,     80,     72,     64,
+        56,     48,     40,     32,     24,     16,      8,      0,
+};
+
+/* 16 bit A-law */
+
+static char alaw2dma16[] = {
+     -5504,  -5248,  -6016,  -5760,  -4480,  -4224,  -4992,  -4736,
+     -7552,  -7296,  -8064,  -7808,  -6528,  -6272,  -7040,  -6784,
+     -2752,  -2624,  -3008,  -2880,  -2240,  -2112,  -2496,  -2368,
+     -3776,  -3648,  -4032,  -3904,  -3264,  -3136,  -3520,  -3392,
+    -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
+    -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
+    -11008, -10496, -12032, -11520,  -8960,  -8448,  -9984,  -9472,
+    -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
+      -344,   -328,   -376,   -360,   -280,   -264,   -312,   -296,
+      -472,   -456,   -504,   -488,   -408,   -392,   -440,   -424,
+       -88,    -72,   -120,   -104,    -24,     -8,    -56,    -40,
+      -216,   -200,   -248,   -232,   -152,   -136,   -184,   -168,
+     -1376,  -1312,  -1504,  -1440,  -1120,  -1056,  -1248,  -1184,
+     -1888,  -1824,  -2016,  -1952,  -1632,  -1568,  -1760,  -1696,
+      -688,   -656,   -752,   -720,   -560,   -528,   -624,   -592,
+      -944,   -912,  -1008,   -976,   -816,   -784,   -880,   -848,
+      5504,   5248,   6016,   5760,   4480,   4224,   4992,   4736,
+      7552,   7296,   8064,   7808,   6528,   6272,   7040,   6784,
+      2752,   2624,   3008,   2880,   2240,   2112,   2496,   2368,
+      3776,   3648,   4032,   3904,   3264,   3136,   3520,   3392,
+     22016,  20992,  24064,  23040,  17920,  16896,  19968,  18944,
+     30208,  29184,  32256,  31232,  26112,  25088,  28160,  27136,
+     11008,  10496,  12032,  11520,   8960,   8448,   9984,   9472,
+     15104,  14592,  16128,  15616,  13056,  12544,  14080,  13568,
+       344,    328,    376,    360,    280,    264,    312,    296,
+       472,    456,    504,    488,    408,    392,    440,    424,
+        88,     72,    120,    104,     24,      8,     56,     40,
+       216,    200,    248,    232,    152,    136,    184,    168,
+      1376,   1312,   1504,   1440,   1120,   1056,   1248,   1184,
+      1888,   1824,   2016,   1952,   1632,   1568,   1760,   1696,
+       688,    656,    752,    720,    560,    528,    624,    592,
+       944,    912,   1008,    976,    816,    784,    880,    848,
+};
+#endif /* HAS_16BIT_TABLES */
+
+
+#ifdef HAS_14BIT_TABLES
+
+/* 14 bit mu-law (LSB) */
+
+static char alaw2dma14l[] = {
+        33,     33,     33,     33,     33,     33,     33,     33,
+        33,     33,     33,     33,     33,     33,     33,     33,
+        33,     33,     33,     33,     33,     33,     33,     33,
+        33,     33,     33,     33,     33,     33,     33,     33,
+         1,      1,      1,      1,      1,      1,      1,      1,
+         1,      1,      1,      1,      1,      1,      1,      1,
+        49,     17,     49,     17,     49,     17,     49,     17,
+        49,     17,     49,     17,     49,     17,     49,     17,
+        41,     57,      9,     25,     41,     57,      9,     25,
+        41,     57,      9,     25,     41,     57,      9,     25,
+        37,     45,     53,     61,      5,     13,     21,     29,
+        37,     45,     53,     61,      5,     13,     21,     29,
+        35,     39,     43,     47,     51,     55,     59,     63,
+         3,      7,     11,     15,     19,     23,     27,     31,
+        34,     36,     38,     40,     42,     44,     46,     48,
+        50,     52,     54,     56,     58,     60,     62,      0,
+        31,     31,     31,     31,     31,     31,     31,     31,
+        31,     31,     31,     31,     31,     31,     31,     31,
+        31,     31,     31,     31,     31,     31,     31,     31,
+        31,     31,     31,     31,     31,     31,     31,     31,
+        63,     63,     63,     63,     63,     63,     63,     63,
+        63,     63,     63,     63,     63,     63,     63,     63,
+        15,     47,     15,     47,     15,     47,     15,     47,
+        15,     47,     15,     47,     15,     47,     15,     47,
+        23,      7,     55,     39,     23,      7,     55,     39,
+        23,      7,     55,     39,     23,      7,     55,     39,
+        27,     19,     11,      3,     59,     51,     43,     35,
+        27,     19,     11,      3,     59,     51,     43,     35,
+        29,     25,     21,     17,     13,      9,      5,      1,
+        61,     57,     53,     49,     45,     41,     37,     33,
+        30,     28,     26,     24,     22,     20,     18,     16,
+        14,     12,     10,      8,      6,      4,      2,      0
+};
+
+/* 14 bit A-law (LSB) */
+
+static char alaw2dma14l[] = {
+        32,     32,     32,     32,     32,     32,     32,     32,
+        32,     32,     32,     32,     32,     32,     32,     32,
+        16,     48,     16,     48,     16,     48,     16,     48,
+        16,     48,     16,     48,     16,     48,     16,     48,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+        42,     46,     34,     38,     58,     62,     50,     54,
+        10,     14,      2,      6,     26,     30,     18,     22,
+        42,     46,     34,     38,     58,     62,     50,     54,
+        10,     14,      2,      6,     26,     30,     18,     22,
+        40,     56,      8,     24,     40,     56,      8,     24,
+        40,     56,      8,     24,     40,     56,      8,     24,
+        20,     28,      4,     12,     52,     60,     36,     44,
+        20,     28,      4,     12,     52,     60,     36,     44,
+        32,     32,     32,     32,     32,     32,     32,     32,
+        32,     32,     32,     32,     32,     32,     32,     32,
+        48,     16,     48,     16,     48,     16,     48,     16,
+        48,     16,     48,     16,     48,     16,     48,     16,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+         0,      0,      0,      0,      0,      0,      0,      0,
+        22,     18,     30,     26,      6,      2,     14,     10,
+        54,     50,     62,     58,     38,     34,     46,     42,
+        22,     18,     30,     26,      6,      2,     14,     10,
+        54,     50,     62,     58,     38,     34,     46,     42,
+        24,      8,     56,     40,     24,      8,     56,     40,
+        24,      8,     56,     40,     24,      8,     56,     40,
+        44,     36,     60,     52,     12,      4,     28,     20,
+        44,     36,     60,     52,     12,      4,     28,     20
+};
+#endif /* HAS_14BIT_TABLES */
+
+
+/*** Translations ************************************************************/
+
+
+#ifdef CONFIG_ATARI
+static long ata_ct_law(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_law(const u_char *userPtr, unsigned long userCount,
+			u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft);
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+static long ami_ct_law(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft);
+#endif /* CONFIG_AMIGA */
+
+
+/*** Machine definitions *****************************************************/
+
+
+typedef struct {
+    int type;
+    void *(*dma_alloc)(unsigned int, int);
+    void (*dma_free)(void *, unsigned int);
+    int (*irqinit)(void);
+#ifdef MODULE
+    void (*irqcleanup)(void);
+#endif /* MODULE */
+    void (*init)(void);
+    void (*silence)(void);
+    int (*setFormat)(int);
+    int (*setVolume)(int);
+    int (*setBass)(int);
+    int (*setTreble)(int);
+    void (*play)(void);
+} MACHINE;
+
+
+/*** Low level stuff *********************************************************/
+
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
+
+typedef struct {
+    long (*ct_ulaw)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_alaw)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_s8)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_u8)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_s16be)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_u16be)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_s16le)(const u_char *, unsigned long, u_char *, long *, long);
+    long (*ct_u16le)(const u_char *, unsigned long, u_char *, long *, long);
+} TRANS;
+
+struct sound_settings {
+    MACHINE mach;	/* machine dependent things */
+    SETTINGS hard;	/* hardware settings */
+    SETTINGS soft;	/* software settings */
+    SETTINGS dsp;	/* /dev/dsp default settings */
+    TRANS *trans;	/* supported translations */
+    int volume_left;	/* volume (range is machine dependent) */
+    int volume_right;
+    int bass;		/* tone (range is machine dependent) */
+    int treble;
+    int minDev;		/* minor device number currently open */
+#ifdef CONFIG_ATARI
+    int bal;		/* balance factor for expanding (not volume!) */
+    u_long data;	/* data for expanding */
+#endif /* CONFIG_ATARI */
+};
+
+static struct sound_settings sound;
+
+
+#ifdef CONFIG_ATARI
+static void *AtaAlloc(unsigned int size, int flags);
+static void AtaFree(void *, unsigned int size);
+static int AtaIrqInit(void);
+#ifdef MODULE
+static void AtaIrqCleanUp(void);
+#endif /* MODULE */
+static int AtaSetBass(int bass);
+static int AtaSetTreble(int treble);
+static void TTSilence(void);
+static void TTInit(void);
+static int TTSetFormat(int format);
+static int TTSetVolume(int volume);
+static void FalconSilence(void);
+static void FalconInit(void);
+static int FalconSetFormat(int format);
+static int FalconSetVolume(int volume);
+static void ata_sq_play_next_frame(int index);
+static void AtaPlay(void);
+static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+static void *AmiAlloc(unsigned int size, int flags);
+static void AmiFree(void *, unsigned int);
+static int AmiIrqInit(void);
+#ifdef MODULE
+static void AmiIrqCleanUp(void);
+#endif /* MODULE */
+static void AmiSilence(void);
+static void AmiInit(void);
+static int AmiSetFormat(int format);
+static int AmiSetVolume(int volume);
+static int AmiSetTreble(int treble);
+static void ami_sq_play_next_frame(int index);
+static void AmiPlay(void);
+static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
+#endif /* CONFIG_AMIGA */
+
+
+/*** Mid level stuff *********************************************************/
+
+
+static void sound_silence(void);
+static void sound_init(void);
+static int sound_set_format(int format);
+static int sound_set_speed(int speed);
+static int sound_set_stereo(int stereo);
+static int sound_set_volume(int volume);
+#ifdef CONFIG_ATARI
+static int sound_set_bass(int bass);
+#endif /* CONFIG_ATARI */
+static int sound_set_treble(int treble);
+static long sound_copy_translate(const u_char *userPtr,
+				 unsigned long userCount,
+				 u_char frame[], long *frameUsed,
+				 long frameLeft);
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+struct sound_mixer {
+    int busy;
+};
+
+static struct sound_mixer mixer;
+
+static void mixer_init(void);
+static int mixer_open(int open_mode);
+static int mixer_release(void);
+static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg);
+
+
+/*
+ * Sound queue stuff, the heart of the driver
+ */
+
+struct sound_queue {
+    int max_count, block_size;
+    char **buffers;
+
+    /* it shouldn't be necessary to declare any of these volatile */
+    int front, rear, count;
+    int rear_size;
+    /*
+     *	The use of the playing field depends on the hardware
+     *
+     *	Atari: The number of frames that are loaded/playing
+     *
+     *	Amiga: Bit 0 is set: a frame is loaded
+     *	       Bit 1 is set: a frame is playing
+     */
+    int playing;
+    struct wait_queue *write_queue, *open_queue, *sync_queue;
+    int open_mode;
+    int busy, syncing;
+#ifdef CONFIG_ATARI
+    int ignore_int;		/* ++TeSche: used for Falcon */
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+    int block_size_half, block_size_quarter;
+#endif /* CONFIG_AMIGA */
+};
+
+static struct sound_queue sq;
+
+#define sq_block_address(i)	(sq.buffers[i])
+#define SIGNAL_RECEIVED	(current->signal & ~current->blocked)
+#define NON_BLOCKING(open_mode)	(open_mode & O_NONBLOCK)
+#define ONE_SECOND	HZ	/* in jiffies (100ths of a second) */
+#define NO_TIME_LIMIT	0xffffffff
+#define SLEEP(queue, time_limit) \
+	current->timeout = jiffies+(time_limit); \
+	interruptible_sleep_on(&queue);
+#define WAKE_UP(queue)	(wake_up_interruptible(&queue))
+
+static void sq_init(int numBufs, int bufSize, char **buffers);
+static void sq_play(void);
+static long sq_write(const char *src, unsigned long uLeft);
+static int sq_open(int open_mode);
+static void sq_reset(void);
+static int sq_sync(void);
+static int sq_release(void);
+
+
+/*
+ * /dev/sndstat
+ */
+
+struct sound_state {
+    int busy;
+    char buf[512];
+    int len, ptr;
+};
+
+static struct sound_state state;
+
+static void state_init(void);
+static int state_open(int open_mode);
+static int state_release(void);
+static long state_read(char *dest, unsigned long count);
+
+
+/*** High level stuff ********************************************************/
+
+
+static int sound_open(struct inode *inode, struct file *file);
+static int sound_fsync(struct inode *inode, struct file *filp);
+static void sound_release(struct inode *inode, struct file *file);
+static long long sound_lseek(struct inode *inode, struct file *file,
+			     long long offset, int orig);
+static long sound_read(struct inode *inode, struct file *file, char *buf,
+		       unsigned long count);
+static long sound_write(struct inode *inode, struct file *file,
+			const char *buf, unsigned long count);
+static inline int ioctl_return(int *addr, int value)
+{
+    if (value < 0)
+	return(value);
+
+    return put_user(value, addr);
+}
+static int unknown_minor_dev(char *fname, int dev);
+static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg);
+
+
+/*** Config & Setup **********************************************************/
+
+
+void soundcard_init(void);
+void dmasound_setup(char *str, int *ints);
+void sound_setup(char *str, int *ints);		/* ++Martin: stub for now */
+
+
+/*** Translations ************************************************************/
+
+
+/* ++TeSche: radically changed for new expanding purposes...
+ *
+ * These two routines now deal with copying/expanding/translating the samples
+ * from user space into our buffer at the right frequency. They take care about
+ * how much data there's actually to read, how much buffer space there is and
+ * to convert samples into the right frequency/encoding. They will only work on
+ * complete samples so it may happen they leave some bytes in the input stream
+ * if the user didn't write a multiple of the current sample size. They both
+ * return the number of bytes they've used from both streams so you may detect
+ * such a situation. Luckily all programs should be able to cope with that.
+ *
+ * I think I've optimized anything as far as one can do in plain C, all
+ * variables should fit in registers and the loops are really short. There's
+ * one loop for every possible situation. Writing a more generalized and thus
+ * parameterized loop would only produce slower code. Feel free to optimize
+ * this in assembler if you like. :)
+ *
+ * I think these routines belong here because they're not yet really hardware
+ * independent, especially the fact that the Falcon can play 16bit samples
+ * only in stereo is hardcoded in both of them!
+ *
+ * ++geert: split in even more functions (one per format)
+ */
+
+#ifdef CONFIG_ATARI
+static long ata_ct_law(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft)
+{
+    char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+    long count, used;
+    u_char *p = &frame[*frameUsed];
+
+    count = min(userCount, frameLeft);
+    if (sound.soft.stereo)
+	count &= ~1;
+    used = count;
+    while (count > 0) {
+	u_char data;
+	get_user(data, userPtr++);
+	*p++ = table[data];
+	count--;
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ata_ct_s8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    void *p = &frame[*frameUsed];
+
+    count = min(userCount, frameLeft);
+    if (sound.soft.stereo)
+	count &= ~1;
+    used = count;
+    copy_from_user(p, userPtr, count);
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ata_ct_u8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+
+    if (!sound.soft.stereo) {
+	u_char *p = &frame[*frameUsed];
+	count = min(userCount, frameLeft);
+	used = count;
+	while (count > 0) {
+	    u_char data;
+	    get_user(data, userPtr++);
+	    *p++ = data ^ 0x80;
+	    count--;
+	}
+    } else {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>1;
+	used = count*2;
+	while (count > 0) {
+	    u_short data;
+	    get_user(data, ((u_short *)userPtr)++);
+	    *p++ = data ^ 0x8080;
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    *p++ = data;
+	    *p++ = data;
+	    count--;
+	}
+	*frameUsed += used*2;
+    } else {
+	void *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft) & ~3;
+	used = count;
+	copy_from_user(p, userPtr, count);
+	*frameUsed += used;
+    }
+    return(used);
+}
+
+
+static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data ^= 0x8000;
+	    *p++ = data;
+	    *p++ = data;
+	    count--;
+	}
+	*frameUsed += used*2;
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>2;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_int *)userPtr)++);
+	    *p++ = data ^ 0x80008000;
+	    count--;
+	}
+	*frameUsed += used;
+    }
+    return(used);
+}
+
+
+static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    count = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data);
+	    *p++ = data;
+	    *p++ = data;
+	    count--;
+	}
+	*frameUsed += used*2;
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>2;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_int *)userPtr)++);
+	    data = le2be16dbl(data);
+	    *p++ = data;
+	    count--;
+	}
+	*frameUsed += used;
+    }
+    return(used);
+}
+
+
+static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    count = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data) ^ 0x8000;
+	    *p++ = data;
+	    *p++ = data;
+	}
+	*frameUsed += used*2;
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	count = min(userCount, frameLeft)>>2;
+	used = count;
+	while (count > 0) {
+	    get_user(data, ((u_int *)userPtr)++);
+	    data = le2be16dbl(data) ^ 0x80008000;
+	    *p++ = data;
+	    count--;
+	}
+	*frameUsed += used;
+    }
+    return(used);
+}
+
+
+static long ata_ctx_law(const u_char *userPtr, unsigned long userCount,
+			u_char frame[], long *frameUsed, long frameLeft)
+{
+    char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_char *p = &frame[*frameUsed];
+	while (frameLeft) {
+	    u_char c;
+	    if (bal < 0) {
+		if (!userCount)
+		    break;
+		get_user(c, userPtr++);
+		data = table[c];
+		userCount--;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft--;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 2) {
+	    u_char c;
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(c, userPtr++);
+		data = table[c] << 8;
+		get_user(c, userPtr++);
+		data |= table[c];
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 2;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_char *p = &frame[*frameUsed];
+	while (frameLeft) {
+	    if (bal < 0) {
+		if (!userCount)
+		    break;
+		get_user(data, userPtr++);
+		userCount--;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft--;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 2) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 2;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_char *p = &frame[*frameUsed];
+	while (frameLeft) {
+	    if (bal < 0) {
+		if (!userCount)
+		    break;
+		get_user(data, userPtr++);
+		data ^= 0x80;
+		userCount--;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft--;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 2) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		data ^= 0x8080;
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 2;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 4)
+		    break;
+		get_user(data, ((u_int *)userPtr)++);
+		userCount -= 4;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		data ^= 0x8000;
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 4)
+		    break;
+		get_user(data, ((u_int *)userPtr)++);
+		data ^= 0x80008000;
+		userCount -= 4;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		data = le2be16(data);
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 4)
+		    break;
+		get_user(data, ((u_int *)userPtr)++);
+		data = le2be16dbl(data);
+		userCount -= 4;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+
+
+static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount,
+			  u_char frame[], long *frameUsed, long frameLeft)
+{
+    /* this should help gcc to stuff everything into registers */
+    u_long data = sound.data;
+    long bal = sound.bal;
+    long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+    long used, usedf;
+
+    used = userCount;
+    usedf = frameLeft;
+    if (!sound.soft.stereo) {
+	u_short *p = (u_short *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 2)
+		    break;
+		get_user(data, ((u_short *)userPtr)++);
+		data = le2be16(data) ^ 0x8000;
+		userCount -= 2;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    } else {
+	u_long *p = (u_long *)&frame[*frameUsed];
+	while (frameLeft >= 4) {
+	    if (bal < 0) {
+		if (userCount < 4)
+		    break;
+		get_user(data, ((u_int *)userPtr)++);
+		data = le2be16dbl(data) ^ 0x80008000;
+		userCount -= 4;
+		bal += hSpeed;
+	    }
+	    *p++ = data;
+	    frameLeft -= 4;
+	    bal -= sSpeed;
+	}
+    }
+    sound.bal = bal;
+    sound.data = data;
+    used -= userCount;
+    *frameUsed += usedf-frameLeft;
+    return(used);
+}
+#endif /* CONFIG_ATARI */
+
+
+#ifdef CONFIG_AMIGA
+static long ami_ct_law(const u_char *userPtr, unsigned long userCount,
+		       u_char frame[], long *frameUsed, long frameLeft)
+{
+    char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+    long count, used;
+
+    if (!sound.soft.stereo) {
+	u_char *p = &frame[*frameUsed];
+	count = min(userCount, frameLeft) & ~1;
+	used = count;
+	while (count > 0) {
+	    u_char data;
+	    get_user(data, userPtr++);
+	    *p++ = table[data];
+	    count--;
+	}
+    } else {
+	u_char *left = &frame[*frameUsed>>1];
+	u_char *right = left+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    u_char data;
+	    get_user(data, userPtr++);
+	    *left++ = table[data];
+	    get_user(data, userPtr++);
+	    *right++ = table[data];
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_s8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+
+    if (!sound.soft.stereo) {
+	void *p = &frame[*frameUsed];
+	count = min(userCount, frameLeft) & ~1;
+	used = count;
+	copy_from_user(p, userPtr, count);
+    } else {
+	u_char *left = &frame[*frameUsed>>1];
+	u_char *right = left+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(*left++, userPtr++);
+	    get_user(*right++, userPtr++);
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_u8(const u_char *userPtr, unsigned long userCount,
+		      u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+
+    if (!sound.soft.stereo) {
+	char *p = &frame[*frameUsed];
+	count = min(userCount, frameLeft) & ~1;
+	used = count;
+	while (count > 0) {
+	    u_char data;
+	    get_user(data, userPtr++);
+	    *p++ = data ^ 0x80;
+	    count--;
+	}
+    } else {
+	u_char *left = &frame[*frameUsed>>1];
+	u_char *right = left+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    u_char data;
+	    get_user(data, userPtr++);
+	    *left++ = data ^ 0x80;
+	    get_user(data, userPtr++);
+	    *right++ = data ^ 0x80;
+	    count--;
+        }
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_char *high = &frame[*frameUsed>>1];
+	u_char *low = high+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    } else {
+	u_char *lefth = &frame[*frameUsed>>2];
+	u_char *leftl = lefth+sq.block_size_quarter;
+	u_char *righth = lefth+sq.block_size_half;
+	u_char *rightl = righth+sq.block_size_quarter;
+	count = min(userCount, frameLeft)>>2 & ~1;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
+	    get_user(data, ((u_short *)userPtr)++);
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
+	    count--;
+       }
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_char *high = &frame[*frameUsed>>1];
+	u_char *low = high+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data ^= 0x8000;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    } else {
+	u_char *lefth = &frame[*frameUsed>>2];
+	u_char *leftl = lefth+sq.block_size_quarter;
+	u_char *righth = lefth+sq.block_size_half;
+	u_char *rightl = righth+sq.block_size_quarter;
+	count = min(userCount, frameLeft)>>2 & ~1;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data ^= 0x8000;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
+	    get_user(data, ((u_short *)userPtr)++);
+	    data ^= 0x8000;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_char *high = &frame[*frameUsed>>1];
+	u_char *low = high+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data);
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    } else {
+	u_char *lefth = &frame[*frameUsed>>2];
+	u_char *leftl = lefth+sq.block_size_quarter;
+	u_char *righth = lefth+sq.block_size_half;
+	u_char *rightl = righth+sq.block_size_quarter;
+	count = min(userCount, frameLeft)>>2 & ~1;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data);
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data);
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+
+
+static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount,
+			 u_char frame[], long *frameUsed, long frameLeft)
+{
+    long count, used;
+    u_long data;
+
+    if (!sound.soft.stereo) {
+	u_char *high = &frame[*frameUsed>>1];
+	u_char *low = high+sq.block_size_half;
+	count = min(userCount, frameLeft)>>1 & ~1;
+	used = count*2;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data) ^ 0x8000;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    } else {
+	u_char *lefth = &frame[*frameUsed>>2];
+	u_char *leftl = lefth+sq.block_size_quarter;
+	u_char *righth = lefth+sq.block_size_half;
+	u_char *rightl = righth+sq.block_size_quarter;
+	count = min(userCount, frameLeft)>>2 & ~1;
+	used = count*4;
+	while (count > 0) {
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data) ^ 0x8000;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
+	    get_user(data, ((u_short *)userPtr)++);
+	    data = le2be16(data) ^ 0x8000;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
+	    count--;
+	}
+    }
+    *frameUsed += used;
+    return(used);
+}
+#endif /* CONFIG_AMIGA */
+
+
+#ifdef CONFIG_ATARI
+static TRANS transTTNormal = {
+    ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transTTExpanding = {
+    ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL
+};
+
+static TRANS transFalconNormal = {
+    ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, ata_ct_s16be, ata_ct_u16be,
+    ata_ct_s16le, ata_ct_u16le
+};
+
+static TRANS transFalconExpanding = {
+    ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, ata_ctx_s16be,
+    ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le
+};
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+static TRANS transAmiga = {
+    ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, ami_ct_s16be, ami_ct_u16be,
+    ami_ct_s16le, ami_ct_u16le
+};
+#endif /* CONFIG_AMIGA */
+
+
+/*** Low level stuff *********************************************************/
+
+
+#ifdef CONFIG_ATARI
+
+/*
+ * Atari (TT/Falcon)
+ */
+
+static void *AtaAlloc(unsigned int size, int flags)
+{
+    int order;
+    unsigned int a_size;
+    order = 0;
+    a_size = PAGE_SIZE;
+    while (a_size < size) {
+	order++;
+	a_size <<= 1;
+    }
+    return (void *) __get_dma_pages(flags, order);
+}
+
+static void AtaFree(void *obj, unsigned int size)
+{
+    int order;
+    unsigned int a_size;
+    order = 0;
+    a_size = PAGE_SIZE;
+    while (a_size < size) {
+	order++;
+	a_size <<= 1;
+    }
+    free_pages ((unsigned long) obj, order);
+}
+
+static int AtaIrqInit(void)
+{
+    /* Set up timer A. Timer A
+    will receive a signal upon end of playing from the sound
+    hardware. Furthermore Timer A is able to count events
+    and will cause an interrupt after a programmed number
+    of events. So all we need to keep the music playing is
+    to provide the sound hardware with new data upon
+    an interrupt from timer A. */
+    mfp.tim_ct_a = 0;		/* ++roman: Stop timer before programming! */
+    mfp.tim_dt_a = 1;		/* Cause interrupt after first event. */
+    mfp.tim_ct_a = 8;		/* Turn on event counting. */
+    /* Register interrupt handler. */
+    request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW,
+                "DMA sound", ata_sq_interrupt);
+    mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
+    mfp.int_mk_a |= 0x20;
+    return(1);
+}
+
+#ifdef MODULE
+static void AtaIrqCleanUp(void)
+{
+    mfp.tim_ct_a = 0;		/* stop timer */
+    mfp.int_en_a &= ~0x20;	/* turn interrupt off */
+    free_irq(IRQ_MFP_TIMA, ata_sq_interrupt);
+}
+#endif /* MODULE */
+
+
+#define TONE_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25)
+#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50)
+
+
+static int AtaSetBass(int bass)
+{
+    sound.bass = TONE_VOXWARE_TO_DB(bass);
+    atari_microwire_cmd(MW_LM1992_BASS(sound.bass));
+    return(TONE_DB_TO_VOXWARE(sound.bass));
+}
+
+
+static int AtaSetTreble(int treble)
+{
+    sound.treble = TONE_VOXWARE_TO_DB(treble);
+    atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble));
+    return(TONE_DB_TO_VOXWARE(sound.treble));
+}
+
+
+
+/*
+ * TT
+ */
+
+
+static void TTSilence(void)
+{
+    tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+    atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
+}
+
+
+static void TTInit(void)
+{
+    int mode, i, idx;
+    const int freq[4] = {50066, 25033, 12517, 6258};
+
+    /* search a frequency that fits into the allowed error range */
+
+    idx = -1;
+    for (i = 0; i < arraysize(freq); i++)
+	/* this isn't as much useful for a TT than for a Falcon, but
+	 * then it doesn't hurt very much to implement it for a TT too.
+	 */
+	if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+	    idx = i;
+    if (idx > -1) {
+	sound.soft.speed = freq[idx];
+	sound.trans = &transTTNormal;
+    } else
+	sound.trans = &transTTExpanding;
+
+    TTSilence();
+    sound.hard = sound.soft;
+
+    if (sound.hard.speed > 50066) {
+	/* we would need to squeeze the sound, but we won't do that */
+	sound.hard.speed = 50066;
+	mode = DMASND_MODE_50KHZ;
+	sound.trans = &transTTNormal;
+    } else if (sound.hard.speed > 25033) {
+	sound.hard.speed = 50066;
+	mode = DMASND_MODE_50KHZ;
+    } else if (sound.hard.speed > 12517) {
+	sound.hard.speed = 25033;
+	mode = DMASND_MODE_25KHZ;
+    } else if (sound.hard.speed > 6258) {
+	sound.hard.speed = 12517;
+	mode = DMASND_MODE_12KHZ;
+    } else {
+	sound.hard.speed = 6258;
+	mode = DMASND_MODE_6KHZ;
+    }
+
+    tt_dmasnd.mode = (sound.hard.stereo ?
+		      DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		     DMASND_MODE_8BIT | mode;
+
+    sound.bal = -sound.soft.speed;
+}
+
+
+static int TTSetFormat(int format)
+{
+    /* TT sound DMA supports only 8bit modes */
+
+    switch (format) {
+	case AFMT_QUERY:
+	    return(sound.soft.format);
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_S8:
+	case AFMT_U8:
+	    break;
+	default:
+	    format = AFMT_S8;
+    }
+
+    sound.soft.format = format;
+    sound.soft.size = 8;
+    if (sound.minDev == SND_DEV_DSP) {
+	sound.dsp.format = format;
+	sound.dsp.size = 8;
+    }
+    TTInit();
+
+    return(format);
+}
+
+
+#define VOLUME_VOXWARE_TO_DB(v) \
+	(((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40)
+#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2)
+
+
+static int TTSetVolume(int volume)
+{
+    sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
+    atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left));
+    sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
+    atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right));
+    return(VOLUME_DB_TO_VOXWARE(sound.volume_left) |
+	   (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8));
+}
+
+
+
+/*
+ * Falcon
+ */
+
+
+static void FalconSilence(void)
+{
+    /* stop playback, set sample rate 50kHz for PSG sound */
+    tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+    tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
+    tt_dmasnd.int_div = 0; /* STE compatible divider */
+    tt_dmasnd.int_ctrl = 0x0;
+    tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
+    tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
+    tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
+    tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
+}
+
+
+static void FalconInit(void)
+{
+    int divider, i, idx;
+    const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
+
+    /* search a frequency that fits into the allowed error range */
+
+    idx = -1;
+    for (i = 0; i < arraysize(freq); i++)
+	/* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
+	 * be playable without expanding, but that now a kernel runtime
+	 * option
+	 */
+	if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+	    idx = i;
+    if (idx > -1) {
+	sound.soft.speed = freq[idx];
+	sound.trans = &transFalconNormal;
+    } else
+	sound.trans = &transFalconExpanding;
+
+    FalconSilence();
+    sound.hard = sound.soft;
+
+    if (sound.hard.size == 16) {
+	/* the Falcon can play 16bit samples only in stereo */
+	sound.hard.stereo = 1;
+    }
+
+    if (sound.hard.speed > 49170) {
+	/* we would need to squeeze the sound, but we won't do that */
+	sound.hard.speed = 49170;
+	divider = 1;
+	sound.trans = &transFalconNormal;
+    } else if (sound.hard.speed > 32780) {
+	sound.hard.speed = 49170;
+	divider = 1;
+    } else if (sound.hard.speed > 24585) {
+	sound.hard.speed = 32780;
+	divider = 2;
+    } else if (sound.hard.speed > 19668) {
+	sound.hard.speed = 24585;
+	divider = 3;
+    } else if (sound.hard.speed > 16390) {
+	sound.hard.speed = 19668;
+	divider = 4;
+    } else if (sound.hard.speed > 12292) {
+	sound.hard.speed = 16390;
+	divider = 5;
+    } else if (sound.hard.speed > 9834) {
+	sound.hard.speed = 12292;
+	divider = 7;
+    } else if (sound.hard.speed > 8195) {
+	sound.hard.speed = 9834;
+	divider = 9;
+    } else {
+	sound.hard.speed = 8195;
+	divider = 11;
+    }
+    tt_dmasnd.int_div = divider;
+
+    /* Setup Falcon sound DMA for playback */
+    tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
+    tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
+    tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
+    tt_dmasnd.cbar_dst = 0x0000;
+    tt_dmasnd.rec_track_select = 0;
+    tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
+    tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
+
+    tt_dmasnd.mode = (sound.hard.stereo ?
+		      DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+		     ((sound.hard.size == 8) ?
+		       DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
+		     DMASND_MODE_6KHZ;
+
+    sound.bal = -sound.soft.speed;
+}
+
+
+static int FalconSetFormat(int format)
+{
+    int size;
+    /* Falcon sound DMA supports 8bit and 16bit modes */
+
+    switch (format) {
+	case AFMT_QUERY:
+	    return(sound.soft.format);
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+	    size = 8;
+	    break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+	    size = 16;
+	    break;
+	default: /* :-) */
+	    size = 8;
+	    format = AFMT_S8;
+    }
+
+    sound.soft.format = format;
+    sound.soft.size = size;
+    if (sound.minDev == SND_DEV_DSP) {
+	sound.dsp.format = format;
+	sound.dsp.size = sound.soft.size;
+    }
+
+    FalconInit();
+
+    return(format);
+}
+
+
+/* This is for the Falcon output *attenuation* in 1.5dB steps,
+ * i.e. output level from 0 to -22.5dB in -1.5dB steps.
+ */
+#define VOLUME_VOXWARE_TO_ATT(v) \
+	((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20)
+#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3)
+
+
+static int FalconSetVolume(int volume)
+{
+    sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
+    sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
+    tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4;
+    return(VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
+	   VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8);
+}
+
+
+static void ata_sq_play_next_frame(int index)
+{
+    char *start, *end;
+
+    /* used by AtaPlay() if all doubts whether there really is something
+     * to be played are already wiped out.
+     */
+    start = sq_block_address(sq.front);
+    end = start+((sq.count == index) ? sq.rear_size : sq.block_size);
+    /* end might not be a legal virtual address. */
+    DMASNDSetEnd(VTOP(end - 1) + 1);
+    DMASNDSetBase(VTOP(start));
+	/* Since only an even number of samples per frame can
+	be played, we might lose one byte here. (TO DO) */
+    sq.front = (sq.front+1) % sq.max_count;
+    sq.playing++;
+    tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
+}
+
+
+static void AtaPlay(void)
+{
+    /* ++TeSche: Note that sq.playing is no longer just a flag but holds
+     * the number of frames the DMA is currently programmed for instead,
+     * may be 0, 1 (currently being played) or 2 (pre-programmed).
+     *
+     * Changes done to sq.count and sq.playing are a bit more subtle again
+     * so now I must admit I also prefer disabling the irq here rather
+     * than considering all possible situations. But the point is that
+     * disabling the irq doesn't have any bad influence on this version of
+     * the driver as we benefit from having pre-programmed the DMA
+     * wherever possible: There's no need to reload the DMA at the exact
+     * time of an interrupt but only at some time while the pre-programmed
+     * frame is playing!
+     */
+    atari_disable_irq(IRQ_MFP_TIMA);
+
+    if (sq.playing == 2 ||	/* DMA is 'full' */
+	sq.count <= 0) {	/* nothing to do */
+	atari_enable_irq(IRQ_MFP_TIMA);
+	return;
+    }
+
+    if (sq.playing == 0) {
+	/* looks like there's nothing 'in' the DMA yet, so try
+	 * to put two frames into it (at least one is available).
+	 */
+	if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing) {
+	    /* hmmm, the only existing frame is not
+	     * yet filled and we're not syncing?
+	     */
+	    atari_enable_irq(IRQ_MFP_TIMA);
+	    return;
+	}
+	ata_sq_play_next_frame(1);
+	if (sq.count == 1) {
+	    /* no more frames */
+	    atari_enable_irq(IRQ_MFP_TIMA);
+	    return;
+	}
+	if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) {
+	    /* hmmm, there were two frames, but the second
+	     * one is not yet filled and we're not syncing?
+	     */
+	    atari_enable_irq(IRQ_MFP_TIMA);
+	    return;
+	}
+	ata_sq_play_next_frame(2);
+    } else {
+	/* there's already a frame being played so we may only stuff
+	 * one new into the DMA, but even if this may be the last
+	 * frame existing the previous one is still on sq.count.
+	 */
+	if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) {
+	    /* hmmm, the only existing frame is not
+	     * yet filled and we're not syncing?
+	     */
+	    atari_enable_irq(IRQ_MFP_TIMA);
+	    return;
+	}
+	ata_sq_play_next_frame(2);
+    }
+    atari_enable_irq(IRQ_MFP_TIMA);
+}
+
+
+static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+#if 0
+    /* ++TeSche: if you should want to test this... */
+    static int cnt = 0;
+    if (sq.playing == 2)
+	if (++cnt == 10) {
+	    /* simulate losing an interrupt */
+	    cnt = 0;
+	    return;
+	}
+#endif
+
+    if (sq.ignore_int && (sound.mach.type == DMASND_FALCON)) {
+	/* ++TeSche: Falcon only: ignore first irq because it comes
+	 * immediately after starting a frame. after that, irqs come
+	 * (almost) like on the TT.
+	 */
+	sq.ignore_int = 0;
+	return;
+    }
+
+    if (!sq.playing) {
+	/* playing was interrupted and sq_reset() has already cleared
+	 * the sq variables, so better don't do anything here.
+	 */
+	WAKE_UP(sq.sync_queue);
+	return;
+    }
+
+    /* Probably ;) one frame is finished. Well, in fact it may be that a
+     * pre-programmed one is also finished because there has been a long
+     * delay in interrupt delivery and we've completely lost one, but
+     * there's no way to detect such a situation. In such a case the last
+     * frame will be played more than once and the situation will recover
+     * as soon as the irq gets through.
+     */
+    sq.count--;
+    sq.playing--;
+
+    if (!sq.playing) {
+	tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+	sq.ignore_int = 1;
+    }
+
+    WAKE_UP(sq.write_queue);
+	/* At least one block of the queue is free now
+	so wake up a writing process blocked because
+	of a full queue. */
+
+    if ((sq.playing != 1) || (sq.count != 1))
+	/* We must be a bit carefully here: sq.count indicates the
+	 * number of buffers used and not the number of frames to
+	 * be played. If sq.count==1 and sq.playing==1 that means
+	 * the only remaining frame was already programmed earlier
+	 * (and is currently running) so we mustn't call AtaPlay()
+	 * here, otherwise we'll play one frame too much.
+	 */
+	AtaPlay();
+
+    if (!sq.playing) WAKE_UP(sq.sync_queue);
+	/* We are not playing after AtaPlay(), so there
+	is nothing to play any more. Wake up a process
+	waiting for audio output to drain. */
+}
+#endif /* CONFIG_ATARI */
+
+
+#ifdef CONFIG_AMIGA
+
+/*
+ * Amiga
+ */
+
+
+static void *AmiAlloc(unsigned int size, int flags)
+{
+    return(amiga_chip_alloc((long)size));
+}
+
+static void AmiFree(void *obj, unsigned int size)
+{
+    amiga_chip_free (obj);
+}
+
+static int AmiIrqInit(void)
+{
+    /* turn off DMA for audio channels */
+    custom.dmacon = AMI_AUDIO_OFF;
+
+    /* Register interrupt handler. */
+    if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0,
+                    "DMA sound", ami_sq_interrupt))
+	return(0);
+    return(1);
+}
+
+#ifdef MODULE
+static void AmiIrqCleanUp(void)
+{
+    /* turn off DMA for audio channels */
+    custom.dmacon = AMI_AUDIO_OFF;
+    /* release the interrupt */
+    free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt);
+}
+#endif /* MODULE */
+
+static void AmiSilence(void)
+{
+    /* turn off DMA for audio channels */
+    custom.dmacon = AMI_AUDIO_OFF;
+}
+
+
+static void AmiInit(void)
+{
+    int period, i;
+
+    AmiSilence();
+
+    if (sound.soft.speed)
+	period = amiga_colorclock/sound.soft.speed-1;
+    else
+	period = amiga_audio_min_period;
+    sound.hard = sound.soft;
+    sound.trans = &transAmiga;
+
+    if (period < amiga_audio_min_period) {
+	/* we would need to squeeze the sound, but we won't do that */
+	period = amiga_audio_min_period;
+    } else if (period > 65535) {
+	period = 65535;
+    }
+    sound.hard.speed = amiga_colorclock/(period+1);
+
+    for (i = 0; i < 4; i++)
+	custom.aud[i].audper = period;
+    amiga_audio_period = period;
+
+    AmiSetTreble(50);  /* recommended for newer amiga models */
+}
+
+
+static int AmiSetFormat(int format)
+{
+    int size;
+
+    /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
+
+    switch (format) {
+	case AFMT_QUERY:
+	    return(sound.soft.format);
+	case AFMT_MU_LAW:
+	case AFMT_A_LAW:
+	case AFMT_U8:
+	case AFMT_S8:
+	    size = 8;
+	    break;
+	case AFMT_S16_BE:
+	case AFMT_U16_BE:
+	case AFMT_S16_LE:
+	case AFMT_U16_LE:
+	    size = 16;
+	    break;
+	default: /* :-) */
+	    size = 8;
+	    format = AFMT_S8;
+    }
+
+    sound.soft.format = format;
+    sound.soft.size = size;
+    if (sound.minDev == SND_DEV_DSP) {
+	sound.dsp.format = format;
+	sound.dsp.size = sound.soft.size;
+    }
+    AmiInit();
+
+    return(format);
+}
+
+
+#define VOLUME_VOXWARE_TO_AMI(v) \
+	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
+#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
+
+static int AmiSetVolume(int volume)
+{
+    sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
+    custom.aud[0].audvol = sound.volume_left;
+    sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
+    custom.aud[1].audvol = sound.volume_right;
+    return(VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
+	   (VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
+}
+
+static int AmiSetTreble(int treble)
+{
+    sound.treble = treble;
+    if (treble < 50)
+	ciaa.pra &= ~0x02;
+    else
+	ciaa.pra |= 0x02;
+    return(treble);
+}
+
+
+#define AMI_PLAY_LOADED		1
+#define AMI_PLAY_PLAYING	2
+#define AMI_PLAY_MASK		3
+
+
+static void ami_sq_play_next_frame(int index)
+{
+    u_char *start, *ch0, *ch1, *ch2, *ch3;
+    u_long size;
+
+    /* used by AmiPlay() if all doubts whether there really is something
+     * to be played are already wiped out.
+     */
+    start = sq_block_address(sq.front);
+    size = (sq.count == index ? sq.rear_size : sq.block_size)>>1;
+
+    if (sound.hard.stereo) {
+	ch0 = start;
+	ch1 = start+sq.block_size_half;
+	size >>= 1;
+    } else {
+	ch0 = start;
+	ch1 = start;
+    }
+    if (sound.hard.size == 8) {
+	custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+	custom.aud[0].audlen = size;
+	custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+	custom.aud[1].audlen = size;
+	custom.dmacon = AMI_AUDIO_8;
+    } else {
+	size >>= 1;
+	custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
+	custom.aud[0].audlen = size;
+	custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
+	custom.aud[1].audlen = size;
+	if (sound.volume_left == 64 && sound.volume_right == 64) {
+	    /* We can play pseudo 14-bit only with the maximum volume */
+	    ch3 = ch0+sq.block_size_quarter;
+	    ch2 = ch1+sq.block_size_quarter;
+	    custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
+	    custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
+	    custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
+	    custom.aud[2].audlen = size;
+	    custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
+	    custom.aud[3].audlen = size;
+	    custom.dmacon = AMI_AUDIO_14;
+	} else
+	    custom.dmacon = AMI_AUDIO_8;
+    }
+    sq.front = (sq.front+1) % sq.max_count;
+    sq.playing |= AMI_PLAY_LOADED;
+}
+
+
+static void AmiPlay(void)
+{
+    int minframes = 1;
+
+    custom.intena = IF_AUD0;
+
+    if (sq.playing & AMI_PLAY_LOADED) {
+	/* There's already a frame loaded */
+	custom.intena = IF_SETCLR | IF_AUD0;
+	return;
+    }
+
+    if (sq.playing & AMI_PLAY_PLAYING)
+	/* Increase threshold: frame 1 is already being played */
+	minframes = 2;
+
+    if (sq.count < minframes) {
+	/* Nothing to do */
+	custom.intena = IF_SETCLR | IF_AUD0;
+	return;
+    }
+
+    if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing) {
+	/* hmmm, the only existing frame is not
+	 * yet filled and we're not syncing?
+	 */
+	custom.intena = IF_SETCLR | IF_AUD0;
+	return;
+    }
+
+    ami_sq_play_next_frame(minframes);
+
+    custom.intena = IF_SETCLR | IF_AUD0;
+}
+
+
+static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+    int minframes = 1;
+
+    if (!sq.playing) {
+	/* Playing was interrupted and sq_reset() has already cleared
+	 * the sq variables, so better don't do anything here.
+	 */
+	WAKE_UP(sq.sync_queue);
+	return;
+    }
+
+    if (sq.playing & AMI_PLAY_PLAYING) {
+	/* We've just finished a frame */
+	sq.count--;
+	WAKE_UP(sq.write_queue);
+    }
+
+    if (sq.playing & AMI_PLAY_LOADED)
+	/* Increase threshold: frame 1 is already being played */
+	minframes = 2;
+
+    /* Shift the flags */
+    sq.playing = (sq.playing<<1) & AMI_PLAY_MASK;
+
+    if (!sq.playing)
+	/* No frame is playing, disable audio DMA */
+	custom.dmacon = AMI_AUDIO_OFF;
+
+    if (sq.count >= minframes)
+    	/* Try to play the next frame */
+	AmiPlay();
+
+    if (!sq.playing)
+	/* Nothing to play anymore.
+	   Wake up a process waiting for audio output to drain. */
+	WAKE_UP(sq.sync_queue);
+}
+#endif /* CONFIG_AMIGA */
+
+
+/*** Machine definitions *****************************************************/
+
+
+#ifdef CONFIG_ATARI
+static MACHINE machTT = {
+    DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit,
+#ifdef MODULE
+    AtaIrqCleanUp,
+#endif /* MODULE */
+    TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble,
+    AtaPlay
+};
+
+static MACHINE machFalcon = {
+    DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit,
+#ifdef MODULE
+    AtaIrqCleanUp,
+#endif /* MODULE */
+    FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass,
+    AtaSetTreble, AtaPlay
+};
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+static MACHINE machAmiga = {
+    DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit,
+#ifdef MODULE
+    AmiIrqCleanUp,
+#endif /* MODULE */
+    AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble,
+    AmiPlay
+};
+#endif /* CONFIG_AMIGA */
+
+
+/*** Mid level stuff *********************************************************/
+
+
+static void sound_silence(void)
+{
+    /* update hardware settings one more */
+    (*sound.mach.init)();
+
+    (*sound.mach.silence)();
+}
+
+
+static void sound_init(void)
+{
+    (*sound.mach.init)();
+}
+
+
+static int sound_set_format(int format)
+{
+    return(*sound.mach.setFormat)(format);
+}
+
+
+static int sound_set_speed(int speed)
+{
+    if (speed < 0)
+	return(sound.soft.speed);
+
+    sound.soft.speed = speed;
+    (*sound.mach.init)();
+    if (sound.minDev == SND_DEV_DSP)
+	sound.dsp.speed = sound.soft.speed;
+
+    return(sound.soft.speed);
+}
+
+
+static int sound_set_stereo(int stereo)
+{
+    if (stereo < 0)
+	return(sound.soft.stereo);
+
+    stereo = !!stereo;    /* should be 0 or 1 now */
+
+    sound.soft.stereo = stereo;
+    if (sound.minDev == SND_DEV_DSP)
+	sound.dsp.stereo = stereo;
+    (*sound.mach.init)();
+
+    return(stereo);
+}
+
+
+static int sound_set_volume(int volume)
+{
+    return(*sound.mach.setVolume)(volume);
+}
+
+
+#ifdef CONFIG_ATARI
+static int sound_set_bass(int bass)
+{
+    return(sound.mach.setBass ? (*sound.mach.setBass)(bass) : 50);
+}
+#endif /* CONFIG_ATARI */
+
+
+static int sound_set_treble(int treble)
+{
+    return(sound.mach.setTreble ? (*sound.mach.setTreble)(treble) : 50);
+}
+
+
+static long sound_copy_translate(const u_char *userPtr,
+				 unsigned long userCount,
+				 u_char frame[], long *frameUsed,
+				 long frameLeft)
+{
+    long (*ct_func)(const u_char *, unsigned long, u_char *, long *, long) = NULL;
+
+    switch (sound.soft.format) {
+	case AFMT_MU_LAW:
+	    ct_func = sound.trans->ct_ulaw;
+	    break;
+	case AFMT_A_LAW:
+	    ct_func = sound.trans->ct_alaw;
+	    break;
+	case AFMT_S8:
+	    ct_func = sound.trans->ct_s8;
+	    break;
+	case AFMT_U8:
+	    ct_func = sound.trans->ct_u8;
+	    break;
+	case AFMT_S16_BE:
+	    ct_func = sound.trans->ct_s16be;
+	    break;
+	case AFMT_U16_BE:
+	    ct_func = sound.trans->ct_u16be;
+	    break;
+	case AFMT_S16_LE:
+	    ct_func = sound.trans->ct_s16le;
+	    break;
+	case AFMT_U16_LE:
+	    ct_func = sound.trans->ct_u16le;
+	    break;
+    }
+    if (ct_func)
+	return(ct_func(userPtr, userCount, frame, frameUsed, frameLeft));
+    else
+	return(0);
+}
+
+
+/*
+ * /dev/mixer abstraction
+ */
+
+
+#define RECLEVEL_VOXWARE_TO_GAIN(v) \
+	((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20)
+#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3)
+
+
+static void mixer_init(void)
+{
+    mixer.busy = 0;
+    sound.treble = 0;
+    sound.bass = 0;
+    switch (sound.mach.type) {
+#ifdef CONFIG_ATARI
+	case DMASND_TT:
+	    atari_microwire_cmd(MW_LM1992_VOLUME(0));
+	    sound.volume_left = 0;
+	    atari_microwire_cmd(MW_LM1992_BALLEFT(0));
+	    sound.volume_right = 0;
+	    atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
+	    atari_microwire_cmd(MW_LM1992_TREBLE(0));
+	    atari_microwire_cmd(MW_LM1992_BASS(0));
+	    break;
+	case DMASND_FALCON:
+	    sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
+	    sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
+	    break;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+	case DMASND_AMIGA:
+	    sound.volume_left = 64;
+	    sound.volume_right = 64;
+	    custom.aud[0].audvol = sound.volume_left;
+	    custom.aud[3].audvol = 1;			/* For pseudo 14bit */
+	    custom.aud[1].audvol = sound.volume_right;
+	    custom.aud[2].audvol = 1;			/* For pseudo 14bit */
+	    sound.treble = 50;
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+}
+
+
+static int mixer_open(int open_mode)
+{
+    if (mixer.busy)
+	return(-EBUSY);
+    mixer.busy = 1;
+    return(0);
+}
+
+
+static int mixer_release(void)
+{
+    mixer.busy = 0;
+    return(0);
+}
+
+
+static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg)
+{
+    int data;
+    switch (sound.mach.type) {
+#ifdef CONFIG_ATARI
+	case DMASND_FALCON:
+	    switch (cmd) {
+		case SOUND_MIXER_READ_DEVMASK:
+		    return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
+		case SOUND_MIXER_READ_RECMASK:
+		    return(IOCTL_OUT(arg, SOUND_MASK_MIC));
+		case SOUND_MIXER_READ_STEREODEVS:
+		    return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC));
+		case SOUND_MIXER_READ_CAPS:
+		    return(IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT));
+		case SOUND_MIXER_READ_VOLUME:
+		    return(IOCTL_OUT(arg,
+			VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
+			VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8));
+		case SOUND_MIXER_WRITE_MIC:
+		    IOCTL_IN(arg, data);
+		    tt_dmasnd.input_gain =
+			RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
+			RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
+		    /* fall thru, return set value */
+		case SOUND_MIXER_READ_MIC:
+		    return(IOCTL_OUT(arg,
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
+			RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8));
+		case SOUND_MIXER_READ_SPEAKER:
+		    {
+			int porta;
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			porta = sound_ym.rd_data_reg_sel;
+			sti();
+			return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+		    }
+		case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_volume(data)));
+		case SOUND_MIXER_WRITE_SPEAKER:
+		    {
+			int porta;
+			IOCTL_IN(arg, data);
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				(data < 50 ? 0x40 : 0);
+			sound_ym.wd_data = porta;
+			sti();
+			return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+		    }
+	    }
+	    break;
+
+	case DMASND_TT:
+	    switch (cmd) {
+		case SOUND_MIXER_READ_DEVMASK:
+		    return(IOCTL_OUT(arg,
+			SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
+			((atari_mch_cookie >> 16) == ATARI_MCH_TT ?
+			    SOUND_MASK_SPEAKER : 0)));
+		case SOUND_MIXER_READ_RECMASK:
+		    return(IOCTL_OUT(arg, 0));
+		case SOUND_MIXER_READ_STEREODEVS:
+		    return(IOCTL_OUT(arg, SOUND_MASK_VOLUME));
+		case SOUND_MIXER_READ_VOLUME:
+		    return(IOCTL_OUT(arg,
+			VOLUME_DB_TO_VOXWARE(sound.volume_left) |
+			(VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)));
+		case SOUND_MIXER_READ_BASS:
+		    return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass)));
+		case SOUND_MIXER_READ_TREBLE:
+		    return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble)));
+		case SOUND_MIXER_READ_SPEAKER:
+		    {
+			int porta;
+			if ((atari_mch_cookie >> 16) == ATARI_MCH_TT) {
+			    cli();
+			    sound_ym.rd_data_reg_sel = 14;
+			    porta = sound_ym.rd_data_reg_sel;
+			    sti();
+			    return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+			} else
+			    return(-EINVAL);
+		    }
+		case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_volume(data)));
+		case SOUND_MIXER_WRITE_BASS:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_bass(data)));
+		case SOUND_MIXER_WRITE_TREBLE:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_treble(data)));
+		case SOUND_MIXER_WRITE_SPEAKER:
+		    if ((atari_mch_cookie >> 16) == ATARI_MCH_TT) {
+			int porta;
+			IOCTL_IN(arg, data);
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+				(data < 50 ? 0x40 : 0);
+			sound_ym.wd_data = porta;
+			sti();
+			return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+		    } else
+			return(-EINVAL);
+	    }
+	    break;
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+	case DMASND_AMIGA:
+	    switch (cmd) {
+		case SOUND_MIXER_READ_DEVMASK:
+		    return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE));
+		case SOUND_MIXER_READ_RECMASK:
+		    return(IOCTL_OUT(arg, 0));
+		case SOUND_MIXER_READ_STEREODEVS:
+		    return(IOCTL_OUT(arg, SOUND_MASK_VOLUME));
+		case SOUND_MIXER_READ_VOLUME:
+		    return(IOCTL_OUT(arg,
+			VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
+			VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
+		case SOUND_MIXER_WRITE_VOLUME:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_volume(data)));
+		case SOUND_MIXER_READ_TREBLE:
+		    return(IOCTL_OUT(arg, sound.treble));
+		case SOUND_MIXER_WRITE_TREBLE:
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_treble(data)));
+	    }
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+
+    return(-EINVAL);
+}
+
+
+
+/*
+ * Sound queue stuff, the heart of the driver
+ */
+
+
+static void sq_init(int numBufs, int bufSize, char **buffers)
+{
+    sq.max_count = numBufs;
+    sq.block_size = bufSize;
+    sq.buffers = buffers;
+
+    sq.front = sq.count = 0;
+    sq.rear = -1;
+    sq.write_queue = sq.open_queue = sq.sync_queue = 0;
+    sq.busy = 0;
+    sq.syncing = 0;
+
+    sq.playing = 0;
+
+#ifdef CONFIG_ATARI
+    sq.ignore_int = 0;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+    sq.block_size_half = sq.block_size>>1;
+    sq.block_size_quarter = sq.block_size_half>>1;
+#endif /* CONFIG_AMIGA */
+
+    sound_silence();
+
+    /* whatever you like as startup mode for /dev/dsp,
+     * (/dev/audio hasn't got a startup mode). note that
+     * once changed a new open() will *not* restore these!
+     */
+    sound.dsp.format = AFMT_S8;
+    sound.dsp.stereo = 0;
+    sound.dsp.size = 8;
+
+    /* set minimum rate possible without expanding */
+    switch (sound.mach.type) {
+#ifdef CONFIG_ATARI
+	case DMASND_TT:
+	    sound.dsp.speed = 6258;
+	    break;
+	case DMASND_FALCON:
+	    sound.dsp.speed = 8195;
+	    break;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+	case DMASND_AMIGA:
+	    sound.dsp.speed = 8000;
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+
+    /* before the first open to /dev/dsp this wouldn't be set */
+    sound.soft = sound.dsp;
+    sound.hard = sound.dsp;
+}
+
+
+static void sq_play(void)
+{
+    (*sound.mach.play)();
+}
+
+
+/* ++TeSche: radically changed this one too */
+
+static long sq_write(const char *src, unsigned long uLeft)
+{
+    long uWritten = 0;
+    u_char *dest;
+    long uUsed, bUsed, bLeft;
+
+    /* ++TeSche: Is something like this necessary?
+     * Hey, that's an honest question! Or does any other part of the
+     * filesystem already checks this situation? I really don't know.
+     */
+    if (uLeft == 0)
+	return(0);
+
+    /* The interrupt doesn't start to play the last, incomplete frame.
+     * Thus we can append to it without disabling the interrupts! (Note
+     * also that sq.rear isn't affected by the interrupt.)
+     */
+
+    if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) {
+	dest = sq_block_address(sq.rear);
+	bUsed = sq.rear_size;
+	uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
+	src += uUsed;
+	uWritten += uUsed;
+	uLeft -= uUsed;
+	sq.rear_size = bUsed;
+    }
+
+    do {
+	while (sq.count == sq.max_count) {
+	    sq_play();
+	    if (NON_BLOCKING(sq.open_mode))
+		return(uWritten > 0 ? uWritten : -EAGAIN);
+	    SLEEP(sq.write_queue, ONE_SECOND);
+	    if (SIGNAL_RECEIVED)
+		return(uWritten > 0 ? uWritten : -EINTR);
+	}
+
+	/* Here, we can avoid disabling the interrupt by first
+	 * copying and translating the data, and then updating
+	 * the sq variables. Until this is done, the interrupt
+	 * won't see the new frame and we can work on it
+	 * undisturbed.
+	 */
+
+	dest = sq_block_address((sq.rear+1) % sq.max_count);
+	bUsed = 0;
+	bLeft = sq.block_size;
+	uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
+	src += uUsed;
+	uWritten += uUsed;
+	uLeft -= uUsed;
+	if (bUsed) {
+	    sq.rear = (sq.rear+1) % sq.max_count;
+	    sq.rear_size = bUsed;
+	    sq.count++;
+	}
+    } while (bUsed);   /* uUsed may have been 0 */
+
+    sq_play();
+
+    return(uWritten);
+}
+
+
+static int sq_open(int open_mode)
+{
+    if (sq.busy) {
+	if (NON_BLOCKING(open_mode))
+	    return(-EBUSY);
+	while (sq.busy) {
+	    SLEEP(sq.open_queue, ONE_SECOND);
+	    if (SIGNAL_RECEIVED)
+		return(-EINTR);
+	}
+    }
+    sq.open_mode = open_mode;
+    sq.busy = 1;
+#ifdef CONFIG_ATARI
+    sq.ignore_int = 1;
+#endif /* CONFIG_ATARI */
+    return(0);
+}
+
+
+static void sq_reset(void)
+{
+    sound_silence();
+    sq.playing = 0;
+    sq.count = 0;
+    sq.front = (sq.rear+1) % sq.max_count;
+}
+
+
+static int sq_sync(void)
+{
+    int rc = 0;
+
+    sq.syncing = 1;
+    sq_play();	/* there may be an incomplete frame waiting */
+
+    while (sq.playing) {
+	SLEEP(sq.sync_queue, ONE_SECOND);
+	if (SIGNAL_RECEIVED) {
+	    /* While waiting for audio output to drain, an interrupt occurred.
+	       Stop audio output immediately and clear the queue. */
+	    sq_reset();
+	    rc = -EINTR;
+	    break;
+	}
+    }
+
+    sq.syncing = 0;
+    return(rc);
+}
+
+
+static int sq_release(void)
+{
+    int rc = 0;
+    if (sq.busy) {
+	rc = sq_sync();
+	sq.busy = 0;
+	WAKE_UP(sq.open_queue);
+	/* Wake up a process waiting for the queue being released.
+	   Note: There may be several processes waiting for a call to open()
+		 returning. */
+    }
+    return(rc);
+}
+
+
+
+/*
+ * /dev/sndstat
+ */
+
+
+static void state_init(void)
+{
+    state.busy = 0;
+}
+
+
+/* state.buf should not overflow! */
+
+static int state_open(int open_mode)
+{
+    char *buffer = state.buf, *mach = "";
+    int len = 0;
+
+    if (state.busy)
+	return(-EBUSY);
+
+    state.ptr = 0;
+    state.busy = 1;
+
+    switch (sound.mach.type) {
+#ifdef CONFIG_ATARI
+	case DMASND_TT:
+	case DMASND_FALCON:
+	    mach = "Atari ";
+	    break;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+	case DMASND_AMIGA:
+	    mach = "Amiga ";
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+    len += sprintf(buffer+len, "%sDMA sound driver:\n", mach);
+
+    len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format);
+    switch (sound.soft.format) {
+	case AFMT_MU_LAW:
+	    len += sprintf(buffer+len, " (mu-law)");
+	    break;
+	case AFMT_A_LAW:
+	    len += sprintf(buffer+len, " (A-law)");
+	    break;
+	case AFMT_U8:
+	    len += sprintf(buffer+len, " (unsigned 8 bit)");
+	    break;
+	case AFMT_S8:
+	    len += sprintf(buffer+len, " (signed 8 bit)");
+	    break;
+	case AFMT_S16_BE:
+	    len += sprintf(buffer+len, " (signed 16 bit big)");
+	    break;
+	case AFMT_U16_BE:
+	    len += sprintf(buffer+len, " (unsigned 16 bit big)");
+	    break;
+	case AFMT_S16_LE:
+	    len += sprintf(buffer+len, " (signed 16 bit little)");
+	    break;
+	case AFMT_U16_LE:
+	    len += sprintf(buffer+len, " (unsigned 16 bit little)");
+	    break;
+    }
+    len += sprintf(buffer+len, "\n");
+    len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
+		   sound.soft.speed, sound.hard.speed);
+    len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
+		   sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono");
+    switch (sound.mach.type) {
+#ifdef CONFIG_ATARI
+	case DMASND_TT:
+	    len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
+			   sound.volume_left);
+	    len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
+			   sound.volume_right);
+	    len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
+			   sound.bass);
+	    len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
+			   sound.treble);
+	    break;
+	case DMASND_FALCON:
+	    len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+			   sound.volume_left);
+	    len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+			   sound.volume_right);
+	    break;
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+	case DMASND_AMIGA:
+	    len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
+			   sound.volume_left);
+	    len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
+			   sound.volume_right);
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+    len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d\n",
+		   sq.block_size, sq.max_count);
+    len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
+		   sq.rear_size);
+    len += sprintf(buffer+len, "\tsq.playing = %d sq.syncing = %d\n",
+		   sq.playing, sq.syncing);
+    state.len = len;
+    return(0);
+}
+
+
+static int state_release(void)
+{
+    state.busy = 0;
+    return(0);
+}
+
+
+static long state_read(char *dest, unsigned long count)
+{
+    int n = state.len-state.ptr;
+    if (n > count)
+	n = count;
+    if (n <= 0)
+	return(0);
+    copy_to_user(dest, &state.buf[state.ptr], n);
+    state.ptr += n;
+    return(n);
+}
+
+
+
+/*** High level stuff ********************************************************/
+
+
+static int sound_open(struct inode *inode, struct file *file)
+{
+    int dev = MINOR(inode->i_rdev) & 0x0f;
+    int rc = 0;
+
+    switch (dev) {
+	case SND_DEV_STATUS:
+	    rc = state_open(file->f_flags);
+	    break;
+	case SND_DEV_CTL:
+	    rc = mixer_open(file->f_flags);
+	    break;
+	case SND_DEV_DSP:
+	case SND_DEV_AUDIO:
+	    rc = sq_open(file->f_flags);
+	    if (rc == 0) {
+		sound.minDev = dev;
+		sound.soft = sound.dsp;
+		sound.hard = sound.dsp;
+		sound_init();
+		if (dev == SND_DEV_AUDIO) {
+		    sound_set_speed(8000);
+		    sound_set_stereo(0);
+		    sound_set_format(AFMT_MU_LAW);
+		}
+	    }
+	    break;
+	default:
+	    rc = -ENXIO;
+    }
+#ifdef MODULE
+    if (rc >= 0)
+	MOD_INC_USE_COUNT;
+#endif
+    return(rc);
+}
+
+
+static int sound_fsync(struct inode *inode, struct file *filp)
+{
+    int dev = MINOR(inode->i_rdev) & 0x0f;
+
+    switch (dev) {
+	case SND_DEV_STATUS:
+	case SND_DEV_CTL:
+	    return(0);
+	case SND_DEV_DSP:
+	case SND_DEV_AUDIO:
+	    return(sq_sync());
+	default:
+	    return(unknown_minor_dev("sound_fsync", dev));
+    }
+}
+
+
+static void sound_release(struct inode *inode, struct file *file)
+{
+    int dev = MINOR(inode->i_rdev);
+
+    switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	    state_release();
+	    break;
+	case SND_DEV_CTL:
+	    mixer_release();
+	    break;
+	case SND_DEV_DSP:
+	case SND_DEV_AUDIO:
+	    sq_release();
+	    sound.soft = sound.dsp;
+	    sound.hard = sound.dsp;
+	    sound_silence();
+	    break;
+	default:
+	    unknown_minor_dev("sound_release", dev);
+	    return;
+    }
+#ifdef MODULE
+    MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static long long sound_lseek(struct inode *inode, struct file *file,
+			     long long offset, int orig)
+{
+    return -ESPIPE;
+}
+
+
+static long sound_read(struct inode *inode, struct file *file, char *buf,
+		       unsigned long count)
+{
+    int dev = MINOR(inode->i_rdev);
+
+    switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	    return(state_read(buf, count));
+	case SND_DEV_CTL:
+	case SND_DEV_DSP:
+	case SND_DEV_AUDIO:
+	    return(-EPERM);
+	default:
+	    return(unknown_minor_dev("sound_read", dev));
+    }
+}
+
+
+static long sound_write(struct inode *inode, struct file *file,
+			const char *buf, unsigned long count)
+{
+    int dev = MINOR(inode->i_rdev);
+
+    switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	case SND_DEV_CTL:
+	    return(-EPERM);
+	case SND_DEV_DSP:
+	case SND_DEV_AUDIO:
+	    return(sq_write(buf, count));
+	default:
+	    return(unknown_minor_dev("sound_write", dev));
+    }
+}
+
+
+static int unknown_minor_dev(char *fname, int dev)
+{
+    /* printk("%s: Unknown minor device %d\n", fname, dev); */
+    return(-ENXIO);
+}
+
+
+static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg)
+{
+    int dev = MINOR(inode->i_rdev);
+    u_long fmt;
+    int data;
+
+    switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	    return(-EPERM);
+	case SND_DEV_CTL:
+	    return(mixer_ioctl(inode, file, cmd, arg));
+	case SND_DEV_AUDIO:
+	case SND_DEV_DSP:
+	    switch (cmd) {
+		case SNDCTL_DSP_RESET:
+		    sq_reset();
+		    return(0);
+		case SNDCTL_DSP_POST:
+		case SNDCTL_DSP_SYNC:
+		    return(sound_fsync(inode, file));
+
+		/* ++TeSche: before changing any of these it's probably wise to
+		 * wait until sound playing has settled down
+		 */
+		case SNDCTL_DSP_SPEED:
+		    sound_fsync(inode, file);
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_speed(data)));
+		case SNDCTL_DSP_STEREO:
+		    sound_fsync(inode, file);
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_stereo(data)));
+		case SOUND_PCM_WRITE_CHANNELS:
+		    sound_fsync(inode, file);
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1));
+		case SNDCTL_DSP_SETFMT:
+		    sound_fsync(inode, file);
+		    IOCTL_IN(arg, data);
+		    return(IOCTL_OUT(arg, sound_set_format(data)));
+		case SNDCTL_DSP_GETFMTS:
+		    fmt = 0;
+		    if (sound.trans) {
+			if (sound.trans->ct_ulaw)
+			    fmt |= AFMT_MU_LAW;
+			if (sound.trans->ct_alaw)
+			    fmt |= AFMT_A_LAW;
+			if (sound.trans->ct_s8)
+			    fmt |= AFMT_S8;
+			if (sound.trans->ct_u8)
+			    fmt |= AFMT_U8;
+			if (sound.trans->ct_s16be)
+			    fmt |= AFMT_S16_BE;
+			if (sound.trans->ct_u16be)
+			    fmt |= AFMT_U16_BE;
+			if (sound.trans->ct_s16le)
+			    fmt |= AFMT_S16_LE;
+			if (sound.trans->ct_u16le)
+			    fmt |= AFMT_U16_LE;
+		    }
+		    return(IOCTL_OUT(arg, fmt));
+		case SNDCTL_DSP_GETBLKSIZE:
+		    return(IOCTL_OUT(arg, 10240));
+		case SNDCTL_DSP_SUBDIVIDE:
+		case SNDCTL_DSP_SETFRAGMENT:
+		    break;
+
+		default:
+		    return(mixer_ioctl(inode, file, cmd, arg));
+	    }
+	    break;
+
+	default:
+	    return(unknown_minor_dev("sound_ioctl", dev));
+    }
+    return(-EINVAL);
+}
+
+
+static struct file_operations sound_fops =
+{
+    sound_lseek,
+    sound_read,
+    sound_write,
+    NULL,
+    NULL,                      /* select */
+    sound_ioctl,
+    NULL,
+    sound_open,
+    sound_release,
+    sound_fsync
+};
+
+
+
+/*** Config & Setup **********************************************************/
+
+
+void soundcard_init(void)
+{
+    int has_sound = 0;
+    int i;
+
+    switch (m68k_machtype) {
+#ifdef CONFIG_ATARI
+	case MACH_ATARI:
+	    if (ATARIHW_PRESENT(PCM_8BIT)) {
+		if (ATARIHW_PRESENT(CODEC))
+		    sound.mach = machFalcon;
+		else if (ATARIHW_PRESENT(MICROWIRE))
+		    sound.mach = machTT;
+		else
+		    break;
+		if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
+		    has_sound = 1;
+		else
+		    printk("DMA sound driver: Timer A interrupt already in use\n");
+	    }
+	    break;
+
+#endif /* CONFIG_ATARI */
+#ifdef CONFIG_AMIGA
+	case MACH_AMIGA:
+	    if (AMIGAHW_PRESENT(AMI_AUDIO)) {
+		sound.mach = machAmiga;
+		has_sound = 1;
+	    }
+	    break;
+#endif /* CONFIG_AMIGA */
+    }
+    if (!has_sound)
+	return;
+
+    /* Set up sound queue, /dev/audio and /dev/dsp. */
+    sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL);
+    if (!sound_buffers) {
+out_of_memory:
+	printk("DMA sound driver: Not enough buffer memory, driver disabled!\n");
+	return;
+    }
+    for (i = 0; i < numBufs; i++) {
+	sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL);
+	if (!sound_buffers[i]) {
+	    while (i--)
+		sound.mach.dma_free (sound_buffers[i], bufSize << 10);
+	    kfree (sound_buffers);
+	    sound_buffers = 0;
+	    goto out_of_memory;
+        }
+    }
+
+#ifndef MODULE
+    /* Register driver with the VFS. */
+    register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
+#endif
+
+    sq_init(numBufs, bufSize << 10, sound_buffers);
+
+    /* Set up /dev/sndstat. */
+    state_init();
+
+    /* Set up /dev/mixer. */
+    mixer_init();
+
+    if (!sound.mach.irqinit()) {
+	printk("DMA sound driver: Interrupt initialization failed\n");
+	return;
+    }
+#ifdef MODULE
+    irq_installed = 1;
+#endif
+
+    printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs,
+	   bufSize);
+
+    return;
+}
+
+void sound_setup(char *str, int *ints)
+{
+    /* ++Martin: stub, could possibly be merged with soundcard.c et al later */
+}
+
+
+#define MAXARGS		8	/* Should be sufficient for now */
+
+void dmasound_setup(char *str, int *ints)
+{
+    /* check the bootstrap parameter for "dmasound=" */
+
+    switch (ints[0]) {
+	case 3:
+	    if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
+		printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+	    else
+		catchRadius = ints[3];
+	    /* fall through */
+	case 2:
+	    if (ints[1] < MIN_BUFFERS)
+		printk("dmasound_setup: illegal number of buffers, using default = %d\n", numBufs);
+	    else
+		numBufs = ints[1];
+	    if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
+		printk("dmasound_setup: illegal buffer size, using default = %d\n", bufSize);
+	    else
+		bufSize = ints[2];
+	    break;
+	case 0:
+	    break;
+	default:
+	    printk("dmasound_setup: illegal number of arguments\n");
+    }
+}
+
+
+#ifdef MODULE
+
+static int dmasound[MAXARGS] = { 0 };
+
+int init_module(void)
+{
+    int err, i = 0;
+    int ints[MAXARGS+1];
+
+    while (i < MAXARGS && dmasound[i])
+	ints[i + 1] = dmasound[i++];
+    ints[0] = i;
+
+    if (i)
+	dmasound_setup("dmasound=", ints);
+
+    err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
+    if (err) {
+	printk("dmasound: driver already loaded/included in kernel\n");
+	return err;
+    }
+    chrdev_registered = 1;
+    soundcard_init();
+
+    return 0;
+}
+
+
+void cleanup_module(void)
+{
+    int i;
+
+    if (MOD_IN_USE)
+	return;
+
+    if (chrdev_registered)
+	unregister_chrdev(SOUND_MAJOR, "sound");
+
+    if (irq_installed) {
+	sound_silence();
+	sound.mach.irqcleanup();
+    }
+
+    if (sound_buffers) {
+	for (i = 0; i < numBufs; i++)
+	    sound.mach.dma_free(sound_buffers[i], bufSize << 10);
+	kfree(sound_buffers);
+    }
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/dmasound.h linux/drivers/sound/dmasound.h
--- v2.1.40/linux/drivers/sound/dmasound.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/dmasound.h	Sun May 11 02:59:34 1997
@@ -0,0 +1,36 @@
+
+/* linux/drivers/sound/dmasound.h */
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_NDEVS	256	/* Number of supported devices */
+#define SND_DEV_CTL	0	/* Control port /dev/mixer */
+#define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
+				   synthesizer and MIDI output) */
+#define SND_DEV_MIDIN	2	/* Raw midi access */
+#define SND_DEV_DSP	3	/* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO	4	/* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16	5	/* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS	6	/* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2	8	/* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS	SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED	8000
+
+#define ON		1
+#define OFF		0
+
+#define MAX_AUDIO_DEV	5
+#define MAX_MIXER_DEV	2
+#define MAX_SYNTH_DEV	3
+#define MAX_MIDI_DEV	6
+#define MAX_TIMER_DEV	3
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.1.40/linux/drivers/sound/gus_card.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/gus_card.c	Sun May 11 02:59:02 1997
@@ -123,7 +123,7 @@
 
 #ifdef CONFIG_GUSMAX
   if (have_gus_max)
-    ad1848_interrupt (irq, NULL, NULL);
+    adintr (irq, NULL, NULL);
 #endif
 
   while (1)
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c
--- v2.1.40/linux/drivers/sound/gus_midi.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/gus_midi.c	Sun May 11 02:59:02 1997
@@ -248,6 +248,7 @@
 
   std_midi_synth.midi_dev = my_dev = num_midis;
   midi_devs[num_midis++] = &gus_midi_operations;
+  sequencer_init ();
   return;
 }
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.40/linux/drivers/sound/gus_wave.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/gus_wave.c	Sun May 11 02:59:06 1997
@@ -21,7 +21,7 @@
 
 #if defined(CONFIG_GUSHW)
 
-#define GUS_BANK_SIZE (256*1024)
+#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
 
 #define MAX_SAMPLE	150
 #define MAX_PATCH	256
@@ -73,6 +73,7 @@
 static int      gus_dma2 = -1;
 static int      dual_dma_mode = 0;
 static long     gus_mem_size = 0;
+static long     gus_rom_size = 0;
 static long     free_mem_ptr = 0;
 static int      gus_busy = 0;
 static int      gus_no_dma = 0;
@@ -84,6 +85,7 @@
 static int      only_read_access = 0;
 static int      only_8_bits = 0;
 
+int             iw_mode = 0;
 int             gus_wave_volume = 60;
 int             gus_pcm_volume = 80;
 int             have_gus_max = 0;
@@ -105,6 +107,8 @@
 static int      gus_audio_speed;
 static int      gus_audio_channels;
 static int      gus_audio_bits;
+static int      gus_audio_bsize;
+static char     bounce_buf[8 * 1024];	/* Must match value set to max_fragment */
 
 static struct wait_queue *dram_sleeper = NULL;
 static volatile struct snd_wait dram_sleep_flag =
@@ -113,7 +117,7 @@
 /*
  * Variables and buffers for PCM output
  */
-#define MAX_PCM_BUFFERS		(32*MAX_REALTIME_FACTOR)	/* Don't change */
+#define MAX_PCM_BUFFERS		(128*MAX_REALTIME_FACTOR)	/* Don't change */
 
 static int      pcm_bsize, pcm_nblk, pcm_banksize;
 static int      pcm_datasize[MAX_PCM_BUFFERS];
@@ -356,14 +360,22 @@
   cli ();
   if (is16bit)
     {
-      /*
-       * Special processing required for 16 bit patches
-       */
+      if (iw_mode)
+	{
+	  /* Interwave spesific address translations */
+	  address >>= 1;
+	}
+      else
+	{
+	  /*
+	   * Special processing required for 16 bit patches
+	   */
 
-      hold_address = address;
-      address = address >> 1;
-      address &= 0x0001ffffL;
-      address |= (hold_address & 0x000c0000L);
+	  hold_address = address;
+	  address = address >> 1;
+	  address &= 0x0001ffffL;
+	  address |= (hold_address & 0x000c0000L);
+	}
     }
 
   gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
@@ -389,6 +401,8 @@
 static void
 gus_select_max_voices (int nvoices)
 {
+  if (iw_mode)
+    nvoices = 32;
   if (nvoices < 14)
     nvoices = 14;
   if (nvoices > 32)
@@ -430,7 +444,11 @@
   unsigned long   divisor = freq_div_table[nr_voices - 14];
   unsigned short  fc;
 
-  fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+  /* Interwave plays at 44100 Hz with any number of voices */
+  if (iw_mode)
+    fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100);
+  else
+    fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
   fc = fc << 1;
 
   gus_write16 (0x01, fc);
@@ -851,6 +869,8 @@
 
   gus_read8 (0x0f);		/* Clear pending IRQs */
 
+  if (iw_mode)
+    gus_write8 (0x19, gus_read8 (0x19) | 0x01);
   restore_flags (flags);
 }
 
@@ -1022,10 +1042,10 @@
  */
   gus_write16 (0x52, (gus_look16 (0x52) & 0xfff0) | bits);
 
-/*
- *    Return the chip back to GUS compatible mode.
- */
-  gus_write8 (0x19, gus_read8 (0x19) & ~0x01);
+/*      Leave the chip into enhanced mode. Disable LFO  */
+  gus_mem_size = total;
+  iw_mode = 1;
+  gus_write8 (0x19, (gus_read8 (0x19) | 0x01) & ~0x02);
 }
 
 int
@@ -1065,6 +1085,8 @@
 
   if (gus_pnp_flag)
     pnp_mem_init ();
+  if (iw_mode)
+    return 1;
 
   /* See if there is first block there.... */
   gus_poke (0L, 0xaa);
@@ -1479,6 +1501,9 @@
   voices[voice].mode = samples[sample].mode;
   voices[voice].patch_vol = samples[sample].volume;
 
+  if (iw_mode)
+    gus_write8 (0x15, 0x00);	/* RAM, Reset voice deactivate bit of SMSI */
+
   if (voices[voice].mode & WAVE_ENVELOPES)
     {
       int             i;
@@ -1897,7 +1922,7 @@
 
 	  for (i = 0; i < blk_sz; i++)
 	    {
-	      get_user (data, (unsigned char *) &((addr)[sizeof_patch + i]));
+	      get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i]));
 	      if (patch.mode & WAVE_UNSIGNED)
 		if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
 		  data ^= 0x80;	/* Convert to signed */
@@ -1934,17 +1959,37 @@
 	   * Set the DRAM address for the wave data
 	   */
 
-	  address = target;
-
-	  if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+	  if (iw_mode)
 	    {
-	      hold_address = address;
-	      address = address >> 1;
-	      address &= 0x0001ffffL;
-	      address |= (hold_address & 0x000c0000L);
+	      /* Different address translation in enhanced mode */
+
+	      unsigned char   hi;
+
+	      if (gus_dma > 4)
+		address = target >> 1;	/* Convert to 16 bit word address */
+	      else
+		address = target;
+
+	      hi = (unsigned char) ((address >> 16) & 0xf0);
+	      hi += (unsigned char) (address & 0x0f);
+
+	      gus_write16 (0x42, (address >> 4) & 0xffff);	/* DMA address (low) */
+	      gus_write8 (0x50, hi);
 	    }
+	  else
+	    {
+	      address = target;
 
-	  gus_write16 (0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+	      if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+		{
+		  hold_address = address;
+		  address = address >> 1;
+		  address &= 0x0001ffffL;
+		  address |= (hold_address & 0x000c0000L);
+		}
+
+	      gus_write16 (0x42, (address >> 4) & 0xffff);	/* DRAM DMA address */
+	    }
 
 	  /*
 	   * Start the DMA transfer
@@ -2280,6 +2325,7 @@
     }
 }
 
+static int      saved_iw_mode;	/* A hack hack hack */
 
 static int
 gus_audio_open (int dev, int mode)
@@ -2300,6 +2346,13 @@
   gus_reset ();
   reset_sample_memory ();
   gus_select_max_voices (14);
+  saved_iw_mode = iw_mode;
+  if (iw_mode)
+    {
+      /* There are some problems with audio in enhanced mode so disable it */
+      gus_write8 (0x19, gus_read8 (0x19) & ~0x01);	/* Disable enhanced mode */
+      iw_mode = 0;
+    }
 
   pcm_active = 0;
   dma_active = 0;
@@ -2322,6 +2375,7 @@
 static void
 gus_audio_close (int dev)
 {
+  iw_mode = saved_iw_mode;
   gus_reset ();
   gus_busy = 0;
   pcm_opened = 0;
@@ -2428,7 +2482,7 @@
 	  gus_voice_volume (1530 + (25 * gus_pcm_volume));
 	  gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
 
-	  gus_write_addr (0x0a, dram_loc, 0, is16bits);		/* Starting position */
+	  gus_write_addr (0x0a, chn * pcm_banksize, 0, is16bits);	/* Starting position */
 	  gus_write_addr (0x02, chn * pcm_banksize, 0, is16bits);	/* Loop start */
 
 	  if (chn != 0)
@@ -2437,30 +2491,11 @@
 	}
 
       if (chn == 0)
-	gus_write_addr (0x04, dram_loc + pcm_datasize[this_one] - 1,
+	gus_write_addr (0x04, dram_loc + pcm_bsize - 1,
 			0, is16bits);	/* Loop end location */
       else
 	mode[chn] |= 0x08;	/* Enable looping */
 
-      if (pcm_datasize[this_one] != pcm_bsize)
-	{
-	  /*
-	   * Incompletely filled block. Possibly the last one.
-	   */
-	  if (chn == 0)
-	    {
-	      mode[chn] &= ~0x08;	/* Disable looping */
-	      mode[chn] |= 0x20;	/* Enable IRQ at the end */
-	      voices[0].loop_irq_mode = LMODE_PCM_STOP;
-	      ramp_mode[chn] = 0x03;	/* No rollover bit */
-	    }
-	  else
-	    {
-	      gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
-			      0, is16bits);	/* Loop end location */
-	      mode[chn] &= ~0x08;	/* Disable looping */
-	    }
-	}
 
       restore_flags (flags);
     }
@@ -2471,6 +2506,8 @@
       cli ();
       gus_select_voice (chn);
       gus_write8 (0x0d, ramp_mode[chn]);
+      if (iw_mode)
+	gus_write8 (0x15, 0x00);	/* Reset voice deactivate bit of SMSI */
       gus_voice_on (mode[chn]);
       restore_flags (flags);
     }
@@ -2549,7 +2586,7 @@
        */
       dma_active = 1;		/* DMA started. There is a unacknowledged buffer */
       active_device = GUS_DEV_PCM_DONE;
-      if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize))
+      if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize))
 	{
 	  play_next_pcm_block ();
 	}
@@ -2567,13 +2604,58 @@
 }
 
 static void
+gus_uninterleave8 (char *buf, int l)
+{
+/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */
+  int             i, p = 0, halfsize = l / 2;
+  char           *buf2 = buf + halfsize, *src = bounce_buf;
+
+  memcpy (bounce_buf, buf, l);
+
+  for (i = 0; i < halfsize; i++)
+    {
+      buf[i] = src[p++];	/* Left channel */
+      buf2[i] = src[p++];	/* Right channel */
+    }
+}
+
+static void
+gus_uninterleave16 (short *buf, int l)
+{
+/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */
+  int             i, p = 0, halfsize = l / 2;
+  short          *buf2 = buf + halfsize, *src = (short *) bounce_buf;
+
+  memcpy (bounce_buf, (char *) buf, l * 2);
+
+  for (i = 0; i < halfsize; i++)
+    {
+      buf[i] = src[p++];	/* Left channel */
+      buf2[i] = src[p++];	/* Right channel */
+    }
+}
+
+static void
 gus_audio_output_block (int dev, unsigned long buf, int total_count,
 			int intrflag)
 {
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT;
+
   pcm_current_buf = buf;
   pcm_current_count = total_count;
   pcm_current_intrflag = intrflag;
   pcm_current_dev = dev;
+  if (gus_audio_channels == 2)
+    {
+      char           *b = dmap->raw_buf + (buf - dmap->raw_buf_phys);
+
+      if (gus_audio_bits == 8)
+	gus_uninterleave8 (b, total_count);
+      else
+	gus_uninterleave16 ((short *) b, total_count / 2);
+    }
   gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
 }
 
@@ -2587,7 +2669,7 @@
   save_flags (flags);
   cli ();
 
-  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
 
   mode = 0xa0;			/* DMA IRQ enabled, invert MSB */
 
@@ -2607,6 +2689,8 @@
 {
   unsigned int    rate;
 
+  gus_audio_bsize = bsize;
+  audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
   rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
 
   gus_write8 (0x48, rate & 0xff);	/* Set sampling rate */
@@ -2627,6 +2711,7 @@
 
   long            mem_ptr, mem_size;
 
+  audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT;
   mem_ptr = 0;
   mem_size = gus_mem_size / gus_audio_channels;
 
@@ -2636,7 +2721,7 @@
   pcm_bsize = bsize / gus_audio_channels;
   pcm_head = pcm_tail = pcm_qlen = 0;
 
-  pcm_nblk = MAX_PCM_BUFFERS;
+  pcm_nblk = 2;			/* MAX_PCM_BUFFERS; */
   if ((pcm_bsize * pcm_nblk) > mem_size)
     pcm_nblk = mem_size / pcm_bsize;
 
@@ -2658,56 +2743,6 @@
   return pcm_qlen;
 }
 
-static void
-gus_copy_from_user (int dev, char *localbuf, int localoffs,
-		    const char *userbuf, int useroffs, int len)
-{
-  if (gus_audio_channels == 1)
-    {
-      copy_from_user (&localbuf[localoffs], &(userbuf)[useroffs], len);
-    }
-  else if (gus_audio_bits == 8)
-    {
-      int             in_left = useroffs;
-      int             in_right = useroffs + 1;
-      char           *out_left, *out_right;
-      int             i;
-
-      len /= 2;
-      localoffs /= 2;
-      out_left = localbuf + localoffs;
-      out_right = out_left + pcm_bsize;
-
-      for (i = 0; i < len; i++)
-	{
-	  get_user (*out_left++, (unsigned char *) &((userbuf)[in_left]));
-	  in_left += 2;
-	  get_user (*out_right++, (unsigned char *) &((userbuf)[in_right]));
-	  in_right += 2;
-	}
-    }
-  else
-    {
-      int             in_left = useroffs;
-      int             in_right = useroffs + 2;
-      short          *out_left, *out_right;
-      int             i;
-
-      len /= 4;
-      localoffs /= 4;
-
-      out_left = ((short *) localbuf) + localoffs;
-      out_right = out_left + (pcm_bsize / 2);
-
-      for (i = 0; i < len; i++)
-	{
-	  get_user (*out_left++, (unsigned short *) &((userbuf)[in_left]));
-	  in_left += 4;
-	  get_user (*out_right++, (unsigned short *) &((userbuf)[in_right]));
-	  in_right += 4;
-	}
-    }
-}
 
 static struct audio_driver gus_audio_driver =
 {
@@ -2720,7 +2755,7 @@
   gus_audio_prepare_for_output,
   gus_audio_reset,
   gus_local_qlen,
-  gus_copy_from_user
+  NULL
 };
 
 static void
@@ -3218,7 +3253,10 @@
   else
     {
       voice_alloc = &guswave_operations.alloc;
+      if (iw_mode)
+	guswave_operations.id = "IWAVE";
       synth_devs[num_synths++] = &guswave_operations;
+      sequencer_init ();
 #ifdef CONFIG_SEQUENCER
       gus_tmr_install (gus_base + 8);
 #endif
@@ -3237,15 +3275,16 @@
 						  &gus_audio_driver,
 					       sizeof (struct audio_driver),
 						  NEEDS_RESTART |
-			                      ((dma2 != dma && dma2 != -1) ?
-					       DMA_DUPLEX : 0),
+		                  ((!iw_mode && dma2 != dma && dma2 != -1) ?
+				   DMA_DUPLEX : 0),
 						  AFMT_U8 | AFMT_S16_LE,
 						  NULL,
 						  dma,
 						  dma2)) < 0)
 	                  return;
 
-	audio_devs[gus_devnum]->min_fragment = 9;
+	audio_devs[gus_devnum]->min_fragment = 9;	/* 512k */
+	audio_devs[gus_devnum]->max_fragment = 11;	/* 8k (must match size of bounce_buf */
 	audio_devs[gus_devnum]->mixer_dev = num_mixers;		/* Next mixer# */
 	audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
       }
@@ -3347,7 +3386,6 @@
       pcm_active = 0;		/* Signal to the play_next_pcm_block routine */
     case LMODE_PCM:
       {
-	int             flag;	/* 0 or 2 */
 
 	pcm_qlen--;
 	pcm_head = (pcm_head + 1) % pcm_nblk;
@@ -3367,20 +3405,11 @@
 	  }
 
 	/*
-	   * If the queue was full before this interrupt, the DMA transfer was
-	   * suspended. Let it continue now.
+	 * If the queue was full before this interrupt, the DMA transfer was
+	 * suspended. Let it continue now.
 	 */
-	if (dma_active)
-	  {
-	    if (pcm_qlen == 0)
-	      flag = 1;		/* Underflow */
-	    else
-	      flag = 0;
-	    dma_active = 0;
-	  }
-	else
-	  flag = 2;		/* Just notify the dmabuf.c */
-	DMAbuf_outputintr (gus_devnum, flag);
+	if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+	  DMAbuf_outputintr (gus_devnum, 0);
       }
       break;
 
@@ -3413,9 +3442,9 @@
 
   switch (mode)
     {
-    case VMODE_HALT:		/*
-				 * Decay phase finished
-				 */
+    case VMODE_HALT:		/* Decay phase finished */
+      if (iw_mode)
+	gus_write8 (0x15, 0x02);	/* Set voice deactivate bit of SMSI */
       restore_flags (flags);
       gus_voice_init (voice);
       break;
@@ -3523,13 +3552,12 @@
 	gus_write8 (0x41, 0);	/* Disable GF1 DMA */
 	if (pcm_qlen < pcm_nblk)
 	  {
-	    int             flag = (1 - dma_active) * 2;	/* 0 or 2 */
-
-	    if (pcm_qlen == 0)
-	      flag = 1;		/* Underrun */
 	    dma_active = 0;
 	    if (gus_busy)
-	      DMAbuf_outputintr (gus_devnum, flag);
+	      {
+		if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+		  DMAbuf_outputintr (gus_devnum, 0);
+	      }
 	  }
 	break;
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/lowlevel/Config.tmpl linux/drivers/sound/lowlevel/Config.tmpl
--- v2.1.40/linux/drivers/sound/lowlevel/Config.tmpl	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/Config.tmpl	Fri Apr  4 09:06:22 1997
@@ -32,7 +32,7 @@
          fi
       fi
 
-      if [ "$CONFIG_MIDI" = "y" ]; then
+      if [ "$CONFIG_MPU401" = "y" ]; then
          bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
          if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
 	    comment 'Audio Excel DSP 16 [MPU-401]'
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/lowlevel/aci.c linux/drivers/sound/lowlevel/aci.c
--- v2.1.40/linux/drivers/sound/lowlevel/aci.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/aci.c	Fri Mar  7 04:08:42 1997
@@ -22,9 +22,9 @@
  *        First version written.
  *   1995-12-31  Markus Kuhn
  *        Second revision, general code cleanup.
- *   1997-05-16	 Hannu Savolainen
+ *   1996-05-16	 Hannu Savolainen
  *	  Integrated with other parts of the driver.
- *   1997-05-28  Markus Kuhn
+ *   1996-05-28  Markus Kuhn
  *        Initialize CS4231A mixer, make ACI first mixer,
  *        use new private mixer API for solo mode.
  */
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.40/linux/drivers/sound/maui.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/maui.c	Sun May 11 02:59:09 1997
@@ -333,7 +333,7 @@
     {
       unsigned char   data;
 
-      get_user (data, (unsigned char *) &((addr)[hdr_size + i]));
+      get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
       if (i == 0 && !(data & 0x80))
 	return -EINVAL;
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.1.40/linux/drivers/sound/midi_synth.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/midi_synth.c	Sun May 11 02:59:10 1997
@@ -537,7 +537,7 @@
     {
       unsigned char   data;
 
-      get_user (data, (unsigned char *) &((addr)[hdr_size + i]));
+      get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
 
       eox_seen = (i > 0 && data & 0x80);	/* End of sysex */
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.40/linux/drivers/sound/midibuf.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/midibuf.c	Sun May 11 02:59:11 1997
@@ -12,6 +12,7 @@
  */
 #include <linux/config.h>
 
+#define MIDIBUF_C
 
 #include "sound_config.h"
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.40/linux/drivers/sound/mpu401.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/mpu401.c	Sun May 11 02:59:12 1997
@@ -1183,6 +1183,7 @@
 
   irq2dev[devc->irq] = num_midis;
   midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno];
+  sequencer_init ();
 }
 
 static int
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.40/linux/drivers/sound/opl3.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/opl3.c	Sun May 11 23:01:14 1997
@@ -112,6 +112,7 @@
 	struct sbi_instrument ins;
 
 	memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins));
+	printk("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
 
 	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
 	  {
@@ -1168,6 +1169,7 @@
   opl3_operations.info = &devc->fm_info;
 
   synth_devs[num_synths++] = &opl3_operations;
+  sequencer_init ();
   devc->v_alloc = &opl3_operations.alloc;
   devc->chn_info = &opl3_operations.chn_info[0];
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.40/linux/drivers/sound/os.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/os.h	Sun May 11 02:58:40 1997
@@ -7,6 +7,7 @@
 #undef NO_INLINE_ASM
 #define SHORT_BANNERS
 #define MANUAL_PNP
+#undef DO_TIMINGS
 
 #ifdef MODULE
 #define __NO_VERSION__
@@ -20,6 +21,7 @@
 #define LINUX21X
 #endif
 
+#ifdef __KERNEL__
 #include <linux/utsname.h>
 #include <linux/string.h>
 #include <linux/fs.h>
@@ -37,6 +39,10 @@
 #include <linux/vmalloc.h>
 #include <asm/uaccess.h>
 #include <linux/poll.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#else
+#endif
 
 #include <linux/wrapper.h>
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.40/linux/drivers/sound/pas2_card.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/pas2_card.c	Sun May 11 02:59:14 1997
@@ -283,7 +283,7 @@
   foo = board_id ^ 0xe0;
 
   pas_write (foo, 0x0B8B);
-  foo = inb (0x0B8B);
+  foo = pas_read (0x0B8B);
   pas_write (board_id, 0x0B8B);
 
   if (board_id != foo)
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.1.40/linux/drivers/sound/pas2_midi.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/pas2_midi.c	Sun May 11 02:59:15 1997
@@ -236,6 +236,7 @@
 
   std_midi_synth.midi_dev = my_dev = num_midis;
   midi_devs[num_midis++] = &pas_midi_operations;
+  sequencer_init ();
 }
 
 void
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.1.40/linux/drivers/sound/pas2_pcm.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/pas2_pcm.c	Sun May 11 02:59:16 1997
@@ -55,12 +55,12 @@
   if (pcm_channels & 2)
     {
       foo = (596590 + (arg / 2)) / arg;
-      arg = 596590 / foo;
+      arg = (596590 + (foo / 2)) / foo;
     }
   else
     {
       foo = (1193180 + (arg / 2)) / arg;
-      arg = 1193180 / foo;
+      arg = (1193180 + (foo / 2)) / foo;
     }
 
   pcm_speed = arg;
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/pnp.c linux/drivers/sound/pnp.c
--- v2.1.40/linux/drivers/sound/pnp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/pnp.c	Sun May 11 02:59:16 1997
@@ -0,0 +1,36 @@
+/*
+ *    sound/pnp.c - Temporary kludge for PnP soundcards.
+ *
+ *      Copyright by Hannu Savolainen 1997.
+ *
+ *      This file is just a temporary solution to be used with
+ *      PnP soundcards until final kernel PnP support gets ready.
+ *      The code contained in this file is largely untested and
+ *      may cause failures in some systems. In particular it will
+ *      cause troubles with other PnP ISA cards such as network cards.
+ *      This file is also incompatible with (future) PnP support in kernel.
+ *
+ *      For the above reasons I don't want this file to be widely distributed.
+ *      You have permission to use this file with this particular sound driver
+ *      and only for your own evaluation purposes. Any other use of this file
+ *      or parts of it requires written permission by the author.
+ */
+extern int      pnp_trace;
+int             pnp_trace_io = 0;
+
+#define UDELAY(x) udelay(x)
+
+#ifdef TEST_PNP
+#include "pnp.h"
+#include <malloc.h>
+#include <stdio.h>
+#define printk printf
+
+#define MALLOC(sz) malloc(sz)
+
+unsigned char   res[10000];
+int             rp;
+
+#else
+#include "sound_config.h"
+#endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/pnp.h linux/drivers/sound/pnp.h
--- v2.1.40/linux/drivers/sound/pnp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/pnp.h	Sun May 11 02:58:36 1997
@@ -0,0 +1,112 @@
+#ifndef _PNP_H_
+#define _PNP_H_
+
+#define MAX_PNP_CARDS 	16
+
+#define PNP_DEVID(a, b, c, id) \
+	((a-'@')<<26) | ((b-'@')<<21) | ((c-'@') << 16) | (id&0xffff)
+
+#define NO_PORT	0
+#define NO_IRQ  0
+#define NO_DMA	4
+
+struct pnp_port_resource
+{
+	short range_min, range_max;
+	unsigned char align, len;
+};
+
+struct pnp_func
+  {
+    struct pnp_dev *dev;
+    unsigned long   flags;
+    struct pnp_func *next;
+    int nports;
+    struct pnp_port_resource ports[8];
+    int nirq;
+    unsigned short irq[2];
+    int ndma;
+    unsigned char dma[2];
+  };
+
+struct pnp_dev
+  {
+    int             key;	/* A PnP device id identifying this device */
+    char           *name;	/* ANSI identifier string */
+    int		    devno;	/* Logical device number within a card */
+    int             ncompat;	/* Number of compatible device idents */
+    int             compat_keys[8];	/* List of PnP compatible device idents */
+    struct pnp_card *card;	/* Link to the card which holds this device */
+    struct pnp_dev *next;	/* Pointer to next logical device or NULL */
+
+    int 	    nports, nirq, ndma;
+
+    int             nfunc;	/* Number of dependent function records */
+    struct pnp_func *functions;	/* List of dependent functions */
+    int             driver;	/* Driver signature or 0 */
+    int		    preconfig;  /* 1 if config has been set manully */
+
+  };
+
+struct pnp_card
+  {
+    int             key;	/* Unique PnP device identifier */
+    char           *name;	/* ANSI identifier string of the card */
+    int             csn;	/* Card select number */
+
+    char            pnp_version;
+    char            vendor_version;
+
+    int             driven;	/* 0=No driver assigned, */
+    /* 1=Drivers assigned to some of logical devices */
+    /* 2=Card and all of it's devices have a driver */
+    int             relocated;	/* 0=Card is inactive, 1=card is up and running */
+
+    int             ndevs;	/* Number of logical devices on the card */
+    struct pnp_dev *devices;	/* Pointer to first function entry */
+  };
+
+typedef struct pnp_card_info {
+  struct pnp_card card;
+  char name[64];
+} pnp_card_info;
+
+extern int      pnp_card_count;
+extern struct 	pnp_card *pnp_cards[MAX_PNP_CARDS];
+extern struct pnp_dev *pnp_device_list;
+
+extern int pnp_trace;
+
+/*
+ * Callable functions
+ */
+
+extern void pnp_init(void);		/* Called by kernel during boot */
+extern void terminate_pnp(void);
+
+extern int pnp_connect(char *driver_name);
+extern void pnp_disconnect(int driver_signature);
+
+/*
+ * pnp_get_descr() returns an ASCII desctription string for a device.
+ * The parameter is an compressed EISA identifier of the device/card.
+ */
+extern char *pnp_get_descr (int id);
+
+extern void pnp_enable_device(struct pnp_dev *dev, int state);
+extern void pnp_set_port(struct pnp_dev *dev, int selec, unsigned short base);
+extern void pnp_set_irq(struct pnp_dev *dev, int selec, unsigned short val);
+extern void pnp_set_dma(struct pnp_dev *dev, int selec, unsigned short val);
+extern unsigned short pnp_get_port(struct pnp_dev *dev, int selec);
+extern unsigned short pnp_get_irq(struct pnp_dev *dev, int selec);
+extern unsigned short pnp_get_dma(struct pnp_dev *dev, int selec);
+extern int pnp_allocate_device(int driver_sig, struct pnp_dev *dev, int basemask, int irqmask,
+			       int dmamask, int memmask);
+extern void pnp_release_device(int driver_sig, struct pnp_dev *dev);
+extern int pnp_asc2devid(char *name);
+extern char *pnp_devid2asc(int id);
+extern void pnp_dump_resources(void);
+extern int pnp_device_status (struct pnp_dev *dev);
+struct pnp_dev *pnp_get_next_device(int driver_sig, struct pnp_dev *prev);
+unsigned char pnp_readreg (struct pnp_dev *dev, int reg);
+#endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.1.40/linux/drivers/sound/sb.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb.h	Sun May 11 02:58:36 1997
@@ -105,6 +105,7 @@
 	/* MIDI fields */
 	   int my_mididev;
 	   int input_opened;
+	   int midi_broken;
 	   void (*midi_input_intr) (int dev, unsigned char data);
 	} sb_devc;
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.40/linux/drivers/sound/sb_common.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_common.c	Sun May 11 02:59:20 1997
@@ -58,10 +58,7 @@
   int             i;
   unsigned long   limit;
 
-  limit = jiffies + HZ / 10;	/*
-				   * The timeout is 0.1 seconds
-				 */
-
+  limit = jiffies + HZ / 10;	/* Timeout */
   /*
    * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
    * called while interrupts are disabled. This means that the timer is
@@ -173,7 +170,8 @@
 	break;
 
       default:
-	printk ("Sound Blaster: Unexpected interrupt\n");
+	/* printk ("Sound Blaster: Unexpected interrupt\n"); */
+	;
       }
 /*
  * Acknowledge interrupts 
@@ -633,6 +631,7 @@
 		for (i = 0; i < 10000; i++)
 		  inb (DSP_DATA_AVAIL);
 		devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
+		devc->model = MDL_AZTECH;
 	      }
 	}
     }
@@ -666,6 +665,7 @@
   sb_devc        *devc;
   int             n;
   char            name[100];
+  extern int      sb_be_quiet;
 
 /*
  * Check if we had detected a SB device earlier
@@ -776,7 +776,7 @@
 	{
 	  devc->model = hw_config->card_subtype = MDL_SBPRO;
 	  if (hw_config->name == NULL)
-	    hw_config->name = "Sound Blaster Pro";
+	    hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
 	}
       break;
 
@@ -809,10 +809,33 @@
 #endif
 
   if (hw_config->name == NULL)
-    hw_config->name = "Sound Blaster";
+    hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
 
   sprintf (name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor);
   conf_printf (name, hw_config);
+
+/*
+ * Assuming that a soundcard is Sound Blaster (compatible) is the most common
+ * configuration error and the mother of all problems. Usually soundcards
+ * emulate SB Pro but in addition they have a 16 bit native mode which should be
+ * used in Unix. See Readme.cards for more information about configuring OSS/Free
+ * properly.
+ */
+  if (devc->model <= MDL_SBPRO)
+    if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1. */
+      {
+	printk ("This soundcard doesn't seem to be fully Sound Blaster Pro compatible.\n");
+	printk ("Almost certainly there is another way to configure OSS so that\n");
+	printk ("it works properly with OSS (for example in 16 bit mode).\n");
+      }
+    else if (!sb_be_quiet && devc->model == MDL_SBPRO)
+      {
+	printk ("SB DSP version is just %d.%d which means that your card is\n",
+		devc->major, devc->minor);
+	printk ("several years old (8 bit only device)\n");
+	printk ("or alternatively the sound driver is incorrectly configured.\n");
+      }
+
   hw_config->card_subtype = devc->model;
   last_devc = devc;		/* For SB MPU detection */
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.40/linux/drivers/sound/sb_midi.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_midi.c	Sun May 11 02:59:20 1997
@@ -52,6 +52,7 @@
   restore_flags (flags);
 
   devc->irq_mode = IMODE_MIDI;
+  devc->midi_broken = 0;
 
   sb_dsp_reset (devc);
 
@@ -96,9 +97,16 @@
   sb_devc        *devc = midi_devs[dev]->devc;
 
   if (devc == NULL)
-    return -ENXIO;
+    return 1;
+
+  if (devc->midi_broken)
+    return 1;
 
-  sb_dsp_command (devc, midi_byte);
+  if (!sb_dsp_command (devc, midi_byte))
+    {
+      devc->midi_broken = 1;
+      return 1;
+    }
 
   return 1;
 }
@@ -220,6 +228,7 @@
 
   midi_devs[num_midis]->converter->id = "SBMIDI";
   num_midis++;
+  sequencer_init ();
 }
 
 #endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.40/linux/drivers/sound/sequencer.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sequencer.c	Sun May 11 02:59:23 1997
@@ -24,6 +24,9 @@
 static struct sound_timer_operations *tmr;
 static int      tmr_no = -1;	/* Currently selected timer */
 static int      pending_timer = -1;	/* For timer change operation */
+extern unsigned long seq_time;
+
+static int      obsolete_api_used = 0;
 
 /*
  * Local counts for number of synth and MIDI devices. These are initialized
@@ -56,7 +59,6 @@
 
 unsigned long   prev_input_time = 0;
 int             prev_event_time;
-unsigned long   seq_time = 0;
 
 #include "tuning.h"
 
@@ -312,6 +314,9 @@
 	      return -EINVAL;
 	    }
 	  ev_size = 4;
+
+	  if (event_rec[0] != SEQ_MIDIPUTC)
+	    obsolete_api_used = 1;
 	}
 
       if (event_rec[0] == SEQ_MIDIPUTC)
@@ -498,11 +503,12 @@
 static void
 seq_chn_voice_event (unsigned char *event_rec)
 {
-  unsigned char   dev = event_rec[1];
-  unsigned char   cmd = event_rec[2];
-  unsigned char   chn = event_rec[3];
-  unsigned char   note = event_rec[4];
-  unsigned char   parm = event_rec[5];
+#define dev event_rec[1]
+#define cmd event_rec[2]
+#define chn event_rec[3]
+#define note event_rec[4]
+#define parm event_rec[5]
+
   int             voice = -1;
 
   if ((int) dev > max_synthdev)
@@ -570,6 +576,11 @@
 
     default:;
     }
+#undef dev
+#undef cmd
+#undef chn
+#undef note
+#undef parm
 }
 
 
@@ -1105,6 +1116,7 @@
       return -EBUSY;
     }
   sequencer_busy = 1;
+  obsolete_api_used = 0;
   restore_flags (flags);
 
   max_mididev = num_midis;
@@ -1322,6 +1334,8 @@
   if (seq_mode == SEQ_2)
     tmr->close (tmr_no);
 
+  if (obsolete_api_used)
+    printk ("/dev/music: Obsolete (4 byte) API was used by this program\n");
   sequencer_busy = 0;
 }
 
@@ -1628,6 +1642,8 @@
       break;
 
     case SNDCTL_SEQ_RESETSAMPLES:
+    case SNDCTL_SYNTH_REMOVESAMPLE:
+    case SNDCTL_SYNTH_CONTROL:
       {
 	int             err;
 
@@ -1870,7 +1886,7 @@
    */
 
   int             note, octave, note_freq;
-  int             notes[] =
+  static int      notes[] =
   {
     261632, 277189, 293671, 311132, 329632, 349232,
     369998, 391998, 415306, 440000, 466162, 493880
@@ -1937,6 +1953,8 @@
     }
 
   semitones = bend / 100;
+  if (semitones > 99)
+    semitones = 99;
   cents = bend % 100;
 
   amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
@@ -1955,6 +1973,10 @@
   if (sequencer_ok)
     return;
 
+#ifdef CONFIG_MIDI
+  MIDIbuf_init ();
+#endif
+
 
   queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * EV_SZ));
   sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * EV_SZ;
@@ -1971,7 +1993,7 @@
   sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
   if (sound_nblocks < 1024)
     sound_nblocks++;;
-  if (queue == NULL)
+  if (iqueue == NULL)
     {
       printk ("Sound: Can't allocate memory for sequencer input queue\n");
       return;
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.40/linux/drivers/sound/sound_calls.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sound_calls.h	Sun May 11 02:58:37 1997
@@ -10,7 +10,7 @@
 int DMAbuf_start_output(int dev, int buff_no, int l);
 int DMAbuf_move_wrpointer(int dev, int l);
 /* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */
-void DMAbuf_init(void);
+void DMAbuf_init(int dev, int dma1, int dma2);
 void DMAbuf_deinit(int dev);
 int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
 int DMAbuf_open_dma (int dev);
@@ -20,7 +20,7 @@
 struct dma_buffparms;
 int DMAbuf_space_in_queue (int dev);
 int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
-int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap);
+int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
 void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
 int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 void DMAbuf_start_devices(unsigned int devmask);
@@ -52,7 +52,8 @@
 void sequencer_release (int dev, struct fileinfo *file);
 int sequencer_ioctl (int dev, struct fileinfo *file,
 	   unsigned int cmd, caddr_t arg);
-int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+
 void sequencer_init (void);
 void sequencer_timer(unsigned long dummy);
 int note_to_freq(int note_num);
@@ -61,8 +62,6 @@
 void seq_input_event(unsigned char *event, int len);
 void seq_copy_to_input (unsigned char *event, int len);
 
-int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
-
 /*
  *	System calls for the /dev/midi
  */
@@ -73,19 +72,20 @@
 void MIDIbuf_release (int dev, struct fileinfo *file);
 int MIDIbuf_ioctl (int dev, struct fileinfo *file,
 	   unsigned int cmd, caddr_t arg);
-int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+
 void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
 void MIDIbuf_init(void);
 
-int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
-
 /*
  *
  *	Misc calls from various sources
  */
 
 /*	From soundcard.c	*/
+#ifndef __bsdi__
 void tenmicrosec(int *osp);
+#endif
 void request_sound_timer (int count);
 void sound_stop_timer(void);
 int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp);
@@ -205,7 +205,7 @@
 #define AD1848_REROUTE(oldctl, newctl) \
 		ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
 
-void     ad1848_interrupt (int irq, void *dev_id, struct pt_regs * dummy);
+void adintr(int irq, void *dev_id, struct pt_regs * dummy);
 void attach_ms_sound(struct address_info * hw_config);
 int probe_ms_sound(struct address_info *hw_config);
 void attach_pnp_ad1848(struct address_info * hw_config);
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sound_pnp.c linux/drivers/sound/sound_pnp.c
--- v2.1.40/linux/drivers/sound/sound_pnp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/sound_pnp.c	Sun May 11 02:59:25 1997
@@ -0,0 +1,1513 @@
+/*
+ * sound/sound_pnp.c
+ *
+ * PnP soundcard support (Linux spesific)
+ */
+/*
+ * 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.
+ */
+#include <linux/config.h>
+
+#include "sound_config.h"
+
+#if defined(CONFIG_SPNP)
+
+
+static struct wait_queue *maui_sleeper = NULL;
+static volatile struct snd_wait maui_sleep_flag =
+{0};
+
+extern unsigned long init_pnp (unsigned long, int *);
+
+#include "pnp.h"
+extern int     *sound_osp;
+
+extern int      (*pnp_ioctl) (unsigned int cmd, caddr_t arg);
+
+extern int      sound_pnp_port;
+static int      disabled_devices[] =
+{
+  PNP_DEVID ('G', 'R', 'V', 0x0003),	/* GUS SB emulation */
+  PNP_DEVID ('G', 'R', 'V', 0x0004),	/* GUS MPU emulation */
+  0
+};
+
+static int      special_devices[] =
+{
+  PNP_DEVID ('C', 'S', 'C', 0x0010),	/* CS4232/6 control port */
+  PNP_DEVID ('C', 'S', 'C', 0x0002),	/* CS4232/6 control port */
+  0
+};
+
+static int      pnp_sig = 0;
+
+static void
+pnp_delay (long t)
+{
+  t = (t * HZ) / 1000000;	/* Convert to jiffies */
+
+
+  {
+    unsigned long   tlimit;
+
+    if (t)
+      current->timeout = tlimit = jiffies + (t);
+    else
+      tlimit = (unsigned long) -1;
+    maui_sleep_flag.opts = WK_SLEEP;
+    interruptible_sleep_on (&maui_sleeper);
+    if (!(maui_sleep_flag.opts & WK_WAKEUP))
+      {
+	if (jiffies >= tlimit)
+	  maui_sleep_flag.opts |= WK_TIMEOUT;
+      }
+    maui_sleep_flag.opts &= ~WK_SLEEP;
+  };
+}
+
+void
+cs4232_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+  int             old_num_mixers = num_mixers;
+  int             is_4232 = 0;	/* CS4232 (not CS4236 or something better) */
+
+  int             portmask = 0xff;
+  int             irqmask = 0x01, dmamask = 0x03;
+  int             opl3_driver, wss_driver;
+
+
+  if (pnp_trace)
+    printk ("CS4232/6 driver waking up\n");
+
+  if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232)))
+    is_4232 = 1;
+
+#ifndef USE_HOT_PNP_INIT
+  if (is_4232)			/* CS4232 may cause lockups */
+    if (pnp_get_port (dev, 0) == NO_PORT ||
+	pnp_get_port (dev, 1) == NO_PORT ||
+	pnp_get_irq (dev, 0) == NO_IRQ ||
+	pnp_get_dma (dev, 0) == NO_DMA
+      )
+      {
+	printk ("Sound: CS4232 in PnP mode requires prior initialization by PnP BIOS\n");
+	return;
+      }
+#endif
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP WSS";
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x01;		/* MSS */
+  else
+    printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x02;		/* OPL3 */
+  else
+    printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+  /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!is_4232)
+    if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+      {
+	printk ("sound_pnp: Failed to find free resources\n");
+	return;
+      }
+
+  {
+    struct address_info hw_config;
+    int             wss_base, opl3_base;
+    int             irq;
+    int             dma1, dma2;
+
+    if (pnp_trace)
+      printk ("Device activation OK\n");
+    wss_base = pnp_get_port (dev, 0);
+    opl3_base = pnp_get_port (dev, 1);
+    irq = pnp_get_irq (dev, 0);
+    dma1 = pnp_get_dma (dev, 0);
+    dma2 = pnp_get_dma (dev, 1);
+
+    pnp_delay (1000000);
+
+    if (pnp_trace)
+      {
+	printk ("I/O0 %03x\n", wss_base);
+	printk ("I/O1 %03x\n", opl3_base);
+	printk ("IRQ %d\n", irq);
+	printk ("DMA0 %d\n", dma1);
+	printk ("DMA1 %d\n", dma2);
+      }
+
+    if (opl3_base && opl3_driver)
+      {
+	hw_config.io_base = opl3_base;
+	hw_config.irq = 0;
+	hw_config.dma = -1;
+	hw_config.dma2 = -1;
+	hw_config.always_detect = 0;
+	hw_config.name = "";
+	hw_config.driver_use_1 = 0;
+	hw_config.driver_use_2 = 0;
+	hw_config.osp = sound_osp;
+	hw_config.card_subtype = 0;
+
+	sndtable_start_card (opl3_driver, &hw_config);
+
+      }
+
+    if (wss_base && wss_driver)
+      {
+	hw_config.io_base = wss_base;
+	hw_config.irq = irq;
+	hw_config.dma = dma1;
+	hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	hw_config.always_detect = 0;
+	hw_config.name = name;
+	hw_config.driver_use_1 = 0;
+	hw_config.driver_use_2 = 0;
+	hw_config.osp = sound_osp;
+	hw_config.card_subtype = 0;
+
+	sndtable_start_card (wss_driver, &hw_config);
+
+
+	if (num_mixers > old_num_mixers)
+	  {			/* Assume the mixer map is as suggested in the CS4232 spec */
+	    AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+	    AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+	    AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);	/* FM */
+	  }
+      }
+  }
+}
+
+void
+opti82C924_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+  int             opl3_driver, wss_driver;
+
+  if (pnp_trace)
+    printk ("OPTi 82C924 driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP WSS";
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x01;		/* MSS */
+  else
+    printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x02;		/* OPL3 */
+  else
+    printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+  /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             wss_base, opl3_base;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      wss_base = pnp_get_port (dev, 1);
+      opl3_base = pnp_get_port (dev, 2);
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      pnp_delay (2000000);
+
+      if (pnp_trace)
+	{
+	  printk ("I/O0 %03x\n", wss_base);
+	  printk ("I/O1 %03x\n", opl3_base);
+	  printk ("IRQ %d\n", irq);
+	  printk ("DMA0 %d\n", dma1);
+	  printk ("DMA1 %d\n", dma2);
+	}
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base + 8;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+
+	}
+
+      if (wss_base && wss_driver)
+	{
+	  hw_config.io_base = wss_base + 4;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (wss_driver, &hw_config);
+
+	}
+    }
+}
+
+void
+opl3sa2_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             opl3_driver, wss_driver, mpu_driver;
+
+  if (pnp_trace)
+    printk ("OPL3-SA2 driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP WSS";
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x02;		/* MSS */
+  else
+    printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x04;		/* OPL3 */
+  else
+    printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+  if ((mpu_driver = sndtable_identify_card ("UART401")))
+    portmask |= 0x08;		/* OPL3 */
+  else
+    printk ("Sound: PnP UART401 device detected but no driver enabled\n");
+
+  /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             wss_base, opl3_base, mpu_base;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      wss_base = pnp_get_port (dev, 1);
+      opl3_base = pnp_get_port (dev, 2);
+      mpu_base = pnp_get_port (dev, 3);
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      pnp_delay (1000000);
+
+      if (pnp_trace)
+	{
+	  printk ("I/O0 %03x\n", wss_base);
+	  printk ("I/O1 %03x\n", opl3_base);
+	  printk ("I/O3 %03x\n", mpu_base);
+	  printk ("IRQ %d\n", irq);
+	  printk ("DMA0 %d\n", dma1);
+	  printk ("DMA1 %d\n", dma2);
+	}
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+
+	}
+
+      if (wss_base && wss_driver)
+	{
+	  hw_config.io_base = wss_base + 4;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (wss_driver, &hw_config);
+
+	}
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+
+	}
+    }
+}
+
+static unsigned char
+C931_read (int base, int reg)
+{
+  unsigned char   data;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  outb ((0xE4), base);
+  outb ((reg), base + 2);
+  data = inb (base + 3);
+  restore_flags (flags);
+  return data;
+}
+
+static void
+C931_write (int base, int reg, unsigned char data)
+{
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  outb ((0xE4), base);
+  outb ((reg), base + 2);
+  outb ((data), base + 3);
+  restore_flags (flags);
+}
+
+void
+opti82C931_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+  int             opl3_driver, wss_driver;
+
+  if (pnp_trace)
+    printk ("OPTi 82C931 driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP WSS";
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x01;		/* MSS */
+  else
+    printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x02;		/* OPL3 */
+  else
+    printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
+
+  /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             wss_base, opl3_base, master_ctl;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      wss_base = pnp_get_port (dev, 0);
+      opl3_base = pnp_get_port (dev, 1);
+      master_ctl = pnp_get_port (dev, 3);
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      if (pnp_trace)
+	{
+	  int             i;
+
+	  printk ("I/O0 %03x\n", wss_base);
+	  printk ("I/O1 %03x\n", opl3_base);
+	  printk ("Master control port %x\n", master_ctl);
+	  for (i = 0; i < 4; i++)
+	    printk ("Port %x = %x\n", master_ctl + i, inb (master_ctl + i));
+	  printk ("IRQ %d\n", irq);
+	  printk ("DMA0 %d\n", dma1);
+	  printk ("DMA1 %d\n", dma2);
+	}
+      {
+	unsigned char   tmp;
+
+	tmp = C931_read (master_ctl, 5) | 0x20;		/* Enable codec registers I16 to I31 */
+	C931_write (master_ctl, 5, tmp);
+
+	tmp = 0x82;		/* MPU and WSS enabled, SB disabled */
+	C931_write (master_ctl, 6, tmp);
+      }
+
+      pnp_delay (2000000);
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base + 8;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+
+	}
+
+      if (wss_base && wss_driver)
+	{
+	  hw_config.io_base = wss_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (wss_driver, &hw_config);
+
+	  ad1848_control (AD1848_SET_C930_PWD, master_ctl);
+	}
+    }
+}
+
+void
+opti82C924mpu_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+  int             mpu_driver;
+
+  if (pnp_trace)
+    printk ("OPTi 82C924/C925/C931 MPU driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP MPU";
+
+  if ((mpu_driver = sndtable_identify_card ("UART401")))
+    portmask |= 0x01;		/* MPU401 */
+  else
+    printk ("Sound: PnP MPU device detected but no driver enabled\n");
+
+  /* printk ("MPU driver %d\n", mpu_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             mpu_base;
+      int             irq;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      mpu_base = pnp_get_port (dev, 0);
+      irq = pnp_get_irq (dev, 0);
+
+      pnp_delay (1000000);
+
+      if (pnp_trace)
+	{
+	  printk ("I/O %03x\n", mpu_base);
+	  printk ("IRQ %d\n", irq);
+	}
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+
+	}
+    }
+}
+
+void
+cs4236mpu_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
+  int             mpu_driver;
+
+  if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232)))	/* CS4232 */
+    return;
+
+  if (pnp_trace)
+    printk ("CS4236 MPU driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "PnP MPU";
+
+  if ((mpu_driver = sndtable_identify_card ("UART401")))
+    portmask |= 0x01;		/* MPU401 */
+  else
+    printk ("Sound: CS4236 PnP MPU device detected but no driver enabled\n");
+
+  /* printk ("MPU driver %d\n", mpu_driver); */
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             mpu_base;
+      int             irq;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      mpu_base = pnp_get_port (dev, 0);
+      irq = pnp_get_irq (dev, 0);
+
+      pnp_delay (1500000);
+
+      if (pnp_trace)
+	{
+	  printk ("I/O %03x\n", mpu_base);
+	  printk ("IRQ %d\n", irq);
+	}
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+
+	}
+    }
+}
+
+void
+soundscape_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0xff, irqmask = 0x03, dmamask = 0x01;
+  int             sscape_driver, wss_driver;
+
+  if (pnp_trace)
+    printk ("Soundscape PnP driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SoundScape PnP";
+
+  if ((sscape_driver = sndtable_identify_card ("SSCAPE")))
+    portmask |= 0x01;		/* MPU401 */
+  else
+    printk ("Sound: Soundscape PnP device detected but no driver enabled\n");
+
+  /* printk ("Soundscape driver %d\n", sscape_driver); */
+
+  if ((wss_driver = sndtable_identify_card ("SSCAPEMSS")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: Soundscape codec device detected but no driver enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             sscape_base;
+      int             irq, irq2, dma, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      sscape_base = pnp_get_port (dev, 0);
+      irq = pnp_get_irq (dev, 0);
+      irq2 = pnp_get_irq (dev, 1);
+      dma = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      pnp_delay (1000000);
+
+      if (pnp_trace)
+	{
+	  printk ("I/O %03x\n", sscape_base);
+	  printk ("IRQ %d\n", irq);
+	  printk ("IRQ2 %d\n", irq2);
+	  printk ("DMA %d\n", dma);
+	  printk ("DMA2 %d\n", dma2);
+	}
+
+      if (sscape_base && sscape_driver)
+	{
+	  hw_config.io_base = sscape_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma;
+	  hw_config.dma2 = dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0x12345678;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (sscape_driver, &hw_config);
+	}
+
+      if (sscape_base && wss_driver)
+	{
+	  hw_config.io_base = sscape_base + 8;	/* The codec base */
+	  hw_config.irq = irq2;
+	  hw_config.dma = dma;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (wss_driver, &hw_config);
+	  ad1848_control (AD1848_SET_XTAL, 1);	/* 14.3 MHz is used */
+	}
+    }
+}
+
+void
+soundscape_vivo (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x07, irqmask = 0x03, dmamask = 0x03;
+  int             mpu_driver, wss_driver, vivo_driver;
+  int             is_vivo_classic = 0;
+
+  if (pnp_trace)
+    printk ("Soundscape VIVO driver waking up\n");
+
+  if (dev->card->key == (PNP_DEVID ('E', 'N', 'S', 0x4080)))
+    is_vivo_classic = 1;
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SoundScape VIVO";
+
+  if ((mpu_driver = sndtable_identify_card ("UART401")))
+    portmask |= 0x01;		/* MPU401 */
+
+  /* printk ("MPU driver %d\n", mpu_driver); */
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x02;
+  else
+    printk ("Sound: Soundscape codec device detected but no driver enabled\n");
+
+  if ((vivo_driver = sndtable_identify_card ("VIVO")))
+    portmask |= 0x07;
+  else
+    printk ("Sound: Soundscape VIVO/OTTO device detected but no driver installed\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             mpu_base, mss_base, otto_base;
+      int             irq, irq2, dma, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      mpu_base = pnp_get_port (dev, 0);
+      mss_base = pnp_get_port (dev, 1);
+      otto_base = pnp_get_port (dev, 2);
+      irq = pnp_get_irq (dev, 0);
+      irq2 = pnp_get_irq (dev, 1);
+      dma = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+      if (dma2 == NO_DMA)
+	dma2 = dma;
+
+      if (pnp_trace)
+	{
+	  printk ("I/O %03x\n", mpu_base);
+	  printk ("MSS I/O %03x\n", mss_base);
+	  printk ("OTTO I/O %03x\n", otto_base);
+	  printk ("IRQ %d\n", irq);
+	  printk ("IRQ2 %d\n", irq2);
+	  printk ("DMA %d\n", dma);
+	  printk ("DMA2 %d\n", dma2);
+	}
+
+
+      if (mss_base && wss_driver)
+	{
+	  hw_config.io_base = mss_base + 4;	/* The codec base */
+	  hw_config.irq = irq;
+	  hw_config.dma = dma;
+	  hw_config.dma2 = dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (wss_driver, &hw_config);
+	}
+
+      if (otto_base && vivo_driver)
+	{
+	  hw_config.io_base = otto_base;
+	  hw_config.irq = irq2;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = mpu_base;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (vivo_driver, &hw_config);
+
+	  if (is_vivo_classic)
+	    {
+	      /*
+	       * The original VIVO uses XCTL0 pin of AD1845 as a synth (un)mute bit. Turn it
+	       * on _after_ the synth is initialized. Btw, XCTL1 controls 30 dB mic boost
+	       * circuit.
+	       */
+
+	      ad1848_control (AD1848_SET_XCTL0, 1);	/* Unmute */
+	    }
+	  AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH);	/* AUX1 is OTTO input */
+	  AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE);		/* Line in */
+
+	}
+    }
+}
+
+void
+gus_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             gus_driver, wss_driver;
+
+  if (pnp_trace)
+    printk ("GUS PnP driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "Ultrasound PnP";
+
+  if ((gus_driver = sndtable_identify_card ("GUSPNP")))
+    portmask |= 0x07;
+  else
+    printk ("Sound: GUS PnP device detected but no driver enabled\n");
+
+  if ((wss_driver = sndtable_identify_card ("AD1848")))
+    portmask |= 0x01;		/* MAX */
+  else
+    printk ("Sound: GUS PnP codec device detected but no driver enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             gus_base;
+      int             irq;
+      int             dma1, dma2;
+
+      gus_base = pnp_get_port (dev, 0);
+
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      if (pnp_trace)
+	printk ("Device activation OK (P%x I%d D%d d%d)\n",
+		gus_base, irq, dma1, dma2);
+
+      if (gus_base && gus_driver)
+	{
+
+	  hw_config.io_base = gus_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (gus_driver, &hw_config);
+	}
+    }
+}
+
+void
+sb_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             sb_driver, mpu_driver, opl3_driver;
+
+  if (pnp_trace)
+    printk ("SB PnP driver waking up\n");
+  pnp_delay (1000000);
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SoundBlaster PnP";
+
+  if ((sb_driver = sndtable_identify_card ("SBPNP")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: SB PnP device detected but no driver enabled\n");
+
+  if ((mpu_driver = sndtable_identify_card ("SBMPU")))
+    portmask |= 0x02;
+  else
+    printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x04;
+  else
+    printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             sb_base, mpu_base, opl3_base;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      sb_base = pnp_get_port (dev, 0);
+      mpu_base = pnp_get_port (dev, 1);
+      opl3_base = pnp_get_port (dev, 2);
+
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      dma2 = pnp_get_dma (dev, 1);
+
+      if (sb_base && sb_driver)
+	{
+	  hw_config.io_base = sb_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (sb_driver, &hw_config);
+	}
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+
+	}
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+
+	}
+    }
+}
+
+void
+als_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             sb_driver;
+
+  if (pnp_trace)
+    printk ("ALS### PnP driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SB16 clone";
+
+  if ((sb_driver = sndtable_identify_card ("SBPNP")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: ALS PnP device detected but no driver enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             sb_base;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      sb_base = pnp_get_port (dev, 0);
+
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 1);
+      dma2 = pnp_get_dma (dev, 0);
+
+      if (sb_base && sb_driver)
+	{
+	  hw_config.io_base = sb_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (sb_driver, &hw_config);
+	}
+    }
+}
+
+void
+als_pnp_mpu (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             mpu_driver;
+
+  if (pnp_trace)
+    printk ("ALS### PnP MPU driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SB16 clone";
+
+  if ((mpu_driver = sndtable_identify_card ("UART401")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: ALS PnP device detected but no MPU driver enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             mpu_base;
+      int             irq;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      mpu_base = pnp_get_port (dev, 0);
+
+      irq = pnp_get_irq (dev, 0);
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+	}
+    }
+}
+
+void
+als_pnp_opl (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
+  int             opl3_driver;
+
+  if (pnp_trace)
+    printk ("ALS### PnP OPL3 driver waking up\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "SB16 clone";
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: ALS PnP device detected but no OPL3 driver enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             opl3_base;
+      int             irq;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      opl3_base = pnp_get_port (dev, 0);
+
+      irq = pnp_get_irq (dev, 0);
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = sound_osp;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+	}
+    }
+}
+
+void
+ess_pnp (void *parm)
+{
+  struct pnp_dev *dev = (struct pnp_dev *) parm;
+  char           *name;
+
+  int             portmask = 0x03, irqmask = 0x01, dmamask = 0x03;
+  int             sb_driver, mpu_driver, opl3_driver;
+
+  if (pnp_trace)
+    printk ("ESS PnP driver waking up\n");
+
+  if (pnp_trace)
+    {
+      printk ("ESS1868: IRQB,IRQA = %x\n", pnp_readreg (dev, 0x20));
+      printk ("ESS1868: IRQD,IRQC = %x\n", pnp_readreg (dev, 0x21));
+      printk ("ESS1868: IRQF,IRQE = %x\n", pnp_readreg (dev, 0x22));
+      printk ("ESS1868: DRQB,DRQA = %x\n", pnp_readreg (dev, 0x23));
+      printk ("ESS1868: DRQD,DRQC = %x\n", pnp_readreg (dev, 0x24));
+      printk ("ESS1868: Configuration ROM Header 0 = %x\n", pnp_readreg (dev, 0x25));
+      printk ("ESS1868: Configuration ROM Header 1 = %x\n", pnp_readreg (dev, 0x26));
+      printk ("ESS1868: HW Volume IRQ = %x\n", pnp_readreg (dev, 0x27));
+      printk ("ESS1868: MPU401 IRQ = %x\n", pnp_readreg (dev, 0x28));
+    }
+
+  if (pnp_readreg (dev, 0x27) & 0x01)	/* MPU401 is at logical device #3 */
+    printk ("Nonstandard ESS1868 implementation - contact support@4front-tech.com\n");
+
+  if (dev->card && dev->card->name)
+    name = dev->card->name;
+  else
+    name = "ESS AudioDrive PnP";
+
+  if ((sb_driver = sndtable_identify_card ("SBLAST")))
+    portmask |= 0x01;
+  else
+    printk ("Sound: SB PnP device detected but no driver enabled\n");
+
+  if ((mpu_driver = sndtable_identify_card ("SBMPU")))
+    portmask |= 0x02;
+  else
+    printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
+
+  if ((opl3_driver = sndtable_identify_card ("OPL3")))
+    portmask |= 0x04;
+  else
+    printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
+
+  if (!portmask)		/* No drivers available */
+    return;
+
+  if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
+    printk ("sound_pnp: Failed to find free resources\n");
+  else
+    {
+      struct address_info hw_config;
+      int             sb_base, mpu_base, opl3_base;
+      int             irq;
+      int             dma1, dma2;
+
+      if (pnp_trace)
+	printk ("Device activation OK\n");
+      sb_base = pnp_get_port (dev, 0);
+      opl3_base = pnp_get_port (dev, 1);
+      mpu_base = pnp_get_port (dev, 2);
+
+      irq = pnp_get_irq (dev, 0);
+      dma1 = pnp_get_dma (dev, 0);
+      /* dma2 = pnp_get_dma (dev, 1); */ dma2 = -1;
+
+      if (pnp_trace)
+	{
+	  printk ("ESS PnP at %x/%x/%x, %d, %d/%d\n",
+		  sb_base, mpu_base, opl3_base,
+		  irq, dma1, dma2);
+	}
+
+      if (sb_base && sb_driver)
+	{
+	  hw_config.io_base = sb_base;
+	  hw_config.irq = irq;
+	  hw_config.dma = dma1;
+	  hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
+	  hw_config.always_detect = 0;
+	  hw_config.name = name;
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = NULL;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (sb_driver, &hw_config);
+	}
+
+      if (opl3_base && opl3_driver)
+	{
+	  hw_config.io_base = opl3_base;
+	  hw_config.irq = 0;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = NULL;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (opl3_driver, &hw_config);
+
+	}
+
+      if (mpu_base && mpu_driver)
+	{
+	  hw_config.io_base = mpu_base;
+	  hw_config.irq = -irq;
+	  hw_config.dma = -1;
+	  hw_config.dma2 = -1;
+	  hw_config.always_detect = 0;
+	  hw_config.name = "";
+	  hw_config.driver_use_1 = 0;
+	  hw_config.driver_use_2 = 0;
+	  hw_config.osp = NULL;
+	  hw_config.card_subtype = 0;
+
+	  sndtable_start_card (mpu_driver, &hw_config);
+
+	}
+    }
+}
+
+static struct pnp_sounddev pnp_devs[] =
+{
+  {PNP_DEVID ('C', 'S', 'C', 0x0000), cs4232_pnp, "CS4232"},
+  {PNP_DEVID ('C', 'S', 'C', 0x0003), cs4236mpu_pnp, "CS4236MPU"},
+  {PNP_DEVID ('G', 'R', 'V', 0x0000), gus_pnp, "GUS"},
+  {PNP_DEVID ('R', 'V', 'L', 0x0010), gus_pnp, "WAVXTREME"},
+  {PNP_DEVID ('A', 'D', 'V', 0x0010), gus_pnp, "IWAVE"},
+  {PNP_DEVID ('D', 'X', 'P', 0x0010), gus_pnp, "IWAVE"},
+  {PNP_DEVID ('Y', 'M', 'H', 0x0021), opl3sa2_pnp, "OPL3SA2"},
+  {PNP_DEVID ('O', 'P', 'T', 0x0000), opti82C924_pnp, "82C924"},
+  {PNP_DEVID ('O', 'P', 'T', 0x9250), opti82C924_pnp, "82C925"},
+  {PNP_DEVID ('O', 'P', 'T', 0x9310), opti82C931_pnp, "82C931"},
+  {PNP_DEVID ('O', 'P', 'T', 0x0002), opti82C924mpu_pnp, "82C924MPU"},
+  {PNP_DEVID ('E', 'N', 'S', 0x0000), soundscape_pnp, "SSCAPE"},
+  {PNP_DEVID ('N', 'E', 'C', 0x0000), soundscape_pnp, "NEC"},
+  {PNP_DEVID ('E', 'N', 'S', 0x1010), soundscape_vivo, "SSCAPE"},
+  {PNP_DEVID ('E', 'N', 'S', 0x1011), soundscape_vivo, "SSCAPE"},
+  {PNP_DEVID ('C', 'T', 'L', 0x0031), sb_pnp, "SB"},
+  {PNP_DEVID ('C', 'T', 'L', 0x0001), sb_pnp, "SB"},
+  {PNP_DEVID ('C', 'T', 'L', 0x0041), sb_pnp, "SB"},	/* SB32 (new revision) */
+  {PNP_DEVID ('C', 'T', 'L', 0x0042), sb_pnp, "SB"},	/* SB64 */
+  {PNP_DEVID ('C', 'T', 'L', 0x0044), sb_pnp, "SB"},	/* SB64 Gold */
+  {PNP_DEVID ('@', '@', '@', 0x0001), als_pnp, "SB"},
+  {PNP_DEVID ('@', 'X', '@', 0x0001), als_pnp_mpu, "SB"},
+  {PNP_DEVID ('@', 'H', '@', 0x0001), als_pnp_opl, "SB"},
+  {PNP_DEVID ('E', 'S', 'S', 0x1868), ess_pnp, "ESS"}
+};
+
+static int      nr_pnpdevs = sizeof (pnp_devs) / sizeof (struct pnp_sounddev);
+
+static int
+pnp_activate (int id, struct pnp_dev *dev)
+{
+  int             i;
+
+  for (i = 0; i < nr_pnpdevs; i++)
+    if (pnp_devs[i].id == id)
+      {
+
+	if (pnp_trace)
+	  printk ("PnP dev: %08x, %s\n", id,
+		  pnp_devid2asc (id));
+
+	pnp_devs[i].setup ((void *) dev);
+	return 1;
+      }
+
+  return 0;
+}
+
+void
+cs423x_special (struct pnp_dev *dev)
+{
+}
+
+void
+sound_pnp_init (int *osp)
+{
+
+  struct pnp_dev *dev;
+
+  if (pnp_sig == 0)
+    init_pnp (0, osp);
+
+  if (pnp_sig == 0)
+    if ((pnp_sig = pnp_connect ("sound")) == -1)
+      {
+	printk ("Sound: Can't connect to kernel PnP services.\n");
+	return;
+      }
+
+/*
+ * First handle some special configuration ports.
+ */
+  dev = NULL;
+  while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+    {
+      int             i;
+
+      for (i = 0; special_devices[i] != 0; i++)
+	if (special_devices[i] == dev->key)
+	  switch (i)
+	    {
+	    case 0:
+	    case 1:
+	      cs423x_special (dev);
+	      break;
+	    }
+    }
+
+/*
+ * Next disable some unused sound devices so that they don't consume
+ * valuable IRQ and DMA resources.
+ */
+  dev = NULL;
+  while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+    {
+      int             i;
+
+      for (i = 0; disabled_devices[i] != 0; i++)
+	if (disabled_devices[i] == dev->key)
+	  pnp_enable_device (dev, 0);	/* Disable it */
+    }
+
+/*
+ * Then initialize drivers for known PnP devices.
+ */
+  dev = NULL;
+  while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
+    {
+      if (!pnp_activate (dev->key, dev))
+	{
+	  /* Scan all compatible devices */
+
+	  int             i;
+
+	  for (i = 0; i < dev->ncompat; i++)
+	    if (pnp_activate (dev->compat_keys[i], dev))
+	      break;
+	}
+    }
+}
+
+void
+sound_pnp_disconnect (void)
+{
+  pnp_disconnect (pnp_sig);
+}
+
+
+#endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v2.1.40/linux/drivers/sound/sound_switch.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sound_switch.c	Sun May 11 02:59:26 1997
@@ -16,6 +16,7 @@
 #include "sound_config.h"
 
 static int      in_use = 0;	/* Total # of open devices */
+unsigned long   seq_time = 0;	/* Time for /dev/sequencer */
 
 /*
  * Table for configurable mixer volume handling
@@ -167,17 +168,22 @@
   status_ptr = 0;
 
 #ifdef SOUND_UNAME_A
-  put_status ("Sound Driver:" SOUND_VERSION_STRING
+  put_status ("OSS/Free" SOUND_VERSION_STRING
 	      " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n"
 	      SOUND_UNAME_A ")"
 	      "\n");
 #else
-  put_status ("Sound Driver:" SOUND_VERSION_STRING
+  put_status ("OSS/Free:" SOUND_VERSION_STRING
 	      " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
 	      SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
 	      "\n");
 #endif
 
+#ifdef MODULE
+  put_status ("Load type: Driver loaded as a module.\n");
+#else
+  put_status ("Load type: Driver compiled into kernel\n");
+#endif
   put_status ("Kernel: ");
   put_status (system_utsname.sysname);
   put_status (" ");
@@ -189,6 +195,9 @@
   put_status (" ");
   put_status (system_utsname.machine);
   put_status ("\n");
+#ifdef MODULE
+  put_status ("Driver loaded as a module\n");
+#endif
 
 
   if (!put_status ("Config options: "))
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.40/linux/drivers/sound/soundcard.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/sound/soundcard.c	Sun May 11 02:59:27 1997
@@ -139,8 +139,8 @@
   sound_release_sw (dev, &files[dev]);
 #ifdef MODULE
   MOD_DEC_USE_COUNT;
-#endif
   return 0;
+#endif
 }
 
 static int
@@ -156,7 +156,7 @@
 
   files[dev].flags = file->f_flags;
 
-  if (_SIOC_DIR (cmd) != _SIOC_NONE)
+  if (_SIOC_DIR (cmd) != _SIOC_NONE && _SIOC_DIR (cmd) != 0)
     {
       /*
          * Have to validate the address given by the process.
@@ -196,7 +196,7 @@
   if (ptr != NULL && alloced)
     vfree (ptr);
 
-  return err;
+  return ((err < 0) ? err : 0);
 }
 
 static int
@@ -229,7 +229,7 @@
     case SND_DEV_DSP:
     case SND_DEV_DSP16:
     case SND_DEV_AUDIO:
-      return audio_select (dev, &files[dev], sel_type, wait);
+      return DMAbuf_select (dev >> 4, &files[dev], sel_type, wait);
       break;
 #endif
 
@@ -275,20 +275,14 @@
       return -EINVAL;
     }
 
-  if ((vma->vm_flags & (VM_READ | VM_WRITE)) == (VM_READ | VM_WRITE))
+  if (vma->vm_flags & VM_WRITE)	/* Map write and read/write to the output buf */
     {
-      printk ("Sound: Cannot do read/write mmap()\n");
-      return -EINVAL;
+      dmap = audio_devs[dev]->dmap_out;
     }
-
-  if (vma->vm_flags & VM_READ)
+  else if (vma->vm_flags & VM_READ)
     {
       dmap = audio_devs[dev]->dmap_in;
     }
-  else if (vma->vm_flags & VM_WRITE)
-    {
-      dmap = audio_devs[dev]->dmap_out;
-    }
   else
     {
       printk ("Sound: Undefined mmap() access\n");
@@ -379,20 +373,10 @@
 #ifdef CONFIG_AUDIO
   if (num_audiodevs)		/* Audio devices present */
     {
-      DMAbuf_init ();
       audio_init_devices ();
     }
 #endif
 
-#ifdef CONFIG_MIDI
-  if (num_midis)
-    MIDIbuf_init ();
-#endif
-
-#ifdef CONFIG_SEQUENCER
-  if (num_midis + num_synths)
-    sequencer_init ();
-#endif
 
 }
 
@@ -616,9 +600,15 @@
 
 #ifdef CONFIG_SEQUENCER
 
+static void
+do_sequencer_timer (unsigned long dummy)
+{
+  sequencer_timer (0);
+}
+
 
 static struct timer_list seq_timer =
-{NULL, NULL, 0, 0, sequencer_timer};
+{NULL, NULL, 0, 0, do_sequencer_timer};
 
 void
 request_sound_timer (int count)
@@ -681,33 +671,31 @@
 
   dmap->raw_buf = NULL;
 
-  if (debugmem)
-    printk ("sound: buffsize[%d] = %lu\n", dev, audio_devs[dev]->buffsize);
-
-  audio_devs[dev]->buffsize = dma_buffsize;
+  dmap->buffsize = dma_buffsize;
 
-  if (audio_devs[dev]->buffsize > dma_pagesize)
-    audio_devs[dev]->buffsize = dma_pagesize;
+  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.
+ * it fails. Don't accept smaller than 8k buffer for performance
+ * reasons.
  */
 
-  while (start_addr == NULL && audio_devs[dev]->buffsize > PAGE_SIZE)
+  while (start_addr == NULL && dmap->buffsize > PAGE_SIZE)
     {
       int             sz, size;
 
       for (sz = 0, size = PAGE_SIZE;
-	   size < audio_devs[dev]->buffsize;
+	   size < dmap->buffsize;
 	   sz++, size <<= 1);
 
-      audio_devs[dev]->buffsize = PAGE_SIZE * (1 << sz);
+      dmap->buffsize = PAGE_SIZE * (1 << sz);
 
       if ((start_addr = (char *) __get_free_pages (GFP_ATOMIC, sz, MAX_DMA_ADDRESS)) == NULL)
-	audio_devs[dev]->buffsize /= 2;
+	dmap->buffsize /= 2;
     }
 
   if (start_addr == NULL)
@@ -718,7 +706,7 @@
   else
     {
       /* make some checks */
-      end_addr = start_addr + audio_devs[dev]->buffsize - 1;
+      end_addr = start_addr + dmap->buffsize - 1;
 
       if (debugmem)
 	printk ("sound: start 0x%lx, end 0x%lx\n",
@@ -731,9 +719,9 @@
 	  || end_addr >= (char *) (MAX_DMA_ADDRESS))
 	{
 	  printk (
-		   "sound: Got invalid address 0x%lx for %ldb DMA-buffer\n",
+		   "sound: Got invalid address 0x%lx for %db DMA-buffer\n",
 		   (long) start_addr,
-		   audio_devs[dev]->buffsize);
+		   dmap->buffsize);
 	  return -EFAULT;
 	}
     }
@@ -761,11 +749,11 @@
     return;			/* Don't free mmapped buffer. Will use it next time */
 
   for (sz = 0, size = PAGE_SIZE;
-       size < audio_devs[dev]->buffsize;
+       size < dmap->buffsize;
        sz++, size <<= 1);
 
   start_addr = (unsigned long) dmap->raw_buf;
-  end_addr = start_addr + audio_devs[dev]->buffsize;
+  end_addr = start_addr + dmap->buffsize;
 
   for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++)
     {
@@ -776,6 +764,33 @@
   dmap->raw_buf = NULL;
 }
 
+
+/* Intel version !!!!!!!!! */
+int 
+sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
+		 unsigned long physaddr,
+		 int count, int dma_mode, int autoinit)
+{
+  unsigned long   flags;
+
+  /* printk("Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
+  if (autoinit)
+    dma_mode |= DMA_AUTOINIT;
+  save_flags (flags);
+  cli ();
+  disable_dma (chan);
+  clear_dma_ff (chan);
+  set_dma_mode (chan, dma_mode);
+  set_dma_addr (chan, physaddr);
+  set_dma_count (chan, count);
+  enable_dma (chan);
+  restore_flags (flags);
+
+  return 0;
+}
+
+#endif
+
 void
 conf_printf (char *name, struct address_info *hw_config)
 {
@@ -817,29 +832,3 @@
 
   printk ("\n");
 }
-
-/* Intel version !!!!!!!!! */
-int 
-sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
-		 unsigned long physaddr,
-		 int count, int dma_mode, int autoinit)
-{
-  unsigned long   flags;
-
-/* printk("Start DMA %d, %d\n", (int)(physaddr-dmap->raw_buf_phys), count); */
-  if (autoinit)
-    dma_mode |= DMA_AUTOINIT;
-  save_flags (flags);
-  cli ();
-  disable_dma (chan);
-  clear_dma_ff (chan);
-  set_dma_mode (chan, dma_mode);
-  set_dma_addr (chan, physaddr);
-  set_dma_count (chan, count);
-  enable_dma (chan);
-  restore_flags (flags);
-
-  return 0;
-}
-
-#endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/soundvers.h linux/drivers/sound/soundvers.h
--- v2.1.40/linux/drivers/sound/soundvers.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/soundvers.h	Sun May 11 03:16:26 1997
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.8-beta9-970226"
-#define SOUND_INTERNAL_VERSION 0x030803
+#define SOUND_VERSION_STRING "3.8a"
+#define SOUND_INTERNAL_VERSION 0x030804
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v2.1.40/linux/drivers/sound/sscape.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sscape.c	Sun May 11 02:59:28 1997
@@ -59,16 +59,16 @@
 #define CMD_GEN_HOST_ACK        0x80
 #define CMD_GEN_MPU_ACK         0x81
 #define CMD_GET_BOARD_TYPE      0x82
-#define CMD_SET_CONTROL         0x88
-#define CMD_GET_CONTROL         0x89
+#define CMD_SET_CONTROL         0x88	/* Old firmware only */
+#define CMD_GET_CONTROL         0x89	/* Old firmware only */
 #define 	CTL_MASTER_VOL          0
 #define 	CTL_MIC_MODE            2
 #define 	CTL_SYNTH_VOL           4
 #define 	CTL_WAVE_VOL            7
-#define CMD_SET_MT32            0x96
-#define CMD_GET_MT32            0x97
-#define CMD_SET_EXTMIDI         0x9b
-#define CMD_GET_EXTMIDI         0x9c
+#define CMD_SET_EXTMIDI		0x8a
+#define CMD_GET_EXTMIDI		0x8b
+#define CMD_SET_MT32            0x8c
+#define CMD_GET_MT32            0x8d
 
 #define CMD_ACK			0x80
 
@@ -78,15 +78,16 @@
     int             ok;		/* Properly detected */
     int             failed;
     int             dma_allocated;
-    int             my_audiodev;
+    int             codec_audiodev;
     int             opened;
     int            *osp;
   }
-
 sscape_info;
-static struct sscape_info dev_info =
+
+static struct sscape_info adev_info =
 {0};
-static struct sscape_info *devc = &dev_info;
+static struct sscape_info *devc = &adev_info;
+static int      sscape_mididev = -1;
 
 static struct wait_queue *sscape_sleeper = NULL;
 static volatile struct snd_wait sscape_sleep_flag =
@@ -270,19 +271,7 @@
   host_close (devc);
 }
 
-static int
-get_board_type (struct sscape_info *devc)
-{
-  int             tmp;
 
-  host_open (devc);
-  if (!host_command1 (devc, CMD_GET_BOARD_TYPE))
-    tmp = -1;
-  else
-    tmp = host_read (devc);
-  host_close (devc);
-  return tmp;
-}
 
 
 
@@ -297,11 +286,11 @@
       return;
     }
 
-  audio_devs[devc->my_audiodev]->flags &= ~DMA_AUTOMODE;
-  DMAbuf_start_dma (devc->my_audiodev,
+  audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
+  DMAbuf_start_dma (devc->codec_audiodev,
 		    buf,
 		    blk_size, mode);
-  audio_devs[devc->my_audiodev]->flags |= DMA_AUTOMODE;
+  audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
 
   temp = devc->dma << 4;	/* Setup DMA channel select bits */
   if (devc->dma <= 3)
@@ -384,7 +373,7 @@
 {
   unsigned long   flags;
   unsigned char   temp;
-  int             done, timeout_val;
+  volatile int    done, timeout_val;
   static unsigned char codec_dma_bits = 0;
 
   if (flag & CPF_FIRST)
@@ -418,13 +407,19 @@
   /*
    * Transfer one code block using DMA
    */
-  memcpy (audio_devs[devc->my_audiodev]->dmap_out->raw_buf, block, size);
+  if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
+    {
+      printk ("SSCAPE: Error: DMA buffer not available\n");
+      return 0;
+    }
+
+  memcpy (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
 
   save_flags (flags);
   cli ();
 /******** INTERRUPTS DISABLED NOW ********/
   do_dma (devc, SSCAPE_DMA_A,
-	  audio_devs[devc->my_audiodev]->dmap_out->raw_buf_phys,
+	  audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
 	  size, DMA_MODE_WRITE);
 
   /*
@@ -432,17 +427,16 @@
    */
   sscape_sleep_flag.opts = WK_NONE;
   done = 0;
-  timeout_val = 100;
+  timeout_val = 30;
   while (!done && timeout_val-- > 0)
     {
       int             resid;
 
-
       {
 	unsigned long   tlimit;
 
-	if (1)
-	  current->timeout = tlimit = jiffies + (1);
+	if (HZ / 50)
+	  current->timeout = tlimit = jiffies + (HZ / 50);
 	else
 	  tlimit = (unsigned long) -1;
 	sscape_sleep_flag.opts = WK_SLEEP;
@@ -509,7 +503,7 @@
 	  x = inb (PORT (HOST_DATA));
 	  if (x == 0xff || x == 0xfe)	/* OBP startup acknowledge */
 	    {
-	      printk ("Soundscape: Acknowledge = %x\n", x);
+	      DDB (printk ("Soundscape: Acknowledge = %x\n", x));
 	      done = 1;
 	    }
 	}
@@ -555,9 +549,7 @@
 	  return 0;
 	}
 
-      printk ("SoundScape board of type %d initialized OK\n",
-	      get_board_type (devc));
-
+      printk ("SoundScape board initialized OK\n");
       set_control (devc, CTL_MASTER_VOL, 100);
       set_control (devc, CTL_SYNTH_VOL, 100);
 
@@ -633,7 +625,7 @@
   sscape_coproc_close,
   sscape_coproc_ioctl,
   sscape_coproc_reset,
-  &dev_info
+  &adev_info
 };
 
 static int      sscape_detected = 0;
@@ -678,6 +670,7 @@
   if (sscape_detected != hw_config->io_base)
     return;
 
+  request_region (devc->base + 2, 6, "SoundScape");
   if (old_hardware)
     {
       valid_interrupts = valid_interrupts_old;
@@ -765,13 +758,17 @@
     hw_config->irq *= -1;	/* Restore it */
 
     if (num_midis == (prev_devs + 1))	/* The MPU driver installed itself */
-      midi_devs[prev_devs]->coproc = &sscape_coproc_operations;
+      {
+	sscape_mididev = prev_devs;
+	midi_devs[prev_devs]->coproc = &sscape_coproc_operations;
+      }
   }
 #endif
 
   sscape_write (devc, GA_INTENA_REG, 0x80);	/* Master IRQ enable */
   devc->ok = 1;
   devc->failed = 0;
+
 }
 
 static int
@@ -841,17 +838,13 @@
 probe_sscape (struct address_info *hw_config)
 {
 
-  devc->base = hw_config->io_base;
-  devc->irq = hw_config->irq;
-  devc->dma = hw_config->dma;
-
   if (sscape_detected != 0 && sscape_detected != hw_config->io_base)
     return 0;
 
-  devc->failed = 1;
-
-  if (!detect_ga (devc))
-    return 0;
+  devc->base = hw_config->io_base;
+  devc->irq = hw_config->irq;
+  devc->dma = hw_config->dma;
+  devc->osp = hw_config->osp;
 
 #ifdef SSCAPE_DEBUG1
   /*
@@ -866,6 +859,12 @@
   }
 #endif
 
+
+  devc->failed = 1;
+
+  if (!detect_ga (devc))
+    return 0;
+
   if (old_hardware)		/* Check that it's really an old Spea/Reveal card. */
     {
       unsigned char   tmp;
@@ -877,8 +876,6 @@
 	  for (cc = 0; cc < 200000; ++cc)
 	    inb (devc->base + ODIE_ADDR);
 	}
-      else
-	old_hardware = 0;
     }
 
 
@@ -891,6 +888,7 @@
 probe_ss_ms_sound (struct address_info *hw_config)
 {
   int             i, irq_bits = 0xff;
+  int             ad_flags = 0;
 
   if (devc->failed)
     {
@@ -917,7 +915,9 @@
     }
 
 
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
+  if (old_hardware)
+    ad_flags = 0x12345677;	/* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
+  return ad1848_detect (hw_config->io_base, &ad_flags, hw_config->osp);
 }
 
 void
@@ -970,8 +970,13 @@
 	       devc->osp);
 
   if (num_audiodevs == (prev_devs + 1))		/* The AD1848 driver installed itself */
-    audio_devs[prev_devs]->coproc = &sscape_coproc_operations;
-  devc->my_audiodev = prev_devs;
+    {
+      audio_devs[prev_devs]->coproc = &sscape_coproc_operations;
+      devc->codec_audiodev = prev_devs;
+
+      /* Set proper routings here (what are they) */
+      AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+    }
 
 #ifdef SSCAPE_DEBUG5
   /*
@@ -991,10 +996,10 @@
 void
 unload_sscape (struct address_info *hw_config)
 {
+  release_region (devc->base + 2, 6);
 #if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI)
   unload_mpu401 (hw_config);
 #endif
-  snd_release_irq (hw_config->irq);
 }
 
 void
@@ -1006,6 +1011,7 @@
 		 devc->dma,
 		 0);
 }
+
 
 
 #endif
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/trix.c linux/drivers/sound/trix.c
--- v2.1.40/linux/drivers/sound/trix.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/trix.c	Sun May 11 02:59:29 1997
@@ -339,9 +339,19 @@
 void
 attach_trix_sb (struct address_info *hw_config)
 {
+  extern int      sb_be_quiet;
+  int             old_quiet;
+
 #ifdef CONFIG_SBDSP
   hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
+
+  /* Prevent false alarms */
+  old_quiet = sb_be_quiet;
+  sb_be_quiet = 1;
+
   sb_dsp_init (hw_config);
+
+  sb_be_quiet = old_quiet;
 #endif
 }
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.40/linux/drivers/sound/uart401.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/uart401.c	Sun May 11 02:59:30 1997
@@ -346,6 +346,7 @@
   strcpy (midi_devs[num_midis]->info.name, name);
   midi_devs[num_midis]->converter->id = "UART401";
   num_midis++;
+  sequencer_init ();
   devc->opened = 0;
 }
 
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v2.1.40/linux/drivers/sound/uart6850.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/uart6850.c	Sun May 11 02:59:30 1997
@@ -294,6 +294,7 @@
 
   std_midi_synth.midi_dev = my_dev = num_midis;
   midi_devs[num_midis++] = &uart6850_operations;
+  sequencer_init ();
 }
 
 static int
diff -u --recursive --new-file v2.1.40/linux/drivers/sound/ulaw.h linux/drivers/sound/ulaw.h
--- v2.1.40/linux/drivers/sound/ulaw.h	Fri Nov 15 00:14:56 1996
+++ linux/drivers/sound/ulaw.h	Sun May 11 02:58:39 1997
@@ -1,69 +1,69 @@
 static unsigned char ulaw_dsp[] = {
-     0,    0,    0,    0,    0,    0,    0,    0, 
-     0,    0,    0,    0,    0,    0,    0,    0, 
-     0,    0,    0,    0,    0,    0,    0,    0, 
-     0,    0,    0,    0,    0,    0,    0,    2, 
-     5,    9,   13,   17,   21,   25,   29,   33, 
-    37,   41,   45,   49,   53,   57,   61,   65, 
-    68,   70,   72,   74,   76,   78,   80,   82, 
-    84,   86,   88,   90,   92,   94,   96,   98, 
-   100,  101,  102,  103,  104,  105,  106,  107, 
-   108,  109,  110,  111,  112,  113,  114,  115, 
-   115,  116,  116,  117,  117,  118,  118,  119, 
-   119,  120,  120,  121,  121,  122,  122,  123, 
-   123,  123,  124,  124,  124,  124,  125,  125, 
-   125,  125,  126,  126,  126,  126,  127,  127, 
-   127,  127,  127,  127,  128,  128,  128,  128, 
-   128,  128,  128,  128,  128,  128,  128,  128, 
-   255,  255,  255,  255,  255,  255,  255,  255, 
-   255,  255,  255,  255,  255,  255,  255,  255, 
-   255,  255,  255,  255,  255,  255,  255,  255, 
-   255,  255,  255,  255,  255,  255,  255,  255, 
-   252,  248,  244,  240,  236,  232,  228,  224, 
-   220,  216,  212,  208,  204,  200,  196,  192, 
-   189,  187,  185,  183,  181,  179,  177,  175, 
-   173,  171,  169,  167,  165,  163,  161,  159, 
-   157,  156,  155,  154,  153,  152,  151,  150, 
-   149,  148,  147,  146,  145,  144,  143,  142, 
-   142,  141,  141,  140,  140,  139,  139,  138, 
-   138,  137,  137,  136,  136,  135,  135,  134, 
-   134,  134,  133,  133,  133,  133,  132,  132, 
-   132,  132,  131,  131,  131,  131,  130,  130, 
-   130,  130,  130,  130,  129,  129,  129,  129, 
-   129,  129,  129,  129,  128,  128,  128,  128, 
+     3,    7,   11,   15,   19,   23,   27,   31,
+    35,   39,   43,   47,   51,   55,   59,   63,
+    66,   68,   70,   72,   74,   76,   78,   80,
+    82,   84,   86,   88,   90,   92,   94,   96,
+    98,   99,  100,  101,  102,  103,  104,  105,
+   106,  107,  108,  109,  110,  111,  112,  113,
+   113,  114,  114,  115,  115,  116,  116,  117,
+   117,  118,  118,  119,  119,  120,  120,  121,
+   121,  121,  122,  122,  122,  122,  123,  123,
+   123,  123,  124,  124,  124,  124,  125,  125,
+   125,  125,  125,  125,  126,  126,  126,  126,
+   126,  126,  126,  126,  127,  127,  127,  127,
+   127,  127,  127,  127,  127,  127,  127,  127,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   253,  249,  245,  241,  237,  233,  229,  225,
+   221,  217,  213,  209,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   174,  172,  170,  168,  166,  164,  162,  160,
+   158,  157,  156,  155,  154,  153,  152,  151,
+   150,  149,  148,  147,  146,  145,  144,  143,
+   143,  142,  142,  141,  141,  140,  140,  139,
+   139,  138,  138,  137,  137,  136,  136,  135,
+   135,  135,  134,  134,  134,  134,  133,  133,
+   133,  133,  132,  132,  132,  132,  131,  131,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   130,  130,  130,  130,  129,  129,  129,  129,
+   129,  129,  129,  129,  129,  129,  129,  129,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
+   128,  128,  128,  128,  128,  128,  128,  128,
 };
 
 static unsigned char dsp_ulaw[] = {
-    31,   31,   31,   32,   32,   32,   32,   33, 
-    33,   33,   33,   34,   34,   34,   34,   35, 
-    35,   35,   35,   36,   36,   36,   36,   37, 
-    37,   37,   37,   38,   38,   38,   38,   39, 
-    39,   39,   39,   40,   40,   40,   40,   41, 
-    41,   41,   41,   42,   42,   42,   42,   43, 
-    43,   43,   43,   44,   44,   44,   44,   45, 
-    45,   45,   45,   46,   46,   46,   46,   47, 
-    47,   47,   47,   48,   48,   49,   49,   50, 
-    50,   51,   51,   52,   52,   53,   53,   54, 
-    54,   55,   55,   56,   56,   57,   57,   58, 
-    58,   59,   59,   60,   60,   61,   61,   62, 
-    62,   63,   63,   64,   65,   66,   67,   68, 
-    69,   70,   71,   72,   73,   74,   75,   76, 
-    77,   78,   79,   81,   83,   85,   87,   89, 
-    91,   93,   95,   99,  103,  107,  111,  119, 
-   255,  247,  239,  235,  231,  227,  223,  221, 
-   219,  217,  215,  213,  211,  209,  207,  206, 
-   205,  204,  203,  202,  201,  200,  199,  198, 
-   197,  196,  195,  194,  193,  192,  191,  191, 
-   190,  190,  189,  189,  188,  188,  187,  187, 
-   186,  186,  185,  185,  184,  184,  183,  183, 
-   182,  182,  181,  181,  180,  180,  179,  179, 
-   178,  178,  177,  177,  176,  176,  175,  175, 
-   175,  175,  174,  174,  174,  174,  173,  173, 
-   173,  173,  172,  172,  172,  172,  171,  171, 
-   171,  171,  170,  170,  170,  170,  169,  169, 
-   169,  169,  168,  168,  168,  168,  167,  167, 
-   167,  167,  166,  166,  166,  166,  165,  165, 
-   165,  165,  164,  164,  164,  164,  163,  163, 
-   163,  163,  162,  162,  162,  162,  161,  161, 
-   161,  161,  160,  160,  160,  160,  159,  159, 
+     0,    0,    0,    0,    0,    1,    1,    1,
+     1,    2,    2,    2,    2,    3,    3,    3,
+     3,    4,    4,    4,    4,    5,    5,    5,
+     5,    6,    6,    6,    6,    7,    7,    7,
+     7,    8,    8,    8,    8,    9,    9,    9,
+     9,   10,   10,   10,   10,   11,   11,   11,
+    11,   12,   12,   12,   12,   13,   13,   13,
+    13,   14,   14,   14,   14,   15,   15,   15,
+    15,   16,   16,   17,   17,   18,   18,   19,
+    19,   20,   20,   21,   21,   22,   22,   23,
+    23,   24,   24,   25,   25,   26,   26,   27,
+    27,   28,   28,   29,   29,   30,   30,   31,
+    31,   32,   33,   34,   35,   36,   37,   38,
+    39,   40,   41,   42,   43,   44,   45,   46,
+    47,   49,   51,   53,   55,   57,   59,   61,
+    63,   66,   70,   74,   78,   84,   92,  104,
+   254,  231,  219,  211,  205,  201,  197,  193,
+   190,  188,  186,  184,  182,  180,  178,  176,
+   175,  174,  173,  172,  171,  170,  169,  168,
+   167,  166,  165,  164,  163,  162,  161,  160,
+   159,  159,  158,  158,  157,  157,  156,  156,
+   155,  155,  154,  154,  153,  153,  152,  152,
+   151,  151,  150,  150,  149,  149,  148,  148,
+   147,  147,  146,  146,  145,  145,  144,  144,
+   143,  143,  143,  143,  142,  142,  142,  142,
+   141,  141,  141,  141,  140,  140,  140,  140,
+   139,  139,  139,  139,  138,  138,  138,  138,
+   137,  137,  137,  137,  136,  136,  136,  136,
+   135,  135,  135,  135,  134,  134,  134,  134,
+   133,  133,  133,  133,  132,  132,  132,  132,
+   131,  131,  131,  131,  130,  130,  130,  130,
+   129,  129,  129,  129,  128,  128,  128,  128,
 };
diff -u --recursive --new-file v2.1.40/linux/fs/autofs/autofs_i.h linux/fs/autofs/autofs_i.h
--- v2.1.40/linux/fs/autofs/autofs_i.h	Thu May 15 16:48:03 1997
+++ linux/fs/autofs/autofs_i.h	Tue May 27 15:23:46 1997
@@ -12,6 +12,8 @@
 
 /* Internal header file for autofs */
 
+#define DEBUG_WAITLIST 1
+
 #include <linux/auto_fs.h>
 
 /* This is the range of ioctl() numbers we claim as ours */
@@ -120,7 +122,10 @@
 #define END_OF_TIME ((time_t)((unsigned long)((time_t)(~0UL)) >> 1))
 #endif
 
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
 struct autofs_sb_info {
+	u32 magic;
 	struct file *pipe;
 	pid_t oz_pgrp;
 	int catatonic;
@@ -136,6 +141,15 @@
 static inline int autofs_oz_mode(struct autofs_sb_info *sbi) {
 	return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
 }
+
+/* Debug the mysteriously disappearing wait list */
+
+#ifdef DEBUG_WAITLIST
+#define CHECK_WAITLIST(S,O) autofs_check_waitlist_integrity(S,O)
+void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *);
+#else
+#define CHECK_WAITLIST(S,O)
+#endif
 
 /* Hash operations */
 
diff -u --recursive --new-file v2.1.40/linux/fs/autofs/inode.c linux/fs/autofs/inode.c
--- v2.1.40/linux/fs/autofs/inode.c	Tue May 13 22:41:14 1997
+++ linux/fs/autofs/inode.c	Tue May 27 15:20:09 1997
@@ -150,6 +150,7 @@
 	DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
 
 	s->u.generic_sbp = sbi;
+	sbi->magic = AUTOFS_SBI_MAGIC;
 	sbi->catatonic = 0;
 	sbi->exp_timeout = 0;
 	sbi->oz_pgrp = current->pgrp;
diff -u --recursive --new-file v2.1.40/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- v2.1.40/linux/fs/autofs/root.c	Sat May 24 09:10:24 1997
+++ linux/fs/autofs/root.c	Tue May 27 15:20:09 1997
@@ -190,30 +190,33 @@
 	DPRINTK(("autofs_root_symlink: %s <- ", symname));
 	autofs_say(name,len);
 
-	iput(dir);
-	
-	if ( !autofs_oz_mode(sbi) )
+	if ( !autofs_oz_mode(sbi) ) {
+		iput(dir);
 		return -EPERM;
-
-	if ( autofs_hash_lookup(dh,hash,name,len) )
+	}
+	if ( autofs_hash_lookup(dh,hash,name,len) ) {
+		iput(dir);
 		return -EEXIST;
-
+	}
 	n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
-	if ( n >= AUTOFS_MAX_SYMLINKS )
+	if ( n >= AUTOFS_MAX_SYMLINKS ) {
+		iput(dir);
 		return -ENOSPC;
-
+	}
 	set_bit(n,sbi->symlink_bitmap);
 	sl = &sbi->symlink[n];
 	sl->len = strlen(symname);
 	sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
 	if ( !sl->data ) {
 		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
 		return -ENOSPC;
 	}
 	ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
 	if ( !ent ) {
 		kfree(sl->data);
 		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
 		return -ENOSPC;
 	}
 	ent->name = kmalloc(len, GFP_KERNEL);
@@ -221,6 +224,7 @@
 		kfree(sl->data);
 		kfree(ent);
 		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
 		return -ENOSPC;
 	}
 	memcpy(sl->data,symname,slsize);
@@ -231,6 +235,7 @@
 	memcpy(ent->name,name,ent->len = len);
 
 	autofs_hash_insert(dh,ent);
+	iput(dir);
 
 	return 0;
 }
@@ -243,15 +248,19 @@
 	struct autofs_dir_ent *ent;
 	unsigned int n;
 
+	iput(dir);		/* Nothing below can sleep */
+
 	if ( !autofs_oz_mode(sbi) )
 		return -EPERM;
 
 	ent = autofs_hash_lookup(dh,hash,name,len);
 	if ( !ent )
 		return -ENOENT;
+
 	n = ent->ino - AUTOFS_FIRST_SYMLINK;
 	if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
 		return -EINVAL;	/* Not a symlink inode, can't unlink */
+
 	autofs_hash_delete(ent);
 	clear_bit(n,sbi->symlink_bitmap);
 	kfree(sbi->symlink[n].data);
diff -u --recursive --new-file v2.1.40/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c
--- v2.1.40/linux/fs/autofs/waitq.c	Sat May 24 09:10:24 1997
+++ linux/fs/autofs/waitq.c	Tue May 27 15:20:09 1997
@@ -16,6 +16,46 @@
 #include <linux/file.h>
 #include "autofs_i.h"
 
+#ifdef DEBUG_WAITLIST
+#ifndef i386
+#error Only i386 implemented
+#endif
+
+static inline int sane_pointer(void *p)
+{
+	return (p == NULL) || ((unsigned) p > 0xc0000000);
+}
+
+void autofs_check_waitlist_integrity(struct autofs_sb_info *sbi, char *op)
+{
+	struct autofs_wait_queue **wqp, *wq;
+
+	if ( sbi->magic != AUTOFS_SBI_MAGIC ) {
+		printk("autofs: CHECK_WAITLIST with bogus sbi pointer: %p\n",
+		       sbi);
+		return;
+	}
+
+	wqp = &(sbi->queues);
+	while ( (wq = *wqp) ) {
+		if ( !sane_pointer(wq) ) {
+			printk("autofs(%s): wait queue pointer corrupt: ", op);
+			wqp = &(sbi->queues);
+			do {
+				wq = *wqp;
+				printk(" %p", wq);
+				wqp = &(wq->next);
+			} while ( sane_pointer(*wqp) );
+			printk("\n");
+			*wqp = NULL;
+			break;
+		} else {
+			wqp = &(wq->next);
+		}
+	}
+}
+#endif
+
 /* We make this a static variable rather than a part of the superblock; it
    is better if we don't reassign numbers easily even across filesystems */
 static int autofs_next_wait_queue = 1;
@@ -95,6 +135,8 @@
 	struct autofs_wait_queue *wq;
 	int status;
 
+	CHECK_WAITLIST(sbi,"wait");
+
 	for ( wq = sbi->queues ; wq ; wq = wq->next ) {
 		if ( wq->hash == hash &&
 		     wq->len == len &&
@@ -147,6 +189,8 @@
 int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status)
 {
 	struct autofs_wait_queue *wq, **wql;
+
+	CHECK_WAITLIST(sbi,"release");
 
 	for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
 		if ( wq->wait_queue_token == wait_queue_token )
diff -u --recursive --new-file v2.1.40/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.40/linux/fs/nfs/write.c	Tue May 13 22:41:15 1997
+++ linux/fs/nfs/write.c	Sat May 24 09:07:22 1997
@@ -178,7 +178,7 @@
 			wsize = count;
 
 		result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
-					offset, wsize, IS_SWAPFILE(inode),
+					IS_SWAPFILE(inode), offset, wsize,
 					buffer, &fattr);
 
 		if (result < 0) {
diff -u --recursive --new-file v2.1.40/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.40/linux/fs/proc/array.c	Sat May 24 09:10:24 1997
+++ linux/fs/proc/array.c	Tue May 27 12:04:10 1997
@@ -297,20 +297,14 @@
 static int get_meminfo(char * buffer)
 {
 	struct sysinfo i;
-	int len;
 
 	si_meminfo(&i);
 	si_swapinfo(&i);
-	len = sprintf(buffer, "        total:    used:    free:  shared: buffers:  cached:\n"
-		"Mem:  %8lu %8lu %8lu %8lu %8lu %8lu\n"
-		"Swap: %8lu %8lu %8lu\n",
-		i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE,
-		i.totalswap, i.totalswap-i.freeswap, i.freeswap);
+
 	/*
-	 * Tagged format, for easy grepping and expansion. The above will go away
-	 * eventually, once the tools have been updated.
+	 * Tagged format, for easy grepping and expansion.
 	 */
-	return len + sprintf(buffer+len,
+	return sprintf(buffer,
 		"MemTotal:  %8lu kB\n"
 		"MemFree:   %8lu kB\n"
 		"MemShared: %8lu kB\n"
diff -u --recursive --new-file v2.1.40/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v2.1.40/linux/include/linux/mtio.h	Wed Jan  1 08:54:02 1997
+++ linux/include/linux/mtio.h	Tue May 27 13:20:10 1997
@@ -225,6 +225,9 @@
 #define MT_ST_WRITE_THRESHOLD	0x20000000
 #define MT_ST_DEF_BLKSIZE	0x50000000
 #define MT_ST_DEF_OPTIONS	0x60000000
+#define MT_ST_TIMEOUTS		0x70000000
+#define MT_ST_SET_TIMEOUT	(MT_ST_TIMEOUTS | 0x000000)
+#define MT_ST_SET_LONG_TIMEOUT	(MT_ST_TIMEOUTS | 0x100000)
 
 #define MT_ST_BUFFER_WRITES	0x1
 #define MT_ST_ASYNC_WRITES	0x2
@@ -238,6 +241,7 @@
 #define MT_ST_NO_BLKLIMS	0x200
 #define MT_ST_CAN_PARTITIONS    0x400
 #define MT_ST_SCSI2LOGICAL      0x800
+#define MT_ST_SYSV              0x1000
 
 /* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
 #define MT_ST_CLEAR_DEFAULT	0xfffff
diff -u --recursive --new-file v2.1.40/linux/include/linux/soundcard.h linux/include/linux/soundcard.h
--- v2.1.40/linux/include/linux/soundcard.h	Tue Mar  4 10:25:26 1997
+++ linux/include/linux/soundcard.h	Sun May 11 23:02:24 1997
@@ -33,7 +33,7 @@
  * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of
  * the currently active driver.
  */
-#define SOUND_VERSION	0x0307f1
+#define SOUND_VERSION	0x030800
 #define OPEN_SOUND_SYSTEM
 
 /* In Linux we need to be prepared for cross compiling */
@@ -76,7 +76,7 @@
  */
 
 #ifndef _SIOWR
-#if defined(_IOWR) && !defined(sun) && !defined(sparc)
+#if defined(_IOWR) && (defined(_AIX) || (!defined(sun) && !defined(sparc) && !defined(__INCioctlh) && !defined(__Lynx__)))
 /* Use already defined ioctl defines if they exist (except with Sun) */
 #define	SIOCPARM_MASK	IOCPARM_MASK
 #define	SIOC_VOID	IOC_VOID
@@ -127,7 +127,7 @@
 #define SNDCTL_SEQ_GETOUTCOUNT		_SIOR ('Q', 4, int)
 #define SNDCTL_SEQ_GETINCOUNT		_SIOR ('Q', 5, int)
 #define SNDCTL_SEQ_PERCMODE		_SIOW ('Q', 6, int)
-#define SNDCTL_FM_LOAD_INSTR		_SIOW ('Q', 7, struct sbi_instrument)	/* Obsolete */
+#define SNDCTL_FM_LOAD_INSTR		_SIOW ('Q', 7, struct sbi_instrument)	/* Obsolete. Don't use. */
 #define SNDCTL_SEQ_TESTMIDI		_SIOW ('Q', 8, int)
 #define SNDCTL_SEQ_RESETSAMPLES		_SIOW ('Q', 9, int)
 #define SNDCTL_SEQ_NRSYNTHS		_SIOR ('Q',10, int)
@@ -140,10 +140,25 @@
 #define SNDCTL_SEQ_OUTOFBAND		_SIOW ('Q',18, struct seq_event_rec)
 #define SNDCTL_SEQ_GETTIME		_SIOR ('Q',19, int)
 #define SNDCTL_SYNTH_ID			_SIOWR('Q',20, struct synth_info)
+#define SNDCTL_SYNTH_CONTROL		_SIOWR('Q',21, struct synth_control)
+#define SNDCTL_SYNTH_REMOVESAMPLE	_SIOWR('Q',22, struct remove_sample)
 
-	struct seq_event_rec {
-			unsigned char arr[8];
-		};
+typedef struct synth_control
+{
+	int devno;	/* Synthesizer # */
+	char data[4000]; /* Device spesific command/data record */
+}synth_control;
+
+typedef struct remove_sample
+{
+	int devno;	/* Synthesizer # */
+	int bankno;	/* MIDI bank # (0=General MIDI) */
+	int instrno;	/* MIDI instrument number */
+} remove_sample;
+
+typedef struct seq_event_rec {
+		unsigned char arr[8];
+} seq_event_rec;
 
 #define SNDCTL_TMR_TIMEBASE		_SIOWR('T', 1, int)
 #define SNDCTL_TMR_START		_SIO  ('T', 2)
diff -u --recursive --new-file v2.1.40/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.40/linux/kernel/exit.c	Sat May 24 09:10:25 1997
+++ linux/kernel/exit.c	Thu May 22 19:14:18 1997
@@ -32,6 +32,7 @@
 
 static inline void generate(unsigned long sig, struct task_struct * p)
 {
+	unsigned flags;
 	unsigned long mask = 1 << (sig-1);
 	struct sigaction * sa = sig + p->sig->action - 1;
 
@@ -40,7 +41,7 @@
 	 * be handled immediately (ie non-blocked and untraced)
 	 * and that is ignored (either explicitly or by default)
 	 */
-	spin_lock_irq(&p->sig->siglock);
+	spin_lock_irqsave(&p->sig->siglock, flags);
 	if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) {
 		/* don't bother with ignored signals (but SIGCHLD is special) */
 		if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
@@ -56,7 +57,7 @@
 	if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
 		wake_up_process(p);
 out:
-	spin_unlock_irq(&p->sig->siglock);
+	spin_unlock_irqrestore(&p->sig->siglock, flags);
 }
 
 /*
@@ -67,10 +68,11 @@
 {
 	sig--;
 	if (p->sig) {
+		unsigned flags;
 		unsigned long mask = 1UL << sig;
 		struct sigaction *sa = p->sig->action + sig;
 
-		spin_lock_irq(&p->sig->siglock);
+		spin_lock_irqsave(&p->sig->siglock, flags);
 
 		spin_lock(&p->sigmask_lock);
 		p->signal |= mask;
@@ -82,7 +84,7 @@
 		if (p->state == TASK_INTERRUPTIBLE)
 			wake_up_process(p);
 
-		spin_unlock_irq(&p->sig->siglock);
+		spin_unlock_irqrestore(&p->sig->siglock, flags);
 	}
 }
 
@@ -97,7 +99,8 @@
 		return -EPERM;
 
 	if (sig && p->sig) {
-		spin_lock_irq(&p->sigmask_lock);
+		unsigned flags;
+		spin_lock_irqsave(&p->sigmask_lock, flags);
 		if ((sig == SIGKILL) || (sig == SIGCONT)) {
 			if (p->state == TASK_STOPPED)
 				wake_up_process(p);
@@ -107,7 +110,7 @@
 		}
 		if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
 			p->signal &= ~(1<<(SIGCONT-1));
-		spin_unlock_irq(&p->sigmask_lock);
+		spin_unlock_irqrestore(&p->sigmask_lock, flags);
 
 		/* Actually generate the signal */
 		generate(sig,p);
diff -u --recursive --new-file v2.1.40/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.40/linux/mm/page_alloc.c	Tue May 13 22:41:20 1997
+++ linux/mm/page_alloc.c	Sat May 24 09:35:33 1997
@@ -343,4 +343,3 @@
   	swap_free(entry);
   	return;
 }
-
diff -u --recursive --new-file v2.1.40/linux/mm/slab.c linux/mm/slab.c
--- v2.1.40/linux/mm/slab.c	Sat May 24 09:10:25 1997
+++ linux/mm/slab.c	Tue May 27 11:45:01 1997
@@ -1433,6 +1433,7 @@
 			/* Someone may have stolen our objs.  Doesn't matter, we'll
 			 * just come back here again.
 			 */
+			spin_lock_irq(&cachep->c_spinlock);
 			goto try_again;
 		}
 		/* Couldn't grow, but some objs may have been freed. */
diff -u --recursive --new-file v2.1.40/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.40/linux/mm/vmscan.c	Tue May 13 22:41:20 1997
+++ linux/mm/vmscan.c	Sat May 24 09:45:24 1997
@@ -345,7 +345,7 @@
  * to be.  This works out OK, because we now do proper aging on page
  * contents. 
  */
-int try_to_free_page(int priority, int dma, int wait)
+static inline int do_try_to_free_page(int priority, int dma, int wait)
 {
 	static int state = 0;
 	int i=6;
@@ -379,6 +379,22 @@
 	return 0;
 }
 
+/*
+ * This is REALLY ugly.
+ *
+ * We need to make the locks finer granularity, but right
+ * now we need this so that we can do page allocations
+ * without holding the kernel lock etc.
+ */
+int try_to_free_page(int priority, int dma, int wait)
+{
+	int retval;
+
+	lock_kernel();
+	retval = do_try_to_free_page(priority,dma,wait);
+	unlock_kernel();
+	return retval;
+}
 
 /*
  * The background pageout daemon.
