diff -u --recursive --new-file v1.1.31/linux/Makefile linux/Makefile
--- v1.1.31/linux/Makefile	Tue Jul 19 10:18:53 1994
+++ linux/Makefile	Tue Jul 19 09:28:54 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 31
+SUBLEVEL = 32
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.31/linux/config.in linux/config.in
--- v1.1.31/linux/config.in	Sat Jul  9 16:30:45 1994
+++ linux/config.in	Wed Jul 20 21:48:46 1994
@@ -81,7 +81,6 @@
 fi
 bool 'PPP (point-to-point) support' CONFIG_PPP n
 bool 'PLIP (parallel port) support' CONFIG_PLIP n
-bool 'SK_G16 support' CONFIG_SK_G16 n
 bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
 bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
 bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
@@ -110,6 +109,7 @@
 	fi
 	bool 'HP PCLAN support' CONFIG_HPLAN n
 	bool 'NE2000/NE1000 support' CONFIG_NE2000 y
+	bool 'SK_G16 support' CONFIG_SK_G16 n
 fi
 bool 'EISA and on board controllers' CONFIG_NET_EISA n
 	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
diff -u --recursive --new-file v1.1.31/linux/drivers/block/README.hd linux/drivers/block/README.hd
--- v1.1.31/linux/drivers/block/README.hd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.hd	Wed Jul 20 16:09:20 1994
@@ -0,0 +1,98 @@
+IDE Performance Enhancements	Version 2.0
+============================	===========
+
+This version of hd.c includes support for two optional features:
+
+(1) The disk I/O routines can now run with interrupts unmasked
+	most of the time, making them much friendlier to high
+	speed serial ports and other system activity.
+
+(2) Support is included for IDE "Multiple Sector Mode", the use
+	of which can reduce disk I/O kernel overhead by 10-30%
+	on many systems, with a corresponding 10-20% increase in
+	data throughput.
+
+By default, both features are DISABLED, for compatibility with
+	systems on which they may cause troubles.
+
+The IRQ unmasking has been known to CORRUPT FILESYSTEMS in the
+past on systems with strange hard drives.  Backup before trying!
+
+It works on most systems, but use at your own risk!!
+
+Drives which support "Multiple Sector Mode" are identified by the
+kernel at boot time, and a message is displayed indicating the
+largest possible setting for "MaxMult".  I recommend using settings
+of 8, 16, or 32.  Many drives also support non-powers of two,
+but many other drives do not -- try strange values at your own risk!
+
+For more detailed boot-time information about your drive, change
+the definition of VERBOSE_DRIVE_INFO from 0 to 1 near the top
+of hd.c and rebuild/reinstall the kernel.
+
+Some drives (mostly older CONNER drives) do not implement multiple mode
+correctly, and data corruption may occur.. but if you wait long enough
+the error recovery logic *should* be able to recover eventually.
+
+To try this out more safely, mount the drive's partitions read-only
+before using hdparm (see below) for the first time.  If it doesn't
+work, email me (mlord@bnr.ca) with the drive name as displayed at
+boot time, so I can warn others and possibly add a hook to the code.
+
+To enable the features, a small program is included:  hdparm.c
+This one is *different* from previous versions -- be sure to recompile it!
+
+Compile this using   cc -O -o /usr/bin/hdparm hdparm.c
+and then use it to enable/disable the new features, as follows:
+
+To turn on 16-sector multiple mode, with interrupt unmasking:
+
+	hdparm /dev/hda 16 1
+
+To view the current settings:
+
+	hdparm /dev/hda
+
+If you have more than one drive, a separate command would need to		be issued for the second drive as well, using the same or different
+settings as desired:
+
+	hdparm /dev/hdb 16 1
+
+To turn off both features on the first drive, use:
+
+	hdparm /dev/hda 0 0
+
+To benchmark the performance difference, try:
+
+	hdparm /dev/hda 0 0
+	sync
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+	hdparm /dev/hda 16 1
+	sync
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+	time dd if=/dev/hda of=/dev/null bs=1024k count=30
+
+This gives before and after views.  Compare the total elapsed times,
+as well as the percent of CPU used in each case.  Run several trials
+to ensure/verify consistent results.  Some drives are actually *slower*
+with multiple mode enabled, but those are very rare indeed.  Most systems
+experience a 10-30% increase in throughput, with a corresponding 5-50%
+decrease in kernel/system CPU usage.
+
+If you are using linux kernel 1.1.4 or higher (with the "cluster" code),
+then you may not notice a big difference with the above tests.  However,
+I have noticed that the iozone benchmark program does seem to work fairly
+reliably under kernels with the "cluster" code, so you could try that
+instead (under older kernels, iozone seems to give wildly varying results
+from trial to trial, at least on my system).
+
+To have your favorite settings installed automatically at boot time,
+place the hdparm command(s) into the /etc/rc.d/rc.local file.
+Alternatively, one could modify the DEFAULTs near the top of hd.c
+and rebuild and install the new kernel.
+
+Enjoy,
+mlord@bnr.ca
diff -u --recursive --new-file v1.1.31/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v1.1.31/linux/drivers/block/hd.c	Sat Jul 16 19:51:20 1994
+++ linux/drivers/block/hd.c	Wed Jul 20 16:09:20 1994
@@ -14,9 +14,15 @@
  *
  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
  *  in the early extended-partition checks and added DM partitions
+ *
+ *  IDE IRQ-unmask & drive-id & multiple-mode code added by Mark Lord.
  */
 
+#define DEFAULT_MULT_COUNT  0	/* set to 0 to disable multiple mode at boot */
+#define DEFAULT_UNMASK_INTR 0	/* set to 0 to *NOT* unmask irq's more often */
+#define VERBOSE_DRIVE_INFO  0	/* set to 1 for more drive info at boot time */
 
+#include <asm/irq.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
@@ -211,6 +217,128 @@
 	outb_p(cmd,++port);
 }
 
+static void hd_request (void);
+unsigned int identified  [MAX_HD] = {0,}; /* 1 = drive ID already displayed   */
+unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O       */
+unsigned int max_mult    [MAX_HD] = {0,}; /* max sectors for MultMode         */
+unsigned int mult_req    [MAX_HD] = {0,}; /* requested MultMode count         */
+unsigned int mult_count  [MAX_HD] = {0,}; /* currently enabled MultMode count */
+struct request WCURRENT;
+
+static void rawstring (char *prefix, char *s, int n)
+{
+	if (prefix)
+		printk(prefix);
+	if (s && *s) {
+		int i;
+		for (i=0; i < n && s[i^1] == ' '; ++i); /* skip blanks */
+		for (; i < n && s[i^1]; ++i)		/* flip bytes */
+			if (s[i^1] != ' ' || ((i+1) < n && s[(i+1)^1] != ' '))
+				printk("%c",s[i^1]);
+	}
+}
+
+#if VERBOSE_DRIVE_INFO
+
+char *cfg_str[] =
+{	"", " HardSect", " SoftSect", " NotMFM", " HdSw>15uSec", " SpinMotCtl",
+	" Fixed", " Removeable", " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs",
+	" RotSpdTol>.5%", " dStbOff", " TrkOff", " FmtGapReq", "",
+};
+
+char *ioready[]		= {"no", "?", "yes", "on/off"};
+char *SlowMedFast[]	= {"slow", "medium", "fast"};
+char *BuffType[]	= {"?", "1Sect", "DualPort", "DualPortCache"};
+
+#define YN(b)	(((b)==0)?"no":"yes")
+
+static void dmpstr (char *prefix, unsigned int i, char *s[], unsigned int maxi)
+{
+	printk(prefix);
+	printk( (i > maxi) ? "?" : s[i] );
+}
+
+static void dump_identity (unsigned int dev, unsigned short ib[])
+{
+	int i;
+	char dashes[] = "\n+-------------------------------------------------------------------+\n";
+	printk (dashes);
+	printk ("hd%c:  Drive Identification Info:\n", dev+'a');
+	rawstring (" Model=",(char *)&ib[27],40);
+	rawstring (", FwRev=",(char *)&ib[23],8);
+	rawstring (", SerialNo=",(char *)&ib[10],20);
+	printk ("\n Config={");
+	for (i=0; i<=15; i++) if (ib[0] & (1<<i)) printk (cfg_str[i]);
+	printk (" }\n");
+	printk (" Default c/h/s=%d/%d/%d, TrkSize=%d, SectSize=%d, ECCbytes=%d\n",
+		ib[1],ib[3],ib[6],ib[4],ib[5], ib[22]);
+	dmpstr (" BuffType=",ib[20],BuffType,3);
+	ib[47] &= 0xFF;
+	printk (", BuffSize=%dKB, MaxMultSect=%d\n", ib[21]/2, ib[47]);
+	printk (" Features: DblWordIO=%s, IORDY=%s, LBA=%s, DMA=%s",
+		YN(ib[48]&1),ioready[(ib[49]&0xC00)>>10],YN(ib[49]&0x200),YN(ib[49]&0x100));
+	dmpstr (", tPIO=",ib[51]>>8,SlowMedFast,2);
+	if (ib[49]&0x100 && (ib[53]&1))
+		dmpstr (", tDMA=",ib[52]>>8,SlowMedFast,2);
+	printk ("\n (%s): Current c/h/s=%d/%d/%d, TotSect=%d",
+		(((ib[53]&1)==0)?"maybe":"valid"),
+		ib[54],ib[55],ib[56],*(int *)&ib[57]);
+	if (ib[49]&0x200)
+		printk (", MaxLBAsect=%d", *(int *)&ib[60]);
+	printk("\n CurMultSect=%d%s",ib[59]&0xFF,(ib[59]&0x100)?"":"?");
+	if (ib[49]&0x100)
+		printk (", DMA-1w=%04X, DMA-mw=%04X", ib[62], ib[63]);
+	printk ("%s\n",dashes);
+}
+#endif /* VERBOSE_DRIVE_INFO */
+
+static void identify_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->dev);
+	unsigned short ib[64], stat = inb_p(HD_STATUS);
+
+	if (unmask_intr[dev])
+		sti();
+	if (stat & (BUSY_STAT|ERR_STAT))
+		printk ("  hd%c: multiple mode not supported\n", dev+'a');
+	else {
+		insw(HD_DATA,(char *)ib,64); /* get first 128 ID bytes */
+#if VERBOSE_DRIVE_INFO
+		dump_identity(dev, ib);
+#endif
+		printk ("  hd%c: ", dev+'a');
+		rawstring(NULL, (char *)&ib[27], 40);
+		max_mult[dev] = ib[47] & 0xff;
+		printk (" (%dMB IDE w/%dKB Cache, MaxMult=%d)\n",
+			ib[1]*ib[3]*ib[6] / 2048, ib[21]>>1, max_mult[dev]);
+		insw(HD_DATA,(char *)ib,64); /* flush remaining 384 ID bytes */
+		insw(HD_DATA,(char *)ib,64);
+		insw(HD_DATA,(char *)ib,64);
+	}
+	hd_request();
+	return;
+}
+
+static void set_multmode_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->dev), stat = inb_p(HD_STATUS);
+
+	if (unmask_intr[dev])
+		sti();
+	if (stat & (BUSY_STAT|ERR_STAT)) {
+		mult_req[dev] = mult_count[dev] = 0;
+		printk ("  hd%c: set multiple mode failed\n", dev+'a');
+	} else {
+		if ((mult_count[dev] = mult_req[dev]))
+			printk ("  hd%c: enabled %d-sector multiple mode\n",
+				dev+'a', mult_count[dev]);
+		else
+			printk ("  hd%c: disabled multiple mode\n", dev+'a');
+	}
+	hd_request();
+	return;
+}
+
 static int drive_busy(void)
 {
 	unsigned int i;
@@ -254,14 +382,21 @@
 		if (reset)
 			goto repeat;
 	}
