diff -u --recursive --new-file v1.1.90/linux/CREDITS linux/CREDITS
--- v1.1.90/linux/CREDITS	Thu Feb  9 10:18:49 1995
+++ linux/CREDITS	Sun Feb 12 15:00:45 1995
@@ -682,6 +682,11 @@
 S: 10963 Berlin
 S: Germany
 
+N: Ken Pizzini
+E: ken@halcyon.com
+D: CDROM driver "sonycd535" (Sony CDU-535/531)
+S: 
+
 N: Stefan Probst
 E: snprobst@immd4.informatik.uni-erlangen.de
 D: The Linux Support Team Erlangen
diff -u --recursive --new-file v1.1.90/linux/Makefile linux/Makefile
--- v1.1.90/linux/Makefile	Thu Feb  9 10:18:49 1995
+++ linux/Makefile	Thu Feb  9 10:11:16 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 90
+SUBLEVEL = 91
 
 ARCH = i386
 
diff -u --recursive --new-file v1.1.90/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v1.1.90/linux/arch/alpha/kernel/process.c	Sun Feb  5 19:31:50 1995
+++ linux/arch/alpha/kernel/process.c	Thu Feb  9 10:09:53 1995
@@ -47,15 +47,6 @@
 }
 
 /*
- * Do necessary setup to start up a newly executed thread.
- */
-void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
-{
-	regs->pc = pc;
-	wrusp(sp);
-}
-
-/*
  * Free current thread data structures etc..
  */
 void exit_thread(void)
diff -u --recursive --new-file v1.1.90/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.1.90/linux/arch/i386/config.in	Thu Feb  9 10:18:49 1995
+++ linux/arch/i386/config.in	Sun Feb 12 17:10:38 1995
@@ -43,7 +43,7 @@
 bool 'TCP/IP networking' CONFIG_INET y
 if [ "$CONFIG_INET" = "y" ]; then
 bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n
-bool 'IP multicasting (ALPHA)' CONFIG_IP_MULTICAST n
+bool 'IP multicasting' CONFIG_IP_MULTICAST n
 bool 'IP firewalling' CONFIG_IP_FIREWALL n
 bool 'IP accounting' CONFIG_IP_ACCT n
 comment '(it is safe to leave these untouched)'
@@ -119,7 +119,6 @@
 fi
 bool 'PPP (point-to-point) support' CONFIG_PPP n
 bool 'PLIP (parallel port) support' CONFIG_PLIP 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
 if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
@@ -162,7 +161,7 @@
 		bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
 	fi
 	bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
-	bool 'DE425, DE434, DE435 support' CONFIG_DE4x5 n
+	bool 'DE425, DE434, DE435 support' CONFIG_DE4X5 n
 #	bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
 #	bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
 #	bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
@@ -195,6 +194,7 @@
   fi
 fi
 bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
+bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
 
 comment 'Filesystems'
 
@@ -210,7 +210,7 @@
 if [ "$CONFIG_INET" = "y" ]; then
 bool 'NFS filesystem support' CONFIG_NFS_FS y
 fi
-if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" ]; then
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
 	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
 else
 	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
diff -u --recursive --new-file v1.1.90/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.1.90/linux/arch/i386/kernel/process.c	Sun Feb  5 19:31:51 1995
+++ linux/arch/i386/kernel/process.c	Thu Feb  9 10:07:40 1995
@@ -114,17 +114,6 @@
 }
 
 /*
- * Do necessary setup to start up a newly executed thread.
- */
-void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp)
-{
-	regs->cs = USER_CS;
-	regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS;
-	regs->eip = eip;
-	regs->esp = esp;
-}
-
-/*
  * Free current thread data structures etc..
  */
 void exit_thread(void)
diff -u --recursive --new-file v1.1.90/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.1.90/linux/drivers/block/Makefile	Mon Jan 30 08:53:06 1995
+++ linux/drivers/block/Makefile	Sun Feb 12 15:00:45 1995
@@ -79,6 +79,11 @@
 SRCS := $(SRCS) xd.c
 endif
 
+ifdef CONFIG_CDU535
+OBJS := $(OBJS) sonycd535.o
+SRCS := $(SRCS) sonycd535.c
+endif
+
 all: block.a
 
 block.a: $(OBJS)
diff -u --recursive --new-file v1.1.90/linux/drivers/block/README.fd linux/drivers/block/README.fd
--- v1.1.90/linux/drivers/block/README.fd	Thu Feb  9 10:18:50 1995
+++ linux/drivers/block/README.fd	Thu Feb  9 18:29:53 1995
@@ -71,7 +71,7 @@
 	Tells the floppy driver that you don't have a Thinkpad.
 
  floppy=<drive>,<type>,cmos
-	Sets the cmos type of <drive> to <type>. Additionnaly, this drive is
+	Sets the cmos type of <drive> to <type>. Additionally, this drive is
 	allowed in the bitmask. This is useful if you have more than two
 	floppy drives (only two can be described in the physical cmos), or if
 	your BIOS uses non-standard CMOS types. The CMOS types are:
diff -u --recursive --new-file v1.1.90/linux/drivers/block/README.sonycd535 linux/drivers/block/README.sonycd535
--- v1.1.90/linux/drivers/block/README.sonycd535	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.sonycd535	Sun Feb 12 15:00:45 1995
@@ -0,0 +1,110 @@
+              README FOR LINUX SONY CDU-535/531 DRIVER
+              ========================================
+
+This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
+I do not think I have the documentation to add features like DMA support
+so if anyone else wants to pursue it or help me with it, please do.
+(I need to see what was done for the CDU-31A driver -- perhaps I can
+steal some of that code.)
+
+This is a Linux device driver for the Sony CDU-535 CDROM drive.  This is
+one of the older Sony drives with its own interface card (Sony bus).
+The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS
+your drive should be identified as a SONY CDU-535.  The driver works
+with a CDU-531 also.  One user reported that the driver worked on drives
+OEM'ed by Procomm, drive and interface board were labelled Procomm.
+
+The Linux driver is based on Corey Minyard's sonycd 0.3 driver for
+the CDU-31A.  Ron Jeppesen just changed the commands that were sent
+to the drive to correspond to the CDU-535 commands and registers.
+There were enough changes to let bugs creep in but it seems to be stable.
+Ron was able to tar an entire CDROM (should read all blocks) and built
+ghostview and xfig off Walnut Creek's X11R5/GNU CDROM.  xcdplayer and
+workman work with the driver.  Others have used the driver without
+problems except those dealing with wait loops (fixed in third release).
+Like Minyard's original driver this one uses a polled interface (this
+is also the default setup for the DOS driver).  It has not been tried
+with interrupts or DMA enabled on the board.
+
+REQUIREMENTS
+============
+
+	- Sony CDU-535 drive, preferably without interrupts and DMA 
+	  enabled on the card.
+
+	- Drive must be set up as unit 1.  Only the first unit will be 
+	  recognized
+
+	- you must enter your interface address into 
+          /usr/src/linux/include/linux/sonycd535.h and build the
+          appropriate kernel or use the "kernel command line" parameter
+                sonycd535=0x320
+          with the correct interface address.
+
+NOTES:
+======
+
+1) The drive MUST be turned on when booting or it will not be recognized!
+   (but see comments on modulized version below)
+
+2) when the cdrom device is opened the eject button is disabled to keep the
+   user from ejecting a mounted disk and replacing it with another.
+   Unfortunately xcdplayer and workman also open the cdrom device so you
+   have to use the eject button in the software.  Keep this in mind if your
+   cdrom player refuses to give up its disk -- exit workman or xcdplayer, or
+   umount the drive if it has been mounted.
+
+THANKS
+======
+
+Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting
+this project off the ground.  He wrote the initial release and
+the first two patches to this driver (0.1, 0.2, and 0.3).
+
+(aknowlegements from Ron Jeppesen in the 0.3 release:)
+Thanks to Corey Minyard who wrote the original CDU-31A driver on which
+this driver is based.  Thanks to Ken Pizzini and Bob Blair who provided 
+patches and feedback on the first release of this driver.
+
+Ken Pizzini
+ken@halcyon.com
+
+------------------------------------------------------------------------------
+(The following is from Joel Katz <Stimpson@Panix.COM>.)
+
+	To build a version of sony535.o that can be installed as a module,
+use the following command:
+
+gcc -c -D__KERNEL__ -DMODULE -O2 sonycd535.c -o sonycd535.o
+
+	To install the module, simply type:
+
+insmod sony535.o
+
+	And to remove it:
+
+rmmod sony535
+
+	The code checks to see if MODULE is defined and behaves as it used
+to if MODULE is not defined. That means your patched file should behave
+exactly as it used to if compiled into the kernel.
+
+	I have an externel drive, and I usually leave it powered off. I used
+to have to reboot if I needed to use the CDROM drive. Now I don't.
+
+	Even if you have an internal drive, why waste the 268K of memory
+(unswappable) that the driver uses if you use your CD-ROM drive infrequently?
+
+	This driver will not install (whether compiled in or loaded as a
+module) if the CDROM drive is not available during its initialization. This
+means that you can have the driver compiled into the kernel and still load
+the module later (assuming the driver doesn't install itself during
+power-on). This only wastes 12K when you boot with the CDROM drive off.
+
+	This is what I usually do; I leave the driver compiled into the
+kernel, but load it as a module if I powered the system up with the drive
+off and then later decided to use the CDROM drive.
+
+	Since the driver only uses a single page to point to the chunks,
+attempting to set the buffer cache to more than 2 Megabytes would be very
+bad; don't do that.
diff -u --recursive --new-file v1.1.90/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.1.90/linux/drivers/block/blk.h	Sun Jan 22 21:39:39 1995
+++ linux/drivers/block/blk.h	Sun Feb 12 15:00:45 1995
@@ -47,6 +47,9 @@
 #ifdef CONFIG_AZTCD
 extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end);
 #endif
+#ifdef CONFIG_CDU535
+extern unsigned long sony535_init(unsigned long mem_start, unsigned long mem_end);
+#endif
 #ifdef CONFIG_BLK_DEV_HD
 extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
 #endif
@@ -177,6 +180,15 @@
 
 #define DEVICE_NAME "Aztech CD-ROM"
 #define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
diff -u --recursive --new-file v1.1.90/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.1.90/linux/drivers/block/floppy.c	Thu Feb  9 10:18:50 1995
+++ linux/drivers/block/floppy.c	Thu Feb  9 18:29:53 1995
@@ -91,7 +91,7 @@
 
 #include <linux/config.h>
 
-/* do print messages for unexpected interupts */
+/* do print messages for unexpected interrupts */
 static int print_unex=1;
 
 #ifndef FD_MODULE
