diff -u --recursive --new-file v1.3.84/linux/CREDITS linux/CREDITS
--- v1.3.84/linux/CREDITS	Wed Apr  3 16:06:54 1996
+++ linux/CREDITS	Mon Apr  8 11:10:36 1996
@@ -961,7 +961,7 @@
 S: Germany
 
 N: Stephen Rothwell
-E: Stephen.Rothwell@nec.com.au
+E: Stephen.Rothwell@canb.auug.org.au
 D: Boot/setup/build work for setup > 2K
 D: Author, APM driver
 S: 59 Bugden Avenue
diff -u --recursive --new-file v1.3.84/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.84/linux/Documentation/Configure.help	Wed Apr  3 16:06:54 1996
+++ linux/Documentation/Configure.help	Mon Apr  8 11:46:08 1996
@@ -1236,16 +1236,14 @@
 Future Domain 16xx SCSI support
 CONFIG_SCSI_FUTURE_DOMAIN
   This is support for Future Domain's 16-bit SCSI host adaptors
-  (TMC-1660/1680, TMC-1650/1670, TMC-3260) and other adaptors based on
-  the Future Domain chipsets (Quantum ISA-200S, ISA-250MG; and at
-  least one IBM board).  It is explained in section 3.7 of the
-  SCSI-HOWTO, available via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
-  the box, you may have to change some settings in
-  drivers/scsi/fdomain.h. This driver is also available as a module (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want). If you want to compile it as a module, say M
-  here and read Documentation/modules.txt.
+  (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and other
+  adaptors based on the Future Domain chipsets (Quantum ISA-200S,
+  ISA-250MG; Adaptec AHA-2920; and at least one IBM board).  It is
+  explained in section 3.7 of the SCSI-HOWTO, available via ftp (user:
+  anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is
+  also available as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want). If you want to
+  compile it as a module, say M here and read Documentation/modules.txt.
 
 Generic NCR5380/53c400 SCSI support
 CONFIG_SCSI_GENERIC_NCR5380
@@ -2878,14 +2876,21 @@
 
 Advanced Power Management
 CONFIG_APM
-  APM is a BIOS standard for saving power using several different
-  techniques. This is mostly useful for battery powered laptops with
-  APM compliant BIOSes.  Specifically, the time will be reset after a
-  USER RESUME operation, the /proc/apm device will provide battery
-  status information, and ioctls are provided to put the machine in
-  STANDBY or SUSPEND mode.  This driver does not work for the TI 4000M
-  TravelMate and the ACER 486/DX4/75 because they don't follow the
-  standard. Say Y if you have a different laptop.
+  APM is a BIOS specification for saving power using several different
+  techniques.  This is mostly useful for battery powered laptops with APM
+  compliant BIOSes.  Specifically, the time will be reset after a USER
+  RESUME operation, the /proc/apm device will provide battery status
+  information, and user-space programs will receive notification of APM
+  "events" (e.g., battery status change).  This driver does not spin down
+  disk drives (see hdparm(8) for that); and it doesn't turn off
+  VESA-compliant "green" monitors.  This driver does not support the TI
+  4000M TravelMate and the ACER 486/DX4/75 because they don't have
+  compliant BIOSes.  Many "green" desktop machines also don't have
+  compliant BIOSes, and this driver will cause those machines to panic
+  during the boot phase (typically, these machines are using a data
+  segment of 0040, which is reserved for the Linux kernel).  If you don't
+  have a battery in your machine, there isn't much point in using this
+  driver.
 
 Ignore USER SUSPEND
 CONFIG_APM_IGNORE_USER_SUSPEND
@@ -2897,16 +2902,37 @@
 CONFIG_APM_DO_ENABLE
   Enable APM features at boot time.  From page 36 of the APM BIOS
   specification: "When disabled, the APM BIOS does not automatically
-  power manage devices, enter the Standby State, enter the Suspend
-  State, or take power saving steps in response to CPU Idle calls."
-  This driver will make CPU Idle calls when Linux is idle (unless this
-  feature is turned off -- see below).  This should always save
-  battery power, but more complicated APM features will be dependent
-  on your BIOS implementation.  You may need to turn this option off
-  if your computer hangs at boot time when using APM support, or if it
-  beeps continuously instead of suspending.  Turn this off if you have
-  a NEC UltraLite Versa 33/C or a Toshiba T400CDT.  This is off by
-  default since most machines do fine without this feature.
+  power manage devices, enter the Standby State, enter the Suspend State,
+  or take power saving steps in response to CPU Idle calls."  This driver
+  will make CPU Idle calls when Linux is idle (unless this feature is
+  turned off -- see below).  This should always save battery power, but
+  more complicated APM features will be dependent on your BIOS
+  implementation.  You may need to turn this option off if your computer
+  hangs at boot time when using APM support, or if it beeps continuously
+  instead of suspending.  Turn this off if you have a NEC UltraLite Versa
+  33/C or a Toshiba T400CDT.  This is off by default since most machines
+  do fine without this feature.
+
+Do CPU IDLE calls
+CONFIG_APM_CPU_IDLE
+  Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop.
+  On some machines, this can activate improved power savings, such as a
+  slowed CPU clock rate, when the machine is idle.  These idle calls are
+  made after the idle loop has run for some length of time (e.g., 333
+  mS).  On some machines, this will cause a hang at boot time or whenever
+  the CPU becomes idle.  (On machines with more than one CPU, this option
+  does nothing.)
+
+Black display
+CONFIG_APM_DISPLAY_BLANK
+  Enable console blanking using the APM.  Some laptops can use this to
+  turn off the LCD backlight when the VC screen blanker blanks the
+  screen.  Note that this is only used by the VC screen blanker, and
+  won't turn off the backlight when using X11 (this also doesn't have
+  anything to do with your VESA-compliant power-saving monitor).
+  Further, this option doesn't work for all laptops -- it might not turn
+  off your backlight at all, or it might print a lot of errors to the
+  console.
 
 Watchdog Timer Support 
 CONFIG_WATCHDOG
@@ -2959,23 +2985,6 @@
   A software monitoring watchdog. This will fail to reboot your system
   from some situations that the hardware watchdog will recover
   from. Equally it's a lot cheaper to install.
-
-Do CPU IDLE calls
-CONFIG_APM_CPU_IDLE
-  Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop.
-  On some machines, this can activate improved power savings, such as
-  a slowed CPU clock rate, when the machine is idle.  These idle call
-  is made after the idle loop has run for some length of time (e.g.,
-  333 mS).  On some machines, this will cause a hang at boot time or
-  whenever the CPU becomes idle.  (On machines with more than one CPU,
-  this option does nothing.)
-
-Black display
-CONFIG_APM_DISPLAY_BLANK
-  Enable console blanking using the APM.  Some laptops can use this to
-  turn off the LCD backlight when the VC screen blanker blanks the
-  screen.  Note that this is only used by the VC screen blanker, and
-  won't turn off the backlight when using X11.
 
 Sound card support
 CONFIG_SOUND
diff -u --recursive --new-file v1.3.84/linux/Documentation/digiboard.txt linux/Documentation/digiboard.txt
--- v1.3.84/linux/Documentation/digiboard.txt	Fri Apr  5 13:35:27 1996
+++ linux/Documentation/digiboard.txt	Sun Apr  7 09:42:33 1996
@@ -7,12 +7,18 @@
 
 Limitations:
 ------------
-Currently the Driver does not do autoprobing. You have to configure
-the driver with the correct I/O address in drivers/char/pcxxconfig.h.
+Currently the Driver does not do autoprobing!
+
+The preconfigured I/O address is 0200h and the default memory address
+0D0000h, ALT-PIN feature on and 16 ports available.
 
-The preconfigured I/O address is 0200h and the default memory address 0D0000h.
 Use them and you will not have to worry about configuring anything.
 
+You can configure the driver via lilo. HAve  look at the end of this
+message. The default settings vanish as soon as you give a digi= commandline.
+You can give multiple digi= commandline parameters to define multiple
+boards.
+
 Supporting Tools:
 -----------------
 Some tools and more detailed up to date information can be found at
@@ -24,18 +30,23 @@
 Currently the Linux MAKEDEV command does not support generating the Digiboard
 Devices. Use the following script to generate the devices:
 
+WARNING: Most of the stuff available right now uses the WRONG Major Device
+numbers and is severely outdated. Be careful and check them first.
+Better not use any of the software in that directory if you run a recent
+1.3.X Kernel!
+
 ------------------ mkdigidev begin
 #!/bin/sh
 #
 # Script to create Digiboard Devices
-# Christoph Lameter, April 4, 1996
+# Christoph Lameter, April 6, 1996
 #
 # Usage:
 # mkdigidev [<number of devices>]
 # 
 
-DIGIMAJOR=30
-DIGICUMAJOR=31
+DIGI_MAJOR=22
+DIGICU_MAJOR=23
 
 BOARDS=$1
 
@@ -69,4 +80,36 @@
 (Write e-mail to that address to subscribe. Common ListServ commands work.
 Archive of messages available)
 
-Christoph Lameter (clameter@fuller.edu) 4. April 1996.
+Christoph Lameter (clameter@fuller.edu) 6. April 1996.
+
+-----------------------------------------------------------------------------
+
+Changes v1.5.5:
+
+The ability to use the kernel's command line to pass in the configuration for 
+boards.  Using LILO's APPEND command, a string of comma seperated identifiers 
+or integers can be used.  The 6 values in order are:
+
+   Enable/Disable this card,
+   Type of card: PC/Xi(0), PC/Xe(1), PC/Xeve(2), PC/Xem(3)
+   Enable/Disable alternate pin arrangement,
+   Number of ports on this card,
+   I/O Port where card is configured (in HEX if using string identifiers),
+   Base of memory window (in HEX if using string identifiers), 
+
+Samples:
+   append="digi=E,PC/Xi,D,16,200,D0000"
+   append="digi=1,0,0,16,512,(whatever D0000 is in base 10 :)
+
+Driver's minor device numbers are conserved. This means that instead of
+each board getting a block of 16 minors pre-assigned, it gets however
+many it should, with the next card following directly behind it.  A
+system with 4 2-port PC/Xi boards will use minor numbers 0-7.
+This conserves some memory, and removes a few hard coded constants.
+
+NOTE!! NOTE!! NOTE!!
+The definition of PC/Xem as a valid board type is the BEGINNING of support
+for this device.  The driver does not currently recognise the board, nor
+does it want to initialize it.  At least not the EISA version.
+
+Mike McLagan <mike.mclagan@linux.org> 5, April 1996.
diff -u --recursive --new-file v1.3.84/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt
--- v1.3.84/linux/Documentation/mandatory.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/mandatory.txt	Mon Apr  8 11:41:05 1996
@@ -0,0 +1,126 @@
+	Mandatory File Locking For The Linux Operating System
+
+		Andy Walker <andy@lysaker.kvaerner.no>
+
+			   06 April 1996
+
+
+What is  mandatory locking?
+---------------------------
+
+Mandatory locking is kernel enforced file locking, as opposed to the more usual
+cooperative file locking used to guarantee sequential access to files among
+processes. File locks are applied using the flock() and fcntl() system calls
+(and the lockf() library routine which is a wrapper around fcntl().) It is
+normally a process' responsibility to check for locks on a file it wishes to
+update, before applying its own lock, updating the file and unlocking it again.
+The most commonly used example of this (and in the case of sendmail, the most
+troublesome) is access to a user's mailbox. The mail user agent and the mail
+transfer agent must guard against updating the mailbox at the same time, and
+prevent reading the mailbox while it is being updated.
+
+In a perfect world all process would use and honour a cooperative, or
+"advisory" locking scheme. However, the world isn't perfect, and there's
+a lot of poorly written code out there.
+
+In trying to address this problem, the designers of System V UNIX came up
+with a "mandatory" locking scheme, whereby the operating system kernel would
+block attempts by a process to write to a file that another process holds a
+"read" -or- "shared" lock on, and block attempts to both read and write to a 
+file that a process holds a "write " -or- "exclusive" lock on.
+
+The System V mandatory locking scheme was intended to have as little impact as
+possible on existing user code. The scheme is based on marking individual files
+as candidates for mandatory locking, and using the existing fcntl()/lockf()
+interface for applying locks just as if they were normal, advisory locks.
+
+Note 1: In saying "file" in the paragraphs above I am actually not telling
+the whole truth. System V locking is based on fcntl(). The granularity of
+fcntl() is such that it allows the locking of byte ranges in files, in addition
+to entire files, so the mandatory locking rules also have byte level
+granularity.
+
+Note 2: POSIX.1 does not specify any scheme for mandatory locking, despite
+borrowing the fcntl() locking scheme from System V.
+
+Marking a file for mandatory locking
+------------------------------------
+
+A file is marked as a candidate for mandatory by setting the group-id bit in
+its file mode but removing the group-execute bit. This is an otherwise
+meaningless combination, and was chosen by the System V implementors so as not
+to break existing user programs.
+
+Note that the group-id bit is usually automatically cleared by the kernel when
+a setgid file is written to. This is a security measure. The kernel has been
+modified to recognize the special case of a mandatory lock candidate and to
+refrain from clearing this bit. Similarly the kernel has been modified not
+to run mandatory lock candidates with setgid privileges.
+
+Available implementations
+-------------------------
+
+I originally intended to base my implementation on SunOS, only to find out that
+the implementation in SunOS 4.1.1 (the latest version that will work on my Sun
+3/80 at home) is completely hopeless.
+
+For one thing, calls to open() for a file fail with EAGAIN if another process
+holds a mandatory lock on the file. However, processes already holding open
+file descriptors can carry on using them. Wierd!
+
+In addition, SunOS doesn't seem to honour the O_NONBLOCK (O_NDELAY) flag for
+mandatory locks, so reads and writes to locked files always block when they
+should probably return EAGAIN.
+
+I found some test code online, which I think comes from one of Stevens' UNIX
+programming books, that confirmed what I considered to be be the "obvious"
+semantics, described below. I shall attempt to run my test programs on other
+platforms to verify my implementation.
+
+Personally I feel that this is such an esoteric area that these semantics are
+just as valid as any others, so long as the main points seem to agree.
+
+Semantics
+---------
+
+1. Mandatory locks can only be applied via the fcntl()/lockf() locking
+   interface - in other words the System V/POSIX interface. BSD style
+   locks using flock() never result in a mandatory lock.
+
+2. If a process has locked a region of a file with a mandatory read lock, then
+   other processes are permitted to read from that region. If any of these
+   processes attempts to write to the region it will block until the lock is
+   released, unless the process has opened the file opened with the O_NONBLOCK
+   flag in which case the system call will return immediately with the error
+   status EAGAIN.
+
+3. If a process has locked a region of a file with a mandatory write lock, all
+   attempts to read or write to that region block until the lock is released,
+   unless a process has opened the file with the O_NONBLOCK flag in which case
+   the system call will return immediately with the error status EAGAIN.
+
+Which system calls are affected?
+--------------------------------
+
+Those which modify a file's contents, not just the inode. That gives read(),
+write(), readv(), writev(), truncate() and ftruncate(). truncate() and
+ftruncate() are considered to be "write" actions for the purposes of mandatory
+locking.
+
+The affected region is usually defined as stretching from the current position
+for the total number of bytes read or written. For the truncate calls it is
+defined as the bytes of a file removed or added (we must also consider bytes
+added, as a lock can specify just "the whole file", rather than a specific
+range of bytes.)
+
+Note 3: I may have overlooked some system calls that need mandatory lock
+checking in my eagerness to get this code out the door. Please let me know, or
+better still fix the system calls yourself and submit a patch to me or Linus.
+
+Warning!
+--------
+
+Not even root can override a mandatory lock, so runaway process can wreak
+havoc if they lock crucial files. The way around it is to change the file
+permissions (remove the setgid bit) before trying to read or wite to it.
+Of course, that might be a bit tricky if the system is hung :-(
diff -u --recursive --new-file v1.3.84/linux/Documentation/networking/net-modules.txt linux/Documentation/networking/net-modules.txt
--- v1.3.84/linux/Documentation/networking/net-modules.txt	Tue Nov 14 07:48:52 1995
+++ linux/Documentation/networking/net-modules.txt	Sun Apr  7 09:45:56 1996
@@ -179,7 +179,7 @@
 
 eexpress.c:
 	io = 0x300
-	irq = 0
+	irq = 0		(IRQ value read from EEPROM)
 	(Probes ports: 0x300, 0x270, 0x320, 0x340)
 
 eql.c:
@@ -241,7 +241,10 @@
 	(Probes ports: 0x380, 0x300, 0x320, 0x340, 0x360, 0x3A0)
 
 plip.c:
-	No options; goes to IO=0x278, IRQ=2
+	io = 0
+	irq = 0		(by default, uses IRQ 5 for port at 0x3bc, IRQ 7
+			for port at 0x378, and IRQ 2 for port at 0x278)
+	(Probes ports: 0x278, 0x378, 0x3bc)
 
 ppp.c:
 	No options (ppp-2.2+ has some, this is based on non-dynamic
diff -u --recursive --new-file v1.3.84/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.84/linux/MAINTAINERS	Sun Mar 31 00:13:16 1996
+++ linux/MAINTAINERS	Mon Apr  8 11:10:36 1996
@@ -66,9 +66,15 @@
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
+ETHEREXPRESS NETWORK DRIVER
+P:	Philip Blundell
+M:	pjb27@cam.ac.uk
+L:	linux-net@vger.rutgers.edu
+S:	Maintained
+
 APM DRIVER
-P:      Rik Faith
-M:      faith@cs.unc.edu
+P:      Rik Faith & Stephen Rothwell
+M:      faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
 L:      linux-laptop@vger.rutgers.edu
 S:      Maintained
 
@@ -191,6 +197,12 @@
 P:	Martin Mares
 M:	mj@k332.feld.cvut.cz
 L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+DIGIBOARD DRIVER:
+P:	Christoph Lameter
+M:	clameter@fuller.edu
+L:	digiboard@list.fuller.edu
 S:	Maintained
 
 REST:
diff -u --recursive --new-file v1.3.84/linux/Makefile linux/Makefile
--- v1.3.84/linux/Makefile	Fri Apr  5 13:35:27 1996
+++ linux/Makefile	Fri Apr  5 13:36:28 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 84
+SUBLEVEL = 85
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.84/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.84/linux/arch/alpha/defconfig	Fri Apr  5 13:35:27 1996
+++ linux/arch/alpha/defconfig	Mon Apr  8 16:05:02 1996
@@ -146,6 +146,7 @@
 CONFIG_NET_EISA=y
 # CONFIG_APRICOT is not set
 CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
 # CONFIG_ZNET is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_TR is not set
diff -u --recursive --new-file v1.3.84/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v1.3.84/linux/arch/i386/mm/init.c	Wed Apr  3 16:06:55 1996
+++ linux/arch/i386/mm/init.c	Sun Apr  7 07:41:54 1996
@@ -153,7 +153,7 @@
 	pg_dir = swapper_pg_dir;
 	while (address < end_mem) {
 #ifdef USE_PENTIUM_MM
-		if (address <= end_mem + 4*1024*1024 &&
+		if (address + 4*1024*1024 <= end_mem &&
 		    (x86_capability & 8)) {
 #ifdef GAS_KNOWS_CR4
 			__asm__("movl %%cr4,%%eax\n\t"
diff -u --recursive --new-file v1.3.84/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.84/linux/drivers/block/floppy.c	Tue Apr  2 13:32:19 1996
+++ linux/drivers/block/floppy.c	Sun Apr  7 09:45:56 1996
@@ -166,8 +166,17 @@
 #define MAJOR_NR FLOPPY_MAJOR
 
 #include <linux/blk.h>
+#include <linux/cdrom.h> /* for the compatibility eject ioctl */
 
 
+#ifndef FLOPPY_MOTOR_MASK
+#define FLOPPY_MOTOR_MASK 0xf0
+#endif
+
+#ifndef fd_eject
+#define fd_eject(x) -EINVAL
+#endif
+
 /* Dma Memory related stuff */
 
 /* Pure 2^n version of get_order */
@@ -196,6 +205,13 @@
 static unsigned int fake_change = 0;
 static int initialising=1;
 
+#ifdef __sparc__
+/* We hold the FIFO configuration here.  We want to have Polling and
+ * Implied Seek enabled on Sun controllers.
+ */
+unsigned char fdc_cfg = 0;
+#endif
+
 static inline int TYPE(kdev_t x) {
 	return  (MINOR(x)>>2) & 0x1f;
 }
@@ -722,9 +738,9 @@
 			UDRS->select_date = jiffies;
 		}
 	}
-	if (newdor & 0xf0)
+	if (newdor & FLOPPY_MOTOR_MASK)
 		floppy_grab_irq_and_dma();
-	if (olddor & 0xf0)
+	if (olddor & FLOPPY_MOTOR_MASK)
 		floppy_release_irq_and_dma();
 	return olddor;
 }
@@ -766,7 +782,9 @@
 		return;
 	}
 	set_dor(fdc,~0,8);
+#if N_FDC > 1
 	set_dor(1-fdc, ~8, 0);
+#endif
 	if (FDCS->rawcmd == 2)
 		reset_fdc_info(1);
 	if (fd_inb(FD_STATUS) != STATUS_READY)
@@ -1105,7 +1123,7 @@
 
 	if (FDCS->perp_mode == perp_mode)
 		return;
-	if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+	if (FDCS->version >= FDC_82072A && FDCS->has_fifo) {
 		output_byte(FD_PERPENDICULAR);
 		output_byte(perp_mode);
 		FDCS->perp_mode = perp_mode;
@@ -1147,6 +1165,12 @@
 	if (FDCS->need_configure && FDCS->has_fifo) {
 		if (FDCS->reset)
 			return;
+#ifdef __sparc__ 
+		output_byte(FD_CONFIGURE);
+		output_byte(0x64);	/* Motor off timeout */
+		output_byte(fdc_cfg | 0x0A);
+		output_byte(0);
+#else
 		/* Turn on FIFO for 82077-class FDC (improves performance) */
 		/* TODO: lock this in via LOCK during initialization */
 		output_byte(FD_CONFIGURE);
@@ -1157,10 +1181,17 @@
 			FDCS->has_fifo=0;
 			return;
 		}
+#endif
 		FDCS->need_configure = 0;
 		/*DPRINT("FIFO enabled\n");*/
 	}
 
+#ifdef __sparc__
+	/* If doing implied seeks, no specify necessary */
+	if(fdc_cfg&0x40)
+		return;
+#endif
+
 	switch (raw_cmd->rate & 0x03) {
 		case 3:
 			dtr = 1000;
@@ -1481,6 +1512,15 @@
 		}
 	}
 
+#ifdef __sparc__
+	if (fdc_cfg&0x40) {
+		/* Implied seeks being done... */
+		DRS->track = raw_cmd->track;
+		setup_rw_floppy();
+		return;
+	}
+#endif
+
 	SET_INTR(seek_interrupt);
 	output_byte(FD_SEEK);
 	output_byte(UNIT(current_drive));
@@ -1587,6 +1627,7 @@
 	lasthandler = handler;
 	interruptjiffies = jiffies;
 
+	fd_disable_dma();
 	floppy_enable_hlt();
 	CLEAR_INTR;
 	if (fdc >= N_FDC || FDCS->address == -1){
@@ -1632,7 +1673,6 @@
 #ifdef DEBUGT
 	debugt("reset interrupt:");
 #endif
-	/* fdc_specify();	   reprogram fdc */
 	result();		/* get the status ready for set_fdc */
 	if (FDCS->reset) {
 		printk("reset set in interrupt, calling %p\n", cont->error);
@@ -1650,12 +1690,17 @@
 	SET_INTR(reset_interrupt);
 	FDCS->reset = 0;
 	reset_fdc_info(0);
-	if (FDCS->version >= FDC_82077)
+
+	/* Pseudo-DMA may intercept 'reset finished' interrupt.  */
+	/* Irrelevant for systems with true DMA (i386).          */
+	fd_disable_dma();
+
+	if (FDCS->version >= FDC_82072A)
 		fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
 	else {
 		fd_outb(FDCS->dor & ~0x04, FD_DOR);
 		udelay(FD_RESET_DELAY);
-		outb(FDCS->dor, FD_DOR);
+		fd_outb(FDCS->dor, FD_DOR);
 	}
 }
 
@@ -3111,6 +3156,7 @@
     {FDWERRORCLR,	27,  0},
     {FDWERRORGET,	28, 24},
     {FDRAWCMD,		 0,  0},
+    {FDEJECT,		 0,  0},
     {FDTWADDLE,		40,  0} };
 
 static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
@@ -3184,6 +3230,16 @@
 	type = TYPE(device);
 	drive = DRIVE(device);
 
+	/* convert compatibility eject ioctls into floppy eject ioctl.
+	 * We do this in order to provide a means to eject floppy disks before
+	 * installing the new fdutils package */
+	if(cmd == CDROMEJECT || /* CD-Rom eject */
+	   cmd == 0x6470 /* SunOS floppy eject */) {
+		DPRINT("obsolete eject ioctl\n");
+		DPRINT("please use floppycontrol --eject\n");
+		cmd = FDEJECT;
+	}
+
 	/* convert the old style command into a new style command */
 	if ((cmd & 0xff00) == 0x0200) {
 		ECALL(normalize_0x02xx_ioctl(&cmd, &size));
@@ -3207,6 +3263,25 @@
 		ECALL(fd_copyin((void *)param, &inparam, size))
 
 	switch (cmd) {
+		case FDEJECT:
+			if(UDRS->fd_ref != 1)
+				/* somebody else has this drive open */
+				return -EBUSY;
+			LOCK_FDC(drive,1);
+
+			/* do the actual eject. Fails on
+			 * non-Sparc archtitectures */
+			ret=fd_eject(UNIT(drive)); 
+
+			/* switch the motor off, in order to make the
+			 * cached DOR status match the hard DOS status
+			 */
+			motor_off_callback(drive);
+
+			USETF(FD_DISK_CHANGED);
+			USETF(FD_VERIFY);
+			process_fd_request();
+			return ret;			
 		case FDCLRPRM:
 			LOCK_FDC(drive,1);
 			current_type[drive] = NULL;
@@ -3645,8 +3720,10 @@
 	output_byte(FD_UNLOCK);
 	r = result();
 	if ((r == 1) && (reply_buffer[0] == 0x80)){
-		printk(KERN_INFO "FDC %d is a pre-1991 82077\n", fdc);
-		return FDC_82077_ORIG;	/* Pre-1991 82077 doesn't know LOCK/UNLOCK */
+		printk(KERN_INFO "FDC %d is a 82072A\n", fdc);
+		return FDC_82072A;	/* Pre-1991 82077 or 82072A as found
+					 * on Sparcs. These doesn't know 
+					 * LOCK/UNLOCK */
 	}
 	if ((r != 1) || (reply_buffer[0] != 0x00)) {
 		printk("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
@@ -3848,6 +3925,9 @@
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 	reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
+#ifdef __sparc__
+	fdc_cfg = (0x40 | 0x10); /* ImplSeek+Polling+FIFO */
+#endif
 	config_types();
 
 	for (i = 0; i < N_FDC; i++) {
@@ -3906,7 +3986,7 @@
 		 * properly, so force a reset for the standard FDC clones,
 		 * to avoid interrupt garbage.
 		 */
-		FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
+		FDCS->has_fifo = FDCS->version >= FDC_82072A;
 		user_reset_fdc(-1,FD_RESET_ALWAYS,0);
 	}
 	fdc=0;
@@ -4082,6 +4162,8 @@
 	unregister_blkdev(MAJOR_NR, "fd");
 
 	blk_dev[MAJOR_NR].request_fn = 0;
+	/* eject disk, if any */
+	fd_eject(0);
 }
 
 #ifdef __cplusplus
diff -u --recursive --new-file v1.3.84/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v1.3.84/linux/drivers/block/ide-cd.c	Tue Apr  2 13:32:19 1996
+++ linux/drivers/block/ide-cd.c	Sun Apr  7 09:45:57 1996
@@ -87,6 +87,10 @@
  * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
  *                        problem with "hde=cdrom" with no drive present.  -ml
  * 3.08  Mar  6, 1996 -- More Vertos workarounds.
+ * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ *                       Switch to using MSF addressing for audio commands.
+ *                       Reformat to match kernel tabbing style.
+ *                       Add CDROM_GET_UPC ioctl.
  *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
@@ -197,19 +201,17 @@
    They generally do not change after initialization, unless we learn
    more about the drive from stuff failing. */
 struct ide_cd_config_flags {
-  __u8 drq_interrupt : 1; /* Device sends an interrupt when ready
-                                 for a packet command. */
-  __u8 no_doorlock   : 1; /* Drive cannot lock the door. */
+	__u8 drq_interrupt    : 1; /* Device sends an interrupt when ready
+				      for a packet command. */
+	__u8 no_doorlock      : 1; /* Drive cannot lock the door. */
 #if ! STANDARD_ATAPI
-  __u8 no_playaudio12: 1; /* The PLAYAUDIO12 command is not supported. */
- 
-  __u8 no_lba_toc    : 1; /* Drive cannot return TOC info in LBA format. */
-  __u8 playmsf_uses_bcd : 1; /* Drive uses BCD in PLAYAUDIO_MSF. */
-  __u8 old_readcd    : 1; /* Drive uses old READ CD opcode. */
-  __u8 vertos_lossage: 1; /* Drive is a Vertos 300,
-				 and likes to speak BCD. */
+	__u8 old_readcd       : 1; /* Drive uses old READ CD opcode. */
+	__u8 playmsf_as_bcd   : 1; /* PLAYMSF command takes BCD args. */
+	__u8 tocaddr_as_bcd   : 1; /* TOC addresses are in BCD. */
+	__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
+	__u8 subchan_as_bcd   : 1; /* Subchannel info is in BCD. */
 #endif  /* not STANDARD_ATAPI */
-  __u8 reserved : 1;
+	__u8 reserved         : 1;
 };
 #define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_sect))
 
@@ -217,11 +219,11 @@
 /* State flags.  These give information about the current state of the
    drive, and will change during normal operation. */
 struct ide_cd_state_flags {
-  __u8 media_changed : 1; /* Driver has noticed a media change. */
-  __u8 toc_valid     : 1; /* Saved TOC information is current. */
-  __u8 door_locked   : 1; /* We think that the drive door is locked. */
-  __u8 eject_on_close: 1; /* Drive should eject when device is closed. */
-  __u8 reserved : 4;
+	__u8 media_changed : 1; /* Driver has noticed a media change. */
+	__u8 toc_valid     : 1; /* Saved TOC information is current. */
+	__u8 door_locked   : 1; /* We think that the drive door is locked. */
+	__u8 eject_on_close: 1; /* Drive should eject when device is closed. */
+	__u8 reserved : 4;
 };
 #define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
 
@@ -243,24 +245,23 @@
 static inline
 void cdrom_in_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
 {
-  ++bytecount;
-  ide_input_data (drive, buffer, bytecount / 4);
-  if ((bytecount & 0x03) >= 2)
-    {
-      insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
-    }
+	++bytecount;
+	ide_input_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2) {
+		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+	}
 }
 
 
 static inline
 void cdrom_out_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
 {
-  ++bytecount;
-  ide_output_data (drive, buffer, bytecount / 4);
-  if ((bytecount & 0x03) >= 2)
-    {
-      outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
-    }
+	++bytecount;
+	ide_output_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2) {
+		outsw (IDE_DATA_REG,
+		       ((byte *)buffer) + (bytecount & ~0x03), 1);
+	}
 }
 
 
@@ -276,132 +277,133 @@
 /* From Table 124 of the ATAPI 1.2 spec. */
 
 char *sense_key_texts[16] = {
-  "No sense data",
-  "Recovered error",
-  "Not ready",
-  "Medium error",
-  "Hardware error",
-  "Illegal request",
-  "Unit attention",
-  "Data protect",
-  "(reserved)",
-  "(reserved)",
-  "(reserved)",
-  "Aborted command",
-  "(reserved)",
-  "(reserved)",
-  "Miscompare",
-  "(reserved)",
+	"No sense data",
+	"Recovered error",
+	"Not ready",
+	"Medium error",
+	"Hardware error",
+	"Illegal request",
+	"Unit attention",
+	"Data protect",
+	"(reserved)",
+	"(reserved)",
+	"(reserved)",
+	"Aborted command",
+	"(reserved)",
+	"(reserved)",
+	"Miscompare",
+	"(reserved)",
 };
 
 
 /* From Table 125 of the ATAPI 1.2 spec. */
 
 struct {
-  short asc_ascq;
-  char *text;
+	short asc_ascq;
+	char *text;
 } sense_data_texts[] = {
-  { 0x0000, "No additional sense information" },
-  { 0x0011, "Audio play operation in progress" },
-  { 0x0012, "Audio play operation paused" },
-  { 0x0013, "Audio play operation successfully completed" },
-  { 0x0014, "Audio play operation stopped due to error" },
-  { 0x0015, "No current audio status to return" },
+	{ 0x0000, "No additional sense information" },
+	{ 0x0011, "Audio play operation in progress" },
+	{ 0x0012, "Audio play operation paused" },
+	{ 0x0013, "Audio play operation successfully completed" },
+	{ 0x0014, "Audio play operation stopped due to error" },
+	{ 0x0015, "No current audio status to return" },
 
-  { 0x0200, "No seek complete" },
+	{ 0x0200, "No seek complete" },
 
-  { 0x0400, "Logical unit not ready - cause not reportable" },
-  { 0x0401, "Logical unit not ready - in progress (sic) of becoming ready" },
-  { 0x0402, "Logical unit not ready - initializing command required" },
-  { 0x0403, "Logical unit not ready - manual intervention required" },
+	{ 0x0400, "Logical unit not ready - cause not reportable" },
+	{ 0x0401,
+	  "Logical unit not ready - in progress (sic) of becoming ready" },
+	{ 0x0402, "Logical unit not ready - initializing command required" },
+	{ 0x0403, "Logical unit not ready - manual intervention required" },
 
-  { 0x0600, "No reference position found" },
+	{ 0x0600, "No reference position found" },
 
-  { 0x0900, "Track following error" },
-  { 0x0901, "Tracking servo failure" },
-  { 0x0902, "Focus servo failure" },
-  { 0x0903, "Spindle servo failure" },
+	{ 0x0900, "Track following error" },
+	{ 0x0901, "Tracking servo failure" },
+	{ 0x0902, "Focus servo failure" },
+	{ 0x0903, "Spindle servo failure" },
 
-  { 0x1100, "Unrecovered read error" },
-  { 0x1106, "CIRC unrecovered error" },
+	{ 0x1100, "Unrecovered read error" },
+	{ 0x1106, "CIRC unrecovered error" },
 
-  { 0x1500, "Random positioning error" },
-  { 0x1501, "Mechanical positioning error" },
-  { 0x1502, "Positioning error detected by read of medium" },
+	{ 0x1500, "Random positioning error" },
+	{ 0x1501, "Mechanical positioning error" },
+	{ 0x1502, "Positioning error detected by read of medium" },
 
-  { 0x1700, "Recovered data with no error correction applied" },
-  { 0x1701, "Recovered data with retries" },
-  { 0x1702, "Recovered data with positive head offset" },
-  { 0x1703, "Recovered data with negative head offset" },
-  { 0x1704, "Recovered data with retries and/or CIRC applied" },
-  { 0x1705, "Recovered data using previous sector ID" },
+	{ 0x1700, "Recovered data with no error correction applied" },
+	{ 0x1701, "Recovered data with retries" },
+	{ 0x1702, "Recovered data with positive head offset" },
+	{ 0x1703, "Recovered data with negative head offset" },
+	{ 0x1704, "Recovered data with retries and/or CIRC applied" },
+	{ 0x1705, "Recovered data using previous sector ID" },
 
-  { 0x1800, "Recovered data with error correction applied" },
-  { 0x1801, "Recovered data with error correction and retries applied" },
-  { 0x1802, "Recovered data - the data was auto-reallocated" },
-  { 0x1803, "Recovered data with CIRC" },
-  { 0x1804, "Recovered data with L-EC" },
-  { 0x1805, "Recovered data - recommend reassignment" },
-  { 0x1806, "Recovered data - recommend rewrite" },
+	{ 0x1800, "Recovered data with error correction applied" },
+	{ 0x1801, "Recovered data with error correction and retries applied" },
+	{ 0x1802, "Recovered data - the data was auto-reallocated" },
+	{ 0x1803, "Recovered data with CIRC" },
+	{ 0x1804, "Recovered data with L-EC" },
+	{ 0x1805, "Recovered data - recommend reassignment" },
+	{ 0x1806, "Recovered data - recommend rewrite" },
 
-  { 0x1a00, "Parameter list length error" },
+	{ 0x1a00, "Parameter list length error" },
 
-  { 0x2000, "Invalid command operation code" },
+	{ 0x2000, "Invalid command operation code" },
 
-  { 0x2100, "Logical block address out of range" },
+	{ 0x2100, "Logical block address out of range" },
 
-  { 0x2400, "Invalid field in command packet" },
+	{ 0x2400, "Invalid field in command packet" },
 
-  { 0x2600, "Invalid field in parameter list" },
-  { 0x2601, "Parameter not supported" },
-  { 0x2602, "Parameter value invalid" },
-  { 0x2603, "Threshold parameters not supported" },
+	{ 0x2600, "Invalid field in parameter list" },
+	{ 0x2601, "Parameter not supported" },
+	{ 0x2602, "Parameter value invalid" },
+	{ 0x2603, "Threshold parameters not supported" },
 
-  { 0x2800, "Not ready to ready transition, medium may have changed" },
+	{ 0x2800, "Not ready to ready transition, medium may have changed" },
 
-  { 0x2900, "Power on, reset or bus device reset occurred" },
+	{ 0x2900, "Power on, reset or bus device reset occurred" },
 
-  { 0x2a00, "Parameters changed" },
-  { 0x2a01, "Mode parameters changed" },
+	{ 0x2a00, "Parameters changed" },
+	{ 0x2a01, "Mode parameters changed" },
 
-  { 0x3000, "Incompatible medium installed" },
-  { 0x3001, "Cannot read medium - unknown format" },
-  { 0x3002, "Cannot read medium - incompatible format" },
+	{ 0x3000, "Incompatible medium installed" },
+	{ 0x3001, "Cannot read medium - unknown format" },
+	{ 0x3002, "Cannot read medium - incompatible format" },
 
-  { 0x3700, "Rounded parameter" },
+	{ 0x3700, "Rounded parameter" },
 
-  { 0x3900, "Saving parameters not supported" },
+	{ 0x3900, "Saving parameters not supported" },
 
-  { 0x3a00, "Medium not present" },
+	{ 0x3a00, "Medium not present" },
 
-  { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
-  { 0x3f01, "Microcode has been changed" },
-  { 0x3f02, "Changed operating definition" },
-  { 0x3f03, "Inquiry data has changed" },
+	{ 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+	{ 0x3f01, "Microcode has been changed" },
+	{ 0x3f02, "Changed operating definition" },
+	{ 0x3f03, "Inquiry data has changed" },
 
-  { 0x4000, "Diagnostic failure on component (ASCQ)" },
+	{ 0x4000, "Diagnostic failure on component (ASCQ)" },
 
-  { 0x4400, "Internal ATAPI CD-ROM drive failure" },
+	{ 0x4400, "Internal ATAPI CD-ROM drive failure" },
 
-  { 0x4e00, "Overlapped commands attempted" },
+	{ 0x4e00, "Overlapped commands attempted" },
 
-  { 0x5300, "Media load or eject failed" },
-  { 0x5302, "Medium removal prevented" },
+	{ 0x5300, "Media load or eject failed" },
+	{ 0x5302, "Medium removal prevented" },
 
-  { 0x5700, "Unable to recover table of contents" },
+	{ 0x5700, "Unable to recover table of contents" },
 
-  { 0x5a00, "Operator request or state change input (unspecified)" },
-  { 0x5a01, "Operator medium removal request" },
+	{ 0x5a00, "Operator request or state change input (unspecified)" },
+	{ 0x5a01, "Operator medium removal request" },
 
-  { 0x5b00, "Threshold condition met" },
+	{ 0x5b00, "Threshold condition met" },
 
-  { 0x5c00, "Status change" },
+	{ 0x5c00, "Status change" },
 
-  { 0x6300, "End of user area encountered on this track" },
+	{ 0x6300, "End of user area encountered on this track" },
 
-  { 0x6400, "Illegal mode for this track" },
+	{ 0x6400, "Illegal mode for this track" },
 
-  { 0xbf00, "Loss of streaming" },
+	{ 0xbf00, "Loss of streaming" },
 };
 #endif
 
@@ -417,110 +419,111 @@
 			       struct atapi_request_sense *reqbuf,
 			       struct packet_command *failed_command)
 {
-  /* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
-     Workman (and probably other programs) uses this command to poll
-     the drive, and we don't want to fill the syslog with useless errors. */
-  if (failed_command &&
-      failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
-      (reqbuf->sense_key == NOT_READY || reqbuf->sense_key == UNIT_ATTENTION))
-    return;
+	/* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
+	   Workman (and probably other programs) uses this command to poll
+	   the drive, and we don't want to fill the syslog
+	   with useless errors. */
+	if (failed_command &&
+	    failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
+	    (reqbuf->sense_key == NOT_READY ||
+	     reqbuf->sense_key == UNIT_ATTENTION))
+		return;
 
 #if VERBOSE_IDE_CD_ERRORS
-  {
-    int i;
-    char *s;
-    char buf[80];
-
-    printk ("ATAPI device %s:\n", drive->name);
-
-    printk ("  Error code: 0x%02x\n", reqbuf->error_code);
-
-    if (reqbuf->sense_key >= 0 &&
-	reqbuf->sense_key < ARY_LEN (sense_key_texts))
-      s = sense_key_texts[reqbuf->sense_key];
-    else
-      s = "(bad sense key)";
-
-    printk ("  Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
-
-    if (reqbuf->asc == 0x40) {
-      sprintf (buf, "Diagnostic failure on component 0x%02x", reqbuf->ascq);
-      s = buf;
-    }
-
-    else {
-      int lo, hi;
-      int key = (reqbuf->asc << 8);
-      if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
-	key |= reqbuf->ascq;
-
-      lo = 0;
-      hi = ARY_LEN (sense_data_texts);
-      s = NULL;
-
-      while (hi > lo) {
-	int mid = (lo + hi) / 2;
-	if (sense_data_texts[mid].asc_ascq == key) {
-	  s = sense_data_texts[mid].text;
-	  break;
-	}
-	else if (sense_data_texts[mid].asc_ascq > key)
-	  hi = mid;
-	else
-	  lo = mid+1;
-      }
-    }
+	{
+		int i;
+		char *s;
+		char buf[80];
+
+		printk ("ATAPI device %s:\n", drive->name);
+
+		printk ("  Error code: 0x%02x\n", reqbuf->error_code);
+
+		if (reqbuf->sense_key >= 0 &&
+		    reqbuf->sense_key < ARY_LEN (sense_key_texts))
+			s = sense_key_texts[reqbuf->sense_key];
+		else
+			s = "(bad sense key)";
+
+		printk ("  Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
+
+		if (reqbuf->asc == 0x40) {
+			sprintf (buf, "Diagnostic failure on component 0x%02x",
+				 reqbuf->ascq);
+			s = buf;
+		} else {
+			int lo, hi;
+			int key = (reqbuf->asc << 8);
+			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+				key |= reqbuf->ascq;
+
+			lo = 0;
+			hi = ARY_LEN (sense_data_texts);
+			s = NULL;
+
+			while (hi > lo) {
+				int mid = (lo + hi) / 2;
+				if (sense_data_texts[mid].asc_ascq == key) {
+					s = sense_data_texts[mid].text;
+					break;
+				}
+				else if (sense_data_texts[mid].asc_ascq > key)
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+		}
 
-    if (s == NULL) {
-      if (reqbuf->asc > 0x80)
-	s = "(vendor-specific error)";
-      else
-	s = "(reserved error code)";
-    }
-
-    printk ("  Additional sense data: 0x%02x, 0x%02x  - %s\n",
-	    reqbuf->asc, reqbuf->ascq, s);
-
-    if (failed_command != NULL) {
-      printk ("  Failed packet command: ");
-      for (i=0; i<sizeof (failed_command->c); i++)
-	printk ("%02x ", failed_command->c[i]);
-      printk ("\n");
-    }
-
-    if (reqbuf->sense_key == ILLEGAL_REQUEST &&
-	(reqbuf->sense_key_specific[0] & 0x80) != 0)
-      {
-	printk ("  Error in %s byte %d",
-		(reqbuf->sense_key_specific[0] & 0x40) != 0
-		  ? "command packet"
-		  : "command data",
-		(reqbuf->sense_key_specific[1] << 8) +
-		reqbuf->sense_key_specific[2]);
-
-	if ((reqbuf->sense_key_specific[0] & 0x40) != 0)
-	  {
-	    printk (" bit %d", reqbuf->sense_key_specific[0] & 0x07);
-	  }
+		if (s == NULL) {
+			if (reqbuf->asc > 0x80)
+				s = "(vendor-specific error)";
+			else
+				s = "(reserved error code)";
+		}
 
-	printk ("\n");
-      }
-  }
+		printk ("  Additional sense data: 0x%02x, 0x%02x  - %s\n",
+			reqbuf->asc, reqbuf->ascq, s);
 
-#else
+		if (failed_command != NULL) {
+			printk ("  Failed packet command: ");
+			for (i=0; i<sizeof (failed_command->c); i++)
+				printk ("%02x ", failed_command->c[i]);
+			printk ("\n");
+		}
 
-  /* Suppress printing unit attention and `in progress of becoming ready'
-     errors when we're not being verbose. */
+		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+		    (reqbuf->sense_key_specific[0] & 0x80) != 0) {
+			printk ("  Error in %s byte %d",
+				(reqbuf->sense_key_specific[0] & 0x40) != 0
+				? "command packet"
+				: "command data",
+				(reqbuf->sense_key_specific[1] << 8) +
+				reqbuf->sense_key_specific[2]);
+
+			if ((reqbuf->sense_key_specific[0] & 0x40) != 0) {
+				printk (" bit %d",
+					reqbuf->sense_key_specific[0] & 0x07);
+			}
 
-  if (reqbuf->sense_key == UNIT_ATTENTION ||
-      (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
-					  reqbuf->asc == 0x3a)))
-    return;
-
-  printk ("%s: code: 0x%02x  key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
-	  drive->name,
-	  reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);
-#endif
+			printk ("\n");
+		}
+	}
+
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+	/* Suppress printing unit attention and `in progress of becoming ready'
+	   errors when we're not being verbose. */
+
+	if (reqbuf->sense_key == UNIT_ATTENTION ||
+	    (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+						reqbuf->asc == 0x3a)))
+		return;
+
+	printk ("%s: code: 0x%02x  key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+		drive->name,
+		reqbuf->error_code, reqbuf->sense_key,
+		reqbuf->asc, reqbuf->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
 }
 
 
@@ -528,14 +531,13 @@
    start it over entirely, or even put it back on the request queue. */
 static void restore_request (struct request *rq)
 {
-  if (rq->buffer != rq->bh->b_data)
-    {
-      int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
-      rq->buffer = rq->bh->b_data;
-      rq->nr_sectors += n;
-      rq->sector -= n;
-    }
-  rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+	if (rq->buffer != rq->bh->b_data) {
+		int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+		rq->buffer = rq->bh->b_data;
+		rq->nr_sectors += n;
+		rq->sector -= n;
+	}
+	rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
 }
 
 
@@ -544,66 +546,67 @@
 				       struct atapi_request_sense *reqbuf,
 				       struct packet_command *failed_command)
 {
-  struct request *rq;
-  struct packet_command *pc;
-  int len;
-
-  /* If the request didn't explicitly specify where to put the sense data,
-     use the statically allocated structure. */
-  if (reqbuf == NULL)
-    reqbuf = &drive->cdrom_info.sense_data;
-
-  /* Make up a new request to retrieve sense information. */
-
-  pc = &HWIF(drive)->request_sense_pc;
-  memset (pc, 0, sizeof (*pc));
-
-  /* The request_sense structure has an odd number of (16-bit) words,
-     which won't work well with 32-bit transfers.  However, we don't care
-     about the last two bytes, so just truncate the structure down
-     to an even length. */
-  len = sizeof (*reqbuf) / 4;
-  len *= 4;
-
-  pc->c[0] = REQUEST_SENSE;
-  pc->c[4] = len;
-  pc->buffer = (char *)reqbuf;
-  pc->buflen = len;
-  pc->sense_data = (struct atapi_request_sense *)failed_command;
-
-  /* stuff the sense request in front of our current request */
-
-  rq = &HWIF(drive)->request_sense_request;
-  ide_init_drive_cmd (rq);
-  rq->cmd = REQUEST_SENSE_COMMAND;
-  rq->buffer = (char *)pc;
-  rq->sem = sem;
-  (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+	struct request *rq;
+	struct packet_command *pc;
+	int len;
+
+	/* If the request didn't explicitly specify where
+	   to put the sense data, use the statically allocated structure. */
+	if (reqbuf == NULL)
+		reqbuf = &drive->cdrom_info.sense_data;
+
+	/* Make up a new request to retrieve sense information. */
+
+	pc = &HWIF(drive)->request_sense_pc;
+	memset (pc, 0, sizeof (*pc));
+
+	/* The request_sense structure has an odd number of (16-bit) words,
+	   which won't work well with 32-bit transfers.  However, we don't care
+	   about the last two bytes, so just truncate the structure down
+	   to an even length. */
+	len = sizeof (*reqbuf) / 4;
+	len *= 4;
+
+	pc->c[0] = REQUEST_SENSE;
+	pc->c[4] = len;
+	pc->buffer = (char *)reqbuf;
+	pc->buflen = len;
+	pc->sense_data = (struct atapi_request_sense *)failed_command;
+
+	/* stuff the sense request in front of our current request */
+
+	rq = &HWIF(drive)->request_sense_request;
+	ide_init_drive_cmd (rq);
+	rq->cmd = REQUEST_SENSE_COMMAND;
+	rq->buffer = (char *)pc;
+	rq->sem = sem;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
 
 static void cdrom_end_request (int uptodate, ide_drive_t *drive)
 {
-  struct request *rq = HWGROUP(drive)->rq;
+	struct request *rq = HWGROUP(drive)->rq;
 
-  /* The code in blk.h can screw us up on error recovery if the block
-     size is larger than 1k.  Fix that up here. */
-  if (!uptodate && rq->bh != 0)
-    {
-      int adj = rq->current_nr_sectors - 1;
-      rq->current_nr_sectors -= adj;
-      rq->sector += adj;
-    }
-
-  if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate)
-    {
-      struct packet_command *pc = (struct packet_command *)rq->buffer;
-      cdrom_analyze_sense_data (drive,
-				(struct atapi_request_sense *)(pc->buffer - pc->c[4]), 
-				(struct packet_command *)pc->sense_data);
-    }
+	/* The code in blk.h can screw us up on error recovery if the block
+	   size is larger than 1k.  Fix that up here. */
+	if (!uptodate && rq->bh != 0) {
+		int adj = rq->current_nr_sectors - 1;
+		rq->current_nr_sectors -= adj;
+		rq->sector += adj;
+	}
+
+	if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
+		struct packet_command *pc = (struct packet_command *)
+			                      rq->buffer;
+		cdrom_analyze_sense_data (drive,
+					  (struct atapi_request_sense *)
+					  	(pc->buffer - pc->c[4]), 
+					  (struct packet_command *)
+					  	pc->sense_data);
+	}
 
-  ide_end_request (uptodate, HWGROUP(drive));
+	ide_end_request (uptodate, HWGROUP(drive));
 }
 
 
@@ -611,160 +614,144 @@
    buffers. */
 static void cdrom_saw_media_change (ide_drive_t *drive)
 {
-  CDROM_STATE_FLAGS (drive)->media_changed = 1;
-  CDROM_STATE_FLAGS (drive)->toc_valid = 0;
-  drive->cdrom_info.nsectors_buffered = 0;
+	CDROM_STATE_FLAGS (drive)->media_changed = 1;
+	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+	drive->cdrom_info.nsectors_buffered = 0;
 }
 
 
 /* Returns 0 if the request should be continued.
    Returns 1 if the request was ended. */
-static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret)
+static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
+				int *stat_ret)
 {
-  struct request *rq = HWGROUP(drive)->rq;
-  int stat, err, sense_key, cmd;
-
-  /* Check for errors. */
-  stat = GET_STAT();
-  *stat_ret = stat;
-
-  if (OK_STAT (stat, good_stat, BAD_R_STAT))
-    return 0;
-
-  /* Got an error. */
-  err = IN_BYTE (IDE_ERROR_REG);
-  sense_key = err >> 4;
-
-  if (rq == NULL)
-    printk ("%s : missing request in cdrom_decode_status\n", drive->name);
-  else
-    {
-      cmd = rq->cmd;
-
-      if (cmd == REQUEST_SENSE_COMMAND)
-	{
-	  /* We got an error trying to get sense info from the drive
-	     (probably while trying to recover from a former error).
-	     Just give up. */
-
-	  struct packet_command *pc = (struct packet_command *)rq->buffer;
-	  pc->stat = 1;
-	  cdrom_end_request (1, drive);
-	  ide_error (drive, "request sense failure", stat);
-	  return 1;
-	}
-
-      else if (cmd == PACKET_COMMAND)
-	{
-	  /* All other functions, except for READ. */
-
-	  struct packet_command *pc = (struct packet_command *)rq->buffer;
-	  struct semaphore *sem = NULL;
-
-	  /* Check for tray open. */
-	  if (sense_key == NOT_READY)
-	    {
-	      cdrom_saw_media_change (drive);
-
-	      /* Print an error message to the syslog.
-		 Exception: don't print anything if this is a read subchannel
-		 command.  This is because workman constantly polls the drive
-		 with this command, and we don't want to uselessly fill up
-		 the syslog. */
-	      if (pc->c[0] != SCMD_READ_SUBCHANNEL)
-		printk ("%s : tray open or drive not ready\n", drive->name);
-	    }
-
-	  /* Check for media change. */
-	  else if (sense_key == UNIT_ATTENTION)
-	    {
-	      cdrom_saw_media_change (drive);
-	      printk ("%s: media changed\n", drive->name);
-	    }
-
-	  /* Otherwise, print an error. */
-	  else
-	    {
-	      ide_dump_status (drive, "packet command error", stat);
-	    }
-
-	  /* Set the error flag and complete the request.
-	     Then, if we have a CHECK CONDITION status, queue a request
-	     sense command.  We must be careful, though: we don't want
-	     the thread in cdrom_queue_packet_command to wake up until
-	     the request sense has completed.  We do this by transferring
-	     the semaphore from the packet command request to the
-	     request sense request. */
-
-	  if ((stat & ERR_STAT) != 0)
-	    {
-	      sem = rq->sem;
-	      rq->sem = NULL;
-	    }
-
-	  pc->stat = 1;
-	  cdrom_end_request (1, drive);
-
-	  if ((stat & ERR_STAT) != 0)
-	    cdrom_queue_request_sense (drive, sem, pc->sense_data, pc);
-	}
-
-      else
-	{
-	  /* Handle errors from READ requests. */
+	struct request *rq = HWGROUP(drive)->rq;
+	int stat, err, sense_key, cmd;
 
-	  /* Check for tray open. */
-	  if (sense_key == NOT_READY)
-	    {
-	      cdrom_saw_media_change (drive);
-
-	      /* Fail the request. */
-	      printk ("%s : tray open\n", drive->name);
-	      cdrom_end_request (0, drive);
-	    }
-
-	  /* Check for media change. */
-	  else if (sense_key == UNIT_ATTENTION)
-	    {
-	      cdrom_saw_media_change (drive);
-
-	      /* Arrange to retry the request.
-	         But be sure to give up if we've retried too many times. */
-	      if (++rq->errors > ERROR_MAX)
-		{
-		  cdrom_end_request (0, drive);
+	/* Check for errors. */
+	stat = GET_STAT();
+	*stat_ret = stat;
+
+	if (OK_STAT (stat, good_stat, BAD_R_STAT))
+		return 0;
+
+	/* Got an error. */
+	err = IN_BYTE (IDE_ERROR_REG);
+	sense_key = err >> 4;
+
+	if (rq == NULL)
+		printk ("%s : missing request in cdrom_decode_status\n",
+			drive->name);
+	else {
+		cmd = rq->cmd;
+
+		if (cmd == REQUEST_SENSE_COMMAND) {
+			/* We got an error trying to get sense info
+			   from the drive (probably while trying
+			   to recover from a former error).  Just give up. */
+
+			struct packet_command *pc = (struct packet_command *)
+				                      rq->buffer;
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+			ide_error (drive, "request sense failure", stat);
+			return 1;
+
+		} else if (cmd == PACKET_COMMAND) {
+			/* All other functions, except for READ. */
+
+			struct packet_command *pc = (struct packet_command *)
+				                      rq->buffer;
+			struct semaphore *sem = NULL;
+
+			/* Check for tray open. */
+			if (sense_key == NOT_READY) {
+				cdrom_saw_media_change (drive);
+
+				/* Print an error message to the syslog.
+				   Exception: don't print anything if this
+				   is a read subchannel command.  This is
+				   because workman constantly polls the drive
+				   with this command, and we don't want
+				   to uselessly fill up the syslog. */
+				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+					printk ("%s : tray open or drive not ready\n",
+						drive->name);
+			} else if (sense_key == UNIT_ATTENTION) {
+				/* Check for media change. */
+				cdrom_saw_media_change (drive);
+				printk ("%s: media changed\n", drive->name);
+			} else {
+				/* Otherwise, print an error. */
+				ide_dump_status (drive, "packet command error",
+						 stat);
+			}
+			
+			/* Set the error flag and complete the request.
+			   Then, if we have a CHECK CONDITION status,
+			   queue a request sense command.  We must be careful,
+			   though: we don't want the thread in
+			   cdrom_queue_packet_command to wake up until
+			   the request sense has completed.  We do this
+			   by transferring the semaphore from the packet
+			   command request to the request sense request. */
+
+			if ((stat & ERR_STAT) != 0) {
+				sem = rq->sem;
+				rq->sem = NULL;
+			}
+
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense (drive, sem,
+							   pc->sense_data, pc);
+		} else {
+			/* Handle errors from READ requests. */
+
+			if (sense_key == NOT_READY) {
+				/* Tray open. */
+				cdrom_saw_media_change (drive);
+
+				/* Fail the request. */
+				printk ("%s : tray open\n", drive->name);
+				cdrom_end_request (0, drive);
+			} else if (sense_key == UNIT_ATTENTION) {
+				/* Media change. */
+				cdrom_saw_media_change (drive);
+
+				/* Arrange to retry the request.
+				   But be sure to give up if we've retried
+				   too many times. */
+				if (++rq->errors > ERROR_MAX)
+					cdrom_end_request (0, drive);
+			} else if (sense_key == ILLEGAL_REQUEST ||
+				   sense_key == DATA_PROTECT) {
+				/* No point in retrying after an illegal
+				   request or data protect error.*/
+				ide_dump_status (drive, "command error", stat);
+				cdrom_end_request (0, drive);
+			} else if ((err & ~ABRT_ERR) != 0) {
+				/* Go to the default handler
+				   for other errors. */
+				ide_error (drive, "cdrom_decode_status", stat);
+				return 1;
+			} else if ((++rq->errors > ERROR_MAX)) {
+				/* We've racked up too many retries.  Abort. */
+				cdrom_end_request (0, drive);
+			}
+
+			/* If we got a CHECK_CONDITION status,
+			   queue a request sense command. */
+			if ((stat & ERR_STAT) != 0)
+				cdrom_queue_request_sense (drive,
+							   NULL, NULL, NULL);
 		}
-	    }
-	  /* No point in retrying after an illegal request or
-	     data protect error.*/
-	  else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT)
-	    {
-	      ide_dump_status (drive, "command error", stat);
-	      cdrom_end_request (0, drive);
-	    }
-
-	  /* If there were other errors, go to the default handler. */
-	  else if ((err & ~ABRT_ERR) != 0)
-	    {
-	      ide_error (drive, "cdrom_decode_status", stat);
-	      return 1;
-	    }
-
-	  /* Else, abort if we've racked up too many retries. */
-	  else if ((++rq->errors > ERROR_MAX))
-	    {
-	      cdrom_end_request (0, drive);
-	    }
-
-	  /* If we got a CHECK_CONDITION status, queue a request sense
-	     command. */
-	  if ((stat & ERR_STAT) != 0)
-	    cdrom_queue_request_sense (drive, NULL, NULL, NULL);
 	}
-    }
 
-  /* Retry, or handle the next request. */
-  return 1;
+	/* Retry, or handle the next request. */
+	return 1;
 }
 
 
@@ -778,30 +765,27 @@
 static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
 				       ide_handler_t *handler)
 {
-  /* Wait for the controller to be idle. */
-  if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
+	/* Wait for the controller to be idle. */
+	if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
 
-  /* Set up the controller registers. */
-  OUT_BYTE (0, IDE_FEATURE_REG);
-  OUT_BYTE (0, IDE_NSECTOR_REG);
-  OUT_BYTE (0, IDE_SECTOR_REG);
-
-  OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
-  OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
-  OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
-
-  if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
-    {
-      ide_set_handler (drive, handler, WAIT_CMD);
-      OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
-    }
-  else
-    {
-      OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
-      (*handler) (drive);
-    }
+	/* Set up the controller registers. */
+	OUT_BYTE (0, IDE_FEATURE_REG);
+	OUT_BYTE (0, IDE_NSECTOR_REG);
+	OUT_BYTE (0, IDE_SECTOR_REG);
+
+	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
+	OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		ide_set_handler (drive, handler, WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+		(*handler) (drive);
+	}
 
-  return 0;
+	return 0;
 }
 
 
@@ -814,28 +798,26 @@
                                           char *cmd_buf, int cmd_len,
 					  ide_handler_t *handler)
 {
-  if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
-    {
-      /* Here we should have been called after receiving an interrupt
-         from the device.  DRQ should how be set. */
-      int stat_dum;
-
-      /* Check for errors. */
-      if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
-    }
-  else
-    {
-      /* Otherwise, we must wait for DRQ to get set. */
-      if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1;
-    }
+	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+		/* Here we should have been called after receiving an interrupt
+		   from the device.  DRQ should how be set. */
+		int stat_dum;
+
+		/* Check for errors. */
+		if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
+	} else {
+		/* Otherwise, we must wait for DRQ to get set. */
+		if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+			return 1;
+	}
 
-  /* Arm the interrupt handler. */
-  ide_set_handler (drive, handler, WAIT_CMD);
+	/* Arm the interrupt handler. */
+	ide_set_handler (drive, handler, WAIT_CMD);
 
-  /* Send the command to the device. */
-  cdrom_out_bytes (drive, cmd_buf, cmd_len);
+	/* Send the command to the device. */
+	cdrom_out_bytes (drive, cmd_buf, cmd_len);
 
-  return 0;
+	return 0;
 }
 
 
@@ -854,49 +836,48 @@
 static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
                                   int sectors_to_transfer)
 {
-  struct cdrom_info *info = &drive->cdrom_info;
+	struct cdrom_info *info = &drive->cdrom_info;
 
-  /* Number of sectors to read into the buffer. */
-  int sectors_to_buffer = MIN (sectors_to_transfer,
-                               (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
-                                 info->nsectors_buffered);
-
-  char *dest;
-
-  /* If we don't yet have a sector buffer, try to allocate one.
-     If we can't get one atomically, it's not fatal -- we'll just throw
-     the data away rather than caching it. */
-  if (info->sector_buffer == NULL)
-    {
-      info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, GFP_ATOMIC);
-
-      /* If we couldn't get a buffer, don't try to buffer anything... */
-      if (info->sector_buffer == NULL)
-        sectors_to_buffer = 0;
-    }
-
-  /* If this is the first sector in the buffer, remember its number. */
-  if (info->nsectors_buffered == 0)
-    info->sector_buffered = sector;
-
-  /* Read the data into the buffer. */
-  dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
-  while (sectors_to_buffer > 0)
-    {
-      cdrom_in_bytes (drive, dest, SECTOR_SIZE);
-      --sectors_to_buffer;
-      --sectors_to_transfer;
-      ++info->nsectors_buffered;
-      dest += SECTOR_SIZE;
-    }
-
-  /* Throw away any remaining data. */
-  while (sectors_to_transfer > 0)
-    {
-      char dum[SECTOR_SIZE];
-      cdrom_in_bytes (drive, dum, sizeof (dum));
-      --sectors_to_transfer;
-    }
+	/* Number of sectors to read into the buffer. */
+	int sectors_to_buffer = MIN (sectors_to_transfer,
+				     (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+				       info->nsectors_buffered);
+
+	char *dest;
+
+	/* If we don't yet have a sector buffer, try to allocate one.
+	   If we can't get one atomically, it's not fatal -- we'll just throw
+	   the data away rather than caching it. */
+	if (info->sector_buffer == NULL) {
+		info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE,
+							GFP_ATOMIC);
+
+		/* If we couldn't get a buffer,
+		   don't try to buffer anything... */
+		if (info->sector_buffer == NULL)
+			sectors_to_buffer = 0;
+	}
+
+	/* If this is the first sector in the buffer, remember its number. */
+	if (info->nsectors_buffered == 0)
+		info->sector_buffered = sector;
+
+	/* Read the data into the buffer. */
+	dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
+	while (sectors_to_buffer > 0) {
+		cdrom_in_bytes (drive, dest, SECTOR_SIZE);
+		--sectors_to_buffer;
+		--sectors_to_transfer;
+		++info->nsectors_buffered;
+		dest += SECTOR_SIZE;
+	}
+
+	/* Throw away any remaining data. */
+	while (sectors_to_transfer > 0) {
+		char dum[SECTOR_SIZE];
+		cdrom_in_bytes (drive, dum, sizeof (dum));
+		--sectors_to_transfer;
+	}
 }
 
 
@@ -908,35 +889,30 @@
 static inline
 int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
 {
-  ireason &= 3;
-  if (ireason == 2) return 0;
+	ireason &= 3;
+	if (ireason == 2) return 0;
 
-  if (ireason == 0)
-    {
-      /* Whoops... The drive is expecting to receive data from us! */
-      printk ("%s: cdrom_read_intr: "
-              "Drive wants to transfer data the wrong way!\n",
-              drive->name);
-
-      /* Throw some data at the drive so it doesn't hang
-         and quit this request. */
-      while (len > 0)
-        {
-          int dum = 0;
-	  cdrom_out_bytes (drive, &dum, sizeof (dum));
-          len -= sizeof (dum);
-        }
-    }
-
-  else
-    {
-      /* Drive wants a command packet, or invalid ireason... */
-      printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
-              drive->name, ireason);
-    }
+	if (ireason == 0) {
+		/* Whoops... The drive is expecting to receive data from us! */
+		printk ("%s: cdrom_read_intr: "
+			"Drive wants to transfer data the wrong way!\n",
+			drive->name);
+
+		/* Throw some data at the drive so it doesn't hang
+		   and quit this request. */
+		while (len > 0) {
+			int dum = 0;
+			cdrom_out_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+	} else {
+		/* Drive wants a command packet, or invalid ireason... */
+		printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+			drive->name, ireason);
+	}
 
-  cdrom_end_request (0, drive);
-  return -1;
+	cdrom_end_request (0, drive);
+	return -1;
 }
 
 
@@ -945,110 +921,106 @@
  */
 static void cdrom_read_intr (ide_drive_t *drive)
 {
-  int stat;
-  int ireason, len, sectors_to_transfer, nskip;
+	int stat;
+	int ireason, len, sectors_to_transfer, nskip;
+
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Check for errors. */
+	if (cdrom_decode_status (drive, 0, &stat)) return;
 
-  struct request *rq = HWGROUP(drive)->rq;
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* If DRQ is clear, the command has completed. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* If we're not done filling the current buffer, complain.
+		   Otherwise, complete the command normally. */
+		if (rq->current_nr_sectors > 0) {
+			printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+		} else
+			cdrom_end_request (1, drive);
 
-  /* Check for errors. */
-  if (cdrom_decode_status (drive, 0, &stat)) return;
+		return;
+	}
+
+	/* Check that the drive is expecting to do the same thing we are. */
+	if (cdrom_read_check_ireason (drive, len, ireason)) return;
+
+	/* Assume that the drive will always provide data in multiples
+	   of at least SECTOR_SIZE, as it gets hairy to keep track
+	   of the transfers otherwise. */
+	if ((len % SECTOR_SIZE) != 0) {
+		printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+			drive->name, len);
+		printk ("  This drive is not supported by this version of the driver\n");
+		cdrom_end_request (0, drive);
+		return;
+	}
 
-  /* Read the interrupt reason and the transfer length. */
-  ireason = IN_BYTE (IDE_NSECTOR_REG);
-  len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
-
-  /* If DRQ is clear, the command has completed. */
-  if ((stat & DRQ_STAT) == 0)
-    {
-      /* If we're not done filling the current buffer, complain.
-         Otherwise, complete the command normally. */
-      if (rq->current_nr_sectors > 0)
-        {
-          printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
-                  drive->name, rq->current_nr_sectors);
-          cdrom_end_request (0, drive);
-        }
-      else
-        cdrom_end_request (1, drive);
-
-      return;
-    }
-
-  /* Check that the drive is expecting to do the same thing that we are. */
-  if (cdrom_read_check_ireason (drive, len, ireason)) return;
-
-  /* Assume that the drive will always provide data in multiples of at least
-     SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */
-  if ((len % SECTOR_SIZE) != 0)
-    {
-      printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
-              drive->name, len);
-      printk ("  This drive is not supported by this version of the driver\n");
-      cdrom_end_request (0, drive);
-      return;
-    }
-
-  /* The number of sectors we need to read from the drive. */
-  sectors_to_transfer = len / SECTOR_SIZE;
-
-  /* First, figure out if we need to bit-bucket any of the leading sectors. */
-  nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
-               sectors_to_transfer);
-
-  while (nskip > 0)
-    {
-      /* We need to throw away a sector. */
-      char dum[SECTOR_SIZE];
-      cdrom_in_bytes (drive, dum, sizeof (dum));
-
-      --rq->current_nr_sectors;
-      --nskip;
-      --sectors_to_transfer;
-    }
-
-  /* Now loop while we still have data to read from the drive. */
-  while (sectors_to_transfer > 0)
-    {
-      int this_transfer;
-
-      /* If we've filled the present buffer but there's another chained
-         buffer after it, move on. */
-      if (rq->current_nr_sectors == 0 &&
-          rq->nr_sectors > 0)
-        cdrom_end_request (1, drive);
-
-      /* If the buffers are full, cache the rest of the data in our
-         internal buffer. */
-      if (rq->current_nr_sectors == 0)
-        {
-          cdrom_buffer_sectors (drive, rq->sector, sectors_to_transfer);
-          sectors_to_transfer = 0;
-        }
-      else
-        {
-          /* Transfer data to the buffers.
-             Figure out how many sectors we can transfer
-             to the current buffer. */
-          this_transfer = MIN (sectors_to_transfer,
-                               rq->current_nr_sectors);
-
-          /* Read this_transfer sectors into the current buffer. */
-          while (this_transfer > 0)
-            {
-              cdrom_in_bytes (drive, rq->buffer, SECTOR_SIZE);
-              rq->buffer += SECTOR_SIZE;
-              --rq->nr_sectors;
-              --rq->current_nr_sectors;
-              ++rq->sector;
-              --this_transfer;
-              --sectors_to_transfer;
-            }
-        }
-    }
-
-  /* Done moving data!
-     Wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
+	/* The number of sectors we need to read from the drive. */
+	sectors_to_transfer = len / SECTOR_SIZE;
+
+	/* First, figure out if we need to bit-bucket
+	   any of the leading sectors. */
+	nskip = MIN ((int)(rq->current_nr_sectors -
+			   (rq->bh->b_size >> SECTOR_BITS)),
+		     sectors_to_transfer);
+
+	while (nskip > 0) {
+		/* We need to throw away a sector. */
+		char dum[SECTOR_SIZE];
+		cdrom_in_bytes (drive, dum, sizeof (dum));
+
+		--rq->current_nr_sectors;
+		--nskip;
+		--sectors_to_transfer;
+	}
+
+	/* Now loop while we still have data to read from the drive. */
+	while (sectors_to_transfer > 0) {
+		int this_transfer;
+
+		/* If we've filled the present buffer but there's another
+		   chained buffer after it, move on. */
+		if (rq->current_nr_sectors == 0 &&
+		    rq->nr_sectors > 0)
+			cdrom_end_request (1, drive);
+
+		/* If the buffers are full, cache the rest of the data in our
+		   internal buffer. */
+		if (rq->current_nr_sectors == 0) {
+			cdrom_buffer_sectors (drive,
+					      rq->sector, sectors_to_transfer);
+			sectors_to_transfer = 0;
+		} else {
+			/* Transfer data to the buffers.
+			   Figure out how many sectors we can transfer
+			   to the current buffer. */
+			this_transfer = MIN (sectors_to_transfer,
+					     rq->current_nr_sectors);
+
+			/* Read this_transfer sectors
+			   into the current buffer. */
+			while (this_transfer > 0) {
+				cdrom_in_bytes (drive
+						, rq->buffer, SECTOR_SIZE);
+				rq->buffer += SECTOR_SIZE;
+				--rq->nr_sectors;
+				--rq->current_nr_sectors;
+				++rq->sector;
+				--this_transfer;
+				--sectors_to_transfer;
+			}
+		}
+	}
+
+	/* Done moving data!
+	   Wait for another interrupt. */
+	ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
 }
 
 
@@ -1058,56 +1030,54 @@
  */
 static int cdrom_read_from_buffer (ide_drive_t *drive)
 {
-  struct cdrom_info *info = &drive->cdrom_info;
-  struct request *rq = HWGROUP(drive)->rq;
+	struct cdrom_info *info = &drive->cdrom_info;
+	struct request *rq = HWGROUP(drive)->rq;
+
+	/* Can't do anything if there's no buffer. */
+	if (info->sector_buffer == NULL) return 0;
+
+	/* Loop while this request needs data and the next block is present
+	   in our cache. */
+	while (rq->nr_sectors > 0 &&
+	       rq->sector >= info->sector_buffered &&
+	       rq->sector < info->sector_buffered + info->nsectors_buffered) {
+		if (rq->current_nr_sectors == 0)
+			cdrom_end_request (1, drive);
+
+		memcpy (rq->buffer,
+			info->sector_buffer +
+			(rq->sector - info->sector_buffered) * SECTOR_SIZE,
+			SECTOR_SIZE);
+		rq->buffer += SECTOR_SIZE;
+		--rq->current_nr_sectors;
+		--rq->nr_sectors;
+		++rq->sector;
+	}
 
-  /* Can't do anything if there's no buffer. */
-  if (info->sector_buffer == NULL) return 0;
+	/* If we've satisfied the current request,
+	   terminate it successfully. */
+	if (rq->nr_sectors == 0) {
+		cdrom_end_request (1, drive);
+		return -1;
+	}
 
-  /* Loop while this request needs data and the next block is present
-     in our cache. */
-  while (rq->nr_sectors > 0 &&
-         rq->sector >= info->sector_buffered &&
-         rq->sector < info->sector_buffered + info->nsectors_buffered)
-    {
-      if (rq->current_nr_sectors == 0)
-        cdrom_end_request (1, drive);
-
-      memcpy (rq->buffer,
-              info->sector_buffer +
-                (rq->sector - info->sector_buffered) * SECTOR_SIZE,
-              SECTOR_SIZE);
-      rq->buffer += SECTOR_SIZE;
-      --rq->current_nr_sectors;
-      --rq->nr_sectors;
-      ++rq->sector;
-    }
-
-  /* If we've satisfied the current request, terminate it successfully. */
-  if (rq->nr_sectors == 0)
-    {
-      cdrom_end_request (1, drive);
-      return -1;
-    }
-
-  /* Move on to the next buffer if needed. */
-  if (rq->current_nr_sectors == 0)
-    cdrom_end_request (1, drive);
-
-  /* If this condition does not hold, then the kluge i use to
-     represent the number of sectors to skip at the start of a transfer
-     will fail.  I think that this will never happen, but let's be
-     paranoid and check. */
-  if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
-      (rq->sector % SECTORS_PER_FRAME) != 0)
-    {
-      printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
-              drive->name, rq->sector);
-      cdrom_end_request (0, drive);
-      return -1;
-    }
+	/* Move on to the next buffer if needed. */
+	if (rq->current_nr_sectors == 0)
+		cdrom_end_request (1, drive);
+
+	/* If this condition does not hold, then the kluge i use to
+	   represent the number of sectors to skip at the start of a transfer
+	   will fail.  I think that this will never happen, but let's be
+	   paranoid and check. */
+	if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+	    (rq->sector % SECTORS_PER_FRAME) != 0) {
+		printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+			drive->name, rq->sector);
+		cdrom_end_request (0, drive);
+		return -1;
+	}
 
-  return 0;
+	return 0;
 }
 
 
@@ -1120,62 +1090,62 @@
  */
 static void cdrom_start_read_continuation (ide_drive_t *drive)
 {
-  struct packet_command pc;
-  struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
 
-  int nsect, sector, nframes, frame, nskip;
+	int nsect, sector, nframes, frame, nskip;
 
-  /* Number of sectors to transfer. */
-  nsect = rq->nr_sectors;
+	/* Number of sectors to transfer. */
+	nsect = rq->nr_sectors;
+
+	/* Starting sector. */
+	sector = rq->sector;
+
+	/* If the requested sector doesn't start on a cdrom block boundary,
+	   we must adjust the start of the transfer so that it does,
+	   and remember to skip the first few sectors.
+	   If the CURRENT_NR_SECTORS field is larger than the size
+	   of the buffer, it will mean that we're to skip a number
+	   of sectors equal to the amount by which CURRENT_NR_SECTORS
+	   is larger than the buffer size. */
+	nskip = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0) {
+		/* Sanity check... */
+		if (rq->current_nr_sectors !=
+		    (rq->bh->b_size >> SECTOR_BITS)) {
+			printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
+				drive->name, rq->current_nr_sectors);
+			cdrom_end_request (0, drive);
+			return;
+		}
 
-  /* Starting sector. */
-  sector = rq->sector;
-
-  /* If the requested sector doesn't start on a cdrom block boundary,
-     we must adjust the start of the transfer so that it does,
-     and remember to skip the first few sectors.  If the CURRENT_NR_SECTORS
-     field is larger than the size of the buffer, it will mean that
-     we're to skip a number of sectors equal to the amount by which
-     CURRENT_NR_SECTORS is larger than the buffer size. */
-  nskip = (sector % SECTORS_PER_FRAME);
-  if (nskip > 0)
-    {
-      /* Sanity check... */
-      if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS))
-        {
-          printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
-                  drive->name, rq->current_nr_sectors);
-          cdrom_end_request (0, drive);
-          return;
-        }
-
-      sector -= nskip;
-      nsect += nskip;
-      rq->current_nr_sectors += nskip;
-    }
-
-  /* Convert from sectors to cdrom blocks, rounding up the transfer
-     length if needed. */
-  nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
-  frame = sector / SECTORS_PER_FRAME;
-
-  /* Largest number of frames was can transfer at once is 64k-1. */
-  nframes = MIN (nframes, 65535);
-
-  /* Set up the command */
-  memset (&pc.c, 0, sizeof (pc.c));
-  pc.c[0] = READ_10;
-  pc.c[7] = (nframes >> 8);
-  pc.c[8] = (nframes & 0xff);
+		sector -= nskip;
+		nsect += nskip;
+		rq->current_nr_sectors += nskip;
+	}
+
+	/* Convert from sectors to cdrom blocks, rounding up the transfer
+	   length if needed. */
+	nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+	frame = sector / SECTORS_PER_FRAME;
+
+	/* Largest number of frames was can transfer at once is 64k-1. */
+	nframes = MIN (nframes, 65535);
+
+	/* Set up the command */
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = READ_10;
+	pc.c[7] = (nframes >> 8);
+	pc.c[8] = (nframes & 0xff);
 #ifdef __alpha__
-  stl_u (htonl (frame), (unsigned int *) &pc.c[2]);
+	stl_u (htonl (frame), (unsigned int *) &pc.c[2]);
 #else
-  *(int *)(&pc.c[2]) = htonl (frame);
+	*(int *)(&pc.c[2]) = htonl (frame);
 #endif
 
-  /* Send the command to the drive and return. */
-  (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
-					&cdrom_read_intr);
+	/* Send the command to the drive and return. */
+	(void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+					      &cdrom_read_intr);
 }
 
 
@@ -1184,30 +1154,31 @@
  */
 static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
 {
-  struct request *rq = HWGROUP(drive)->rq;
-  int minor = MINOR (rq->rq_dev);
+	struct request *rq = HWGROUP(drive)->rq;
+	int minor = MINOR (rq->rq_dev);
 
-  /* If the request is relative to a partition, fix it up to refer to the
-     absolute address.  */
-  if ((minor & PARTN_MASK) != 0) {
-    rq->sector = block;
-    minor &= ~PARTN_MASK;
-    rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
-  }
-
-  /* We may be retrying this request after an error.
-     Fix up any weirdness which might be present in the request packet. */
-  restore_request (rq);
-
-  /* Satisfy whatever we can of this request from our cached sector. */
-  if (cdrom_read_from_buffer (drive))
-    return;
-
-  /* Clear the local sector buffer. */
-  drive->cdrom_info.nsectors_buffered = 0;
+	/* If the request is relative to a partition, fix it up to refer to the
+	   absolute address.  */
+	if ((minor & PARTN_MASK) != 0) {
+		rq->sector = block;
+		minor &= ~PARTN_MASK;
+		rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+	}
 
-  /* Start sending the read request to the drive. */
-  cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation);
+	/* We may be retrying this request after an error.  Fix up
+	   any weirdness which might be present in the request packet. */
+	restore_request (rq);
+
+	/* Satisfy whatever we can of this request from our cached sector. */
+	if (cdrom_read_from_buffer (drive))
+		return;
+
+	/* Clear the local sector buffer. */
+	drive->cdrom_info.nsectors_buffered = 0;
+
+	/* Start sending the read request to the drive. */
+	cdrom_start_packet_command (drive, 32768,
+				    cdrom_start_read_continuation);
 }
 
 
@@ -1227,140 +1198,135 @@
 /* Interrupt routine for packet command completion. */
 static void cdrom_pc_intr (ide_drive_t *drive)
 {
-  int ireason, len, stat, thislen;
-  struct request *rq = HWGROUP(drive)->rq;
-  struct packet_command *pc = (struct packet_command *)rq->buffer;
-
-  /* Check for errors. */
-  if (cdrom_decode_status (drive, 0, &stat)) return;
-
-  /* Read the interrupt reason and the transfer length. */
-  ireason = IN_BYTE (IDE_NSECTOR_REG);
-  len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
-
-  /* If DRQ is clear, the command has completed.
-     Complain if we still have data left to transfer. */
-  if ((stat & DRQ_STAT) == 0)
-    {
-      /* Some of the trailing request sense fields are optional, and
-	 some drives don't send them.  Sigh. */
-      if (pc->c[0] == REQUEST_SENSE && pc->buflen > 0 && pc->buflen <= 5) {
-	while (pc->buflen > 0) {
-	  *pc->buffer++ = 0;
-	  --pc->buflen;
+	int ireason, len, stat, thislen;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+	/* Check for errors. */
+	if (cdrom_decode_status (drive, 0, &stat)) return;
+
+	/* Read the interrupt reason and the transfer length. */
+	ireason = IN_BYTE (IDE_NSECTOR_REG);
+	len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+	/* If DRQ is clear, the command has completed.
+	   Complain if we still have data left to transfer. */
+	if ((stat & DRQ_STAT) == 0) {
+		/* Some of the trailing request sense fields are optional, and
+		   some drives don't send them.  Sigh. */
+		if (pc->c[0] == REQUEST_SENSE &&
+		    pc->buflen > 0 &&
+		    pc->buflen <= 5) {
+			while (pc->buflen > 0) {
+				*pc->buffer++ = 0;
+				--pc->buflen;
+			}
+		}
+
+		if (pc->buflen == 0)
+			cdrom_end_request (1, drive);
+		else {
+			printk ("%s: cdrom_pc_intr: data underrun %d\n",
+				drive->name, pc->buflen);
+			pc->stat = 1;
+			cdrom_end_request (1, drive);
+		}
+		return;
 	}
-      }
 
-      if (pc->buflen == 0)
-        cdrom_end_request (1, drive);
-      else
-        {
-          printk ("%s: cdrom_pc_intr: data underrun %d\n",
-                  drive->name, pc->buflen);
-          pc->stat = 1;
-          cdrom_end_request (1, drive);
-        }
-      return;
-    }
-
-  /* Figure out how much data to transfer. */
-  thislen = pc->buflen;
-  if (thislen < 0) thislen = -thislen;
-  if (thislen > len) thislen = len;
-
-  /* The drive wants to be written to. */
-  if ((ireason & 3) == 0)
-    {
-      /* Check that we want to write. */
-      if (pc->buflen > 0)
-        {
-          printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
-                  drive->name);
-          pc->stat = 1;
-          thislen = 0;
-        }
-
-      /* Transfer the data. */
-      cdrom_out_bytes (drive, pc->buffer, thislen);
-
-      /* If we haven't moved enough data to satisfy the drive,
-         add some padding. */
-      while (len > thislen)
-        {
-          int dum = 0;
-	  cdrom_out_bytes (drive, &dum, sizeof (dum));
-          len -= sizeof (dum);
-        }
-
-      /* Keep count of how much data we've moved. */
-      pc->buffer += thislen;
-      pc->buflen += thislen;
-    }
-
-  /* Same drill for reading. */
-  else if ((ireason & 3) == 2)
-    {
-      /* Check that we want to read. */
-      if (pc->buflen < 0)
-        {
-          printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
-                  drive->name);
-          pc->stat = 1;
-          thislen = 0;
-        }
-
-      /* Transfer the data. */
-      cdrom_in_bytes (drive, pc->buffer, thislen);
-
-      /* If we haven't moved enough data to satisfy the drive,
-         add some padding. */
-      while (len > thislen)
-        {
-          int dum = 0;
-	  cdrom_in_bytes (drive, &dum, sizeof (dum));
-          len -= sizeof (dum);
-        }
-
-      /* Keep count of how much data we've moved. */
-      pc->buffer += thislen;
-      pc->buflen -= thislen;
-    }
-
-  else
-    {
-      printk ("%s: cdrom_pc_intr: The drive appears confused (ireason = 0x%2x)\n",
-              drive->name, ireason);
-      pc->stat = 1;
-    }
+	/* Figure out how much data to transfer. */
+	thislen = pc->buflen;
+	if (thislen < 0) thislen = -thislen;
+	if (thislen > len) thislen = len;
+
+	/* The drive wants to be written to. */
+	if ((ireason & 3) == 0) {
+		/* Check that we want to write. */
+		if (pc->buflen > 0) {
+			printk ("%s: cdrom_pc_intr: Drive wants "
+				"to transfer data the wrong way!\n",
+				drive->name);
+			pc->stat = 1;
+			thislen = 0;
+		}
+
+		/* Transfer the data. */
+		cdrom_out_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			cdrom_out_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen += thislen;
+	}
 
-  /* Now we wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
+	/* Same drill for reading. */
+	else if ((ireason & 3) == 2) {
+		/* Check that we want to read. */
+		if (pc->buflen < 0) {
+			printk ("%s: cdrom_pc_intr: Drive wants to "
+				"transfer data the wrong way!\n",
+				drive->name);
+			pc->stat = 1;
+			thislen = 0;
+		}
+
+		/* Transfer the data. */
+		cdrom_in_bytes (drive, pc->buffer, thislen);
+
+		/* If we haven't moved enough data to satisfy the drive,
+		   add some padding. */
+		while (len > thislen) {
+			int dum = 0;
+			cdrom_in_bytes (drive, &dum, sizeof (dum));
+			len -= sizeof (dum);
+		}
+
+		/* Keep count of how much data we've moved. */
+		pc->buffer += thislen;
+		pc->buflen -= thislen;
+	} else {
+		printk ("%s: cdrom_pc_intr: The drive "
+			"appears confused (ireason = 0x%2x)\n",
+			drive->name, ireason);
+		pc->stat = 1;
+	}
+
+	/* Now we wait for another interrupt. */
+	ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
 }
 
 
 static void cdrom_do_pc_continuation (ide_drive_t *drive)
 {
-  struct request *rq = HWGROUP(drive)->rq;
-  struct packet_command *pc = (struct packet_command *)rq->buffer;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
 
-  /* Send the command to the drive and return. */
-  cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr);
+	/* Send the command to the drive and return. */
+	cdrom_transfer_packet_command (drive, pc->c,
+				       sizeof (pc->c), &cdrom_pc_intr);
 }
 
 
 static void cdrom_do_packet_command (ide_drive_t *drive)
 {
-  int len;
-  struct request *rq = HWGROUP(drive)->rq;
-  struct packet_command *pc = (struct packet_command *)rq->buffer;
+	int len;
+	struct request *rq = HWGROUP(drive)->rq;
+	struct packet_command *pc = (struct packet_command *)rq->buffer;
 
-  len = pc->buflen;
-  if (len < 0) len = -len;
+	len = pc->buflen;
+	if (len < 0) len = -len;
 
-  pc->stat = 0;
+	pc->stat = 0;
 
-  /* Start sending the command to the drive. */
-  cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+	/* Start sending the command to the drive. */
+	cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
 }
 
 
@@ -1369,78 +1335,73 @@
 static
 void cdrom_sleep (int time)
 {
-  current->state = TASK_INTERRUPTIBLE;
-  current->timeout = jiffies + time;
-  schedule ();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + time;
+	schedule ();
 }
 
 static
 int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
 {
-  struct atapi_request_sense my_reqbuf;
-  int retries = 10;
-  struct request req;
-
-  /* If our caller has not provided a place to stick any sense data,
-     use our own area. */
-  if (pc->sense_data == NULL)
-    pc->sense_data = &my_reqbuf;
-  pc->sense_data->sense_key = 0;
-
-  /* Start of retry loop. */
-  do {
-    ide_init_drive_cmd (&req);
-    req.cmd = PACKET_COMMAND;
-    req.buffer = (char *)pc;
-    (void) ide_do_drive_cmd (drive, &req, ide_wait);
-
-    if (pc->stat != 0)
-      {
-	/* The request failed.  Retry if it was due to a unit attention status
-	   (usually means media was changed). */
-	struct atapi_request_sense *reqbuf = pc->sense_data;
-
-	if (reqbuf->sense_key == UNIT_ATTENTION)
-	  ;
-
-	/* Also retry if the drive is in the process of loading a disk.
-	   This time, however, wait a little between retries to give
-	   the drive time. */
-	else if (reqbuf->sense_key == NOT_READY && reqbuf->asc == 4)
-	  {
-	    cdrom_sleep (HZ);
-	  }
+	struct atapi_request_sense my_reqbuf;
+	int retries = 10;
+	struct request req;
 
-	/* Otherwise, don't retry. */
-	else
-	  retries = 0;
+	/* If our caller has not provided a place to stick any sense data,
+	   use our own area. */
+	if (pc->sense_data == NULL)
+		pc->sense_data = &my_reqbuf;
+	pc->sense_data->sense_key = 0;
+
+	/* Start of retry loop. */
+	do {
+		ide_init_drive_cmd (&req);
+		req.cmd = PACKET_COMMAND;
+		req.buffer = (char *)pc;
+		(void) ide_do_drive_cmd (drive, &req, ide_wait);
+
+		if (pc->stat != 0) {
+			/* The request failed.  Retry if it was due to a unit
+			   attention status
+			   (usually means media was changed). */
+			struct atapi_request_sense *reqbuf = pc->sense_data;
+
+			if (reqbuf->sense_key == UNIT_ATTENTION)
+				;
+			else if (reqbuf->sense_key == NOT_READY &&
+				 reqbuf->asc == 4) {
+				/* The drive is in the process of loading
+				   a disk.  Retry, but wait a little to give
+				   the drive time to complete the load. */
+				cdrom_sleep (HZ);
+			} else
+				/* Otherwise, don't retry. */
+				retries = 0;
 
-	--retries;
-      }
+			--retries;
+		}
 
-    /* End of retry loop. */
-  } while (pc->stat != 0 && retries >= 0);
+		/* End of retry loop. */
+	} while (pc->stat != 0 && retries >= 0);
 
 
-  /* Return an error if the command failed. */
-  if (pc->stat != 0)
-    return -EIO;
-
-  else
-    {
-      /* The command succeeded.  If it was anything other than a request sense,
-	 eject, or door lock command, and we think that the door is presently
-	 unlocked, lock it again.  (The door was probably unlocked via
-	 an explicit CDROMEJECT ioctl.) */
-      if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
-	  (pc->c[0] != REQUEST_SENSE &&
-	   pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
-	   pc->c[0] != START_STOP))
-	{
-	  (void) cdrom_lockdoor (drive, 1, NULL);
+	/* Return an error if the command failed. */
+	if (pc->stat != 0)
+		return -EIO;
+	else {
+		/* The command succeeded.  If it was anything other than
+		   a request sense, eject, or door lock command,
+		   and we think that the door is presently, lock it again.
+		   (The door was probably unlocked via an explicit
+		   CDROMEJECT ioctl.) */
+		if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+		    (pc->c[0] != REQUEST_SENSE &&
+		     pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
+		     pc->c[0] != START_STOP)) {
+			(void) cdrom_lockdoor (drive, 1, NULL);
+		}
+		return 0;
 	}
-      return 0;
-    }
 }
 
 
@@ -1450,25 +1411,19 @@
 
 void ide_do_rw_cdrom (ide_drive_t *drive, unsigned long block)
 {
-  struct request *rq = HWGROUP(drive)->rq;
-
-  if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
-    cdrom_do_packet_command (drive);
+	struct request *rq = HWGROUP(drive)->rq;
 
-  else if (rq -> cmd == RESET_DRIVE_COMMAND)
-    {
-      cdrom_end_request (1, drive);
-      ide_do_reset (drive);
-      return;
-    }
-
-  else if (rq -> cmd != READ)
-    {
-      printk ("ide-cd: bad cmd %d\n", rq -> cmd);
-      cdrom_end_request (0, drive);
-    }
-  else
-    cdrom_start_read (drive, block);
+	if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
+		cdrom_do_packet_command (drive);
+	else if (rq -> cmd == RESET_DRIVE_COMMAND) {
+		cdrom_end_request (1, drive);
+		ide_do_reset (drive);
+		return;
+	} else if (rq -> cmd != READ) {
+		printk ("ide-cd: bad cmd %d\n", rq -> cmd);
+		cdrom_end_request (0, drive);
+	} else
+		cdrom_start_read (drive, block);
 }
 
 
@@ -1484,37 +1439,46 @@
  */
 
 #if ! STANDARD_ATAPI
-static
+static inline
 int bin2bcd (int x)
 {
-  return (x%10) | ((x/10) << 4);
+	return (x%10) | ((x/10) << 4);
 }
 
 
-static
+static inline
 int bcd2bin (int x)
 {
-  return (x >> 4) * 10 + (x & 0x0f);
+	return (x >> 4) * 10 + (x & 0x0f);
 }
+
+static
+void msf_from_bcd (struct atapi_msf *msf)
+{
+	msf->minute = bcd2bin (msf->minute);
+	msf->second = bcd2bin (msf->second);
+	msf->frame  = bcd2bin (msf->frame);
+}
+
 #endif /* not STANDARD_ATAPI */
 
 
 static inline
 void lba_to_msf (int lba, byte *m, byte *s, byte *f)
 {
-  lba += CD_BLOCK_OFFSET;
-  lba &= 0xffffff;  /* negative lbas use only 24 bits */
-  *m = lba / (CD_SECS * CD_FRAMES);
-  lba %= (CD_SECS * CD_FRAMES);
-  *s = lba / CD_FRAMES;
-  *f = lba % CD_FRAMES;
+	lba += CD_BLOCK_OFFSET;
+	lba &= 0xffffff;  /* negative lbas use only 24 bits */
+	*m = lba / (CD_SECS * CD_FRAMES);
+	lba %= (CD_SECS * CD_FRAMES);
+	*s = lba / CD_FRAMES;
+	*f = lba % CD_FRAMES;
 }
 
 
 static inline
 int msf_to_lba (byte m, byte s, byte f)
 {
-  return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
 }
 
 
@@ -1522,14 +1486,14 @@
 cdrom_check_status (ide_drive_t  *drive,
 		    struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
+	memset (&pc, 0, sizeof (pc));
 
-  pc.sense_data = reqbuf;
-  pc.c[0] = TEST_UNIT_READY;
+	pc.sense_data = reqbuf;
+	pc.c[0] = TEST_UNIT_READY;
 
-  return cdrom_queue_packet_command (drive, &pc);
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1538,40 +1502,39 @@
 cdrom_lockdoor (ide_drive_t *drive, int lockflag,
 		struct atapi_request_sense *reqbuf)
 {
-  struct atapi_request_sense my_reqbuf;
-  int stat;
-  struct packet_command pc;
-
-  if (reqbuf == NULL)
-    reqbuf = &my_reqbuf;
-
-  /* If the drive cannot lock the door, just pretend. */
-  if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
-    stat = 0;
-  else
-    {
-      memset (&pc, 0, sizeof (pc));
-      pc.sense_data = reqbuf;
-
-      pc.c[0] = ALLOW_MEDIUM_REMOVAL;
-      pc.c[4] = (lockflag != 0);
-      stat = cdrom_queue_packet_command (drive, &pc);
-    }
-
-  if (stat == 0)
-    CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
-  else
-    {
-      /* If we got an illegal field error, the drive
-	 probably cannot lock the door. */
-      if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24)
-	{
-	  printk ("%s: door locking not supported\n", drive->name);
-	  CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
-	  stat = 0;
-	  CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+	struct atapi_request_sense my_reqbuf;
+	int stat;
+	struct packet_command pc;
+
+	if (reqbuf == NULL)
+		reqbuf = &my_reqbuf;
+
+	/* If the drive cannot lock the door, just pretend. */
+	if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+		stat = 0;
+	else {
+		memset (&pc, 0, sizeof (pc));
+		pc.sense_data = reqbuf;
+
+		pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+		pc.c[4] = (lockflag != 0);
+		stat = cdrom_queue_packet_command (drive, &pc);
+	}
+
+	if (stat == 0)
+		CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+	else {
+		/* If we got an illegal field error, the drive
+		   probably cannot lock the door. */
+		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+		    reqbuf->asc == 0x24) {
+			printk ("%s: door locking not supported\n",
+				drive->name);
+			CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+			stat = 0;
+			CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+		}
 	}
-    }
   return stat;
 }
 
@@ -1582,14 +1545,14 @@
 cdrom_eject (ide_drive_t *drive, int ejectflag,
 	     struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.c[0] = START_STOP;
-  pc.c[4] = 2 + (ejectflag != 0);
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.c[0] = START_STOP;
+	pc.c[4] = 2 + (ejectflag != 0);
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1597,14 +1560,14 @@
 cdrom_pause (ide_drive_t *drive, int pauseflag,
 	     struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.c[0] = SCMD_PAUSE_RESUME;
-  pc.c[8] = !pauseflag;
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.c[0] = SCMD_PAUSE_RESUME;
+	pc.c[8] = !pauseflag;
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1612,15 +1575,15 @@
 cdrom_startstop (ide_drive_t *drive, int startflag,
 		 struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.c[0] = START_STOP;
-  pc.c[1] = 1;
-  pc.c[4] = startflag;
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.c[0] = START_STOP;
+	pc.c[1] = 1;
+	pc.c[4] = startflag;
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1628,28 +1591,26 @@
 cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity,
 		     struct atapi_request_sense *reqbuf)
 {
-  struct {
-    unsigned lba;
-    unsigned blocklen;
-  } capbuf;
-
-  int stat;
-  struct packet_command pc;
-
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
-
-  pc.c[0] = READ_CAPACITY;
-  pc.buffer = (char *)&capbuf;
-  pc.buflen = sizeof (capbuf);
-
-  stat = cdrom_queue_packet_command (drive, &pc);
-  if (stat == 0)
-    {
-      *capacity = ntohl (capbuf.lba);
-    }
+	struct {
+		unsigned lba;
+		unsigned blocklen;
+	} capbuf;
 
-  return stat;
+	int stat;
+	struct packet_command pc;
+
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
+
+	pc.c[0] = READ_CAPACITY;
+	pc.buffer = (char *)&capbuf;
+	pc.buflen = sizeof (capbuf);
+
+	stat = cdrom_queue_packet_command (drive, &pc);
+	if (stat == 0)
+		*capacity = ntohl (capbuf.lba);
+
+	return stat;
 }
 
 
@@ -1658,20 +1619,20 @@
                      int format, char *buf, int buflen,
 		     struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.buffer =  buf;
-  pc.buflen = buflen;
-  pc.c[0] = SCMD_READ_TOC;
-  pc.c[6] = trackno;
-  pc.c[7] = (buflen >> 8);
-  pc.c[8] = (buflen & 0xff);
-  pc.c[9] = (format << 6);
-  if (msf_flag) pc.c[1] = 2;
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.buffer =  buf;
+	pc.buflen = buflen;
+	pc.c[0] = SCMD_READ_TOC;
+	pc.c[6] = trackno;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	pc.c[9] = (format << 6);
+	if (msf_flag) pc.c[1] = 2;
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1680,149 +1641,130 @@
 cdrom_read_toc (ide_drive_t *drive,
 		struct atapi_request_sense *reqbuf)
 {
-  int msf_flag;
-  int stat, ntracks, i;
-  struct atapi_toc *toc = drive->cdrom_info.toc;
-  struct {
-    struct atapi_toc_header hdr;
-    struct atapi_toc_entry  ent;
-  } ms_tmp;
-
-  if (toc == NULL)
-    {
-      /* Try to allocate space. */
-      toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
-                                          GFP_KERNEL);
-      drive->cdrom_info.toc = toc;
-    }
-
-  if (toc == NULL)
-    {
-      printk ("%s: No cdrom TOC buffer!\n", drive->name);
-      return -EIO;
-    }
-
-  /* Check to see if the existing data is still valid.
-     If it is, just return. */
-  if (CDROM_STATE_FLAGS (drive)->toc_valid)
-    (void) cdrom_check_status (drive, NULL);
-
-  if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
-
-#if STANDARD_ATAPI
-  msf_flag = 0;
-#else  /* not STANDARD_ATAPI */
-  /* Some drives can't return TOC data in LBA format. */
-  msf_flag = (CDROM_CONFIG_FLAGS (drive)->no_lba_toc);
-#endif  /* not STANDARD_ATAPI */
-
-  /* First read just the header, so we know how long the TOC is. */
-  stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
-                              sizeof (struct atapi_toc_header) +
-                              sizeof (struct atapi_toc_entry),
-			      reqbuf);
-  if (stat) return stat;
+	int stat, ntracks, i;
+	struct atapi_toc *toc = drive->cdrom_info.toc;
+	struct {
+		struct atapi_toc_header hdr;
+		struct atapi_toc_entry  ent;
+	} ms_tmp;
+
+	if (toc == NULL) {
+		/* Try to allocate space. */
+		toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+						    GFP_KERNEL);
+		drive->cdrom_info.toc = toc;
+	}
 
-#if ! STANDARD_ATAPI
-  if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
-    {
-      toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
-      toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
-      /* hopefully the length is not BCD, too ;-| */
-    }
-#endif  /* not STANDARD_ATAPI */
+	if (toc == NULL) {
+		printk ("%s: No cdrom TOC buffer!\n", drive->name);
+		return -EIO;
+	}
 
-  ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
-  if (ntracks <= 0) return -EIO;
-  if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
-
-  /* Now read the whole schmeer. */
-  stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
-                              sizeof (struct atapi_toc_header) +
-                              (ntracks+1) * sizeof (struct atapi_toc_entry),
-			      reqbuf);
-  if (stat) return stat;
-  toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+	/* Check to see if the existing data is still valid.
+	   If it is, just return. */
+	if (CDROM_STATE_FLAGS (drive)->toc_valid)
+		(void) cdrom_check_status (drive, NULL);
+
+	if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+	/* First read just the header, so we know how long the TOC is. */
+	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+				    sizeof (struct atapi_toc_header) +
+				    sizeof (struct atapi_toc_entry),
+				    reqbuf);
+	if (stat) return stat;
 
 #if ! STANDARD_ATAPI
-  if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
-    {
-      toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
-      toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
-      /* hopefully the length is not BCD, too ;-| */
-    }
+	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
+	}
 #endif  /* not STANDARD_ATAPI */
 
-  for (i=0; i<=ntracks; i++)
-    {
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (ntracks <= 0) return -EIO;
+	if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+	/* Now read the whole schmeer. */
+	stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+				    sizeof (struct atapi_toc_header) +
+				    (ntracks+1) *
+				      sizeof (struct atapi_toc_entry),
+				    reqbuf);
+	if (stat) return stat;
+	toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
 #if ! STANDARD_ATAPI
-      if (msf_flag)
-	{
-	  if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
-	    {
-	      toc->ent[i].track = bcd2bin (toc->ent[i].track);
-	      toc->ent[i].addr.msf.m = bcd2bin (toc->ent[i].addr.msf.m);
-	      toc->ent[i].addr.msf.s = bcd2bin (toc->ent[i].addr.msf.s);
-	      toc->ent[i].addr.msf.f = bcd2bin (toc->ent[i].addr.msf.f);
-	    }
-	  toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.m,
-					     toc->ent[i].addr.msf.s,
-					     toc->ent[i].addr.msf.f);
+	if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+		toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+		toc->hdr.last_track  = bcd2bin (toc->hdr.last_track);
 	}
-      else
 #endif  /* not STANDARD_ATAPI */
-	toc->ent[i].addr.lba = ntohl (toc->ent[i].addr.lba);
-    }
 
-  /* Read the multisession information. */
-  stat = cdrom_read_tocentry (drive, 0, msf_flag, 1,
-			      (char *)&ms_tmp, sizeof (ms_tmp),
-			      reqbuf);
-  if (stat) return stat;
+	for (i=0; i<=ntracks; i++) {
 #if ! STANDARD_ATAPI
-  if (msf_flag)
-    toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.m,
-					ms_tmp.ent.addr.msf.s,
-					ms_tmp.ent.addr.msf.f);
-  else
+		if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) {
+			if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd)
+				toc->ent[i].track = bcd2bin (toc->ent[i].track);
+			msf_from_bcd (&toc->ent[i].addr.msf);
+		}
 #endif  /* not STANDARD_ATAPI */
-    toc->last_session_lba = ntohl (ms_tmp.ent.addr.lba);
+		toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+						   toc->ent[i].addr.msf.second,
+						   toc->ent[i].addr.msf.frame);
+	}
 
-  toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+	/* Read the multisession information. */
+	stat = cdrom_read_tocentry (drive, 0, 1, 1,
+				    (char *)&ms_tmp, sizeof (ms_tmp),
+				    reqbuf);
+	if (stat) return stat;
 
-  /* Now try to get the total cdrom capacity. */
-  stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
-  if (stat) toc->capacity = 0x1fffff;
+#if ! STANDARD_ATAPI
+	if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+		msf_from_bcd (&ms_tmp.ent.addr.msf);
+#endif  /* not STANDARD_ATAPI */
 
-  HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
-    = toc->capacity * SECTORS_PER_FRAME;
-  drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+	toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute,
+					    ms_tmp.ent.addr.msf.second,
+					    ms_tmp.ent.addr.msf.frame);
+
+	toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+	/* Now try to get the total cdrom capacity. */
+	stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
+	if (stat) toc->capacity = 0x1fffff;
+
+	HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
+		= toc->capacity * SECTORS_PER_FRAME;
+	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
 
-  /* Remember that we've read this stuff. */
-  CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+	/* Remember that we've read this stuff. */
+	CDROM_STATE_FLAGS (drive)->toc_valid = 1;
 
-  return 0;
+	return 0;
 }
 
 
 static int
-cdrom_read_subchannel (ide_drive_t *drive,
+cdrom_read_subchannel (ide_drive_t *drive, int format,
                        char *buf, int buflen,
 		       struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.buffer =  buf;
-  pc.buflen = buflen;
-  pc.c[0] = SCMD_READ_SUBCHANNEL;
-  pc.c[2] = 0x40;  /* request subQ data */
-  pc.c[3] = 0x01;  /* Format 1: current position */
-  pc.c[7] = (buflen >> 8);
-  pc.c[8] = (buflen & 0xff);
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.buffer =  buf;
+	pc.buflen = buflen;
+	pc.c[0] = SCMD_READ_SUBCHANNEL;
+	pc.c[1] = 2;     /* MSF addressing */
+	pc.c[2] = 0x40;  /* request subQ data */
+	pc.c[3] = format,
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1832,18 +1774,18 @@
                   char *buf, int buflen,
 		  struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.buffer =  buf;
-  pc.buflen = buflen;
-  pc.c[0] = MODE_SENSE_10;
-  pc.c[2] = pageno | (modeflag << 6);
-  pc.c[7] = (buflen >> 8);
-  pc.c[8] = (buflen & 0xff);
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.buffer =  buf;
+	pc.buflen = buflen;
+	pc.c[0] = MODE_SENSE_10;
+	pc.c[2] = pageno | (modeflag << 6);
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1851,120 +1793,47 @@
 cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
 		   struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
-
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
-
-  pc.buffer =  buf;
-  pc.buflen = - buflen;
-  pc.c[0] = MODE_SELECT_10;
-  pc.c[1] = 0x10;
-  pc.c[2] = pageno;
-  pc.c[7] = (buflen >> 8);
-  pc.c[8] = (buflen & 0xff);
-  return cdrom_queue_packet_command (drive, &pc);
-}
-
-
-static int
-cdrom_play_lba_range_play12 (ide_drive_t *drive, int lba_start, int lba_end,
-			     struct atapi_request_sense *reqbuf)
-{
-  struct packet_command pc;
+	struct packet_command pc;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
-
-  pc.c[0] = SCMD_PLAYAUDIO12;
-#ifdef __alpha__
-  stq_u(((long) htonl (lba_end - lba_start) << 32) | htonl(lba_start),
-	(unsigned long *) &pc.c[2]);
-#else
-  *(int *)(&pc.c[2]) = htonl (lba_start);
-  *(int *)(&pc.c[6]) = htonl (lba_end - lba_start);
-#endif
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  return cdrom_queue_packet_command (drive, &pc);
+	pc.buffer =  buf;
+	pc.buflen = - buflen;
+	pc.c[0] = MODE_SELECT_10;
+	pc.c[1] = 0x10;
+	pc.c[2] = pageno;
+	pc.c[7] = (buflen >> 8);
+	pc.c[8] = (buflen & 0xff);
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
-#if !  STANDARD_ATAPI
-static int
-cdrom_play_lba_range_msf (ide_drive_t *drive, int lba_start, int lba_end,
-			  struct atapi_request_sense *reqbuf)
-{
-  struct packet_command pc;
-
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
-
-  pc.c[0] = SCMD_PLAYAUDIO_MSF;
-  lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
-  lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
-
-  if (CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd)
-    {
-      pc.c[3] = bin2bcd (pc.c[3]);
-      pc.c[4] = bin2bcd (pc.c[4]);
-      pc.c[5] = bin2bcd (pc.c[5]);
-      pc.c[6] = bin2bcd (pc.c[6]);
-      pc.c[7] = bin2bcd (pc.c[7]);
-      pc.c[8] = bin2bcd (pc.c[8]);
-    }
-
-  return cdrom_queue_packet_command (drive, &pc);
-}
-#endif  /* not STANDARD_ATAPI */
-
-
 static int
 cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
-			struct atapi_request_sense *reqbuf)
+			    struct atapi_request_sense *reqbuf)
 {
-  /* This is rather annoying.
-     My NEC-260 won't recognize group 5 commands such as PLAYAUDIO12;
-     the only way to get it to play more than 64k of blocks at once
-     seems to be the PLAYAUDIO_MSF command.  However, the parameters
-     the NEC 260 wants for the PLAYMSF command are incompatible with
-     the new version of the spec.
-
-     So what i'll try is this.  First try for PLAYAUDIO12.  If it works,
-     great.  Otherwise, if the drive reports an illegal command code,
-     try PLAYAUDIO_MSF using the NEC 260-style bcd parameters. */
+	struct packet_command pc;
 
-#if ! STANDARD_ATAPI
-  if (CDROM_CONFIG_FLAGS (drive)->no_playaudio12)
-    return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
-  else
-#endif  /* not STANDARD_ATAPI */
-    {
-      int stat;
-      struct atapi_request_sense my_reqbuf;
-
-      if (reqbuf == NULL)
-	reqbuf = &my_reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-      stat = cdrom_play_lba_range_play12 (drive, lba_start, lba_end, reqbuf);
-      if (stat == 0) return 0;
+	pc.c[0] = SCMD_PLAYAUDIO_MSF;
+	lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+	lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
 
 #if ! STANDARD_ATAPI
-      /* It failed.  Try to find out why. */
-      if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20)
-        {
-          /* The drive didn't recognize the command.
-             Retry with the MSF variant. */
-          printk ("%s: Drive does not support PLAYAUDIO12; "
-                  "trying PLAYAUDIO_MSF\n", drive->name);
-          CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
-          CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
-          return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
-        }
-#endif  /* not STANDARD_ATAPI */
+	if (CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd) {
+		pc.c[3] = bin2bcd (pc.c[3]);
+		pc.c[4] = bin2bcd (pc.c[4]);
+		pc.c[5] = bin2bcd (pc.c[5]);
+		pc.c[6] = bin2bcd (pc.c[6]);
+		pc.c[7] = bin2bcd (pc.c[7]);
+		pc.c[8] = bin2bcd (pc.c[8]);
+	}
+#endif /* not STANDARD_ATAPI */
 
-      /* Failed for some other reason.  Give up. */
-      return stat;
-    }
+	return cdrom_queue_packet_command (drive, &pc);
 }
 
 
@@ -1974,29 +1843,31 @@
 cdrom_play_lba_range (ide_drive_t *drive, int lba_start, int lba_end,
 		      struct atapi_request_sense *reqbuf)
 {
-  int i, stat;
-  struct atapi_request_sense my_reqbuf;
+	int i, stat;
+	struct atapi_request_sense my_reqbuf;
 
-  if (reqbuf == NULL)
-    reqbuf = &my_reqbuf;
+	if (reqbuf == NULL)
+		reqbuf = &my_reqbuf;
 
-  /* Some drives, will, for certain audio cds,
-     give an error if you ask them to play the entire cd using the
-     values which are returned in the TOC.  The play will succeed, however,
-     if the ending address is adjusted downwards by a few frames. */
-  for (i=0; i<75; i++)
-    {
-      stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end, reqbuf);
+	/* Some drives, will, for certain audio cds,
+	   give an error if you ask them to play the entire cd using the
+	   values which are returned in the TOC.  The play will succeed,
+	   however, if the ending address is adjusted downwards
+	   by a few frames. */
+	for (i=0; i<75; i++) {
+		stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end,
+					       reqbuf);
+
+		if (stat == 0 ||
+		    !(reqbuf->sense_key == ILLEGAL_REQUEST &&
+		      reqbuf->asc == 0x24))
+			return stat;
 
-      if (stat == 0 ||
-          !(reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24))
-	return stat;
-
-      --lba_end;
-      if (lba_end <= lba_start) break;
-    }
+		--lba_end;
+		if (lba_end <= lba_start) break;
+	}
 
-  return stat;
+	return stat;
 }
 
 
@@ -2005,26 +1876,26 @@
                          struct atapi_toc_entry **ent,
 			 struct atapi_request_sense *reqbuf)
 {
-  int stat, ntracks;
-  struct atapi_toc *toc;
+	int stat, ntracks;
+	struct atapi_toc *toc;
 
-  /* Make sure our saved TOC is valid. */
-  stat = cdrom_read_toc (drive, reqbuf);
-  if (stat) return stat;
-
-  toc = drive->cdrom_info.toc;
-
-  /* Check validity of requested track number. */
-  ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
-  if (track == CDROM_LEADOUT)
-    *ent = &toc->ent[ntracks];
-  else if (track < toc->hdr.first_track ||
-           track > toc->hdr.last_track)
-    return -EINVAL;
-  else
-    *ent = &toc->ent[track - toc->hdr.first_track];
+	/* Make sure our saved TOC is valid. */
+	stat = cdrom_read_toc (drive, reqbuf);
+	if (stat) return stat;
+
+	toc = drive->cdrom_info.toc;
 
-  return 0;
+	/* Check validity of requested track number. */
+	ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+	if (track == CDROM_LEADOUT)
+		*ent = &toc->ent[ntracks];
+	else if (track < toc->hdr.first_track ||
+		 track > toc->hdr.last_track)
+		return -EINVAL;
+	else
+		*ent = &toc->ent[track - toc->hdr.first_track];
+
+	return 0;
 }
 
 
@@ -2033,513 +1904,536 @@
 		  char *buf, int buflen,
 		  struct atapi_request_sense *reqbuf)
 {
-  struct packet_command pc;
-  struct atapi_request_sense my_reqbuf;
-  int stat;
+	struct packet_command pc;
+	struct atapi_request_sense my_reqbuf;
+	int stat;
 
-  if (reqbuf == NULL)
-    reqbuf = &my_reqbuf;
+	if (reqbuf == NULL)
+		reqbuf = &my_reqbuf;
 
-  memset (&pc, 0, sizeof (pc));
-  pc.sense_data = reqbuf;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
 
-  pc.buffer = buf;
-  pc.buflen = buflen;
+	pc.buffer = buf;
+	pc.buflen = buflen;
 
 #if ! STANDARD_ATAPI
-  if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
-    pc.c[0] = 0xd4;
-  else
+	if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
+		pc.c[0] = 0xd4;
+	else
 #endif  /* not STANDARD_ATAPI */
-    pc.c[0] = READ_CD;
+		pc.c[0] = READ_CD;
 
-  pc.c[1] = (format << 2);
+	pc.c[1] = (format << 2);
 #ifdef __alpha__
-  stl_u(htonl (lba), (unsigned int *) &pc.c[2]);
+	stl_u(htonl (lba), (unsigned int *) &pc.c[2]);
 #else
-  *(int *)(&pc.c[2]) = htonl (lba);
+	*(int *)(&pc.c[2]) = htonl (lba);
 #endif
-  pc.c[8] = 1;  /* one block */
-  pc.c[9] = 0x10;
+	pc.c[8] = 1;  /* one block */
+	pc.c[9] = 0x10;
 
-  stat = cdrom_queue_packet_command (drive, &pc);
+	stat = cdrom_queue_packet_command (drive, &pc);
 
 #if ! STANDARD_ATAPI
-  /* If the drive doesn't recognize the READ CD opcode, retry the command
-     with an older opcode for that command. */
-  if (stat && reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20 &&
-      CDROM_CONFIG_FLAGS (drive)->old_readcd == 0)
-    {
-      printk ("%s: Drive does not recognize READ_CD; trying opcode 0xd4\n",
-	      drive->name);
-      CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
-      return cdrom_read_block (drive, format, lba, buf, buflen, reqbuf);
-    }
+	/* If the drive doesn't recognize the READ CD opcode, retry the command
+	   with an older opcode for that command. */
+	if (stat && reqbuf->sense_key == ILLEGAL_REQUEST &&
+	    reqbuf->asc == 0x20 &&
+	    CDROM_CONFIG_FLAGS (drive)->old_readcd == 0) {
+		printk ("%s: Drive does not recognize READ_CD;"
+			"trying opcode 0xd4\n",
+			drive->name);
+		CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
+		return cdrom_read_block (drive, format, lba, buf, buflen,
+					 reqbuf);
+	}
 #endif  /* not STANDARD_ATAPI */
 
-  return stat;
+	return stat;
 }
 
 
 int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
 		     struct file *file, unsigned int cmd, unsigned long arg)
 {
-  switch (cmd)
-    {
-    case CDROMEJECT:
-      {
-	int stat;
+	switch (cmd) {
+	case CDROMEJECT: {
+		int stat;
 
-	if (drive->usage > 1)
-	  return -EBUSY;
+		if (drive->usage > 1)
+			return -EBUSY;
 
-	stat = cdrom_lockdoor (drive, 0, NULL);
-	if (stat) return stat;
+		stat = cdrom_lockdoor (drive, 0, NULL);
+		if (stat) return stat;
 
-	return cdrom_eject (drive, 0, NULL);
-      }
+		return cdrom_eject (drive, 0, NULL);
+	}
 
-    case CDROMEJECT_SW:
-      {
-	CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
-	return 0;
-      }
+	case CDROMCLOSETRAY: {
+		int stat;
+		if (drive->usage > 1)
+			return -EBUSY;
 
-    case CDROMCLOSETRAY:
-      {
-        if (drive->usage > 1)
-          return -EBUSY;
+		stat = cdrom_eject (drive, 1, NULL);
+		if (stat) return stat;
 
-        return cdrom_eject (drive, 1, NULL);
-      }
+		return cdrom_lockdoor (drive, 1, NULL);
+	}
 
-    case CDROMPAUSE:
-      return cdrom_pause (drive, 1, NULL);
+	case CDROMEJECT_SW: {
+		CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
+		return 0;
+	}
 
-    case CDROMRESUME:
-      return cdrom_pause (drive, 0, NULL);
+	case CDROMPAUSE:
+		return cdrom_pause (drive, 1, NULL);
 
-    case CDROMSTART:
-      return cdrom_startstop (drive, 1, NULL);
+	case CDROMRESUME:
+		return cdrom_pause (drive, 0, NULL);
 
-    case CDROMSTOP:
-      {
-	int stat;
+	case CDROMSTART:
+		return cdrom_startstop (drive, 1, NULL);
 
-	stat = cdrom_startstop (drive, 0, NULL);
-	if (stat) return stat;
-	/* pit says the Dolphin needs this. */
-	return cdrom_eject (drive, 1, NULL);
+	case CDROMSTOP: {
+		int stat;
+
+		stat = cdrom_startstop (drive, 0, NULL);
+		if (stat) return stat;
+		/* pit says the Dolphin needs this. */
+		return cdrom_eject (drive, 1, NULL);
       }
 
-    case CDROMPLAYMSF:
-      {
-        struct cdrom_msf msf;
-        int stat, lba_start, lba_end;
+	case CDROMPLAYMSF: {
+		struct cdrom_msf msf;
+		int stat, lba_start, lba_end;
 
-        stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
-        if (stat) return stat;
+		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+		if (stat) return stat;
 
-        memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
+		memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
 
-        lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
-                                msf.cdmsf_frame0);
-        lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
-                              msf.cdmsf_frame1) + 1;
+		lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
+					msf.cdmsf_frame0);
+		lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
+				      msf.cdmsf_frame1) + 1;
 
-        if (lba_end <= lba_start) return -EINVAL;
+		if (lba_end <= lba_start) return -EINVAL;
 
-        return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
-      }
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+	}
 
-    /* Like just about every other Linux cdrom driver, we ignore the
-       index part of the request here. */
-    case CDROMPLAYTRKIND:
-      {
-        int stat, lba_start, lba_end;
-        struct cdrom_ti ti;
-        struct atapi_toc_entry *first_toc, *last_toc;
-
-        stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
-        if (stat) return stat;
-
-        memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
-
-        stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc, NULL);
-        if (stat) return stat;
-        stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc, NULL);
-        if (stat) return stat;
-
-        if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
-        lba_start = first_toc->addr.lba;
-        lba_end   = last_toc->addr.lba;
+	/* Like just about every other Linux cdrom driver, we ignore the
+	   index part of the request here. */
+	case CDROMPLAYTRKIND: {
+		int stat, lba_start, lba_end;
+		struct cdrom_ti ti;
+		struct atapi_toc_entry *first_toc, *last_toc;
+
+		stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+		if (stat) return stat;
+
+		memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
+
+		stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc,
+					    NULL);
+		if (stat) return stat;
+		stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc,
+					    NULL);
+		if (stat) return stat;
+
+		if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
 
-        if (lba_end <= lba_start) return -EINVAL;
+		if (lba_end <= lba_start) return -EINVAL;
 
-        return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
-      }
+		return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+	}
 
-    case CDROMREADTOCHDR:
-      {
-        int stat;
-        struct cdrom_tochdr tochdr;
-        struct atapi_toc *toc;
-
-        stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tochdr));
-        if (stat) return stat;
-
-        /* Make sure our saved TOC is valid. */
-        stat = cdrom_read_toc (drive, NULL);
-        if (stat) return stat;
-
-        toc = drive->cdrom_info.toc;
-        tochdr.cdth_trk0 = toc->hdr.first_track;
-        tochdr.cdth_trk1 = toc->hdr.last_track;
+	case CDROMREADTOCHDR: {
+		int stat;
+		struct cdrom_tochdr tochdr;
+		struct atapi_toc *toc;
+
+		stat = verify_area (VERIFY_WRITE, (void *) arg,
+				    sizeof (tochdr));
+		if (stat) return stat;
+
+		/* Make sure our saved TOC is valid. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
+
+		toc = drive->cdrom_info.toc;
+		tochdr.cdth_trk0 = toc->hdr.first_track;
+		tochdr.cdth_trk1 = toc->hdr.last_track;
 
-        memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
+		memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
 
-        return stat;
-      }
+		return stat;
+	}
 
-    case CDROMREADTOCENTRY:
-      {
-        int stat;
-        struct cdrom_tocentry tocentry;
-        struct atapi_toc_entry *toce;
-
-        stat = verify_area (VERIFY_READ, (void *) arg, sizeof (tocentry));
-        if (stat) return stat;
-        stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tocentry));
-        if (stat) return stat;
-
-        memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
-
-        stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce, NULL);
-        if (stat) return stat;
-
-        tocentry.cdte_ctrl = toce->control;
-        tocentry.cdte_adr  = toce->adr;
-
-        if (tocentry.cdte_format == CDROM_MSF)
-          {
-            /* convert to MSF */
-            lba_to_msf (toce->addr.lba,
-                        &tocentry.cdte_addr.msf.minute,
-                        &tocentry.cdte_addr.msf.second,
-                        &tocentry.cdte_addr.msf.frame);
-          }
-        else
-          tocentry.cdte_addr.lba = toce->addr.lba;
+	case CDROMREADTOCENTRY: {
+		int stat;
+		struct cdrom_tocentry tocentry;
+		struct atapi_toc_entry *toce;
+
+		stat = verify_area (VERIFY_READ, (void *) arg,
+				    sizeof (tocentry));
+		if (stat) return stat;
+		stat = verify_area (VERIFY_WRITE, (void *) arg,
+				    sizeof (tocentry));
+		if (stat) return stat;
+
+		memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
+
+		stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce,
+					    NULL);
+		if (stat) return stat;
+
+		tocentry.cdte_ctrl = toce->control;
+		tocentry.cdte_adr  = toce->adr;
+
+		if (tocentry.cdte_format == CDROM_MSF) {
+			/* convert to MSF */
+			lba_to_msf (toce->addr.lba,
+				    &tocentry.cdte_addr.msf.minute,
+				    &tocentry.cdte_addr.msf.second,
+				    &tocentry.cdte_addr.msf.frame);
+		} else
+			tocentry.cdte_addr.lba = toce->addr.lba;
 
-        memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
+		memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
 
-        return stat;
-      }
+		return stat;
+	}
 
-    case CDROMSUBCHNL:
-      {
-        struct atapi_cdrom_subchnl scbuf;
-        int stat, abs_lba, rel_lba;
-        struct cdrom_subchnl subchnl;
-
-        stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (subchnl));
-        if (stat) return stat;
-        stat = verify_area (VERIFY_READ, (void *) arg, sizeof (subchnl));
-        if (stat) return stat;
-
-        memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
-
-        stat = cdrom_read_subchannel (drive, (char *)&scbuf, sizeof (scbuf),
-				      NULL);
-        if (stat) return stat;
+	case CDROMSUBCHNL: {
+		struct atapi_cdrom_subchnl scbuf;
+		int stat, msf_flag;
+		struct cdrom_subchnl subchnl;
+
+		stat = verify_area (VERIFY_WRITE, (void *) arg,
+				    sizeof (subchnl));
+		if (stat) return stat;
+		stat = verify_area (VERIFY_READ, (void *) arg,
+				    sizeof (subchnl));
+		if (stat) return stat;
+
+		memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
+
+		stat = cdrom_read_subchannel (drive, 1, /* current position */
+					      (char *)&scbuf, sizeof (scbuf),
+					      NULL);
+		if (stat) return stat;
 
 #if ! STANDARD_ATAPI
-	if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
-	  {
-	    abs_lba = msf_to_lba (bcd2bin (scbuf.acdsc_absaddr.msf.minute),
-				  bcd2bin (scbuf.acdsc_absaddr.msf.second),
-				  bcd2bin (scbuf.acdsc_absaddr.msf.frame));
-	    rel_lba = msf_to_lba (bcd2bin (scbuf.acdsc_reladdr.msf.minute),
-				  bcd2bin (scbuf.acdsc_reladdr.msf.second),
-				  bcd2bin (scbuf.acdsc_reladdr.msf.frame));
-	    scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
-	  }
-	else
+		if (CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd) {
+			msf_from_bcd (&scbuf.acdsc_absaddr.msf);
+			msf_from_bcd (&scbuf.acdsc_reladdr.msf);
+		}
+		if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+			scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
 #endif /* not STANDARD_ATAPI */
-	  {
-	    abs_lba = ntohl (scbuf.acdsc_absaddr.lba);
-	    rel_lba = ntohl (scbuf.acdsc_reladdr.lba);
-	  }
-
-        if (subchnl.cdsc_format == CDROM_MSF)
-          {
-            lba_to_msf (abs_lba,
-                        &subchnl.cdsc_absaddr.msf.minute,
-                        &subchnl.cdsc_absaddr.msf.second,
-                        &subchnl.cdsc_absaddr.msf.frame);
-            lba_to_msf (rel_lba,
-                        &subchnl.cdsc_reladdr.msf.minute,
-                        &subchnl.cdsc_reladdr.msf.second,
-                        &subchnl.cdsc_reladdr.msf.frame);
-          }
-        else
-          {
-            subchnl.cdsc_absaddr.lba = abs_lba;
-            subchnl.cdsc_reladdr.lba = rel_lba;
-          }
-
-        subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
-        subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
-        subchnl.cdsc_trk  = scbuf.acdsc_trk;
-        subchnl.cdsc_ind  = scbuf.acdsc_ind;
 
-        memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
-
-        return stat;
-      }
-
-    case CDROMVOLCTRL:
-      {
-        struct cdrom_volctrl volctrl;
-        char buffer[24], mask[24];
-        int stat;
-
-        stat = verify_area (VERIFY_READ, (void *) arg, sizeof (volctrl));
-        if (stat) return stat;
-        memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
-
-        stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer),NULL);
-        if (stat) return stat;
-        stat = cdrom_mode_sense (drive, 0x0e, 1, mask  , sizeof (buffer),NULL);
-        if (stat) return stat;
-
-        buffer[1] = buffer[2] = 0;
-
-        buffer[17] = volctrl.channel0 & mask[17];
-        buffer[19] = volctrl.channel1 & mask[19];
-        buffer[21] = volctrl.channel2 & mask[21];
-        buffer[23] = volctrl.channel3 & mask[23];
-
-        return cdrom_mode_select (drive, 0x0e, buffer, sizeof (buffer), NULL);
-      }
-
-    case CDROMVOLREAD:
-      {
-        struct cdrom_volctrl volctrl;
-        char buffer[24];
-        int stat;
-
-        stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (volctrl));
-        if (stat) return stat;
-
-        stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer), NULL);
-        if (stat) return stat;
-
-        volctrl.channel0 = buffer[17];
-        volctrl.channel1 = buffer[19];
-        volctrl.channel2 = buffer[21];
-        volctrl.channel3 = buffer[23];
-
-        memcpy_tofs ((void *) arg, &volctrl, sizeof (volctrl));
-
-	return 0;
-      }
-
-    case CDROMMULTISESSION:
-      {
-	struct cdrom_multisession ms_info;
-	struct atapi_toc *toc;
-	int stat;
-
-	stat = verify_area (VERIFY_READ,  (void *)arg, sizeof (ms_info));
-        if (stat) return stat;
-	stat = verify_area (VERIFY_WRITE, (void *)arg, sizeof (ms_info));
-        if (stat) return stat;
+		if (subchnl.cdsc_format == CDROM_MSF) {
+			subchnl.cdsc_absaddr.msf.minute =
+				scbuf.acdsc_absaddr.msf.minute;
+			subchnl.cdsc_absaddr.msf.second =
+				scbuf.acdsc_absaddr.msf.second;
+			subchnl.cdsc_absaddr.msf.frame =
+				scbuf.acdsc_absaddr.msf.frame;
+
+			subchnl.cdsc_reladdr.msf.minute =
+				scbuf.acdsc_reladdr.msf.minute;
+			subchnl.cdsc_reladdr.msf.second =
+				scbuf.acdsc_reladdr.msf.second;
+			subchnl.cdsc_reladdr.msf.frame =
+				scbuf.acdsc_reladdr.msf.frame;
+		} else {
+			subchnl.cdsc_absaddr.lba =
+				msf_to_lba (scbuf.acdsc_absaddr.msf.minute,
+					    scbuf.acdsc_absaddr.msf.second,
+					    scbuf.acdsc_absaddr.msf.frame);
+			subchnl.cdsc_reladdr.lba =
+				msf_to_lba (scbuf.acdsc_reladdr.msf.minute,
+					    scbuf.acdsc_reladdr.msf.second,
+					    scbuf.acdsc_reladdr.msf.frame);
+		}
 
-	memcpy_fromfs (&ms_info, (void *)arg, sizeof (ms_info));
+		subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
+		subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
+		subchnl.cdsc_trk  = scbuf.acdsc_trk;
+		subchnl.cdsc_ind  = scbuf.acdsc_ind;
 
-	/* Make sure the TOC information is valid. */
-	stat = cdrom_read_toc (drive, NULL);
-	if (stat) return stat;
+		memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
 
-	toc = drive->cdrom_info.toc;
-
-	if (ms_info.addr_format == CDROM_MSF)
-	  lba_to_msf (toc->last_session_lba,
-		      &ms_info.addr.msf.minute,
-		      &ms_info.addr.msf.second,
-		      &ms_info.addr.msf.frame);
+		return stat;
+	}
 
-	else if (ms_info.addr_format == CDROM_LBA)
-	  ms_info.addr.lba = toc->last_session_lba;
+	case CDROMVOLCTRL: {
+		struct cdrom_volctrl volctrl;
+		char buffer[24], mask[24];
+		int stat;
+
+		stat = verify_area (VERIFY_READ, (void *) arg,
+				    sizeof (volctrl));
+		if (stat) return stat;
+		memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
+
+		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
+		stat = cdrom_mode_sense (drive, 0x0e, 1, mask,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
+
+		buffer[1] = buffer[2] = 0;
+
+		buffer[17] = volctrl.channel0 & mask[17];
+		buffer[19] = volctrl.channel1 & mask[19];
+		buffer[21] = volctrl.channel2 & mask[21];
+		buffer[23] = volctrl.channel3 & mask[23];
 
-	else
-	  return -EINVAL;
+		return cdrom_mode_select (drive, 0x0e, buffer,
+					  sizeof (buffer), NULL);
+	}
 
-	ms_info.xa_flag = toc->xa_flag;
+	case CDROMVOLREAD: {
+		struct cdrom_volctrl volctrl;
+		char buffer[24];
+		int stat;
+
+		stat = verify_area (VERIFY_WRITE, (void *) arg,
+				    sizeof (volctrl));
+		if (stat) return stat;
+
+		stat = cdrom_mode_sense (drive, 0x0e, 0, buffer,
+					 sizeof (buffer), NULL);
+		if (stat) return stat;
+
+		volctrl.channel0 = buffer[17];
+		volctrl.channel1 = buffer[19];
+		volctrl.channel2 = buffer[21];
+		volctrl.channel3 = buffer[23];
 
-	memcpy_tofs ((void *)arg, &ms_info, sizeof (ms_info));
+		memcpy_tofs ((void *) arg, &volctrl, sizeof (volctrl));
 
-	return 0;
-      }
+		return 0;
+	}
 
-    /* Read 2352 byte blocks from audio tracks. */
-    case CDROMREADAUDIO:
-      {
-	int stat, lba;
-	struct atapi_toc *toc;
-	struct cdrom_read_audio ra;
-	char buf[CD_FRAMESIZE_RAW];
+	case CDROMMULTISESSION: {
+		struct cdrom_multisession ms_info;
+		struct atapi_toc *toc;
+		int stat;
+
+		stat = verify_area (VERIFY_READ,  (void *)arg,
+				    sizeof (ms_info));
+		if (stat) return stat;
+		stat = verify_area (VERIFY_WRITE, (void *)arg,
+				    sizeof (ms_info));
+		if (stat) return stat;
+
+		memcpy_fromfs (&ms_info, (void *)arg, sizeof (ms_info));
+
+		/* Make sure the TOC information is valid. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
+
+		toc = drive->cdrom_info.toc;
+
+		if (ms_info.addr_format == CDROM_MSF)
+			lba_to_msf (toc->last_session_lba,
+				    &ms_info.addr.msf.minute,
+				    &ms_info.addr.msf.second,
+				    &ms_info.addr.msf.frame);
+		else if (ms_info.addr_format == CDROM_LBA)
+			ms_info.addr.lba = toc->last_session_lba;
+		else
+			return -EINVAL;
 
-	/* Make sure the TOC is up to date. */
-	stat = cdrom_read_toc (drive, NULL);
-	if (stat) return stat;
+		ms_info.xa_flag = toc->xa_flag;
 
-	toc = drive->cdrom_info.toc;
+		memcpy_tofs ((void *)arg, &ms_info, sizeof (ms_info));
 
-	stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
-	if (stat) return stat;
+		return 0;
+	}
 
-	memcpy_fromfs (&ra, (void *)arg, sizeof (ra));
+	/* Read 2352 byte blocks from audio tracks. */
+	case CDROMREADAUDIO: {
+		int stat, lba;
+		struct atapi_toc *toc;
+		struct cdrom_read_audio ra;
+		char buf[CD_FRAMESIZE_RAW];
+
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
+
+		toc = drive->cdrom_info.toc;
+
+		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+		if (stat) return stat;
+
+		memcpy_fromfs (&ra, (void *)arg, sizeof (ra));
+
+		if (ra.nframes < 0 || ra.nframes > toc->capacity)
+			return -EINVAL;
+		else if (ra.nframes == 0)
+			return 0;
+
+		stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+				    ra.nframes * CD_FRAMESIZE_RAW);
+		if (stat) return stat;
+
+		if (ra.addr_format == CDROM_MSF)
+			lba = msf_to_lba (ra.addr.msf.minute,
+					  ra.addr.msf.second,
+					  ra.addr.msf.frame);
+		else if (ra.addr_format == CDROM_LBA)
+			lba = ra.addr.lba;
+		else
+			return -EINVAL;
+
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
+
+		while (ra.nframes > 0) {
+			stat = cdrom_read_block (drive, 1, lba, buf,
+						 CD_FRAMESIZE_RAW, NULL);
+			if (stat) return stat;
+			memcpy_tofs (ra.buf, buf, CD_FRAMESIZE_RAW);
+			ra.buf += CD_FRAMESIZE_RAW;
+			--ra.nframes;
+			++lba;
+		}
 
-	if (ra.nframes < 0 || ra.nframes > toc->capacity)
-	  return -EINVAL;
-	else if (ra.nframes == 0)
-	  return 0;
+		return 0;
+	}
 
-	stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
-			                  ra.nframes * CD_FRAMESIZE_RAW);
-	if (stat) return stat;
+	case CDROMREADMODE1:
+	case CDROMREADMODE2: {
+		struct cdrom_msf msf;
+		int blocksize, format, stat, lba;
+		struct atapi_toc *toc;
+		char buf[CD_FRAMESIZE_RAW0];
+
+		if (cmd == CDROMREADMODE1) {
+			blocksize = CD_FRAMESIZE;
+			format = 2;
+		} else {
+			blocksize = CD_FRAMESIZE_RAW0;
+			format = 3;
+		}
 
-	if (ra.addr_format == CDROM_MSF)
-	  lba = msf_to_lba (ra.addr.msf.minute, ra.addr.msf.second,
-			    ra.addr.msf.frame);
+		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (msf));
+		if (stat) return stat;
+		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
+		if (stat) return stat;
+
+		memcpy_fromfs (&msf, (void *)arg, sizeof (msf));
+
+		lba = msf_to_lba (msf.cdmsf_min0,
+				  msf.cdmsf_sec0,
+				  msf.cdmsf_frame0);
 	
-	else if (ra.addr_format == CDROM_LBA)
-	  lba = ra.addr.lba;
-
-	else
-	  return -EINVAL;
+		/* Make sure the TOC is up to date. */
+		stat = cdrom_read_toc (drive, NULL);
+		if (stat) return stat;
 
-	if (lba < 0 || lba >= toc->capacity)
-	  return -EINVAL;
+		toc = drive->cdrom_info.toc;
 
-	while (ra.nframes > 0)
-	  {
-	    stat = cdrom_read_block (drive, 1, lba, buf,
-				     CD_FRAMESIZE_RAW, NULL);
-	    if (stat) return stat;
-	    memcpy_tofs (ra.buf, buf, CD_FRAMESIZE_RAW);
-	    ra.buf += CD_FRAMESIZE_RAW;
-	    --ra.nframes;
-	    ++lba;
-	  }
+		if (lba < 0 || lba >= toc->capacity)
+			return -EINVAL;
 
-	return 0;
-      }
-
-    case CDROMREADMODE1:
-    case CDROMREADMODE2:
-      {
-	struct cdrom_msf msf;
-	int blocksize, format, stat, lba;
-	struct atapi_toc *toc;
-	char buf[CD_FRAMESIZE_RAW0];
+		stat = cdrom_read_block (drive, format, lba, buf, blocksize,
+					 NULL);
+		if (stat) return stat;
 
-	if (cmd == CDROMREADMODE1)
-	  {
-	    blocksize = CD_FRAMESIZE;
-	    format = 2;
-	  }
-	else
-	  {
-	    blocksize = CD_FRAMESIZE_RAW0;
-	    format = 3;
-	  }
-
-	stat = verify_area (VERIFY_READ, (char *)arg, sizeof (msf));
-	if (stat) return stat;
-	stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
-	if (stat) return stat;
-
-	memcpy_fromfs (&msf, (void *)arg, sizeof (msf));
-
-	lba = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
-	
-	/* Make sure the TOC is up to date. */
-	stat = cdrom_read_toc (drive, NULL);
-	if (stat) return stat;
-
-	toc = drive->cdrom_info.toc;
+		memcpy_tofs ((char *)arg, buf, blocksize);
+		return 0;
+	}
 
-	if (lba < 0 || lba >= toc->capacity)
-	  return -EINVAL;
+	case CDROM_GET_UPC: {
+		int stat;
+		char mcnbuf[24];
+		struct cdrom_mcn mcn;
+
+		stat = verify_area (VERIFY_WRITE, (void *) arg,
+				    sizeof (mcn));
+		if (stat) return stat;
+
+		stat = cdrom_read_subchannel (drive, 2, /* get MCN */
+					      mcnbuf, sizeof (mcnbuf),
+					      NULL);
+		if (stat) return stat;
+
+		memcpy (mcn.medium_catalog_number, mcnbuf+9,
+			sizeof (mcn.medium_catalog_number)-1);
+		mcn.medium_catalog_number[sizeof (mcn.medium_catalog_number)-1]
+			= '\0';
 
-	stat = cdrom_read_block (drive, format, lba, buf, blocksize, NULL);
-	if (stat) return stat;
+		memcpy_tofs ((void *) arg, &mcn, sizeof (mcn));
 
-	memcpy_tofs ((char *)arg, buf, blocksize);
-	return 0;
-      }
+		return stat;
+	}
 
 #if 0 /* Doesn't work reliably yet. */
-    case CDROMRESET:
-      {
-	struct request req;
-	ide_init_drive_cmd (&req);
-	req.cmd = RESET_DRIVE_COMMAND;
-	return ide_do_drive_cmd (drive, &req, ide_wait);
-      }
+	case CDROMRESET: {
+		struct request req;
+		ide_init_drive_cmd (&req);
+		req.cmd = RESET_DRIVE_COMMAND;
+		return ide_do_drive_cmd (drive, &req, ide_wait);
+	}
 #endif
 
  
 #ifdef TEST
-    case 0x1234:
-      {
-        int stat;
-        struct packet_command pc;
-	int len, lena;
-
-        memset (&pc, 0, sizeof (pc));
-
-        stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
-        if (stat) return stat;
-        memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
-	arg += sizeof (pc.c);
-
-	stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
-        if (stat) return stat;
-        memcpy_fromfs (&len, (void *) arg , sizeof (len));
-	arg += sizeof (len);
-
-	if (len > 0) {
-	  stat = verify_area (VERIFY_WRITE, (void *) arg, len);
-	  if (stat) return stat;
-	}
+	case 0x1234: {
+		int stat;
+		struct packet_command pc;
+		int len, lena;
+
+		memset (&pc, 0, sizeof (pc));
+
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+		if (stat) return stat;
+		memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
+		arg += sizeof (pc.c);
+
+		stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
+		if (stat) return stat;
+		memcpy_fromfs (&len, (void *) arg , sizeof (len));
+		arg += sizeof (len);
+
+		if (len > 0) {
+			stat = verify_area (VERIFY_WRITE, (void *) arg, len);
+			if (stat) return stat;
+		}
 
-	lena = len;
-	if (lena  < 0) lena = 0;
+		lena = len;
+		if (lena  < 0) lena = 0;
 
-	{
-	  char buf[lena];
-	  if (len > 0) {
-	    pc.buflen = len;
-	    pc.buffer = buf;
-	  }
+		{
+			char buf[lena];
+			if (len > 0) {
+				pc.buflen = len;
+				pc.buffer = buf;
+			}
 
-	  stat = cdrom_queue_packet_command (drive, &pc);
+			stat = cdrom_queue_packet_command (drive, &pc);
 
-	  if (len > 0)
-	    memcpy_tofs ((void *)arg, buf, len);
-	}
+			if (len > 0)
+				memcpy_tofs ((void *)arg, buf, len);
+		}
 
-        return stat;
-      }
+		return stat;
+	}
 #endif
 
-    default:
-      return -EPERM;
-    }
+	default:
+		return -EPERM;
+	}
 
 }
 
@@ -2551,58 +2445,54 @@
 
 int ide_cdrom_check_media_change (ide_drive_t *drive)
 {
-  int retval;
+	int retval;
 
-  (void) cdrom_check_status (drive, NULL);
+	(void) cdrom_check_status (drive, NULL);
 
-  retval = CDROM_STATE_FLAGS (drive)->media_changed;
-  CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	retval = CDROM_STATE_FLAGS (drive)->media_changed;
+	CDROM_STATE_FLAGS (drive)->media_changed = 0;
 
-  return retval;
+	return retval;
 }
 
 
 int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
 {
   /* no write access */
-  if (fp->f_mode & 2)
-    {
-      --drive->usage;
-      return -EROFS;
-    }
-
-  /* If this is the first open, check the drive status. */
-  if (drive->usage == 1)
-    {
-      int stat;
-      struct atapi_request_sense my_reqbuf;
-      my_reqbuf.sense_key = 0;
-
-      /* Get the drive status. */
-      stat = cdrom_check_status (drive, &my_reqbuf);
-
-      /* If the tray is open, try to close it. */
-      if (stat && my_reqbuf.sense_key == NOT_READY)
-	{
-	  cdrom_eject (drive, 1, &my_reqbuf);
-	  stat = cdrom_check_status (drive, &my_reqbuf);
+	if (fp->f_mode & 2) {
+		--drive->usage;
+		return -EROFS;
 	}
 
-      /* Return an error if there are still problems. */
-      if (stat && my_reqbuf.sense_key != UNIT_ATTENTION)
-	{
-	  --drive->usage;
-	  return -ENXIO;
-	}
+	/* If this is the first open, check the drive status. */
+	if (drive->usage == 1) {
+		int stat;
+		struct atapi_request_sense my_reqbuf;
+		my_reqbuf.sense_key = 0;
+
+		/* Get the drive status. */
+		stat = cdrom_check_status (drive, &my_reqbuf);
+
+		/* If the tray is open, try to close it. */
+		if (stat && my_reqbuf.sense_key == NOT_READY) {
+			cdrom_eject (drive, 1, &my_reqbuf);
+			stat = cdrom_check_status (drive, &my_reqbuf);
+		}
 
-      /* Now lock the door. */
-      (void) cdrom_lockdoor (drive, 1, &my_reqbuf);
+		/* Return an error if there are still problems. */
+		if (stat && my_reqbuf.sense_key != UNIT_ATTENTION) {
+			--drive->usage;
+			return -ENXIO;
+		}
+
+		/* Now lock the door. */
+		(void) cdrom_lockdoor (drive, 1, &my_reqbuf);
 
-      /* And try to read the TOC information now. */
-      (void) cdrom_read_toc (drive, &my_reqbuf);
-    }
+		/* And try to read the TOC information now. */
+		(void) cdrom_read_toc (drive, &my_reqbuf);
+	}
 
-  return 0;
+	return 0;
 }
 
 
@@ -2610,19 +2500,19 @@
  * Close down the device.  Invalidate all cached blocks.
  */
 
-void ide_cdrom_release (struct inode *inode, struct file *file, ide_drive_t *drive)
+void ide_cdrom_release (struct inode *inode, struct file *file,
+			ide_drive_t *drive)
 {
-  if (drive->usage == 0)
-    {
-      invalidate_buffers (inode->i_rdev);
-
-      /* Unlock the door. */
-      (void) cdrom_lockdoor (drive, 0, NULL);
-
-      /* Do an eject if we were requested to do so. */
-      if (CDROM_STATE_FLAGS (drive)->eject_on_close)
-	(void) cdrom_eject (drive, 0, NULL);
-    }
+	if (drive->usage == 0) {
+		invalidate_buffers (inode->i_rdev);
+
+		/* Unlock the door. */
+		(void) cdrom_lockdoor (drive, 0, NULL);
+
+		/* Do an eject if we were requested to do so. */
+		if (CDROM_STATE_FLAGS (drive)->eject_on_close)
+			(void) cdrom_eject (drive, 0, NULL);
+	}
 }
 
 
@@ -2633,107 +2523,78 @@
 
 void ide_cdrom_setup (ide_drive_t *drive)
 {
-  blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] = CD_FRAMESIZE;
+	blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
+		CD_FRAMESIZE;
 
-  drive->special.all = 0;
-  drive->ready_stat = 0;
+	drive->special.all = 0;
+	drive->ready_stat = 0;
 
-  CDROM_STATE_FLAGS (drive)->media_changed = 0;
-  CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
-  CDROM_STATE_FLAGS (drive)->door_locked   = 0;
+	CDROM_STATE_FLAGS (drive)->media_changed = 0;
+	CDROM_STATE_FLAGS (drive)->toc_valid     = 0;
+	CDROM_STATE_FLAGS (drive)->door_locked   = 0;
 
-  /* Turn this off by default, since many people don't like it. */
-  CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
+	/* Turn this off by default, since many people don't like it. */
+	CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
 
 #if NO_DOOR_LOCKING
-  CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 #else
-  CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+	CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
 #endif
 
-  if (drive->id != NULL) {
-    CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
-      ((drive->id->config & 0x0060) == 0x20);
-  } else {
-    CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
-  }
+	if (drive->id != NULL)
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+			((drive->id->config & 0x0060) == 0x20);
+	else
+		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+
+	CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
+	CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0;
 
 #if ! STANDARD_ATAPI
-  CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 0;
-  CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
-  CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 0;
-  CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 0;
-  CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 0;
-
-  if (drive->id != NULL) {
-    /* Accommodate some broken drives... */
-    if (strcmp (drive->id->model, "CD220E") == 0 ||
-        strcmp (drive->id->model, "CD")     == 0)        /* Creative Labs */
-      CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-    else if (strcmp (drive->id->model, "TO-ICSLYAL") == 0 ||  /* Acer CD525E */
-             strcmp (drive->id->model, "OTI-SCYLLA") == 0)
-      CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-    /* I don't know who makes this.
-       Francesco Messineo <sidera@ccii.unipi.it> says this one's broken too. */
-    else if (strcmp (drive->id->model, "DCI-2S10") == 0)
-      CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-    else if (strcmp (drive->id->model, "CDA26803I SE") == 0) /* Aztech */
-      {
-        CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+	if (drive->id != NULL) {
+		if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+		    drive->id->fw_rev[4] == '1' &&
+		    drive->id->fw_rev[6] <= '2') {
+			/* Vertos 300.
+			   Some versions of this drive like to talk BCD. */
+			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+		}
 
-        /* This drive _also_ does not implement PLAYAUDIO12 correctly. */
-        CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
-      }
+		else if (strcmp (drive->id->model,
+				 "NEC CD-ROM DRIVE:260") == 0 &&
+			 strcmp (drive->id->fw_rev, "1.01") == 0) {
+			/* Old NEC260 (not R). */
+			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+		}
 
-    /* Vertos 300. */
-    else if (strcmp (drive->id->model, "V003S0DS") == 0)
-      {
-        CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-	/* Some versions of this drive like to talk BCD. */
-	if (drive->id->fw_rev[4] == '1' &&
-	    drive->id->fw_rev[6] <= '2')
-	  {
-	    CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 1;
-	    CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
-	  }
-      }
-    else if (strcmp (drive->id->model, "0V300SSD") == 0 ||
-	     strcmp (drive->id->model, "V003M0DP") == 0 ||
-	     strcmp (drive->id->model, "0V300MPD") == 0 ||
-	     strcmp (drive->id->model, "0V300HPD") == 0 ||
-	     strcmp (drive->id->model, "V003H0DP") == 0)
-      CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-    /* Vertos 400. */
-    else if (strcmp (drive->id->model, "V004E0DT") == 0 ||
-	     strcmp (drive->id->model, "0V400ETD") == 0 ||
-	     strcmp (drive->id->model, "V004H0DT") == 0 ||
-	     strcmp (drive->id->model, "0V400HTD") == 0)
-      CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
-
-    else if ( strcmp (drive->id->model, "CD-ROM CDU55D") == 0) /*sony cdu55d */
-      CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
-
-   else if (strcmp (drive->id->model, "CD-ROM CDU55E") == 0)
-  	CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
-  } /* drive-id != NULL */
-#endif  /* not STANDARD_ATAPI */
+		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
+			 strcmp (drive->id->fw_rev, "A1.1") == 0) {
+			/* Wearnes */
+			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+		}
+	}
+#endif /* not STANDARD_ATAPI */
 
-  drive->cdrom_info.toc               = NULL;
-  drive->cdrom_info.sector_buffer     = NULL;
-  drive->cdrom_info.sector_buffered   = 0;
-  drive->cdrom_info.nsectors_buffered = 0;
+	drive->cdrom_info.toc               = NULL;
+	drive->cdrom_info.sector_buffer     = NULL;
+	drive->cdrom_info.sector_buffered   = 0;
+	drive->cdrom_info.nsectors_buffered = 0;
 }
 
 
 
 /*
  * TODO:
- *  CDROM_GET_UPC
  *  CDROMRESET
  *  Lock the door when a read request completes successfully and the
  *   door is not already locked.  Also try to reorganize to reduce
@@ -2742,4 +2603,13 @@
  *   code into a loadable module.
  *  Support changers.
  *  Write some real documentation.
+ */
+
+
+
+/*==========================================================================*/
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
  */
diff -u --recursive --new-file v1.3.84/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v1.3.84/linux/drivers/block/ide-tape.c	Sat Mar 16 13:52:14 1996
+++ linux/drivers/block/ide-tape.c	Mon Apr  8 12:07:47 1996
@@ -3154,6 +3154,7 @@
 	}
 #endif /* IDETAPE_DEBUG_BUGS */
 
+	run_task_queue(&tq_disk);
 	rq->sem=&sem;
 	down (&sem);
 }
diff -u --recursive --new-file v1.3.84/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.84/linux/drivers/block/ide.c	Tue Apr  2 13:32:19 1996
+++ linux/drivers/block/ide.c	Mon Apr  8 12:06:22 1996
@@ -1753,8 +1753,10 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
-	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
+	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE) {
+		run_task_queue(&tq_disk);
 		down(&sem);	/* wait for it to be serviced */
+	}
 	restore_flags(flags);
 	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 }
diff -u --recursive --new-file v1.3.84/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.84/linux/drivers/block/ide.h	Mon Mar 25 10:23:22 1996
+++ linux/drivers/block/ide.h	Mon Apr  8 18:25:19 1996
@@ -159,62 +159,68 @@
 #ifdef CONFIG_BLK_DEV_IDECD
 
 struct atapi_request_sense {
-  unsigned char error_code : 7;
-  unsigned char valid      : 1;
-  byte reserved1;
-  unsigned char sense_key  : 4;
-  unsigned char reserved2  : 1;
-  unsigned char ili        : 1;
-  unsigned char reserved3  : 2;
-  byte info[4];
-  byte sense_len;
-  byte command_info[4];
-  byte asc;
-  byte ascq;
-  byte fru;
-  byte sense_key_specific[3];
+	unsigned char error_code : 7;
+	unsigned char valid      : 1;
+	byte reserved1;
+	unsigned char sense_key  : 4;
+	unsigned char reserved2  : 1;
+	unsigned char ili        : 1;
+	unsigned char reserved3  : 2;
+	byte info[4];
+	byte sense_len;
+	byte command_info[4];
+	byte asc;
+	byte ascq;
+	byte fru;
+	byte sense_key_specific[3];
 };
 
 struct packet_command {
-  char *buffer;
-  int buflen;
-  int stat;
-  struct atapi_request_sense *sense_data;
-  unsigned char c[12];
+	char *buffer;
+	int buflen;
+	int stat;
+	struct atapi_request_sense *sense_data;
+	unsigned char c[12];
 };
 
+
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+	byte reserved;
+	byte minute;
+	byte second;
+	byte frame;
+};
+
+
 /* Space to hold the disk TOC. */
 
 #define MAX_TRACKS 99
 struct atapi_toc_header {
-  unsigned short toc_length;
-  byte first_track;
-  byte last_track;
+	unsigned short toc_length;
+	byte first_track;
+	byte last_track;
 };
 
 struct atapi_toc_entry {
-  byte reserved1;
-  unsigned control : 4;
-  unsigned adr     : 4;
-  byte track;
-  byte reserved2;
-  union {
-    unsigned lba;
-    struct {
-      byte reserved3;
-      byte m;
-      byte s;
-      byte f;
-    } msf;
-  } addr;
+	byte reserved1;
+	unsigned control : 4;
+	unsigned adr     : 4;
+	byte track;
+	byte reserved2;
+	union {
+		unsigned lba;
+		struct atapi_msf msf;
+	} addr;
 };
 
 struct atapi_toc {
-  int    last_session_lba;
-  int    xa_flag;
-  unsigned capacity;
-  struct atapi_toc_header hdr;
-  struct atapi_toc_entry  ent[MAX_TRACKS+1];  /* One extra for the leadout. */
+	int    last_session_lba;
+	int    xa_flag;
+	unsigned capacity;
+	struct atapi_toc_header hdr;
+	struct atapi_toc_entry  ent[MAX_TRACKS+1];
+	  /* One extra for the leadout. */
 };
 
 
@@ -222,62 +228,49 @@
    the cdrom_subchnl structure from cdrom.h. */
 struct atapi_cdrom_subchnl 
 {
-  u_char  acdsc_reserved;
-  u_char  acdsc_audiostatus;
-  u_short acdsc_length;
-  u_char  acdsc_format;
-
-  u_char  acdsc_adr:	4;
-  u_char  acdsc_ctrl:	4;
-  u_char  acdsc_trk;
-  u_char  acdsc_ind;
-  union
-  {
-    struct 			
-    {
-      u_char    reserved;
-      u_char	minute;
-      u_char	second;
-      u_char	frame;
-    } msf;
-    int	lba;
-  } acdsc_absaddr;
-  union 
-  {
-    struct 
-    {
-      u_char    reserved;
-      u_char	minute;
-      u_char	second;
-      u_char	frame;
-    } msf;
-    int	lba;
-  } acdsc_reladdr;
+	u_char  acdsc_reserved;
+	u_char  acdsc_audiostatus;
+	u_short acdsc_length;
+	u_char  acdsc_format;
+
+	u_char  acdsc_adr:	4;
+	u_char  acdsc_ctrl:	4;
+	u_char  acdsc_trk;
+	u_char  acdsc_ind;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_absaddr;
+	union {
+		struct atapi_msf msf;
+		int	lba;
+	} acdsc_reladdr;
 };
 
 
 /* Extra per-device info for cdrom drives. */
 struct cdrom_info {
 
-  /* Buffer for table of contents.  NULL if we haven't allocated
-     a TOC buffer for this device yet. */
+	/* Buffer for table of contents.  NULL if we haven't allocated
+	   a TOC buffer for this device yet. */
 
-  struct atapi_toc *toc;
+	struct atapi_toc *toc;
 
-  /* Sector buffer.  If a read request wants only the first part of a cdrom
-     block, we cache the rest of the block here, in the expectation that that
-     data is going to be wanted soon.  SECTOR_BUFFERED is the number of the
-     first buffered sector, and NSECTORS_BUFFERED is the number of sectors
-     in the buffer.  Before the buffer is allocated, we should have
-     SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
-
-  unsigned long sector_buffered;
-  unsigned long nsectors_buffered;
-  char *sector_buffer;
-
-  /* The result of the last successful request sense command
-     on this device. */
-  struct atapi_request_sense sense_data;
+	/* Sector buffer.  If a read request wants only the first part
+	   of a cdrom block, we cache the rest of the block here,
+	   in the expectation that that data is going to be wanted soon.
+	   SECTOR_BUFFERED is the number of the first buffered sector,
+	   and NSECTORS_BUFFERED is the number of sectors in the buffer.
+	   Before the buffer is allocated, we should have
+	   SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+	unsigned long sector_buffered;
+	unsigned long nsectors_buffered;
+	char *sector_buffer;
+
+	/* The result of the last successful request sense command
+	   on this device. */
+	struct atapi_request_sense sense_data;
 };
 
 #endif /* CONFIG_BLK_DEV_IDECD */
diff -u --recursive --new-file v1.3.84/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.84/linux/drivers/block/ll_rw_blk.c	Sun Mar 31 00:13:16 1996
+++ linux/drivers/block/ll_rw_blk.c	Mon Apr  8 12:03:28 1996
@@ -28,6 +28,12 @@
 static struct request all_requests[NR_REQUEST];
 
 /*
+ * The "disk" task queue is used to start the actual requests
+ * after a plug
+ */
+DECLARE_TASK_QUEUE(tq_disk);
+
+/*
  * used to wait on when there are no free requests
  */
 struct wait_queue * wait_for_request = NULL;
@@ -101,7 +107,7 @@
 static inline void plug_device(struct blk_dev_struct * dev)
 {
 	dev->current_request = &dev->plug;
-	queue_task_irq_off(&dev->plug_tq, &tq_scheduler);
+	queue_task_irq_off(&dev->plug_tq, &tq_disk);
 }
 
 /*
@@ -152,6 +158,7 @@
 		sti();
 		if (req)
 			break;
+		run_task_queue(&tq_disk);
 		schedule();
 	}
 	remove_wait_queue(&wait_for_request, &wait);
@@ -593,6 +600,7 @@
 			req[j]->next = NULL;
 			add_request(major+blk_dev,req[j]);
 		}
+		run_task_queue(&tq_disk);
 		while (j > 0) {
 			j--;
 			down(&sem);
diff -u --recursive --new-file v1.3.84/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.84/linux/drivers/char/Makefile	Wed Apr  3 16:06:55 1996
+++ linux/drivers/char/Makefile	Mon Apr  8 11:10:35 1996
@@ -140,6 +140,7 @@
 
 ifdef CONFIG_APM
 LX_OBJS += apm_bios.o
+M = y
 endif
 
 ifdef M
diff -u --recursive --new-file v1.3.84/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v1.3.84/linux/drivers/char/apm_bios.c	Sat Mar 16 20:17:27 1996
+++ linux/drivers/char/apm_bios.c	Mon Apr  8 11:10:35 1996
@@ -1,6 +1,7 @@
 /* -*- linux-c -*-
  * APM BIOS driver for Linux
- * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ * Copyright 1994, 1995, 1996 Stephen Rothwell
+ *                           (Stephen.Rothwell@canb.auug.org.au)
  *
  * 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
@@ -22,12 +23,16 @@
  * March 1996, Rik Faith (faith@cs.unc.edu):
  *    Prohibit APM BIOS calls unless apm_enabled.
  *    (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
+ * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
+ *    Version 1.0
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
  *    0.7: changed /proc/apm format, Linux 1.3.58
  *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  *    0.9: only call bios if bios is present, Linux 1.3.72
+ *    1.0: use fixed device number, consolidate /proc/apm into
+ *         this file, Linux 1.3.85
  *
  * Reference:
  *
@@ -54,6 +59,11 @@
 #include <linux/fcntl.h>
 #include <linux/malloc.h>
 #include <linux/linkage.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif
+#include <linux/miscdevice.h>
 #include <linux/apm_bios.h>
 
 static struct symbol_table	apm_syms = {
@@ -65,6 +75,12 @@
 
 extern unsigned long get_cmos_time(void);
 
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
 /* Configurable options:
  *  
  * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
@@ -285,6 +301,10 @@
 			  select_table *);
 static int	do_ioctl(struct inode *, struct file *, u_int, u_long);
 
+#ifdef CONFIG_PROC_FS
+static int	apm_get_info(char *, char **, off_t, int, int);
+#endif
+
 extern int	apm_register_callback(int (*)(apm_event_t));
 extern void	apm_unregister_callback(int (*)(apm_event_t));
 
@@ -311,7 +331,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "0.9";/* no spaces */
+static char			driver_version[] = "1.0";/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -345,6 +365,18 @@
 	NULL		/* fasync */
 };
 
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm",
+	&apm_bios_fops
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry	apm_proc_entry = {
+        0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
+};
+#endif
+
 typedef struct callback_list_t {
 	int (*				callback)(apm_event_t);
 	struct callback_list_t *	next;
@@ -472,7 +504,7 @@
 #ifdef CONFIG_APM_DISPLAY_BLANK
 	int	error;
 
-	if (!apm_enabled || apm_bios_info.version == 0)
+	if (!apm_enabled)
 		return 0;
 	error = apm_set_display_power_state(APM_STATE_STANDBY);
 	if (error == APM_SUCCESS)
@@ -488,7 +520,7 @@
 #ifdef CONFIG_APM_DISPLAY_BLANK
 	int error;
 
-	if (!apm_enabled || apm_bios_info.version == 0)
+	if (!apm_enabled)
 		return 0;
 	error = apm_set_display_power_state(APM_STATE_READY);
 	if (error == APM_SUCCESS)
@@ -914,7 +946,8 @@
 	return 0;
 }
 
-int apm_proc(char *buf)
+#ifdef CONFIG_PROC_FS
+int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
 {
 	char *		p;
 	unsigned short	bx;
@@ -924,31 +957,29 @@
 	unsigned short  ac_line_status = 0xff;
 	unsigned short  battery_status = 0xff;
 	unsigned short  battery_flag   = 0xff;
-	unsigned short  percentage     = -1;
+	int		percentage     = -1;
 	int             time_units     = -1;
 	char            *units         = "?";
 
-	if (apm_bios_info.version == 0)
+	if (!apm_enabled)
 		return 0;
 	p = buf;
 
-	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) != 0) {
-		if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
-			ac_line_status = (bx >> 8) & 0xff;
-			battery_status = bx & 0xff;
-			if ((cx & 0xff) != 0xff)
-				percentage = cx & 0xff;
-	
-			if (apm_bios_info.version > 0x100) {
-				battery_flag = (cx >> 8) & 0xff;
-				if (dx != 0xffff) {
-					if ((dx & 0x8000) == 0x8000) {
-						units = "min";
-						time_units = dx & 0x7ffe;
-					} else {
-						units = "sec";
-						time_units = dx & 0x7fff;
-					}
+	if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
+		ac_line_status = (bx >> 8) & 0xff;
+		battery_status = bx & 0xff;
+		if ((cx & 0xff) != 0xff)
+			percentage = cx & 0xff;
+
+		if (apm_bios_info.version > 0x100) {
+			battery_flag = (cx >> 8) & 0xff;
+			if (dx != 0xffff) {
+				if ((dx & 0x8000) == 0x8000) {
+					units = "min";
+					time_units = dx & 0x7ffe;
+				} else {
+					units = "sec";
+					time_units = dx & 0x7fff;
 				}
 			}
 		}
@@ -1000,12 +1031,13 @@
 		     battery_flag,
 		     percentage,
 		     time_units,
-		     units );
+		     units);
 
 	return p - buf;
 }
+#endif
 
-static int apm_setup(void)
+void apm_bios_init(void)
 {
 	unsigned short	bx;
 	unsigned short	cx;
@@ -1016,7 +1048,7 @@
 
 	if (apm_bios_info.version == 0) {
 		printk("APM BIOS not found.\n");
-		return -1;
+		return;
 	}
 	printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
 	       ((apm_bios_info.version >> 8) & 0xff) + '0',
@@ -1025,7 +1057,7 @@
 	       driver_version);
 	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
 		printk("    No 32 bit BIOS support\n");
-		return -1;
+		return;
 	}
 
 	/*
@@ -1128,7 +1160,7 @@
 	if (error)
 		apm_error("enable power management", error);
 	if (error == APM_DISABLED)
-		return -1;
+		return;
 #endif
 
 	init_timer(&apm_timer);
@@ -1138,15 +1170,11 @@
 
 	register_symtab(&apm_syms);
 
-	apm_enabled = 1;
-
-	if ((apm_major = register_chrdev(0, "apm_bios", &apm_bios_fops)) < 0)
-		printk("APM BIOS: Cannot allocate major device number\n");
+#ifdef CONFIG_PROC_FS
+	proc_register_dynamic(&proc_root, &apm_proc_entry);
+#endif
 
-	return 0;
-}
+	misc_register(&apm_device);
 
-void apm_bios_init(void)
-{
-	apm_setup();
+	apm_enabled = 1;
 }
diff -u --recursive --new-file v1.3.84/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v1.3.84/linux/drivers/char/mem.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/mem.c	Mon Apr  8 11:10:35 1996
@@ -389,7 +389,8 @@
 #endif
 #if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \
     defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
-    defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG)
+    defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
+    defined (CONFIG_APM)
 	misc_init();
 #endif
 #ifdef CONFIG_SOUND
diff -u --recursive --new-file v1.3.84/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v1.3.84/linux/drivers/char/misc.c	Tue Apr  2 13:32:20 1996
+++ linux/drivers/char/misc.c	Mon Apr  8 11:10:35 1996
@@ -39,6 +39,9 @@
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <linux/tty.h> /* needed by selection.h */
 #include "selection.h" /* export its symbols */
@@ -204,6 +207,9 @@
 #ifdef CONFIG_SOFT_WATCHDOG
 	watchdog_init();
 #endif	
+#ifdef CONFIG_APM
+	apm_bios_init();
+#endif
 #endif /* !MODULE */
 	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
 	  printk("unable to get major %d for misc devices\n",
diff -u --recursive --new-file v1.3.84/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v1.3.84/linux/drivers/char/pcxx.c	Fri Apr  5 13:35:28 1996
+++ linux/drivers/char/pcxx.c	Sun Apr  7 09:42:34 1996
@@ -25,6 +25,9 @@
  *		allocation harmonized with 1.3.X Series.
  *  1.5.4 March 30, 1996 Christoph Lameter: Fixup for 1.3.81. Use init_bh
  *		instead of direct assigment to kernel arrays.
+ *  1.5.5 April 5, 1996 Major device numbers corrected.
+ *              Mike McLagan<mike.mclagan@linux.org>: Add setup
+ *              variable handling, instead of using the old pcxxconfig.h
  *
  */
 
@@ -49,18 +52,21 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
-#include <linux/major.h>
 #include <linux/delay.h>
 #include <linux/serial.h>
 #include <linux/tty_driver.h>
 #include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
 
-#define VERSION 	"1.5.4"
+#define VERSION 	"1.5.5"
+static char *banner = "Digiboard PC/X{i,e,eve,em} driver v1.5.5.  Christoph Lameter <clameter@fuller.edu>.";
+
 /*#define	DEFAULT_HW_FLOW	1 */
 /*#define	DEBUG_IOCTL */
 
@@ -69,21 +75,26 @@
 #include "pcxx.h"
 #include "digi_fep.h"
 #include "digi_bios.h"
-#include "pcxxconfig.h"
-
-#define DIGIMAJOR   30
-#define DIGICUMAJOR 31 
-
-#define MAXPORTS 16     /* Max ports per PC/Xx type board */
-#define NBDEVS   (NUMCARDS * MAXPORTS)
-#define PORTNUM(x)  ((x)->dev % MAXPORTS)
-#define LINENUM(x)  (MINOR((x)->device) - (x)->driver.minor_start)
 
+/* Define one default setting if no digi= config line is used.
+ * Default is ALTPIN = ON, PC/16E, 16 ports, I/O 200h Memory 0D0000h
+ */
+static struct board_info boards[MAX_DIGI_BOARDS] = { {	ENABLED, 0, ON, 16, 0x200, 0xd0000,0 } };
+ 
+static int               numcards = 1;
+static int               nbdevs = 0;
+ 
+/* C is a pain!  I want a pointer to an array of structs */
+static struct channel    *(*digi_channels);
+
+/* this is supposed to be a pointer to an array of pointers */
+static struct tty_struct *(*pcxe_table)[];
+static struct termios    *(*pcxe_termios)[];
+static struct termios    *(*pcxe_termios_locked)[];
+ 
 int pcxx_ncook=sizeof(pcxx_cook);
 int pcxx_nbios=sizeof(pcxx_bios);
 
-struct channel digi_channels[NBDEVS];
-
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #define pcxxassert(x, msg)  if(!(x)) pcxx_error(__LINE__, msg)
 
@@ -97,9 +108,6 @@
 static int pcxe_refcount;
 
 DECLARE_TASK_QUEUE(tq_pcxx);
-static struct tty_struct *pcxe_table[NBDEVS];
-static struct termios *pcxe_termios[NBDEVS];
-static struct termios *pcxe_termios_locked[NBDEVS];
 
 static void pcxxpoll(void);
 static void pcxxdelay(int);
@@ -142,7 +150,7 @@
 {
 	if (tty) {
 		register struct channel *ch=(struct channel *)tty->driver_data;
-		if ((ch >= &digi_channels[0]) && (ch < &digi_channels[NBDEVS])) {
+		if ((ch >= &((*digi_channels)[0])) && (ch < &((*digi_channels)[nbdevs]))) {
 			if (ch->magic==PCXX_MAGIC)
 				return ch;
 		}
@@ -293,20 +301,25 @@
 	int retval;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
-	if(line < 0 || line >= NBDEVS) {
+
+	if(line < 0 || line >= nbdevs) {
 		printk("line out of range in pcxe_open\n");
 		tty->driver_data = NULL;
 		return(-ENODEV);
 	}
 
-	boardnum = line / 16;
-	if(boardnum >= NUMCARDS || boards[boardnum].status == DISABLED ||
-								(line % MAXPORTS) >= boards[boardnum].numports) {
+	for(boardnum=0;boardnum<numcards;boardnum++)
+		if ((line >= boards[boardnum].first_minor) && 
+			(line <= boards[boardnum].first_minor + boards[boardnum].numports))
+		break;
+
+	if(boardnum >= numcards || boards[boardnum].status == DISABLED ||
+		(line - boards[boardnum].first_minor) >= boards[boardnum].numports) {
 		tty->driver_data = NULL;   /* Mark this device as 'down' */
 		return(-ENODEV);
 	}
-	
-	ch = &digi_channels[line];
+
+	ch = &((*digi_channels)[line]);
 
 	if(ch->brdchan == 0) {
 		tty->driver_data = NULL;
@@ -746,10 +759,211 @@
 	}
 }
 
+/* Flag if lilo configuration option is used. If so the
+ * default settings are removed
+ */
+
+static int liloconfig=0;
+
+void pcxx_setup(char *str, int *ints)
+{
+
+	struct board_info board;
+	int               i, j, last;
+	char              *temp, *t2;
+	unsigned          len;
+
+#if 0
+	if (!numcards)
+		memset(&boards, 0, sizeof(boards));
+#endif
+	if (liloconfig==0) { liloconfig=1;numcards=0; }
+
+	memset(&board, 0, sizeof(board));
+
+	for(last=0,i=1;i<=ints[0];i++)
+		switch(i)
+		{
+			case 1:
+				board.status = ints[i];
+				last = i;
+				break;
+
+			case 2:
+				board.type = ints[i];
+				last = i;
+				break;
+
+			case 3:
+				board.altpin = ints[i];
+				last = i;
+				break;
+
+			case 4:
+				board.numports = ints[i];
+				last = i;
+				break;
+
+			case 5:
+				board.port = ints[i];
+				last = i;
+				break;
+
+			case 6:
+				board.membase = ints[i];
+				last = i;
+				break;
+
+			default:
+				printk("PC/Xx: Too many integer parms\n");
+				return;
+		}
+
+	
+	while (str && *str) 
+	{
+		/* find the next comma or terminator */
+		temp = str;
+		while (*temp && (*temp != ','))
+			temp++;
+
+		if (!*temp)
+			temp = NULL;
+		else
+			*temp++ = 0;
+
+		i = last + 1;
+
+		switch(i)
+		{
+			case 1:
+				len = strlen(str);
+				if (strncmp("Disable", str, len) == 0) 
+					board.status = 0;
+				else
+					if (strncmp("Enable", str, len) == 0)
+						board.status = 1;
+					else
+					{
+						printk("PC/Xx: Invalid status %s\n", str);
+						return;
+					}
+				last = i;
+				break;
+
+			case 2:
+				for(j=0;j<PCXX_NUM_TYPES;j++)
+					if (strcmp(board_desc[j], str) == 0)
+						break;
+
+				if (i<PCXX_NUM_TYPES) 
+					board.type = j;
+				else
+				{
+					printk("PC/Xx: Invalid board name: %s\n", str);
+					return;
+				}
+				last = i;
+				break;
+
+			case 3:
+				len = strlen(str);
+				if (strncmp("Disable", str, len) == 0) 
+					board.altpin = 0;
+				else
+					if (strncmp("Enable", str, len) == 0)
+						board.altpin = 1;
+					else
+					{
+						printk("PC/Xx: Invalid altpin %s\n", str);
+						return;
+					}
+				last = i;
+				break;
+
+			case 4:
+				t2 = str;
+				while (isdigit(*t2))
+					t2++;
+
+				if (*t2)
+				{
+					printk("PC/Xx: Invalid port count %s\n", str);
+					return;
+				}
+
+				board.numports = simple_strtoul(str, NULL, 0);
+				last = i;
+				break;
+
+			case 5:
+				t2 = str;
+				while (isxdigit(*t2))
+					t2++;
+
+				if (*t2)
+				{
+					printk("PC/Xx: Invalid port count %s\n", str);
+					return;
+				}
+
+				board.port = simple_strtoul(str, NULL, 16);
+				last = i;
+				break;
+
+			case 6:
+				t2 = str;
+				while (isxdigit(*t2))
+					t2++;
+
+				if (*t2)
+				{
+					printk("PC/Xx: Invalid memory base %s\n", str);
+					return;
+				}
+
+				board.membase = simple_strtoul(str, NULL, 16);
+				last = i;
+				break;
+
+			default:
+				printk("PC/Xx: Too many string parms\n");
+				return;
+		}
+		str = temp;
+	}
+
+	if (last < 6)  
+	{
+		printk("PC/Xx: Insufficient parms specified\n");
+		return;
+	}
+ 
+        /* I should REALLY validate the stuff here */
+
+	memcpy(&boards[numcards],&board, sizeof(board));
+	printk("PC/Xx: Added board %i, %s %s %i ports at 0x%4.4X base 0x%6.6X\n", 
+		numcards, board_desc[board.type], board_mem[board.type], 
+		board.numports, board.port, (unsigned int) board.membase);
+
+	/* keep track of my inital minor number */
+        if (numcards)
+		boards[numcards].first_minor = boards[numcards-1].first_minor + boards[numcards-1].numports;
+	else
+		boards[numcards].first_minor = 0;
+
+	/* yeha!  string parameter was successful! */
+	numcards++;
+}
+
 
 int pcxe_init(void)
 {
-	ulong flags, save_loops_per_sec, memory_seg=0, memory_size;
+#if 0
+	ulong save_loops_per_sec;
+#endif
+
+	ulong flags, memory_seg=0, memory_size;
 	int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
 	unchar *fepos, *memaddr, *bios, v;
 	volatile struct global_data *gd;
@@ -757,6 +971,45 @@
 	volatile struct board_chan *bc;
 	struct channel *ch;
 
+	printk("%s\n", banner);
+
+	if (numcards <= 0)
+	{
+		printk("PC/Xx: No cards configured, exiting.\n");
+		return(0);
+	}
+
+	for (i=0;i<numcards;i++)
+		nbdevs += boards[i].numports;
+
+	if (nbdevs <= 0)
+	{
+		printk("PC/Xx: No devices activated, exiting.\n");
+		return(0);
+	}
+
+	/*
+	 * this turns out to be more memory efficient, as there are no 
+	 * unused spaces.  There is *NO* way I'm going to explain these
+	 * convoluted casts, suffice it to say they WORK!  :)
+	 */
+	digi_channels = (struct channel **) kmalloc(sizeof(struct channel) * nbdevs, GFP_KERNEL);
+	if (!digi_channels)
+		panic("Unable to allocate digi_channel struct");
+
+	pcxe_table = (struct tty_struct *(*)[]) kmalloc(sizeof(struct tty_struct *) * nbdevs, GFP_KERNEL);
+	if (!pcxe_table)
+		panic("Unable to allocate pcxe_table struct");
+
+	pcxe_termios = (struct termios *(*)[]) kmalloc(sizeof(struct termios *) * nbdevs, GFP_KERNEL);
+	if (!pcxe_termios)
+		panic("Unable to allocate pcxe_termios struct");
+
+	pcxe_termios_locked = (struct termios *(*)[]) kmalloc(sizeof(struct termios *) * nbdevs, GFP_KERNEL);
+	if (!pcxe_termios_locked)
+		panic("Unable to allocate pcxe_termios_locked struct");
+
+
 	init_bh(DIGI_BH,do_pcxe_bh);
 	enable_bh(DIGI_BH);
 
@@ -766,18 +1019,21 @@
 	memset(&pcxe_driver, 0, sizeof(struct tty_driver));
 	pcxe_driver.magic = TTY_DRIVER_MAGIC;
 	pcxe_driver.name = "ttyd";
-	pcxe_driver.major = DIGIMAJOR; 
+	pcxe_driver.major = DIGI_MAJOR; 
 	pcxe_driver.minor_start = 0;
-	pcxe_driver.num = NBDEVS;
+
+	pcxe_driver.num = nbdevs;
+
 	pcxe_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	pcxe_driver.subtype = SERIAL_TYPE_NORMAL;
 	pcxe_driver.init_termios = tty_std_termios;
 	pcxe_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
 	pcxe_driver.flags = TTY_DRIVER_REAL_RAW;
 	pcxe_driver.refcount = &pcxe_refcount;
-	pcxe_driver.table = pcxe_table;
-	pcxe_driver.termios = pcxe_termios;
-	pcxe_driver.termios_locked = pcxe_termios_locked;
+
+	pcxe_driver.table = *pcxe_table;
+	pcxe_driver.termios = *pcxe_termios;
+	pcxe_driver.termios_locked = *pcxe_termios_locked;
 
 	pcxe_driver.open = pcxe_open;
 	pcxe_driver.close = pcxe_close;
@@ -797,28 +1053,33 @@
 
 	pcxe_callout = pcxe_driver;
 	pcxe_callout.name = "ttyD";
-	pcxe_callout.major = DIGICUMAJOR;
+	pcxe_callout.major = DIGICU_MAJOR;
 	pcxe_callout.subtype = SERIAL_TYPE_CALLOUT;
 	pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
 
+#if 0 
+
+/* strangely enough, this is FALSE */
+
 	/* 
 	 * loops_per_sec hasn't been set at this point :-(, so fake it out... 
 	 * I set it so that I can use the __delay() function.
 	 */
 	save_loops_per_sec = loops_per_sec;
 	loops_per_sec = 13L*500000L;
+#endif
 
 	save_flags(flags);
 	cli();
 
-	for(crd=0; crd < NUMCARDS; crd++) {
+	for(crd=0; crd < numcards; crd++) {
 		bd = &boards[crd];
 		outb(FEPRST, bd->port);
 		pcxxdelay(1);
 
 		for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) {
 			if(i > 1000) {
-				printk("Board not found at port 0x%x! Check switch settings.\n",
+				printk("PC/Xx: Board not found at port 0x%x! Check switch settings.\n",
 					bd->port);
 				bd->status = DISABLED;
 				break;
@@ -854,7 +1115,7 @@
 		} else {
 			if((v & 0x1) == 0x1) {
 				bd->status = DISABLED;   /* PC/Xm unsupported card */
-				printk("PC/Xm at 0x%x not supported!!\n", bd->port);
+				printk("PC/Xx: PC/Xm at 0x%x not supported!!\n", bd->port);
 				continue;
 			} else {
 				if(v & 0xC0) {    
@@ -877,7 +1138,7 @@
 
 		for(i=0; (inb(bd->port) & FEPMASK) != (FEPRST|FEPMEM); i++) {
 			if(i > 10000) {
-				printk("%s not resetting at port 0x%x! Check switch settings.\n",
+				printk("PC/Xx: %s not resetting at port 0x%x! Check switch settings.\n",
 					board_desc[bd->type], bd->port);
 				bd->status = DISABLED;
 				break;
@@ -893,7 +1154,7 @@
 
 		if(*(ulong *)(memaddr + botwin) != 0xa55a3cc3 ||
 					*(ulong *)(memaddr + topwin) != 0x5aa5c33c) {
-			printk("Failed memory test at %lx for %s at port %x, check switch settings.\n",
+			printk("PC/Xx: Failed memory test at %lx for %s at port %x, check switch settings.\n",
 				bd->membase, board_desc[bd->type], bd->port);
 			bd->status = DISABLED;
 			continue;
@@ -917,7 +1178,7 @@
 				pcxxdelay(1);
 			}
 
-			printk("BIOS download failed on the %s at 0x%x!\n",
+			printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
 							board_desc[bd->type], bd->port);
 			bd->status = DISABLED;
 			continue;
@@ -939,7 +1200,7 @@
 				pcxxdelay(1);
 			}
 
-			printk("BIOS download failed on the %s at 0x%x!\n",
+			printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
 				board_desc[bd->type], bd->port);
 			bd->status = DISABLED;
 			continue;
@@ -966,7 +1227,7 @@
 
 		for(i=0; *(ushort *)((ulong)memaddr + MBOX); i++) {
 			if(i > 2000) {
-				printk("Command failed for the %s at 0x%x!\n",
+				printk("PC/Xx: Command failed for the %s at 0x%x!\n",
 					board_desc[bd->type], bd->port);
 				bd->status = DISABLED;
 				break;
@@ -988,7 +1249,7 @@
 
 		for(i=0; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
 			if(i > 10000) {
-				printk("FEP/OS download failed on the %s at 0x%x!\n",
+				printk("PC/Xx: FEP/OS download failed on the %s at 0x%x!\n",
 					board_desc[bd->type], bd->port);
 				bd->status = DISABLED;
 				break;
@@ -998,8 +1259,8 @@
 		if(bd->status == DISABLED)
 			continue;
 
-		ch = &digi_channels[MAXPORTS * crd];
-		pcxxassert(ch <= &digi_channels[NBDEVS-1], "ch out of range");
+		ch = &((*digi_channels)[bd->first_minor]);
+		pcxxassert(ch < &((*digi_channels)[nbdevs]), "ch out of range");
 
 		bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT);
 		gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL);
@@ -1035,7 +1296,7 @@
 			ch->boardnum = crd;
 			ch->channelnum = i;
 
-			ch->dev = (MAXPORTS * crd) + i;
+			ch->dev = bd->first_minor + i;
 			ch->tty = 0;
 
 			if(shrinkmem) {
@@ -1089,8 +1350,9 @@
 			ch->close_wait = 0;
 		}
 
-		printk("DigiBoard PC/Xx Driver V%s:  %s I/O=0x%x Mem=0x%lx Ports=%d\n", 
-				VERSION, board_desc[bd->type], bd->port, bd->membase, bd->numports);
+		printk("PC/Xx: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
+			board_desc[bd->type], board_mem[bd->type], bd->port, 
+			bd->membase, bd->numports);
 
 		memwinoff(bd, 0);
 	}
@@ -1101,8 +1363,9 @@
 	if(tty_register_driver(&pcxe_callout))
 		panic("Couldn't register PC/Xe callout");
 
-
+#if 0
 	loops_per_sec = save_loops_per_sec;  /* reset it to what it should be */
+#endif
 
 	/*
 	 * Start up the poller to check for events on all enabled boards
@@ -1125,9 +1388,10 @@
 	save_flags(flags);
 	cli();
 
-	for(crd=0; crd < NUMCARDS; crd++) {
+	for(crd=0; crd < numcards; crd++) {
 		bd = &boards[crd];
-		ch = &digi_channels[MAXPORTS*crd];
+
+		ch = &((*digi_channels)[bd->first_minor]);
 
 		if(bd->status == DISABLED)
 			continue;
@@ -1164,8 +1428,9 @@
 
 	bd = &boards[crd];
 
-	chan0 = &digi_channels[MAXPORTS * crd];
-	pcxxassert(chan0 <= &digi_channels[NBDEVS-1], "ch out of range");
+	chan0 = &((*digi_channels)[bd->first_minor]);
+	pcxxassert(chan0 < &((*digi_channels)[nbdevs]), "ch out of range");
+
 
 	assertgwinon(chan0);
 
@@ -1292,12 +1557,15 @@
 
 	if(bytecmd) {
 		*(unchar *)(memaddr+head+CSTART+0) = cmd;
-		*(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch);
+
+		*(unchar *)(memaddr+head+CSTART+1) = ch->dev - ch->board->first_minor;
+
 		*(unchar *)(memaddr+head+CSTART+2) = word_or_byte;
 		*(unchar *)(memaddr+head+CSTART+3) = byte2;
 	} else {
 		*(unchar *)(memaddr+head+CSTART+0) = cmd;
-		*(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch);
+
+		*(unchar *)(memaddr+head+CSTART+1) = ch->dev - ch->board->first_minor;
 		*(ushort*)(memaddr+head+CSTART+2) = word_or_byte;
 	}
 
@@ -1660,7 +1928,7 @@
 					break;
 
 				case TIOCMBIC:
-					ch->modemfake |= mflag;
+					ch->modemfake &= ~mflag;
 					ch->modem &= ~mflag;
 					break;
 			}
@@ -1964,4 +2232,3 @@
 	memoff(ch);
 	restore_flags(flags);
 }
-
diff -u --recursive --new-file v1.3.84/linux/drivers/char/pcxx.h linux/drivers/char/pcxx.h
--- v1.3.84/linux/drivers/char/pcxx.h	Wed Apr  3 16:06:55 1996
+++ linux/drivers/char/pcxx.h	Sun Apr  7 09:42:34 1996
@@ -14,16 +14,29 @@
 #define	FEPMASK     0x0e
 #define	FEPWIN      0x80
 
-#define PCXI    0
-#define PCXE    1
-#define	PCXEVE	2
+/* Maximum Number of Boards supported */
+#define MAX_DIGI_BOARDS 4
+
+#define PCXX_NUM_TYPES	4
+
+#define PCXI		0
+#define PCXE		1
+#define	PCXEVE		2
+#define PCXEM		3
 
 static char *board_desc[] = {
-	"PC/Xi (64K)",
-	"PC/Xe (64K)",
-	"PC/Xe (8K) ",
+	"PC/Xi",
+	"PC/Xe",
+	"PC/Xeve",
+	"PC/Xem",
 };
 
+static char *board_mem[] = {
+	"64k",
+	"64k",
+	"8k",
+	"32k",
+};
 #define STARTC      021
 #define STOPC       023
 #define IAIXON      0x2000
@@ -36,6 +49,7 @@
 	ushort numports;
 	ushort port;
 	ulong  membase;
+	ushort first_minor;
 };
 
 
diff -u --recursive --new-file v1.3.84/linux/drivers/char/pcxxconfig.h linux/drivers/char/pcxxconfig.h
--- v1.3.84/linux/drivers/char/pcxxconfig.h	Wed Apr  3 16:06:55 1996
+++ linux/drivers/char/pcxxconfig.h	Thu Jan  1 02:00:00 1970
@@ -1,5 +0,0 @@
-#define NUMCARDS 1
-
-struct board_info boards[NUMCARDS]={
-{	ENABLED, 0, ON, 16, 0x200, 0xd0000 }
-};
diff -u --recursive --new-file v1.3.84/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.3.84/linux/drivers/char/serial.c	Fri Mar  8 09:43:24 1996
+++ linux/drivers/char/serial.c	Sun Apr  7 09:42:33 1996
@@ -171,18 +171,20 @@
 /* You can have up to four HUB6's in the system, but I've only
  * included two cards here for a total of twelve ports.
  */
+#ifdef CONFIG_HUB6
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS32 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS33 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS34 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS35 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS36 */
-	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS37 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS38 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS39 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS40 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS41 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
+#endif
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
@@ -2426,7 +2428,7 @@
  */
 static void show_serial_version(void)
 {
-	printk("Serial driver version 4.11 with");
+	printk("Serial driver version 4.11a with");
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
 #define SERIAL_OPT
diff -u --recursive --new-file v1.3.84/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.84/linux/drivers/net/Config.in	Tue Apr  2 13:32:20 1996
+++ linux/drivers/net/Config.in	Sun Apr  7 08:47:04 1996
@@ -78,7 +78,7 @@
   fi
   tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
   tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5
-# tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP
+  tristate 'DEC 21040 PCI support' CONFIG_DEC_ELCP
 # bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100
 # bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32
   bool 'Zenith Z-Note support' CONFIG_ZNET
diff -u --recursive --new-file v1.3.84/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.84/linux/drivers/net/Space.c	Tue Apr  2 13:32:20 1996
+++ linux/drivers/net/Space.c	Sun Apr  7 08:47:04 1996
@@ -38,6 +38,7 @@
    ethernet adaptor have the name "eth[0123...]".
    */
 
+extern int tulip_probe(struct device *dev);
 extern int hp100_probe(struct device *dev);
 extern int ultra_probe(struct device *dev);
 extern int wd_probe(struct device *dev);
@@ -91,6 +92,9 @@
 #endif
 #if defined(CONFIG_SEEQ8005)
 	&& seeq8005_probe(dev)
+#endif
+#if defined(CONFIG_DEC_ELCP)
+	&& tulip_probe(dev)
 #endif
 #if defined(CONFIG_HP100)
 	&& hp100_probe(dev)
diff -u --recursive --new-file v1.3.84/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v1.3.84/linux/drivers/net/ibmtr.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/net/ibmtr.c	Fri Apr  5 13:33:10 1996
@@ -153,7 +153,7 @@
 int tok_probe(struct device *dev);
 unsigned char get_sram_size(struct tok_info *adapt_info);
 
-static void tok_init_card(unsigned long dev_addr);
+static int tok_init_card(struct device *dev);
 int trdev_init(struct device *dev);
 void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
@@ -543,14 +543,8 @@
 
 	dev->base_addr=PIOaddr; /* set the value for device */
 	
-	dev->open=tok_open;
-	dev->stop=tok_close;
-	dev->hard_start_xmit=tok_send_packet;
-	dev->get_stats = NULL;
-	dev->get_stats = tok_get_stats;
-	dev->set_multicast_list = NULL;
-	tr_setup(dev);
-	tok_init_card((unsigned long)dev);
+	trdev_init(dev);
+	tok_init_card(dev);
 	
 	return 0;  /* Return 0 to indicate we have found a Token Ring card. */
 }
@@ -577,11 +571,31 @@
 		return 1<<((readb(adapt_info->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD)>>2)+4);
 }
 
+int trdev_init(struct device *dev)
+{
+  struct tok_info *ti=(struct tok_info *)dev->priv;
+
+  ti->open_status=CLOSED;
+
+  dev->init=tok_init_card;
+  dev->open=tok_open;
+  dev->stop=tok_close;
+  dev->hard_start_xmit=tok_send_packet;
+  dev->get_stats = NULL;
+  dev->get_stats = tok_get_stats;
+  dev->set_multicast_list = NULL;
+  tr_setup(dev);
+
+  return 0;
+}
+
+
+
 static int tok_open(struct device *dev) 
 {
 	struct tok_info *ti=(struct tok_info *)dev->priv;
 	
-	if (ti->open_status==CLOSED) tok_init_card((unsigned long)dev);
+	if (ti->open_status==CLOSED) tok_init_card(dev);
 	
 	if (ti->open_status==IN_PROGRESS) sleep_on(&ti->wait_for_reset);
 	
@@ -650,13 +664,31 @@
 			outb(0, ti->global_int_enable);
 		
 		status=readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_ODD);
+		#ifdef PCMCIA
+      		/* Check if the PCMCIA card was pulled. */
+    		if (status == 0xFF)
+       		{
+		          DPRINTK("PCMCIA card removed.\n");
+        		  dev->interrupt = 0;
+          		  return;
+       		}
+
+    	        /* Check ISRP EVEN too. */
+      	        if ( *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) == 0xFF)
+    	        {
+         		 DPRINTK("PCMCIA card removed.\n");
+         		 dev->interrupt = 0;
+         		 return;
+      		 }
+		#endif
+
 		
 		if (status & ADAP_CHK_INT) {
 			
 			int i;
 			__u32 check_reason;
 
-			check_reason=ti->mmio + ntohs(readw(ti->mmio + ACA_OFFSET + ACA_RW +WWCR_EVEN));
+			check_reason=ti->mmio + ntohs(readw(ti->sram + ACA_OFFSET + ACA_RW +WWCR_EVEN));
 			
 			DPRINTK("Adapter check interrupt\n");
 			DPRINTK("8 reason bytes follow: ");
@@ -1046,12 +1078,11 @@
 	tok_open_adapter((unsigned long)dev);
 }
 
-static void tok_init_card(unsigned long dev_addr) 
+static int tok_init_card(struct device *dev) 
 {
 	struct tok_info *ti;
 	short PIOaddr;
 	int i;
-	struct device *dev=(struct device *)dev_addr;
 	PIOaddr = dev->base_addr;
 	ti=(struct tok_info *) dev->priv;
 	
@@ -1082,7 +1113,7 @@
 	
 	ti->open_status=IN_PROGRESS;
 	writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN);
-		
+	return 0;	
 }
 
 static void open_sap(unsigned char type,struct device *dev) 
diff -u --recursive --new-file v1.3.84/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.3.84/linux/drivers/net/plip.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/net/plip.c	Sun Apr  7 09:45:56 1996
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.15 1995/10/03 01:47:09 gniibe Exp $ */
+/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -26,6 +26,7 @@
  *			# insmod plip.o io=0x3bc irq=7
  *		  - MTU fix.
  *		  - Make sure other end is OK, before sending a packet.
+ *		  - Fix immediate timer problem.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -51,7 +52,7 @@
  *     To use with DOS box, please do (Turn on ARP switch):
  *	# ifconfig plip[0-2] arp
  */
-static const char *version = "NET3 PLIP version 2.1 gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
 
 /*
   Sources:
@@ -591,6 +592,7 @@
 			nl->connection = PLIP_CN_SEND;
 			sti();
 			queue_task(&nl->immediate, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
 			outb(PAR_INTR_ON, PAR_CONTROL(dev));
 			enable_irq(dev->irq);
 			return OK;
diff -u --recursive --new-file v1.3.84/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v1.3.84/linux/drivers/net/strip.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/net/strip.c	Sun Apr  7 09:42:34 1996
@@ -256,8 +256,8 @@
 		/*
 		 *	Recover state from last call, if applicable 
 		 */
-		code  = *code_ptr & Stuff_CodeMask;
-		count = *code_ptr & Stuff_CountMask;
+		code  = (*code_ptr ^ Stuff_Magic) & Stuff_CodeMask;
+		count = (*code_ptr ^ Stuff_Magic) & Stuff_CountMask;
 	}
 
 	while (src < end) 
diff -u --recursive --new-file v1.3.84/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v1.3.84/linux/drivers/net/tulip.c	Fri Mar  1 07:50:49 1996
+++ linux/drivers/net/tulip.c	Sun Apr  7 08:46:30 1996
@@ -14,9 +14,56 @@
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 */
 
-static const char *version = "tulip.c:v0.05 1/20/95 becker@cesdis.gsfc.nasa.gov\n";
+static char *version =
+"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n"
+"        +0.68 3/09/96 "
+"http://www.dsl.tutics.tut.ac.jp/~manabe/linux/tulip.html\n";
+
+/* A few user-configurable values. */
+
+/* Default to using non-10baseT (i.e. AUI/10base2/100baseT port) port. */
+#define	TULIP_10TP_PORT		0
+#define	TULIP_100TP_PORT	1
+#define	TULIP_AUI_PORT		1
+#define	TULIP_BNC_PORT		2
 
+#ifndef	TULIP_PORT
+#define	TULIP_PORT			TULIP_10TP_PORT
+#endif
+
+/* Define to force full-duplex operation on all Tulip interfaces. */
+/* #define  TULIP_FULL_DUPLEX 1 */
+
+/* Define to probe only first detected device */
+/* #define	TULIP_ONLY_ONE 1 */
+
+#include <linux/config.h>
+
+#if defined(MODULE) && defined(CONFIG_MODVERSIONS)
+#define	MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x10300
+/* i.e. version 1.2.x */
+#define	virt_to_bus(address)	(unsigned long)(address)
+#define	bus_to_virt(address)	(void *)(address)
+#define	PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014
+#ifdef	MODULE
+#include <linux/module.h>
+char kernel_version[] = UTS_RELEASE;
+#else
+#undef	MOD_INC_USE_COUNT
+#undef	MOD_DEC_USE_COUNT
+#define	MOD_INC_USE_COUNT
+#define	MOD_DEC_USE_COUNT
+#endif
+#else
+/* i.e. version 1.3.x */
 #include <linux/module.h>
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -28,6 +75,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
+#include <asm/byteorder.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -40,16 +88,10 @@
    longword-wide registers on a quadword boundary. */
 #define TULIP_TOTAL_SIZE 0x80
 
-#ifdef HAVE_DEVLIST
-struct netdev_entry tulip_drv =
-{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
-#endif
-
-#define TULIP_DEBUG 1
 #ifdef TULIP_DEBUG
 int tulip_debug = TULIP_DEBUG;
 #else
-int tulip_debug = 1;
+int tulip_debug = 3;
 #endif
 
 /*
@@ -59,7 +101,9 @@
 
 This device driver is designed for the DECchip 21040 "Tulip", Digital's
 single-chip ethernet controller for PCI, as used on the SMC EtherPower
-ethernet adapter.
+ethernet adapter.  It also works with boards based the 21041 (new/experimental)
+and 21140 (10/100mbps).
+
 
 II. Board-specific settings
 
@@ -107,160 +151,498 @@
 register of the set CSR12-15 written.   Hmmm, now how is that possible?
 */
 
-#define DEC_VENDOR_ID	0x1011		/* Hex 'D' :-> */
-#define DEC_21040_ID	0x0002		/* Change for 21140. */
 
+/* A few values that may be tweaked. */
 /* Keep the ring sizes a power of two for efficiency. */
 #define TX_RING_SIZE	4
 #define RX_RING_SIZE	4
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
+/* This is a mysterious value that can be written to CSR11 in the 21040
+   to detect a full-duplex frame.  No one knows what it should be, but if
+   left at its default value some 10base2(!) packets trigger a
+   full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC	0x6969
+
+/* The rest of these values should never change. */
+
+#define PCI_DEVICE_ID_NONE	0xFFFF
+#define	ETHNAMSIZ	8
+#define	ROUND_UP(size, n)	((size + n - 1) & ~(n - 1))
+
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
 enum tulip_offsets {
-	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
-	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
-	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+				/* 21040		21041		21140		*/
+	CSR0=0,		/* BUS mode								*/
+    CSR1=0x08,	/* TX poll demand						*/
+	CSR2=0x10,	/* RX poll demand						*/
+	CSR3=0x18,	/* RX ring base addr					*/
+	CSR4=0x20,	/* TX ring base addr					*/
+	CSR5=0x28,	/* Status								*/
+	CSR6=0x30,	/* Command mode							*/
+	CSR7=0x38,	/* Interrupt Mask						*/
+	CSR8=0x40,	/* Missed frame counter					*/
+	CSR9=0x48,	/* Eth.addrROM	SROM mii	SROM mii	*/
+	CSR10=0x50,	/* Diagn.		boot ROM	-			*/
+	CSR11=0x58,	/* Full duplex	G.P. timer	G.P. timer	*/
+	CSR12=0x60,	/* SIA status				G.P.		*/
+	CSR13=0x68,	/* SIA connectivity			-			*/
+	CSR14=0x70,	/* SIA TX/RX				-			*/
+	CSR15=0x78	/* SIA general				watchdog	*/
+};
 
+/* description of CSR0 bus mode register */
+#define	TBMOD_RESERVED		0xfff80000	/* I don't know */
+#define	TBMOD_RESET			0x00000001
+#define	TBMOD_BIGENDIAN		0x00000080
+/*
+	   Cache alignment bits 15:14	     Burst length 13:8
+    	0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
+*/
+#define	TBMOD_ALIGN0		0x00000000	/* no cache alignment */
+#define	TBMOD_ALIGN8		0x00004000	/* 8 longwords */
+#define	TBMOD_ALIGN16		0x00008000
+#define	TBMOD_ALIGN32		(TBMOD_ALIGN8|TBMOD_ALIGN16)
+#define	TBMOD_BURST0		0x00000000	/* unlimited=rx buffer size */
+#define	TBMOD_BURST1		0x00000100	/* 1 longwords */
+#define	TBMOD_BURST2		0x00000200
+#define	TBMOD_BURST4		0x00000400
+#define	TBMOD_BURST8		0x00000800
+#define	TBMOD_BURST16		0x00001000
+#define	TBMOD_BURST32		0x00002000
+
+/* description of CSR1 Tx poll demand register */
+/* description of CSR2 Rx poll demand register */
+#define	TPOLL_START			0x00000001	/* ? */
+#define	TPOLL_TRIGGER		0x00000000	/* ? */
+
+/* description of CSR5 status register from de4x5.h */
+#define	TSTAT_BUSERROR		0x03800000
+#define	TSTAT_SYSERROR		0x00002000
+#define	TSTAT_TxSTAT		0x00700000
+#define	TSTAT_RxSTAT		0x000e0000
+#define	TSTAT_LKFAIL		0x00001000
+#define	TSTAT_NORINTR		0x00010000	/* Normal interrupt */
+#define	TSTAT_ABNINTR		0x00008000	/* Abnormal interrupt */
+#define	TSTAT_RxMISSED		0x00000100	/* Rx frame missed */
+#define	TSTAT_RxUNABL		0x00000080
+#define	TSTAT_RxINTR		0x00000040
+#define	TSTAT_LKPASS		0x00000010
+#define	TSTAT_TEXPIRED		0x00000800	/* Timer Expired */
+#define	TSTAT_TxTOUT		0x00000008
+#define	TSTAT_TxUNABL		0x00000004
+#define	TSTAT_TxINTR		0x00000001
+#define	TSTAT_CLEARINTR		0x0001ffff	/* clear all interrupt sources */
+
+/* description of CSR6 command mode register */
+#define	TCMOD_SCRM			0x01000000	/* scrambler mode */
+#define	TCMOD_PCS			0x00800000	/* PCS function */
+#define	TCMOD_TxTHMODE		0x00400000	/* Tx threshold mode */
+#define	TCMOD_SW100TP		0x00040000  /* 21140: 100MB */
+#define	TCMOD_CAPTURE		0x00020000	/* capture effect */
+#define	TCMOD_FULLDUPLEX	0x00000200
+#define TCMOD_TH128			0x00008000	/* 10 - 128 bytes threshold */
+#define	TCMOD_TxSTART		0x00002000
+#define	TCMOD_RxSTART		0x00000002
+#define	TCMOD_ALLMCAST		0x00000080	/* pass all multicast */
+#define	TCMOD_PROMISC		0x00000040	/* promisc */
+#define	TCMOD_BOFFCOUNTER	0x00000020	/* backoff counter */
+#define	TCMOD_INVFILTER		0x00000010	/* invert filtering */
+#define	TCMOD_HONLYFILTER	0x00000004	/* hash only filtering */
+#define TCMOD_HPFILTER		0x00000001	/* hash/perfect Rx filtering */
+#define	TCMOD_MODEMASK		(TCMOD_ALLMCAST|TCMOD_PROMISC)
+#define	TCMOD_FILTERMASK	(TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER)
+#define	TCMOD_TRxSTART		(TCMOD_TxSTART|TCMOD_RxSTART)
+#define	TCMOD_BASE			(TCMOD_CAPTURE|TCMOD_BOFFCOUNTER)
+#define	TCMOD_10TP			(TCMOD_TxTHMODE|TCMOD_BASE)
+#define	TCMOD_100TP			(TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE)
+#define	TCMOD_AUTO			(TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP)
+
+/* description of CSR7 interrupt mask register */
+#define	TINTR_ENABLE		0xFFFFFFFF
+#define	TINTR_DISABLE		0x00000000
+
+/* description of CSR12 SIA status(2104x)/GP(21140) register */
+#define	TSIAS_CONERROR		0x00000002	/* connection error */
+#define	TSIAS_LNKERROR		0x00000004	/* link error */
+#define	TGEPR_LK10NG		0x00000080	/* 10Mbps N.G. (R) */
+#define	TGEPR_LK100NG		0x00000040	/* 100Mbps N.G. (R) */
+#define	TGEPR_DETECT		0x00000020	/* detect signal (R) */
+#define	TGEPR_HALFDUPLEX	0x00000008	/* half duplex (W) */
+#define	TGEPR_PHYLOOPBACK	0x00000004	/* PHY loopback (W) */
+#define	TGEPR_FORCEALED		0x00000002	/* force activity LED on (W) */
+#define	TGEPR_FORCE100		0x00000001	/* force 100Mbps mode */
+
+/* description of CSR13 SIA connectivity register */
+#define	TSIAC_OUTEN			0x0000e000	/* 21041: Output enable */
+#define TSIAC_SELED			0x00000f00	/* 21041: AUI or TP with LEDs */
+#define	TSIAC_INEN			0x00001000	/* 21041: Input enable */
+#define	TSIAC_NO10TP		0x00000008	/* 10baseT(0) or not(1) */
+#define TSIAC_CONFIG		0x00000004	/* Configuration */
+#define	TSIAC_SWRESET		0x00000001	/* 21041: software reset */
+#define	TSIAC_RESET			0x00000000	/* reset */
+#define	TSIAC_C21041		(TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET)
+#define	TSIAC_C21040		TSIAC_CONFIG
+
+/* description of CSR14 SIA TX/RX register */
+#define	TSIAX_NO10TP		0x0000f73d
+#define	TSIAX_10TP			0x0000ff3f
+
+/* description of CSR15 SIA general register */
+#define	TSIAG_SWBNCAUI		0x00000008 /* BNC(0) or AUI(1) */
+#define	TSIAG_BNC			0x00000006
+#define	TSIAG_AUI			(TSIAG_BNC|TSIAG_SWBNCAUI)
+#define	TSIAG_10TP			0x00000000
+
+/* description of rx_ring.status */
+#define	TRING_OWN			0x80000000	/* Owned by chip */
+#define	TRING_CLEAR			0x00000000	/* clear */
+#define	TRING_ERROR			0x00008000	/* error summary */
+#define	TRING_ETxTO			0x00004000	/* Tx time out */
+#define	TRING_ELCOLL		0x00000200	/* late collition */
+#define	TRING_EFCOLL		0x00000100	/* fatal collition */
+#define	TRING_ELCARR		0x00000800	/* carrier lost */
+#define	TRING_ENCARR		0x00000400	/* no carrier */
+#define	TRING_ENOHB			0x00000080	/* heartbeat fail */
+#define	TRING_ELINK			0x00000004	/* link fail */
+#define	TRING_EUFLOW		0x00000002	/* underflow */
+
+#define	TRING_ELEN			0x00004000	/* length error */
+#define	TRING_FDESC			0x00000200	/* first descriptor */
+#define	TRING_LDESC			0x00000100	/* last descriptor */
+#define	TRING_ERUNT			0x00000800	/* runt frame */
+#define	TRING_ELONG			0x00000080	/* frame too long */
+#define	TRING_EWATCHDOG		0x00000010	/* receive watchdog */
+#define	TRING_EDRBIT		0x00000004	/* dribble bit */
+#define	TRING_ECRC			0x00000002	/* CRC error */
+#define	TRING_EOVERFLOW		0x00000001	/* overflow */
+
+#define	TRING_RxDESCMASK	(TRING_FDESC|TRING_LDESC)
+#define	TRING_RxLENGTH		(TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG)
+#define	TRING_RxFRAME		(TRING_EDRBIT)
+#define	TRING_RxCRC			(TRING_ECRC)
+#define	TRING_RxFIFO		(TRING_EOVERFLOW)
+#define	TRING_TxABORT		(TRING_ETxTO|TRING_EFCOLL|TRING_ELINK)
+#define	TRING_TxCARR		(TRING_ELCARR|TRING_ENCARR)
+#define	TRING_TxWINDOW		(TRING_ELCOLL)
+#define	TRING_TxFIFO		(TRING_EUFLOW)
+#define	TRING_TxHEARTBEAT	(TRING_ENOHB)
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
-	int status;
-	int length;
-	char *buffer1, *buffer2;			/* We use only buffer 1.  */
+	s32 status;
+	s32 length;
+	u32 buffer1, buffer2;			/* We use only buffer 1.  */
 };
 
 struct tulip_tx_desc {
-	int status;
-	int length;
-	char *buffer1, *buffer2;			/* We use only buffer 1.  */
+	s32 status;
+	s32 length;
+	u32 buffer1, buffer2;			/* We use only buffer 1.  */
 };
 
 struct tulip_private {
-	char devname[8];			/* Used only for kernel debugging. */
 	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
 	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	long rx_buffs;				/* Address of temporary Rx buffers. */
+	char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ];
+	/* temporary Rx buffers. */
 	struct enet_statistics stats;
-	int setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	int setup_frame[48];	/* Pseudo-Tx frame to init address table. */
+	void (*port_select)(struct device *dev);
+	int (*port_error)(struct device *dev);
+	char *signature;
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	unsigned int tx_full:1;
-	int pad0, pad1;						/* Used for 8-byte alignment */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+};
+
+struct eeprom {
+    union {
+		struct { /* broken EEPROM structure */
+			u_char addr[ETH_ALEN];
+		} ng;
+		struct { /* DEC EtherWorks
+					and other cards which have correct eeprom structure */
+			u_char dum1[20];
+			u_char addr[ETH_ALEN];
+		} ok;
+    } hw;
+#define	ng_addr	hw.ng.addr
+#define	ok_addr	hw.ok.addr
+#define	EE_SIGNLEN	14		/* should be 102 ? */
+	u_char sign[EE_SIGNLEN];
 };
 
-static void tulip_probe1(int ioaddr, int irq);
+static int read_eeprom(int ioaddr, struct eeprom *eepp);
 static int tulip_open(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void tulip_interrupt(int irq, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
 static struct enet_statistics *tulip_get_stats(struct device *dev);
+static struct device *tulip_alloc(struct device *dev);
+#if LINUX_VERSION_CODE < 0x10300
+static void set_multicast_list(struct device *dev, int num_addrs,
+							   void *addrs);
+#else
 static void set_multicast_list(struct device *dev);
-static int set_mac_address(struct device *dev, void *addr);
+#endif
+
+#define	generic21140_error	NULL
+static void generic21040_select(struct device *dev);
+static void generic21140_select(struct device *dev);
+static void generic21041_select(struct device *dev);
+static void auto21140_select(struct device *dev);
+static int generic21040_error(struct device *dev);
+static int generic21041_error(struct device *dev);
+
+static struct {
+	void (*port_select)(struct device *dev);
+	int (*port_error)(struct device *dev);
+	unsigned int vendor_id, device_id;
+	char *signature;
+	unsigned int port_auto:1;
+} cardVendor[] = {
+	{generic21140_select, generic21140_error,
+		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0},
+	{generic21041_select, generic21041_error,
+		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0},
+	{generic21040_select, generic21040_error,
+		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0},
+	{auto21140_select, generic21140_error,
+		 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 1},
+	{generic21140_select, generic21140_error,
+		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0},
+	{generic21041_select, generic21041_error,
+		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0},
+	{generic21040_select, generic21040_error,
+		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0},
+	{generic21040_select, generic21040_error,
+		 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0},
+	{generic21040_select, generic21040_error,
+		 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 0},
+	{generic21040_select, generic21040_error,
+		 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0},
+	{generic21140_select, generic21140_error,
+		 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0},
+	{generic21041_select, generic21041_error,
+		 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0},
+	{NULL, NULL, 0, 0, "Unknown", 0}
+};
 
 
+/* Serial EEPROM section.
+   A "bit" grungy, but we work our way through bit-by-bit :->. */
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS			0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   This is a "nasty" timing loop, but PC compatible machines are *supposed*
+   to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */
+
+#define eeprom_delay(nanosec)\
+	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ_CMD		(6 << 6)
+#define EE_ERASE_CMD	(7 << 6)
 
-#ifndef MODULE
-/* This 21040 probe is unlike most other board probes.  We can use memory
-   efficiently by allocating a large contiguous region and dividing it
-   ourselves.  This is done by having the initialization occur before
-   the 'kmalloc()' memory management system is started. */
-
-int dec21040_init(void)
-{
-
-    if (pcibios_present()) {
-	    int pci_index;
-		for (pci_index = 0; pci_index < 8; pci_index++) {
-			unsigned char pci_bus, pci_device_fn, pci_irq_line;
-			unsigned long pci_ioaddr;
-		
-			if (pcibios_find_device (DEC_VENDOR_ID, DEC_21040_ID, pci_index,
-									 &pci_bus, &pci_device_fn) != 0)
-				break;
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-									 PCI_INTERRUPT_LINE, &pci_irq_line);
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
-			/* Remove I/O space marker in bit 0. */
-			pci_ioaddr &= ~3;
-			if (tulip_debug > 2)
-				printk("Found DEC PCI Tulip at I/O %#lx, IRQ %d.\n",
-					   pci_ioaddr, pci_irq_line);
-			tulip_probe1(pci_ioaddr, pci_irq_line);
-		}
-	}
+#ifdef MODULE
+static u_long alloc_size;
+#endif
 
-	return 0;
-}
+#ifdef	__i386__
+#define	tio_write(val, port)	outl(val, ioaddr + port)
+#define	tio_read(port)			inl(ioaddr + port)
 #endif
-#ifdef MODULE
-static int tulip_probe(struct device *dev)
+
+static void inline
+tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15)
 {
-	printk("tulip: This driver does not yet install properly from module!\n");
-	return -1;
+	tio_write(0,CSR13);
+	tio_write(val15,CSR15);
+	tio_write(val14,CSR14);
+	tio_write(val13,CSR13);
 }
-#endif
 
-static void tulip_probe1(int ioaddr, int irq)
+static char *
+card_type(struct tulip_private *tp, int device_id, int vendor_id)
 {
-	static int did_version = 0;			/* Already printed version info. */
-	struct device *dev;
-	struct tulip_private *tp;
-	int i;
+	int n;
 
-	if (tulip_debug > 0  &&  did_version++ == 0)
-		printk(version);
+	for (n = 0; cardVendor[n].device_id; n ++)
+		if (cardVendor[n].device_id == device_id
+			&& (cardVendor[n].vendor_id == vendor_id
+				|| cardVendor[n].vendor_id == 0)) break;
+	tp->port_select = cardVendor[n].port_select;
+	tp->port_error = cardVendor[n].port_error;
+	tp->signature = cardVendor[n].signature;
+	return(cardVendor[n].signature);
+}
 
-	dev = init_etherdev(0, 0);
+static int
+read_eeprom(int ioaddr, struct eeprom *eepp)
+{
+    int i, n;
+    unsigned short val = 0;
+    int read_cmd = EE_READ_CMD;
+    u_char *p=(u_char *)eepp;
+
+	for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) {
+		tio_write(EE_ENB & ~EE_CS, CSR9);
+		tio_write(EE_ENB, CSR9);
+
+		/* Shift the read command bits out. */
+		for (i = 10; i >= 0; i--) {
+			short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+			tio_write(EE_ENB | dataval, CSR9);
+			eeprom_delay(100);
+			tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9);
+			eeprom_delay(150);
+			tio_write(EE_ENB | dataval, CSR9);
+			/* Finish EEPROM a clock tick. */
+			eeprom_delay(250);
+		}
+		tio_write(EE_ENB, CSR9);
 
-	printk("%s: DEC 21040 Tulip at %#3x,", dev->name, ioaddr);
+		for (i = 16; i > 0; i--) {
+			tio_write(EE_ENB | EE_SHIFT_CLK, CSR9);
+			eeprom_delay(100);
+			val = (val << 1)
+				| ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0);
+			tio_write(EE_ENB, CSR9);
+			eeprom_delay(100);
+		}
 
-	/* Stop the chip's Tx and Rx processes. */
-	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
-	/* Clear the missed-packet counter. */
-	inl(ioaddr + CSR8) & 0xffff;
+		/* Terminate the EEPROM access. */
+		tio_write(EE_ENB & ~EE_CS, CSR9);
+		*p ++ = val;
+		*p ++ = val >> 8;
+    }
+	/* broken eeprom ? */
+	p = (u_char *)eepp;
+	for (i = 0; i < 8; i ++)
+		if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0);
+	return(-1); /* broken */
+}
 
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
-	for (i = 0; i < 6; i++) {
-		int value, boguscnt = 100000;
-		do
-			value = inl(ioaddr + CSR9);
-		while (value < 0  && --boguscnt > 0);
-		printk(" %2.2x", dev->dev_addr[i] = value);
-	}
-	printk(", IRQ %d\n", irq);
+/* Is this correct ? */
+static int
+generic21040_error(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
 
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, "DEC Tulip Ethernet");
+	return(tio_read(CSR12) & TSIAS_CONERROR);
+}
 
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
+static int
+generic21041_error(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
 
-	/* Make certain the data structures are quadword aligned. */
-	tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
-	dev->priv = tp;
-	tp->rx_buffs = kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_KERNEL | GFP_DMA);
+	return(tio_read(CSR12) & TSIAS_LNKERROR);
+}
 
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-	dev->set_multicast_list = &set_multicast_list;
-	dev->set_mac_address = &set_mac_address;
+static void
+generic21040_select(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
 
-	return;
+	dev->if_port &= 3;
+	printk("%s: enabling %s port.\n",
+		   dev->name, dev->if_port ?  "AUI":"10baseT");
+	/* Set the full duplux match frame. */
+	tio_write(FULL_DUPLEX_MAGIC, CSR11);
+	tio_write(TSIAC_RESET, CSR13);
+	/* Reset the serial interface */
+	tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13);
+}
+
+static void
+generic21041_select(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	u32 tsiac = TSIAC_C21041;
+	u32 tsiax = TSIAX_10TP;
+	u32 tsiag = TSIAG_10TP;
+
+	printk("%s: enabling ", dev->name);
+	switch(dev->if_port) {
+	case TULIP_AUI_PORT:
+		tsiac |= TSIAC_NO10TP;
+		tsiax = TSIAX_NO10TP;
+		tsiag = TSIAG_AUI;
+		printk("AUI");
+		break;
+	case TULIP_BNC_PORT:
+		tsiac |= TSIAC_NO10TP;
+		tsiax = TSIAX_NO10TP;
+		tsiag = TSIAG_BNC;
+		printk("BNC");
+		break;
+	default:
+		dev->if_port = TULIP_10TP_PORT;
+		printk("10TP");
+		break;
+	}
+	tio_sia_write(ioaddr, tsiac, tsiax, tsiag);
+	printk(" port.\n");
+}
+
+static void
+auto21140_select(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	/* kick port */
+	tio_write(TPOLL_TRIGGER, CSR1);
+	tio_write(TINTR_ENABLE, CSR7);
+	tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6);
+	dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED);
+	printk("%s: probed %s port.\n",
+		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
+	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
+			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);	
+	tio_write(TINTR_DISABLE, CSR7);
+	tio_read(CSR8) & 0xffff;
+	tio_write(TCMOD_AUTO, CSR6);
+}
+
+static void
+generic21140_select(struct device *dev)
+{
+	int ioaddr = dev->base_addr, csr6;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+	dev->if_port &= 1;
+	csr6 = tio_read(CSR6) &
+		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
+
+	/* Stop the transmit process. */
+	tio_write(csr6 | TCMOD_RxSTART, CSR6);
+	printk("%s: enabling %s port.\n",
+		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
+	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
+			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
+	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
+			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
 }
 
-
 static int
 tulip_open(struct device *dev)
 {
@@ -268,27 +650,32 @@
 	int ioaddr = dev->base_addr;
 
 	/* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
-	outl(0xfff80001, ioaddr + CSR0);
+	tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0);
+/*	tio_write(TBMOD_RESERVED|TBMOD_RESET, CSR0);*/
 	SLOW_DOWN_IO;
 	/* Deassert reset.  Set 8 longword cache alignment, 8 longword burst.
-	   Cache alignment bits 15:14	     Burst length 13:8
-    	0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
+	   -> Set 32 longword cache alignment, unlimited longword burst ?
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-	outl(0xfff84800, ioaddr + CSR0);
+	tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);
+/*	tio_write(TBMOD_RESERVED|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);*/
 
 	if (irq2dev_map[dev->irq] != NULL
 		|| (irq2dev_map[dev->irq] = dev) == NULL
 		|| dev->irq == 0
-		|| request_irq(dev->irq, &tulip_interrupt, 0, "DEC 21040 Tulip", NULL)) {
+#if LINUX_VERSION_CODE < 0x10346
+		|| request_irq(dev->irq, &tulip_interrupt, 0, tp->signature)) {
+#else
+		|| request_irq(dev->irq, (void *)&tulip_interrupt, 0,
+					   tp->signature, dev)) {
+#endif
 		return -EAGAIN;
 	}
 
+/*
 	if (tulip_debug > 1)
 		printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+*/
 
 	tulip_init_ring(dev);
 
@@ -309,37 +696,32 @@
 		}
 		/* Put the setup frame on the Tx list. */
 		tp->tx_ring[0].length = 0x08000000 | 192;
-		tp->tx_ring[0].buffer1 = (char *)tp->setup_frame;
+		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
 		tp->tx_ring[0].buffer2 = 0;
-		tp->tx_ring[0].status = 0x80000000;
+		tp->tx_ring[0].status = TRING_OWN;
 
 		tp->cur_tx++, tp->dirty_tx++;
 	}
 
-	outl((int)tp->rx_ring, ioaddr + CSR3);
-	outl((int)tp->tx_ring, ioaddr + CSR4);
-
-	/* Turn on the xcvr interface. */
-	outl(0x00000000, ioaddr + CSR13);
-	outl(0x00000004, ioaddr + CSR13);
+	tio_write(virt_to_bus(tp->rx_ring), CSR3);
+	tio_write(virt_to_bus(tp->tx_ring), CSR4);
 
+	dev->if_port = TULIP_PORT;
+	if (tp->port_select) tp->port_select(dev);
 	/* Start the chip's Tx and Rx processes. */
-	outl(0xfffe2002, ioaddr + CSR6);
+	tio_write(tio_read(CSR6) | TCMOD_TRxSTART
+			  | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6);
 
 	/* Trigger an immediate transmit demand to process the setup frame. */
-	outl(0, ioaddr + CSR1);
+	tio_write(TPOLL_TRIGGER, CSR1);
 
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
 
 	/* Enable interrupts by setting the interrupt mask. */
-	outl(0xFFFFFFFF, ioaddr + CSR7);
+	tio_write(TINTR_ENABLE, CSR7);
 
-	if (tulip_debug > 2) {
-		printk("%s: Done tulip_open(), CSR0 %8.8x, CSR13 %8.8x.\n",
-			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR13));
-	}
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -356,14 +738,14 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
+		tp->rx_ring[i].status = TRING_OWN;
 		tp->rx_ring[i].length = PKT_BUF_SZ;
-		tp->rx_ring[i].buffer1 = (char *)(tp->rx_buffs + i*PKT_BUF_SZ);
-		tp->rx_ring[i].buffer2 = (char *)&tp->rx_ring[i+1];
+		tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]);
+		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
 	}
 	/* Mark the last entry as wrapping the ring. */ 
 	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
-	tp->rx_ring[i-1].buffer2 = (char *)&tp->rx_ring[0];
+	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
 	   do need to clear the ownership bit. */
@@ -383,11 +765,18 @@
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
 		int i;
-		if (tickssofar < 20)
-			return 1;
-		printk("%s: transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
-			   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+		if (tickssofar < 40) return(1);
+		if (tp->port_select
+			&& (!tp->port_error || tp->port_error(dev))) {
+			dev->if_port ++;
+			tp->port_select(dev);
+			dev->trans_start = jiffies;
+			return(0);
+		}
+		printk("%s: transmit timed out, status %8.8x,"
+			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+			   dev->name, tio_read(CSR5), tio_read(CSR12),
+			   tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
 		printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++)
 			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
@@ -397,17 +786,24 @@
 		printk("\n");
 
 		tp->stats.tx_errors++;
-		/* We should reinitialize the hardware here. */
+		/* Perhaps we should reinitialize the hardware here. */
+		dev->if_port = 0;
+		tio_write(TSIAC_CONFIG, CSR13);
+		/* Start the chip's Tx and Rx processes . */
+		tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6);
+		/* Trigger an immediate transmit demand. */
+		tio_write(TPOLL_TRIGGER, CSR1);
+
 		dev->tbusy=0;
 		dev->trans_start = jiffies;
-		return 0;
+		return(0);
 	}
 
 	if (skb == NULL || skb->len <= 0) {
 		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
 			   dev->name);
 		dev_tint(dev);
-		return 0;
+		return(0);
 	}
 
 	/* Block a timer-based transmit from overlapping.  This could better be
@@ -428,23 +824,23 @@
 	tp->tx_skbuff[entry] = skb;
 	tp->tx_ring[entry].length = skb->len |
 		(entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
-	tp->tx_ring[entry].buffer1 = skb->data;
+	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
 	tp->tx_ring[entry].buffer2 = 0;
-	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
+	tp->tx_ring[entry].status = TRING_OWN;	/* Pass ownership to the chip. */
 
 	tp->cur_tx++;
 
 	/* Trigger an immediate transmit demand. */
-	outl(0, ioaddr + CSR1);
+	tio_write(TPOLL_TRIGGER, CSR1);
 
 	dev->trans_start = jiffies;
 
-	return 0;
+	return(0);
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void tulip_interrupt(int irq, struct pt_regs *regs)
 {
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
 	struct tulip_private *lp;
@@ -463,21 +859,17 @@
 	dev->interrupt = 1;
 
 	do {
-		csr5 = inl(ioaddr + CSR5);
+		csr5 = tio_read(CSR5);
 		/* Acknowledge all of the current interrupt sources ASAP. */
-		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
-
-		if (tulip_debug > 4)
-			printk("%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
-				   dev->name, csr5, inl(dev->base_addr + CSR5));
+		tio_write(csr5 & TSTAT_CLEARINTR, CSR5);
 
-		if ((csr5 & 0x00018000) == 0)
-			break;
+		/* check interrupt ? */
+		if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break;
 
-		if (csr5 & 0x0040)			/* Rx interrupt */
+		if (csr5 & TSTAT_RxINTR)			/* Rx interrupt */
 			tulip_rx(dev);
 
-		if (csr5 & 0x0001) {		/* Tx-done interrupt */
+		if (csr5 & TSTAT_TxINTR) {		/* Tx-done interrupt */
 			int dirty_tx = lp->dirty_tx;
 
 			while (dirty_tx < lp->cur_tx) {
@@ -487,14 +879,15 @@
 				if (status < 0)
 					break;			/* It still hasn't been Txed */
 
-				if (status & 0x8000) {
+				if (status & TRING_ERROR) {
 					/* There was an major error, log it. */
 					lp->stats.tx_errors++;
-					if (status & 0x4104) lp->stats.tx_aborted_errors++;
-					if (status & 0x0C00) lp->stats.tx_carrier_errors++;
-					if (status & 0x0200) lp->stats.tx_window_errors++;
-					if (status & 0x0002) lp->stats.tx_fifo_errors++;
-					if (status & 0x0080) lp->stats.tx_heartbeat_errors++;
+					if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++;
+					if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++;
+					if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++;
+					if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++;
+					if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex)
+						lp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
 					if (status & 0x0100) lp->stats.collisions16++;
 #endif
@@ -531,13 +924,13 @@
 		}
 
 		/* Log errors. */
-		if (csr5 & 0x8000) {	/* Abnormal error summary bit. */
-			if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
-			if (csr5 & 0x0100) { 		/* Missed a Rx frame. */
+		if (csr5 & TSTAT_ABNINTR) {	/* Abnormal error summary bit. */
+			if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */
+			if (csr5 & TSTAT_RxMISSED) { 		/* Missed a Rx frame. */
 				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+				lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
 			}
-			if (csr5 & 0x0800) {
+			if (csr5 & TSTAT_TEXPIRED) {
 				printk("%s: Something Wicked happened! %8.8x.\n",
 					   dev->name, csr5);
 				/* Hmmmmm, it's not clear what to do here. */
@@ -547,22 +940,22 @@
 			printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
 				   dev->name, csr5);
 			/* Clear all interrupt sources. */
-			outl(0x0001ffff, ioaddr + CSR5);
+			tio_write(TSTAT_CLEARINTR, CSR5);
 			break;
 		}
 	} while (1);
 
-	if (tulip_debug > 3)
-		printk("%s: exiting interrupt, csr5=%#4.4x.\n",
-			   dev->name, inl(ioaddr + CSR5));
-
 	/* Special code for testing *only*. */
 	{
 		static int stopit = 10;
 		if (dev->start == 0  &&  --stopit < 0) {
 			printk("%s: Emergency stop, looping startup interrupt.\n",
 				   dev->name);
-			free_irq(irq, NULL);
+#if LINUX_VERSION_CODE < 0x10346
+			free_irq(irq);
+#else
+			free_irq(irq, dev);
+#endif
 		}
 	}
 
@@ -576,33 +969,34 @@
 	struct tulip_private *lp = (struct tulip_private *)dev->priv;
 	int entry = lp->cur_rx % RX_RING_SIZE;
 	int i;
-		
-	if (tulip_debug > 4)
-		printk(" In tulip_rx().\n");
+
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while (lp->rx_ring[entry].status >= 0) {
 		int status = lp->rx_ring[entry].status;
 
-		if (tulip_debug > 4)
-			printk("  tulip_rx() status was %8.8x.\n", status);
-		if ((status & 0x0300) != 0x0300) {
-			printk("%s: Ethernet frame spanned multiple buffers, status %8.8x!\n",
-				   dev->name, status);
-		} else if (status & 0x8000) {
+		if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
+			printk("%s: Ethernet frame spanned multiple buffers,"
+				   "status %8.8x!\n", dev->name, status);
+		} else if (status & TRING_ERROR) {
 			/* There was a fatal error. */
 			lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x0890) lp->stats.rx_length_errors++;
-			if (status & 0x0004) lp->stats.rx_frame_errors++;
-			if (status & 0x0002) lp->stats.rx_crc_errors++;
-			if (status & 0x0001) lp->stats.rx_fifo_errors++;
+			if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++;
+			if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++;
+			if (status & TRING_RxCRC) lp->stats.rx_crc_errors++;
+			if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++;
 		} else {
 			/* Malloc up new buffer, compatible with net-2e. */
 			short pkt_len = lp->rx_ring[entry].status >> 16;
 			struct sk_buff *skb;
 
-			skb = dev_alloc_skb(pkt_len+2);
+#if LINUX_VERSION_CODE < 0x10300
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+			skb = dev_alloc_skb(pkt_len + 2);
+#endif
 			if (skb == NULL) {
-				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+				printk("%s: Memory squeeze, deferring packet.\n",
+					   dev->name);
 				/* Check that at least two ring entries are free.
 				   If not, free one and mark stats->rx_dropped++. */
 				for (i=0; i < RX_RING_SIZE; i++)
@@ -611,24 +1005,31 @@
 
 				if (i > RX_RING_SIZE -2) {
 					lp->stats.rx_dropped++;
-					lp->rx_ring[entry].status = 0x80000000;
+					lp->rx_ring[entry].status = TRING_OWN;
 					lp->cur_rx++;
 				}
 				break;
 			}
 			skb->dev = dev;
-			skb_reserve(skb,2);	/* 16 byte align the data fields */
-			memcpy(skb_put(skb,pkt_len), lp->rx_ring[entry].buffer1, pkt_len);
-			skb->protocol=eth_type_trans(skb,dev);
+#if LINUX_VERSION_CODE < 0x10300
+			skb->len = pkt_len;
+			memcpy(skb->data, bus_to_virt(lp->rx_ring[entry].buffer1),
+				   pkt_len);
+#else
+			skb_reserve(skb, 2);
+			memcpy(skb_put(skb, pkt_len),
+				   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
+			/* Needed for 1.3.x */
+			skb->protocol = eth_type_trans(skb,dev);
+#endif
 			netif_rx(skb);
 			lp->stats.rx_packets++;
 		}
 
-		lp->rx_ring[entry].status = 0x80000000;
+		lp->rx_ring[entry].status = TRING_OWN;
 		entry = (++lp->cur_rx) % RX_RING_SIZE;
 	}
-
-	return 0;
+	return(0);
 }
 
 static int
@@ -640,22 +1041,27 @@
 	dev->start = 0;
 	dev->tbusy = 1;
 
-	if (tulip_debug > 1)
-		printk("%s: Shutting down ethercard, status was %2.2x.\n",
-			   dev->name, inl(ioaddr + CSR5));
-
 	/* Disable interrupts by clearing the interrupt mask. */
-	outl(0x00000000, ioaddr + CSR7);
+	tio_write(TINTR_DISABLE, CSR7);
 	/* Stop the chip's Tx and Rx processes. */
-	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
+	/* Leave the card in 10baseT state. */
+	tio_write(TSIAC_CONFIG, CSR13);
 
-	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+	tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
 
-	free_irq(dev->irq, NULL);
+	tio_write(0, CSR13);
+/*	tio_write(0, CSR8);	wake up chip ? */
+
+#if LINUX_VERSION_CODE < 0x10346
+	free_irq(dev->irq);
+#else
+	free_irq(dev->irq, dev);
+#endif
 	irq2dev_map[dev->irq] = 0;
 
 	MOD_DEC_USE_COUNT;
-	return 0;
+	return(0);
 }
 
 static struct enet_statistics *
@@ -664,30 +1070,35 @@
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	short ioaddr = dev->base_addr;
 
-	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+	tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
 
-	return &tp->stats;
+	return(&tp->stats);
 }
 
 /*
  *	Set or clear the multicast filter for this adaptor.
  */
 
+#if LINUX_VERSION_CODE < 0x10300
+static void set_multicast_list(struct device *dev, int num_addrs,
+							   void *addrs)
+#else
 static void set_multicast_list(struct device *dev)
+#endif
 {
 	short ioaddr = dev->base_addr;
-	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+	int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
 
 	if (dev->flags&IFF_PROMISC) 
-	{			/* Set promiscuous. */
-		outl(csr6 | 0x00C0, ioaddr + CSR6);
+	{			/* Set promiscuous. why ALLMULTI ? */
+		tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6);
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
 	}
 	else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI)) 
 	{
 		/* Too many to filter perfectly -- accept all multicasts. */
-		outl(csr6 | 0x0080, ioaddr + CSR6);
+		tio_write(csr6 | TCMOD_ALLMCAST, CSR6);
 	}
 	else
 	{
@@ -700,7 +1111,7 @@
 		/* We have <= 15 addresses that we can use the wonderful
 		   16 address perfect filtering of the Tulip.  Note that only
 		   the low shortword of setup_frame[] is valid. */
-		outl(csr6 | 0x0000, ioaddr + CSR6);
+		tio_write(csr6 | 0x0000, CSR6);
 		i=0;
 		while(dmi) 
 		{
@@ -723,6 +1134,7 @@
 	}
 }
 
+#if 0
 static int
 set_mac_address(struct device *dev, void *addr)
 {
@@ -731,47 +1143,280 @@
 	if (dev->start)
 		return -EBUSY;
 	printk("%s: Setting MAC address to ", dev->name);
-	for (i = 0; i < 6; i++)
-		printk(" %2.2x", dev->dev_addr[i] = sa->sa_data[i]);
-	printk(".\n");
+	for (i = 0; i < ETH_ALEN - 1; i++)
+		printk("%2.2x:", dev->dev_addr[i] = sa->sa_data[i]);
+	printk("%2.2x.\n", dev->dev_addr[i] = sa->sa_data[i]);
 	return 0;
 }
+#endif
+
+static struct device *tulip_alloc(struct device *dev)
+{
+	struct tulip_private *tp;
+	char *buff;
+#ifndef	MODULE
+	int alloc_size;
+#endif
+	if (!dev || dev->priv) {
+		struct device *olddev = dev;
+
+		alloc_size = sizeof(struct device)
+			+ sizeof(struct tulip_private)
+			+ ETHNAMSIZ;
+		alloc_size = ROUND_UP(alloc_size, 8);
+
+		buff = (char *)kmalloc(alloc_size, GFP_KERNEL);
+		dev = (struct device *)buff;
+		if (dev == NULL) {
+			printk("tulip_alloc: kmalloc failed.\n");
+			return(NULL);
+		}
+		tp = (struct tulip_private *)(buff + sizeof(struct device));
+		memset(buff, 0, alloc_size);
+		dev->priv = (void *)tp;
+		dev->name = (char *)(buff + sizeof(struct device)
+							 + sizeof(struct tulip_private));
+		if (olddev) {
+			dev->next = olddev->next;
+			olddev->next = dev;
+		}
+	} else {
+		alloc_size = ROUND_UP(sizeof(struct tulip_private), 8);
+		tp = (struct tulip_private *)kmalloc(alloc_size, GFP_KERNEL);
+		memset((void *)tp, 0, alloc_size);
+		dev->priv = (void *)tp;
+	}
+	return(dev);
+}
+
+int
+tulip_hwinit(struct device *dev, int ioaddr,
+			 int irq, int device_id)
+{
+	/* See note below on the Znyx 315 etherarray. */
+	unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+	char detect_mesg[80], *mesgp=detect_mesg, *card_name=NULL;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
+	unsigned short sum, bitsum;
+
+	if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
+		printk("tulip_alloc: region already allocated at %#3x.\n",
+			   ioaddr);
+		return(-1);
+	}
+
+	mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip",
+					 device_id == PCI_DEVICE_ID_DEC_TULIP_FAST,
+					 device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS);
+
+	/* Stop the chip's Tx and Rx processes. */
+	tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6);
+	/* Clear the missed-packet counter. */
+	tio_read(CSR8) & 0xffff;
+
+	if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS
+	    && (tio_read(CSR9) & 0x8000)) {
+		mesgp += sprintf(mesgp, "treat as 21040 ");
+	    device_id = PCI_DEVICE_ID_DEC_TULIP;
+	}
+	
+	/* The station address ROM is read byte serially.  The register must
+	   be polled, waiting for the value to be read bit serially from the
+	   EEPROM.
+	   */
+	sum = 0;
+	if (device_id == PCI_DEVICE_ID_DEC_TULIP) {
+		tio_write(0, CSR9);
+	    /* Reset the pointer with a dummy write. */
+	    bitsum = 0xff;
+	    for (i = 0; i < 6; i++) {
+			int value, boguscnt = 100000;
+			do
+				value = tio_read(CSR9);
+			while (value < 0  && --boguscnt > 0);
+			dev->dev_addr[i] = value;
+			sum += value;
+			bitsum &= value;
+	    }
+	} else {
+	    /* Must be a 21140/21041, with a serial EEPROM interface. */
+	    struct eeprom eep;
+	    u_char *addr;
+
+	    if (read_eeprom(ioaddr, &eep) < 0) {
+			addr = eep.ng_addr;/* broken EEPROM structure */
+	    } else {
+			addr = eep.ok_addr;/* DEC EtherWorks */
+	    }
+	    for (i = 0; i < ETH_ALEN; i++) {
+			sum += addr[i];
+			dev->dev_addr[i] = addr[i];
+	    }
+	}
+	/* Make certain the data structures are quadword aligned. */
+
+	mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr);
+
+	/* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
+	   The addresses of the subsequent ports are derived from the first. */
+	if (sum == 0) {
+		for (i = 0; i < ETH_ALEN - 1; i++)
+			dev->dev_addr[i] = last_phys_addr[i];
+		dev->dev_addr[i] = last_phys_addr[i] + 1;
+	}
+	for (i = 0; i < ETH_ALEN - 1; i++)
+		mesgp += sprintf(mesgp, "%2.2x:",
+						 last_phys_addr[i] = dev->dev_addr[i]);
+	mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n",
+					 last_phys_addr[i] = dev->dev_addr[i], irq);
+
+	card_name = card_type(tp, device_id,
+						  htonl((*(int*)dev->dev_addr) & 0xFFFFFF));
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+#ifdef TULIP_FULL_DUPLEX
+	tp->full_duplex = 1;
+#endif
+
+	/* The Tulip-specific entries in the device structure. */
+	dev->open = &tulip_open;
+	dev->hard_start_xmit = &tulip_start_xmit;
+	dev->stop = &tulip_close;
+	dev->get_stats = &tulip_get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+#if 0
+	dev->set_mac_address = &set_mac_address;
+#endif
+
+#ifdef	MODULE
+    ether_setup(dev);
+#else
+    init_etherdev(dev, 0);
+#endif
+
+	printk("%s: %s %s", dev->name, card_name, detect_mesg);
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	tio_write(TSIAC_RESET, CSR13);
+	tio_write(TSIAC_CONFIG, CSR13);
+
+	return(0);
+}
+
+int tulip_probe(struct device *dev)
+{
+	static u_short probed_irqs=0;
+	u_char pci_bus, pci_device_fn, pci_latency, pci_irq;
+	u_int pci_ioaddr;
+	u_short pci_command;
+	u_long pci_chips[] = {
+		PCI_DEVICE_ID_DEC_TULIP,
+		PCI_DEVICE_ID_DEC_TULIP_FAST,
+		PCI_DEVICE_ID_DEC_TULIP_PLUS,
+		PCI_DEVICE_ID_NONE
+	};
+	int num=0, cno;
+	int pci_index;
+
+    if (!pcibios_present()) return(-ENODEV);
+
+	for (pci_index = 0; pci_index < 8; pci_index++) {
+		/* Search for the PCI_DEVICE_ID_DEV_TULIP* chips */
+		for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno ++)
+			if (pcibios_find_device(PCI_VENDOR_ID_DEC,
+									pci_chips[cno],
+									pci_index, &pci_bus,
+									&pci_device_fn) == 0) {
+				/* get IRQ */
+				pcibios_read_config_byte(pci_bus, pci_device_fn,
+										 PCI_INTERRUPT_LINE, &pci_irq);
+				if (probed_irqs & (1 << pci_irq)) continue;
+				/* get IO address */
+				pcibios_read_config_dword(pci_bus, pci_device_fn,
+										  PCI_BASE_ADDRESS_0,
+										  &pci_ioaddr);
+				/* Remove I/O space marker in bit 0. */
+				pci_ioaddr &= ~3;
+#ifdef	MODULE
+				/* cmpare requested IRQ/IO address */
+				if (dev && dev->irq && dev->base_addr &&
+					(dev->irq != pci_irq
+					 || dev->base_addr != pci_ioaddr)) continue;
+#else
+				if ((dev = tulip_alloc(dev)) == NULL) break;
+#endif
+				if (!probed_irqs) printk(version);
+				probed_irqs |= (1 << pci_irq);
+				
+				/* Get and check the bus-master and latency values. */
+				pcibios_read_config_word(pci_bus, pci_device_fn,
+										 PCI_COMMAND, &pci_command);
+				if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+					printk("  PCI Master Bit has not been set!"
+						   " Setting...\n");
+					pci_command |= PCI_COMMAND_MASTER;
+					pcibios_write_config_word(pci_bus, pci_device_fn,
+											  PCI_COMMAND, pci_command);
+				}
+				pcibios_read_config_byte(pci_bus, pci_device_fn,
+										 PCI_LATENCY_TIMER,
+										 &pci_latency);
+				if (pci_latency < 10) {
+					printk("  PCI latency timer (CFLT) is"
+						   " unreasonably low at %d."
+						   "  Setting to 100 clocks.\n", pci_latency);
+					pcibios_write_config_byte(pci_bus, pci_device_fn,
+											  PCI_LATENCY_TIMER, 100);
+				}
+				if (tulip_hwinit(dev, pci_ioaddr, pci_irq,
+								 pci_chips[cno]) < 0) continue;
+				num ++;
+#if defined(MODULE) || defined(TULIP_ONLY_ONE)
+				return(0);
+#endif
+		}
+	}
+	return(num > 0 ? 0: -ENODEV);
+}
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_tulip = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, tulip_probe
-};
+static int io = 0xfc00;
+static int irq = 9;
 
-static int io = 0;
-static int irq = 0;
+static struct device *mod_dev;
 
 int init_module(void)
 {
-	printk("tulip: Sorry, modularization is not completed\n");
-	return -EIO;
-#if 0
-	if (io == 0)
-	  printk("tulip: You should not use auto-probing with insmod!\n");
-	dev_tulip.base_addr = io;
-	dev_tulip.irq       = irq;
-	if (register_netdev(&dev_tulip) != 0) {
+	if ((mod_dev = tulip_alloc(0)) == NULL) return(-EIO);
+
+	mod_dev->base_addr = io;
+	mod_dev->irq = irq;
+	mod_dev->init = &tulip_probe;
+
+	if (register_netdev(mod_dev)) {
 		printk("tulip: register_netdev() returned non-zero.\n");
+		kfree_s(mod_dev, alloc_size);
 		return -EIO;
 	}
-	return 0;
-#endif
+	return(0);
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_tulip);
+	release_region(mod_dev->base_addr, TULIP_TOTAL_SIZE);
+	unregister_netdev(mod_dev);
+	kfree_s(mod_dev, alloc_size);
 }
+
 #endif /* MODULE */
+
 
 /*
  * Local variables:
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.84/linux/drivers/scsi/Config.in	Tue Apr  2 13:32:21 1996
+++ linux/drivers/scsi/Config.in	Fri Apr  5 13:33:52 1996
@@ -35,10 +35,10 @@
 fi
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
-fi
-if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
-  bool '   always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
-  bool '   allow FAST-SCSI [10MHz]' CONFIG_SCSI_NCR53C7xx_FAST
+  if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
+    bool '   always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
+    bool '   allow FAST-SCSI [10MHz]' CONFIG_SCSI_NCR53C7xx_FAST
+  fi
 fi
 dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/README.ppa linux/drivers/scsi/README.ppa
--- v1.3.84/linux/drivers/scsi/README.ppa	Thu Mar 14 12:57:45 1996
+++ linux/drivers/scsi/README.ppa	Mon Apr  8 15:47:34 1996
@@ -48,7 +48,7 @@
 Built-in drivers accept parameters using this LILO/LOADLIN command line
 syntax (omitted parameters retain their default values):
 
-        ppa=base[,speed_high[,speed_low[,call_sched[,nybble]]]]
+        ppa=base[,speed_high[,speed_low[,nybble]]]
 
 For example:    ppa=0x378,0,3
 
@@ -66,7 +66,6 @@
 ppa_base        0x378   The base address of PPA's parallel port.
 ppa_speed_high  1       Microsecond i/o delay used in data transfers
 ppa_speed_low   6       Microsecond delay used in other operations
-ppa_call_sched  1       1 to give up CPU during busy waits
 ppa_nybble      0       1 to force the driver to use 4-bit mode.
 
 A word about the timing parameters:  the ppa_speed_low parameter controls 
@@ -84,15 +83,6 @@
 have set these parameters too low.  The default values appear to be
 safe on most machines.
 
-The ppa_call_sched parameter controls the 'friendliness' of this driver
-to the rest of your system.  When it is set to zero, the driver waits for
-events on the drive by continuously reading a port until the 0x80 bit is
-asserted.  Since it is in a tight loop inside the kernel, the rest of the
-system becomes very sluggish.  If you set ppa_call_sched to one, then
-ppa will call the scheduler in such circumstances, voluntarily giving up the
-CPU to other processes.   This appears to reduce the ZIP drive's 
-responsiveness, however.
-
 If you have both the lp and ppa drivers in your kernel, you must ensure
 that they access different parallel ports.  By default, the lp driver is
 initialised early in the booting process, and it claims all parallel
@@ -113,6 +103,12 @@
 
 In this case lp would use the polling driver, since an interrupt was not 
 specified.
+
+If you want to share the same parallel port between a ZIP drive and a
+printer, you should build both the lp and ppa drivers as modules and
+load and unload one or the other as required.  This is clumsy but we
+currently have no protocol for synchronising access to shared parallel
+ports.
 
 For information about using the ZIP drive, please read the generic
 instructions in the SCSI-HOWTO and the man pages for the normal disk
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.3.84/linux/drivers/scsi/README.st	Sun Dec 17 10:59:24 1995
+++ linux/drivers/scsi/README.st	Mon Apr  8 11:22:20 1996
@@ -1,14 +1,66 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Sun Nov 26 12:54:21 1995 by root@kai.makisara.fi
+Last modified: Mon Apr  8 09:34:33 1996 by makisara@kai.makisara.fi
+
 
 BASICS
 
-The driver is generic. The state of a drive is not modified when the
-driver is initialized or a device is opened. The mode parameters of the
-drive can be modified with ioctls. The driver supports fixed and
-variable block size (within buffer limits). Both the auto-rewind
-(minor equals device number) and non-rewind devices (minor is 128 +
-device number) are implemented.
+The driver is generic, i.e., it does not contain any code tailored
+to any specific tape drive. The tape parameters can be specified with
+one of the following three methods:
+
+1. Each user can specify the tape parameters he/she wants to use
+directly with ioctls. This is administratively a very simple and
+flexible method and applicable to single-user workstations. However,
+in a multiuser environment the next user finds the tape parameters in
+state the previous user left them.
+
+2. The system manager (root) can define default values for some tape
+parameters, like block size and density using the MTSETDRVBUFFER ioctl.
+These parameters can be programmed to come into effect either when a
+new tape is loaded into the drive or if writing begins at the
+beginning of the tape. The second method is applicable if the tape
+drive performs auto-detection of the tape format well (like some
+QIC-drives). The result is that any tape can be read, writing can be
+continued using existing format, and the default format is used if
+the tape is rewritten from the beginning (or a new tape is written
+for the first time). The first method is applicable if the drive
+does not perform auto-detection well enough and there is a single
+"sensible" mode for the device. An example is a DAT drive that is
+used only in variable block mode (I don't know if this is sensible
+or not :-).
+
+The user can override the parameters defined by the system
+manager. The changes persist until the defaults again come into
+effect.
+
+3. Up to four modes can be defined and selected using the minor number
+(bits 5 and 6). Mode 0 corresponds to the defaults discussed
+above. Additional modes are dormant until they are defined by the
+system manager (root). When specification of a new mode is started,
+the configuration of mode 0 is used to provide a starting point for
+definition of the new mode.
+
+Using the modes allows the system manager to give the users choices
+over some of the buffering parameters not directly accessible to the
+users (buffered and asynchronous writes). The modes also allow choices
+between formats in multi-tape operations (the explicitly overridden
+parameters are reset when a new tape is loaded).
+
+If more than one mode is used, all modes should contain definitions
+for the same set of parameters.
+
+Many Unices contain internal tables that associate different modes to
+supported devices. The Linux SCSI tape driver does not contain such
+tables (and will not do that in future). Instead of that, a utility
+program can be made that fetches the inquiry data sent by the device,
+scans its database, and sets up the modes using the ioctls. Another
+alternative is to make a small script that uses mt to set the defaults
+tailored to the system.
+
+
+The driver supports fixed and variable block size (within buffer
+limits). Both the auto-rewind (minor equals device number) and
+non-rewind devices (minor is 128 + device number) are implemented.
 
 By default the driver writes one filemark when the device is closed after
 writing and the last operation has been a write. Two filemarks can be
@@ -17,6 +69,7 @@
 
 The compile options are defined in the file linux/drivers/scsi/st_options.h.
 
+
 BUFFERING
 
 The driver uses tape buffers allocated either at system initialization
@@ -112,7 +165,7 @@
 MTREW   Rewind tape.
 MTOFFL  Set device off line (often rewind plus eject).
 MTNOP   Do nothing except flush the buffers.
-MTRETEN Retension tape.
+MTRETEN Re-tension tape.
 MTEOM   Space to end of recorded data.
 MTERASE Erase tape.
 MTSEEK	Seek to tape block count. Uses Tandberg-compatible seek (QFA)
@@ -122,25 +175,54 @@
         variable block mode (if applicable).
 MTSETDENSITY Sets the drive density code to arg. See drive
         documentation for available codes.
+MTLOCK and MTUNLOCK Explicitly lock/unlock the tape drive door.
+MTLOAD and MTUNLOAD Explicitly load and unload the tape.
+MTCOMPRESSION Sets compressing or uncompressing drive mode using the
+	SCSI mode page 15. Note that some drives other methods for
+	control of compression. Some drives (like the Exabytes) use
+	density codes for compression control. Some drives use another
+	mode page but this page has not been implemented in the driver.
 MTSETDRVBUFFER
-	Is used for several things. The command is obtained from count
+	Is used for several purposes. The command is obtained from count
         with mask MT_SET_OPTIONS, the low order bits are used as argument.
-	The subcommands are:
+	This command is only allowed for the superuser (root). The
+	subcommands are:
 	0
            The drive buffer option is set to the argument. Zero means
            no buffering.
         MT_ST_BOOLEANS
            Sets the buffering options. The bits are the new states
-           (enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES),
-           asynchronous writes (MT_ST_ASYNC_WRITES), read ahead
-           (MT_ST_READ_AHEAD), writing of two filemark (ST_TWO_FM),
-	   using the SCSI spacing to EOD (MT_ST_FAST_EOM), automatic
-	   locking of the drive door (MT_ST_AUTO_LOCK) and debugging
-	   (MT_ST_DEBUGGING ; debugging must be compiled into the
-	   driver).
+           (enabled/disabled) the following options (in the
+	   parenthesis is specified whether the option is global or
+	   can be specified differently for each mode):
+	     MT_ST_BUFFER_WRITES write buffering (mode)
+	     MT_ST_ASYNC_WRITES asynchronous writes (mode)
+             MT_ST_READ_AHEAD  read ahead (global)
+             MT_ST_TWO_FM writing of two filemarks (global)
+	     MT_ST_FAST_EOM using the SCSI spacing to EOD (global)
+	     MT_ST_AUTO_LOCK automatic locking of the drive door (global)
+             MT_ST_DEF_WRITES the defaults are meant only for writes (mode)
+	     MT_ST_CAN_BSR backspacing over records can be used for
+		repositioning the tape (global)
+	     MT_ST_DEBUGGING debugging (global; debugging must be
+		compiled into the driver)
+	MT_ST_SETBOOLEANS
+	MT_ST_CLEARBOOLEANS
+	   Sets or clears the option bits.
         MT_ST_WRITE_THRESHOLD
            Sets the write threshold for this device to kilobytes
            specified by the lowest bits.
+	MT_ST_DEF_BLKSIZE
+	   Defines the default block size set automatically. Value
+	   0xffffff means that the default is not used any more.
+	MT_ST_DEF_DENSITY
+	MT_ST_DEF_DRVBUFFER
+	MT_ST_DEF_COMPRESSION
+	   Used to set or clear the density (8 bits), drive buffer
+	   state (3 bits), and compression (single bit). If the value is
+	   MT_ST_CLEAR_DEFAULT (0xfffff), the default will not be used
+	   any more. Otherwise the lower-most bits of the value contain
+	   the new value of the parameter.
 
 The following ioctl uses the structure mtpos:
 MTIOCPOS Reads the current position from the drive. Uses
@@ -168,7 +250,7 @@
 is defined.
 
 The maximum number of tape devices is determined by the define
-ST_MAX_TAPES. If more tapes are detected at driver intialization, the
+ST_MAX_TAPES. If more tapes are detected at driver initialization, the
 maximum is adjusted accordingly.
 
 Immediate return from tape positioning SCSI commands can be enabled by
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.3.84/linux/drivers/scsi/fdomain.c	Sat Mar 23 13:15:00 1996
+++ linux/drivers/scsi/fdomain.c	Fri Apr  5 12:54:58 1996
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Thu Feb  8 08:21:33 1996 by r.faith@ieee.org
+ * Revised: Thu Apr  4 20:44:47 1996 by r.faith@ieee.org
  * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
+ * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.39 1995/10/12 20:31:47 root Exp $
+ * $Id: fdomain.c,v 5.41 1996/04/05 04:22:25 root Exp $
 
  * 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
@@ -21,9 +21,26 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
 
  **************************************************************************
- 
- DESCRIPTION:
 
+ SUMMARY:
+
+ Future Domain BIOS versions supported for autodetect:
+    2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
+ Chips are supported:
+    TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
+ Boards supported:
+    Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
+    Future Domain TMC-3260 (PCI)
+    Quantum ISA-200S, ISA-250MG
+    Adaptec AHA-2920 (PCI)
+    IBM ?
+ LILO command-line options:
+    fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
+
+
+
+ DESCRIPTION:
+ 
  This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
  TMC-1650/1670, and TMC-3260 SCSI host adapters.  The 1650 and 1670 have a
  25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
@@ -38,11 +55,6 @@
  signature, then the driver may fail to function after the board is
  detected.
 
- The following BIOS versions are supported: 2.0, 3.0, 3.2, 3.4, and 3.5.
- The following chips are supported: TMC-1800, TMC-18C50, TMC-18C30.
- Reports suggest that the driver will also work with the 36C70 chip and
- with the Quantum ISA-200S and ISA-250MG SCSI adapters.
-
  Please note that the drive ordering that Future Domain implemented in BIOS
  versions 3.4 and 3.5 is the opposite of the order (currently) used by the
  rest of the SCSI industry.  If you have BIOS version 3.4 or 3.5, and have
@@ -51,12 +63,48 @@
  SCSI ID 1 will be C: (the boot device).  Under Linux, SCSI ID 0 will be
  /dev/sda and SCSI ID 1 will be /dev/sdb.  The Linux ordering is consistent
  with that provided by all the other SCSI drivers for Linux.  If you want
- this changed, send me patches that are protected by #ifdefs.
+ this changed, you will probably have to patch the higher level SCSI code.
+ If you do so, please send me patches that are protected by #ifdefs.
 
  If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
  your board.  Please refer to the Seagate driver for more information and
  possible support.
 
+
+ 
+ HISTORY:
+
+ Linux       Driver      Driver
+ Version     Version     Date         Support/Notes
+
+             0.0          3 May 1992  V2.0 BIOS; 1800 chip
+ 0.97        1.9         28 Jul 1992
+ 0.98.6      3.1         27 Nov 1992
+ 0.99        3.2          9 Dec 1992
+
+ 0.99.3      3.3         10 Jan 1993  V3.0 BIOS
+ 0.99.5      3.5         18 Feb 1993
+ 0.99.10     3.6         15 May 1993  V3.2 BIOS; 18C50 chip
+ 0.99.11     3.17         3 Jul 1993  (now under RCS)
+ 0.99.12     3.18        13 Aug 1993
+ 0.99.14     5.6         31 Oct 1993  (reselection code removed)
+
+ 0.99.15     5.9         23 Jan 1994  V3.4 BIOS (preliminary)
+ 1.0.8/1.1.1 5.15         1 Apr 1994  V3.4 BIOS; 18C30 chip (preliminary)
+ 1.0.9/1.1.3 5.16         7 Apr 1994  V3.4 BIOS; 18C30 chip
+ 1.1.38      5.18        30 Jul 1994  36C70 chip (PCI version of 18C30)
+ 1.1.62      5.20         2 Nov 1994  V3.5 BIOS
+ 1.1.73      5.22         7 Dec 1994  Quantum ISA-200S board; V2.0 BIOS
+
+ 1.1.82      5.26        14 Jan 1995  V3.5 BIOS; TMC-1610M/MER/MEX board
+ 1.2.10      5.28         5 Jun 1995  Quantum ISA-250MG board; V2.0, V2.01 BIOS
+ 1.3.4       5.31        23 Jun 1995  PCI BIOS-32 detection (preliminary)
+ 1.3.7       5.33         4 Jul 1995  PCI BIOS-32 detection
+ 1.3.28      5.36        17 Sep 1995  V3.61 BIOS; LILO command-line support
+ 1.3.34      5.39        12 Oct 1995  V3.60 BIOS; /proc
+ 1.3.72      5.39         8 Feb 1996  Adaptec AHA-2920 board
+ 1.3.85      5.41         4 Apr 1996
+
  
 
  REFERENCES USED:
@@ -165,7 +213,8 @@
  ENABLE_PARITY: This turns on SCSI parity checking.  With the current
  driver, all attached devices must support SCSI parity.  If none of your
  devices support parity, then you can probably get the driver to work by
- turning this option off.  I have no way of testing this, however.
+ turning this option off.  I have no way of testing this, however, and it
+ would appear that no one ever uses this option.
 
  FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
  18C30 chip have a 2k cache).  When this many 512 byte blocks are filled by
@@ -226,7 +275,7 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
   
-#define VERSION          "$Revision: 5.39 $"
+#define VERSION          "$Revision: 5.41 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v1.3.84/linux/drivers/scsi/ppa.c	Thu Mar 14 12:57:46 1996
+++ linux/drivers/scsi/ppa.c	Mon Apr  8 15:47:34 1996
@@ -31,7 +31,7 @@
 
 */
 
-#define   PPA_VERSION   "0.20"
+#define   PPA_VERSION   "0.26"
 
 /* Change these variables here or with insmod or with a LILO or LOADLIN
    command line argument
@@ -40,7 +40,6 @@
 static int      ppa_base       = 0x378;  /* parallel port address    */
 static int      ppa_speed_high = 1;      /* port delay in data phase */
 static int      ppa_speed_low  = 6;      /* port delay otherwise     */
-static int      ppa_call_sched = 1;      /* give up the CPU ?        */
 static int      ppa_nybble     = 0;      /* don't force nybble mode  */
 
 
@@ -52,6 +51,7 @@
 #include  <unistd.h>
 #include  <linux/module.h>
 #include  <linux/kernel.h>
+#include  <linux/tqueue.h>
 #include  <linux/ioport.h>
 #include  <linux/delay.h>
 #include  <linux/blk.h>
@@ -70,10 +70,8 @@
 static char             ppa_info_string[132];
 static Scsi_Cmnd        *ppa_current = 0;
 static void             (*ppa_done) (Scsi_Cmnd *);
-static int              ppa_busy = 0;
 static int              ppa_port_delay;
 
-
 void    out_p( short port, char byte)
 
 {       outb(byte,ppa_base+port);
@@ -140,11 +138,7 @@
 
         In principle, this could be tied to an interrupt, but the adapter
         doesn't appear to be designed to support interrupts.  We spin on
-        the 0x80 ready bit.  If ppa_call_sched is 1, we call the scheduler 
-        to allow other processes to run while we are waiting, just like
-        the lp driver does in polling mode.  The performance hit is
-        significant, so this behaviour is configurable.
-
+        the 0x80 ready bit. 
 */
 
 {       int     k;
@@ -153,8 +147,8 @@
         ppa_error_code = DID_OK;
         k = 0;
         while (!((r = in_p(1)) & 0x80) 
-                && (k++ < PPA_SPIN_TMO) && !ppa_abort_flag  )
-                        if (need_resched && ppa_call_sched) schedule();
+                && (k++ < PPA_SPIN_TMO) && !ppa_abort_flag  ) barrier();
+                        
         if (ppa_abort_flag) {
                 if (ppa_abort_flag == 1) ppa_error_code = DID_ABORT;
                 else {  ppa_do_reset();
@@ -330,10 +324,13 @@
         return (r & STATUS_MASK);
 }
 
+/* deprecated synchronous interface */
+
 int     ppa_command( Scsi_Cmnd * cmd )
 
 {       int     s;
 
+        sti();
         s = 0;
         if (ppa_start(cmd))
            if (ppa_wait()) 
@@ -341,29 +338,59 @@
         return s + (ppa_error_code << 16);
 }
 
-int     ppa_queuecommand( Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+/* pseudo-interrupt queueing interface */
 
-/* This is not really a queued interface at all, but apparently there may
-   be some bugs in the mid-level support for the non-queued interface.
-   This function is re-entrant, but only to one level.
+/* Since the PPA itself doesn't generate interrupts, we use
+   the scheduler's task queue to generate a stream of call-backs and
+   complete the request when the drive is ready.
 */
 
-{       int     s;
+static void ppa_interrupt( void *data);
+
+static struct tq_struct ppa_tq = {0,0,ppa_interrupt,NULL};
+
+static void ppa_interrupt( void *data)
+
+{       Scsi_Cmnd *cmd;
+        void  (*done) (Scsi_Cmnd *);
+
+        cmd = ppa_current;
+        done = ppa_done;
+        if (!cmd) return;
+        
+        if (ppa_abort_flag) {
+                ppa_disconnect();
+                if(ppa_abort_flag == 1) cmd->result = DID_ABORT << 16;
+                else { ppa_do_reset();
+                       cmd->result = DID_RESET << 16;
+                }
+                ppa_current = 0;
+                done(cmd);
+                return;
+        }
+        if (!( in_p(1) & 0x80)) {
+                queue_task(&ppa_tq,&tq_scheduler);
+                return;
+        }
+        cmd->result = ppa_completion(cmd) + (ppa_error_code << 16);
+        ppa_current = 0;
+        done(cmd);
+        return;
+}
+
+int     ppa_queuecommand( Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
 
-        ppa_current = cmd; ppa_done = done;
-        if (ppa_busy) return 0;
-        ppa_busy = 1;
-        while (ppa_current) {
-                cmd = ppa_current;  done = ppa_done;
-                s = 0;
-                if (ppa_start(cmd))
-                    if (ppa_wait())
-                        s = ppa_completion(cmd);
-                cmd->result = s + (ppa_error_code << 16);
+{       if (ppa_current) return 0;
+        sti();
+        ppa_current = cmd;
+        ppa_done = done;
+        if (!ppa_start(cmd)) {
+                cmd->result = ppa_error_code << 16;
                 ppa_current = 0;
-                done(cmd);              /* can reenter this function */
+                done(cmd);
+                return 0;
         }
-        ppa_busy = 0;
+        queue_task(&ppa_tq,&tq_scheduler);
         return 0;
 }
 
@@ -453,7 +480,7 @@
 
 /* Command line parameters (for built-in driver):
 
-   Syntax:  ppa=base[,speed_high[,speed_low[,call_sched[,nybble]]]]
+   Syntax:  ppa=base[,speed_high[,speed_low[,nybble]]]
 
    For example:  ppa=0x378   or   ppa=0x378,0,3
 
@@ -464,7 +491,7 @@
 {       if (ints[0] > 0) ppa_base = ints[1];
         if (ints[0] > 1) ppa_speed_high = ints[2];
         if (ints[0] > 2) ppa_speed_low = ints[3];
-        if (ints[0] > 3) ppa_call_sched = ints[4];
+        if (ints[0] > 3) ppa_nybble = ints[4];
         if (ints[0] > 4) ppa_nybble = ints[5];
 }
 
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.3.84/linux/drivers/scsi/scsi_ioctl.c	Sat Dec 30 11:59:29 1995
+++ linux/drivers/scsi/scsi_ioctl.c	Mon Apr  8 15:47:34 1996
@@ -149,6 +149,10 @@
     
     result = SCpnt->result;
     SCpnt->request.rq_status = RQ_INACTIVE;
+
+    if(SCpnt->device->scsi_request_fn)
+	(*SCpnt->device->scsi_request_fn)();
+
     wake_up(&SCpnt->device->device_wait);
     return result;
 }
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.3.84/linux/drivers/scsi/st.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/scsi/st.c	Mon Apr  8 11:22:20 1996
@@ -8,10 +8,10 @@
   order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer,
   J"org Weule, and Eric Youngdale.
 
-  Copyright 1992, 1993, 1994, 1995 Kai Makisara
+  Copyright 1992 - 1996 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Fri Mar 29 20:55:05 1996 by root@kai.makisara.fi
+  Last modified: Sun Apr  7 20:33:27 1996 by root@kai.makisara.fi
   Some small formal changes - aeb, 950809
 */
 
@@ -74,7 +74,8 @@
 #define ST_TIMEOUT (900 * HZ)
 #define ST_LONG_TIMEOUT (2000 * HZ)
 
-#define TAPE_NR(x) (MINOR(x) & 127)
+#define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK))
+#define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
 
 static int st_nbr_buffers;
 static ST_buffer **st_buffers;
@@ -84,6 +85,8 @@
 
 Scsi_Tape * scsi_tapes = NULL;
 
+static int modes_defined = FALSE;
+
 static ST_buffer *new_tape_buffer(int, int);
 static int enlarge_buffer(ST_buffer *, int, int);
 static void normalize_buffer(ST_buffer *);
@@ -98,6 +101,8 @@
 					     st_detect, st_init,
 					     NULL, st_attach, st_detach};
 
+static int st_compression(Scsi_Tape *, int);
+
 static int st_int_ioctl(struct inode * inode,struct file * file,
 	     unsigned int cmd_in, unsigned long arg);
 
@@ -454,11 +459,13 @@
 scsi_tape_open(struct inode * inode, struct file * filp)
 {
     unsigned short flags;
-    int i, need_dma_buffer;
+    int i, need_dma_buffer, new_session = FALSE, setting_failed;
     unsigned char cmd[10];
     Scsi_Cmnd * SCpnt;
     Scsi_Tape * STp;
+    ST_mode * STm;
     int dev = TAPE_NR(inode->i_rdev);
+    int mode = TAPE_MODE(inode->i_rdev);
 
     if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
       return (-ENXIO);
@@ -471,6 +478,19 @@
     }
     STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
 
+    if (mode != STp->current_mode) {
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n",
+	       dev, STp->current_mode, mode);
+#endif
+      /*      if (!STp->modes[mode].defined)
+	return (-ENXIO); */
+      new_session = TRUE;
+      STp->current_mode = mode;
+    }
+    STm = &(STp->modes[STp->current_mode]);
+
     /* Allocate buffer for this user */
     need_dma_buffer = STp->restr_dma;
     for (i=0; i < st_nbr_buffers; i++)
@@ -522,6 +542,7 @@
       (STp->mt_status)->mt_fileno = STp->drv_block = 0;
       STp->eof = ST_NOEOF;
       (STp->device)->was_reset = 0;
+      new_session = TRUE;
     }
 
     if ((STp->buffer)->last_result_fatal != 0) {
@@ -649,6 +670,46 @@
       }
     }
 
+    if (new_session) {
+      STp->density_changed = STp->blksize_changed = FALSE;
+      STp->compression_changed = FALSE;
+      if (!(STm->defaults_for_writes)) {
+	setting_failed = FALSE;
+	if (STm->default_blksize >= 0 &&
+	    STm->default_blksize != STp->block_size) {
+	  if (STm->default_density >= 0 &&
+	      STm->default_density != STp->density)
+	    STp->density = STm->default_density;  /* Dirty trick! */
+	  if (st_int_ioctl(inode, filp, MTSETBLK, STm->default_blksize)) {
+	    printk(KERN_WARNING "st%d: Can't set default block size to %d bytes.\n",
+		   dev, STm->default_blksize);
+	    if (modes_defined)
+	      setting_failed = TRUE;
+	  }
+	}
+	if (STm->default_density >= 0 &&
+	    STm->default_density != STp->density) {
+	  if (st_int_ioctl(inode, filp, MTSETDENSITY, STm->default_density)) {
+	    printk(KERN_WARNING "st%d: Can't set default density to %x.\n",
+		   dev, STm->default_density);
+	    if (modes_defined)
+	      setting_failed = TRUE;
+	  }
+	}
+	if (setting_failed) {
+	  (STp->buffer)->in_use = 0;
+	  STp->buffer = 0;
+	  STp->in_use = 0;
+	  return (-EINVAL);
+	}
+      }
+      if (STp->default_drvbuffer != 0xff) {
+	if (st_int_ioctl(inode, filp, MTSETDRVBUFFER, STp->default_drvbuffer))
+	  printk(KERN_WARNING "st%d: Can't set default drive buffering to %d.\n",
+		 dev, STp->default_drvbuffer);
+      }
+    }
+
     if (scsi_tapes[dev].device->host->hostt->usage_count)
       (*scsi_tapes[dev].device->host->hostt->usage_count)++;
     if(st_template.usage_count) (*st_template.usage_count)++;
@@ -714,12 +775,10 @@
 #endif
     }
     else if (!STp->rew_at_close) {
-#if ST_IN_FILE_POS
-      flush_buffer(inode, filp, 0);
-#else
-      if ((STp->eof == ST_FM) && !STp->eof_hit)
+      if (STp->can_bsr)
+	flush_buffer(inode, filp, 0);
+      else if ((STp->eof == ST_FM) && !STp->eof_hit)
 	back_over_eof(STp);
-#endif
     }
 
     if (STp->rew_at_close)
@@ -753,11 +812,15 @@
     const char *b_point;
     Scsi_Cmnd * SCpnt = NULL;
     Scsi_Tape * STp;
+    ST_mode * STm;
     int dev = TAPE_NR(inode->i_rdev);
 
     STp = &(scsi_tapes[dev]);
     if (STp->ready != ST_READY)
       return (-EIO);
+    STm = &(STp->modes[STp->current_mode]);
+    if (!STm->defined)
+      return (-ENXIO);
 
     /*
      * If there was a bus reset, block further access
@@ -791,6 +854,42 @@
        return retval;
       STp->rw = ST_WRITING;
     }
+    else if (STp->rw != ST_WRITING) {
+      if (STm->defaults_for_writes &&
+	  (STp->mt_status)->mt_fileno == 0 && STp->drv_block == 0) {
+	/* Force unless explicitly changed by the user */
+	if (!(STp->blksize_changed) && STm->default_blksize >= 0 &&
+	    STp->block_size != STm->default_blksize) {
+	  if (STm->default_density >= 0 &&
+	      STm->default_density != STp->density)
+	    STp->density = STm->default_density;  /* Dirty trick! */
+	  if (st_int_ioctl(inode, filp, MTSETBLK, STm->default_blksize)) {
+	    printk(KERN_WARNING "st%d: Can't set default block size to %d bytes.\n",
+		   dev, STm->default_blksize);
+	    if (modes_defined)
+	      return (-EINVAL);
+	  }
+	}
+	if (!(STp->density_changed) && STm->default_density >= 0 &&
+	    STm->default_density != STp->density) {
+	  if (st_int_ioctl(inode, filp, MTSETDENSITY, STm->default_density)) {
+	    printk(KERN_WARNING "st%d: Can't set default density %x.\n",
+		   dev, STm->default_density);
+	    if (modes_defined)
+	      return (-EINVAL);
+	  }
+	}
+      }
+      if (STm->default_compression != ST_DONT_TOUCH &&
+	  !(STp->compression_changed)) {
+	if (st_compression(STp, (STm->default_compression == ST_YES))) {
+	  printk(KERN_WARNING "st%d: Can't set default compression.\n",
+		 dev);
+	  if (modes_defined)
+	    return (-EINVAL);
+	}
+      }
+    }
 
     if (STp->moves_after_eof < 255)
       STp->moves_after_eof++;
@@ -817,14 +916,14 @@
     else if (STp->eof == ST_EOM_ERROR)
       return (-EIO);
 
-    if (!STp->do_buffer_writes) {
+    if (!STm->do_buffer_writes) {
       if (STp->block_size != 0 && (count % STp->block_size) != 0)
 	return (-EIO);   /* Write must be integral number of blocks */
       write_threshold = 1;
     }
     else
       write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
-    if (!STp->do_async_writes)
+    if (!STm->do_async_writes)
       write_threshold--;
 
     total = count;
@@ -836,7 +935,7 @@
     STp->rw = ST_WRITING;
 
     b_point = buf;
-    while((STp->block_size == 0 && !STp->do_async_writes && count > 0) ||
+    while((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
 	  (STp->block_size != 0 &&
 	   (STp->buffer)->buffer_bytes + count > write_threshold))
     {
@@ -951,7 +1050,7 @@
       return (STp->buffer)->last_result_fatal;
     }
 
-    if (STp->do_async_writes &&
+    if (STm->do_async_writes &&
 	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
 	 STp->block_size == 0) ) {
       /* Schedule an asynchronous write */
@@ -1010,6 +1109,8 @@
     STp = &(scsi_tapes[dev]);
     if (STp->ready != ST_READY)
       return (-EIO);
+    if (!STp->modes[STp->current_mode].defined)
+      return (-ENXIO);
 #if DEBUG
     if (!STp->in_use) {
       printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev);
@@ -1239,48 +1340,238 @@
 
 
 /* Set the driver options */
+	static void
+st_log_options(Scsi_Tape *STp, ST_mode *STm, int dev)
+{
+  printk(KERN_INFO
+"st%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+	 dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+	 STp->do_read_ahead);
+  printk(KERN_INFO
+"st%d:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d, defs for wr: %d \n",
+	 dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock,
+	 STm->defaults_for_writes);
+#if DEBUG
+  printk(KERN_INFO
+	 "st%d:    debugging: %d\n",
+	 dev, debugging);
+#endif
+}
+
+  
 	static int
 st_set_options(struct inode * inode, long options)
 {
   int value;
+  long code;
   Scsi_Tape *STp;
+  ST_mode *STm;
   int dev = TAPE_NR(inode->i_rdev);
 
   STp = &(scsi_tapes[dev]);
-  if ((options & MT_ST_OPTIONS) == MT_ST_BOOLEANS) {
-    STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
-    STp->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+  STm = &(STp->modes[STp->current_mode]);
+  if (!STm->defined) {
+    memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
+    modes_defined = TRUE;
+#if DEBUG
+    if (debugging)
+      printk(ST_DEB_MSG "st%d: Initialized mode %d definition from mode 0\n",
+	     dev, STp->current_mode);
+#endif
+  }
+
+  code = options & MT_ST_OPTIONS;
+  if (code == MT_ST_BOOLEANS) {
+    STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+    STm->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+    STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
     STp->do_read_ahead    = (options & MT_ST_READ_AHEAD) != 0;
     STp->two_fm		  = (options & MT_ST_TWO_FM) != 0;
     STp->fast_mteom	  = (options & MT_ST_FAST_MTEOM) != 0;
     STp->do_auto_lock     = (options & MT_ST_AUTO_LOCK) != 0;
+    STp->can_bsr          = (options & MT_ST_CAN_BSR) != 0;
 #if DEBUG
     debugging = (options & MT_ST_DEBUGGING) != 0;
-    printk(ST_DEB_MSG
-	   "st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
-	   dev, STp->do_buffer_writes, STp->do_async_writes, STp->do_read_ahead);
-    printk(ST_DEB_MSG
-	   "st%d:          two FMs: %d, fast mteom: %d auto lock: %d, debugging: %d\n",
-	   dev, STp->two_fm, STp->fast_mteom, STp->do_auto_lock, debugging);
 #endif
+    st_log_options(STp, STm, dev);
   }
-  else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
+  else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+    value = (code == MT_ST_SETBOOLEANS);
+    if ((options & MT_ST_BUFFER_WRITES) != 0)
+      STm->do_buffer_writes = value;
+    if ((options & MT_ST_ASYNC_WRITES) != 0)
+      STm->do_async_writes = value;
+    if ((options & MT_ST_DEF_WRITES) != 0)
+      STm->defaults_for_writes = value;
+    if ((options & MT_ST_READ_AHEAD) != 0)
+      STp->do_read_ahead = value;
+    if ((options & MT_ST_TWO_FM) != 0)
+      STp->two_fm = value;
+    if ((options & MT_ST_FAST_MTEOM) != 0)
+      STp->fast_mteom = value;
+    if ((options & MT_ST_AUTO_LOCK) != 0)
+      STp->do_auto_lock = value;
+    if ((options & MT_ST_CAN_BSR) != 0)
+      STp->can_bsr = value;
+#if DEBUG
+    if ((options & MT_ST_DEBUGGING) != 0)
+      debugging = value;
+#endif
+    st_log_options(STp, STm, dev);
+  }
+  else if (code == MT_ST_WRITE_THRESHOLD) {
     value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE;
     if (value < 1 || value > st_buffer_size) {
-      printk(KERN_WARNING "st: Write threshold %d too small or too large.\n",
-	     value);
+      printk(KERN_WARNING "st%d: Write threshold %d too small or too large.\n",
+	     dev, value);
       return (-EIO);
     }
     STp->write_threshold = value;
+    printk(KERN_INFO "st%d: Write threshold set to %d bytes.\n",
+	   dev, value);
+  }
+  else if (code == MT_ST_DEF_BLKSIZE) {
+    value = (options & ~MT_ST_OPTIONS);
+    if (value == ~MT_ST_OPTIONS) {
+      STm->default_blksize = (-1);
+      printk(KERN_INFO "st%d: Default block size disabled.\n", dev);
+    }
+    else {
+      STm->default_blksize = value;
+      printk(KERN_INFO "st%d: Default block size set to %d bytes.\n",
+	     dev, STm->default_blksize);
+    }
+  }
+  else if (code == MT_ST_DEF_OPTIONS) {
+    code = (options & ~MT_ST_CLEAR_DEFAULT);
+    value = (options & MT_ST_CLEAR_DEFAULT);
+    if (code == MT_ST_DEF_DENSITY) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STm->default_density = (-1);
+	printk(KERN_INFO "st%d: Density default disabled.\n", dev);
+      }
+      else {
+	STm->default_density = value & 0xff;
+	printk(KERN_INFO "st%d: Density default set to %x\n",
+	       dev, STm->default_density);
+      }
+    }
+    else if (code == MT_ST_DEF_DRVBUFFER) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STp->default_drvbuffer = 0xff;
+	printk(KERN_INFO "st%d: Drive buffer default disabled.\n", dev);
+      }
+      else {
+	STp->default_drvbuffer = value & 7;
+	printk(KERN_INFO "st%d: Drive buffer default set to %x\n",
+	       dev, STp->default_drvbuffer);
+      }
+    }
+    else if (code == MT_ST_DEF_COMPRESSION) {
+      if (value == MT_ST_CLEAR_DEFAULT) {
+	STm->default_compression = ST_DONT_TOUCH;
+	printk(KERN_INFO "st%d: Compression default disabled.\n", dev);
+      }
+      else {
+	STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+	printk(KERN_INFO "st%d: Compression default set to %x\n",
+	       dev, (value & 1));
+      }
+    }
+  }
+  else
+    return (-EIO);
+
+  return 0;
+}
+
+
+#define COMPRESSION_PAGE        0x0f
+#define COMPRESSION_PAGE_LENGTH 16
+
+#define MODE_HEADER_LENGTH  4
+
+#define DCE_MASK  0x80
+#define DCC_MASK  0x40
+#define RED_MASK  0x60
+
+
+/* Control the compression with mode page 15. Algorithm not changed if zero. */
+	static int
+st_compression(Scsi_Tape * STp, int state)
+{
+  int dev;
+  unsigned char cmd[10];
+  Scsi_Cmnd * SCpnt = NULL;
+
+  /* Read the current page contents */
+  memset(cmd, 0, 10);
+  cmd[0] = MODE_SENSE;
+  cmd[1] = 8;
+  cmd[2] = COMPRESSION_PAGE;
+  cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_TIMEOUT, 0);
+  dev = TAPE_NR(SCpnt->request.rq_dev);
+
+  if ((STp->buffer)->last_result_fatal != 0) {
 #if DEBUG
-    printk(ST_DEB_MSG "st%d: Write threshold set to %d bytes.\n", dev,
-	   STp->write_threshold);
+    if (debugging)
+      printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev);
 #endif
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+    return (-EIO);
+  }
+#if DEBUG
+  if (debugging)
+    printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
+	   ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0));
+#endif
+
+  /* Check if compression can be changed */
+  if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
+#if DEBUG
+    if (debugging)
+      printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev);
+#endif
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+    return (-EIO);
   }
+
+  /* Do the change */
+  if (state)
+    (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK;
   else
+    (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
+
+  memset(cmd, 0, 10);
+  cmd[0] = MODE_SELECT;
+  cmd[1] = 0x10;
+  cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+  (STp->buffer)->b_data[0] = 0;  /* Reserved data length */
+  (STp->buffer)->b_data[1] = 0;  /* Reserved media type byte */
+  (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
+  SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_TIMEOUT, 0);
+
+  if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+    if (debugging)
+      printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev);
+#endif
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
     return (-EIO);
+  }
 
-  return 0;
+#if DEBUG
+  if (debugging)
+    printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
+	   dev, state);
+#endif
+
+  SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+  STp->compression_changed = TRUE;
+  return 0;  /* Not implemented yet */
 }
 
 
@@ -1469,8 +1760,12 @@
        timeout = ST_LONG_TIMEOUT * 8;
 #endif
 #if DEBUG
-       if (debugging)
-	 printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev);
+       if (debugging) {
+	 if (cmd_in != MTLOAD)
+	   printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev);
+	 else
+	   printk(ST_DEB_MSG "st%d: Loading tape.\n", dev);
+       }
 #endif
        fileno = blkno = at_sm = 0 ;
        break; 
@@ -1599,12 +1894,16 @@
 	 (STp->buffer)->b_data[2] = 
 	   STp->drv_buffer << 4;
        (STp->buffer)->b_data[3] = 8;     /* block descriptor length */
-       if (cmd_in == MTSETDENSITY)
+       if (cmd_in == MTSETDENSITY) {
 	 (STp->buffer)->b_data[4] = arg;
+	 STp->density_changed = TRUE;  /* At least we tried ;-) */
+       }
        else
 	 (STp->buffer)->b_data[4] = STp->density;
-       if (cmd_in == MTSETBLK)
+       if (cmd_in == MTSETBLK) {
 	 ltmp = arg;
+	 STp->blksize_changed = TRUE;  /* At least we tried ;-) */
+       }
        else
 	 ltmp = STp->block_size;
        (STp->buffer)->b_data[9] = (ltmp >> 16);
@@ -1684,7 +1983,6 @@
        STp->rew_at_close = 0;
      else if (cmd_in == MTLOAD)
        STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
-
    } else {  /* SCSI command was not completely successful */
      if (SCpnt->sense_buffer[2] & 0x40) {
        if (cmd_in != MTBSF && cmd_in != MTBSFM &&
@@ -1760,6 +2058,7 @@
    unsigned char scmd[10];
    Scsi_Cmnd *SCpnt;
    Scsi_Tape *STp;
+   ST_mode *STm;
    int dev = TAPE_NR(inode->i_rdev);
 
    STp = &(scsi_tapes[dev]);
@@ -1769,6 +2068,7 @@
      return (-EIO);
    }
 #endif
+   STm = &(STp->modes[STp->current_mode]);
 
    /*
     * If this is something intended for the lower layer, just pass it
@@ -1791,6 +2091,14 @@
 
      memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
 
+     if (mtc.mt_op == MTSETDRVBUFFER && !suser()) {
+       printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev);
+       return (-EPERM);
+     }
+     if (!STm->defined &&
+	 (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0))
+       return (-ENXIO);
+
      if (!(STp->device)->was_reset) {
 
        if (STp->eof_hit) {
@@ -1807,7 +2115,8 @@
        i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
 			mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
 			mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
-			mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD);
+			mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
+			mtc.mt_op == MTCOMPRESSION);
        if (i < 0)
 	 return i;
      }
@@ -1846,10 +2155,16 @@
      if (mtc.mt_op == MTSETDRVBUFFER &&
 	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
        return st_set_options(inode, mtc.mt_count);
+     else if (mtc.mt_op == MTCOMPRESSION)
+       return st_compression(STp, (mtc.mt_count & 1));
      else
        return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count);
    }
-   else if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+
+   if (!STm->defined)
+     return (-ENXIO);
+
+   if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
 
      if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
        return (-EINVAL);
@@ -2135,18 +2450,33 @@
    tpnt->drv_buffer = 1;  /* Try buffering if no mode sense */
    tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
    tpnt->density = 0;
-   tpnt->do_buffer_writes = ST_BUFFER_WRITES;
-   tpnt->do_async_writes = ST_ASYNC_WRITES;
    tpnt->do_read_ahead = ST_READ_AHEAD;
    tpnt->do_auto_lock = ST_AUTO_LOCK;
+   tpnt->can_bsr = ST_IN_FILE_POS;
    tpnt->two_fm = ST_TWO_FM;
    tpnt->fast_mteom = ST_FAST_MTEOM;
    tpnt->write_threshold = st_write_threshold;
+   tpnt->default_drvbuffer = 0xff; /* No forced buffering */
    tpnt->drv_block = (-1);
    tpnt->moves_after_eof = 1;
    tpnt->at_sm = 0;
    (tpnt->mt_status)->mt_fileno = (tpnt->mt_status)->mt_blkno = (-1);
 
+   for (i=0; i < ST_NBR_MODES; i++) {
+     tpnt->modes[i].defined = FALSE;
+     tpnt->modes[i].defaults_for_writes = 0;
+     tpnt->modes[i].do_async_writes = ST_ASYNC_WRITES;
+     tpnt->modes[i].do_buffer_writes = ST_BUFFER_WRITES;
+     tpnt->modes[i].default_compression = ST_DONT_TOUCH;
+     tpnt->modes[i].default_blksize = (-1);  /* No forced size */
+     tpnt->modes[i].default_density = (-1);  /* No forced density */
+   }
+   tpnt->current_mode = 0;
+   tpnt->modes[0].defined = TRUE;
+
+   tpnt->density_changed = tpnt->compression_changed =
+     tpnt->blksize_changed = FALSE;
+
    st_template.nr_dev++;
    return 0;
 };
@@ -2187,6 +2517,8 @@
   st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
   if (st_template.dev_max < ST_MAX_TAPES)
     st_template.dev_max = ST_MAX_TAPES;
+  if (st_template.dev_max > 128 / ST_NBR_MODES)
+    printk(KERN_INFO "st: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
   scsi_tapes =
     (Scsi_Tape *) scsi_init_malloc(st_template.dev_max * sizeof(Scsi_Tape),
 				   GFP_ATOMIC);
@@ -2201,27 +2533,10 @@
 	 st_buffer_size, st_write_threshold);
 #endif
 
+  memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape));
   for (i=0; i < st_template.dev_max; ++i) {
     STp = &(scsi_tapes[i]);
-    STp->device = NULL;
     STp->capacity = 0xfffff;
-    STp->dirty = 0;
-    STp->rw = ST_IDLE;
-    STp->eof = ST_NOEOF;
-    STp->waiting = NULL;
-    STp->in_use = 0;
-    STp->drv_buffer = 1;  /* Try buffering if no mode sense */
-    STp->density = 0;
-    STp->do_buffer_writes = ST_BUFFER_WRITES;
-    STp->do_async_writes = ST_ASYNC_WRITES;
-    STp->do_read_ahead = ST_READ_AHEAD;
-    STp->do_auto_lock = ST_AUTO_LOCK;
-    STp->two_fm = ST_TWO_FM;
-    STp->fast_mteom = ST_FAST_MTEOM;
-    STp->write_threshold = st_write_threshold;
-    STp->drv_block = 0;
-    STp->moves_after_eof = 1;
-    STp->at_sm = 0;
     STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget),
 						       GFP_ATOMIC);
     /* Initialize status */
diff -u --recursive --new-file v1.3.84/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v1.3.84/linux/drivers/scsi/st.h	Sun Mar 31 00:13:17 1996
+++ linux/drivers/scsi/st.h	Mon Apr  8 11:22:20 1996
@@ -9,6 +9,7 @@
 #include "scsi.h"
 #endif
 
+/* The tape buffer descriptor. */
 typedef struct {
   unsigned char in_use;
   unsigned char dma;	/* DMA-able buffer */
@@ -24,11 +25,48 @@
   unsigned char *orig_b_data;
 } ST_buffer;
 
+
+/* The tape mode definition */
+typedef struct {
+  unsigned char defined;
+  unsigned char do_async_writes;
+  unsigned char do_buffer_writes;
+  unsigned char defaults_for_writes;
+  unsigned char default_compression; /* 0 = don't touch, etc */
+  short default_density; /* Forced density, -1 = no value */
+  int default_blksize;	/* Forced blocksize, -1 = no value */
+} ST_mode;
+
+#define ST_NBR_MODE_BITS 2
+#define ST_NBR_MODES (1 << ST_NBR_MODE_BITS)
+#define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
+#define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
+
+/* The tape drive descriptor */
 typedef struct {
   kdev_t devt;
   unsigned capacity;
   struct wait_queue * waiting;
   Scsi_Device* device;
+  Scsi_Cmnd SCpnt;
+  struct semaphore sem;
+  ST_buffer * buffer;
+
+  /* Drive characteristics */
+  unsigned char do_read_ahead;
+  unsigned char do_auto_lock;
+  unsigned char can_bsr;
+  unsigned char two_fm;
+  unsigned char fast_mteom;
+  unsigned char restr_dma;
+  unsigned char default_drvbuffer;  /* 0xff = don't touch, value 3 bits */
+  int write_threshold;
+
+  /* Mode characteristics */
+  ST_mode modes[ST_NBR_MODES];
+  int current_mode;
+
+  /* Status variables */
   unsigned char dirty;
   unsigned char rw;
   unsigned char ready;
@@ -37,29 +75,22 @@
   unsigned char drv_write_prot;
   unsigned char in_use;
   unsigned char eof_hit;
+  unsigned char moves_after_eof;
+  unsigned char at_sm;
+  unsigned char blksize_changed;
+  unsigned char density_changed;
+  unsigned char compression_changed;
   unsigned char drv_buffer;
-  unsigned char restr_dma;
-  unsigned char do_buffer_writes;
-  unsigned char do_async_writes;
-  unsigned char do_read_ahead;
-  unsigned char do_auto_lock;
-  unsigned char two_fm;
-  unsigned char fast_mteom;
   unsigned char density;
   unsigned char door_locked;
   unsigned char rew_at_close;
-  ST_buffer * buffer;
-  struct semaphore sem;
   int block_size;
   int min_block;
   int max_block;
-  int write_threshold;
   int recover_count;
   int drv_block;	/* The block where the drive head is */
-  unsigned char moves_after_eof;
-  unsigned char at_sm;
   struct mtget * mt_status;
-  Scsi_Cmnd SCpnt;
+
 #if DEBUG
   unsigned char write_pending;
   int nbr_finished;
@@ -95,6 +126,11 @@
 /* Positioning SCSI-commands for Tandberg, etc. drives */
 #define	QFA_REQUEST_BLOCK	0x02
 #define	QFA_SEEK_BLOCK		0x0c
+
+/* Setting the binary options */
+#define ST_DONT_TOUCH  0
+#define ST_NO          1
+#define ST_YES         2
 
 #endif
 
diff -u --recursive --new-file v1.3.84/linux/fs/Config.in linux/fs/Config.in
--- v1.3.84/linux/fs/Config.in	Sun Mar 31 00:13:18 1996
+++ linux/fs/Config.in	Mon Apr  8 15:55:41 1996
@@ -20,10 +20,10 @@
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
   if [ "$CONFIG_NFS_FS" = "y" ]; then
-    bool 'Root file system on NFS' CONFIG_ROOT_NFS
+    bool '   Root file system on NFS' CONFIG_ROOT_NFS
     if [ "$CONFIG_ROOT_NFS" = "y" ]; then
-      bool 'BOOTP support' CONFIG_RNFS_BOOTP
-      bool 'RARP support' CONFIG_RNFS_RARP
+      bool '      BOOTP support' CONFIG_RNFS_BOOTP
+      bool '      RARP support' CONFIG_RNFS_RARP
     fi
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
diff -u --recursive --new-file v1.3.84/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.84/linux/fs/buffer.c	Wed Mar 27 08:19:28 1996
+++ linux/fs/buffer.c	Mon Apr  8 10:45:00 1996
@@ -130,6 +130,7 @@
 	bh->b_count++;
 	add_wait_queue(&bh->b_wait, &wait);
 repeat:
+	run_task_queue(&tq_disk);
 	current->state = TASK_UNINTERRUPTIBLE;
 	if (buffer_locked(bh)) {
 		schedule();
@@ -993,6 +994,7 @@
 		 * wait for old buffer heads to become free due to
 		 * finishing IO..
 		 */
+		run_task_queue(&tq_disk);
 		sleep_on(&buffer_wait);
 	}
 
@@ -1817,6 +1819,7 @@
 
 static void wakeup_bdflush(int wait)
 {
+	run_task_queue(&tq_disk);
 	wake_up(&bdflush_wait);
 	if(wait) sleep_on(&bdflush_done);
 }
@@ -1930,7 +1933,7 @@
 			return -EINVAL;
 		bdf_prm.data[i] = data;
 		return 0;
-	};
+	}
 
 	/* Having func 0 used to launch the actual bdflush and then never
 	return (unless explicitly killed). We return zero here to 
@@ -2023,6 +2026,7 @@
 		if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount);
 		printk("sleeping again.\n");
 #endif
+		run_task_queue(&tq_disk);
 		wake_up(&bdflush_done);
 		
 		/* If there are still a lot of dirty buffers around, skip the sleep
diff -u --recursive --new-file v1.3.84/linux/fs/exec.c linux/fs/exec.c
--- v1.3.84/linux/fs/exec.c	Tue Mar  5 09:45:28 1996
+++ linux/fs/exec.c	Mon Apr  8 11:46:39 1996
@@ -465,7 +465,13 @@
 		bprm->e_gid = current->egid;
 	} else {
 		bprm->e_uid = (i & S_ISUID) ? bprm->inode->i_uid : current->euid;
-		bprm->e_gid = (i & S_ISGID) ? bprm->inode->i_gid : current->egid;
+		/*
+		 * If setgid is set but no group execute bit then this
+		 * is a candidate for mandatory locking, not a setgid
+		 * executable.
+		 */
+		bprm->e_gid = ((i & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) ?
+			bprm->inode->i_gid : current->egid;
 	}
 	if ((retval = permission(bprm->inode, MAY_EXEC)) != 0)
 		return retval;
diff -u --recursive --new-file v1.3.84/linux/fs/fcntl.c linux/fs/fcntl.c
--- v1.3.84/linux/fs/fcntl.c	Fri Nov 24 07:17:31 1995
+++ linux/fs/fcntl.c	Mon Apr  8 11:41:04 1996
@@ -13,8 +13,6 @@
 #include <linux/fcntl.h>
 #include <linux/string.h>
 
-extern int fcntl_getlk(unsigned int, struct flock *);
-extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
 extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
 
 static inline int dupfd(unsigned int fd, unsigned int arg)
diff -u --recursive --new-file v1.3.84/linux/fs/inode.c linux/fs/inode.c
--- v1.3.84/linux/fs/inode.c	Mon Mar 18 10:38:59 1996
+++ linux/fs/inode.c	Mon Apr  8 09:46:20 1996
@@ -54,11 +54,14 @@
 
 static inline void insert_inode_free(struct inode *inode)
 {
-	inode->i_next = first_inode;
-	inode->i_prev = first_inode->i_prev;
-	inode->i_next->i_prev = inode;
-	inode->i_prev->i_next = inode;
+	struct inode * prev, * next = first_inode;
+
 	first_inode = inode;
+	prev = next->i_prev;
+	inode->i_next = next;
+	inode->i_prev = prev;
+	prev->i_next = inode;
+	next->i_prev = inode;
 }
 
 static inline void remove_inode_free(struct inode *inode)
diff -u --recursive --new-file v1.3.84/linux/fs/locks.c linux/fs/locks.c
--- v1.3.84/linux/fs/locks.c	Fri Apr  5 13:35:28 1996
+++ linux/fs/locks.c	Mon Apr  8 12:13:18 1996
@@ -51,21 +51,24 @@
  *
  *  Removed some race conditions in flock_lock_file(), marked other possible
  *  races. Just grep for FIXME to see them. 
- *  Dmitry Gorodchanin (begemot@bgm.rosprint.net), Feb 09, 1996.
+ *  Dmitry Gorodchanin (begemot@bgm.rosprint.net), February 09, 1996.
  *
  *  Addressed Dmitry's concerns. Deadlock checking no longer recursive.
  *  Lock allocation changed to GFP_ATOMIC as we can't afford to sleep
  *  once we've checked for blocking and deadlocking.
- *  Andy Walker (andy@lysaker.kvaerner.no), Apr 03, 1996.
+ *  Andy Walker (andy@lysaker.kvaerner.no), April 03, 1996.
  *
  *  NOTE:
  *  Starting to look at mandatory locks - using SunOS as a model.
  *  Probably a configuration option because mandatory locking can cause
  *  all sorts  of chaos with runaway processes.
+ *
+ *  Initial implementation of mandatory locks. SunOS turned out to be
+ *  a rotten model, so I implemented the "obvious" semantics.
+ *  See 'linux/Documentation/mandatory.txt' for details.
+ *  Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
  */
 
-#include <asm/segment.h>
-
 #include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -73,6 +76,7 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 
+#include <asm/segment.h>
 
 #define OFFSET_MAX	((off_t)0x7fffffff)	/* FIXME: move elsewhere? */
 
@@ -282,6 +286,63 @@
 	}
 
 	return;
+}
+
+int locks_verify(int read_write, struct inode *inode, struct file *filp,
+		 unsigned int offset, unsigned int count)
+{
+	/* Candidates for mandatory locking have the setgid bit set
+	 * but no group execute bit -  an otherwise meaningless combination.
+	 */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return (locks_locked_mandatory(read_write, inode, filp,
+					       offset, count));
+	return (0);
+}
+	
+int locks_locked_mandatory(int read_write, struct inode *inode,
+			   struct file *filp, unsigned int offset,
+			   unsigned int count)
+{
+	struct file_lock *fl;
+
+repeat:
+	/*
+	 * Search the lock list for this inode for locks that conflict with
+	 * the proposed read/write.
+	 */
+	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (fl->fl_flags == F_FLOCK ||
+		    (fl->fl_flags == F_POSIX && fl->fl_owner == current))
+			continue;
+		if (fl->fl_end < offset ||
+		    fl->fl_start >= offset + count)
+			continue;
+		/*
+		 * Block for writes against a "read" lock, and both reads and
+		 * writes against a "write" lock.
+		 */
+		if (read_write == FLOCK_VERIFY_WRITE ||
+		    fl->fl_type == F_WRLCK) {
+			if (filp && (filp->f_flags & O_NONBLOCK))
+				return (-EAGAIN);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			if (posix_locks_deadlock(current, fl->fl_owner))
+				return (-EDEADLOCK);
+			interruptible_sleep_on(&fl->fl_wait);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			/*
+			 * If we've been sleeping someone might have changed
+			 * the permissions behind our back.
+			 */
+			if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
+				break;
+			goto repeat;
+		}
+	}
+	return (0);
 }
 
 /* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
diff -u --recursive --new-file v1.3.84/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c
--- v1.3.84/linux/fs/nfs/nfsiod.c	Tue Apr  2 13:32:22 1996
+++ linux/fs/nfs/nfsiod.c	Sun Apr  7 06:53:36 1996
@@ -17,6 +17,7 @@
 
 #include <linux/sched.h>
 #include <linux/nfs_fs.h>
+#include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/rpcsock.h>
 #include <linux/nfsiod.h>
diff -u --recursive --new-file v1.3.84/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.84/linux/fs/nfs/nfsroot.c	Tue Apr  2 13:32:22 1996
+++ linux/fs/nfs/nfsroot.c	Mon Apr  8 15:55:41 1996
@@ -43,6 +43,10 @@
  *				different RARP and NFS servers.
  *	Gero Kuhlmann	:	"0.0.0.0" addresses from command line are
  *				now mapped to INADDR_NONE.
+ *	Gero Kuhlmann	:	Fixed a bug which prevented BOOTP path name
+ *				from being used (thanks to Leo Spiekman)
+ *	Andy Walker	:	Allow to specify the NFS server in nfs_root
+ *				without giving a path name
  *
  */
 
@@ -1049,11 +1053,23 @@
 static int root_nfs_name(char *name)
 {
 	char buf[NFS_MAXPATHLEN];
-	char *cp, *options, *val;
+	char *cp, *cq, *options, *val;
+	int octets = 0;
 
 	/* It is possible to override the server IP number here */
-	if (*name >= '0' && *name <= '9' && (cp = strchr(name, ':')) != NULL) {
-		*cp++ = '\0';
+	cp = cq = name;
+	while (octets < 4) {
+		while (*cp >= '0' && *cp <= '9')
+			cp++;
+		if (cp == cq || cp - cq > 3)
+			break;
+		if (*cp == '.' || octets == 3)
+			octets++;
+		cq = cp;
+	}
+	if (octets == 4 && (*cp == ':' || *cp == '\0')) {
+		if (*cp == ':')
+			*cp++ = '\0';
 		server.sin_addr.s_addr = in_aton(name);
 		name = cp;
 	}
@@ -1064,12 +1080,15 @@
 						sizeof(nfs_data.hostname)-1);
 
 	/* Set the name of the directory to mount */
-	cp = in_ntoa(myaddr.sin_addr.s_addr);
-	strncpy(buf, name, 255);
+	if (nfs_path[0] == '\0' || !strncmp(name, "default", 7))
+		strncpy(buf, name, NFS_MAXPATHLEN);
+	else
+		strncpy(buf, nfs_path, NFS_MAXPATHLEN);
 	if ((options = strchr(buf, ',')))
 		*options++ = '\0';
 	if (!strcmp(buf, "default"))
 		strcpy(buf, NFS_ROOT);
+	cp = in_ntoa(myaddr.sin_addr.s_addr);
 	if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
 		printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
 		return -1;
diff -u --recursive --new-file v1.3.84/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v1.3.84/linux/fs/nfs/rpcsock.c	Wed Apr  3 16:06:56 1996
+++ linux/fs/nfs/rpcsock.c	Mon Apr  8 15:55:41 1996
@@ -285,7 +285,7 @@
 static inline void
 rpc_send_check(char *where, u32 *ptr)
 {
-	if (ptr[1] != 0 || ptr[2] != htonl(2) || ptr[3] != htonl(100003)) {
+	if (ptr[1] != htonl(RPC_CALL) || ptr[2] != htonl(RPC_VERSION)) {
 		printk("RPC: %s sending evil packet:\n"
 		       "     %08x %08x %08x %08x %08x %08x %08x %08x\n",
 		       where,
diff -u --recursive --new-file v1.3.84/linux/fs/open.c linux/fs/open.c
--- v1.3.84/linux/fs/open.c	Sat Feb 17 09:19:41 1996
+++ linux/fs/open.c	Mon Apr  8 12:13:18 1996
@@ -20,8 +20,6 @@
 
 #include <asm/segment.h>
 
-extern void locks_remove_locks(struct task_struct *, struct file *);
-
 asmlinkage int sys_statfs(const char * path, struct statfs * buf)
 {
 	struct inode * inode;
@@ -109,6 +107,11 @@
 		iput(inode);
 		return error;
 	}
+	error = locks_verify(FLOCK_VERIFY_WRITE, inode, NULL,
+			     length < inode->i_size ? length : inode->i_size,
+			     abs(inode->i_size - length));
+	if (error)
+		return error;
 	error = do_truncate(inode, length);
 	put_write_access(inode);
 	iput(inode);
@@ -119,6 +122,7 @@
 {
 	struct inode * inode;
 	struct file * file;
+	int error;
 
 	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 		return -EBADF;
@@ -128,6 +132,11 @@
 		return -EACCES;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
+	error = locks_verify(FLOCK_VERIFY_WRITE, inode, file,
+			     length < inode->i_size ? length : inode->i_size,
+			     abs(inode->i_size - length));
+	if (error)
+		return error;
 	return do_truncate(inode, length);
 }
 
@@ -391,8 +400,12 @@
 	}
 	/*
 	 * If the group has been changed, remove the setgid bit
+	 *
+	 * Don't remove the setgid bit if no group execute bit.
+	 * This is a file marked for mandatory locking.
 	 */
-	if (group != inode->i_gid && (inode->i_mode & S_ISGID)) {
+	if (group != inode->i_gid &&
+	    ((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
@@ -443,8 +456,12 @@
 	}
 	/*
 	 * If the group has been changed, remove the setgid bit
+	 *
+	 * Don't remove the setgid bit if no group execute bit.
+	 * This is a file marked for mandatory locking.
 	 */
-	if (group != inode->i_gid && (inode->i_mode & S_ISGID)) {
+	if (group != inode->i_gid &&
+	    ((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
 		newattrs.ia_mode &= ~S_ISGID;
 		newattrs.ia_valid |= ATTR_MODE;
 	}
diff -u --recursive --new-file v1.3.84/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.84/linux/fs/proc/array.c	Sun Mar 31 00:13:18 1996
+++ linux/fs/proc/array.c	Mon Apr  8 11:10:35 1996
@@ -42,9 +42,6 @@
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-#endif
 #include <linux/swap.h>
 
 #include <asm/segment.h>
@@ -1022,10 +1019,6 @@
 #ifdef CONFIG_BLK_DEV_MD
 	        case PROC_MD:
 			return get_md_status(page);
-#endif
-#ifdef CONFIG_APM
-		case PROC_APM:
-			return apm_proc(page);
 #endif
 #ifdef __SMP_PROF__
 		case PROC_SMP_PROF:
diff -u --recursive --new-file v1.3.84/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.84/linux/fs/proc/root.c	Thu Feb 15 14:37:11 1996
+++ linux/fs/proc/root.c	Mon Apr  8 11:10:35 1996
@@ -13,9 +13,6 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/config.h>
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-#endif
 #include <asm/bitops.h>
 
 /*
@@ -351,12 +348,6 @@
 		PROC_IOPORTS, 7, "ioports",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 	});
-#ifdef CONFIG_APM
-	proc_register(&proc_root, &(struct proc_dir_entry) {
-	        PROC_APM, 3, "apm",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-	});
-#endif
 	proc_register(&proc_root, &(struct proc_dir_entry) {
 		PROC_CMDLINE, 7, "cmdline",
 		S_IFREG | S_IRUGO, 1, 0, 0,
diff -u --recursive --new-file v1.3.84/linux/fs/read_write.c linux/fs/read_write.c
--- v1.3.84/linux/fs/read_write.c	Wed Feb  7 13:46:32 1996
+++ linux/fs/read_write.c	Mon Apr  8 16:05:02 1996
@@ -18,7 +18,7 @@
 asmlinkage int sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
 {
 	struct file * file;
-	int tmp = -1;
+	long tmp = -1;
 
 	if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode))
 		return -EBADF;
@@ -114,6 +114,9 @@
 		return -EINVAL;
 	if (!count)
 		return 0;
+	error = locks_verify(FLOCK_VERIFY_READ,inode,file,file->f_pos,count);
+	if (error)
+		return error;
 	error = verify_area(VERIFY_WRITE,buf,count);
 	if (error)
 		return error;
@@ -135,6 +138,9 @@
 		return -EINVAL;
 	if (!count)
 		return 0;
+	error = locks_verify(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
+	if (error)
+		return error;
 	error = verify_area(VERIFY_READ,buf,count);
 	if (error)
 		return error;
@@ -147,7 +153,12 @@
 	 */
 	if (!suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
 		struct iattr newattrs;
-		newattrs.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
+		/*
+		 * Don't turn off setgid if no group execute. This special
+		 * case marks candidates for mandatory locking.
+		 */
+		newattrs.ia_mode = inode->i_mode &
+			~(S_ISUID | ((inode->i_mode & S_IXGRP) ? S_ISGID : 0));
 		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_FORCE;
 		notify_change(inode, &newattrs);
 	}
@@ -215,6 +226,11 @@
 		if (retval)
 			return retval;
 	}
+
+	retval = locks_verify(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+			      inode, file, file->f_pos, tot_len);
+	if (retval)
+		return retval;
 
 	/*
 	 * Then do the actual IO.  Note that sockets need to be handled
diff -u --recursive --new-file v1.3.84/linux/fs/super.c linux/fs/super.c
--- v1.3.84/linux/fs/super.c	Sun Mar 24 20:07:00 1996
+++ linux/fs/super.c	Mon Apr  8 15:57:11 1996
@@ -860,6 +860,11 @@
 				current->fs->root = inode;
 				ROOT_DEV = sb->s_dev;
 				printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+				vfsmnt = add_vfsmnt(ROOT_DEV, "rootfs", "/");
+				if (!vfsmnt)
+					panic("VFS: add_vfsmnt failed for NFS root.\n");
+				vfsmnt->mnt_sb = sb;
+				vfsmnt->mnt_flags = sb->s_flags;
 				return;
 			}
 			sb->s_dev = 0;
diff -u --recursive --new-file v1.3.84/linux/include/asm-sparc/floppy.h linux/include/asm-sparc/floppy.h
--- v1.3.84/linux/include/asm-sparc/floppy.h	Mon Mar  4 08:50:00 1996
+++ linux/include/asm-sparc/floppy.h	Sun Apr  7 09:45:56 1996
@@ -66,6 +66,9 @@
 #define fd_cacheflush(addr, size) /* nothing... */
 #define fd_request_irq()          sun_fd_request_irq()
 #define fd_free_irq()             /* nothing... */
+#define fd_eject(x)               sun_fd_eject()
+
+#define FLOPPY_MOTOR_MASK         0x10
 
 /* It's all the same... */
 #define virt_to_bus(x)            (x)
@@ -205,7 +208,11 @@
  */
 char *pdma_vaddr;
 unsigned long pdma_size;
-int doing_pdma = 0;
+volatile int doing_pdma = 0;
+
+/* This is software state */
+char *pdma_base = 0;
+unsigned long pdma_areasize;
 
 /* Common routines to all controller types on the Sparc. */
 static inline void virtual_dma_init(void)
@@ -216,6 +223,10 @@
 static inline void sun_fd_disable_dma(void)
 {
 	doing_pdma = 0;
+	if (pdma_base) {
+		mmu_unlockarea(pdma_base, pdma_areasize);
+		pdma_base = 0;
+	}
 }
 
 static inline void sun_fd_set_dma_mode(int mode)
@@ -245,8 +256,17 @@
 
 static inline void sun_fd_enable_dma(void)
 {
-	/* We're about to let it rip, lock any tlb entries necessary. */
 	pdma_vaddr = mmu_lockarea(pdma_vaddr, pdma_size);
+	pdma_base = pdma_vaddr;
+	pdma_areasize = pdma_size;
+}
+
+static int sun_fd_eject(void)
+{
+	set_auxio(AUXIO_FLPY_DSEL, AUXIO_FLPY_EJCT);
+	udelay(1000);
+	set_auxio(AUXIO_FLPY_EJCT, AUXIO_FLPY_DSEL);
+	return 0;
 }
 
 /* Our low-level entry point in arch/sparc/kernel/entry.S */
@@ -338,14 +358,12 @@
 		}
 	}
 
-	/* P3: The only realiable way which I found for ejection
+	/* P3: The only reliable way which I found for ejection
 	 * of boot floppy. AUXIO_FLPY_EJCT is not enougth alone.
 	 */
-	set_auxio(AUXIO_FLPY_EJCT, 0);
-	udelay(1000);
-	set_auxio(AUXIO_FLPY_DSEL, AUXIO_FLPY_EJCT);
+	set_auxio(AUXIO_FLPY_EJCT, 0); /* Bring EJECT line to normal. */
 	udelay(1000);
-	set_auxio(0, AUXIO_FLPY_DSEL);
+	sun_fd_eject(0);		/* Send Eject Pulse. */
 
 	/* Success... */
 	return (int) sun_fdc;
diff -u --recursive --new-file v1.3.84/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v1.3.84/linux/include/linux/apm_bios.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/apm_bios.h	Mon Apr  8 18:25:44 1996
@@ -81,8 +81,6 @@
 extern int		apm_register_callback(int (*callback)(apm_event_t));
 extern void		apm_unregister_callback(int (*callback)(apm_event_t));
 
-extern int		apm_proc(char *);
-
 extern int		apm_display_blank(void);
 extern int		apm_display_unblank(void);
 
diff -u --recursive --new-file v1.3.84/linux/include/linux/fd.h linux/include/linux/fd.h
--- v1.3.84/linux/include/linux/fd.h	Wed Nov 15 09:23:34 1995
+++ linux/include/linux/fd.h	Sun Apr  7 09:45:56 1996
@@ -365,4 +365,7 @@
 /* flicker motor-on bit before reading a sector. Experimental */
 
 
+#define FDEJECT _IO(2, 0x5a)
+/* eject the disk */
+
 #endif
diff -u --recursive --new-file v1.3.84/linux/include/linux/fdreg.h linux/include/linux/fdreg.h
--- v1.3.84/linux/include/linux/fdreg.h	Wed Feb  7 08:55:40 1996
+++ linux/include/linux/fdreg.h	Sun Apr  7 09:45:56 1996
@@ -105,7 +105,8 @@
 #define FDC_8272A	0x20	/* Intel 8272a, NEC 765 */
 #define FDC_765ED	0x30	/* Non-Intel 1MB-compatible FDC, can't detect */
 #define FDC_82072	0x40	/* Intel 82072; 8272a + FIFO + DUMPREGS */
-#define FDC_82077_ORIG	0x50	/* Original version of 82077AA, sans LOCK */
+#define FDC_82072A	0x50	/* 82072 on a Sparc */
+				/* Was: Original version of 82077AA, sans LOCK */
 #define FDC_82077	0x52	/* 82077AA-1 */
 #define FDC_82077_UNKN	0x53	/* Unknown 82077 variant */
 #define FDC_82078	0x60	/* 44pin 82078 or 64pin 82078SL */
diff -u --recursive --new-file v1.3.84/linux/include/linux/firewall.h linux/include/linux/firewall.h
--- v1.3.84/linux/include/linux/firewall.h	Tue Oct 17 13:42:35 1995
+++ linux/include/linux/firewall.h	Mon Apr  8 11:41:03 1996
@@ -15,11 +15,11 @@
 {
 	struct firewall_ops *next;
 	int (*fw_forward)(struct firewall_ops *this, int pf, 
-			struct sk_buff *skb, void *phdr);
+			struct device *dev, void *phdr);
 	int (*fw_input)(struct firewall_ops *this, int pf, 
-			struct sk_buff *skb, void *phdr);
+			struct device *dev, void *phdr);
 	int (*fw_output)(struct firewall_ops *this, int pf, 
-			struct sk_buff *skb, void *phdr);
+			struct device *dev, void *phdr);
 	/* Data falling in the second 486 cache line isn't used directly
 	   during a firewall call and scan, only by insert/delete and other
 	   unusual cases
@@ -31,9 +31,9 @@
 #ifdef __KERNEL__
 extern int register_firewall(int pf, struct firewall_ops *fw);
 extern int unregister_firewall(int pf, struct firewall_ops *fw);
-extern int call_fw_firewall(int pf, struct sk_buff *skb, void *phdr);
-extern int call_in_firewall(int pf, struct sk_buff *skb, void *phdr);
-extern int call_out_firewall(int pf, struct sk_buff *skb, void *phdr);
+extern int call_fw_firewall(int pf, struct device *dev, void *phdr);
+extern int call_in_firewall(int pf, struct device *dev, void *phdr);
+extern int call_out_firewall(int pf, struct device *dev, void *phdr);
 extern void fwchain_init(void);
 #endif
 
diff -u --recursive --new-file v1.3.84/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.84/linux/include/linux/fs.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/fs.h	Mon Apr  8 18:24:28 1996
@@ -320,7 +320,7 @@
 	loff_t f_pos;
 	unsigned short f_flags;
 	unsigned short f_count;
-	off_t f_reada;
+	loff_t f_reada;
 	struct file *f_next, *f_prev;
 	int f_owner;		/* pid or -pgrp where SIGIO should be sent */
 	struct inode * f_inode;
@@ -342,6 +342,33 @@
 	off_t fl_start;
 	off_t fl_end;
 };
+
+#include <linux/fcntl.h>
+
+extern int fcntl_getlk(unsigned int fd, struct flock *l);
+extern int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l);
+extern void locks_remove_locks(struct task_struct *task, struct file *filp);
+
+#include <linux/stat.h>
+
+#define FLOCK_VERIFY_READ  1
+#define FLOCK_VERIFY_WRITE 2
+
+extern int locks_locked_mandatory(int read_write, struct inode *inode,
+				  struct file *filp, unsigned int offset,
+				  unsigned int count);
+extern inline int locks_verify(int read_write, struct inode *inode,
+			       struct file *filp, unsigned int offset,
+			       unsigned int count)
+{
+	/* Candidates for mandatory locking have the setgid bit set
+	 * but no group execute bit -  an otherwise meaningless combination.
+	 */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return (locks_locked_mandatory(read_write, inode, filp,
+					       offset, count));
+	return (0);
+}
 
 struct fasync_struct {
 	int    magic;
diff -u --recursive --new-file v1.3.84/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.84/linux/include/linux/major.h	Sun Mar 31 00:13:18 1996
+++ linux/include/linux/major.h	Sun Apr  7 09:42:34 1996
@@ -41,7 +41,9 @@
 #define MITSUMI_X_CDROM_MAJOR 20
 #define SCSI_GENERIC_MAJOR 21
 #define Z8530_MAJOR 34
+#define DIGI_MAJOR 22
 #define IDE1_MAJOR	22
+#define DIGICU_MAJOR 23
 #define MITSUMI_CDROM_MAJOR 23
 #define CDU535_CDROM_MAJOR 24
 #define STL_SERIALMAJOR 24
diff -u --recursive --new-file v1.3.84/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v1.3.84/linux/include/linux/mtio.h	Wed Nov 15 09:25:48 1995
+++ linux/include/linux/mtio.h	Mon Apr  8 11:22:20 1996
@@ -56,7 +56,7 @@
 #define MTUNLOCK 29	/* unlock the drive door */
 #define MTLOAD  30	/* execute the SCSI load command */
 #define MTUNLOAD 31	/* execute the SCSI unload command */
-
+#define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */
 
 /* structure for MTIOCGET - mag tape get status command */
 
@@ -205,12 +205,9 @@
 #define GMT_IM_REP_EN(x)        ((x) & 0x00010000)  /* immediate report mode */
 /* 16 generic status bits unused */
 
-/* DDS drives have 'setmarks', sort of like filemarks but used to group
- * files, rather than blocks. Not used. Not supported.
- * I think DDS drives are DAT drives.
- */
 
 /* SCSI-tape specific definitions */
+/* Bitfield shifts in the status  */
 #define MT_ST_BLKSIZE_SHIFT	0
 #define MT_ST_BLKSIZE_MASK	0xffffff
 #define MT_ST_DENSITY_SHIFT	24
@@ -219,9 +216,15 @@
 #define MT_ST_SOFTERR_SHIFT	0
 #define MT_ST_SOFTERR_MASK	0xffff
 
+/* Bitfields for the MTSETDRVBUFFER ioctl */
 #define MT_ST_OPTIONS		0xf0000000
 #define MT_ST_BOOLEANS		0x10000000
+#define MT_ST_SETBOOLEANS	0x30000000
+#define MT_ST_CLEARBOOLEANS	0x40000000
 #define MT_ST_WRITE_THRESHOLD	0x20000000
+#define MT_ST_DEF_BLKSIZE	0x50000000
+#define MT_ST_DEF_OPTIONS	0x60000000
+
 #define MT_ST_BUFFER_WRITES	0x1
 #define MT_ST_ASYNC_WRITES	0x2
 #define MT_ST_READ_AHEAD	0x4
@@ -229,5 +232,13 @@
 #define MT_ST_TWO_FM		0x10
 #define MT_ST_FAST_MTEOM	0x20
 #define MT_ST_AUTO_LOCK		0x40
+#define MT_ST_DEF_WRITES	0x80
+#define MT_ST_CAN_BSR		0x100
+
+/* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
+#define MT_ST_CLEAR_DEFAULT	0xfffff
+#define MT_ST_DEF_DENSITY	(MT_ST_DEF_OPTIONS | 0x100000)
+#define MT_ST_DEF_COMPRESSION	(MT_ST_DEF_OPTIONS | 0x200000)
+#define MT_ST_DEF_DRVBUFFER	(MT_ST_DEF_OPTIONS | 0x300000)
 
 #endif /* _LINUX_MTIO_H */
diff -u --recursive --new-file v1.3.84/linux/include/linux/pci.h linux/include/linux/pci.h
--- v1.3.84/linux/include/linux/pci.h	Thu Mar 28 17:34:34 1996
+++ linux/include/linux/pci.h	Mon Apr  8 10:16:06 1996
@@ -569,8 +569,8 @@
  * devices.  The slot/function address of each device is encoded
  * in a single byte as follows:
  *
- *	7:4 = slot
- *	3:0 = function
+ *	7:3 = slot
+ *	2:0 = function
  */
 #define PCI_DEVFN(slot,func)	((((slot) & 0x1f) << 3) | ((func) & 0x07))
 #define PCI_SLOT(devfn)		(((devfn) >> 3) & 0x1f)
diff -u --recursive --new-file v1.3.84/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.84/linux/include/linux/proc_fs.h	Sun Mar 31 00:13:18 1996
+++ linux/include/linux/proc_fs.h	Mon Apr  8 18:24:34 1996
@@ -34,7 +34,6 @@
 	PROC_KSYMS,
 	PROC_DMA,	
 	PROC_IOPORTS,
-	PROC_APM,
 #ifdef __SMP_PROF__
 	PROC_SMP_PROF,
 #endif
diff -u --recursive --new-file v1.3.84/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.84/linux/include/linux/skbuff.h	Fri Apr  5 13:35:28 1996
+++ linux/include/linux/skbuff.h	Mon Apr  8 18:24:34 1996
@@ -53,7 +53,7 @@
 #if CONFIG_SKB_CHECK
 	int		magic_debug_cookie;
 #endif
-	struct sk_buff	* volatile link3;	/* Link for IP protocol level buffer chains 	*/
+	struct sk_buff	*link3;			/* Link for IP protocol level buffer chains 	*/
 	struct sock	*sk;			/* Socket we are owned by 			*/
 	unsigned long	when;			/* used to compute rtt's			*/
 	struct timeval	stamp;			/* Time we arrived				*/
diff -u --recursive --new-file v1.3.84/linux/include/linux/tqueue.h linux/include/linux/tqueue.h
--- v1.3.84/linux/include/linux/tqueue.h	Tue Apr  2 13:32:22 1996
+++ linux/include/linux/tqueue.h	Mon Apr  8 10:26:51 1996
@@ -50,7 +50,7 @@
 
 #define DECLARE_TASK_QUEUE(q)  task_queue q = NULL
 
-extern task_queue tq_timer, tq_immediate, tq_scheduler;
+extern task_queue tq_timer, tq_immediate, tq_scheduler, tq_disk;
 
 /*
  * To implement your own list of active bottom halfs, use the following
diff -u --recursive --new-file v1.3.84/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.84/linux/include/net/tcp.h	Mon Mar 25 10:26:32 1996
+++ linux/include/net/tcp.h	Mon Apr  8 18:29:25 1996
@@ -199,13 +199,8 @@
 	/* 
          * compute the actual window i.e. 
          * old_window - received_bytes_on_that_win.
-	 *
-	 * Don't raise the window if we have lots left:
-	 * that only results in unnecessary packets.
 	 */
 	window = sk->window - (sk->acked_seq - sk->lastwin_seq);	
-	if (window >= MAX_WINDOW/2)
-		return 0;
 
 	free_space = sock_rspace(sk);
 	if (free_space > 1024)
diff -u --recursive --new-file v1.3.84/linux/init/main.c linux/init/main.c
--- v1.3.84/linux/init/main.c	Tue Apr  2 13:32:23 1996
+++ linux/init/main.c	Mon Apr  8 11:10:35 1996
@@ -32,9 +32,6 @@
 #include <linux/mm.h>
 #include <linux/major.h>
 #include <linux/blk.h>
-#ifdef CONFIG_APM
-#include <linux/apm_bios.h>
-#endif
 #ifdef CONFIG_ROOT_NFS
 #include <linux/nfs_fs.h>
 #endif
@@ -135,6 +132,10 @@
 extern void teles_setup(char *str, int *ints);
 #endif
 
+#ifdef CONFIG_DIGI
+extern void pcxx_setup(char *str, int *ints);
+#endif
+
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
 #endif
@@ -335,6 +336,9 @@
 #ifdef CONFIG_ISDN_DRV_TELES
 	{ "teles=", teles_setup },
 #endif
+#ifdef CONFIG_DIGI
+	{ "digi=", pcxx_setup },
+#endif
 	{ 0, 0 }
 };
 
@@ -727,9 +731,6 @@
 	sock_init();
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 	ipc_init();
-#endif
-#ifdef CONFIG_APM
-	apm_bios_init();
 #endif
 	dquot_init();
 	arch_syms_export();
diff -u --recursive --new-file v1.3.84/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.84/linux/mm/filemap.c	Wed Apr  3 16:06:57 1996
+++ linux/mm/filemap.c	Mon Apr  8 16:05:02 1996
@@ -276,6 +276,7 @@
 	page->count++;
 	add_wait_queue(&page->wait, &wait);
 repeat:
+	run_task_queue(&tq_disk);
 	current->state = TASK_UNINTERRUPTIBLE;
 	if (page->locked) {
 		schedule();
@@ -300,6 +301,7 @@
 {
 	int error, read;
 	unsigned long pos, page_cache;
+	unsigned long ra_pos, ra_end;	/* read-ahead */
 	
 	if (count <= 0)
 		return 0;
@@ -308,6 +310,14 @@
 	page_cache = 0;
 
 	pos = filp->f_pos;
+	ra_pos = filp->f_reada;
+	ra_end = MAX_READAHEAD;
+	if (!ra_pos) {
+		ra_pos = (pos + PAGE_SIZE) & PAGE_MASK;
+		ra_end = 0;
+	}
+	ra_end += pos + count;
+
 	for (;;) {
 		struct page *page;
 		unsigned long offset, addr, nr;
@@ -358,15 +368,9 @@
 		 *  - if "f_reada" is set
 		 */
 		if (page->locked) {
-			unsigned long max_ahead, ahead;
-
-			max_ahead = count - nr;
-			if (filp->f_reada || max_ahead > MAX_READAHEAD)
-				max_ahead = MAX_READAHEAD;
-			ahead = 0;
-			while (ahead < max_ahead) {
-				ahead += PAGE_SIZE;
-				page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
+			while (ra_pos < ra_end) {
+				page_cache = try_to_read_ahead(inode, ra_pos, page_cache);
+				ra_pos += PAGE_SIZE;
 				if (!page->locked)
 					goto unlocked_page;
 			}
@@ -418,17 +422,30 @@
 		break;
 	}
 
-	filp->f_pos = pos;
-	filp->f_reada = 1;
+	if (read) {
+		error = read;
+
+		/*
+		 * Start some extra read-ahead if we haven't already
+		 * read ahead enough..
+		 */
+		while (ra_pos < ra_end) {
+			page_cache = try_to_read_ahead(inode, ra_pos, page_cache);
+			ra_pos += PAGE_SIZE;
+		}
+		run_task_queue(&tq_disk);
+
+		filp->f_pos = pos;
+		filp->f_reada = ra_pos;
+		if (!IS_RDONLY(inode)) {
+			inode->i_atime = CURRENT_TIME;
+			inode->i_dirt = 1;
+		}
+	}
 	if (page_cache)
 		free_page(page_cache);
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
-	if (!read)
-		read = error;
-	return read;
+
+	return error;
 }
 
 /*
diff -u --recursive --new-file v1.3.84/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.84/linux/net/appletalk/ddp.c	Tue Apr  2 13:32:23 1996
+++ linux/net/appletalk/ddp.c	Mon Apr  8 11:41:04 1996
@@ -1472,7 +1472,7 @@
 
 #ifdef CONFIG_FIREWALL
 	
-	if(call_in_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+	if(call_in_firewall(AF_APPLETALK, skb->dev, ddp)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -1506,7 +1506,7 @@
 		 *	Check firewall allows this routing
 		 */
 		
-		if(call_fw_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+		if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp)!=FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_READ);
 			return(0);
@@ -1767,7 +1767,7 @@
 		
 #ifdef CONFIG_FIREWALL
 
-	if(call_out_firewall(AF_APPLETALK, skb, ddp)!=FW_ACCEPT)
+	if(call_out_firewall(AF_APPLETALK, skb->dev, ddp)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_WRITE);
 		return -EPERM;
diff -u --recursive --new-file v1.3.84/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.84/linux/net/ax25/af_ax25.c	Tue Apr  2 13:32:23 1996
+++ linux/net/ax25/af_ax25.c	Mon Apr  8 11:41:04 1996
@@ -1617,7 +1617,7 @@
 	skb->h.raw = skb->data;
 	
 #ifdef CONFIG_FIREWALL
-	if (call_in_firewall(PF_AX25, skb, skb->h.raw) != FW_ACCEPT) {
+	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw) != FW_ACCEPT) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -1675,7 +1675,7 @@
 
 			build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
 #ifdef CONFIG_FIREWALL
-			if (call_fw_firewall(PF_AX25, skb, skb->data) != FW_ACCEPT) {
+			if (call_fw_firewall(PF_AX25, skb->dev, skb->data) != FW_ACCEPT) {
 				kfree_skb(skb, FREE_READ);
 				return 0;
 			}
@@ -2461,7 +2461,7 @@
 	unsigned char *ptr;
 	
 #ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_AX25, skb, skb->data) != FW_ACCEPT) {
+	if (call_out_firewall(PF_AX25, skb->dev, skb->data) != FW_ACCEPT) {
 		dev_kfree_skb(skb, FREE_WRITE);
 		return;
 	}
diff -u --recursive --new-file v1.3.84/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.84/linux/net/core/dev.c	Sun Mar 31 00:13:19 1996
+++ linux/net/core/dev.c	Sun Apr  7 08:47:04 1996
@@ -1305,7 +1305,6 @@
 extern int lance_init(void);
 extern int ni65_init(void);
 extern int pi_init(void);
-extern int dec21040_init(void);
 extern void sdla_setup(void);
 extern void dlci_setup(void);
 
@@ -1336,9 +1335,6 @@
 #if defined(CONFIG_PT)
 	pt_init();
 #endif
-#if defined(CONFIG_DEC_ELCP)
-	dec21040_init();
-#endif	
 #if defined(CONFIG_DLCI)
         dlci_setup();
 #endif
diff -u --recursive --new-file v1.3.84/linux/net/core/firewall.c linux/net/core/firewall.c
--- v1.3.84/linux/net/core/firewall.c	Wed Apr  3 16:06:57 1996
+++ linux/net/core/firewall.c	Mon Apr  8 11:41:04 1996
@@ -98,13 +98,13 @@
 	return -ENOENT;
 }
 
-int call_fw_firewall(int pf, struct sk_buff *skb, void *phdr)
+int call_fw_firewall(int pf, struct device *dev, void *phdr)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 	
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_forward(fw,pf,skb,phdr);
+		int rc=fw->fw_forward(fw,pf,dev,phdr);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
@@ -116,13 +116,13 @@
  *	Actual invocation of the chains
  */
  
-int call_in_firewall(int pf, struct sk_buff *skb, void *phdr)
+int call_in_firewall(int pf, struct device *dev, void *phdr)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 	
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_input(fw,pf,skb,phdr);
+		int rc=fw->fw_input(fw,pf,dev,phdr);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
@@ -130,13 +130,13 @@
 	return firewall_policy[pf];
 }
 
-int call_out_firewall(int pf, struct sk_buff *skb, void *phdr)
+int call_out_firewall(int pf, struct device *dev, void *phdr)
 {
 	struct firewall_ops *fw=firewall_chain[pf];
 	
 	while(fw!=NULL)
 	{
-		int rc=fw->fw_output(fw,pf,skb,phdr);
+		int rc=fw->fw_output(fw,pf,dev,phdr);
 		if(rc!=FW_SKIP)
 			return rc;
 		fw=fw->next;
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.84/linux/net/ipv4/ip_forward.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_forward.c	Mon Apr  8 11:41:05 1996
@@ -102,17 +102,18 @@
 	int fw_res = 0;		/* Forwarding result */	
 #ifdef CONFIG_IP_MASQUERADE	
 	struct sk_buff *skb_in = skb;	/* So we can remember if the masquerader did some swaps */
-#endif
+#endif /* CONFIG_IP_MASQUERADE */
+#endif /* CONFIG_FIREWALL */
 	
 	/* 
 	 *	See if we are allowed to forward this.
  	 *	Note: demasqueraded fragments are always 'back'warded.
 	 */
-
 	
+#ifdef CONFIG_FIREWALL
 	if(!(is_frag&IPFWD_MASQUERADED))
 	{
-		fw_res=call_fw_firewall(PF_INET, skb, skb->h.iph);
+		fw_res=call_fw_firewall(PF_INET, dev, skb->h.iph);
 		switch (fw_res) {
 		case FW_ACCEPT:
 		case FW_MASQUERADE:
@@ -125,6 +126,7 @@
 		}
 	}
 #endif
+
 	/*
 	 *	According to the RFC, we must first decrease the TTL field. If
 	 *	that reaches zero, we must reply an ICMP control message telling
@@ -200,7 +202,8 @@
 		}
 
 		/*
-		 *	Having picked a route we can now send the frame out.
+		 *	Having picked a route we can now send the frame out
+		 *	after asking the firewall permission to do so.
 		 */
 
 		dev2 = rt->rt_dev;
@@ -234,7 +237,6 @@
 	}
 #endif	
 	
-
 	/*
 	 * We now may allocate a new buffer, and copy the datagram into it.
 	 * If the indicated interface is up and running, kick it.
@@ -351,7 +353,7 @@
 #endif			
 		}
 #ifdef CONFIG_FIREWALL
-		if((fw_res = call_out_firewall(PF_INET, skb2, iph)) < FW_ACCEPT)
+		if((fw_res = call_out_firewall(PF_INET, skb2->dev, iph)) < FW_ACCEPT)
 		{
 			/* FW_ACCEPT and FW_MASQUERADE are treated equal:
 			   masquerading is only supported via forward rules */
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.84/linux/net/ipv4/ip_fw.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_fw.c	Mon Apr  8 11:41:04 1996
@@ -96,7 +96,6 @@
 #include <net/route.h>
 #include <net/tcp.h>
 #include <net/udp.h>
-#include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/icmp.h>
 #include <linux/firewall.h>
@@ -607,8 +606,12 @@
 	}
 
 	memcpy(ftmp, frwl, len);
-	ftmp->fw_tosand |= 0x03;
-	ftmp->fw_tosxor &= 0xFC;
+	/*
+	 *	Allow the more recent "minimise cost" flag to be
+	 *	set. [Rob van Nieuwkerk]
+	 */
+	ftmp->fw_tosand |= 0x01;
+	ftmp->fw_tosxor &= 0xFE;
 	ftmp->fw_pcnt=0L;
 	ftmp->fw_bcnt=0L;
 
@@ -1063,19 +1066,19 @@
  *	Interface to the generic firewall chains.
  */
  
-int ipfw_input_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr)
 {
-	return ip_fw_chk(phdr, skb->dev, ip_fw_in_chain, ip_fw_in_policy, 0);
+	return ip_fw_chk(phdr, dev, ip_fw_in_chain, ip_fw_in_policy, 0);
 }
 
-int ipfw_output_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr)
 {
-	return ip_fw_chk(phdr, skb->dev, ip_fw_out_chain, ip_fw_out_policy, 0);
+	return ip_fw_chk(phdr, dev, ip_fw_out_chain, ip_fw_out_policy, 0);
 }
 
-int ipfw_forward_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr)
 {
-	return ip_fw_chk(phdr, skb->dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0);
+	return ip_fw_chk(phdr, dev, ip_fw_fwd_chain, ip_fw_fwd_policy, 0);
 }
  
 struct firewall_ops ipfw_ops=
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.84/linux/net/ipv4/ip_input.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_input.c	Mon Apr  8 11:41:04 1996
@@ -304,7 +304,7 @@
 	
 #ifdef	CONFIG_FIREWALL
 
-	if ((err=call_in_firewall(PF_INET, skb, iph))<FW_ACCEPT)
+	if ((err=call_in_firewall(PF_INET, skb->dev, iph))<FW_ACCEPT)
 	{
 		if(err==FW_REJECT)
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v1.3.84/linux/net/ipv4/ip_options.c	Tue Nov 14 16:03:58 1995
+++ linux/net/ipv4/ip_options.c	Mon Apr  8 16:55:12 1996
@@ -274,7 +274,7 @@
 			continue;
 		}
 		optlen = optptr[1];
-		if (l<2 || optlen>l) 
+		if (l<2 || optlen>l || !optlen)
 		{
 			pp_ptr = optptr;
 			break;
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.84/linux/net/ipv4/ip_output.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_output.c	Mon Apr  8 11:41:04 1996
@@ -359,7 +359,7 @@
 	iph->tot_len = htons(skb->len-(((unsigned char *)iph)-skb->data));
 
 #ifdef CONFIG_FIREWALL
-	if(call_out_firewall(PF_INET, skb, iph) < FW_ACCEPT)
+	if(call_out_firewall(PF_INET, skb->dev, iph) < FW_ACCEPT)
 		/* just don't send this packet */
 		return;
 #endif	
@@ -701,7 +701,7 @@
 			getfrag(frag,saddr,(void *)iph,0,length-20);
 		dev_unlock_list();
 #ifdef CONFIG_FIREWALL
-		if(call_out_firewall(PF_INET, skb, iph)< FW_ACCEPT)
+		if(call_out_firewall(PF_INET, skb->dev, iph)< FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_WRITE);
 			return -EPERM;
@@ -905,7 +905,7 @@
 		 */
 		 
 #ifdef CONFIG_FIREWALL
-		if(!offset && call_out_firewall(PF_INET, skb, iph) < FW_ACCEPT)
+		if(!offset && call_out_firewall(PF_INET, skb->dev, iph) < FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_WRITE);
 			dev_unlock_list();
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v1.3.84/linux/net/ipv4/ipip.c	Thu Feb 15 14:52:54 1996
+++ linux/net/ipv4/ipip.c	Mon Apr  8 11:41:04 1996
@@ -85,7 +85,7 @@
 	 *	Check the firewall [well spotted Olaf]
 	 */
 	 
-	if((err=call_in_firewall(PF_INET, skb, skb->ip_hdr))<FW_ACCEPT)
+	if((err=call_in_firewall(PF_INET, skb->dev, skb->ip_hdr))<FW_ACCEPT)
 	{
 		if(err==FW_REJECT)
 			icmp_send(skb,ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0 , dev);
diff -u --recursive --new-file v1.3.84/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.84/linux/net/ipv4/tcp_input.c	Fri Apr  5 13:35:28 1996
+++ linux/net/ipv4/tcp_input.c	Fri Apr  5 18:31:24 1996
@@ -755,12 +755,14 @@
 	/* 
 	 *	See if we can take anything off of the retransmit queue.
 	 */
-   
-	while(sk->send_head != NULL) 
-	{
+
+	for (;;) {
+		struct sk_buff * skb = sk->send_head;
+		if (!skb)
+			break;
+
 		/* Check for a bug. */
-		if (sk->send_head->link3 &&
-		    after(sk->send_head->end_seq, sk->send_head->link3->end_seq)) 
+		if (skb->link3 && after(skb->end_seq, skb->link3->end_seq)) 
 			printk("INET: tcp.c: *** bug send_list out of order.\n");
 			
 		/*
@@ -768,78 +770,57 @@
 		 *	discard it as it's confirmed to have arrived the other end.
 		 */
 		 
-		if (before(sk->send_head->end_seq, ack+1)) 
-		{
-			struct sk_buff *oskb;	
-			if (sk->retransmits) 
-			{	
-				/*
-				 *	We were retransmitting.  don't count this in RTT est 
-				 */
-				flag |= 2;
-
-				/*
-				 * even though we've gotten an ack, we're still
-				 * retransmitting as long as we're sending from
-				 * the retransmit queue.  Keeping retransmits non-zero
-				 * prevents us from getting new data interspersed with
-				 * retransmissions.
-				 */
-
-				if (sk->send_head->link3)	/* Any more queued retransmits? */
-					sk->retransmits = 1;
-				else
-					sk->retransmits = 0;
-			}
-  			/*
-			 * Note that we only reset backoff and rto in the
-			 * rtt recomputation code.  And that doesn't happen
-			 * if there were retransmissions in effect.  So the
-			 * first new packet after the retransmissions is
-			 * sent with the backoff still in effect.  Not until
-			 * we get an ack from a non-retransmitted packet do
-			 * we reset the backoff and rto.  This allows us to deal
-			 * with a situation where the network delay has increased
-			 * suddenly.  I.e. Karn's algorithm. (SIGCOMM '87, p5.)
-			 */
+		if (after(skb->end_seq, ack))
+			break;
 
+		if (sk->retransmits) 
+		{	
 			/*
-			 *	We have one less packet out there. 
+			 *	We were retransmitting.  don't count this in RTT est 
 			 */
-			 
-			if (sk->packets_out > 0) 
-				sk->packets_out --;
+			flag |= 2;
+		}
 
-			oskb = sk->send_head;
+		if ((sk->send_head = skb->link3) == NULL)
+		{
+			sk->send_tail = NULL;
+			sk->retransmits = 0;
+		}
+		/*
+		 * Note that we only reset backoff and rto in the
+		 * rtt recomputation code.  And that doesn't happen
+		 * if there were retransmissions in effect.  So the
+		 * first new packet after the retransmissions is
+		 * sent with the backoff still in effect.  Not until
+		 * we get an ack from a non-retransmitted packet do
+		 * we reset the backoff and rto.  This allows us to deal
+		 * with a situation where the network delay has increased
+		 * suddenly.  I.e. Karn's algorithm. (SIGCOMM '87, p5.)
+		 */
+
+		/*
+		 *	We have one less packet out there. 
+		 */
+			 
+		if (sk->packets_out > 0) 
+			sk->packets_out --;
 
-			if (!(flag&2)) 	/* Not retransmitting */
-				tcp_rtt_estimator(sk,oskb);
-			flag |= (2|4);	/* 2 is really more like 'don't adjust the rtt 
-					   In this case as we just set it up */
-			cli();
-			oskb = sk->send_head;
-			IS_SKB(oskb);
-			sk->send_head = oskb->link3;
-			if (sk->send_head == NULL) 
-			{
-				sk->send_tail = NULL;
-			}
+		if (!(flag&2)) 	/* Not retransmitting */
+			tcp_rtt_estimator(sk,skb);
+		flag |= (2|4);	/* 2 is really more like 'don't adjust the rtt 
+				   In this case as we just set it up */
+		IS_SKB(skb);
 
 		/*
 		 *	We may need to remove this from the dev send list. 
 		 */
-
-			if (oskb->next)
-				skb_unlink(oskb);
-			sti();
-			kfree_skb(oskb, FREE_WRITE); /* write. */
-			if (!sk->dead)
-				sk->write_space(sk);
-		}
-		else
-		{
-			break;
-		}
+		cli();
+		if (skb->next)
+			skb_unlink(skb);
+		sti();
+		kfree_skb(skb, FREE_WRITE); /* write. */
+		if (!sk->dead)
+			sk->write_space(sk);
 	}
 
 	/*
diff -u --recursive --new-file v1.3.84/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.84/linux/net/ipx/af_ipx.c	Tue Apr  2 13:32:24 1996
+++ linux/net/ipx/af_ipx.c	Mon Apr  8 11:41:04 1996
@@ -716,7 +716,7 @@
 	 *	We firewall first, ask questions later.
 	 */
 	 
-	if (call_in_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+	if (call_in_firewall(PF_IPX, skb->dev, ipx)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -760,7 +760,7 @@
 		/*
 		 *	See if we are allowed to firewall forward
 		 */
-		if (call_fw_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+		if (call_fw_firewall(PF_IPX, skb->dev, ipx)!=FW_ACCEPT)
 		{
 			kfree_skb(skb, FREE_READ);
 			return 0;
@@ -1318,7 +1318,7 @@
 		ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(ipx_packet));
 
 #ifdef CONFIG_FIREWALL	
-	if(call_out_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
+	if(call_out_firewall(PF_IPX, skb->dev, ipx)!=FW_ACCEPT)
 	{
 		kfree_skb(skb, FREE_WRITE);
 		return -EPERM;
diff -u --recursive --new-file v1.3.84/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v1.3.84/linux/net/netrom/nr_route.c	Wed Feb 21 08:33:27 1996
+++ linux/net/netrom/nr_route.c	Mon Apr  8 11:41:04 1996
@@ -656,9 +656,9 @@
 	
 #ifdef CONFIG_FIREWALL
 
-	if(ax25 && call_in_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+	if(ax25 && call_in_firewall(PF_NETROM, skb->dev, skb->data)!=FW_ACCEPT)
 		return 0;
-	if(!ax25 && call_out_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+	if(!ax25 && call_out_firewall(PF_NETROM, skb->dev, skb->data)!=FW_ACCEPT)
 		return 0;
 #endif
 	nr_src  = (ax25_address *)(skb->data + 0);
@@ -695,7 +695,7 @@
 		return 0;
 
 #ifdef CONFIG_FIREWALL
-	if(ax25 && call_fw_firewall(PF_NETROM, skb, skb->data)!=FW_ACCEPT)
+	if(ax25 && call_fw_firewall(PF_NETROM, skb->dev, skb->data)!=FW_ACCEPT)
 		return 0;
 #endif
 
diff -u --recursive --new-file v1.3.84/linux/net/netsyms.c linux/net/netsyms.c
--- v1.3.84/linux/net/netsyms.c	Fri Apr  5 13:35:28 1996
+++ linux/net/netsyms.c	Fri Apr  5 13:32:21 1996
@@ -168,6 +168,7 @@
 	X(n_tty_ioctl),
 	X(tty_register_ldisc),
 	X(kill_fasync),
+	X(arp_query),
 #ifdef CONFIG_FIREWALL
 	X(call_in_firewall),
 	X(call_out_firewall),
diff -u --recursive --new-file v1.3.84/linux/scripts/Makefile linux/scripts/Makefile
--- v1.3.84/linux/scripts/Makefile	Sat Mar  9 13:05:20 1996
+++ linux/scripts/Makefile	Sun Apr  7 08:54:04 1996
@@ -2,7 +2,13 @@
 HEADER=header.tk
 TAIL=tail.tk
 
-kconfig.tk: ../arch/${ARCH}/config.in tkparse ${HEADER} ${TAIL}
+SOUNDSCRIPT=${TOPDIR}/drivers/sound/Config.in
+
+${SOUNDSCRIPT}: ${TOPDIR}/drivers/sound/configure.c
+	make -C ${TOPDIR}/drivers/sound mkscript
+
+kconfig.tk: ${SOUNDSCRIPT} ${TOPDIR}/Makefile ../arch/${ARCH}/config.in \
+		tkparse ${HEADER} ${TAIL}
 	./tkparse < ../arch/${ARCH}/config.in  > kconfig.tmp
 	@if [ -f /usr/local/bin/wish ];	then \
 		echo '#!'"/usr/local/bin/wish -f" > kconfig.tk; \
diff -u --recursive --new-file v1.3.84/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.84/linux/scripts/header.tk	Thu Mar 28 17:34:35 1996
+++ linux/scripts/header.tk	Sun Apr  7 08:54:04 1996
@@ -1,19 +1,34 @@
 #
-# Set to be the x and y position of subwindows.
+# Create a "reference" object to steal colors from.
 #
-set winx 100
-set winy 200
+button .ref
 
 #
-# Create a "reference" object to steal colors from.
+# This is a handy replacement for ".widget cget" that requires neither tk4
+# nor additional source code uglification.
 #
-button .ref
+proc cget { w option } {
+	return [lindex [$w configure $option] 4]
+}
+
+#
+# Function to compensate for broken config.in scripts like the sound driver,
+# which make dependencies on variables that are never even conditionally
+# defined.
+#
+proc vfix { var } {
+	global $var
+	if [catch {set $var [subst $$var]}] {
+		puts stdout "WARNING - broken Config.in!  $var was not declared!"
+		set $var 0
+	}
+}
 
 #
 # Define some macros we will need to parse the config.in file.
 #
 proc mainmenu_name { text } {
-	message .header.message -width 400 -relief raised -bg grey -text "$text"
+	message .header.message -width 400 -relief raised -text "$text"
 	pack .header.label .header.message -side left -padx 15
         wm title . "$text"
 }
@@ -27,6 +42,9 @@
 # Not used at the moment, but this runs a command in a subprocess and
 # displays the result in a window with a scrollbar.
 #
+# For now, we just do external "make" commands to stdout with do_make, so
+# this function is never called.
+#
 proc do_cmd { w command } {
 	catch {destroy $w}
 	toplevel $w -class Dialog
@@ -38,9 +56,9 @@
 
 	set oldFocus [focus]
 	frame $w.back
-	button $w.back.ok -text "OK" -activebackground green -width 20 \
+	button $w.back.ok -text "OK" -width 20 \
 		-command "destroy $w; focus $oldFocus" -state disabled
-	button $w.back.ccl -text "Cancel" -activebackground green -width 20 \
+	button $w.back.ccl -text "Cancel" -width 20 \
 		-command "destroy $w; focus $oldFocus"
 	pack $w.tb -side top
 	pack $w.back.ok $w.back.ccl -side left
@@ -74,9 +92,9 @@
 
 	set oldFocus [focus]
 	frame $w.f
-	button $w.f.back -text "OK" -activebackground green -width 20 \
+	button $w.f.back -text "OK" -width 20 \
 		-command "destroy $w; focus $oldFocus;$func .fileio"
-	button $w.f.canc -text "Cancel" -activebackground red \
+	button $w.f.canc -text "Cancel" \
 		-width 20 -command "destroy $w; focus $oldFocus"
 	pack $w.f.back $w.f.canc -side left -pady 10 -padx 45
 	pack $w.f -pady 10 -side bottom -padx 10 -anchor w
@@ -98,9 +116,9 @@
 
 	set oldFocus [focus]
 	frame $w.f
-	button $w.f.back -text "OK" -activebackground green -width 20 \
+	button $w.f.back -text "OK" -width 20 \
 		-command "exit"
-	button $w.f.canc -text "Cancel" -activebackground red \
+	button $w.f.canc -text "Cancel" \
 		-width 20 -command "destroy $w; focus $oldFocus"
 	pack $w.f.back $w.f.canc -side left -pady 10 -padx 45
 	pack $w.f -pady 10 -side bottom -padx 10 -anchor w
@@ -126,7 +144,7 @@
 
 		set oldFocus [focus]
 		frame $w.f
-		button $w.f.back -text "Bummer" -activebackground green \
+		button $w.f.back -text "Bummer" \
 			-width 10 -command "destroy $w; focus $oldFocus"
 		pack $w.f.back -side bottom -pady 10 -anchor s
 		pack $w.f -pady 10 -side top -padx 10 -anchor s
@@ -154,7 +172,7 @@
 
 		set oldFocus [focus]
 		frame $w.f
-		button $w.f.back -text "OK" -activebackground green \
+		button $w.f.back -text "OK" \
 			-width 10 -command "destroy $w; focus $oldFocus"
 		pack $w.f.back -side bottom -pady 10 -anchor s
 		pack $w.f -pady 10 -side top -padx 10 -anchor s
@@ -234,10 +252,12 @@
 	}
 }
 
-proc option_name {w mnum line text variable} {
-	button $w.x$line.help -text "Help" -relief raised \
-		-command "dohelp .dohelp $variable"
+proc option_name {w mnum line text helpidx} {
 	button $w.x$line.l -text "$text" -relief groove -anchor w
+	$w.x$line.l configure -activefore [cget $w.x$line.l -fg] \
+				-activeback [cget $w.x$line.l -bg]
+	button $w.x$line.help -text "Help" -relief raised \
+		-command "dohelp .dohelp $helpidx"
 	pack $w.x$line.help -side right -fill y
 	pack $w.x$line.l -side right -fill both -expand on
 }
@@ -246,10 +266,10 @@
 	frame $w.x$line -relief sunken
 	radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \
 		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
-		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
 	radiobutton $w.x$line.m -text "m"  -variable $variable -value 2 \
 		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
+	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
+		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
 		
 	option_name $w $mnum $line $text $variable
 		
@@ -273,9 +293,9 @@
 
 proc int { w mnum line text variable } {
 	frame $w.x$line
-	option_name $w $mnum $line $text $variable
 	entry $w.x$line.x -width 18 -relief sunken -borderwidth 2 \
 		-textvariable $variable
+	option_name $w $mnum $line $text $variable
 	pack $w.x$line.x -anchor w -side right -fill y
 	pack $w.x$line -anchor w -fill both -expand on
 }
@@ -284,12 +304,12 @@
 	int $w $mnum $line $text $variable
 }
 
-proc minimenu { w mnum line text variable } {
+proc minimenu { w mnum line text variable helpidx } {
 	frame $w.x$line
-	option_name $w $mnum $line $text $variable
 	menubutton $w.x$line.x -textvariable $variable -menu \
 		$w.x$line.x.menu -relief raised \
 		-width 15 -anchor w
+	option_name $w $mnum $line $text $helpidx
 	pack $w.x$line.x -anchor w -side right -fill y
 	pack $w.x$line -anchor w -fill both -expand on
 }
@@ -299,7 +319,8 @@
 }
 
 proc do_make { command } {
-	exec sh -c $command
+	exec sh -c $command <@stdin >@stdout 2>@stderr
+#	do_cmd .make_window "sh -c $command"
 }
 
 proc dohelp {w var }  {
@@ -356,7 +377,7 @@
 	# Do the OK button
 	#
 	frame $w.f2
-	button $w.f2.ok -text "OK" -activebackground green \
+	button $w.f2.ok -text "OK" \
 		-width 10 -command "destroy $w; focus $oldFocus"
 	pack $w.f2.ok -side bottom -pady 10 -anchor s
 	pack $w.f2 -side bottom -padx 10 -anchor s
@@ -380,7 +401,7 @@
 
 	set oldFocus [focus]
 	frame $w.f
-	button $w.f.back -text "OK" -activebackground green \
+	button $w.f.back -text "OK" \
 		-width 10 -command "exit"
 	pack $w.f.back -side bottom -pady 10 -anchor s
 	pack $w.f -pady 10 -side top -padx 10 -anchor s
diff -u --recursive --new-file v1.3.84/linux/scripts/tail.tk linux/scripts/tail.tk
--- v1.3.84/linux/scripts/tail.tk	Wed Feb  7 08:56:11 1996
+++ linux/scripts/tail.tk	Sun Apr  7 08:54:04 1996
@@ -33,12 +33,10 @@
 update_mainmenu .f0
 
 button .f0_bot.r.save -text "Save and Exit" -width 25 -command {
-	writeconfig .config include/linux/autoconf.h; wrapup .wrap } \
-	-activebackground green
+	writeconfig .config include/linux/autoconf.h; wrapup .wrap }
 
 button .f0_bot.r.quit -text "Quit Without Saving" -width 25 \
-	-command { maybe_exit .maybe } \
-	-activebackground red -activeforeground white
+	-command { maybe_exit .maybe }
 
 button .f0_bot.l.store -text "Store Configuration to File" -width 25 -command {
 	load_configfile .load "Save Configuration in file" write_config_file
diff -u --recursive --new-file v1.3.84/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.84/linux/scripts/tkgen.c	Sun Mar 17 09:58:21 1996
+++ linux/scripts/tkgen.c	Sun Apr  7 08:54:04 1996
@@ -27,14 +27,31 @@
  * 1996 03 16
  * Avery Pennarun - basic "do_make" support added to let sound config work.
  *
+ * 1996 03 25
+ *     Axel Boldt - Help now works on "choice" buttons.
+ *
+ * 1996 04 06
+ * Avery Pennarun - Improved sound config stuff. (I think it actually works
+ *                  now!)
+ *                - Window-resize-limits don't use ugly /usr/lib/tk4.0 hack.
+ *                - int/hex work with tk3 again. (The "cget" error.)
+ *                - Next/Prev buttons switch between menus.  I can't take
+ *                  much credit for this; the code was already there, but
+ *                  ifdef'd out for some reason.  It flickers a lot, but
+ *                  I suspect there's no "easy" fix for that.
+ *                - Labels no longer highlight as you move the mouse over
+ *                  them (although you can still press them... oh well.)
+ *                - Got rid of the last of the literal color settings, to
+ *                  help out people with mono X-Windows systems. 
+ *                  (Apparently there still are some out there!)
+ *                - Tabstops seem sensible now.
+ *
  * TO DO:
  *   - clean up - there are useless ifdef's everywhere.
- *   - do more sensible things with the 'config -resizable" business.
  *   - better comments throughout - C code generating tcl is really cryptic.
- *   - eliminate silly "update idletasks" hack to improve display speed.
- *   - make tabstops work left->right instead of right->left.
+ *   - eliminate silly "update idletasks" hack to improve display speed and
+ *     reduce flicker.  But how?
  *   - make canvas contents resize with the window (good luck).
- *   - make next/prev buttons go to next/previous menu.
  *   - some way to make submenus inside of submenus (ie. Main->Networking->IP)
  *           (perhaps a button where the description would be)
  *   - make the main menu use the same tcl code as the submenus.
@@ -44,10 +61,8 @@
  *   - choice buttons should default to the first menu option, rather than a
  *           blank.  Also look up the right variable when the help button
  *           is pressed.
- *   - remove the remaining bits of the now-unnecessary "next/prev" submenu
- *           code.
  *   - clean up +/- 16 confusion for enabling/disabling variables; causes
- *           problems with dependencies.
+ *           (theoretical, at the moment) problems with dependencies.
  *   
  */
 #include <stdio.h>
@@ -63,13 +78,6 @@
 #endif
 
 /*
- * This prevents the Prev/Next buttons from going through the entire sequence
- * of submenus.  I need to fix the window titles before it would really be
- * appropriate to enable this.
- */
-#define PREVLAST_LIMITED_RANGE
-
-/*
  * This is the total number of submenus that we have.
  */
 static int tot_menu_num =0;
@@ -90,18 +98,45 @@
   printf("\tpack $w.m -pady 10 -side top -padx 10\n");
   printf("\twm title $w \"%s\" \n\n", label);
   
+  /*
+   * Attach the "Prev", "Next" and "OK" buttons at the end of the window.
+   */
+  printf("\tset oldFocus [focus]\n");
+  printf("\tframe $w.f\n");
+  printf("\tbutton $w.f.back -text \"Main Menu\" \\\n"
+         "\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
+  printf("\tbutton $w.f.next -text \"Next\" \\\n"
+         "\t\t-width 15 -command \" destroy $w; focus $oldFocus;  menu%d .menu%d \\\"$title\\\"\"\n",
+         	menu_num+1, menu_num+1);
+  if (menu_num == tot_menu_num)
+  	printf("\t$w.f.next configure -state disabled\n");
+  printf("\tbutton $w.f.prev -text \"Prev\" \\\n"
+         "\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n",
+       		menu_num-1, menu_num-1);
+  if (1 == menu_num)
+  	printf("\t$w.f.prev configure -state disabled\n");
+  printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n");
+  printf("\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n");
+
+  /*
+   * Lines between canvas and other areas of the window.
+   */
   printf("\tframe $w.topline -relief ridge -borderwidth 2 -height 2\n");
   printf("\tpack $w.topline -side top -fill x\n\n");
+  printf("\tframe $w.botline -relief ridge -borderwidth 2 -height 2\n");
+  printf("\tpack $w.botline -side bottom -fill x\n\n");
   
+  /*
+   * The "config" frame contains the canvas and a scrollbar.
+   */
   printf("\tframe $w.config\n");
   printf("\tpack $w.config -fill y -expand on\n\n");
-  
   printf("\tscrollbar $w.config.vscroll -command \"$w.config.canvas yview\"\n");
   printf("\tpack $w.config.vscroll -side right -fill y\n\n");
   
-  printf("\tframe $w.botline -relief ridge -borderwidth 2 -height 2\n");
-  printf("\tpack $w.botline -side top -fill x\n\n");
-  
+  /*
+   * The scrollable canvas itself, where the real work (and mess) gets done.
+   */
   printf("\tcanvas $w.config.canvas -height 1\\\n"
   	 "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n"
   	 "\t\t-width [expr [winfo screenwidth .] * 1 / 2] \n");
@@ -126,6 +161,16 @@
 }
 
 /*
+ * Output a "global" line for a given variable.  Also include the
+ * call to "vfix".  (If vfix is not needed, then it's fine to just printf
+ * a "global" line).
+ */
+void inline global(char *var)
+{
+  printf("\tglobal %s; vfix %s\n", var, var);
+}
+
+/*
  * This function walks the chain of conditions that we got from cond.c,
  * and creates a wish conditional to enable/disable a given widget.
  */
@@ -145,12 +190,12 @@
     {
       switch(cond->op){
       case op_variable:
-	printf("\tglobal %s\n", cond->variable.str);
+      	global(cond->variable.str);
 	break;
       case op_kvariable:
 	if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break;
 	cond->variable.cfg->flags |= GLOBAL_WRITTEN;
-	printf("\tglobal %s\n", cond->variable.cfg->optionname);
+	global(cond->variable.cfg->optionname);
 	break;
       default:
 	break;
@@ -164,7 +209,7 @@
   if(   (item->flags & GLOBAL_WRITTEN) == 0
      && (item->optionname != NULL) )
     {
-      printf("\tglobal %s\n", item->optionname);
+      global(item->optionname);
       item->flags |= GLOBAL_WRITTEN;
     }
   /*
@@ -240,10 +285,10 @@
     case tok_int:
     case tok_hex:
       printf("} then { ");
-      printf(".menu%d.config.f.x%d.x configure -state normal -fore [ .ref cget -foreground ]; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.x configure -state normal -fore [ cget .ref -foreground ]; ", menu_num, line_num);
       printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
       printf("} else { ");
-      printf(".menu%d.config.f.x%d.x configure -state disabled -fore [ .ref cget -disabledforeground ];", menu_num, line_num );
+      printf(".menu%d.config.f.x%d.x configure -state disabled -fore [ cget .ref -disabledforeground ];", menu_num, line_num );
       printf(".menu%d.config.f.x%d.l configure -state disabled;", menu_num, line_num );
       printf("}\n");
       break;
@@ -277,7 +322,7 @@
       printf("} then { ");
       if( item->tok == tok_dep_tristate )
 	{
-	  printf("global %s;", item->depend.str);
+	  global(item->depend.str);
 	  printf("if { $%s != 1 && $%s != 0 } then {", 
 	  	item->depend.str,item->depend.str);
 	  printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
@@ -338,12 +383,12 @@
     {
       switch(cond->op){
       case op_variable:
-	printf("\tglobal %s\n", cond->variable.str);
+        global(cond->variable.str);
 	break;
       case op_kvariable:
 	if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break;
 	cond->variable.cfg->flags |= GLOBAL_WRITTEN;
-	printf("\tglobal %s\n", cond->variable.cfg->optionname);
+	global(cond->variable.cfg->optionname);
 	break;
       default:
 	break;
@@ -452,48 +497,16 @@
 /*
  * Generates a fragment of wish script that closes out a submenu procedure.
  */
-static void end_proc(int menu_num, int first, int last)
+static void end_proc(int menu_num)
 {
   struct kconfig * cfg;
 
   printf("\n\n\n");
-  printf("\tset oldFocus [focus]\n");
-  printf("\tframe $w.f\n");
-
-  /*
-   * Attach the "Prev", "Next" and "OK" buttons at the end of the window.
-   */
-  printf("\tbutton $w.f.prev -text \"Prev\" -activebackground green \\\n");
-      printf("\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n", menu_num-1, menu_num-1);
-#ifdef PREVLAST_LIMITED_RANGE
-  if(first == menu_num ) printf("\t$w.f.prev configure -state disabled\n");
-#else
-  if( 1 == menu_num ) printf("\t$w.f.prev configure -state disabled\n");
-#endif
-
-  printf("\tbutton $w.f.next -text \"Next\" -activebackground green \\\n");
-  printf("\t\t-width 15 -command \" destroy $w; focus $oldFocus;  menu%d .menu%d \\\"$title\\\"\"\n", menu_num+1, menu_num+1);
-#ifdef PREVLAST_LIMITED_RANGE
-  if(last == menu_num ) printf("\t$w.f.next configure -state disabled\n");
-#else
-  if(last == tot_menu_num ) printf("\t$w.f.next configure -state disabled\n");
-#endif
-
-  printf("\tbutton $w.f.back -text \"Main Menu\" -activebackground green \\\n");
-  printf("\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
-
-  printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n");
-  printf("\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n");
   printf("\tfocus $w\n");
   printf("\tupdate_menu%d $w.config.f\n", menu_num);
   printf("\tglobal winx; global winy\n");
   printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n");
   printf("\twm geometry $w +$winx+$winy\n");
-  /*
-   *	We have a cunning plan....
-   */
-  if(access("/usr/lib/tk4.0",0)==0)
-	  printf("\twm resizable $w no yes\n\n");
   
   /*
    * Now that the whole window is in place, we need to wait for an "update"
@@ -526,6 +539,14 @@
   	 "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n"
   	 "\t}\n");
   
+  /*
+   * Limit the min/max window size.  Height can vary, but not width,
+   * because of the limitations of canvas and our laziness.
+   */
+  printf("\tupdate idletasks\n");
+  printf("\twm maxsize $w [winfo width $w] [winfo screenheight $w]\n");
+  printf("\twm minsize $w [winfo width $w] 100\n\n");
+  
   printf("}\n\n\n");
 
   /*
@@ -577,7 +598,7 @@
 	   */
 	  if(cfg->tok == tok_dep_tristate)
 	    {
-	      printf("\tglobal %s;", cfg->depend.str);
+	      global(cfg->depend.str);
 	      printf("\tif {$%s != 1 && $%s != 0 } then { .menu%d.config.f.x%d.y configure -state disabled } else { .menu%d.config.f.x%d.y configure -state normal}\n",
 		     cfg->depend.str,cfg->depend.str,
 		     menu_num, cfg->menu_line,
@@ -724,7 +745,7 @@
 	   */
 	  if( cfg->menu_number > 1 )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	    }
 	  menulabel = cfg->label;
 	  start_proc(cfg->label, cfg->menu_number, TRUE);
@@ -740,7 +761,7 @@
 	   */
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
@@ -762,23 +783,30 @@
 	case tok_choose:
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
 	  printf("\tglobal %s\n",cfg->optionname);
-	  printf("\tminimenu $w.config.f %d %d \"%s\" %s\n",
+	  printf("\tminimenu $w.config.f %d %d \"%s\" %s %s\n",
 	  	cfg->menu_number,
 	  	cfg->menu_line,
 	  	cfg->label,
-	  	cfg->optionname);
+	  	cfg->optionname,
+	  	/*
+	  	 * We rely on the fact that the first tok_choice corresponding
+	  	 * to the current tok_choose is cfg->next (compare parse() in
+	  	 * tkparse.c).  We need its name to pick out the right help
+	  	 * text from Configure.help.
+	  	 */
+	  	cfg->next->optionname);
 	  printf("\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line);
 	  cfg1 = cfg;
 	  break;
 	case tok_tristate:
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
@@ -791,7 +819,7 @@
 	case tok_dep_tristate:
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
@@ -805,7 +833,7 @@
 	case tok_int:
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
@@ -818,7 +846,7 @@
 	case tok_hex:
 	  if( cfg->menu_number != menu_num )
 	    {
-	      end_proc(menu_num, menu_min, menu_max);
+	      end_proc(menu_num);
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
@@ -837,7 +865,7 @@
   /*
    * Generate the code to close out the last menu.
    */
-  end_proc(menu_num, menu_min, menu_max);
+  end_proc(menu_num);
 
 #ifdef ERIC_DONT_DEF
   /*
@@ -855,7 +883,7 @@
   /*
    * Close out the last menu.
    */
-  end_proc(menu_num, menu_num, menu_num);
+  end_proc(menu_num);
 #endif
 
   /*
@@ -1046,7 +1074,7 @@
 	  cfg1 != NULL && cfg1->tok == tok_choice;
 	  cfg1 = cfg1->next)
 	{
-	  printf("\tglobal %s; set %s 0\n",  cfg1->optionname, cfg1->optionname);
+	  printf("\tglobal %s; set %s 0\n",cfg1->optionname,cfg1->optionname);
 	}
     }
   printf("}\n\n\n");