-	i++;
-	if (i < NR_HD) {
+	if (++i < NR_HD) {
+		if (unmask_intr[i]) {
+			printk("hd%c: disabled irq-unmasking\n",i+'a');
+			unmask_intr[i] = 0;
+		}
+		if (mult_req[i] || mult_count[i]) {
+			printk("hd%c: disabled multiple mode\n",i+'a');
+			mult_req[i] = mult_count[i] = 0;
+		}
 		hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
 			hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
 		if (reset)
 			goto repeat;
 	} else
-		do_hd_request();
+		hd_request();
 }
 
 /*
@@ -313,9 +448,11 @@
 
 static void read_intr(void)
 {
-	int i;
-	int retries = 100000;
+	unsigned int dev = DEVICE_NR(CURRENT->dev);
+	int i, retries = 100000, msect, nsect;
 
+	if (unmask_intr[dev])
+		sti();			/* permit other IRQs during xfer */
 	do {
 		i = (unsigned) inb_p(HD_STATUS);
 		if (i & BUSY_STAT)
@@ -326,42 +463,111 @@
 			goto ok_to_read;
 	} while (--retries > 0);
 	sti();
-	printk("HD: read_intr: status = 0x%02x\n",i);
+	printk("hd%c: read_intr: status = 0x%02x\n",dev+'a',i);
 	if (i & ERR_STAT) {
 		hd_error = (unsigned) inb(HD_ERROR);
-		printk("HD: read_intr: error = 0x%02x\n",hd_error);
+		printk("hd%c: read_intr: error = 0x%02x\n",dev+'a',hd_error);
 	}
 	bad_rw_intr();
 	cli();
-	do_hd_request();
+	hd_request();
 	return;
 ok_to_read:
-	insw(HD_DATA,CURRENT->buffer,256);
+	msect = mult_count[dev];
+read_next:
+	if (msect) {
+		if ((nsect = CURRENT->current_nr_sectors) > msect)
+			nsect = msect;
+		msect -= nsect;
+	} else
+		nsect = 1;
+	insw(HD_DATA,CURRENT->buffer,nsect<<8);
+	CURRENT->sector += nsect;
+	CURRENT->buffer += nsect<<9;
 	CURRENT->errors = 0;
-	CURRENT->buffer += 512;
-	CURRENT->sector++;
-	i = --CURRENT->nr_sectors;
-	--CURRENT->current_nr_sectors;
+	i = (CURRENT->nr_sectors -= nsect);
+
 #ifdef DEBUG
-	printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
-		MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT-> 
-		buffer);
+	printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=%08lx\n",
+		dev+'a', CURRENT->sector, CURRENT->sector+nsect,
+		CURRENT->nr_sectors, (long) CURRENT->buffer+(nsect<<9));
 #endif
-	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+	if ((CURRENT->current_nr_sectors -= nsect) <= 0)
 		end_request(1);
 	if (i > 0) {
+		if (msect)
+			goto read_next;
 		SET_INTR(&read_intr);
-		sti();
 		return;
 	}
 	(void) inb_p(HD_STATUS);
 #if (HD_DELAY > 0)
 	last_req = read_timer();
 #endif
-	do_hd_request();
+	if (CURRENT)
+		hd_request();
 	return;
 }
 
+static inline void multwrite (unsigned int dev)
+{
+	unsigned int mcount = mult_count[dev];
+
+	while (mcount--) {
+		outsw(HD_DATA,WCURRENT.buffer,256);
+		if (!--WCURRENT.nr_sectors)
+			return;
+		WCURRENT.buffer += 512;
+		if (!--WCURRENT.current_nr_sectors) {
+			WCURRENT.bh = WCURRENT.bh->b_reqnext;
+			if (WCURRENT.bh == NULL)
+				panic("buffer list corrupted\n");
+			WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
+			WCURRENT.buffer             = WCURRENT.bh->b_data;
+		}
+	}
+}
+
+static void multwrite_intr(void)
+{
+	int i;
+	unsigned int dev = DEVICE_NR(WCURRENT.dev);
+
+	if (unmask_intr[dev])
+		sti();
+	if (((i = inb_p(HD_STATUS)) & STAT_MASK) == STAT_OK) {
+		if (i & DRQ_STAT) {
+			if (WCURRENT.nr_sectors) {
+				multwrite(dev);
+				SET_INTR(&multwrite_intr);
+				return;
+			}
+		} else {
+			if (!WCURRENT.nr_sectors) {	/* all done? */
+				for (i = CURRENT->nr_sectors; i > 0;){
+					i -= CURRENT->current_nr_sectors;
+					end_request(1);
+				}
+#if (HD_DELAY > 0)
+				last_req = read_timer();
+#endif
+				if (CURRENT)
+					hd_request();
+				return;
+			}
+		}
+	}
+	sti();
+	printk("hd%c: multwrite_intr: status = 0x%02x\n",dev+'a',i);
+	if (i & ERR_STAT) {
+		hd_error = (unsigned) inb(HD_ERROR);
+		printk("hd:%c multwrite_intr: error = 0x%02x\n",dev+'a',hd_error);
+	}
+	bad_rw_intr();
+	cli();
+	hd_request();
+}
+
 static void write_intr(void)
 {
 	int i;
@@ -384,7 +590,7 @@
 	}
 	bad_rw_intr();
 	cli();
-	do_hd_request();
+	hd_request();
 	return;
 ok_to_write:
 	CURRENT->sector++;
@@ -401,7 +607,7 @@
 #if (HD_DELAY > 0)
 		last_req = read_timer();
 #endif
-		do_hd_request();
+		hd_request();
 	}
 	return;
 }
@@ -410,7 +616,7 @@
 {
 	if (win_result())
 		bad_rw_intr();
-	do_hd_request();
+	hd_request();
 }
 
 /*
@@ -433,7 +639,7 @@
 		end_request(0);
 	}
 
-	do_hd_request();
+	hd_request();
 }
 
 /*
@@ -443,7 +649,7 @@
  * worst that can happen is that an unexpected HD-interrupt comes in and
  * sets the "reset" variable and starts the timer)
  */
-static void do_hd_request(void)
+static void hd_request(void)
 {
 	unsigned int block,dev;
 	unsigned int sec,head,cyl,track;
@@ -462,7 +668,7 @@
 	nsect = CURRENT->nr_sectors;
 	if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
 #ifdef DEBUG
-		printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
+		printk("hd : attempted read for sector %d past end of device at sector %d.\n",
 		   	block, hd[dev].nr_sects);
 #endif
 		end_request(0);
@@ -475,17 +681,18 @@
 	head = track % hd_info[dev].head;
 	cyl = track / hd_info[dev].head;
 #ifdef DEBUG
-	printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
-		dev, cyl, head, sec, CURRENT->buffer);
+	printk("hd%c : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
+		dev+'a', cyl, head, sec, CURRENT->buffer);
 #endif