@@ -3205,7 +3205,7 @@
 
 /* we make the invert_dcl function global. One day, somebody might
 want to centralize all thinkpad related options into one lilo option,
-there are just so many thinpad related quirks! */
+there are just so many thinkpad related quirks! */
 void floppy_invert_dcl(int *ints,int param)
 {
 	int i;
diff -u --recursive --new-file v1.1.90/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.1.90/linux/drivers/block/ll_rw_blk.c	Thu Jan 26 07:49:04 1995
+++ linux/drivers/block/ll_rw_blk.c	Sun Feb 12 15:00:45 1995
@@ -399,6 +399,9 @@
 	int correct_size;
 	struct blk_dev_struct * dev;
 	int i;
+#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A)
+	int sonycd_save_mem_start;
+#endif
 
 	/* Make sure that the first block contains something reasonable */
 	while (!*bh) {
@@ -542,9 +545,24 @@
 #ifdef CONFIG_BLK_DEV_XD
 	mem_start = xd_init(mem_start,mem_end);
 #endif
+#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A)
+	{  /* since controllers for 535 and 31A can be at same location
+	    * we have to be careful.
+	    */
+		sonycd_save_mem_start = mem_start;
+		mem_start = cdu31a_init(mem_start,mem_end);
+		if ( mem_start == sonycd_save_mem_start ) {  /* CDU31A not found */
+			mem_start = sony535_init(mem_start,mem_end);
+		}
+	}
+#else
 #ifdef CONFIG_CDU31A
 	mem_start = cdu31a_init(mem_start,mem_end);
 #endif
+#ifdef CONFIG_CDU535
+	mem_start = sony535_init(mem_start,mem_end);
+#endif
+#endif  /* CONFIG_CDU31A && CONFIG_CDU535 */
 #ifdef CONFIG_MCD
 	mem_start = mcd_init(mem_start,mem_end);
 #endif
diff -u --recursive --new-file v1.1.90/linux/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c
--- v1.1.90/linux/drivers/block/sonycd535.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/sonycd535.c	Sun Feb 12 15:00:45 1995
@@ -0,0 +1,1548 @@
+/*
+ * Sony CDU-535 interface device driver
+ *
+ * This is a modified version of the CDU-31A device driver (see below).
+ * Changes were made using documentation for the CDU-531 (which Sony
+ * assures me is very similar to the 535) and partial disassembly of the
+ * DOS driver.  I used Minyard's driver and replaced the the CDU-31A 
+ * commands with the CDU-531 commands.  This was complicated by a different
+ * interface protocol with the drive.  The driver is still polled.
+ *
+ * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
+ * I tried polling without the sony_sleep during the data transfers but
+ * it did not speed things up any.
+ *
+ *  5/23/93 (rgj) changed the major number to 21 to get rid of conflict
+ * with CDU-31A driver.  This is the also the number from the Linux
+ * Device Driver Registry for the Sony Drive.  Hope nobody else is using it.
+ *
+ *  8/29/93 (rgj) remove the configuring of the interface board address
+ * from the top level configuration, you have to modify it in this file.
+ *
+ * 1/26/95 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
+ *
+ * Things to do:
+ *  - handle errors and status better, put everything into a single word
+ *  - use interrupts, DMA
+ *
+ *  Known Bugs:
+ *  -
+ *
+ *   Ron Jeppesen (ronj.an@site007.saic.com)
+ *
+ *
+ *------------------------------------------------------------------------
+ * Sony CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to ronj above)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunatly) a polled interface.  This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables.  Some (like mine) do not even have the capability to
+ * handle interrupts or DMA.  For this reason you will see a lot of
+ * the following:
+ *
+ *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ *   while ((retry_count > jiffies) && (! <some condition to wait for))
+ *   {
+ *      while (handle_sony_cd_attention())
+ *         ;
+ *
+ *      sony_sleep();
+ *   }
+ *   if (the condition not met)
+ *   {
+ *      return an error;
+ *   }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try.  it also handles attentions, which are
+ * asyncronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk.  The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal.  A lot of conversion goes on.
+ *
+ *  Copyright (C) 1993  Corey Minyard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#ifdef CONFIG_CDU535
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/mm.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+#include <linux/sonycd535.h>
+
+#define MAJOR_NR CDU535_CDROM_MAJOR
+
+#ifdef MODULE
+# include "/usr/src/linux/drivers/block/blk.h"
+#else
+# include "blk.h"
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+/*
+ * this is the base address of the interface card for the Sony CDU535
+ * CDROM drive.  If your jumpers are set for an address other than
+ * this one (the default), change the following line to the
+ * proper address.
+ */
+#ifndef CDU535_ADDRESS
+#define CDU535_ADDRESS	(0x340)
+#endif
+
+#define DEBUG	1
+
+/*
+ *  SONY535_BUFFER_SIZE determines the size of internal buffer used
+ *  by the drive.  It must be at least 2K and the larger the buffer
+ *  the better the transfer rate.  It does however take system memory.
+ *  On my system I get the following transfer rates using dd to read
+ *  10 Mb off /dev/cdrom.
+ *
+ *    8K buffer      43 Kb/sec
+ *   16K buffer      66 Kb/sec
+ *   32K buffer      91 Kb/sec
+ *   64K buffer     111 Kb/sec
+ *  128K buffer     123 Kb/sec
+ *  512K buffer     123 Kb/sec
+ */
+#define SONY535_BUFFER_SIZE	(64*1024)
+
+/*
+ *  if LOCK_DOORS is defined then the eject button is disabled while
+ * the device is open.
+ */
+#define LOCK_DOORS
+
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int cdu_open(struct inode *inode, struct file *filp);
+static inline unsigned int int_to_bcd(unsigned int val);
+static unsigned int bcd_to_int(unsigned int bcd);
+static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
+					   Byte * response, int nResponse, int ignoreStatusBit7);
+
+/* The base I/O address of the Sony Interface.  This is a variable (not a
+   #define) so it can be easily changed via some future ioctl() */
+static unsigned short sony_cd_base_io = CDU535_ADDRESS;
+
+/*
+ * The following are I/O addresses of the various registers for the drive.  The
+ * comment for the base address also applies here.
+ */
+static unsigned short select_unit_reg;
+static unsigned short result_reg;
+static unsigned short command_reg;
+static unsigned short read_status_reg;
+static unsigned short data_reg;
+
+static int initialized = 0;			/* Has the drive been initialized? */
+static int sony_disc_changed = 1;	/* Has the disk been changed
+									   since the last check? */
+static int sony_toc_read = 0;		/* Has the table of contents been
+									   read? */
+static unsigned int sony_buffer_size;	/* Size in bytes of the read-ahead
+										   buffer. */
+static unsigned int sony_buffer_sectors;	/* Size (in 2048 byte records) of
+											   the read-ahead buffer. */
+static unsigned int sony_usage = 0;	/* How many processes have the
+									   drive open. */
+
+static int sony_first_block = -1;	/* First OS block (512 byte) in
+									   the read-ahead buffer */
+static int sony_last_block = -1;	/* Last OS block (512 byte) in
+									   the read-ahead buffer */
+
+static struct s535_sony_toc *sony_toc;	/* Points to the table of
+										   contents. */
+static struct s535_sony_subcode *last_sony_subcode;		/* Points to the last
+														   subcode address read */
+#ifndef MODULE
+static unsigned char *sony_buffer;	/* Points to the read-ahead buffer */
+#else
+static unsigned char **sony_buffer;		/* Points to the pointers
+										   to the sector buffers */
+#endif
+static int sony_inuse = 0;		/* is the drive in use? Only one
+								   open at a time allowed */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play.  The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over.  This holds the
+ * position during a pause so a resume can restart it.  It uses the
+ * audio status variable above to tell if it is paused.
+ *   I just kept the CDU-31A driver behavior rather than using the PAUSE
+ * command on the CDU-535.
+ */
+static unsigned char cur_pos_msf[3] = {0, 0, 0};
+static unsigned char final_pos_msf[3] = {0, 0, 0};
+
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
+ */
+static int
+cdu535_check_media_change(dev_t full_dev)
+{
+	int     retval;
+
+	if (MINOR(full_dev) != 0) {
+		printk("Sony CD-ROM request error: invalid device.\n");
+		return 0;
+	}
+
+	/* if driver is not initialized, always return 0 */
+	retval = initialized ? sony_disc_changed : 0;
+	sony_disc_changed = 0;
+	return retval;
+}
+
+
+/*
+ * Wait a little while (used for polling the drive).  If in initialization,
+ * setting a timeout doesn't work, so just loop for a while  (we trust
+ * that the sony_sleep() call is protected by a test for proper jiffies count).
+ */
+static inline void
+sony_sleep(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies;
+	schedule();
+}
+
+/*------------------start of SONY CDU535 very specific ---------------------*/
+
+/****************************************************************************
+ * void select_unit( int unit_no )
+ *
+ *  Select the specified unit (0-3) so that subsequent commands reference it
+ ****************************************************************************/
+static void
+select_unit(int unit_no)
+{
+	unsigned int select_mask = ~(1 << unit_no);
+	outb(select_mask, select_unit_reg);
+}
+
+/***************************************************************************
+ * int read_result_reg( unsigned char *data_ptr )
+ *
+ *  Read a result byte from the Sony CDU controller, store in location pointed
+ * to by data_ptr.  Return zero on success, TIME_OUT if we did not receive
+ * data.
+ ***************************************************************************/
+static int
+read_result_reg(unsigned char *data_ptr)
+{
+	int     retry_count;
+	int     read_status;
+
+	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+	while (jiffies < retry_count) {
+		if (((read_status = inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0) {
+#if DEBUG > 1
+			printk("read_result_reg(): readStatReg = 0x%x\n", read_status);
+#endif
+			*data_ptr = inb(result_reg);
+			return 0;
+		} else {
+			sony_sleep();
+		}
+	}
+	printk(" Sony CDROM read_result_reg: TIME OUT!\n");
+	return TIME_OUT;
+}
+
+/****************************************************************************
+ * int read_exec_status( Byte status[2] )
+ *
+ *  Read the execution status of the last command and put into status. 
+ * Handles reading second status word if available.  Returns 0 on success,
+ * TIME_OUT on failure.
+ ****************************************************************************/
+static int
+read_exec_status(Byte status[2])
+{
+	status[1] = 0;
+	if (read_result_reg(&(status[0])) != 0)
+		return TIME_OUT;
+	if ((status[0] & 0x80) != 0) {	/* byte two follows */
+		if (read_result_reg(&(status[1])) != 0)
+			return TIME_OUT;
+	}
+#if DEBUG > 1
+	printk("read_exec_status: read 0x%x\n", status[0]);
+	if (status[0] & 0x80)
+		printk(" and 0x%x\n", status[1]);
+	printk("\n");
+#endif
+	return 0;
+}
+
+/****************************************************************************
+ * int check_drive_status( void )
+ *
+ *  Check the current drive status.  Using this before executing a command
+ * takes care of the problem of unsolicited drive status-2 messages.
+ * Add a check of the audio status if we think the disk is playing.
+ ****************************************************************************/
+static int
+check_drive_status(void)
+{
+	Byte    status, e_status[2];
+	int     CDD, ATN;
+	unsigned char cmd;
+
+	select_unit(0);
+	if (sony_audio_status == CDROM_AUDIO_PLAY) {	/* check status */
+		outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
+		if (read_result_reg(&status) == 0) {
+			switch (status) {
+			case 0x0:
+				break;		/* play in progress */
+			case 0x1:
+				break;		/* paused */
+			case 0x3:		/* audio play completed */
+			case 0x5:		/* play not requested */
+				sony_audio_status = CDROM_AUDIO_COMPLETED;
+				read_subcode();
+				break;
+			case 0x4:		/* error during play */
+				sony_audio_status = CDROM_AUDIO_ERROR;
+				break;
+			}
+		}
+	}
+	/* now check drive status */
+	outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
+	if (read_result_reg(&status) != 0)
+		return TIME_OUT;
+
+#if DEBUG > 1
+	printk("--check_drive_status() got 0x%x\n", status);
+#endif
+
+	if (status == 0)
+		return 0;
+
+	ATN = status & 0xf;
+	CDD = (status >> 4) & 0xf;
+
+	switch (ATN) {
+	case 0x0:
+		break;					/* go on to CDD stuff */
+	case SONY535_ATN_BUSY:
+		if (initialized)
+			printk("Sony CDROM error, drive busy\n");
+		return CD_BUSY;
+	case SONY535_ATN_EJECT_IN_PROGRESS:
+		printk("Sony CDROM error, eject in progress\n");
+		sony_audio_status = CDROM_AUDIO_INVALID;
+		return CD_BUSY;
+	case SONY535_ATN_RESET_OCCURRED:
+	case SONY535_ATN_DISC_CHANGED:
+	case SONY535_ATN_RESET_AND_DISC_CHANGED:
+#if DEBUG > 0
+		printk("Sony CDROM, reset occurred or disc changed\n");
+#endif
+		sony_disc_changed = 1;
+		sony_toc_read = 0;
+		sony_audio_status = CDROM_AUDIO_NO_STATUS;
+		sony_first_block = -1;
+		sony_last_block = -1;
+		if (initialized) {
+			cmd = SONY535_SPIN_UP;
+			do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
+			sony_get_toc();
+		}
+		return 0;
+	default:
+		printk("Sony CDROM error, drive busy (ATN=0x%x)\n", ATN);
+		return CD_BUSY;
+	}
+	switch (CDD) {				/* the 531 docs are not helpful in decoding this */
+	case 0x0:					/* just use the values from the DOS driver */
+	case 0x2:
+	case 0xa:
+		break;					/* no error */
+	case 0xc:
+		printk("check_drive_status(): CDD = 0xc! Not properly handled!\n");
+		return CD_BUSY;		/* ? */
+	default:
+		return CD_BUSY;
+	}
+	return 0;
+}	/* check_drive_status() */
+
+/*****************************************************************************
+ * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], 
+ *                Byte *response, int n_response, int ignore_status_bit7 )
+ *
+ *  Generic routine for executing commands.  The command and its parameters
+ *  should be placed in the cmd[] array, number of bytes in the command is
+ *  stored in nCmd.  The response from the command will be stored in the
+ *  response array.  The number of bytes you expect back (excluding status)
+ *  should be passed in nReponse.  Finally, some
+ *  commands set bit 7 of the return status even when there is no second
+ *  status byte, on these commands set ignoreStatusBit7 TRUE.
+ *    If the command was sent and data recieved back, then we return 0,
+ *  else we return TIME_OUT.  You still have to check the status yourself.
+ *    You should call check_drive_status() before calling this routine
+ *  so that you do not lose notifications of disk changes, etc.
+ ****************************************************************************/
+static int
+do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
+			Byte * response, int n_response, int ignore_status_bit7)
+{
+	int     i;
+
+	/* write out the command */
+	for (i = 0; i < n_cmd; i++)
+		outb(cmd[i], command_reg);
+
+	/* read back the status */
+	if (read_result_reg(status) != 0)
+		return TIME_OUT;
+	if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {	
+		/* get second status byte */
+		if (read_result_reg(status + 1) != 0)
+			return TIME_OUT;
+	} else {
+		status[1] = 0;
+	}
+#if DEBUG > 2
+	printk("do_sony_cmd %x: %x %x\n", *cmd, status[0], status[1]);
+#endif
+
+	/* do not know about when I should read set of data and when not to */
+	if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
+		return 0;
+
+	/* else, read in rest of data */
+	for (i = 0; 0 < n_response; n_response--, i++)
+		if (read_result_reg(response + i) != 0)
+			return TIME_OUT;
+	return 0;
+}	/* do_sony_cmd() */
+
+/**************************************************************************
+ * int set_drive_mode( int mode, Byte status[2] )
+ *
+ *  Set the drive mode to the specified value (mode=0 is audio, mode=e0
+ * is mode-1 CDROM
+ **************************************************************************/
+static int
+set_drive_mode(int mode, Byte status[2])
+{
+	Byte    cmd_buff[2], ret_buff[1];
+
+	cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+	cmd_buff[1] = mode;
+	return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
+}
+
+/***************************************************************************
+ * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], 
+ *                             Byte *data_buff, int buff_size )
+ *
+ *  Read n_blocks of data from the CDROM starting at position params[0:2],
+ *  number of blocks in stored in params[3:5] -- both these are already
+ *  int bcd format.
+ *  Transfer the data into the buffer pointed at by data_buff.  buff_size
+ *  gives the number of bytes available in the buffer.
+ *    The routine returns number of bytes read in if successful, otherwise
+ *  it returns one of the standard error returns.
+ ***************************************************************************/
+static int
+#ifndef MODULE
+seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
+					   Byte * data_buff, int buf_size)
+#else
+seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
+					   unsigned char **buff, int buf_size)
+#endif
+{
+	int     i;
+	const int block_size = 2048;
+	Byte    cmd_buff[7];
+	int     read_status;
+	int     retry_count;
+#ifdef MODULE
+	Byte   *data_buff;
+	int     sector_count = 0;
+#else
+	Byte   *start_pos = data_buff;
+#endif
+
+	if (buf_size < ((long)block_size) * n_blocks)
+		return NO_ROOM;
+
+	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
+
+	/* send command to read the data */
+	cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
+	for (i = 0; i < 6; i++)
+		cmd_buff[i + 1] = params[i];
+	for (i = 0; i < 7; i++)
+		outb(cmd_buff[i], command_reg);
+
+	/* read back the data one block at a time */
+	while (0 < n_blocks--) {
+		/* wait for data to be ready */
+		retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+		while (jiffies < retry_count) {
+			read_status = inb(read_status_reg);
+			if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
+				read_exec_status(status);
+				return BAD_STATUS;
+			}
+			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
+				/* data is ready, read it */
+#ifdef MODULE
+				data_buff = buff[sector_count++];
+#endif
+				for (i = 0; i < block_size; i++)
+					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
+				break;			/* exit the timeout loop */
+			}
+			sony_sleep();		/* data not ready, sleep a while */
+		}
+		if (retry_count <= jiffies)
+			return TIME_OUT;	/* if we reach this stage */
+	}
+
+	/* read all the data, now read the status */
+	if ((i = read_exec_status(status)) != 0)
+		return i;
+#ifndef MODULE
+	return data_buff - start_pos;
+#else
+	return block_size * sector_count;
+#endif
+}	/* seek_and_read_N_blocks() */
+
+/****************************************************************************
+ * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
+ *
+ *  Read in the table of contents data.  Converts all the bcd data
+ * into integers in the toc structure.
+ ****************************************************************************/
+static int
+request_toc_data(Byte status[2], struct s535_sony_toc *toc)
+{
+	int     to_status;
+	int     i, j, n_tracks, track_no;
+	Byte    cmd_no = 0xb2;
+	Byte    track_address_buffer[5];
+	int     first_track_num, last_track_num;
+
+	/* read the fixed portion of the table of contents */
+	if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
+		return to_status;
+
+	/* convert the data into integers so we can use them */
+	first_track_num = bcd_to_int(toc->first_track_num);
+	last_track_num = bcd_to_int(toc->last_track_num);
+	n_tracks = last_track_num - first_track_num + 1;
+
+	/* read each of the track address descriptors */
+	for (i = 0; i < n_tracks; i++) {
+		/* read the descriptor into a temporary buffer */
+		for (j = 0; j < 5; j++) {
+			if (read_result_reg(track_address_buffer + j) != 0)
+				return TIME_OUT;
+			if (j == 1)		/* need to convert from bcd */
+				track_no = bcd_to_int(track_address_buffer[j]);
+		}
+		/* copy the descriptor to proper location - sonycd.c just fills */
+		memcpy(toc->tracks + i, track_address_buffer, 5);
+	}
+	return 0;
+}	/* request_toc_data() */
+
+/***************************************************************************
+ * int spin_up_drive( Byte status[2] )
+ *
+ *  Spin up the drive (unless it is already spinning).
+ ***************************************************************************/
+static int
+spin_up_drive(Byte status[2])
+{
+	Byte    cmd_buff[1];
+
+	/* first see if the drive is already spinning */
+	cmd_buff[0] = SONY535_REQUEST_DRIVE_STATUS_1;
+	if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0)
+		return TIME_OUT;
+	if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
+		return 0;				/* its already spinning */
+
+	/* else, give the spin-up command */
+	cmd_buff[0] = SONY535_SPIN_UP;
+	return do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+}	/* spin_up_drive() */
+
+/*--------------------end of SONY CDU535 very specific ---------------------*/
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+	int     retval;
+
+
+	retval = (val / 10) << 4;
+	retval = retval | val % 10;
+	return retval;
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+	return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, unsigned char *msf)
+{
+	log = log + LOG_START_OFFSET;
+	msf[0] = int_to_bcd(log / 4500);
+	log = log % 4500;
+	msf[1] = int_to_bcd(log / 75);
+	msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(unsigned char *msf)
+{
+	unsigned int log;
+
+
+	log = bcd_to_int(msf[2]);
+	log += bcd_to_int(msf[1]) * 75;
+	log += bcd_to_int(msf[0]) * 4500;
+	log = log - LOG_START_OFFSET;
+
+	return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size,
+			unsigned char *buf)
+{
+	buf[0] = size / 65536;
+	size = size % 65536;
+	buf[1] = size / 256;
+	buf[2] = size % 256;
+}
+
+
+/*
+ * The OS calls this to perform a read or write operation to the drive.
+ * Write obviously fail.  Reads to a read ahead of sony_buffer_size
+ * bytes to help speed operations.  This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu535_request(void)
+{
+	int     block;
+	unsigned int dev;
+	int     nsect;
+	unsigned char params[10];
+	int     copyoff;
+	int     spin_up_retry;
+	unsigned int read_size;
+	unsigned char status[2], cmd[2];
+
+
+	if (!sony_inuse) {
+		cdu_open(NULL, NULL);
+	}
+	while (1) {
+		/*
+		 * The beginning here is stolen from the hard disk driver.  I hope
+		 * its right.
+		 */
+		if (!(CURRENT) || CURRENT->dev < 0) {
+			return;
+		}
+		INIT_REQUEST;
+		dev = MINOR(CURRENT->dev);
+		block = CURRENT->sector;
+		nsect = CURRENT->nr_sectors;
+		if (dev != 0) {
+			end_request(0);
+			continue;
+		}
+		switch (CURRENT->cmd) {
+		case READ:
+			/*
+			 * If the block address is invalid or the request goes beyond the end of
+			 * the media, return an error.
+			 */
+
+			if (sony_toc->lead_out_start_lba <= (block / 4)) {
+				end_request(0);
+				return;
+			}
+			if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
+				end_request(0);
+				return;
+			}
+			while (0 < nsect) {
+				/*
+				 * If the requested sector is not currently in the read-ahead buffer,
+				 * it must be read in.
+				 */
+				if ((block < sony_first_block) || (sony_last_block < block)) {
+					sony_first_block = (block / 4) * 4;
+					log_to_msf(block / 4, params);
+
+					/*
+					 * If the full read-ahead would go beyond the end of the media, trim
+					 * it back to read just till the end of the media.
+					 */
+					if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
+						sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+						read_size = sony_toc->lead_out_start_lba - (block / 4);
+					} else {
+						sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+						read_size = sony_buffer_sectors;
+					}
+					size_to_buf(read_size, &params[3]);
+
+					/*
+					 * Read the data.  If the drive was not spinning, spin it up and try
+					 * once more.  I know, the goto is ugly, but I am too lazy to fix it.
+					 */
+					spin_up_retry = 0;
+				  try_read_again:
+#if DEBUG > 1
+					if (check_drive_status() != 0) {	/* drive not ready */
+						sony_first_block = -1;
+						sony_last_block = -1;
+						end_request(0);
+						return;
+					}
+#endif
+					if (seek_and_read_N_blocks(params, read_size, status, sony_buffer,
+											   (read_size * 2048)) < 0) {
+						if ((status[0] & SONY535_STATUS1_NOT_SPINNING) && (!spin_up_retry)) {
+							printk(" Sony535 Debug -- calling spin up when reading data!\n");
+							cmd[0] = SONY535_SPIN_UP;
+							do_sony_cmd(cmd, 1, status, NULL, 0, 0);
+							spin_up_retry = 1;
+							goto try_read_again;
+						}
+						printk("Sony CDROM Read error: 0x%.2x\n", status[0]);
+						sony_first_block = -1;
+						sony_last_block = -1;
+						end_request(0);
+						return;
+					}
+				}
+				/*
+				 * The data is in memory now, copy it to the buffer and advance to the
+				 * next block to read.
+				 */
+#ifndef MODULE
+				copyoff = (block - sony_first_block) * 512;
+				memcpy(CURRENT->buffer, sony_buffer + copyoff, 512);
+#else
+				copyoff = block - sony_first_block;
+				memcpy(CURRENT->buffer,
+					   sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
+#endif
+
+				block += 1;
+				nsect -= 1;
+				CURRENT->buffer += 512;
+			}
+
+			end_request(1);
+			break;
+
+		case WRITE:
+			end_request(0);
+			break;
+
+		default:
+			panic("Unkown SONY CD cmd");
+		}
+	}
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+	unsigned char status[2];
+	if (!sony_toc_read) {
+		/* do not call check_drive_status() from here since it can call this routine */
+		if (request_toc_data(status, sony_toc) < 0)
+			return;
+		sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+		sony_toc_read = 1;
+	}
+}
+
+
+/*
+ * Search for a specific track in the table of contents.  track is
+ * passed in bcd format
+ */
+static int
+find_track(int track)
+{
+	int     i;
+	int     num_tracks;
+
+
+	num_tracks = bcd_to_int(sony_toc->last_track_num) -
+		bcd_to_int(sony_toc->first_track_num) + 1;
+	for (i = 0; i < num_tracks; i++) {
+		if (sony_toc->tracks[i].track == track) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+	Byte    cmd = SONY535_REQUEST_SUB_Q_DATA, status[2];
+	int     dsc_status;
+
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
+							   sizeof (struct s535_sony_subcode), 1)) != 0) {
+		printk("Sony CDROM error 0x%.2x, %d (read_subcode)\n", status[0],
+			   dsc_status);
+		return -EIO;
+	}
+	return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing).  If the drive is paused or completed, the subcode information has
+ * already been stored, just use that.  The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+	struct cdrom_subchnl schi;
+
+
+	/* Get attention stuff */
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	sony_get_toc();
+	if (!sony_toc_read) {
+		return -EIO;
+	}
+	verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof (schi));
+
+	memcpy_fromfs(&schi, (char *)arg, sizeof (schi));
+
+	switch (sony_audio_status) {
+	case CDROM_AUDIO_PLAY:
+		if (read_subcode() < 0) {
+			return -EIO;
+		}
+		break;
+
+	case CDROM_AUDIO_PAUSED:
+	case CDROM_AUDIO_COMPLETED:
+		break;
+
+	case CDROM_AUDIO_NO_STATUS:
+		schi.cdsc_audiostatus = sony_audio_status;
+		memcpy_tofs((char *)arg, &schi, sizeof (schi));
+		return 0;
+		break;
+
+	case CDROM_AUDIO_INVALID:
+	case CDROM_AUDIO_ERROR:
+	default:
+		return -EIO;
+	}
+
+	schi.cdsc_audiostatus = sony_audio_status;
+	schi.cdsc_adr = last_sony_subcode->address;
+	schi.cdsc_ctrl = last_sony_subcode->control;
+	schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
+	schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
+	if (schi.cdsc_format == CDROM_MSF) {
+		schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
+		schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
+		schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
+
+		schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
+		schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
+		schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
+	} else if (schi.cdsc_format == CDROM_LBA) {
+		schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
+		schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
+	}
+	memcpy_tofs((char *)arg, &schi, sizeof (schi));
+	return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+cdu_ioctl(struct inode *inode,
+		  struct file *file,
+		  unsigned int cmd,
+		  unsigned long arg)
+{
+	unsigned int dev;
+	unsigned char status[2];
+	unsigned char cmd_buff[10], params[10];
+	int     i, dsc_status;
+
+
+	if (!inode) {
+		return -EINVAL;
+	}
+	dev = MINOR(inode->i_rdev) >> 6;
+	if (dev != 0) {
+		return -EINVAL;
+	}
+	if (check_drive_status() != 0)
+		return -EIO;
+
+	switch (cmd) {
+	case CDROMSTART:			/* Spin up the drive */
+		if (spin_up_drive(status) < 0) {
+			printk("Sony CDROM error 0x%.2x (CDROMSTART)\n", status[0]);
+			return -EIO;
+		}
+		return 0;
+		break;
+
+	case CDROMSTOP:			/* Spin down the drive */
+		cmd_buff[0] = SONY535_HOLD;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+		/*
+		 * Spin the drive down, ignoring the error if the disk was
+		 * already not spinning.
+		 */
+		sony_audio_status = CDROM_AUDIO_NO_STATUS;
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
+			((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
+			printk("Sony CDROM error 0x%.2x (CDROMSTOP)\n", status[0]);
+			return -EIO;
+		}
+		return 0;
+		break;
+
+	case CDROMPAUSE:			/* Pause the drive */
+		cmd_buff[0] = SONY535_HOLD;		/* CDU-31 driver uses AUDIO_STOP, not pause */
+		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+			printk("Sony CDROM error 0x%.2x (CDROMPAUSE)\n", status[0]);
+			return -EIO;
+		}
+		/* Get the current position and save it for resuming */
+		if (read_subcode() < 0) {
+			return -EIO;
+		}
+		cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+		cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+		cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+		sony_audio_status = CDROM_AUDIO_PAUSED;
+		return 0;
+		break;
+
+	case CDROMRESUME:			/* Start the drive after being paused */
+		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+		if (sony_audio_status != CDROM_AUDIO_PAUSED) {
+			return -EINVAL;
+		}
+		spin_up_drive(status);
+
+		/* Start the drive at the saved position. */
+		cmd_buff[0] = SONY535_PLAY_AUDIO;
+		cmd_buff[1] = 0;		/* play back starting at this address */
+		cmd_buff[2] = cur_pos_msf[0];
+		cmd_buff[3] = cur_pos_msf[1];
+		cmd_buff[4] = cur_pos_msf[2];
+		cmd_buff[5] = SONY535_PLAY_AUDIO;
+		cmd_buff[6] = 2;		/* set ending address */
+		cmd_buff[7] = final_pos_msf[0];
+		cmd_buff[8] = final_pos_msf[1];
+		cmd_buff[9] = final_pos_msf[2];
+		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+			printk("Sony CDROM error 0x%.2x (CDROMRESUME)\n", status[0]);
+			return -EIO;
+		}
+		sony_audio_status = CDROM_AUDIO_PLAY;
+		return 0;
+		break;
+
+	case CDROMPLAYMSF:			/* Play starting at the given MSF address. */
+		verify_area(VERIFY_READ, (char *)arg, 6);
+		spin_up_drive(status);
+		set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+		memcpy_fromfs(params, (void *)arg, 6);
+
+		/* The parameters are given in int, must be converted */
+		for (i = 0; i < 3; i++) {
+			cmd_buff[2 + i] = int_to_bcd(params[i]);
+			cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
+		}
+		cmd_buff[0] = SONY535_PLAY_AUDIO;
+		cmd_buff[1] = 0;		/* play back starting at this address */
+		/* cmd_buff[2-4] are filled in for loop above */
+		cmd_buff[5] = SONY535_PLAY_AUDIO;
+		cmd_buff[6] = 2;		/* set ending address */
+		/* cmd_buff[7-9] are filled in for loop above */
+		if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+			(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+			printk("Sony CDROM error 0x%.2x (CDROMPLAYMSF)\n", status[0]);
+			return -EIO;
+		}
+		/* Save the final position for pauses and resumes */
+		final_pos_msf[0] = cmd_buff[7];
+		final_pos_msf[1] = cmd_buff[8];
+		final_pos_msf[2] = cmd_buff[9];
+		sony_audio_status = CDROM_AUDIO_PLAY;
+		return 0;
+		break;
+
+	case CDROMREADTOCHDR:		/* Read the table of contents header */
+		{
+			struct cdrom_tochdr *hdr;
+			struct cdrom_tochdr loc_hdr;
+
+			sony_get_toc();
+			if (!sony_toc_read)
+				return -EIO;
+			hdr = (struct cdrom_tochdr *)arg;
+			verify_area(VERIFY_WRITE, hdr, sizeof (*hdr));
+			loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+			loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+			memcpy_tofs(hdr, &loc_hdr, sizeof (*hdr));
+		}
+		return 0;
+		break;
+
+	case CDROMREADTOCENTRY:	/* Read a given table of contents entry */
+		{
+			struct cdrom_tocentry *entry;
+			struct cdrom_tocentry loc_entry;
+			int     track_idx;
+			unsigned char *msf_val = NULL;
+
+			sony_get_toc();
+			if (!sony_toc_read) {
+				return -EIO;
+			}
+			entry = (struct cdrom_tocentry *)arg;
+			verify_area(VERIFY_WRITE /* and read */ , entry, sizeof (*entry));
+
+			memcpy_fromfs(&loc_entry, entry, sizeof (loc_entry));
+
+			/* Lead out is handled separately since it is special. */
+			if (loc_entry.cdte_track == CDROM_LEADOUT) {
+				loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ;
+				loc_entry.cdte_ctrl = sony_toc->control2;
+				msf_val = sony_toc->lead_out_start_msf;
+			} else {
+				track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+				if (track_idx < 0)
+					return -EINVAL;
+				loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ;
+				loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+				msf_val = sony_toc->tracks[track_idx].track_start_msf;
+			}
+
+			/* Logical buffer address or MSF format requested? */
+			if (loc_entry.cdte_format == CDROM_LBA) {
+				loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+			} else if (loc_entry.cdte_format == CDROM_MSF) {
+				loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
+				loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1));
+				loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2));
+			}
+			memcpy_tofs(entry, &loc_entry, sizeof (*entry));
+		}
+		return 0;
+		break;
+
+	case CDROMPLAYTRKIND:		/* Play a track.  This currently ignores index. */
+		{
+			struct cdrom_ti ti;
+			int     track_idx;
+
+			sony_get_toc();
+			if (!sony_toc_read)
+				return -EIO;
+			verify_area(VERIFY_READ, (char *)arg, sizeof (ti));
+
+			memcpy_fromfs(&ti, (char *)arg, sizeof (ti));
+			if ((ti.cdti_trk0 < sony_toc->first_track_num)
+				|| (sony_toc->last_track_num < ti.cdti_trk0)
+				|| (ti.cdti_trk1 < ti.cdti_trk0)) {
+				return -EINVAL;
+			}
+			track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+			if (track_idx < 0)
+				return -EINVAL;
+			params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+			params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+			params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
+			/*
+			 * If we want to stop after the last track, use the lead-out
+			 * MSF to do that.
+			 */
+			if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
+				log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1,
+						   &(params[4]));
+			} else {
+				track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1));
+				if (track_idx < 0)
+					return -EINVAL;
+				log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1,
+						   &(params[4]));
+			}
+			params[0] = 0x03;
+
+			spin_up_drive(status);
+
+			set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+			/* Start the drive at the saved position. */
+			cmd_buff[0] = SONY535_PLAY_AUDIO;
+			cmd_buff[1] = 0;	/* play back starting at this address */
+			cmd_buff[2] = params[1];
+			cmd_buff[3] = params[2];
+			cmd_buff[4] = params[3];
+			cmd_buff[5] = SONY535_PLAY_AUDIO;
+			cmd_buff[6] = 2;	/* set ending address */
+			cmd_buff[7] = params[4];
+			cmd_buff[8] = params[5];
+			cmd_buff[9] = params[6];
+			if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+				(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+				printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
+					   params[2], params[3], params[4], params[5], params[6]);
+				printk("Sony CDROM error 0x%.2x (CDROMPLAYTRKIND)\n", status[0]);
+				return -EIO;
+			}
+			/* Save the final position for pauses and resumes */
+			final_pos_msf[0] = params[4];
+			final_pos_msf[1] = params[5];
+			final_pos_msf[2] = params[6];
+			sony_audio_status = CDROM_AUDIO_PLAY;
+			return 0;
+		}
+
+	case CDROMSUBCHNL:			/* Get subchannel info */
+		return sony_get_subchnl_info(arg);
+
+	case CDROMVOLCTRL:			/* Volume control.  What volume does this change, anyway? */
+		{
+			struct cdrom_volctrl volctrl;
+
+			verify_area(VERIFY_READ, (char *)arg, sizeof (volctrl));
+
+			memcpy_fromfs(&volctrl, (char *)arg, sizeof (volctrl));
+			cmd_buff[0] = SONY535_SET_VOLUME;
+			cmd_buff[1] = volctrl.channel0;
+			cmd_buff[2] = volctrl.channel1;
+			if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
+				printk("Sony CDROM error 0x%.2x (CDROMVOLCTRL)\n", status[0]);
+				return -EIO;
+			}
+		}
+		return 0;
+
+	case CDROMEJECT:			/* Eject the drive */
+		cmd_buff[0] = SONY535_STOP;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+		sony_audio_status = CDROM_AUDIO_INVALID;
+		cmd_buff[0] = SONY535_EJECT_CADDY;
+		if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+			printk("Sony CDROM error 0x%.2x (CDROMEJECT)\n", status[0]);
+			return -EIO;
+		}
+		return 0;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/*
+ * Open the drive for operations.  Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+cdu_open(struct inode *inode,
+		 struct file *filp)
+{
+	unsigned char status[2], cmd_buff[2];
+
+
+	if (sony_inuse)
+		return -EBUSY;
+	if (check_drive_status() != 0)
+		return -EIO;
+	sony_inuse = 1;
+	MOD_INC_USE_COUNT;
+
+	if (spin_up_drive(status) != 0) {
+		printk("Sony CDROM error 0x%.2x (cdu_open, spin up)\n", status[0]);
+		sony_inuse = 0;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	sony_get_toc();
+	if (!sony_toc_read) {
+		cmd_buff[0] = SONY535_SPIN_DOWN;
+		do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+		sony_inuse = 0;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	if (inode) {
+		check_disk_change(inode->i_rdev);
+	}
+	sony_usage++;
+
+#ifdef LOCK_DOORS
+	/* disable the eject button while mounted */
+	cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
+	do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+#endif
+
+	return 0;
+}
+
+
+/*
+ * Close the drive.  Spin it down if no task is using it.  The spin
+ * down will fail if playing audio, so audio play is OK.
+ */
+static void
+cdu_release(struct inode *inode,
+			struct file *filp)
+{
+	unsigned char status[2], cmd_no;
+
+	sony_inuse = 0;
+	MOD_DEC_USE_COUNT;
+
+	if (0 < sony_usage) {
+		sony_usage--;
+	}
+	if (sony_usage == 0) {
+		sync_dev(inode->i_rdev);
+		check_drive_status();
+
+		if (sony_audio_status != CDROM_AUDIO_PLAY) {
+			cmd_no = SONY535_SPIN_DOWN;
+			do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+		}
+#ifdef LOCK_DOORS
+		/* enable the eject button after umount */
+		cmd_no = SONY535_ENABLE_EJECT_BUTTON;
+		do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+#endif
+	}
+}
+
+
+static struct file_operations cdu_fops =
+{
+	NULL,						/* lseek - default */
+	block_read,					/* read - general block-dev read */
+	block_write,				/* write - general block-dev write */
+	NULL,						/* readdir - bad */
+	NULL,						/* select */
+	cdu_ioctl,					/* ioctl */
+	NULL,						/* mmap */
+	cdu_open,					/* open */
+	cdu_release,				/* release */
+	NULL,						/* fsync */
+	NULL,						/* fasync */
+	cdu535_check_media_change,	/* check media change */
+	NULL						/* revalidate */
+};
+
+/*
+ * Initialize the driver.
+ */
+#ifndef MODULE
+unsigned long
+sony535_init(unsigned long mem_start, unsigned long mem_end)
+#else
+int 
+init_module(void)
+#endif
+{
+	struct s535_sony_drive_config drive_config;
+	unsigned char cmd_buff[3], ret_buff[2];
+	unsigned char status[2];
+	int     retry_count;
+#ifdef MODULE
+	int     i;
+#endif
+
+	/* Set up all the register locations */
+	result_reg = sony_cd_base_io;
+	command_reg = sony_cd_base_io;
+	data_reg = sony_cd_base_io + 1;
+	read_status_reg = sony_cd_base_io + 2;
+	select_unit_reg = sony_cd_base_io + 3;
+
+	printk("sonycd535: probing base address %03X\n", sony_cd_base_io);
+	if (check_region(sony_cd_base_io,4)) {
+		printk("sonycd535: my base address is not free!\n");
+#ifndef MODULE
+		return mem_start;
+#else
+		return -EIO;
+#endif
+	}
+	/* look for the CD-ROM, follows the procedure in the DOS driver */
+	inb(select_unit_reg);
+	retry_count = jiffies + 2 * HZ;
+	while (jiffies < retry_count)
+		sony_sleep();			/* wait for 40 18 Hz ticks (from DOS driver) */
+	inb(result_reg);
+
+	outb(0, read_status_reg);	/* does a reset? */
+	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+	while (jiffies < retry_count) {
+		select_unit(0);
+		if (inb(result_reg) != 0xff)
+			break;
+		sony_sleep();			/* about 1-2 ms on my machine */
+	}
+
+	if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) {
+		/* CD-ROM drive responded --  get the drive configuration */
+		cmd_buff[0] = SONY535_INQUIRY;
+		if (do_sony_cmd(cmd_buff, 1, status, (Byte *) & drive_config, 28, 1) == 0) {
+			/* was able to get the configuration, set drive mode as rest of init */
+#if DEBUG > 0
+			if ( (status[0] & 0x7f) != 0 )
+				printk("Inquiry command returned status = 0x%x\n", status[0]);
+#endif
+			cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+			cmd_buff[1] = 0x0;	/* default audio */
+			if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
+				/* set the drive mode successful, we are set! */
+				sony_buffer_size = SONY535_BUFFER_SIZE;
+				sony_buffer_sectors = sony_buffer_size / 2048;
+
+				printk("Sony I/F CDROM : %8.8s %16.16s %4.4s",
+					   drive_config.vendor_id,
+					   drive_config.product_id,
+					   drive_config.product_rev_level);
+				printk("  using %d byte buffer\n", sony_buffer_size);
+
+				if (register_blkdev(MAJOR_NR, "cdu-535", &cdu_fops)) {
+					printk("Unable to get major %d for sony CDU-535 cd\n", MAJOR_NR);
+#ifndef MODULE
+					return mem_start;
+#else
+					return -EIO;
+#endif
+				}
+				blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+				read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read-ahead */
+
+#ifndef MODULE
+				sony_toc = (struct s535_sony_toc *)mem_start;
+				mem_start += sizeof (*sony_toc);
+				last_sony_subcode = (struct s535_sony_subcode *)mem_start;
+				mem_start += sizeof (*last_sony_subcode);
+				sony_buffer = (unsigned char *)mem_start;
+				mem_start += sony_buffer_size;
+
+#else /* MODULE */
+				sony_toc = (struct s535_sony_toc *)
+					kmalloc(sizeof (*sony_toc), GFP_KERNEL);
+				last_sony_subcode = (struct s535_sony_subcode *)
+					kmalloc(sizeof (*last_sony_subcode), GFP_KERNEL);
+				sony_buffer = (unsigned char **)
+					kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
+				for (i = 0; i < sony_buffer_sectors; i++)
+					sony_buffer[i] = (unsigned char *)kmalloc(2048, GFP_KERNEL);
+#endif /* MODULE */
+				initialized = 1;
+			}
+		}
+	}
+
+#ifndef MODULE
+	if (!initialized)
+		printk("Did not find a Sony CDU-535 drive\n");
+	else
+		snarf_region(sony_cd_base_io, 4);
+	return mem_start;
+#else
+	if (!initialized) {
+		printk("Did not find a Sony CDU-535 drive\n");
+		return -EIO;
+	} else {
+		snarf_region(sony_cd_base_io, 4);
+	}
+	return 0;
+#endif
+}
+
+#ifndef MODULE
+/*
+ * accept "kernel command line" parameters 
+ * (added by emoenke@gwdg.de)
+ *
+ * use: tell LILO:
+ *                 sonycd535=0x320
+ *
+ * the address value has to be the existing CDROM port address.
+ */
+void
+sonycd535_setup(char *strings, int *ints)
+{
+	if (ints[0] > 0)
+		sony_cd_base_io = ints[1];
+#if 0	/* placeholder for future use */
+	if (ints[0] > 1)
+		irq_used = ints[2];
+#endif
+	if ((strings != NULL) && (*strings != '\0'))
+		printk("Sony CDU-535: Warning: Unknown interface type: %s\n", strings);
+}
+
+#else /* MODULE */
+
+void 
+cleanup_module(void)
+{
+	int     i;
+	if (MOD_IN_USE) {
+		printk("Sony 535 module in use, cannot remove\n");
+		return;
+	}
+	if (unregister_blkdev(MAJOR_NR, "cdu-535") == (-EINVAL)) {
+		printk("Uh oh, couldn't unregister cdu-535\n");
+		return;
+	}
+	kfree_s(sony_toc, sizeof (*sony_toc));
+	kfree_s(last_sony_subcode, sizeof (*last_sony_subcode));
+	for (i = 0; i < sony_buffer_sectors; i++)
+		kfree_s(sony_buffer[i], 2048);
+	kfree_s(sony_buffer, 4 * sony_buffer_sectors);
+	printk("cdu-535 module released\n");
+}
+#endif	/* MODULE */
+
+#endif	/* CONFIG_CDU535 */
diff -u --recursive --new-file v1.1.90/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v1.1.90/linux/drivers/char/keyboard.c	Thu Feb  9 10:18:51 1995
+++ linux/drivers/char/keyboard.c	Thu Feb  9 20:11:38 1995
@@ -91,7 +91,8 @@
 /* shift state counters.. */
 static unsigned char k_down[NR_SHIFT] = {0, };
 /* keyboard key bitmap */