-	cli();
+	if (!unmask_intr[dev])
+		cli();
 	if (reset) {
 		int i;
 
 		for (i=0; i < NR_HD; i++)
 			recalibrate[i] = 1;
+		cli(); /* better play it safe, as resets are the last resort */
 		reset_hd();
-		sti();
 		return;
 	}
 	if (recalibrate[dev]) {
@@ -493,32 +700,70 @@
 		hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
 		if (reset)
 			goto repeat;
-		sti();
 		return;
 	}	
-	if (CURRENT->cmd == WRITE) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+	if (!identified[dev]) {
+		identified[dev]  = 1;
+		unmask_intr[dev] = DEFAULT_UNMASK_INTR;
+		mult_req[dev]    = DEFAULT_MULT_COUNT;
+		hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
 		if (reset)
 			goto repeat;
-		if (wait_DRQ()) {
-			printk("HD: do_hd_request: no DRQ\n");
-			bad_rw_intr();
+		return;
+	}
+	if (mult_req[dev] != mult_count[dev]) {
+		hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
+		if (reset)
 			goto repeat;
-		}
-		outsw(HD_DATA,CURRENT->buffer,256);
-		sti();
 		return;
 	}
 	if (CURRENT->cmd == READ) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
+		unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
+		hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
 		if (reset)
 			goto repeat;
-		sti();
+#ifdef DEBUG
+		printk("hd%c: reading %d sectors(%ld-%ld), buffer=%08lx\n",
+			dev+'a', nsect, CURRENT->sector,
+			CURRENT->sector+nsect-1, (long) CURRENT->buffer);
+#endif
+		return;
+	}
+	if (CURRENT->cmd == WRITE) {
+		if (mult_count[dev])
+			hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
+		else
+			hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+		if (reset)
+			goto repeat;
+#ifdef DEBUG
+		printk("hd%c: writing %d sectors(%ld-%ld), buffer=%08lx\n",
+			dev+'a', nsect, CURRENT->sector,
+			CURRENT->sector+nsect-1, (long) CURRENT->buffer);
+#endif
+		if (wait_DRQ()) {
+			printk("hd%c: hd_request: no DRQ\n", dev+'a');
+			bad_rw_intr();
+			goto repeat;
+		}
+		if (mult_count[dev]) {
+			WCURRENT = *CURRENT;
+			multwrite(dev);
+		} else {
+			outsw(HD_DATA,CURRENT->buffer,256);
+		}
 		return;
 	}
 	panic("unknown hd-command");
 }
 
+static void do_hd_request (void)
+{
+	disable_irq(HD_IRQ);
+	hd_request();
+	enable_irq(HD_IRQ);
+}
+
 static int hd_ioctl(struct inode * inode, struct file * file,
 	unsigned int cmd, unsigned long arg)
 {
@@ -568,6 +813,53 @@
 
 		case BLKRRPART: /* Re-read partition tables */
 			return revalidate_hddisk(inode->i_rdev, 1);
+
+		case HDIO_SETUNMASKINTR:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			unmask_intr[dev] = get_fs_long((long *) arg);
+			return 0;
+
+                case HDIO_GETUNMASKINTR:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(unmask_intr[dev], (long *) arg);
+			return 0;
+
+                case HDIO_GETMULTCOUNT:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(mult_count[dev], (long *) arg);
+			return 0;
+
+		case HDIO_SETMULTCOUNT:
+		{
+			unsigned long flags;
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			arg = get_fs_long((long *) arg);
+			save_flags(flags);
+			cli();	/* a prior request might still be in progress */
+			if (arg > max_mult[dev])
+				err = -EINVAL;	/* out of range for device */
+			else if (mult_req[dev] != mult_count[dev])
+				err = -EBUSY;	/* busy, try again */
+			else {
+				mult_req[dev] = arg;
+				err = 0;
+			}
+			restore_flags(flags);
+			return err;
+		}
+
 		RO_IOCTLS(inode->i_rdev,arg);
 		default:
 			return -EINVAL;
diff -u --recursive --new-file v1.1.31/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.1.31/linux/drivers/char/console.c	Mon Jun 27 16:46:58 1994
+++ linux/drivers/char/console.c	Wed Jul 20 15:54:08 1994
@@ -83,6 +83,7 @@
 int set_selection(const int arg);
 int paste_selection(struct tty_struct *tty);
 static void clear_selection(void);
+static void highlight_pointer(const int currcons, const int where);
 
 /* Variables for selection control. */
 #define SEL_BUFFER_SIZE 4096
@@ -1703,6 +1704,9 @@
 		return;
 	lock = 1;
 	kbdsave(new_console);
+#ifdef CONFIG_SELECTION
+	highlight_pointer(fg_console,-1);
+#endif /* CONFIG_SELECTION */
 	get_scrmem(fg_console); 
 	fg_console = new_console;
 	set_scrmem(fg_console); 
@@ -1778,8 +1782,49 @@
 		*p = (*p & 0x88) | ((*p << 4) & 0x70) | ((*p >> 4) & 0x07);
 }
 
-/* is c in range [a-zA-Z0-9_]? */
-static inline int inword(const char c) { return (isalnum(c) || c == '_'); }
+/* use complementary color to show the pointer */
+static void highlight_pointer(const int currcons, const int where)
+{
+        unsigned char *p;
+	static char *prev=NULL;
+
+	if (where==-1) /* remove the pointer */
+	{
+                if (prev)
+		{
+			*prev ^= 0x77;
+			prev=NULL;
+		}
+        }
+	else
+	{
+	        p = (unsigned char *)origin - hwscroll_offset + where + 1;
+		*p ^= 0x77;
+		if (prev) *prev ^= 0x77; /* remove the previous one */
+		prev=p;
+	}
+}
+
+
+/*
+ * This function uses a 128-bit look up table
+ */
+static unsigned long inwordLut[4]={
+  0x00000000, /* control chars     */
+  0x03FF0000, /* digits            */
+  0x87FFFFFE, /* uppercase and '_' */
+  0x07FFFFFE  /* lowercase         */
+};
+static inline int inword(const char c) {
+   return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
+}
+
+/* set inwordLut conntents. Invoked by ioctl(). */
+int sel_loadlut(const int arg)
+{
+    memcpy_fromfs(inwordLut,(unsigned long *)(arg+4),16);
+    return 0;
+}
 
 /* does screen address p correspond to character at LH/RH edge of screen? */
 static inline int atedge(const int p)
@@ -1828,7 +1873,6 @@
 	switch (sel_mode)
 	{
 		case 0:	/* character-by-character selection */
-		default:
 			new_sel_start = ps;
 			new_sel_end = pe;
 			break;
@@ -1859,6 +1903,17 @@
 			new_sel_end = pe + video_size_row
 				    - pe % video_size_row - 2;
 			break;
+                case 3: /* pointer highlight */
+		        if (sel_cons != currcons)
+        		{
+				highlight_pointer(sel_cons,-1);
+        	        	clear_selection();
+                		sel_cons = currcons;
+        		}
+			highlight_pointer(sel_cons,pe);
+			return 0; /* nothing more */
+	        default:
+			return -EINVAL;
 	}
 	/* select to end of line if on trailing space */
 	if (new_sel_end > new_sel_start &&
@@ -1962,6 +2017,7 @@
    the selection. */
 static void clear_selection()
 {
+        highlight_pointer(sel_cons, -1); /* hide the pointer */
 	if (sel_start != -1)
 	{
 		highlight(sel_cons, sel_start, sel_end);
diff -u --recursive --new-file v1.1.31/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v1.1.31/linux/drivers/char/keyboard.c	Fri Jun 17 15:20:04 1994
+++ linux/drivers/char/keyboard.c	Wed Jul 20 15:54:08 1994
@@ -86,7 +86,12 @@
 static int want_console = -1;
 static int last_console = 0;		/* last used VC */
 static int dead_key_next = 0;
-static int shift_state = 0;
+/* 
+ * In order to retrieve the shift_state (for the mouse server), either
+ * the variable must be global, or a new procedure must be create to 
+ * return the value. I chose the former way.
+ */
+/*static*/ int shift_state = 0;
 static int npadch = -1;			/* -1 or number assembled on pad */
 static unsigned char diacr = 0;
 static char rep = 0;			/* flag telling character repeat */
diff -u --recursive --new-file v1.1.31/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v1.1.31/linux/drivers/char/mem.c	Tue Jun 21 14:16:20 1994
+++ linux/drivers/char/mem.c	Wed Jul 20 22:11:10 1994
@@ -420,7 +420,7 @@
 #ifdef CONFIG_FTAPE
         /* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
         ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
-        printk( "ftape: allocated %d buffers alligned at: %p\n",
+        printk( "ftape: allocated %d buffers aligned at: %p\n",
                NR_FTAPE_BUFFERS, ftape_big_buffer);
         mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
 #endif 
diff -u --recursive --new-file v1.1.31/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.31/linux/drivers/char/tty_io.c	Thu Jul 14 00:48:21 1994
+++ linux/drivers/char/tty_io.c	Wed Jul 20 15:54:08 1994
@@ -68,6 +68,8 @@
 #ifdef CONFIG_SELECTION
 extern int set_selection(const int arg);
 extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const int arg);
+extern int shift_state;
 #endif /* CONFIG_SELECTION */
 extern int do_screendump(int arg);
 
@@ -1384,6 +1386,11 @@
 					return paste_selection(tty);
 				case 4:
 					unblank_screen();
+					return 0;
+				case 5:
+					return sel_loadlut(arg);
+				case 6:
+					put_fs_byte(shift_state,arg);
 					return 0;
 #endif /* CONFIG_SELECTION */
 				default: 
diff -u --recursive --new-file v1.1.31/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.1.31/linux/drivers/net/Space.c	Fri Jul  8 13:26:34 1994
+++ linux/drivers/net/Space.c	Wed Jul 20 17:09:03 1994
@@ -54,7 +54,9 @@
 extern int elplus_probe(struct device *);
 extern int ac3200_probe(struct device *);
 extern int e2100_probe(struct device *);
-extern int SK_init(struct device *dev);
+extern int ni52_probe(struct device *);
+extern int ni65_probe(struct device *);
+extern int SK_init(struct device *);
 
 /* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
 extern int atp_init(struct device *);
@@ -129,6 +131,12 @@
 #endif
 #if defined(CONFIG_SK_G16)
 	&& SK_init(dev)
+#endif
+#ifdef CONFIG_NI52
+	&& ni52_probe(dev)
+#endif
+#ifdef CONFIG_NI65
+	&& ni65_probe(dev)
 #endif
 	&& 1 ) {
 	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
diff -u --recursive --new-file v1.1.31/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.1.31/linux/drivers/net/ppp.c	Wed Jun 22 14:57:09 1994
+++ linux/drivers/net/ppp.c	Wed Jul 20 15:26:04 1994
@@ -267,7 +267,6 @@
   dev->mtu             = PPP_MTU;
   dev->hard_start_xmit = ppp_xmit;
   dev->open            = ppp_dev_open;
-  dev->do_ioctl        = ppp_dev_ioctl;
   dev->stop            = ppp_dev_close;
   dev->get_stats       = ppp_get_stats;
   dev->hard_header     = ppp_header;
@@ -280,6 +279,8 @@
 #ifdef NET02D
   dev->add_arp         = ppp_add_arp;
   dev->queue_xmit      = dev_queue_xmit;
+#else
+  dev->do_ioctl        = ppp_dev_ioctl;
 #endif
 
   for (i = 0; i < DEV_NUMBUFFS; i++)
@@ -605,6 +606,7 @@
   return 0;
 }
 
+#ifndef NET02D
 static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr)
 {
   struct ppp *ppp = &ppp_ctrl[dev->base_addr];
@@ -630,6 +632,7 @@
 
   return error;
 }
+#endif
 
 /*************************************************************
  * TTY OUTPUT
@@ -650,7 +653,11 @@
   /* If the device is still up then enable the transmitter of the
      next frame. */
   if (((struct ppp *) ppp)->dev->flags & IFF_UP)
+#ifndef NET02D
+    mark_bh (NET_BH);
+#else
     dev_tint (((struct ppp *) ppp)->dev);
+#endif
 
   /* enable any blocked process pending transmission */
   wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
@@ -1124,9 +1131,7 @@
   }
 
   /* receive the frame through the network software */
-  while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0)
-    ;
-
+  (void) dev_rint (c, count, 0, ppp->dev);
   return 1;
 }
 
Only in v1.1.31/linux/drivers/sound: .blurb.orig
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v1.1.31/linux/drivers/sound/Makefile	Tue Jul 19 10:18:55 1994
+++ linux/drivers/sound/Makefile	Wed Jul 20 15:00:35 1994
@@ -5,7 +5,7 @@
 #
 #
 
-VERSION		= 2.90
+VERSION		= 2.90-2
 TARGET_OS	= linux
 
 .c.s:
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v1.1.31/linux/drivers/sound/Readme	Tue Jul 19 10:18:55 1994
+++ linux/drivers/sound/Readme	Wed Jul 20 15:00:28 1994
@@ -12,9 +12,9 @@
 		there are some new features required by a popular 
 		application. In addition there is also support
 		for the GUS MAX and the 16 bit sampling option of GUS.
-		Also the Windows Sound System stuff is there but may not
-		work yet (may work with some WSS compatible cards).
 
+		The MSS/WSS support works now. At least with SG NX Pro 16.
+
 ********* IMPORTANT *****************************************
 Linux 1.0 or later is required to by this driver version.
 
@@ -65,6 +65,7 @@
 	Markus Aroharju	and
 	Risto Kankkunen		Major contributions to the mixer support
 				of GUS v3.7.
+	Hunyue Yau	Mixer support for SG NX Pro.
 	Marc Hoffman	PSS support.
 
 Regards,
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v1.1.31/linux/drivers/sound/ad1848.c	Tue Jul 19 10:18:56 1994
+++ linux/drivers/sound/ad1848.c	Wed Jul 20 15:00:28 1994
@@ -903,16 +903,14 @@
 int
 probe_ms_sound (struct address_info *hw_config)
 {
-  int             config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
-
   if ((INB (hw_config->io_base + 3) & 0x04) == 0)
     return 0;			/* WSS ID test failed */
 
   if (hw_config->irq > 11)
-    return;
+    return 0;
 
-  if (hw_config->dma > 3 || hw_config->dma == 2)
-    return;
+  if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+	return 0;
 
   return ad1848_detect (hw_config->io_base + 4);
 }
@@ -920,10 +918,12 @@
 long
 attach_ms_sound (long mem_start, struct address_info *hw_config)
 {
-  static unsigned char interrupt_bits[11] =
+  static unsigned char interrupt_bits[12] =
   {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
   char            bits;
 
+  static unsigned char dma_bits[4] = {1, 2, 0, 3};
+
   int             config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
 
   if (!ad1848_detect (hw_config->io_base + 4))
@@ -941,7 +941,7 @@
   if ((INB (version_port) & 0x40) == 0)
     printk ("[IRQ?]");
 
-  OUTB (bits | hw_config->dma, config_port);	/* Write IRQ+DMA setup */
+  OUTB (bits | dma_bits[hw_config->dma], config_port);	/* Write IRQ+DMA setup */
 
   ad1848_init ("MS Sound System", hw_config->io_base + 4,
 	       hw_config->irq,
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.1.31/linux/drivers/sound/configure.c	Tue Jul 19 10:18:56 1994
+++ linux/drivers/sound/configure.c	Wed Jul 20 15:00:29 1994
@@ -322,6 +322,13 @@
 	  }
     }
 
+  if (selected_options & B (OPT_SBPRO))
+    {
+      fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
+      if (think_positively (0))
+	 printf("#define __SGNXPRO__\n");
+    }
+
   if (selected_options & B (OPT_SB16))
     selected_options |= B (OPT_SBPRO);
 
@@ -333,7 +340,7 @@
        "if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
 	 "then you must include the LD in the kernel.\n"
 	 "(do you wish to include a LD) ? ");
-      if (think_positively (1))
+      if (think_positively (0))
 	{
 	  char            path[512];
 
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v1.1.31/linux/drivers/sound/dev_table.h	Tue Jul 19 10:18:57 1994
+++ linux/drivers/sound/dev_table.h	Wed Jul 20 15:00:29 1994
@@ -278,6 +278,9 @@
 #endif
 #ifndef EXCLUDE_MSS
 		{SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE},
+#	ifdef MSS2_BASE
+		{SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE},
+#	endif
 #endif
 
 #if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v1.1.31/linux/drivers/sound/dmabuf.c	Tue Jul 19 10:18:58 1994
+++ linux/drivers/sound/dmabuf.c	Wed Jul 20 14:58:28 1994
@@ -320,7 +320,7 @@
       if (!(dmap->flags & DMA_ALLOC_DONE))
 	reorganize_buffers (dev);
 
-      if (dmap->dma_mode)
+      if (!dmap->dma_mode)
 	{
 	  int             err;
 
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.1.31/linux/drivers/sound/sb_dsp.c	Tue Jul 19 10:19:08 1994
+++ linux/drivers/sound/sb_dsp.c	Wed Jul 20 15:00:55 1994
@@ -1,9 +1,9 @@
 /*
  * sound/sb_dsp.c
  *
- * The low level driver for the SoundBlaster DSP chip.
+ * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
  *
- * Copyright by Hannu Savolainen 1993
+ * Copyright by Hannu Savolainen 1994
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -25,6 +25,10 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
+ * Modified:
+ *	Hunyue Yau	Jan 6 1994
+ *	Added code to support Sound Galaxy NX Pro
+ *
  */
 
 #include "sound_config.h"
@@ -756,6 +760,7 @@
 sb_dsp_init (long mem_start, struct address_info *hw_config)
 {
   int             i;
+  int             mixer_type = 0;
 
   sbc_major = sbc_minor = 0;
   sb_dsp_command (0xe1);	/*
@@ -786,7 +791,7 @@
 
 #ifndef EXCLUDE_SBPRO
   if (sbc_major >= 3)
-    sb_mixer_init (sbc_major);
+    mixer_type = sb_mixer_init (sbc_major);
 #endif
 
 #ifndef EXCLUDE_YM8312
@@ -799,7 +804,16 @@
   if (sbc_major >= 3)
     {
 #ifndef SCO
-      sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+#  ifdef __SGNXPRO__
+      if (mixer_type == 2)
+	{
+	  sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+	}
+      else
+#  endif
+	{
+	  sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+	}
 #endif
     }
   else
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v1.1.31/linux/drivers/sound/sb_mixer.c	Tue Jul 19 10:19:09 1994
+++ linux/drivers/sound/sb_mixer.c	Wed Jul 20 15:00:55 1994
@@ -4,7 +4,7 @@
  *
  * The low level mixer driver for the SoundBlaster Pro and SB16 cards.
  *
- * Copyright by Hannu Savolainen 1993
+ * Copyright by Hannu Savolainen 1994
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -26,6 +26,10 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
+ * Modified:
+ *	Hunyue Yau	Jan 6 1994
+ *	Added code to support the Sound Galaxy NX Pro mixer.
+ *
  */
 
 #include "sound_config.h"
@@ -91,9 +95,21 @@
 			    | (mode ? STEREO_DAC : MONO_DAC)));
 }
 