-static unsigned long key_down[8] = { 0, };
+#define BITS_PER_LONG (8*sizeof(unsigned long))
+static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
 
 extern int last_console;
 static int want_console = -1;
@@ -954,8 +955,8 @@
 
 	for(i=0; i < SIZE(key_down); i++)
 	  if(key_down[i]) {	/* skip this word if not a single bit on */
-	    k = (i<<5);
-	    for(j=0; j<32; j++,k++)
+	    k = i*BITS_PER_LONG;
+	    for(j=0; j<BITS_PER_LONG; j++,k++)
 	      if(test_bit(k, key_down)) {
 		sym = U(plain_map[k]);
 		if(KTYP(sym) == KT_SHIFT) {
diff -u --recursive --new-file v1.1.90/linux/drivers/net/CONFIG linux/drivers/net/CONFIG
--- v1.1.90/linux/drivers/net/CONFIG	Mon Jan 30 06:41:56 1995
+++ linux/drivers/net/CONFIG	Sun Feb 12 16:07:22 1995
@@ -42,6 +42,8 @@
 #  DE4x5		The DIGITAL series of PCI/EISA Ethernet Cards,
 #			DE425, DE434 and DE435
 #       DE4x5_DEBUG	Set the desired debug level
+#	IS_ZYNX         May allow driver to work with Zynx cards - 
+#			see linux/drivers/net/README.de4x5
 #
 
 # The following options exist, but cannot be set in this file.
diff -u --recursive --new-file v1.1.90/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.1.90/linux/drivers/net/Makefile	Wed Feb  1 09:23:05 1995
+++ linux/drivers/net/Makefile	Sun Feb 12 16:07:22 1995
@@ -194,11 +194,13 @@
 NETDRV_OBJS := $(NETDRV_OBJS) atp.o
 endif
 
-ifdef CONFIG_DE4x5
+ifdef CONFIG_DE4X5
 NETDRV_OBJS := $(NETDRV_OBJS) de4x5.o
+else
+MODULES := $(MODULES) de4x5.o
+endif
 de4x5.o: de4x5.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(DE4x5_OPTS) -c $<
-endif
 
 ifdef CONFIG_NI52
 NETDRV_OBJS := $(NETDRV_OBJS) ni52.o
@@ -252,7 +254,7 @@
 
 net.a: $(NETDRV_OBJS)
 	rm -f net.a
-	ar rcs net.a $(NETDRV_OBJS)
+	$(AR) rcs net.a $(NETDRV_OBJS)
 
 clean:
 	rm -f core *.o *.a *.s
diff -u --recursive --new-file v1.1.90/linux/drivers/net/README.de4x5 linux/drivers/net/README.de4x5
--- v1.1.90/linux/drivers/net/README.de4x5	Mon Jan 30 06:41:56 1995
+++ linux/drivers/net/README.de4x5	Sun Feb 12 16:07:47 1995
@@ -21,6 +21,16 @@
     The cause is under investigation.
     ************************************************************************
 
+ZYNX  cards,  which   use  the PCI  DECchip  DC21040,  are  not specifically
+supported in this driver because
+
+a) I have  no information on them.   
+b) I cannot test  them with the driver.
+c) Donald Becker's 'tulip.c' driver  works with them....well one person says
+   they do and another doesn't, so take your pick!
+
+This driver can be made to work with the ZYNX  (and may be  the SMC) card by
+setting a compile time flag in linux/drivers/net/CONFIG
 
 Enjoy!
 
diff -u --recursive --new-file v1.1.90/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.1.90/linux/drivers/net/Space.c	Thu Feb  9 10:18:51 1995
+++ linux/drivers/net/Space.c	Sun Feb 12 16:11:56 1995
@@ -115,7 +115,7 @@
 #ifdef CONFIG_EWRK3             /* DEC EtherWORKS 3 */
         && ewrk3_probe(dev)
 #endif
-#ifdef CONFIG_DE4x5             /* DEC DE425, DE434, DE435 adapters */
+#ifdef CONFIG_DE4X5             /* DEC DE425, DE434, DE435 adapters */
         && de4x5_probe(dev)
 #endif
 #ifdef CONFIG_APRICOT		/* Apricot I82596 */
diff -u --recursive --new-file v1.1.90/linux/drivers/net/apricot.c linux/drivers/net/apricot.c
--- v1.1.90/linux/drivers/net/apricot.c	Thu Jan 26 07:49:05 1995
+++ linux/drivers/net/apricot.c	Thu Feb  9 12:23:10 1995
@@ -547,7 +547,7 @@
 
     irq2dev_map[dev->irq] = dev;
 
-     i = init_rx_bufs(dev, RX_RING_SIZE);
+    i = init_rx_bufs(dev, RX_RING_SIZE);
 
     if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
         printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
@@ -743,6 +743,7 @@
     dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
 
     lp = (struct i596_private *)dev->priv;
+    memset((void *)lp, 0, sizeof(struct i596_private));
     lp->scb.command = 0;
     lp->scb.cmd = (struct i596_cmd *) I596_NULL;
     lp->scb.rfd = (struct i596_rfd *)I596_NULL;
diff -u --recursive --new-file v1.1.90/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v1.1.90/linux/drivers/net/de4x5.c	Mon Jan 30 06:41:56 1995
+++ linux/drivers/net/de4x5.c	Sun Feb 12 16:07:22 1995
@@ -41,12 +41,24 @@
     differences  in the  EISA and PCI    CSR address offsets  from the  base
     address.
 
-    The ability to load this driver  as a loadable  module has been included
-    and used  extensively during the  driver development (to save those long
-    reboot sequences).  I  don't  recommend using loadable drivers  with PCI
-    however,  since the  PCI BIOS allocates   the  I/O and  memory addresses
-    dynamically at  boot time.  To  utilise this  ability, you  have to do 8
-    things:
+    The ability to load  this driver as a loadable  module has been included
+    and  used extensively during the  driver development (to save those long
+    reboot sequences).  Loadable module support under  PCI has been achieved
+    by letting any I/O address less than 0x1000 be assigned as:
+
+                       0xghh
+
+    where g is the bus number (usually 0 until the BIOS's get fixed)
+         hh is the device number (max is 32 per bus).
+
+    Essentially, the I/O address and IRQ information  are ignored and filled
+    in later by  the PCI BIOS   during the PCI  probe.  Note  that the board
+    should be in the system at boot time so that its I/O address and IRQ are
+    allocated by the PCI BIOS automatically. The special case of device 0 on
+    bus 0  is  not allowed  as  the probe  will think   you're autoprobing a
+    module.
+
+    To utilise this ability, you have to do 8 things:
 
     0) have a copy of the loadable modules code installed on your system.
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
@@ -76,10 +88,19 @@
     pause whilst the   driver figures out   where its media went).  My tests
     using ping showed that it appears to work....
 
+    A compile time  switch to allow  Zynx  recognition has been  added. This
+    "feature" is in no way supported nor tested  in this driver and the user
+    may use it at his/her sole discretion.  I have had 2 conflicting reports
+    that  my driver  will or   won't  work with   Zynx. Try Donald  Becker's
+    'tulip.c' if this driver doesn't work for  you. I will not be supporting
+    Zynx cards since I have no information on them  and can't test them in a
+    system.
+
     TO DO:
     ------
-      1.      Improve the timing loops to be accurate across different CPUs
-              and speeds.
+    1. Add DC21041 Nway/Autosense support
+    2. Add DC21140 Autosense support
+    3. Add timer support
 
 
     Revision History
@@ -90,22 +111,34 @@
       0.1     17-Nov-94   Initial writing. ALPHA code release.
       0.2     13-Jan-95   Added PCI support for DE435's
       0.21    19-Jan-95   Added auto media detection
+      0.22    10-Feb-95   Fix interrupt handler call <chris@cosy.sbg.ac.at>
+                          Fix recognition bug reported by <bkm@star.rl.ac.uk>
+			  Add request/release_region code
+			  Add loadable modules support for PCI
+			  Clean up loadable modules support
 
     =========================================================================
 */
 
-static char *version = "de4x5.c:v0.21 1/19/95 davies@wanton.lkg.dec.com\n";
+static char *version = "de4x5.c:v0.22 2/10/95 davies@wanton.lkg.dec.com\n";
 
-#include <stdarg.h>
 #include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif /* MODULE */
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/interrupt.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
-#include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -120,11 +153,6 @@
 #include <linux/types.h>
 #include <linux/unistd.h>
 
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#endif /* MODULE */
-
 #include "de4x5.h"
 
 #ifdef DE4X5_DEBUG
@@ -133,32 +161,60 @@
 static int de4x5_debug = 1;
 #endif
 
-#ifndef PROBE_LENGTH
+/*
+** Ethernet PROM defines
+*/
 #define PROBE_LENGTH    32
-#endif
+#define ETH_PROM_SIG    0xAA5500FFUL
 
-#define ETH_PROM_SIG "FF0055AAFF0055AA"
+/*
+** Ethernet Info
+*/
+#define PKT_BUF_SZ	1544            /* Buffer size for each Tx/Rx buffer */
+#define MAX_PKT_SZ   	1514            /* Maximum ethernet packet length */
+#define MAX_DAT_SZ   	1500            /* Maximum ethernet data length */
+#define MIN_DAT_SZ   	1               /* Minimum ethernet data length */
+#define PKT_HDR_LEN     14              /* Addresses and data length info */
 
-#define DE4X5_SIGNATURE {"DE425",""}
-#define DE4X5_NAME_LENGTH 8
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL   /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL   /* Ethernet CRC, little endian */
 
-#define DE4X5_EISA_IO_PORTS 0x0c00       /* I/O port base address, slot 0 */
+/*
+** EISA bus defines
+*/
+#define DE4X5_EISA_IO_PORTS   0x0c00     /* I/O port base address, slot 0 */
+#define DE4X5_EISA_TOTAL_SIZE 0xfff      /* I/O address extent */
 
 #define MAX_EISA_SLOTS 16
 #define EISA_SLOT_INC 0x1000
-#define DE4X5_EISA_SEARCH 0x00000001     /* probe search mask */
-static u_long eisa_slots_full =
-                       DE4X5_EISA_SEARCH;/* holds which EISA slots hold */
-				         /* DE425s, for multi-DE425 case */
+
+#define DE4X5_SIGNATURE {"DE425",""}
+#define DE4X5_NAME_LENGTH 8
+
+/*
+** PCI Bus defines
+*/
 #define PCI_MAX_BUS_NUM 8
-static u_long pci_slots_full[PCI_MAX_BUS_NUM];  /* Which PCI slots used */
-                                         /* on up to PCI_MAX_BUS_NUM buses */
+#define DE4X5_PCI_TOTAL_SIZE 0x80        /* I/O address extent */
 
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL   /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL   /* Ethernet CRC, little endian */
+/*
+** Timer defines
+*/
+#define TIMER_WIDTH   16
+#define TIMER_PORT    0x43
+#define TIMER_LATCH   0x06
+#define TIMER_READ    0x40
+#define TIMER_TICK    419  /*ns*/
+#define DELAY_QUANT   5    /*us*/
 
 #define LWPAD ((long)(sizeof(long) - 1)) /* for longword alignment */
 
+#ifndef IS_ZYNX                          /* See README.de4x5 for using this */
+static int is_zynx = 0;
+#else
+static int is_zynx = 1;
+#endif
+
 /*
 ** DE4X5 IRQ ENABLE/DISABLE
 */
@@ -167,52 +223,43 @@
 static u_long irq_en   = IMR_NIM | IMR_AIM;
 
 #define ENABLE_IRQs \
-  imr |= irq_en;\
-  outl(imr, DE4X5_IMR)                      /* Enable the IRQs */
+    imr |= irq_en;\
+    outl(imr, DE4X5_IMR)                    /* Enable the IRQs */
 
 #define DISABLE_IRQs \
-  imr = inl(DE4X5_IMR);\
-  imr &= ~irq_en;\
-  outl(imr, DE4X5_IMR)                      /* Disable the IRQs */
+    imr = inl(DE4X5_IMR);\
+    imr &= ~irq_en;\
+    outl(imr, DE4X5_IMR)                    /* Disable the IRQs */
 
 #define UNMASK_IRQs \
-  imr |= irq_mask;\
-  outl(imr, DE4X5_IMR)                      /* Unmask the IRQs */
+    imr |= irq_mask;\
+    outl(imr, DE4X5_IMR)                    /* Unmask the IRQs */
 
 #define MASK_IRQs \