+/*
+ * Returns:
+ *	0	No mixer detected.
+ *	1	Only a plain Sound Blaster Pro style mixer detected.
+ *	2	The Sound Galaxy NX Pro mixer detected.
+ */
 static int
 detect_mixer (void)
 {
+#ifdef __SGNXPRO__
+  int             oldbass, oldtreble;
+
+#endif
+  int             retcode = 1;
+
   /*
    * Detect the mixer by changing parameters of two volume channels. If the
    * values read back match with the values written, the mixer is there (is
@@ -109,7 +125,30 @@
   if (sb_getmixer (VOC_VOL) != 0x33)
     return 0;
 
-  return 1;
+#ifdef __SGNXPRO__
+  /* Attempt to detect the SG NX Pro by check for valid bass/treble
+ * registers.
+ */
+  oldbass = sb_getmixer (BASS_LVL);
+  oldtreble = sb_getmixer (TREBLE_LVL);
+
+  sb_setmixer (BASS_LVL, 0xaa);
+  sb_setmixer (TREBLE_LVL, 0x55);
+
+  if ((sb_getmixer (BASS_LVL) != 0xaa) ||
+      (sb_getmixer (TREBLE_LVL) != 0x55))
+    {
+      retcode = 1;		/* 1 == Only SB Pro detected */
+    }
+  else
+    retcode = 2;		/* 2 == SG NX Pro detected */
+  /* Restore register in either case since SG NX Pro has EEPROM with
+   * 'preferred' values stored.
+   */
+  sb_setmixer (BASS_LVL, oldbass);
+  sb_setmixer (TREBLE_LVL, oldtreble);
+#endif
+  return retcode;
 }
 
 static void
@@ -350,17 +389,23 @@
   set_recmask (SOUND_MASK_MIC);
 }
 
-void
+/*
+ * Returns a code depending on whether a SG NX Pro was detected.
+ * 1 == Plain SB Pro
+ * 2 == SG NX Pro detected.
+ * 3 == SB16
+ *
+ * Used to update message.
+ */
+int
 sb_mixer_init (int major_model)
 {
-  sb_setmixer (0x00, 0);	/*
-				 * Reset mixer
-				 */
+  int             mixer_type = 0;
 
-  if (!detect_mixer ())
-    return;			/*
-				 * No mixer. Why?
-				 */
+  sb_setmixer (0x00, 0);	/* Reset mixer */
+
+  if (!(mixer_type = detect_mixer ()))
+    return 0;			/* No mixer. Why? */
 
   mixer_initialized = 1;
   mixer_model = major_model;
@@ -369,9 +414,21 @@
     {
     case 3:
       mixer_caps = SOUND_CAP_EXCL_INPUT;
-      supported_devices = SBPRO_MIXER_DEVICES;
-      supported_rec_devices = SBPRO_RECORDING_DEVICES;
-      iomap = &sbpro_mix;
+#ifdef __SGNXPRO__
+      if (mixer_type == 2)	/* A SGNXPRO was detected */
+	{
+	  supported_devices = SGNXPRO_MIXER_DEVICES;
+	  supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+	  iomap = &sgnxpro_mix;
+	}
+      else
+#endif
+	{
+	  supported_devices = SBPRO_MIXER_DEVICES;
+	  supported_rec_devices = SBPRO_RECORDING_DEVICES;
+	  iomap = &sbpro_mix;
+	  mixer_type = 1;
+	}
       break;
 
     case 4:
@@ -379,16 +436,18 @@
       supported_devices = SB16_MIXER_DEVICES;
       supported_rec_devices = SB16_RECORDING_DEVICES;
       iomap = &sb16_mix;
+      mixer_type = 3;
       break;
 
     default:
       printk ("SB Warning: Unsupported mixer type\n");
-      return;
+      return 0;
     }
 
   if (num_mixers < MAX_MIXER_DEV)
     mixer_devs[num_mixers++] = &sb_mixer_operations;
   sb_mixer_reset ();
+  return mixer_type;
 }
 
 #endif
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/sb_mixer.h linux/drivers/sound/sb_mixer.h
--- v1.1.31/linux/drivers/sound/sb_mixer.h	Tue Jul 19 10:19:09 1994
+++ linux/drivers/sound/sb_mixer.h	Wed Jul 20 15:00:55 1994
@@ -24,13 +24,29 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
+ *
+ * Modified:
+ *	Hunyue Yau	Jan 6 1994
+ *	Added defines for the Sound Galaxy NX Pro mixer.
  * 
  */
+
 #define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
 
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
 #define SBPRO_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
 					 SOUND_MASK_CD | SOUND_MASK_VOLUME)
 
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES	(SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+				 SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
 #define SB16_RECORDING_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
 					 SOUND_MASK_CD)
 
@@ -62,6 +78,13 @@
 #define IRQ_STAT	0x82
 #define OPSW		0x3c
 
+/*
+ * Additional registers on the SG NX Pro 
+ */
+#define COVOX_VOL	0x42
+#define TREBLE_LVL	0x44
+#define BASS_LVL	0x46
+
 #define FREQ_HI         (1 << 3)/* Use High-frequency ANFI filters */
 #define FREQ_LOW        0	/* Use Low-frequency ANFI filters */
 #define FILT_ON         0	/* Yes, 0 to turn it on, 1 for off */
@@ -107,6 +130,23 @@
 MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
 };
+
+#ifdef	__SGNXPRO__
+mixer_tab sgnxpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
 
 mixer_tab sb16_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v1.1.31/linux/drivers/sound/sound_calls.h	Tue Jul 19 10:19:10 1994
+++ linux/drivers/sound/sound_calls.h	Wed Jul 20 15:00:55 1994
@@ -140,7 +140,7 @@
 void sb_setmixer (unsigned int port, unsigned int value);
 int sb_getmixer (unsigned int port);
 void sb_mixer_set_stereo(int mode);
-void sb_mixer_init(int major_model);
+int sb_mixer_init(int major_model);
 
 /*	From opl3.c	*/
 int opl3_detect (int ioaddr);
diff -u --recursive --new-file v1.1.31/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v1.1.31/linux/drivers/sound/sound_config.h	Tue Jul 19 10:19:11 1994
+++ linux/drivers/sound/sound_config.h	Wed Jul 20 15:00:55 1994
@@ -180,7 +180,7 @@
  * In v3.0 it's /dev/sndproc but this could be a temporary solution.
  */
 
-#define SND_NDEVS	64	/* Number of supported devices */
+#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) */
@@ -199,7 +199,7 @@
 #define ON		1
 #define OFF		0
 
-#define MAX_AUDIO_DEV	4
+#define MAX_AUDIO_DEV	5
 #define MAX_MIXER_DEV	2
 #define MAX_SYNTH_DEV	3
 #define MAX_MIDI_DEV	6
diff -u --recursive --new-file v1.1.31/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v1.1.31/linux/include/linux/hdreg.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/hdreg.h	Wed Jul 20 16:09:21 1994
@@ -42,6 +42,12 @@
 #define WIN_DIAGNOSE		0x90
 #define WIN_SPECIFY		0x91
 
+#define WIN_MULTREAD		0xC4	/* read multiple sectors	*/
+#define WIN_MULTWRITE		0xC5	/* write multiple sectors	*/
+#define WIN_SETMULT		0xC6	/* enable read multiple		*/
+#define WIN_IDENTIFY		0xEC	/* ask drive to identify itself	*/
+#define WIN_SETFEATURES		0xEF	/* set special drive features   */
+
 /* Bits for HD_ERROR */
 #define MARK_ERR	0x01	/* Bad address mark */
 #define TRK0_ERR	0x02	/* couldn't find track 0 */
@@ -61,4 +67,9 @@
       unsigned short cylinders;
       unsigned long start;
 };
+#define HDIO_GETUNMASKINTR	0x302
+#define HDIO_SETUNMASKINTR	0x303
+#define HDIO_GETMULTCOUNT	0x304
+#define HDIO_SETMULTCOUNT	0x305
+#define HDIO_SETFEATURE  	0x306
 #endif
diff -u --recursive --new-file v1.1.31/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.1.31/linux/include/linux/mm.h	Tue Jul 19 10:19:14 1994
+++ linux/include/linux/mm.h	Tue Jul 19 20:08:58 1994
@@ -145,10 +145,10 @@
 extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask);
 extern int zeromap_page_range(unsigned long from, unsigned long size, int mask);
 