-  imr = inl(DE4X5_IMR);\
-  imr &= ~irq_mask;\
-  outl(imr, DE4X5_IMR)                      /* Mask the IRQs */
+    imr = inl(DE4X5_IMR);\
+    imr &= ~irq_mask;\
+    outl(imr, DE4X5_IMR)                    /* Mask the IRQs */
 
 /*
 ** DE4X5 START/STOP
 */
 #define START_DE4X5 \
-  omr = inl(DE4X5_OMR);\
-  omr |= OMR_ST | OMR_SR;\
-  outl(omr, DE4X5_OMR)                      /* Enable the TX and/or RX */
+    omr = inl(DE4X5_OMR);\
+    omr |= OMR_ST | OMR_SR;\
+    outl(omr, DE4X5_OMR)                    /* Enable the TX and/or RX */
 
 #define STOP_DE4X5 \
-  omr = inl(DE4X5_OMR);\
-  omr &= ~(OMR_ST|OMR_SR);\
-  outl(omr, DE4X5_OMR)                      /* Disable the TX and/or RX */
+    omr = inl(DE4X5_OMR);\
+    omr &= ~(OMR_ST|OMR_SR);\
+    outl(omr, DE4X5_OMR)                    /* Disable the TX and/or RX */
 
 /*
 ** DE4X5 SIA RESET
 */
 #define RESET_SIA \
-  outl(SICR_RESET, DE4X5_SICR);             /* Reset SIA connectivity regs */ \
-  outl(STRR_RESET, DE4X5_STRR);             /* Write reset values */ \
-  outl(SIGR_RESET, DE4X5_SIGR)              /* Write reset values */
-
-/*
-** Ethernet Packet Info
-*/
-#define PKT_BUF_SZ	1544            /* Buffer size for each Tx/Rx buffer */
-#define MAX_PKT_SZ   	1514            /* Maximum ethernet packet length */
-#define MAX_DAT_SZ   	1500            /* Maximum ethernet data length */
-#define MIN_DAT_SZ   	1               /* Minimum ethernet data length */
-#define PKT_HDR_LEN     14              /* Addresses and data length info */
+    outl(SICR_RESET, DE4X5_SICR);           /* Reset SIA connectivity regs */ \
+    outl(STRR_RESET, DE4X5_STRR);           /* Write reset values */ \
+    outl(SIGR_RESET, DE4X5_SIGR)            /* Write reset values */
 
 /*
 ** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous
@@ -231,7 +278,7 @@
     u_long des1;
     char *buf;
     char *next;
-  };
+};
 
 /*
 ** The DE4X5 private structure
@@ -241,6 +288,7 @@
                                                 increase DE4X5_PKT_STAT_SZ */
 
 struct de4x5_private {
+    char adapter_name[80];                   /* Adapter name */
     struct de4x5_desc rx_ring[NUM_RX_DESC];  /* RX descriptor ring */
     struct de4x5_desc tx_ring[NUM_TX_DESC];  /* TX descriptor ring */
     struct sk_buff *skb[NUM_TX_DESC];        /* TX skb for freeing when sent */
@@ -249,13 +297,13 @@
     char setup_frame[SETUP_FRAME_LEN];       /* Holds MCA and PA info. */
     struct enet_statistics stats;            /* Public stats */
     struct {
-      unsigned long bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
-      unsigned long unicast;
-      unsigned long multicast;
-      unsigned long broadcast;
-      unsigned long excessive_collisions;
-      unsigned long tx_underruns;
-      unsigned long excessive_underruns;
+	unsigned long bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+	unsigned long unicast;
+	unsigned long multicast;
+	unsigned long broadcast;
+	unsigned long excessive_collisions;
+	unsigned long tx_underruns;
+	unsigned long excessive_underruns;
     } pktStats;
     char rxRingSize;
     char txRingSize;
@@ -273,7 +321,7 @@
 */
 static int  de4x5_open(struct device *dev);
 static int  de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
-static void de4x5_interrupt(int reg_ptr);
+static void de4x5_interrupt(int irq, struct pt_regs * regs);
 static int  de4x5_close(struct device *dev);
 static struct enet_statistics *de4x5_get_stats(struct device *dev);
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
@@ -289,6 +337,9 @@
 
 static int  autoconf_media(struct device *dev);
 static void create_packet(struct device *dev, char *frame, int len);
+static u_short dce_get_ticks(void);
+static void dce_us_delay(u_long usec);
+static void dce_ms_delay(u_long msec);
 static void load_packet(struct device *dev, char *buf, u_long flags, struct sk_buff *skb);
 static void EISA_signature(char * name, short iobase);
 static int  DevicePresent(short iobase);
@@ -303,11 +354,14 @@
 #ifdef MODULE
 int  init_module(void);
 void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
 # else
 static unsigned char de4x5_irq[] = {5,9,10,11};
+static int autoprobed = 0, loading_module = 0;
 #endif /* MODULE */
 
-static int num_de4x5s = 0, num_eth = 0, autoprobed = 0;
+static char name[DE4X5_NAME_LENGTH + 1];
+static int num_de4x5s = 0, num_eth = 0;
 
 /*
 ** Kludge to get around the fact that the CSR addresses have different
@@ -315,8 +369,8 @@
 ** PROM is accessed differently.
 */
 static struct bus_type {
-  int bus;
-  int device;
+    int bus;
+    int device;
 } bus;
 
 /*
@@ -338,22 +392,17 @@
   int tmp = num_de4x5s, iobase = dev->base_addr;
   int status = -ENODEV;
 
-  if ((iobase > 0) && (iobase <0x100)) {   /* Don't probe at all. */
-    status = -ENXIO;
-
-#ifdef MODULE
-  } else if (iobase == 0){
+  if ((iobase == 0) && loading_module){
     printk("Autoprobing is not supported when loading a module based driver.\n");
     status = -EIO;
-#endif
-  } else {                            /* First probe for the Ethernet */
-                                      /* Address PROM pattern */
+  } else {                              /* First probe for the Ethernet */
+	                                /* Address PROM pattern */
     eisa_probe(dev, iobase);
     pci_probe(dev, iobase);
 
     if ((tmp == num_de4x5s) && (iobase != 0)) {
-      printk("%s: de4x5_probe() cannot find device at 0x%04x.\n", dev->name,
-                                                                  iobase);
+      printk("%s: de4x5_probe() cannot find device at 0x%04x.\n", dev->name, 
+	                                                               iobase);
     }
 
     /*
@@ -374,27 +423,29 @@
 {
   struct bus_type *lp = &bus;
   int tmpbus, i, j, status=0;
-  char *tmp, name[DE4X5_NAME_LENGTH + 1];
+  char *tmp;
   u_long nicsr;
 
-  /*
-  ** First, RESET the board.
-  */
   RESET_DE4X5;
 
-  if (((nicsr=inl(DE4X5_STS)) & (STS_TS | STS_RS)) == 0) {/* Really stopped */
-    
+  if (((nicsr=inl(DE4X5_STS)) & (STS_TS | STS_RS)) == 0) {
     /* 
-    ** Now find out what kind of DC21040/DC21140 board we have.
+    ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
     */
     if (lp->bus == PCI) {
-      strcpy(name,"DE435");
+      if (!is_zynx) {
+	strcpy(name, "DE435");
+      } else {
+	strcpy(name, "ZYNX");
+      }
     } else {
       EISA_signature(name, EISA_ID0);
     }
 
     if (*name != '\0') {                         /* found a board signature */
       dev->base_addr = iobase;
+      request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
+			                       DE4X5_EISA_TOTAL_SIZE), name);
       
       if (lp->bus == EISA) {
 	printk("%s: %s at %#3x (EISA slot %d)", 
@@ -405,7 +456,7 @@
 	
       printk(", h/w address ");
       status = aprom_crc(dev);
-      for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+      for (i = 0; i < ETH_ALEN - 1; i++) {       /* get the ethernet addr. */
 	printk("%2.2x:", dev->dev_addr[i]);
       }
       printk("%2.2x,\n", dev->dev_addr[i]);
@@ -428,6 +479,7 @@
 	lp = (struct de4x5_private *)dev->priv;
 	memset(dev->priv, 0, sizeof(struct de4x5_private));
 	lp->bus = tmpbus;
+	strcpy(lp->adapter_name, name);
 
 	/*
 	** Allocate contiguous receive buffers, long word aligned. 
@@ -488,7 +540,8 @@
 	    }
 		
 	    outl(0, DE4X5_IMR);               /* Re-mask RUM interrupt */
-#endif  /* MODULE */
+
+#endif /* MODULE */
 	  } else {
 	    printk("      and requires IRQ%d (not probed).\n", dev->irq);
 	  }
@@ -500,7 +553,10 @@
       } else {
 	printk("      which has an Ethernet PROM CRC error.\n");
 	status = -ENXIO;
-      } 
+      }
+      if (status) release_region(iobase, (lp->bus == PCI ? 
+					             DE4X5_PCI_TOTAL_SIZE :
+			                             DE4X5_EISA_TOTAL_SIZE));
     } else {
       status = -ENXIO;
     }
@@ -530,7 +586,7 @@
   } else {                            /* Incorrectly initialised hardware */
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     if (lp) {
-      kfree_s(lp->rx_ring, RX_BUFF_SZ * NUM_RX_DESC + LWPAD);
+      kfree_s(lp->rx_ring[0].buf, RX_BUFF_SZ * NUM_RX_DESC + LWPAD);
     }
     if (dev->priv) {
       kfree_s(dev->priv, sizeof(struct de4x5_private) + LWPAD);
@@ -555,7 +611,7 @@
   */
   STOP_DE4X5;
 
-  if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, "de4x5")) {
+  if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, lp->adapter_name)) {
     printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
     status = -EAGAIN;
   } else {
@@ -573,7 +629,6 @@
 	printk("%2.2x:",(short)dev->dev_addr[i]);
       }
       printk("\n");
-      printk("\tchecked memory: 0x%08lx\n",eisa_slots_full);
       printk("Descriptor head addresses:\n");
       printk("\t0x%8.8lx  0x%8.8lx\n",(long)lp->rx_ring,(long)lp->tx_ring);
       printk("Descriptor addresses:\nRX: ");
@@ -642,9 +697,7 @@
     }
   }
 
-#ifdef MODULE
-      MOD_INC_USE_COUNT;
-#endif       
+  MOD_INC_USE_COUNT;
 
   return status;
 }
@@ -829,9 +882,8 @@
 ** The DE4X5 interrupt handler. 
 */
 static void
-de4x5_interrupt(int reg_ptr)
+de4x5_interrupt(int irq, struct pt_regs * regs)
 {
-    int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
     struct device *dev = (struct device *)(irq2dev_map[irq]);
     struct de4x5_private *lp;
     int iobase;
@@ -1046,9 +1098,7 @@
   free_irq(dev->irq);
   irq2dev_map[dev->irq] = 0;
 
-#ifdef MODULE
   MOD_DEC_USE_COUNT;
-#endif    
 
   return 0;
 }
@@ -1179,7 +1229,8 @@
   u_short iobase;
   struct bus_type *lp = &bus;
 
-  if (!ioaddr && autoprobed) return ;    /* Been here before ! */
+  if (!ioaddr && autoprobed) return ;            /* Been here before ! */
+  if ((ioaddr < 0x1000) && (ioaddr > 0)) return; /* PCI MODULE special */
 
   lp->bus = EISA;
 
@@ -1194,18 +1245,17 @@
   }
 
   for (status = -ENODEV; i<maxSlots && dev!=NULL; i++, iobase+=EISA_SLOT_INC) {
-    if (((eisa_slots_full >> i) & 0x01) == 0) {
-      if (DevicePresent(EISA_APROM) == 0) {
-	eisa_slots_full |= (0x01 << i);
+    if ((DevicePresent(EISA_APROM) == 0) || is_zynx) { 
+      if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
 	if ((dev = alloc_device(dev, iobase)) != NULL) {
 	  if ((status = de4x5_hw_init(dev, iobase)) == 0) {
 	    num_de4x5s++;
 	  }
 	  num_eth++;
 	}
+      } else if (autoprobed) {
+	printk("%s: region already allocated at 0x%04x.\n", dev->name, iobase);
       }
-    } else {
-      printk("%s: EISA device already allocated at 0x%04x.\n", dev->name, iobase);
     }
   }
 
@@ -1215,55 +1265,60 @@
 /*
 ** PCI bus I/O device probe
 */
-#define PCI_DEVICE (dev_num << 3)
+#define PCI_DEVICE    (dev_num << 3)
+#define PCI_LAST_DEV  32
 
 static void pci_probe(struct device *dev, short ioaddr)
 
 {
   u_char irq;
-  u_short pb, dev_num;
-  u_short i, vendor, device, status;
+  u_short pb, dev_num, dev_last;
+  u_short vendor, device, status;
   u_long class, iobase;
   struct bus_type *lp = &bus;
-  static char pci_init = 0;
 
   if (!ioaddr && autoprobed) return ;        /* Been here before ! */
 
-  if (!pci_init) {
-    for (i=0;i<PCI_MAX_BUS_NUM; i++) {
-      pci_slots_full[i] = 0;
-      pci_init = 1;
-    }
-  }
-
   if (pcibios_present()) {
     lp->bus = PCI;
 
-    for (pb = 0, dev_num = 0; dev_num < 32 && dev != NULL; dev_num++) {
+    if (ioaddr < 0x1000) {
+      pb = (u_short)(ioaddr >> 8);
+      dev_num = (u_short)(ioaddr & 0xff);
+    } else {
+      pb = 0;
+      dev_num = 0;
+    }
+    if (ioaddr > 0) {
+      dev_last = (dev_num < PCI_LAST_DEV) ? dev_num + 1 : PCI_LAST_DEV;
+    } else {
+      dev_last = PCI_LAST_DEV;
+    }
+
+    for (; dev_num < dev_last && dev != NULL; dev_num++) {
       pcibios_read_config_dword(pb, PCI_DEVICE, PCI_CLASS_REVISION, &class);
       if (class != 0xffffffff) {
-	if (((pci_slots_full[pb] >> dev_num) & 0x01) == 0) {
-	  pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	  pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
-	  if ((vendor == DC21040_VID) && (device == DC21040_DID)) {
-	    /* Set the device number information */
-	    lp->device = dev_num;
-
-	    /* Get the board I/O address */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
-	    iobase &= CBIO_MASK;
-
-	    /* Fetch the IRQ to be used */
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
-
-	    /* Enable I/O Accesses and Bus Mastering */
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
-	    status |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
-	    pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
-
-	    /* If a device is present, initialise it */
-	    if (DevicePresent(DE4X5_APROM) == 0) {
-	      pci_slots_full[pb] |= (0x01 << dev_num);
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
+	if ((vendor == DC21040_VID) && (device == DC21040_DID)) {
+	  /* Set the device number information */
+	  lp->device = dev_num;
+
+	  /* Get the board I/O address */
+	  pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+	  iobase &= CBIO_MASK;
+	  
+	  /* Fetch the IRQ to be used */
+	  pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+
+	  /* Enable I/O Accesses and Bus Mastering */
+	  pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	  status |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
+	  pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+
+	  /* If there is a device and I/O region is open, initialise dev. */
+	  if ((DevicePresent(DE4X5_APROM) == 0) || is_zynx) {
+	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
 	      if ((dev = alloc_device(dev, iobase)) != NULL) {
 		dev->irq = irq;
 		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
@@ -1271,10 +1326,10 @@
 		}
 		num_eth++;
 	      }
+	    } else if (autoprobed) {
+	      printk("%s: region already allocated at 0x%04x.\n", dev->name, (u_short)iobase);
 	    }
 	  }
-	} else {
-	  printk("%s: PCI device already allocated at slot %d.\n", dev->name, dev_num);
 	}
       }
     }
@@ -1296,94 +1351,98 @@
   /*
   ** Check the device structures for an end of list or unused device
   */
-  while (dev->next != NULL) {
-    if ((dev->base_addr == 0xffe0) || (dev->base_addr == 0)) break;
-    dev = dev->next;         /* walk through eth device list */
-    num_eth++;               /* increment eth device number */
-  }
-
-  /*
-  ** If an autoprobe is requested for another device, we must re-insert
-  ** the request later in the list. Remember the current position first.
-  */
-  if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
-    addAutoProbe++;
-    tmp = dev->next;                       /* point to the next device */
-    init = dev->init;                      /* remember the probe function */
-  }
+  if (!loading_module) {
+    while (dev->next != NULL) {
+      if ((dev->base_addr == 0xffe0) || (dev->base_addr == 0)) break;
+      dev = dev->next;                     /* walk through eth device list */
+      num_eth++;                           /* increment eth device number */
+    }
 
-  /*
-  ** If at end of list and can't use current entry, malloc one up. 
-  ** If memory could not be allocated, print an error message.
-  */
-  if ((dev->next == NULL) &&  
-      !((dev->base_addr == 0xffe0) || (dev->base_addr == 0))){
-    dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
-					 GFP_KERNEL);
+    /*
+    ** If an autoprobe is requested for another device, we must re-insert
+    ** the request later in the list. Remember the current position first.
+    */
+    if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
+      addAutoProbe++;
+      tmp = dev->next;                     /* point to the next device */
+      init = dev->init;                    /* remember the probe function */
+    }
 
-    dev = dev->next;                       /* point to the new device */
-    if (dev == NULL) {
-      printk("eth%d: Device not initialised, insufficient memory\n",
-	     num_eth);
-    } else {
-      /*
-      ** If the memory was allocated, point to the new memory area
-      ** and initialize it (name, I/O address, next device (NULL) and
-      ** initialisation probe routine).
-      */
-      dev->name = (char *)(dev + sizeof(struct device));
-      if (num_eth > 9999) {
-	sprintf(dev->name,"eth????");       /* New device name */
+    /*
+    ** If at end of list and can't use current entry, malloc one up. 
+    ** If memory could not be allocated, print an error message.
+    */
+    if ((dev->next == NULL) &&  
+	!((dev->base_addr == 0xffe0) || (dev->base_addr == 0))){
+      dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+					   GFP_KERNEL);
+
+      dev = dev->next;                     /* point to the new device */
+      if (dev == NULL) {
+	printk("eth%d: Device not initialised, insufficient memory\n",
+	       num_eth);
       } else {
-	sprintf(dev->name,"eth%d", num_eth);/* New device name */
+	/*
+	** If the memory was allocated, point to the new memory area
+	** and initialize it (name, I/O address, next device (NULL) and
+	** initialisation probe routine).
+	*/
+	dev->name = (char *)(dev + sizeof(struct device));
+	if (num_eth > 9999) {
+	  sprintf(dev->name,"eth????");    /* New device name */
+	} else {
+	  sprintf(dev->name,"eth%d", num_eth);/* New device name */
+	}
+	dev->base_addr = iobase;           /* assign the io address */
+	dev->next = NULL;                  /* mark the end of list */
+	dev->init = &de4x5_probe;          /* initialisation routine */
+	num_de4x5s++;
       }
-      dev->base_addr = iobase;             /* assign the io address */
-      dev->next = NULL;                    /* mark the end of list */
-      dev->init = &de4x5_probe;            /* initialisation routine */
-      num_de4x5s++;
     }
-  }
-  ret = dev;                               /* return current struct, or NULL */
+    ret = dev;                             /* return current struct, or NULL */
   
-  /*
-  ** Now figure out what to do with the autoprobe that has to be inserted.
-  ** Firstly, search the (possibly altered) list for an empty space.
-  */
-  if (ret != NULL) {
-    if (addAutoProbe) {
-      for (; (tmp->next!=NULL) && (tmp->base_addr!=0xffe0); tmp=tmp->next);
+    /*
+    ** Now figure out what to do with the autoprobe that has to be inserted.
+    ** Firstly, search the (possibly altered) list for an empty space.
+    */
+    if (ret != NULL) {
+      if (addAutoProbe) {
+	for (; (tmp->next!=NULL) && (tmp->base_addr!=0xffe0); tmp=tmp->next);
 
-      /*
-      ** If no more device structures and can't use the current one, malloc
-      ** one up. If memory could not be allocated, print an error message.
-      */
-      if ((tmp->next == NULL) && !(tmp->base_addr == 0xffe0)) {
-	tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
-					     GFP_KERNEL);
-	tmp = tmp->next;                     /* point to the new device */
-	if (tmp == NULL) {
-	  printk("%s: Insufficient memory to extend the device list.\n", 
-		 dev->name);
-	} else {
-	  /*
-	  ** If the memory was allocated, point to the new memory area
-	  ** and initialize it (name, I/O address, next device (NULL) and
-	  ** initialisation probe routine).
-	  */
-	  tmp->name = (char *)(tmp + sizeof(struct device));
-	  if (num_eth > 9999) {
-	    sprintf(tmp->name,"eth????");       /* New device name */
+	/*
+	** If no more device structures and can't use the current one, malloc
+	** one up. If memory could not be allocated, print an error message.
+	*/
+	if ((tmp->next == NULL) && !(tmp->base_addr == 0xffe0)) {
+	  tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+					       GFP_KERNEL);
+	  tmp = tmp->next;                     /* point to the new device */
+	  if (tmp == NULL) {
+	    printk("%s: Insufficient memory to extend the device list.\n", 
+		   dev->name);
 	  } else {
-	    sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+	    /*
+	    ** If the memory was allocated, point to the new memory area
+	    ** and initialize it (name, I/O address, next device (NULL) and
+	    ** initialisation probe routine).
+	    */
+	    tmp->name = (char *)(tmp + sizeof(struct device));
+	    if (num_eth > 9999) {
+	      sprintf(tmp->name,"eth????");       /* New device name */
+	    } else {
+	      sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+	    }
+	    tmp->base_addr = 0;                /* re-insert the io address */
+	    tmp->next = NULL;                  /* mark the end of list */
+	    tmp->init = init;                  /* initialisation routine */
 	  }
-	  tmp->base_addr = 0;                /* re-insert the io address */
-	  tmp->next = NULL;                  /* mark the end of list */
-	  tmp->init = init;                  /* initialisation routine */
+	} else {                               /* structure already exists */
+	  tmp->base_addr = 0;                  /* re-insert the io address */
 	}
-      } else {                               /* structure already exists */
-	tmp->base_addr = 0;                  /* re-insert the io address */
       }
     }
+  } else {
+    ret = dev;
   }
 
   return ret;
@@ -1401,7 +1460,7 @@
   int media, entry, iobase = dev->base_addr;
   char frame[64];
   u_long i, omr, sisr, linkBad;
-  u_long t_330ms = 920000;
+/*  u_long t_330ms = 920000;*/
   u_long t_3s    = 8000000;
 
   /* Set up for TP port, with LEDs */
@@ -1425,10 +1484,11 @@
  	                                    SICR_AUI | SICR_SRL, DE4X5_SICR);
 
     /* Wait 330ms */
-    for (i=0; i<t_330ms; i++) {
+    dce_ms_delay(330);
+/*    for (i=0; i<t_330ms; i++) {
       sisr = inl(DE4X5_SISR);
     }
-
+*/
     /* Make up a dummy packet with CRC error */
     create_packet(dev, frame, sizeof(frame));
 
@@ -1462,11 +1522,14 @@
       outl(SIGR_JCK | SIGR_HUJ, DE4X5_SIGR);
       outl(STRR_CLD | STRR_CSQ | STRR_RSQ | STRR_DREN | STRR_ECEN, DE4X5_STRR);
       outl(SICR_OE57| SICR_SEL | SICR_AUI | SICR_SRL, DE4X5_SICR);
+      
+      /* Wait 330ms */
+      dce_ms_delay(330);
 
       /* Setup the packet descriptor */
       entry = lp->tx_new;                      /* Remember the ring position */
       load_packet(dev, frame, TD_LS | TD_FS | TD_AC | sizeof(frame), NULL);
-
+      
       /* Start the TX process */
       omr = inl(DE4X5_OMR);
       outl(omr|OMR_ST, DE4X5_OMR);
@@ -1535,6 +1598,60 @@
   
   return;
 }
+
+/*
+** Get the timer ticks from the PIT
+*/
+static u_short dce_get_ticks(void)
+{
+  u_short ticks = 0;
+  
+  /* Command 8254 to latch T0's count */
+  outb(TIMER_PORT, TIMER_LATCH);
+  
+  /* Read the counter */
+  ticks = inb(TIMER_READ);
+  ticks |= (inb(TIMER_READ) << 8);
+  
+  return ticks;
+}
+
+/*
+** Known delay in microseconds
+*/
+static void dce_us_delay(u_long usec)
+{
+  u_long i, start, now, quant=(DELAY_QUANT*1000)/TIMER_TICK+1;
+  
+  for (i=0; i<usec/DELAY_QUANT; i++) {
+    start=dce_get_ticks();  
+    for (now=start; (start-now)<quant;) {
+      now=dce_get_ticks();
+      if (now > start) {         /* Wrapped counter counting down */
+	quant -= start;
+	start = (1 << TIMER_WIDTH);
+      }
+    }
+  }
+
+  return;
+}
+
+/*
+** Known delay in milliseconds
+*/
+static void dce_ms_delay(u_long msec)
+{
+  u_long i;
+  
+  for (i=0; i<msec; i++) {
+    dce_us_delay(1000);
+  }
+
+  return;
+}
+
+
 /*
 ** Look for a particular board name in the EISA configuration space
 */
@@ -1563,72 +1680,61 @@
       strcpy(name,ManCode);
     }
   }
-
+  
   return;                                   /* return the device name string */
 }
 
 /*
 ** Look for a special sequence in the Ethernet station address PROM that
 ** is common across all DIGITAL network adapter products.
+** 
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
 */
 
 static int DevicePresent(short aprom_addr)
 {
-  static short fp=1, sigLength=0;
-  static char devSig[] = ETH_PROM_SIG;
+  union {
+    struct {
+      u_long a;
+      u_long b;
+    } llsig;
+    char Sig[sizeof(long) << 1];
+  } dev;
   char data;
-  long i, j;
+  long i, j, tmp;
+  short sigLength;
   int status = 0;
   struct bus_type *lp = &bus;
-  static char asc2hex(char value);
 
-/* 
-** Convert the ascii signature to a hex equivalent & pack in place 
-*/
-  if (fp) {                               /* only do this once!... */
-    for (i=0,j=0;devSig[i] != '\0' && !status;i+=2,j++) {
-      if ((devSig[i]=asc2hex(devSig[i]))>=0) {
-	devSig[i]<<=4;
-	if((devSig[i+1]=asc2hex(devSig[i+1]))>=0){
-	  devSig[j]=devSig[i]+devSig[i+1];
-	} else {
-	  status= -1;
-	}
-      } else {
-	status= -1;
-      }
-    }
-    sigLength=j;
-    fp = 0;
-  }
+  dev.llsig.a = ETH_PROM_SIG;
+  dev.llsig.b = ETH_PROM_SIG;
+  sigLength = sizeof(long) << 1;
 
-/* 
-** Search the Ethernet address ROM for the signature. Since the ROM address
-** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the (length_of_the_signature - 1).
-** Stop the search IMMEDIATELY after the signature is found so that the
-** PROM address counter is correctly positioned at the start of the
-** ethernet address for later read out.
-*/
-  if (!status) {
-    long tmp;
-    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
-      if (lp->bus == PCI) {
-	while ((tmp = inl(aprom_addr)) < 0);
-	data = (char)tmp;
+  for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+    if (lp->bus == PCI) {
+      while ((tmp = inl(aprom_addr)) < 0);
+      data = (char)tmp;
+    } else {
+      data = inb(aprom_addr);
+    }
+    if (dev.Sig[j] == data) {   /* track signature */
+      j++;
+    } else {                    /* lost signature; begin search again */
+      if (data == dev.Sig[0]) {
+	j=1;
       } else {
-	data = inb(aprom_addr);
-      }
-      if (devSig[j] == data) {    /* track signature */
-	j++;
-      } else {                    /* lost signature; begin search again */
 	j=0;
       }
     }
+  }
 
-    if (j!=sigLength) {
-      status = -ENODEV;           /* search failed */
-    }
+  if (j!=sigLength) {
+    status = -ENODEV;           /* search failed */
   }
 
   return status;
@@ -1690,7 +1796,7 @@
   int i, j, iobase = dev->base_addr, status = 0;
   u_long omr;
   union {
-    unsigned char  addr[HASH_TABLE_LEN * ETH_ALEN];
+    unsigned char  addr[(HASH_TABLE_LEN * ETH_ALEN)];
     unsigned short sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
     unsigned long  lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
   } tmp;
@@ -1851,7 +1957,6 @@
       tmp.addr[j++] = dev->dev_addr[i];
     }
     tmp.addr[j++] = lp->rxRingSize;
-    tmp.lval[j>>2] = eisa_slots_full; j+=4;
     tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
     tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
 
@@ -1911,23 +2016,6 @@
   return status;
 }
 
-static char asc2hex(char value)
-{
-  value -= 0x30;                  /* normalise to 0..9 range */
-  if (value >= 0) {
-    if (value > 9) {              /* but may not be 10..15 */
-      value &= 0x1f;              /* make A..F & a..f be the same */
-      value -= 0x07;              /* normalise to 10..15 range */
-      if ((value < 0x0a) || (value > 0x0f)) { /* if outside range then... */
-	value = -1;               /* ...signal error */
-      }
-    }
-  } else {                        /* outside 0..9 range... */
-    value = -1;                   /* ...signal error */
-  }
-  return value;                   /* return hex char or error */
-}
-
 #ifdef MODULE
 char kernel_version[] = UTS_RELEASE;
 static struct device thisDE4X5 = {
@@ -1936,7 +2024,7 @@
   0x2000, 10, /* I/O address, IRQ */
   0, 0, 0, NULL, de4x5_probe };
 	
-int io=0x2000;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+int io=0x000b;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
 int irq=10;	/* or use the insmod io= irq= options 		*/
 
 int
@@ -1952,9 +2040,20 @@
 void
 cleanup_module(void)
 {
+  struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
+
   if (MOD_IN_USE) {
     printk("%s: device busy, remove delayed\n",thisDE4X5.name);
   } else {
+    release_region(thisDE4X5.base_addr, (lp->bus == PCI ? 
+					             DE4X5_PCI_TOTAL_SIZE :
+			                             DE4X5_EISA_TOTAL_SIZE));
+    if (lp) {
+      kfree_s(lp->rx_ring[0].buf, RX_BUFF_SZ * NUM_RX_DESC + LWPAD);
+    }
+    kfree_s(thisDE4X5.priv, sizeof(struct de4x5_private) + LWPAD);
+    thisDE4X5.priv = NULL;
+
     unregister_netdev(&thisDE4X5);
   }
 }
@@ -1968,6 +2067,5 @@
  *  module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
  * End:
  */
-
 
 
diff -u --recursive --new-file v1.1.90/linux/drivers/net/depca.c linux/drivers/net/depca.c
--- v1.1.90/linux/drivers/net/depca.c	Thu Jan 26 07:49:05 1995
+++ linux/drivers/net/depca.c	Sun Feb 12 16:19:28 1995
@@ -137,7 +137,7 @@
 
     To unload a module, turn off the associated interface 
     'ifconfig eth?? down' then 'rmmod depca'.
-    
+
     [Alan Cox: Changed to split off the module values as ints for insmod
      
      you can now do insmod depca.c irq=7 io=0x200 ]
@@ -168,20 +168,24 @@
       0.37    22-jul-94   Added MODULE support
       0.38    15-aug-94   Added DBR ROM switch in depca_close(). 
                           Multi DEPCA bug fix.
-      0.38axp 15-sep-94   Special version for Alpha AXP Linux V1.0
-      0.381   12-dec-94   Added DE101 recognition, fix multicast bug
+      0.38axp 15-sep-94   Special version for Alpha AXP Linux V1.0.
+      0.381   12-dec-94   Added DE101 recognition, fix multicast bug.
+      0.382    9-feb-95   Fix recognition bug reported by <bkm@star.rl.ac.uk>.
 
     =========================================================================
 */
 
-static char *version = "depca.c:v0.381 12/12/94 davies@wanton.lkg.dec.com\n";
+static char *version = "depca.c:v0.382 2/9/94 davies@wanton.lkg.dec.com\n";
 
+#include <linux/config.h>
 #ifdef MODULE
 #include <linux/module.h>
 #include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
 #endif /* MODULE */
 
-#include <stdarg.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -210,9 +214,7 @@
 #define PROBE_LENGTH    32
 #endif
 
-#ifndef PROBE_SEQUENCE
-#define PROBE_SEQUENCE "FF0055AAFF0055AA"
-#endif
+#define ETH_PROM_SIG    0xAA5500FFUL
 
 #ifndef DEPCA_SIGNATURE
 #define DEPCA_SIGNATURE {"DEPCA",\
@@ -274,13 +276,13 @@
 ** The DEPCA Rx and Tx ring descriptors. 
 */
 struct depca_rx_head {
-    long base;
+    volatile long base;
     short buf_length;		/* This length is negative 2's complement! */
     short msg_length;		/* This length is "normal". */
 };
 
 struct depca_tx_head {
-    long base;
+    volatile long base;
     short length;		/* This length is negative 2's complement! */
     short misc;                 /* Errors and TDR info */
 };
@@ -479,9 +481,7 @@
 	  j=inb(DEPCA_PROM);
 	}
 
-#ifdef HAVE_PORTRESERVE
-	request_region(ioaddr, DEPCA_TOTAL_SIZE,"depca");
-#endif
+	request_region(ioaddr, DEPCA_TOTAL_SIZE, dev->name);
 
 	/*
 	** Set up the maximum amount of network RAM(kB)
@@ -762,9 +762,7 @@
       printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
     }
 
-#ifdef MODULE
-  MOD_INC_USE_COUNT;
-#endif       
+    MOD_INC_USE_COUNT;
 
     return 0;			          /* Always succeed */
 }