-extern void do_wp_page(unsigned long error_code, unsigned long address,
-	struct task_struct *tsk);
-extern void do_no_page(unsigned long error_code, unsigned long address,
-	struct task_struct *tsk);
+extern void do_wp_page(struct vm_area_struct * vma, unsigned long address,
+	unsigned long error_code);
+extern void do_no_page(struct vm_area_struct * vma, unsigned long address,
+	unsigned long error_code);
 
 extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
 extern void mem_init(unsigned long low_start_mem,
diff -u --recursive --new-file v1.1.31/linux/include/linux/wait.h linux/include/linux/wait.h
--- v1.1.31/linux/include/linux/wait.h	Mon Jul 18 14:48:31 1994
+++ linux/include/linux/wait.h	Tue Jul 19 13:10:15 1994
@@ -6,6 +6,8 @@
 
 #define __WCLONE	0x80000000
 
+#ifdef __KERNEL__
+
 struct wait_queue {
 	struct task_struct * task;
 	struct wait_queue * next;
@@ -30,5 +32,7 @@
 } select_table;
 
 #define __MAX_SELECT_TABLE_ENTRIES (4096 / sizeof (struct select_table_entry))
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.31/linux/kernel/ptrace.c linux/kernel/ptrace.c
--- v1.1.31/linux/kernel/ptrace.c	Tue Jul 19 10:19:15 1994
+++ linux/kernel/ptrace.c	Tue Jul 19 09:28:35 1994
@@ -81,24 +81,20 @@
  * tables. NOTE! You should check that the long isn't on a page boundary,
  * and that it is in the task area before calling this: this routine does
  * no checking.
- *
- * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
- * zero. This routine shouldn't have to change when we make a better mm.
  */
-static unsigned long get_long(struct task_struct * tsk,
-	unsigned long addr)
+static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
 {
 	unsigned long page;
 
 repeat:
-	page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
+	page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
 	if (page & PAGE_PRESENT) {
 		page &= PAGE_MASK;
 		page += PAGE_PTR(addr);
 		page = *((unsigned long *) page);
 	}
 	if (!(page & PAGE_PRESENT)) {
-		do_no_page(0,addr,tsk);
+		do_no_page(vma, addr, 0);
 		goto repeat;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
@@ -118,14 +114,14 @@
  * Now keeps R/W state of page so that a text page stays readonly
  * even if a debugger scribbles breakpoints into it.  -M.U-
  */
-static void put_long(struct task_struct * tsk, unsigned long addr,
+static void put_long(struct vm_area_struct * vma, unsigned long addr,
 	unsigned long data)
 {
 	unsigned long page, pte = 0;
 	int readonly = 0;
 
 repeat:
-	page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
+	page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
 	if (page & PAGE_PRESENT) {
 		page &= PAGE_MASK;
 		page += PAGE_PTR(addr);
@@ -133,13 +129,13 @@
 		page = *((unsigned long *) page);
 	}
 	if (!(page & PAGE_PRESENT)) {
-		do_no_page(0 /* PAGE_RW */ ,addr,tsk);
+		do_no_page(vma, addr, 0 /* PAGE_RW */);
 		goto repeat;
 	}
 	if (!(page & PAGE_RW)) {
-		if(!(page & PAGE_COW))
+		if (!(page & PAGE_COW))
 			readonly = 1;
-		do_wp_page(PAGE_RW | PAGE_PRESENT,addr,tsk);
+		do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT);
 		goto repeat;
 	}
 /* this is a hack for non-kernel-mapped video buffers and similar */
@@ -150,12 +146,34 @@
 	page &= PAGE_MASK;
 	page += addr & ~PAGE_MASK;
 	*(unsigned long *) page = data;
-	if(readonly) {
+	if (readonly) {
 		*(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
 		invalidate();
 	} 
 }
 
+static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr)
+{
+	struct vm_area_struct * vma;
+
+	addr &= PAGE_MASK;
+	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			return NULL;
+		if (vma->vm_end > addr)
+			break;
+	}
+	if (vma->vm_start <= addr)
+		return vma;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		return NULL;
+	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
+		return NULL;
+	vma->vm_offset -= vma->vm_start - addr;
+	vma->vm_start = addr;
+	return vma;
+}
+
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
@@ -163,13 +181,21 @@
 static int read_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long * result)
 {
-	unsigned long low,high;
+	struct vm_area_struct * vma = find_vma(tsk, addr);
 
-	if (addr > TASK_SIZE-sizeof(long))
+	if (!vma)
 		return -EIO;
 	if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
-		low = get_long(tsk,addr & ~(sizeof(long)-1));
-		high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+		unsigned long low,high;
+		struct vm_area_struct * vma_high = vma;
+
+		if (addr + sizeof(long) >= vma->vm_end) {
+			vma_high = vma->vm_next;
+			if (!vma_high || vma_high->vm_start != vma->vm_end)
+				return -EIO;
+		}
+		low = get_long(vma, addr & ~(sizeof(long)-1));
+		high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
 		switch (addr & (sizeof(long)-1)) {
 			case 1:
 				low >>= 8;
@@ -186,7 +212,7 @@
 		}
 		*result = low;
 	} else
-		*result = get_long(tsk,addr);
+		*result = get_long(vma, addr);
 	return 0;
 }
 
@@ -197,13 +223,21 @@
 static int write_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long data)
 {
-	unsigned long low,high;
+	struct vm_area_struct * vma = find_vma(tsk, addr);
 
-	if (addr > TASK_SIZE-sizeof(long))
+	if (!vma)
 		return -EIO;
 	if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
-		low = get_long(tsk,addr & ~(sizeof(long)-1));
-		high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+		unsigned long low,high;
+		struct vm_area_struct * vma_high = vma;
+
+		if (addr + sizeof(long) >= vma->vm_end) {
+			vma_high = vma->vm_next;
+			if (!vma_high || vma_high->vm_start != vma->vm_end)
+				return -EIO;
+		}
+		low = get_long(vma, addr & ~(sizeof(long)-1));
+		high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
 		switch (addr & (sizeof(long)-1)) {
 			case 0: /* shouldn't happen, but safety first */
 				low = data;
@@ -227,10 +261,10 @@
 				high |= data >> 8;
 				break;
 		}
-		put_long(tsk,addr & ~(sizeof(long)-1),low);
-		put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high);
+		put_long(vma, addr & ~(sizeof(long)-1),low);
+		put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
 	} else
-		put_long(tsk,addr,data);
+		put_long(vma, addr, data);
 	return 0;
 }
 
diff -u --recursive --new-file v1.1.31/linux/kernel/sched.c linux/kernel/sched.c
--- v1.1.31/linux/kernel/sched.c	Thu Jun  9 18:56:13 1994
+++ linux/kernel/sched.c	Wed Jul 20 21:12:31 1994
@@ -148,7 +148,6 @@
 
 unsigned long itimer_ticks = 0;
 unsigned long itimer_next = ~0;
-static unsigned long lost_ticks = 0;
 
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
@@ -361,54 +360,76 @@
 	__sleep_on(p,TASK_UNINTERRUPTIBLE);
 }
 