@@ -1176,9 +1174,7 @@
 
   irq2dev_map[dev->irq] = 0;
 
-#ifdef MODULE
   MOD_DEC_USE_COUNT;
-#endif    
 
   return 0;
 }
@@ -1452,98 +1448,61 @@
 ** its ROM address counter to be initialized and enabled. Only enable
 ** if the first address octet is a 0x08 - this minimises the chances of
 ** messing around with some other hardware, but it assumes that this DEPCA
-** card initialized itself correctly. It also assumes that all past and
-** future DEPCA/EtherWORKS cards will have ethernet addresses beginning with
-** a 0x08.
+** card initialized itself correctly.
+** 
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
 */
-
 static int DevicePresent(short ioaddr)
 {
-  static short fp=1,sigLength=0;
-  static char devSig[] = PROBE_SEQUENCE;
+  union {
+    struct {
+      u_long a;
+      u_long b;
+    } llsig;
+    char Sig[sizeof(long) << 1];
+  } dev;
+  short sigLength=0;
   char data;
   int i, j, nicsr, status = 0;
-  static char asc2hex(char value);
 
-/*
-** Initialize the counter on a DEPCA card. Two reads to ensure DEPCA ethernet
-** address counter is a) cleared and b) the correct data read.
-*/
-  data = inb(DEPCA_PROM);                /* clear counter */
+  data = inb(DEPCA_PROM);                /* clear counter on DEPCA */
   data = inb(DEPCA_PROM);                /* read data */
 
-/*
-** Enable counter
-*/
-  if (data == 0x08) {
+  if (data == 0x08) {                    /* Enable counter on DEPCA */
     nicsr = inb(DEPCA_NICSR);
     nicsr |= AAC;
     outb(nicsr, DEPCA_NICSR);
   }
   
-/* 
-** Convert the ascii signature to a hex equivalent & pack in place 
-*/
-  if (fp) {                               /* only do this once!... */
-    for (i=0,j=0;devSig[i] != '\0' && !status;i+=2,j++) {
-      if ((devSig[i]=asc2hex(devSig[i]))>=0) {
-	devSig[i]<<=4;
-	if((devSig[i+1]=asc2hex(devSig[i+1]))>=0){
-	  devSig[j]=devSig[i]+devSig[i+1];
-	} else {
-	  status= -1;
-	}
+  dev.llsig.a = ETH_PROM_SIG;
+  dev.llsig.b = ETH_PROM_SIG;
+  sigLength = sizeof(long) << 1;
+
+  for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+    data = inb(DEPCA_PROM);
+    if (dev.Sig[j] == data) {    /* track signature */
+      j++;
+    } else {                    /* lost signature; begin search again */
+      if (data == dev.Sig[0]) {  /* rare case.... */
+	j=1;
       } else {
-	status= -1;
-      }
-    }
-    sigLength=j;
-    fp = 0;
-  }
-
-/* 
-** Search the Ethernet address ROM for the signature. Since the ROM address
-** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the (length_of_the_signature - 1).
-** Stop the search IMMEDIATELY after the signature is found so that the
-** PROM address counter is correctly positioned at the start of the
-** ethernet address for later read out.
-*/
-  if (!status) {
-    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
-      data = inb(DEPCA_PROM);
-      if (devSig[j] == data) {    /* track signature */
-	j++;
-      } else {                    /* lost signature; begin search again */
 	j=0;
       }
     }
+  }
 
-    if (j!=sigLength) {
-      status = -ENODEV;           /* search failed */
-    }
+  if (j!=sigLength) {
+    status = -ENODEV;           /* search failed */
   }
 
   return status;
 }
 
-static char asc2hex(char value)
-{
-  value -= 0x30;                  /* normalise to 0..9 range */
-  if (value >= 0) {
-    if (value > 9) {              /* but may not be 10..15 */
-      value &= 0x1f;              /* make A..F & a..f be the same */
-      value -= 0x07;              /* normalise to 10..15 range */
-      if ((value < 0x0a) || (value > 0x0f)) { /* if outside range then... */
-	value = -1;               /* ...signal error */
-      }
-    }
-  } else {                        /* outside 0..9 range... */
-    value = -1;                   /* ...signal error */
-  }
-  return value;                   /* return hex char or error */
-}
-
 #ifdef MODULE
 char kernel_version[] = UTS_RELEASE;
 static struct device thisDepca = {
@@ -1576,6 +1535,7 @@
   if (MOD_IN_USE) {
     printk("%s: device busy, remove delayed\n",thisDepca.name);
   } else {
+    release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
     unregister_netdev(&thisDepca);
   }
 }
diff -u --recursive --new-file v1.1.90/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.1.90/linux/drivers/net/plip.c	Thu Feb  9 10:18:52 1995
+++ linux/drivers/net/plip.c	Sun Feb 12 21:11:01 1995
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.10 1995/02/08 05:47:12 gniibe Exp $ */
+/* $Id: plip.c,v 1.12 1995/02/11 10:26:05 gniibe Exp $ */
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -163,7 +163,29 @@
 struct plip_local {
 	enum plip_packet_state state;
 	enum plip_nibble_state nibble;
-	unsigned short length;
+	union {
+		struct {
+#if defined(__i386__)
+			unsigned char lsb;
+			unsigned char msb;
+#elif defined(__mc68000__)
+			unsigned char msb;
+			unsigned char lsb;
+#elif defined(__MIPSEL__)
+			unsigned char lsb;
+			unsigned char msb;
+#elif defined(__MIPSEB__)
+			unsigned char msb;
+			unsigned char lsb;
+#elif defined(__alpha__)
+			unsigned char lsb;
+			unsigned char msb;
+#else
+#error	"Adjust this structure to match your CPU"
+#endif						
+		} b;
+		unsigned short h;
+	} length;
 	unsigned short byte;
 	unsigned char  checksum;
 	unsigned char  data;
@@ -178,10 +200,11 @@
 	struct plip_local rcv_data;
 	unsigned long  trigger;
 	unsigned long  nibble;
-	unsigned long  unit;
 	enum plip_connection_state connection;
 	unsigned short timeout_count;
 	char is_deferred;
+	int (*orig_rebuild_header)(void *eth, struct device *dev,
+				   unsigned long raddr, struct sk_buff *skb);
 };
 
 /* Entry point of PLIP driver.
@@ -235,24 +258,27 @@
 	ether_setup(dev);
 
 	/* Then, override parts of it */
-	dev->rebuild_header 	= plip_rebuild_header;
 	dev->hard_start_xmit	= plip_tx_packet;
 	dev->open		= plip_open;
 	dev->stop		= plip_close;
 	dev->get_stats 		= plip_get_stats;
 	dev->set_config		= plip_config;
 	dev->do_ioctl		= plip_ioctl;
-	dev->flags	        = IFF_POINTOPOINT;
+	dev->flags	        = IFF_POINTOPOINT|IFF_NOARP;
 
 	/* Set the private structure */
 	dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return EAGAIN;
 	memset(dev->priv, 0, sizeof(struct net_local));
 	nl = (struct net_local *) dev->priv;
 
+	nl->orig_rebuild_header = dev->rebuild_header;
+	dev->rebuild_header 	= plip_rebuild_header;
+
 	/* Initialize constants */
 	nl->trigger	= PLIP_TRIGGER_WAIT;
 	nl->nibble	= PLIP_NIBBLE_WAIT;
-	nl->unit	= PLIP_DELAY_UNIT;
 
 	/* Initialize task queue structures */
 	nl->immediate.next = &tq_last;
@@ -407,8 +433,7 @@
 /* PLIP_RECEIVE --- receive a byte(two nibbles)
    Returns OK on success, TIMEOUT on timeout */
 inline static int
-plip_receive(unsigned short nibble_timeout, unsigned short unit,
-	     unsigned short status_addr, unsigned short data_addr,
+plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
 	     enum plip_nibble_state *ns_p, unsigned char *data_p)
 {
 	unsigned char c0, c1;
@@ -419,7 +444,7 @@
 		cx = nibble_timeout;
 		while (1) {
 			c0 = inb(status_addr);
-			udelay(unit);
+			udelay(PLIP_DELAY_UNIT);
 			if ((c0 & 0x80) == 0) {
 				c1 = inb(status_addr);
 				if (c0 == c1)
@@ -429,14 +454,15 @@
 				return TIMEOUT;
 		}
 		*data_p = (c0 >> 3) & 0x0f;
-		outb(0x10, data_addr); /* send ACK */
+		outb(0x10, --status_addr); /* send ACK */
+		status_addr++;
 		*ns_p = PLIP_NB_1;
 
 	case PLIP_NB_1:
 		cx = nibble_timeout;
 		while (1) {
 			c0 = inb(status_addr);
-			udelay(unit);
+			udelay(PLIP_DELAY_UNIT);
 			if (c0 & 0x80) {
 				c1 = inb(status_addr);
 				if (c0 == c1)
@@ -446,7 +472,8 @@
 				return TIMEOUT;
 		}
 		*data_p |= (c0 << 1) & 0xf0;
-		outb(0x00, data_addr); /* send ACK */
+		outb(0x00, --status_addr); /* send ACK */
+		status_addr++;
 		*ns_p = PLIP_NB_BEGIN;
 		return OK;
 
@@ -459,9 +486,8 @@
 plip_receive_packet(struct device *dev, struct net_local *nl,
 		    struct plip_local *snd, struct plip_local *rcv)
 {
-	unsigned short data_addr = PAR_DATA(dev),
-	               status_addr = PAR_STATUS(dev);
-	unsigned short nibble_timeout = nl->nibble, unit = nl->unit;
+	unsigned short status_addr = PAR_STATUS(dev);
+	unsigned short nibble_timeout = nl->nibble;
 	unsigned char *lbuf;
 
 	switch (rcv->state) {
@@ -477,9 +503,8 @@
 
 	case PLIP_PK_LENGTH_LSB:
 		if (snd->state != PLIP_PK_DONE) {
-			if (plip_receive(nl->trigger, unit,
-					 status_addr, data_addr, &rcv->nibble,
-					 (unsigned char *)&rcv->length)) {
+			if (plip_receive(nl->trigger, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb)) {
 				/* collision, here dev->tbusy == 1 */
 				rcv->state = PLIP_PK_DONE;
 				nl->is_deferred = 1;
@@ -490,29 +515,27 @@
 				return OK;
 			}
 		} else {
-			if (plip_receive(nibble_timeout, unit,
-					 status_addr, data_addr, &rcv->nibble,
-					 (unsigned char *)&rcv->length))
+			if (plip_receive(nibble_timeout, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb))
 				return TIMEOUT;
 		}
 		rcv->state = PLIP_PK_LENGTH_MSB;
 
 	case PLIP_PK_LENGTH_MSB:
-		if (plip_receive(nibble_timeout, unit,
-				 status_addr, data_addr, &rcv->nibble,
-				 (unsigned char *)&rcv->length+1))
+		if (plip_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &rcv->length.b.msb))
 			return TIMEOUT;
-		if (rcv->length > dev->mtu || rcv->length < 8) {
-			printk("%s: bogus packet size %d.\n", dev->name, rcv->length);
+		if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
+			printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
 			return ERROR;
 		}
 		/* Malloc up new buffer. */
-		rcv->skb = alloc_skb(rcv->length, GFP_ATOMIC);
+		rcv->skb = alloc_skb(rcv->length.h, GFP_ATOMIC);
 		if (rcv->skb == NULL) {
 			printk("%s: Memory squeeze.\n", dev->name);
 			return ERROR;
 		}
-		rcv->skb->len = rcv->length;
+		rcv->skb->len = rcv->length.h;
 		rcv->skb->dev = dev;
 		rcv->state = PLIP_PK_DATA;
 		rcv->byte = 0;
@@ -520,18 +543,19 @@
 
 	case PLIP_PK_DATA:
 		lbuf = rcv->skb->data;
-		do {
-			if (plip_receive(nibble_timeout, unit,
-					 status_addr, data_addr,
+		do
+			if (plip_receive(nibble_timeout, status_addr, 
 					 &rcv->nibble, &lbuf[rcv->byte]))
 				return TIMEOUT;
-			rcv->checksum += lbuf[rcv->byte];
-		} while (++rcv->byte < rcv->length);
+		while (++rcv->byte < rcv->length.h);
+		do
+			rcv->checksum += lbuf[--rcv->byte];
+		while (rcv->byte);
 		rcv->state = PLIP_PK_CHECKSUM;
 
 	case PLIP_PK_CHECKSUM:
-		if (plip_receive(nibble_timeout, unit, status_addr,
-				 data_addr, &rcv->nibble, &rcv->data))
+		if (plip_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &rcv->data))
 			return TIMEOUT;
 		if (rcv->data != rcv->checksum) {
 			nl->enet_stats.rx_crc_errors++;
@@ -573,8 +597,7 @@
 /* PLIP_SEND --- send a byte (two nibbles) 
    Returns OK on success, TIMEOUT when timeout    */
 inline static int
-plip_send(unsigned short nibble_timeout, unsigned short unit,
-	  unsigned short status_addr, unsigned short data_addr,
+plip_send(unsigned short nibble_timeout, unsigned short data_addr,
 	  enum plip_nibble_state *ns_p, unsigned char data)
 {
 	unsigned char c0;
@@ -588,28 +611,31 @@
 	case PLIP_NB_1:
 		outb(0x10 | (data & 0x0f), data_addr);
 		cx = nibble_timeout;
+		data_addr++;
 		while (1) {
-			c0 = inb(status_addr);
+			c0 = inb(data_addr);
 			if ((c0 & 0x80) == 0) 
 				break;
 			if (--cx == 0)
 				return TIMEOUT;
-			udelay(unit);
+			udelay(PLIP_DELAY_UNIT);
 		}
-		outb(0x10 | (data >> 4), data_addr);
+		outb(0x10 | (data >> 4), --data_addr);
 		*ns_p = PLIP_NB_2;
 
 	case PLIP_NB_2:
 		outb((data >> 4), data_addr);
+		data_addr++;
 		cx = nibble_timeout;
 		while (1) {
-			c0 = inb(status_addr);
+			c0 = inb(data_addr);
 			if (c0 & 0x80)
 				break;
 			if (--cx == 0)
 				return TIMEOUT;
-			udelay(unit);
+			udelay(PLIP_DELAY_UNIT);
 		}
+		data_addr--;
 		*ns_p = PLIP_NB_BEGIN;
 		return OK;
 	}
@@ -620,9 +646,8 @@
 plip_send_packet(struct device *dev, struct net_local *nl,
 		 struct plip_local *snd, struct plip_local *rcv)
 {
-	unsigned short data_addr = PAR_DATA(dev),
-		       status_addr = PAR_STATUS(dev);
-	unsigned short nibble_timeout = nl->nibble, unit = nl->unit;
+	unsigned short data_addr = PAR_DATA(dev);
+	unsigned short nibble_timeout = nl->nibble;
 	unsigned char *lbuf;
 	unsigned char c0;
 	unsigned int cx;
@@ -637,10 +662,10 @@
 	switch (snd->state) {
 	case PLIP_PK_TRIGGER:
 		/* Trigger remote rx interrupt. */
-		outb(0x08, PAR_DATA(dev));
+		outb(0x08, data_addr);
 		cx = nl->trigger;
 		while (1) {
-			udelay(nl->unit);
+			udelay(PLIP_DELAY_UNIT);
 			cli();
 			if (nl->connection == PLIP_CN_RECEIVE) {
 				sti();
@@ -664,37 +689,38 @@
 			}
 			sti();
 			if (--cx == 0) {
-				outb(0x00, PAR_DATA(dev));
+				outb(0x00, data_addr);
 				return TIMEOUT;
 			}
 		}
 
 	case PLIP_PK_LENGTH_LSB:
-		if (plip_send(nibble_timeout, unit, status_addr, data_addr,
-			      &snd->nibble, snd->length & 0xff))
+		if (plip_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.lsb))
 			return TIMEOUT;
 		snd->state = PLIP_PK_LENGTH_MSB;
 
 	case PLIP_PK_LENGTH_MSB:
-		if (plip_send(nibble_timeout, unit, status_addr, data_addr,
-			      &snd->nibble, snd->length >> 8))
+		if (plip_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.msb))
 			return TIMEOUT;
 		snd->state = PLIP_PK_DATA;
 		snd->byte = 0;
 		snd->checksum = 0;
 
 	case PLIP_PK_DATA:
-		do {
-			if (plip_send(nibble_timeout, unit,
-				      status_addr, data_addr,
+		do
+			if (plip_send(nibble_timeout, data_addr,
 				      &snd->nibble, lbuf[snd->byte]))
 				return TIMEOUT;
-			snd->checksum += lbuf[snd->byte];
-		} while (++snd->byte < snd->length);
+		while (++snd->byte < snd->length.h);
+		do
+			snd->checksum += lbuf[--snd->byte];
+		while (snd->byte);
 		snd->state = PLIP_PK_CHECKSUM;
 
 	case PLIP_PK_CHECKSUM:
-		if (plip_send(nibble_timeout, unit, status_addr, data_addr,
+		if (plip_send(nibble_timeout, data_addr,
 			      &snd->nibble, snd->checksum))
 			return TIMEOUT;
 
@@ -704,7 +730,7 @@
 
 	case PLIP_PK_DONE:
 		/* Close the connection */
-		outb (0x00, PAR_DATA(dev));
+		outb (0x00, data_addr);
 		snd->skb = NULL;
 		if (net_debug > 2)
 			printk("%s: send end\n", dev->name);
@@ -816,9 +842,13 @@
 plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
 		    struct sk_buff *skb)
 {
+	struct net_local *nl = (struct net_local *)dev->priv;
 	struct ethhdr *eth = (struct ethhdr *)buff;
 	int i;
 
+	if ((dev->flags & IFF_NOARP)==0)
+		return nl->orig_rebuild_header(buff, dev, dst, skb);
+
 	if (eth->h_proto != htons(ETH_P_IP)) {
 		printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
 		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
@@ -865,7 +895,7 @@
 	cli();
 	dev->trans_start = jiffies;
 	snd->skb = skb;
-	snd->length = skb->len;
+	snd->length.h = skb->len;
 	snd->state = PLIP_PK_TRIGGER;
 	if (nl->connection == PLIP_CN_NONE) {
 		nl->connection = PLIP_CN_SEND;
@@ -997,12 +1027,10 @@
 	case PLIP_GET_TIMEOUT:
 		pc->trigger = nl->trigger;
 		pc->nibble  = nl->nibble;
-		pc->unit    = nl->unit;
 		break;
 	case PLIP_SET_TIMEOUT:
 		nl->trigger = pc->trigger;
 		nl->nibble  = pc->nibble;
-		nl->unit    = pc->unit;
 		break;
 	default:
 		return -EOPNOTSUPP;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v1.1.90/linux/drivers/scsi/eata.c	Thu Feb  9 10:18:52 1995
+++ linux/drivers/scsi/eata.c	Thu Feb  9 18:42:47 1995
@@ -1,8 +1,12 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *       9 Feb 1995 rev. 1.16 for linux 1.1.90
+ *          Use host->wish_block instead of host->block.
+ *          New list of Data Out SCSI commands.
+ *
  *       8 Feb 1995 rev. 1.15 for linux 1.1.89
- *          Cleared target_time_out counter while preforming a reset.
+ *          Cleared target_time_out counter while performing a reset.
  *          All external symbols renamed to avoid possible name conflicts.
  *
  *      28 Jan 1995 rev. 1.14 for linux 1.1.86
@@ -88,7 +92,7 @@
  *  CACHE             : DISABLED
  *
  *  In order to support multiple ISA boards in a reliable way,
- *  the driver sets host->block = TRUE for all ISA boards.
+ *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
 #if defined(MODULE)
@@ -167,6 +171,8 @@
 #define ASOK              0x00
 #define ASST              0x01
 
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+
 /* "EATA", in Big Endian format */
 #define EATA_SIGNATURE 0x41544145
 
@@ -466,7 +472,7 @@
    if (HD(j)->subversion == ESA)
       sh[j]->unchecked_isa_dma = FALSE;
    else {
-      sh[j]->block = sh[j];
+      sh[j]->wish_block = TRUE;
       sh[j]->unchecked_isa_dma = TRUE;
       disable_dma(dma_channel);
       clear_dma_ff(dma_channel);
@@ -562,6 +568,12 @@
    struct mscp *cpp;
    struct mssp *spp;
 
+   static const unsigned char data_out_cmds[] = {
+      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d, 
+      0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 
+      0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+      };
+
    save_flags(flags);
    cli();
    /* j is the board number */
@@ -619,11 +631,13 @@
    if (do_trace) printk("%s: qcomm, mbox %d, target %d, pid %ld.\n",
                         BN(j), i, SCpnt->target, SCpnt->pid);
 
-   if (SCpnt->cmnd[0] == WRITE_10 || SCpnt->cmnd[0] == WRITE_6)
-      cpp->dout = TRUE;
-   else
-      cpp->din = TRUE;
+   for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+     if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+        cpp->dout = TRUE;
+        break;
+        }
 
+   cpp->din = !cpp->dout;
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
    cpp->one = TRUE;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v1.1.90/linux/drivers/scsi/eata.h	Thu Feb  9 10:18:52 1995
+++ linux/drivers/scsi/eata.h	Thu Feb  9 18:42:47 1995
@@ -7,7 +7,7 @@
 
 #include <linux/scsicam.h>
 
-#define EATA_VERSION "1.15.00"
+#define EATA_VERSION "1.16.00"
 
 int eata2x_detect(Scsi_Host_Template *);
 int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.1.90/linux/drivers/scsi/hosts.c	Thu Feb  9 10:18:52 1995
+++ linux/drivers/scsi/hosts.c	Thu Feb  9 18:42:47 1995
@@ -241,6 +241,7 @@
 						      (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
 	retval->host_busy = 0;
 	retval->block = NULL;
+	retval->wish_block = 0;
 	if(j > 0xffff) panic("Too many extra bytes requested\n");
 	retval->extra_bytes = j;
 	retval->loaded_as_module = scsi_loadable_module_flag;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v1.1.90/linux/drivers/scsi/hosts.h	Sun Feb  5 19:31:54 1995
+++ linux/drivers/scsi/hosts.h	Thu Feb  9 18:42:47 1995
@@ -243,6 +243,7 @@
 		   that should be locked out of performing I/O while we have an active
 		   command on this host. */
 		struct Scsi_Host * block;
+		unsigned wish_block:1;
 
 		/* These parameters should be set by the detect routine */
 		unsigned char *base;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.1.90/linux/drivers/scsi/scsi.c	Thu Feb  9 10:18:52 1995
+++ linux/drivers/scsi/scsi.c	Thu Feb  9 18:42:47 1995
@@ -212,10 +212,7 @@
 
   /*
    * Create a circular linked list from the scsi hosts which have
-   * the "block" field in the Scsi_Host structure set to any value
-   * different from NULL.
-   * If there is only one host such that host->block != NULL, the list is
-   * empty and host->block is reset to NULL.
+   * the "wish_block" field in the Scsi_Host structure set.
    * The blocked list should include all the scsi hosts using ISA DMA.
    * In some systems, using two dma channels simultaneously causes
    * unpredictable results.
@@ -243,10 +240,10 @@
       * Useful to put into the blocked list all the hosts whose driver
       * does not know about the host->block feature.
       */
-     if (shpnt->unchecked_isa_dma) shpnt->block = shpnt;
+     if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
 #endif
 
-     if (shpnt->block) sh[block_count++] = shpnt;
+     if (shpnt->wish_block) sh[block_count++] = shpnt;
      }
 
   if (block_count == 1) sh[0]->block = NULL;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.1.90/linux/drivers/scsi/sg.c	Sun Feb  5 19:31:54 1995
+++ linux/drivers/scsi/sg.c	Thu Feb  9 18:44:38 1995
@@ -374,7 +374,7 @@
    memset(scsi_generics, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
 	  * sizeof(struct scsi_generic));
 
-   sg_template.dev_max = sg_template.dev_noticed;
+   sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
  }
 
 static int sg_attach(Scsi_Device * SDp)
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v1.1.90/linux/drivers/scsi/u14-34f.c	Thu Feb  9 10:18:53 1995
+++ linux/drivers/scsi/u14-34f.c	Thu Feb  9 18:42:47 1995
@@ -1,8 +1,11 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *       9 Feb 1995 rev. 1.16 for linux 1.1.90
+ *          Use host->wish_block instead of host->block.
+ *
  *       8 Feb 1995 rev. 1.15 for linux 1.1.89
- *          Cleared target_time_out counter while preforming a reset.
+ *          Cleared target_time_out counter while performing a reset.
  *
  *      28 Jan 1995 rev. 1.14 for linux 1.1.86
  *          Added module support.
@@ -111,7 +114,7 @@
  *    The new firmware has fixed all the above problems.
  *
  *  In order to support multiple ISA boards in a reliable way,
- *  the driver sets host->block = TRUE for all ISA boards.
+ *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
 #if defined(MODULE)
@@ -428,7 +431,7 @@
       sprintf(BN(j), "U34F%d", j);
       }
    else {
-      sh[j]->block = sh[j];
+      sh[j]->wish_block = TRUE;
 
 #if defined (HAVE_OLD_U14F_FIRMWARE)
       sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
diff -u --recursive --new-file v1.1.90/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v1.1.90/linux/drivers/scsi/u14-34f.h	Thu Feb  9 10:18:53 1995
+++ linux/drivers/scsi/u14-34f.h	Thu Feb  9 18:42:47 1995
@@ -10,7 +10,7 @@
 int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_biosparam(Disk *, int, int *);
 
-#define U14_34F_VERSION "1.15.00"
+#define U14_34F_VERSION "1.16.00"
 
 #define ULTRASTOR_14_34F {                                            \
                 NULL, /* Ptr for modules */                           \
diff -u --recursive --new-file v1.1.90/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v1.1.90/linux/drivers/sound/Readme	Thu Feb  9 10:18:53 1995
+++ linux/drivers/sound/Readme	Thu Feb  9 18:29:53 1995
@@ -79,7 +79,7 @@
 		Finland
 FAX:		+358 0 341 6272 (answers if I have my machine (mgetty) on).
 
-NOTE!	I propably don't answer to Snail mail or FAX messages. Sending answer
+NOTE!	I probably don't answer to Snail mail or FAX messages. Sending answer
 	to each of them is simply too expensive and time consuming. However I
 	try to reply every email message I get (within a week). If you don't
 	get response, please check how your address is written in the message
diff -u --recursive --new-file v1.1.90/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v1.1.90/linux/fs/proc/inode.c	Sun Feb  5 19:31:54 1995
+++ linux/fs/proc/inode.c	Sun Feb 12 21:11:02 1995
@@ -140,17 +140,17 @@
 	}
 
 #ifdef CONFIG_IP_ACCT
-	/* be careful: /proc/net/ip_acct_0 resets IP accounting */
-	if (ino == PROC_NET_IPACCT0) {
-		inode->i_mode = S_IFREG | S_IRUSR;
+	/* this file may be opened R/W by root to reset the accounting */
+	if (ino == PROC_NET_IPACCT) {
+		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
 		inode->i_op = &proc_net_inode_operations;
 		return;
 	}
 #endif
 #ifdef CONFIG_IP_FIREWALL
-	/* /proc/net/ip_forward_0 and /proc/net/ip_block_0 reset counters */
-	if ((ino == PROC_NET_IPFWFWD0) || (ino == PROC_NET_IPFWBLK0)) {
-		inode->i_mode = S_IFREG | S_IRUSR;
+	/* these files may be opened R/W by root to reset the counters */
+	if ((ino == PROC_NET_IPFWFWD) || (ino == PROC_NET_IPFWBLK)) {
+		inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
 		inode->i_op = &proc_net_inode_operations;
 		return;
 	}
diff -u --recursive --new-file v1.1.90/linux/fs/proc/net.c linux/fs/proc/net.c
--- v1.1.90/linux/fs/proc/net.c	Sun Feb  5 19:31:54 1995
+++ linux/fs/proc/net.c	Sun Feb 12 21:11:02 1995
@@ -32,6 +32,7 @@
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/fcntl.h>
 #include <linux/config.h>
 #include <linux/mm.h>
 
@@ -59,14 +60,11 @@
 extern int wavelan_get_info(char *, char **, off_t, int);
 #endif	/* defined(CONFIG_WAVELAN) */
 #ifdef CONFIG_IP_ACCT
-extern int ip_acct_procinfo(char *, char **, off_t, int);
-extern int ip_acct0_procinfo(char *, char **, off_t, int);
+extern int ip_acct_procinfo(char *, char **, off_t, int, int);
 #endif /* CONFIG_IP_ACCT */
 #ifdef CONFIG_IP_FIREWALL
-extern int ip_fw_blk_procinfo(char *, char **, off_t, int);
-extern int ip_fw_blk0_procinfo(char *, char **, off_t, int);
-extern int ip_fw_fwd_procinfo(char *, char **, off_t, int);
-extern int ip_fw_fwd0_procinfo(char *, char **, off_t, int);
+extern int ip_fw_blk_procinfo(char *, char **, off_t, int, int);
+extern int ip_fw_fwd_procinfo(char *, char **, off_t, int, int);
 #endif /* CONFIG_IP_FIREWALL */
 extern int ip_msqhst_procinfo(char *, char **, off_t, int);
 extern int ip_mc_procinfo(char *, char **, off_t, int);
@@ -147,16 +145,13 @@
 #endif
 #ifdef CONFIG_IP_FIREWALL
 	{ PROC_NET_IPFWFWD,	10, "ip_forward"},
-	{ PROC_NET_IPFWFWD0,	12, "ip_forward_0"},
 	{ PROC_NET_IPFWBLK,	8,  "ip_block"},
-	{ PROC_NET_IPFWBLK0,	10, "ip_block_0"},
 #endif
 #ifdef CONFIG_IP_MASQUERADE
 	{ PROC_NET_IPMSQHST,	13, "ip_masquerade"},
 #endif
 #ifdef CONFIG_IP_ACCT
 	{ PROC_NET_IPACCT,	7,  "ip_acct"},
-	{ PROC_NET_IPACCT0,	9,  "ip_acct_0"},
 #endif
 #if	defined(CONFIG_WAVELAN)
 	{ PROC_NET_WAVELAN,	7, "wavelan" },
@@ -300,24 +295,18 @@
 #endif
 #ifdef CONFIG_IP_FIREWALL
 			case PROC_NET_IPFWFWD:
-				length = ip_fw_fwd_procinfo(page, &start, file->f_pos,thistime);
-				break;
-			case PROC_NET_IPFWFWD0:
-				length = ip_fw_fwd0_procinfo(page, &start, file->f_pos,thistime);
+				length = ip_fw_fwd_procinfo(page, &start, file->f_pos,
+					thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
 				break;
 			case PROC_NET_IPFWBLK:
-				length = ip_fw_blk_procinfo(page, &start, file->f_pos,thistime);
-				break;
-			case PROC_NET_IPFWBLK0:
-				length = ip_fw_blk0_procinfo(page, &start, file->f_pos,thistime);
+				length = ip_fw_blk_procinfo(page, &start, file->f_pos,
+					thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
 				break;
 #endif
 #ifdef CONFIG_IP_ACCT
 			case PROC_NET_IPACCT:
-				length = ip_acct_procinfo(page, &start, file->f_pos,thistime);
-				break;
-			case PROC_NET_IPACCT0:
-				length = ip_acct0_procinfo(page, &start, file->f_pos,thistime);
+				length = ip_acct_procinfo(page, &start, file->f_pos,
+					thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
 				break;
 #endif
 #ifdef CONFIG_IP_MASQUERADE
diff -u --recursive --new-file v1.1.90/linux/include/asm-alpha/bitops.h linux/include/asm-alpha/bitops.h
--- v1.1.90/linux/include/asm-alpha/bitops.h	Sun Nov 20 21:50:47 1994
+++ linux/include/asm-alpha/bitops.h	Fri Feb 10 10:06:07 1995
@@ -32,7 +32,7 @@
 		 "=&r" (oldbit)
 		:"r" (1UL << (nr & 63)),
 		 "m" (((unsigned long *) addr)[nr >> 6]));
-	return oldbit;
+	return oldbit != 0;
 }
 
 extern __inline__ unsigned long clear_bit(unsigned long nr, void * addr)
@@ -54,7 +54,7 @@
 		 "=&r" (oldbit)
 		:"r" (1UL << (nr & 63)),
 		 "m" (((unsigned long *) addr)[nr >> 6]));
-	return oldbit;
+	return oldbit != 0;
 }
 
 extern __inline__ unsigned long change_bit(unsigned long nr, void * addr)
@@ -74,7 +74,7 @@
 		 "=&r" (oldbit)
 		:"r" (1UL << (nr & 63)),
 		 "m" (((unsigned long *) addr)[nr >> 6]));
-	return oldbit;
+	return oldbit != 0;
 }
 
 extern __inline__ unsigned long test_bit(int nr, void * addr)
diff -u --recursive --new-file v1.1.90/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v1.1.90/linux/include/asm-alpha/pgtable.h	Sun Feb  5 19:31:54 1995
+++ linux/include/asm-alpha/pgtable.h	Thu Feb  9 19:55:52 1995
@@ -158,16 +158,19 @@
 
 extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
 extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & _PAGE_VALID; }
+extern inline int pte_inuse(pte_t *ptep)	{ return mem_map[MAP_NR(ptep)] > 1; }
 extern inline void pte_clear(pte_t *ptep)	{ pte_val(*ptep) = 0; }
 
 extern inline int pmd_none(pmd_t pmd)		{ return !pmd_val(pmd); }
 extern inline int pmd_bad(pmd_t pmd)		{ return (pmd_val(pmd) & ~_PFN_MASK) != _PAGE_TABLE || pmd_page(pmd) > high_memory; }
 extern inline int pmd_present(pmd_t pmd)	{ return pmd_val(pmd) & _PAGE_VALID; }
+extern inline int pmd_inuse(pmd_t *pmdp)	{ return mem_map[MAP_NR(pmdp)] > 1; }
 extern inline void pmd_clear(pmd_t * pmdp)	{ pmd_val(*pmdp) = 0; }
 
 extern inline int pgd_none(pgd_t pgd)		{ return !pgd_val(pgd); }
 extern inline int pgd_bad(pgd_t pgd)		{ return (pgd_val(pgd) & ~_PFN_MASK) != _PAGE_TABLE || pgd_page(pgd) > high_memory; }
 extern inline int pgd_present(pgd_t pgd)	{ return pgd_val(pgd) & _PAGE_VALID; }
+extern inline int pgd_inuse(pgd_t *pgdp)	{ return mem_map[MAP_NR(pgdp)] > 1; }
 extern inline void pgd_clear(pgd_t * pgdp)	{ pgd_val(*pgdp) = 0; }
 
 /*
@@ -342,6 +345,16 @@
 		return NULL;
 	}
 	return (pmd_t *) pgd_page(*pgd) + address;
+}
+
+extern inline void pgd_free(pgd_t * pgd)
+{
+	free_page((unsigned long) pgd);
+}
+
+extern inline pgd_t * pgd_alloc(void)
+{
+	return (pgd_t *) get_free_page(GFP_KERNEL);
 }
 
 extern pgd_t swapper_pg_dir[1024];
diff -u --recursive --new-file v1.1.90/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v1.1.90/linux/include/asm-alpha/processor.h	Thu Jan 26 07:49:15 1995
+++ linux/include/asm-alpha/processor.h	Thu Feb  9 10:16:43 1995
@@ -77,4 +77,13 @@
 		: "0" (0));
 }
 
+/*
+ * Do necessary setup to start up a newly executed thread.
+ */
+static inline void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
+{
+	regs->pc = pc;
+	wrusp(sp);
+}
+
 #endif /* __ASM_ALPHA_PROCESSOR_H */
diff -u --recursive --new-file v1.1.90/linux/include/asm-alpha/ptrace.h linux/include/asm-alpha/ptrace.h
--- v1.1.90/linux/include/asm-alpha/ptrace.h	Sun Feb  5 19:31:54 1995
+++ linux/include/asm-alpha/ptrace.h	Thu Feb  9 19:58:45 1995
@@ -36,7 +36,7 @@
 	unsigned long r26;
 	unsigned long r27;
 	unsigned long r28;
-	unsigned long padding;
+	unsigned long hae;
 /* These are saved by PAL-code: */
 	unsigned long ps;
 	unsigned long pc;
diff -u --recursive --new-file v1.1.90/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v1.1.90/linux/include/asm-i386/pgtable.h	Wed Feb  1 09:23:07 1995
+++ linux/include/asm-i386/pgtable.h	Thu Feb  9 18:22:17 1995
@@ -29,9 +29,6 @@
 #define PTRS_PER_PMD	1
 #define PTRS_PER_PGD	1024
 
-/* the no. of pointers that fit on a page: this will go away */
-#define PTRS_PER_PAGE	(PAGE_SIZE/sizeof(void*))
-
 /* Just any arbitrary offset to the start of the vmalloc VM area: the
  * current 8MB value just means that there will be a 8MB "hole" after the
  * physical memory until the kernel virtual memory starts.  That means that
@@ -133,17 +130,36 @@
 
 extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
 extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & _PAGE_PRESENT; }
+extern inline int pte_inuse(pte_t *ptep)	{ return mem_map[MAP_NR(ptep)] > 1; }
 extern inline void pte_clear(pte_t *ptep)	{ pte_val(*ptep) = 0; }
 
 extern inline int pmd_none(pmd_t pmd)		{ return !pmd_val(pmd); }
 extern inline int pmd_bad(pmd_t pmd)		{ return (pmd_val(pmd) & ~PAGE_MASK) != _PAGE_TABLE || pmd_val(pmd) > high_memory; }
 extern inline int pmd_present(pmd_t pmd)	{ return pmd_val(pmd) & _PAGE_PRESENT; }
+extern inline int pmd_inuse(pmd_t *pmdp)	{ return 0; }
 extern inline void pmd_clear(pmd_t * pmdp)	{ pmd_val(*pmdp) = 0; }
 
+#ifdef THREE_LEVEL
+/*
+ * The "pgd_xxx()" functions here are trivial for a folded two-level
+ * setup: the pgd is never bad, and a pmd always exists (as it's folded
+ * into the pgd entry)
+ */
+extern inline int pgd_none(pgd_t pgd)		{ return 0; }
+extern inline int pgd_bad(pgd_t pgd)		{ return 0; }
+extern inline int pgd_present(pgd_t pgd)	{ return 1; }
+extern inline int pgd_inuse(pgd_t * pgdp)	{ return mem_map[MAP_NR(pgdp)] > 1; }
+extern inline void pgd_clear(pgd_t * pgdp)	{ }
+#else
+/*
+ * These are the old (and incorrect) ones needed for code that doesn't
+ * know about three-level yet..
+ */
 extern inline int pgd_none(pgd_t pgd)		{ return !pgd_val(pgd); }
 extern inline int pgd_bad(pgd_t pgd)		{ return (pgd_val(pgd) & ~PAGE_MASK) != _PAGE_TABLE || pgd_val(pgd) > high_memory; }
 extern inline int pgd_present(pgd_t pgd)	{ return pgd_val(pgd) & _PAGE_PRESENT; }
 extern inline void pgd_clear(pgd_t * pgdp)	{ pgd_val(*pgdp) = 0; }
+#endif
 
 /*
  * The following only work if pte_present() is true.
@@ -185,6 +201,8 @@
 extern inline unsigned long pmd_page(pmd_t pmd)
 { return pmd_val(pmd) & PAGE_MASK; }
 
+#ifndef THREE_LEVEL
+
 extern inline unsigned long pgd_page(pgd_t pgd)
 { return pgd_val(pgd) & PAGE_MASK; }
 
@@ -193,6 +211,11 @@
 
 #define PAGE_DIR_OFFSET(tsk,address) pgd_offset((tsk),(address))
 
+/* the no. of pointers that fit on a page: this will go away */
+#define PTRS_PER_PAGE	(PAGE_SIZE/sizeof(void*))
+
+#endif
+
 /* to find an entry in a page-table-directory */
 extern inline pgd_t * pgd_offset(struct task_struct * tsk, unsigned long address)
 {
@@ -298,6 +321,16 @@
 extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
 {
 	return (pmd_t *) pgd;
+}
+
+extern inline void pgd_free(pgd_t * pgd)
+{
+	free_page((unsigned long) pgd);
+}
+
+extern inline pgd_t * pgd_alloc(void)
+{
+	return (pgd_t *) get_free_page(GFP_KERNEL);
 }
 
 extern pgd_t swapper_pg_dir[1024];
diff -u --recursive --new-file v1.1.90/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v1.1.90/linux/include/asm-i386/processor.h	Thu Jan 26 07:49:15 1995
+++ linux/include/asm-i386/processor.h	Thu Feb  9 18:22:17 1995
@@ -39,7 +39,7 @@
  * User space process size: 3GB. This is hardcoded into a few places,
  * so don't change it unless you know what you are doing.
  */
-#define TASK_SIZE	0xc0000000
+#define TASK_SIZE	(0xC0000000UL)
 
 /*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
@@ -126,6 +126,14 @@
 	_TSS(0), 0, 0,0, \
 	{ { 0, }, },  /* 387 state */ \
 	NULL, 0, 0, 0, 0 /* vm86_info */ \
+}
+
+static inline void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp)
+{
+	regs->cs = USER_CS;
+	regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS;
+	regs->eip = eip;
+	regs->esp = esp;
 }
 
 #endif /* __ASM_I386_PROCESSOR_H */
diff -u --recursive --new-file v1.1.90/linux/include/asm-mips/processor.h linux/include/asm-mips/processor.h
--- v1.1.90/linux/include/asm-mips/processor.h	Thu Jan 26 07:49:15 1995
+++ linux/include/asm-mips/processor.h	Thu Feb  9 18:22:17 1995
@@ -80,7 +80,7 @@
  * User space process size: 2GB. This is hardcoded into a few places,
  * so don't change it unless you know what you are doing.
  */
-#define TASK_SIZE	0x80000000
+#define TASK_SIZE	(0x80000000UL)
 
 /*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
diff -u --recursive --new-file v1.1.90/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v1.1.90/linux/include/asm-sparc/processor.h	Thu Jan 26 07:49:15 1995
+++ linux/include/asm-sparc/processor.h	Thu Feb  9 18:22:17 1995
@@ -27,7 +27,7 @@
  *
  * "this is gonna have to change to 1gig for the sparc" - David S. Miller
  */
-#define TASK_SIZE	(0xc0000000UL)
+#define TASK_SIZE	(0xC0000000UL)
 
 /*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
diff -u --recursive --new-file v1.1.90/linux/include/linux/if_plip.h linux/include/linux/if_plip.h
--- v1.1.90/linux/include/linux/if_plip.h	Tue Jun 21 13:23:51 1994
+++ linux/include/linux/if_plip.h	Sun Feb 12 21:11:01 1995
@@ -20,7 +20,6 @@
 	unsigned short pcmd;
 	unsigned long  nibble;
 	unsigned long  trigger;
-	unsigned long  unit;
 };
 
 #define PLIP_GET_TIMEOUT	0x1
diff -u --recursive --new-file v1.1.90/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.1.90/linux/include/linux/proc_fs.h	Sun Feb  5 19:31:55 1995
+++ linux/include/linux/proc_fs.h	Sun Feb 12 21:11:02 1995
@@ -69,13 +69,10 @@
 #endif
 #ifdef CONFIG_IP_FIREWALL
 	PROC_NET_IPFWFWD,
-	PROC_NET_IPFWFWD0,
 	PROC_NET_IPFWBLK,
-	PROC_NET_IPFWBLK0,
 #endif
 #ifdef CONFIG_IP_ACCT
 	PROC_NET_IPACCT,
-	PROC_NET_IPACCT0,
 #endif
 #if	defined(CONFIG_WAVELAN)
 	PROC_NET_WAVELAN,
diff -u --recursive --new-file v1.1.90/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.90/linux/include/linux/sched.h	Sun Feb  5 19:31:55 1995
+++ linux/include/linux/sched.h	Thu Feb  9 10:22:15 1995
@@ -269,7 +269,6 @@
 extern void free_irq(unsigned int irq);
 
 extern unsigned long copy_thread(int, unsigned long, struct task_struct *, struct pt_regs *);
-extern void start_thread(struct pt_regs *, unsigned long pc, unsigned long sp);
 extern void flush_thread(void);
 extern void exit_thread(void);
 
diff -u --recursive --new-file v1.1.90/linux/include/linux/sonycd535.h linux/include/linux/sonycd535.h
--- v1.1.90/linux/include/linux/sonycd535.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/sonycd535.h	Sun Feb 12 15:00:45 1995
@@ -0,0 +1,183 @@
+#ifndef SONYCD535_H
+#define SONYCD535_H
+
+/*
+ * define all the commands recognized by the CDU-531/5
+ */
+#define SONY535_REQUEST_DRIVE_STATUS_1		(0x80)
+#define SONY535_REQUEST_SENSE			(0x82)
+#define SONY535_REQUEST_DRIVE_STATUS_2		(0x84)
+#define SONY535_REQUEST_ERROR_STATUS		(0x86)
+#define SONY535_REQUEST_AUDIO_STATUS		(0x88)
+#define SONY535_INQUIRY				(0x8a)
+
+#define SONY535_SET_INACTIVITY_TIME		(0x90)
+
+#define SONY535_SEEK_AND_READ_N_BLOCKS_1	(0xa0)
+#define SONY535_SEEK_AND_READ_N_BLOCKS_2	(0xa4)
+#define SONY535_PLAY_AUDIO			(0xa6)
+
+#define SONY535_REQUEST_DISC_CAPACITY		(0xb0)
+#define SONY535_REQUEST_TOC_DATA		(0xb2)
+#define SONY535_REQUEST_SUB_Q_DATA		(0xb4)
+#define SONY535_REQUEST_ISRC			(0xb6)
+#define SONY535_REQUEST_UPC_EAN			(0xb8)
+
+#define SONY535_SET_DRIVE_MODE			(0xc0)
+#define SONY535_REQUEST_DRIVE_MODE		(0xc2)
+#define SONY535_SET_RETRY_COUNT			(0xc4)
+
+#define SONY535_DIAGNOSTIC_1			(0xc6)
+#define SONY535_DIAGNOSTIC_4			(0xcc)
+#define SONY535_DIAGNOSTIC_5			(0xce)
+
+#define SONY535_EJECT_CADDY			(0xd0)
+#define SONY535_DISABLE_EJECT_BUTTON		(0xd2)
+#define SONY535_ENABLE_EJECT_BUTTON		(0xd4)
+
+#define SONY535_HOLD				(0xe0)
+#define SONY535_AUDIO_PAUSE_ON_OFF		(0xe2)
+#define SONY535_SET_VOLUME			(0xe8)
+
+#define SONY535_STOP				(0xf0)
+#define SONY535_SPIN_UP				(0xf2)
+#define SONY535_SPIN_DOWN			(0xf4)
+
+#define SONY535_CLEAR_PARAMETERS		(0xf6)
+#define SONY535_CLEAR_ENDING_ADDRESS		(0xf8)
+
+/*
+ * define some masks
+ */
+#define SONY535_DATA_NOT_READY_BIT		(0x1)
+#define SONY535_RESULT_NOT_READY_BIT		(0x2)
+
+/*
+ *  drive status 1
+ */
+#define SONY535_STATUS1_COMMAND_ERROR		(0x1)
+#define SONY535_STATUS1_DATA_ERROR		(0x2)
+#define SONY535_STATUS1_SEEK_ERROR		(0x4)
+#define SONY535_STATUS1_DISC_TYPE_ERROR		(0x8)
+#define SONY535_STATUS1_NOT_SPINNING		(0x10)
+#define SONY535_STATUS1_EJECT_BUTTON_PRESSED	(0x20)
+#define SONY535_STATUS1_CADDY_NOT_INSERTED	(0x40)
+#define SONY535_STATUS1_BYTE_TWO_FOLLOWS	(0x80)
+
+/*
+ * drive status 2
+ */
+#define SONY535_CDD_LOADING_ERROR		(0x7)
+#define SONY535_CDD_NO_DISC			(0x8)
+#define SONY535_CDD_UNLOADING_ERROR		(0x9)
+#define SONY535_CDD_CADDY_NOT_INSERTED		(0xd)
+#define SONY535_ATN_RESET_OCCURRED		(0x2)
+#define SONY535_ATN_DISC_CHANGED		(0x4)
+#define SONY535_ATN_RESET_AND_DISC_CHANGED	(0x6)
+#define SONY535_ATN_EJECT_IN_PROGRESS		(0xe)
+#define SONY535_ATN_BUSY			(0xf)
+
+/*
+ * define some parameters
+ */
+#define SONY535_AUDIO_DRIVE_MODE		(0)
+#define SONY535_CDROM_DRIVE_MODE		(0xe0)
+
+#define SONY535_PLAY_OP_PLAYBACK		(0)
+#define SONY535_PLAY_OP_ENTER_HOLD		(1)
+#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR	(2)
+#define SONY535_PLAY_OP_SCAN_FORWARD		(3)
+#define SONY535_PLAY_OP_SCAN_BACKWARD		(4)
+
+/*
+ *  convert from msf format to block number 
+ */
+#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f))
+#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2])
+
+/*
+ *  error return values from the doSonyCmd() routines
+ */
+#define TIME_OUT			(-1)
+#define NO_CDROM			(-2)
+#define BAD_STATUS			(-3)
+#define CD_BUSY				(-4)
+#define NOT_DATA_CD			(-5)
+#define NO_ROOM				(-6)
+
+#define LOG_START_OFFSET        150     /* Offset of first logical sector */
+
+#define SONY_JIFFIES_TIMEOUT            500  /* Maximum number of jiffies (10ms)
+                                                  the drive will wait/try for an
+                                                  operation */
+#define SONY_READY_RETRIES      (50000)  /* How many times to retry a
+                                                  spin waiting for a register
+                                                  to come ready */
+#define SONY535_FAST_POLLS	(10000)   /* how many times recheck 
+                                                  status waiting for a data
+                                                  to become ready */
+
+typedef unsigned char Byte;
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s535_sony_drive_config
+{
+   char vendor_id[8];
+   char product_id[16];
+   char product_rev_level[4];
+};
+
+/* The following is returned from the request sub-q data command */
+struct s535_sony_subcode
+{
+   unsigned char address        :4;
+   unsigned char control        :4;
+   unsigned char track_num;
+   unsigned char index_num;
+   unsigned char rel_msf[3];
+   unsigned char abs_msf[3];
+};
+
+struct s535_sony_disc_capacity
+{
+   Byte mFirstTrack, sFirstTrack, fFirstTrack;
+   Byte mLeadOut, sLeadOut, fLeadOut;
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s535_sony_toc
+{
+   unsigned char reserved0      :4;
+   unsigned char control0       :4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char reserved0a;
+   unsigned char reserved0b;
+   unsigned char reserved1      :4;
+   unsigned char control1       :4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char reserved2      :4;
+   unsigned char control2       :4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char reserved    :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[100];
+
+   unsigned int lead_out_start_lba;
+};
+
+#endif /* SONYCD535_H */
diff -u --recursive --new-file v1.1.90/linux/init/main.c linux/init/main.c
--- v1.1.90/linux/init/main.c	Thu Jan 26 07:49:15 1995
+++ linux/init/main.c	Sun Feb 12 15:00:45 1995
@@ -105,6 +105,9 @@
 #ifdef CONFIG_CDU31A
 extern void cdu31a_setup(char *str, int *ints);
 #endif CONFIG_CDU31A
+#ifdef CONFIG_CDU535
+extern void sonycd535_setup(char *str, int *ints);
+#endif CONFIG_CDU535
 void ramdisk_setup(char *str, int *ints);
 
 #ifdef CONFIG_SYSVIPC
@@ -222,6 +225,9 @@
 #ifdef CONFIG_AZTCD
 	{ "aztcd=", aztcd_setup },
 #endif
+#ifdef CONFIG_CDU535
+	{ "sonycd535=", sonycd535_setup },
+#endif CONFIG_CDU535
 #ifdef CONFIG_SOUND
 	{ "sound=", sound_setup },
 #endif
diff -u --recursive --new-file v1.1.90/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.1.90/linux/kernel/ksyms.c	Thu Feb  9 10:18:53 1995
+++ linux/kernel/ksyms.c	Thu Feb  9 10:57:08 1995
@@ -67,6 +67,7 @@
 extern int request_dma(unsigned int dmanr, char * deviceID);
 extern void free_dma(unsigned int dmanr);
 
+extern int close_fp(struct file *filp, unsigned int fd);
 extern void (* iABI_hook)(struct pt_regs * regs);
 
 struct symbol_table symbol_table = {
@@ -128,6 +129,7 @@
 	X(namei),
 	X(lnamei),
 	X(open_namei),
+	X(close_fp),
 	X(check_disk_change),
 	X(invalidate_buffers),
 	X(fsync_dev),
diff -u --recursive --new-file v1.1.90/linux/mm/memory.c linux/mm/memory.c
--- v1.1.90/linux/mm/memory.c	Thu Feb  9 10:18:53 1995
+++ linux/mm/memory.c	Thu Feb  9 18:22:18 1995
@@ -1,3 +1,4 @@
+#define THREE_LEVEL
 /*
  *  linux/mm/memory.c
  *
@@ -62,9 +63,9 @@
 
 #define copy_page(from,to) memcpy((void *) to, (void *) from, PAGE_SIZE)
 
-mem_map_t * mem_map = NULL;
+#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE)
 
-#define CODE_SPACE(addr,p) ((addr) < (p)->end_code)
+mem_map_t * mem_map = NULL;
 
 /*
  * oom() prints a message (so that the user knows why the process died),
@@ -93,29 +94,53 @@
 	return;
 }
 
-static void free_one_table(pgd_t * page_dir)
+static inline void free_one_pmd(pmd_t * dir)
 {
 	int j;
-	pgd_t pg_table = *page_dir;
-	pte_t * page_table;
-	unsigned long page;
+	pte_t * pte;
 
-	if (pgd_none(pg_table))
+	if (pmd_none(*dir))
 		return;
-	pgd_clear(page_dir);
-	if (pgd_bad(pg_table)) {
-		printk("Bad page table: [%p]=%08lx\n",page_dir,pgd_val(pg_table));
+	if (pmd_bad(*dir)) {
+		printk("free_one_pmd: bad directory entry %08lx\n", pmd_val(*dir));
+		pmd_clear(dir);
 		return;
 	}
-	page = pgd_page(pg_table);
-	if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)
+	pte = pte_offset(dir, 0);
+	pmd_clear(dir);
+	if (pte_inuse(pte)) {
+		pte_free(pte);
 		return;
-	page_table = (pte_t *) page;
-	for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++)
-		free_one_pte(page_table);
-	free_page(page);
+	}
+	for (j = 0; j < PTRS_PER_PTE ; j++)
+		free_one_pte(pte+j);
+	pte_free(pte);
 }
 
+static inline void free_one_pgd(pgd_t * dir)
+{
+	int j;
+	pmd_t * pmd;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		printk("free_one_pgd: bad directory entry %08lx\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, 0);
+	pgd_clear(dir);
+	if (pmd_inuse(pmd)) {
+		pmd_free(pmd);
+		return;
+	}
+	for (j = 0; j < PTRS_PER_PMD ; j++)
+		free_one_pmd(pmd+j);
+	pmd_free(pmd);
+}
+	
+
 /*
  * This function clears all user-level page tables of a process - this
  * is needed by execve(), so that old pages aren't in the way. Note that
@@ -132,26 +157,26 @@
 		return;
 	if (tsk == task[0])
 		panic("task[0] (swapper) doesn't support exec()\n");
-	page_dir = PAGE_DIR_OFFSET(tsk, 0);
+	page_dir = pgd_offset(tsk, 0);
 	if (!page_dir || page_dir == swapper_pg_dir) {
 		printk("Trying to clear kernel page-directory: not good\n");
 		return;
 	}
-	if (mem_map[MAP_NR((unsigned long) page_dir)] > 1) {
+	if (pgd_inuse(page_dir)) {
 		pgd_t * new_pg;
 
-		if (!(new_pg = (pgd_t *) get_free_page(GFP_KERNEL))) {
+		if (!(new_pg = pgd_alloc())) {
 			oom(tsk);
 			return;
 		}
-		for (i = 768 ; i < 1024 ; i++)
+		for (i = USER_PTRS_PER_PGD ; i < PTRS_PER_PGD ; i++)
 			new_pg[i] = page_dir[i];
-		free_page((unsigned long) page_dir);
 		SET_PAGE_DIR(tsk, new_pg);
+		pgd_free(page_dir);
 		return;
 	}
-	for (i = 0 ; i < 768 ; i++,page_dir++)
-		free_one_table(page_dir);
+	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
+		free_one_pgd(page_dir + i);
 	invalidate();
 	return;
 }
@@ -170,19 +195,19 @@
 		printk("task[0] (swapper) killed: unable to recover\n");
 		panic("Trying to free up swapper memory space");
 	}
-	page_dir = PAGE_DIR_OFFSET(tsk, 0);
+	page_dir = pgd_offset(tsk, 0);
 	if (!page_dir || page_dir == swapper_pg_dir) {
 		printk("Trying to free kernel page-directory: not good\n");
 		return;
 	}
 	SET_PAGE_DIR(tsk, swapper_pg_dir);
-	if (mem_map[MAP_NR((unsigned long) page_dir)] > 1) {
-		free_page((unsigned long) page_dir);
+	if (pgd_inuse(page_dir)) {
+		pgd_free(page_dir);
 		return;
 	}
-	for (i = 0 ; i < PTRS_PER_PAGE ; i++)
-		free_one_table(page_dir + i);
-	free_page((unsigned long) page_dir);
+	for (i = 0 ; i < PTRS_PER_PGD ; i++)
+		free_one_pgd(page_dir + i);
+	pgd_free(page_dir);
 	invalidate();
 }
 
@@ -196,12 +221,94 @@
 {
 	pgd_t * pg_dir;
 
-	pg_dir = PAGE_DIR_OFFSET(current, 0);
-	mem_map[MAP_NR((unsigned long) pg_dir)]++;
+	pg_dir = pgd_offset(current, 0);
+	mem_map[MAP_NR(pg_dir)]++;
 	SET_PAGE_DIR(tsk, pg_dir);
 	return 0;
 }
 
+static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte)
+{
+	pte_t pte = *old_pte;
+
+	if (pte_none(pte))
+		return;
+	if (!pte_present(pte)) {
+		swap_duplicate(pte_val(pte));
+		*new_pte = pte;
+		return;
+	}
+	if (pte_page(pte) > high_memory || (mem_map[MAP_NR(pte_page(pte))] & MAP_PAGE_RESERVED)) {
+		*new_pte = pte;
+		return;
+	}
+	if (pte_cow(pte))
+		pte = pte_wrprotect(pte);
+	if (delete_from_swap_cache(pte_page(pte)))
+		pte = pte_mkdirty(pte);
+	*new_pte = pte;
+	*old_pte = pte;
+	mem_map[MAP_NR(pte_page(pte))]++;
+}
+
+static inline int copy_one_pmd(pmd_t * old_pmd, pmd_t * new_pmd)
+{
+	int j;
+	pte_t *old_pte, *new_pte;
+
+	if (pmd_none(*old_pmd))
+		return 0;
+	if (pmd_bad(*old_pmd)) {
+		printk("copy_one_pmd: bad page table: probable memory corruption\n");
+		pmd_clear(old_pmd);
+		return 0;
+	}
+	old_pte = pte_offset(old_pmd, 0);
+	if (pte_inuse(old_pte)) {
+		*new_pmd = *old_pmd;
+		return 0;
+	}
+	new_pte = pte_alloc(new_pmd, 0);
+	if (!new_pte)
+		return -ENOMEM;
+	for (j = 0 ; j < PTRS_PER_PTE ; j++) {
+		copy_one_pte(old_pte, new_pte);
+		old_pte++;
+		new_pte++;
+	}
+	return 0;
+}
+
+static inline int copy_one_pgd(pgd_t * old_pgd, pgd_t * new_pgd)
+{
+	int j;
+	pmd_t *old_pmd, *new_pmd;
+
+	if (pgd_none(*old_pgd))
+		return 0;
+	if (pgd_bad(*old_pgd)) {
+		printk("copy_one_pgd: bad page table: probable memory corruption\n");
+		pgd_clear(old_pgd);
+		return 0;
+	}
+	old_pmd = pmd_offset(old_pgd, 0);
+	if (pmd_inuse(old_pmd)) {
+		*new_pgd = *old_pgd;
+		return 0;
+	}
+	new_pmd = pmd_alloc(new_pgd, 0);
+	if (!new_pmd)
+		return -ENOMEM;
+	for (j = 0 ; j < PTRS_PER_PMD ; j++) {
+		int error = copy_one_pmd(old_pmd, new_pmd);
+		if (error)
+			return error;
+		old_pmd++;
+		new_pmd++;
+	}
+	return 0;
+}
+
 /*
  * copy_page_tables() just copies the whole process memory range:
  * note the special handling of RESERVED (ie kernel) pages, which
@@ -210,59 +317,23 @@
 int copy_page_tables(struct task_struct * tsk)
 {
 	int i;
-	pgd_t *old_page_dir;
-	pgd_t *new_page_dir;
+	pgd_t *old_pgd;
+	pgd_t *new_pgd;
 
-	new_page_dir = (pgd_t *) get_free_page(GFP_KERNEL);
-	if (!new_page_dir)
+	new_pgd = pgd_alloc();
+	if (!new_pgd)
 		return -ENOMEM;
-	old_page_dir = PAGE_DIR_OFFSET(current, 0);
-	SET_PAGE_DIR(tsk, new_page_dir);
-	for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) {
-		int j;
-		pgd_t old_pg_table;
-		pte_t *old_page_table, *new_page_table;
-
-		old_pg_table = *old_page_dir;
-		if (pgd_none(old_pg_table))
-			continue;
-		if (pgd_bad(old_pg_table)) {
-			printk("copy_page_tables: bad page table: "
-				"probable memory corruption\n");
-			pgd_clear(old_page_dir);
-			continue;
-		}
-		if (mem_map[MAP_NR(pgd_page(old_pg_table))] & MAP_PAGE_RESERVED) {
-			*new_page_dir = old_pg_table;
-			continue;
-		}
-		if (!(new_page_table = (pte_t *) get_free_page(GFP_KERNEL))) {
+	old_pgd = pgd_offset(current, 0);
+	SET_PAGE_DIR(tsk, new_pgd);
+	for (i = 0 ; i < PTRS_PER_PGD ; i++) {
+		int errno = copy_one_pgd(old_pgd, new_pgd);
+		if (errno) {
 			free_page_tables(tsk);
-			return -ENOMEM;
-		}
-		old_page_table = (pte_t *) pgd_page(old_pg_table);
-		pgd_set(new_page_dir, new_page_table);
-		for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) {
-			pte_t pte = *old_page_table;
-			if (pte_none(pte))
-				continue;
-			if (!pte_present(pte)) {
-				swap_duplicate(pte_val(pte));
-				*new_page_table = pte;
-				continue;
-			}
-			if (pte_page(pte) > high_memory || (mem_map[MAP_NR(pte_page(pte))] & MAP_PAGE_RESERVED)) {
-				*new_page_table = pte;
-				continue;
-			}
-			if (pte_cow(pte))
-				pte = pte_wrprotect(pte);
-			if (delete_from_swap_cache(pte_page(pte)))
-				pte = pte_mkdirty(pte);
-			*new_page_table = pte;
-			*old_page_table = pte;
-			mem_map[MAP_NR(pte_page(pte))]++;
+			invalidate();
+			return errno;
 		}
+		old_pgd++;
+		new_pgd++;
 	}
 	invalidate();
 	return 0;
@@ -418,67 +489,67 @@
  * mappings are removed. any references to nonexistent pages results
  * in null mappings (currently treated as "copy-on-access")
  */
-int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot)
+static inline void remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t oldpage = *pte;
+		*pte = mk_pte(offset, prot);
+		forget_pte(oldpage);
+		address += PAGE_SIZE;
+		offset += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline int remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot)
 {
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	offset -= address;
+	do {
+		pte_t * pte = pte_alloc(pmd, address);
+		if (!pte)
+			return -ENOMEM;
+		remap_pte_range(pte, address, end - address, address + offset, prot);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+int remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot)
+{
+	int error = 0;
 	pgd_t * dir;
-	pte_t * page_table;
-	unsigned long poff, pcnt;
+	unsigned long end = from + size;
 
-	if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) {
-		printk("remap_page_range: from = %08lx, to=%08lx\n",from,to);
-		return -EINVAL;
-	}
-	dir = PAGE_DIR_OFFSET(current,from);
-	size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
-	poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
-	if ((pcnt = PTRS_PER_PAGE - poff) > size)
-		pcnt = size;
-
-	while (size > 0) {
-		if (!pgd_present(*dir)) {
-			if (!(page_table = (pte_t *) get_free_page(GFP_KERNEL))) {
-				invalidate();
-				return -1;
-			}
-			if (pgd_present(*dir)) {
-				free_page((unsigned long) page_table);
-				page_table = (pte_t *) pgd_page(*dir);
-			} else
-				pgd_set(dir, page_table);
-		} else
-			page_table = (pte_t *) pgd_page(*dir);
+	offset -= from;
+	dir = pgd_offset(current, from);
+	while (from < end) {
+		pmd_t *pmd = pmd_alloc(dir, from);
+		error = -ENOMEM;
+		if (!pmd)
+			break;
+		error = remap_pmd_range(pmd, from, end - from, offset + from, prot);
+		if (error)
+			break;
+		from = (from + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
-		page_table += poff;
-		poff = 0;
-
-		for (size -= pcnt; pcnt-- ;) {
-			pte_t page = *page_table;
-			if (!pte_none(page)) {
-				pte_clear(page_table);
-				if (pte_present(page)) {
-					if (!(mem_map[MAP_NR(pte_page(page))] & MAP_PAGE_RESERVED))
-						if (current->mm->rss > 0)
-							--current->mm->rss;
-					free_page(pte_page(page));
-				} else
-					swap_free(pte_val(page));
-			}
-			if (to >= high_memory)
-				*page_table = mk_pte(to, prot);
-			else if (mem_map[MAP_NR(to)]) {
-				*page_table = mk_pte(to, prot);
-				if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) {
-					++current->mm->rss;
-					mem_map[MAP_NR(to)]++;
-				}
-			}
-			page_table++;
-			to += PAGE_SIZE;
-		}
-		pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size);
 	}
 	invalidate();
-	return 0;
+	return error;
 }
 
 /*
@@ -501,33 +572,27 @@
  */
 unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address)
 {
-	pgd_t * page_dir;
-	pte_t * page_table;
+	pgd_t * pgd;
+	pmd_t * pmd;
+	pte_t * pte;
 
 	if (page >= high_memory)
 		printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address);
 	if (mem_map[MAP_NR(page)] != 1)
 		printk("mem_map disagrees with %08lx at %08lx\n",page,address);
-	page_dir = PAGE_DIR_OFFSET(tsk,address);
-	if (pgd_present(*page_dir)) {
-		page_table = (pte_t *) pgd_page(*page_dir);
-	} else {
-		if (!(page_table = (pte_t *) get_free_page(GFP_KERNEL)))
-			return 0;
-		if (pgd_present(*page_dir)) {
-			free_page((unsigned long) page_table);
-			page_table = (pte_t *) pgd_page(*page_dir);
-		} else {
-			pgd_set(page_dir, page_table);
-		}
-	}
-	page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
-	if (!pte_none(*page_table)) {
+	pgd = pgd_offset(tsk,address);
+	pmd = pmd_alloc(pgd, address);
+	if (!pmd)
+		return 0;
+	pte = pte_alloc(pmd, address);
+	if (!pte)
+		return 0;
+	if (!pte_none(*pte)) {
 		printk("put_dirty_page: page already exists\n");
-		pte_clear(page_table);
+		pte_clear(pte);
 		invalidate();
 	}
-	*page_table = pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY)));
+	*pte = pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY)));
 /* no need for invalidate */
 	return page;
 }
@@ -553,17 +618,22 @@
 	int write_access)
 {
 	pgd_t *page_dir;
+	pmd_t *page_middle;
 	pte_t *page_table, pte;
 	unsigned long old_page, new_page;
 
 	new_page = __get_free_page(GFP_KERNEL);
-	page_dir = PAGE_DIR_OFFSET(vma->vm_task,address);
+	page_dir = pgd_offset(vma->vm_task,address);
 	if (pgd_none(*page_dir))
 		goto end_wp_page;
 	if (pgd_bad(*page_dir))
-		goto bad_wp_pagetable;
-	page_table = (pte_t *) pgd_page(*page_dir);
-	page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
+		goto bad_wp_pagedir;
+	page_middle = pmd_offset(page_dir, address);
+	if (pmd_none(*page_middle))
+		goto end_wp_page;
+	if (pmd_bad(*page_middle))
+		goto bad_wp_pagemiddle;
+	page_table = pte_offset(page_middle, address);
 	pte = *page_table;
 	if (!pte_present(pte))
 		goto end_wp_page;
@@ -599,12 +669,14 @@
 	return;
 bad_wp_page:
 	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
-	*page_table = BAD_PAGE;
 	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, pgd_val(*page_dir));
-	pgd_set(page_dir, BAD_PAGETABLE);
+bad_wp_pagemiddle:
+	printk("do_wp_page: bogus page-middle at address %08lx (%08lx)\n", address, pmd_val(*page_middle));
+	send_sig(SIGKILL, vma->vm_task, 1);
+	goto end_wp_page;
+bad_wp_pagedir:
+	printk("do_wp_page: bogus page-dir entry at address %08lx (%08lx)\n", address, pgd_val(*page_dir));
 	send_sig(SIGKILL, vma->vm_task, 1);
 end_wp_page:
 	if (new_page)
@@ -719,14 +791,27 @@
 	unsigned long newpage)
 {
 	pgd_t * from_dir, * to_dir;
+	pmd_t * from_middle, * to_middle;
 	pte_t * from_table, * to_table;
 	pte_t from, to;
 
-	from_dir = PAGE_DIR_OFFSET(from_area->vm_task,from_address);
+	from_dir = pgd_offset(from_area->vm_task,from_address);
 /* is there a page-directory at from? */
-	if (!pgd_present(*from_dir))
+	if (pgd_none(*from_dir))
+		return 0;
+	if (pgd_bad(*from_dir)) {
+		printk("try_to_share: bad page directory %08lx\n", pgd_val(*from_dir));
 		return 0;
-	from_table = (pte_t *) (pgd_page(*from_dir) + PAGE_PTR(from_address));
+	}
+	from_middle = pmd_offset(from_dir, from_address);
+/* is there a mid-directory at from? */
+	if (pmd_none(*from_middle))
+		return 0;
+	if (pmd_bad(*from_middle)) {
+		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*from_middle));
+		return 0;
+	}
+	from_table = pte_offset(from_middle, from_address);
 	from = *from_table;
 /* is the page present? */
 	if (!pte_present(from))
@@ -746,10 +831,23 @@
 	if (mem_map[MAP_NR(pte_page(from))] & MAP_PAGE_RESERVED)
 		return 0;
 /* is the destination ok? */
-	to_dir = PAGE_DIR_OFFSET(to_area->vm_task,to_address);
-	if (!pgd_present(*to_dir))
+	to_dir = pgd_offset(to_area->vm_task,to_address);
+/* is there a page-directory at to? */
+	if (pgd_none(*to_dir))
 		return 0;
-	to_table = (pte_t *) (pgd_page(*to_dir) + PAGE_PTR(to_address));
+	if (pgd_bad(*to_dir)) {
+		printk("try_to_share: bad page directory %08lx\n", pgd_val(*to_dir));
+		return 0;
+	}
+	to_middle = pmd_offset(to_dir, to_address);
+/* is there a mid-directory at to? */
+	if (pmd_none(*to_middle))
+		return 0;
+	if (pmd_bad(*to_middle)) {
+		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*to_middle));
+		return 0;
+	}
+	to_table = pte_offset(to_middle, to_address);
 	to = *to_table;
 	if (!pte_none(to))
 		return 0;
@@ -855,32 +953,16 @@
  */
 static inline pte_t * get_empty_pgtable(struct task_struct * tsk,unsigned long address)
 {
-	pgd_t *p;
-	unsigned long page;
-
-	p = PAGE_DIR_OFFSET(tsk,address);
-	if (pgd_present(*p))
-		return (pte_t *) (PAGE_PTR(address) + pgd_page(*p));
-	if (!pgd_none(*p)) {
-		printk("get_empty_pgtable: bad page-directory entry \n");
-		pgd_clear(p);
-	}
-	page = get_free_page(GFP_KERNEL);
-	if (pgd_present(*p)) {
-		free_page(page);
-		return (pte_t *) (PAGE_PTR(address) + pgd_page(*p));
-	}
-	if (!pgd_none(*p)) {
-		printk("get_empty_pgtable: bad page-directory entry \n");
-		pgd_clear(p);
-	}
-	if (page) {
-		pgd_set(p, (pte_t *) page);
-		return (pte_t *) (PAGE_PTR(address) + page);
-	}
-	oom(current);
-	pgd_set(p, BAD_PAGETABLE);
-	return NULL;
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgd_offset(tsk, address);
+	pmd = pmd_alloc(pgd, address);
+	if (!pmd)
+		return NULL;
+	pte = pte_alloc(pmd, address);
+	return pte;
 }
 
 static inline void do_swap_page(struct vm_area_struct * vma, unsigned long address,
@@ -929,7 +1011,6 @@
 		return;
 	}
 	address &= PAGE_MASK;