-static struct timer_list * next_timer = NULL;
+/*
+ * The head for the timer-list has a "expires" field of MAX_UINT,
+ * and the sorting routine counts on this..
+ */
+static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
+#define SLOW_BUT_DEBUGGING_TIMERS 1
 
 void add_timer(struct timer_list * timer)
 {
 	unsigned long flags;
-	struct timer_list ** p;
+	struct timer_list *p;
 
-	if (!timer)
+#if SLOW_BUT_DEBUGGING_TIMERS
+	if (timer->next || timer->prev) {
+		printk("add_timer() called with non-zero list from %08lx\n",
+			((unsigned long *) &timer)[-1]);
 		return;
-	timer->next = NULL;
-	p = &next_timer;
+	}
+#endif
+	p = &timer_head;
+	timer->expires += jiffies;
 	save_flags(flags);
 	cli();
-	while (*p) {
-		if ((*p)->expires > timer->expires) {
-			(*p)->expires -= timer->expires;
-			timer->next = *p;
-			break;
-		}
-		timer->expires -= (*p)->expires;
-		p = &(*p)->next;
-	}
-	*p = timer;
+	do {
+		p = p->next;
+	} while (timer->expires > p->expires);
+	timer->next = p;
+	timer->prev = p->prev;
+	p->prev = timer;
+	timer->prev->next = timer;
 	restore_flags(flags);
 }
 
 int del_timer(struct timer_list * timer)
 {
 	unsigned long flags;
-	unsigned long expires = 0;
-	struct timer_list **p;
+#if SLOW_BUT_DEBUGGING_TIMERS
+	struct timer_list * p;
 
-	p = &next_timer;
+	p = &timer_head;
 	save_flags(flags);
 	cli();
-	while (*p) {
-		if (*p == timer) {
-			if ((*p = timer->next) != NULL)
-				(*p)->expires += timer->expires;
-			timer->expires += expires;
+	while ((p = p->next) != &timer_head) {
+		if (p == timer) {
+			timer->next->prev = timer->prev;
+			timer->prev->next = timer->next;
+			timer->next = timer->prev = NULL;
 			restore_flags(flags);
+			timer->expires -= jiffies;
 			return 1;
 		}
-		expires += (*p)->expires;
-		p = &(*p)->next;
+	}
+	if (p->next || p->prev)
+		printk("del_timer() called with timer not initialized\n");
+	restore_flags(flags);
+	return 0;
+#else	
+	save_flags(flags);
+	cli();
+	if (timer->next) {
+		timer->next->prev = timer->prev;
+		timer->prev->next = timer->next;
+		timer->next = timer->prev = NULL;
+		restore_flags(flags);
+		timer->expires -= jiffies;
+		return 1;
 	}
 	restore_flags(flags);
 	return 0;
+#endif
 }
 
 unsigned long timer_active = 0;
@@ -528,12 +549,15 @@
 {
 	unsigned long mask;
 	struct timer_struct *tp;
+	struct timer_list * timer;
 
 	cli();
-	while (next_timer && next_timer->expires == 0) {
-		void (*fn)(unsigned long) = next_timer->function;
-		unsigned long data = next_timer->data;
-		next_timer = next_timer->next;
+	while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) {
+		void (*fn)(unsigned long) = timer->function;
+		unsigned long data = timer->data;
+		timer->next->prev = timer->prev;
+		timer->prev->next = timer->next;
+		timer->next = timer->prev = NULL;
 		sti();
 		fn(data);
 		cli();
@@ -667,16 +691,8 @@
 	itimer_ticks++;
 	if (itimer_ticks > itimer_next)
 		need_resched = 1;
-	if (next_timer) {
-		if (next_timer->expires) {
-			next_timer->expires--;
-			if (!next_timer->expires)
-				mark_bh(TIMER_BH);
-		} else {
-			lost_ticks++;
-			mark_bh(TIMER_BH);
-		}
-	}
+	if (timer_head.next->expires < jiffies)
+		mark_bh(TIMER_BH);
 	if (tq_timer != &tq_last)
 		mark_bh(TQUEUE_BH);
 	sti();
diff -u --recursive --new-file v1.1.31/linux/mm/memory.c linux/mm/memory.c
--- v1.1.31/linux/mm/memory.c	Tue Jul 19 10:19:15 1994
+++ linux/mm/memory.c	Tue Jul 19 20:10:12 1994
@@ -554,20 +554,17 @@
  * to a shared page. It is done by copying the page to a new address
  * and decrementing the shared-page counter for the old page.
  *
- * Note that we do many checks twice (look at do_wp_page()), as
- * we have to be careful about race-conditions.
- *
  * Goto-purists beware: the only reason for goto's here is that it results
  * in better assembly code.. The "default" path will see no jumps at all.
  */
-static void __do_wp_page(unsigned long error_code, unsigned long address,
-	struct task_struct * tsk)
+void do_wp_page(struct vm_area_struct * vma, unsigned long address,
+	unsigned long error_code)
 {
 	unsigned long *pde, pte, old_page, prot;
 	unsigned long new_page;
 
 	new_page = __get_free_page(GFP_KERNEL);
-	pde = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
+	pde = PAGE_DIR_OFFSET(vma->vm_task->tss.cr3,address);
 	pte = *pde;
 	if (!(pte & PAGE_PRESENT))
 		goto end_wp_page;
@@ -582,13 +579,13 @@
 		goto bad_wp_page;
 	if (old_page & PAGE_RW)
 		goto end_wp_page;
-	tsk->mm->min_flt++;
+	vma->vm_task->mm->min_flt++;
 	prot = (old_page & ~PAGE_MASK) | PAGE_RW;
 	old_page &= PAGE_MASK;
 	if (mem_map[MAP_NR(old_page)] != 1) {
 		if (new_page) {
 			if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
-				++tsk->mm->rss;
+				++vma->vm_task->mm->rss;
 			copy_page(old_page,new_page);
 			*(unsigned long *) pte = new_page | prot;
 			free_page(old_page);
@@ -596,7 +593,7 @@
 			return;
 		}
 		free_page(old_page);
-		oom(tsk);
+		oom(vma->vm_task);
 		*(unsigned long *) pte = BAD_PAGE | prot;
 		invalidate();
 		return;
@@ -609,12 +606,12 @@
 bad_wp_page:
 	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
 	*(unsigned long *) pte = BAD_PAGE | PAGE_SHARED;
-	send_sig(SIGKILL, tsk, 1);
+	send_sig(SIGKILL, vma->vm_task, 1);
 	goto end_wp_page;
 bad_wp_pagetable:
 	printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte);
 	*pde = BAD_PAGETABLE | PAGE_TABLE;
-	send_sig(SIGKILL, tsk, 1);
+	send_sig(SIGKILL, vma->vm_task, 1);
 end_wp_page:
 	if (new_page)
 		free_page(new_page);
@@ -622,63 +619,12 @@
 }
 
 /*
- * check that a page table change is actually needed, and call
- * the low-level function only in that case..
+ * Ugly, ugly, but the goto's result in better assembly..
  */
-void do_wp_page(unsigned long error_code, unsigned long address,
-	struct task_struct * tsk)
-{
-	unsigned long page;
-	unsigned long * pg_table;
-
-	pg_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
-	page = *pg_table;
-	if (!page)
-		return;
-	if ((page & PAGE_PRESENT) && page < high_memory) {
-		pg_table = (unsigned long *) ((page & PAGE_MASK) + PAGE_PTR(address));
-		page = *pg_table;
-		if (!(page & PAGE_PRESENT))
-			return;
-		if (page & PAGE_RW)
-			return;
-		if (!(page & PAGE_COW)) {
-			if ((error_code & PAGE_USER) && tsk == current) {
-				current->tss.cr2 = address;
-				current->tss.error_code = error_code;
-				current->tss.trap_no = 14;
-				send_sig(SIGSEGV, tsk, 1);
-				return;
-			}
-		}
-		if (mem_map[MAP_NR(page)] == 1) {
-			*pg_table |= PAGE_RW | PAGE_DIRTY;
-			invalidate();
-			return;
-		}
-		__do_wp_page(error_code, address, tsk);
-		return;
-	}
-	printk("bad page directory entry %08lx\n",page);
-	*pg_table = 0;
-}
-
-static int __verify_write(unsigned long start, unsigned long size)
-{
-	size--;
-	size += start & ~PAGE_MASK;
-	size >>= PAGE_SHIFT;
-	start &= PAGE_MASK;
-	do {
-		do_wp_page(1,start,current);
-		start += PAGE_SIZE;
-	} while (size--);
-	return 0;
-}
-
 int verify_area(int type, const void * addr, unsigned long size)
 {
 	struct vm_area_struct * vma;
+	unsigned long start = (unsigned long) addr;
 
 	/* If the current user space is mapped to kernel space (for the
 	 * case where we use a fake user buffer with get_fs/set_fs()) we
@@ -690,27 +636,52 @@
 	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
 		if (!vma)
 			goto bad_area;
-		if (vma->vm_end > (unsigned long) addr)
+		if (vma->vm_end > start)
 			break;
 	}
-	if (vma->vm_start <= (unsigned long) addr)
+	if (vma->vm_start <= start)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
 		goto bad_area;
-	if (vma->vm_end - (unsigned long) addr > current->rlim[RLIMIT_STACK].rlim_cur)
+	if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur)
 		goto bad_area;
+
 good_area:
-	while (vma->vm_end - (unsigned long) addr < size) {
-		struct vm_area_struct * next = vma->vm_next;
-		if (!next)
+	if (!wp_works_ok && type == VERIFY_WRITE)
+		goto check_wp_fault_by_hand;
+	for (;;) {
+		struct vm_area_struct * next;
+		if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
 			goto bad_area;
-		if (vma->vm_end != next->vm_start)
+		if (vma->vm_end - start >= size)
+			return 0;
+		next = vma->vm_next;
+		if (!next || vma->vm_end != next->vm_start)
 			goto bad_area;
 		vma = next;
 	}
-	if (wp_works_ok || type == VERIFY_READ || !size)
-		return 0;
-	return __verify_write((unsigned long) addr,size);
+
+check_wp_fault_by_hand:
+	size--;
+	size += start & ~PAGE_MASK;
+	size >>= PAGE_SHIFT;
+	start &= PAGE_MASK;
+
+	for (;;) {
+		if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
+			goto bad_area;
+		do_wp_page(vma, start, PAGE_PRESENT);
+		if (!size)
+			return 0;
+		size--;
+		start += PAGE_SIZE;
+		if (start < vma->vm_end)
+			continue;
+		vma = vma->vm_next;
+		if (!vma || vma->vm_start != start)
+			break;
+	}
+
 bad_area:
 	return -EFAULT;
 }
@@ -885,12 +856,33 @@
 	return 0;
 }
 
-static void handle_no_page(struct vm_area_struct * vma,
-	unsigned long address, unsigned long error_code)
+void do_no_page(struct vm_area_struct * vma, unsigned long address,
+	unsigned long error_code)
 {
-	unsigned long page;
-	int prot;
+	unsigned long page, entry, prot;
+
+	page = get_empty_pgtable(vma->vm_task,address);
+	if (!page)
+		return;
+	page &= PAGE_MASK;
+	page += PAGE_PTR(address);
+	entry = *(unsigned long *) page;
+	if (entry & PAGE_PRESENT)
+		return;
+	if (entry) {
+		++vma->vm_task->mm->rss;
+		++vma->vm_task->mm->maj_flt;
+		swap_in((unsigned long *) page);
+		return;
+	}
+	address &= PAGE_MASK;
 
+	if (!vma->vm_ops || !vma->vm_ops->nopage) {
+		++vma->vm_task->mm->rss;
+		++vma->vm_task->mm->min_flt;
+		get_empty_page(vma->vm_task,address);
+		return;
+	}
 	page = get_free_page(GFP_KERNEL);
 	if (share_page(vma, address, error_code, page)) {
 		++vma->vm_task->mm->min_flt;
@@ -904,8 +896,10 @@
 	++vma->vm_task->mm->maj_flt;
 	++vma->vm_task->mm->rss;
 	page = vma->vm_ops->nopage(vma, address, page, error_code);
-	if (share_page(vma, address, error_code, 0))
+	if (share_page(vma, address, error_code, 0)) {
+		free_page(page);
 		return;
+	}
 	prot = vma->vm_page_prot;
 	if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1)
 		prot &= ~PAGE_RW;
@@ -915,28 +909,20 @@
 	oom(current);
 }
 
-void do_no_page(unsigned long error_code, unsigned long address,
-	struct task_struct *tsk)
+/*
+ * This routine handles page faults.  It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
-	unsigned long page, tmp;
 	struct vm_area_struct * vma;
+	unsigned long address;
+	unsigned long page;
 
-	page = get_empty_pgtable(tsk,address);
-	if (!page)
-		return;
-	page &= PAGE_MASK;
-	page += PAGE_PTR(address);
-	tmp = *(unsigned long *) page;
-	if (tmp & PAGE_PRESENT)
-		return;
-	if (tmp) {
-		++tsk->mm->rss;
-		++tsk->mm->maj_flt;
-		swap_in((unsigned long *) page);
-		return;
-	}
-	address &= PAGE_MASK;
-	for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
+	/* get the address */
+	__asm__("movl %%cr2,%0":"=r" (address));
+	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
 		if (!vma)
 			goto bad_area;
 		if (vma->vm_end > address)
@@ -946,81 +932,63 @@
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
 		goto bad_area;
-	if (vma->vm_end - address > tsk->rlim[RLIMIT_STACK].rlim_cur)
+	if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur)
 		goto bad_area;
-	vma->vm_offset -= vma->vm_start - address;
-	vma->vm_start = address;
-
+	vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+	vma->vm_start = (address & PAGE_MASK);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
 good_area:
-	if (!vma->vm_ops || !vma->vm_ops->nopage) {
-		++tsk->mm->rss;
-		++tsk->mm->min_flt;
-		get_empty_page(tsk,address);
+	if (regs->eflags & VM_MASK) {
+		unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
+		if (bit < 32)
+			current->screen_bitmap |= 1 << bit;
+	}
+	if (error_code & PAGE_PRESENT) {
+		if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT)
+			goto bad_area;
+#ifdef CONFIG_TEST_VERIFY_AREA
+		if (regs->cs == KERNEL_CS)
+			printk("WP fault at %08x\n", regs->eip);
+#endif
+		do_wp_page(vma, address, error_code);
 		return;
 	}
-	handle_no_page(vma, address, error_code);
+	if (!(vma->vm_page_prot & PAGE_PRESENT))
+		goto bad_area;
+	do_no_page(vma, address, error_code);
 	return;
 
-bad_area:
-	if (tsk != current)
-		goto kernel_needs_bad_page;
-	tsk->tss.cr2 = address;
-	tsk->tss.error_code = error_code;
-	tsk->tss.trap_no = 14;
-	send_sig(SIGSEGV,tsk,1);
-	if (error_code & 4)	/* user level access? */
-		return;
-
-kernel_needs_bad_page:
-	++tsk->mm->rss;
-	++tsk->mm->min_flt;
-	get_empty_page(tsk,address);
-}
-
 /*
- * This routine handles page faults.  It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
  */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
-{
-	unsigned long address;
-	unsigned long page;
-	unsigned int bit;
-
-	/* get the address */
-	__asm__("movl %%cr2,%0":"=r" (address));
-	if (address < TASK_SIZE) {
-		if (regs->eflags & VM_MASK) {
-			bit = (address - 0xA0000) >> PAGE_SHIFT;
-			if (bit < 32)
-				current->screen_bitmap |= 1 << bit;
-		}
-		if (error_code & PAGE_PRESENT) {
-#ifdef CONFIG_TEST_VERIFY_AREA
-			if (regs->cs == KERNEL_CS)
-				printk("WP fault at %08x\n", regs->eip);
-#endif
-			do_wp_page(error_code, address, current);
-		} else {
-			do_no_page(error_code, address, current);
-		}
+bad_area:
+	if (error_code & PAGE_USER) {
+		current->tss.cr2 = address;
+		current->tss.error_code = error_code;
+		current->tss.trap_no = 14;
+		send_sig(SIGSEGV, current, 1);
 		return;
 	}
-	address -= TASK_SIZE;
-	if (wp_works_ok < 0 && address == 0 && (error_code & PAGE_PRESENT)) {
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+	if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_PRESENT)) {
 		wp_works_ok = 1;
 		pg0[0] = PAGE_SHARED;
 		printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
 		return;
 	}
-	if (address < PAGE_SIZE) {
+	if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) {
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
 		pg0[0] = PAGE_SHARED;
 	} else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
-	printk(" at kernel address %08lx\n",address);
-	address += TASK_SIZE;
+	printk(" at virtual address %08lx\n",address);
 	__asm__("movl %%cr3,%0" : "=r" (page));
 	printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n",
 		current->tss.cr3, page);
diff -u --recursive --new-file v1.1.31/linux/net/inet/af_inet.c linux/net/inet/af_inet.c
--- v1.1.31/linux/net/inet/af_inet.c	Fri Jul  8 13:26:42 1994
+++ linux/net/inet/af_inet.c	Wed Jul 20 18:01:59 1994
@@ -615,6 +615,7 @@
 	sk->timeout = 0;
 	sk->broadcast = 0;
 	sk->localroute = 0;
+	sk->timer.next = sk->timer.prev = NULL;
 	sk->timer.data = (unsigned long)sk;
 	sk->timer.function = &net_timer;
 	skb_queue_head_init(&sk->back_log);
diff -u --recursive --new-file v1.1.31/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.31/linux/net/inet/arp.c	Tue Jun 21 14:16:27 1994
+++ linux/net/inet/arp.c	Wed Jul 20 17:56:56 1994
@@ -752,7 +752,7 @@
 		skb_queue_head_init(&entry->skb);
 		entry->next = arp_tables[hash];
 		arp_tables[hash] = entry;
-
+		entry->timer.next = entry->timer.prev = NULL;
 		sti();
 	}
 
@@ -841,6 +841,7 @@
 		entry->next = arp_tables[hash];
 		entry->dev = dev;
 		arp_tables[hash] = entry;
+		entry->timer.next = entry->timer.prev = NULL;
 		entry->timer.function = arp_expire_request;
 		entry->timer.data = (unsigned long)entry;
 		entry->timer.expires = ARP_RES_TIME;
@@ -1048,6 +1049,7 @@
 		entry->hlen = hlen;
 		entry->htype = htype;
 		entry->next = arp_tables[hash];
+		entry->timer.next = entry->timer.prev = NULL;
 		arp_tables[hash] = entry;
 		skb_queue_head_init(&entry->skb);
 	}
diff -u --recursive --new-file v1.1.31/linux/net/inet/dev.c linux/net/inet/dev.c
--- v1.1.31/linux/net/inet/dev.c	Tue Jun 21 14:16:28 1994
+++ linux/net/inet/dev.c	Wed Jul 20 15:26:05 1994
@@ -336,6 +336,8 @@
 void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
 	unsigned long flags;
+	int nitcount;
+	struct packet_type *ptype;
 	int where = 0;		/* used to say if the packet should go	*/
 				/* at the front or the back of the	*/
 				/* queue - front is a retranmsit try	*/
@@ -418,6 +420,17 @@
 #endif		
 	}
 	restore_flags(flags);