-
 	if (!vma->vm_ops || !vma->vm_ops->nopage) {
 		++vma->vm_task->mm->rss;
 		++vma->vm_task->mm->min_flt;
diff -u --recursive --new-file v1.1.90/linux/mm/mprotect.c linux/mm/mprotect.c
--- v1.1.90/linux/mm/mprotect.c	Wed Feb  1 09:23:08 1995
+++ linux/mm/mprotect.c	Sun Feb 12 17:26:16 1995
@@ -53,6 +53,7 @@
 				*page_table = pte_modify(entry, newprot);
 			++page_table;
 		} while (--offset);
+		dir++;
 	}
 	return;
 }
diff -u --recursive --new-file v1.1.90/linux/mm/vmalloc.c linux/mm/vmalloc.c
--- v1.1.90/linux/mm/vmalloc.c	Wed Feb  1 09:23:08 1995
+++ linux/mm/vmalloc.c	Thu Feb  9 18:22:18 1995
@@ -1,3 +1,4 @@
+#define THREE_LEVEL
 /*
  *  linux/mm/vmalloc.c
  *
@@ -32,7 +33,7 @@
 	struct task_struct * p;
 
 	for_each_task(p)
-		*PAGE_DIR_OFFSET(p,address) = entry;
+		*pgd_offset(p,address) = entry;
 }
 
 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
@@ -152,7 +153,7 @@
 	pgd_t * dir;
 	unsigned long end = address + size;
 
-	dir = PAGE_DIR_OFFSET(&init_task, address);
+	dir = pgd_offset(&init_task, address);
 	while (address < end) {
 		pmd_t *pmd = pmd_alloc_kernel(dir, address);
 		if (!pmd)
diff -u --recursive --new-file v1.1.90/linux/net/inet/af_inet.c linux/net/inet/af_inet.c
--- v1.1.90/linux/net/inet/af_inet.c	Wed Jan 11 21:14:29 1995
+++ linux/net/inet/af_inet.c	Fri Feb 10 13:06:24 1995
@@ -26,6 +26,8 @@
  *					this fixed and the accept bug fixed 
  *					some RPC stuff seems happier.
  *		Niibe Yutaka	:	4.4BSD style write async I/O
+ *		Alan Cox, 
+ *		Tony Gale 	:	Fixed reuse semantics.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -854,7 +856,7 @@
 			destroy_sock(sk2);
 			goto outside_loop;
 		}
-		if (!sk->reuse) 
+		if (!sk->reuse || sk2->state==TCP_LISTEN) 
 		{
 			sti();
 			return(-EADDRINUSE);
diff -u --recursive --new-file v1.1.90/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.90/linux/net/inet/arp.c	Mon Jan 30 06:41:59 1995
+++ linux/net/inet/arp.c	Sun Feb 12 21:15:05 1995
@@ -680,7 +680,7 @@
 			     having to use a huge number of proxy arp entries
 			     and having to keep them uptodate.
 			     */