+
+	/* copy outgoing packets to any sniffer packet handlers */
+	for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) {
+		if (ptype->type == htons(ETH_P_ALL)) {
+			struct sk_buff *skb2;
+			if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+				break;
+			ptype->func(skb2, skb->dev, ptype);
+			nitcount--;
+		}
+	}
 
 	if (dev->hard_start_xmit(skb, dev) == 0) {
 		/*
diff -u --recursive --new-file v1.1.31/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.31/linux/net/inet/tcp.c	Mon Jul 18 14:48:32 1994
+++ linux/net/inet/tcp.c	Wed Jul 20 18:05:15 1994
@@ -681,8 +681,7 @@
 	save_flags(flags);
 	cli();
 	skb = sk->partial;
-	if (skb) 
-	{
+	if (skb) {
 		sk->partial = NULL;
 		del_timer(&sk->partial_timer);
 	}
@@ -711,6 +710,7 @@
 	if (tmp)
 		del_timer(&sk->partial_timer);
 	sk->partial = skb;
+	sk->partial_timer.next = sk->partial_timer.prev = NULL;
 	sk->partial_timer.expires = HZ;
 	sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
 	sk->partial_timer.data = (unsigned long) sk;
@@ -1987,6 +1987,7 @@
 	newsk->urg_data = 0;
 	newsk->retransmits = 0;
 	newsk->destroy = 0;
+	newsk->timer.next = newsk->timer.prev = NULL;
 	newsk->timer.data = (unsigned long)newsk;
 	newsk->timer.function = &net_timer;
 	newsk->dummy_th.source = skb->h.th->dest;
@@ -2217,6 +2218,15 @@
 			 * XXX if retransmit count reaches limit, is tcp_close()
 			 * called with timeout == 1 ? if not, we need to fix that.
 			 */
+			if (!timeout) {
+				int timer_active;
+
+				timer_active = del_timer(&sk->timer);
+				if (timer_active)
+					add_timer(&sk->timer);
+				else
+					reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
+			}
 #ifdef NOTDEF
 			/* 
 			 *	Start a timer.