-			  if (proxy_entry->htype == htype && 
+			  if (proxy_entry->dev != dev &&
 			      !((proxy_entry->ip^tip)&proxy_entry->mask))
 			    break;
 
diff -u --recursive --new-file v1.1.90/linux/net/inet/igmp.c linux/net/inet/igmp.c
--- v1.1.90/linux/net/inet/igmp.c	Mon Jan 30 08:53:06 1995
+++ linux/net/inet/igmp.c	Sun Feb 12 21:11:01 1995
@@ -179,7 +179,7 @@
 	del_timer(&im->timer);
 	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 	ip_mc_filter_del(im->interface, im->multiaddr);
-	printk("Left group %lX\n",im->multiaddr);
+/*	printk("Left group %lX\n",im->multiaddr);*/
 }
 
 static void igmp_group_added(struct ip_mc_list *im)
@@ -187,7 +187,7 @@
 	igmp_init_timer(im);
 	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 	ip_mc_filter_add(im->interface, im->multiaddr);
-	printk("Joined group %lX\n",im->multiaddr);
+/*	printk("Joined group %lX\n",im->multiaddr);*/
 }
 
 int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
diff -u --recursive --new-file v1.1.90/linux/net/inet/ip_fw.c linux/net/inet/ip_fw.c
--- v1.1.90/linux/net/inet/ip_fw.c	Thu Feb  9 10:18:54 1995
+++ linux/net/inet/ip_fw.c	Sun Feb 12 21:11:02 1995
@@ -1005,38 +1005,23 @@
 
 #ifdef CONFIG_IP_ACCT
 
-int ip_acct_procinfo(char *buffer, char **start, off_t offset, int length)
+int ip_acct_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,0);
-}
-
-int ip_acct0_procinfo(char *buffer, char **start, off_t offset, int length)
-{
-	return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,1);
+	return ip_chain_procinfo(IP_INFO_ACCT,buffer,start,offset,length,reset);
 }
 
 #endif
 
 #ifdef CONFIG_IP_FIREWALL
 
-int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset, int length)
-{
-	return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,0);
-}
-
-int ip_fw_blk0_procinfo(char *buffer, char **start, off_t offset, int length)
-{
-	return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,1);
-}
-
-int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, int length)
+int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,0);
+	return ip_chain_procinfo(IP_INFO_BLK,buffer,start,offset,length,reset);
 }
 
-int ip_fw_fwd0_procinfo(char *buffer, char **start, off_t offset, int length)
+int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,1);
+	return ip_chain_procinfo(IP_INFO_FWD,buffer,start,offset,length,reset);
 }
 
 #endif
diff -u --recursive --new-file v1.1.90/linux/net/inet/ipx.c linux/net/inet/ipx.c
--- v1.1.90/linux/net/inet/ipx.c	Wed Feb  1 09:23:08 1995
+++ linux/net/inet/ipx.c	Thu Feb  9 18:29:53 1995
@@ -544,7 +544,12 @@
 	ipx_interface	*i;
 
 	/* See if we should update our network number */
-	if ((intrfc->if_netnum == 0L) && (ipx->ipx_tctrl == 0)) {
+	if ((intrfc->if_netnum == 0L) && 
+		(ipx->ipx_source.net == ipx->ipx_dest.net)) {
+		/* NB: NetWare servers lie about their hop count so we
+		 * dropped the test based on it.  This is the best way
+		 * to determine this is a 0 hop count packet.
+		 */
 		if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL) {
 			intrfc->if_netnum = ipx->ipx_source.net;
 			(void) ipxitf_add_local_route(intrfc);
@@ -558,8 +563,15 @@
 		}
 	}
 
-	if (intrfc->if_netnum != ipx->ipx_dest.net)
-		return ipxrtr_route_skb(skb);
+	if (intrfc->if_netnum != ipx->ipx_dest.net) {
+		/* We only route point-to-point packets. */
+		if ((skb->pkt_type != PACKET_BROADCAST) &&
+			(skb->pkt_type != PACKET_MULTICAST))
+			return ipxrtr_route_skb(skb);
+		
+		kfree_skb(skb,FREE_READ);
+		return 0;
+	}
 
 	/* see if we should keep it */
 	if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0) 
diff -u --recursive --new-file v1.1.90/linux/net/inet/route.c linux/net/inet/route.c
--- v1.1.90/linux/net/inet/route.c	Thu Jan 26 07:49:17 1995
+++ linux/net/inet/route.c	Sun Feb 12 21:11:01 1995
@@ -301,7 +301,8 @@
 	rp = &rt_base;
 	while ((r = *rp) != NULL) 
 	{
-		if (r->rt_dst != dst) 
+		if (r->rt_dst != dst || 
+		    r->rt_mask != mask) 
 		{
 			rp = &r->rt_next;
 			continue;
