diff -u --recursive --new-file v1.3.96/linux/CREDITS linux/CREDITS
--- v1.3.96/linux/CREDITS	Sat Apr 27 15:19:43 1996
+++ linux/CREDITS	Mon Apr 29 07:21:20 1996
@@ -786,8 +786,8 @@
 N: Dirk Melchers
 E: dirk@merlin.nbg.sub.org
 D: 8 bit XT hard disk driver for OMTI5520
-S: Branderweg 4
-S: D-91058 Erlangen
+S: Heidackerstrass 19
+S: D-91056 Erlangen
 S: Germany
 
 N: Michael Meskes
@@ -1306,7 +1306,7 @@
 S: The Netherlands
 
 N: Lars Wirzenius
-E: lars.wirzenius@helsinki.fi
+E: liw@iki.fi
 D: Linux System Administrator's Guide
 D: Co-moderator, comp.os.linux.announce
 D: Original sprintf in kernel
@@ -1316,8 +1316,8 @@
 D: Meta-FAQ, originator
 D: INFO-SHEET, former maintainer
 D: Author of the longest-living linux bug
-S: Ohratie 16 C 198
-S: sf-01370 Vantaa
+S: Hernesaarenkatu 15 A 2
+S: Fin-00150 Helsinki
 S: Finland
 
 N: Roger E. Wolff
diff -u --recursive --new-file v1.3.96/linux/Documentation/Changes linux/Documentation/Changes
--- v1.3.96/linux/Documentation/Changes	Sat Apr 27 15:19:43 1996
+++ linux/Documentation/Changes	Mon Apr 29 14:43:54 1996
@@ -2,13 +2,9 @@
 important packages for Linux as well as instructions for newcomers to
 the 1.3.x series of kernels.
 
-Last updated: Apr 27, 1996.
+Last updated: Apr 29, 1996.
 Author: Chris Ricker (gt1355b@prism.gatech.edu), based on the original by
-Alessandro Sigala (ssigala@globalnet.it.
-
-Note:  Due to time constraints, Alessandro is getting out of the Changes
-business and I'll be picking up the job.  Be gentle while I get my feet
-wet ;-).
+Alessandro Sigala (ssigala@globalnet.it).
 
 Current Releases
 ****************
@@ -18,7 +14,7 @@
 - Dynamic linker (ld.so) 1.7.14
 - GNU CC		 2.7.2
 - Binutils		 2.6.0.12
-- Linux C Library	 Stable:  5.2.18, Exp:  5.3.9, Alpha:  5.3.12
+- Linux C Library	 Stable:  5.2.18, Beta:  5.3.12
 - Linux C++ Library	 2.7.1.4
 - Termcap		 2.0.8
 - Procps		 0.99a
@@ -75,9 +71,9 @@
 file, since GNU make and a few other fairly important utils can be
 broken by the upgrade.
 
-   The current (beta) Linux C Library release is 5.3.9. In this release
-there are some important changes that may cause troubles to buggy
-programs (programs that call free() on a pointer not returned by
+   The current (beta) Linux C Library release is 5.3.12. In this
+release there are some important changes that may cause troubles to
+buggy programs (programs that call free() on a pointer not returned by
 malloc() work with previous libc, but not with this release) then read
 the `release.libc-5.3.9' file carefully!  In the latest libc releases a
 dirent bug, which erroneously defined d->reclen to d->namlen if USE_GNU
@@ -88,8 +84,8 @@
 is at the end of this file).
 
    Also, the libc-5.3.x line has a known security hole relating to
-rlogin.  Unless you really need to upgrade for some reason, just stick
-with 5.2.18 for now.
+rlogin.  Libc-5.3.12 fixes this, so if you're going to run an
+experimental libc, be sure to upgrade to 5.3.12.
 
    If you're getting an error message that is something to the effect of
 
@@ -199,8 +195,18 @@
 
    The new named pipe behavior also causes problems with Hylafax.  If
 you're running the hylafax daemon, it will just keep eating up CPU time
-until you have no idle time free.  I'm not currently aware of any
-patches that eliminate this behavior.
+until you have no idle time free.  To fix this, edit port.h included
+with the Hylafax distribution and change the line
+
+   CONFIG_OPENFIFO="O_RDONLY"
+
+   to
+
+   CONFIG_OPENFIFO="O_RDWR"
+
+   A similar method (finding all named pipes opened read-only and
+changing them to read-write) will fix any program that broke because of
+this change.
 
 Uugetty
 =======
@@ -252,8 +258,8 @@
 =====
 
    Older versions of xntpd will not work with the latest kernels.
-Upgrade to xntp3.5c.tar.Z, available from
-http://www.eecis.udel.edu/~ntp/.
+Upgrade to xntp3.5f.tar.Z, available from
+ftp://louie.udel.edu/pub/ntp/xntp3.5f.tar.Z.
 
 Sound driver
 ============
@@ -315,7 +321,8 @@
    Quota support has also been added.  You need to get quotas-1.51 from
 ftp://ftp.funet.fi/pub/Linux/PEOPLE/Linus/subsystems/quota/all.tar.gz.
 This will compile just fine after you copy its mntent.h over to
-/usr/include/mntent.h.
+/usr/include/mntent.h.  I've uploaded this to sunsite as
+ftp://sunsite.unc.edu/pub/Linux/system/Admin/quotas-1.51-tar.gz
 
 APM support
 ===========
@@ -376,20 +383,20 @@
 Linux C Library
 ===============
 
-The latest 5.3.9 release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.3.9.bin.tar.gz
-Installation notes:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.3.9
+The stable 5.2.18 release:
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.2.18.bin.tar.gz
+Installation notes for 5.2.18:
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.2.18
+
+The latest 5.3.12 release:
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.3.12.bin.tar.gz
+Installation notes for 5.2.18:
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.3.12
 
 Patched make sources:
 ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74.patched.tar.gz
 Patched make binary:
 ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74-direntfix-elf.tgz
-
-The stable 5.2.18 release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.2.18.bin.tar.gz
-Installation notes:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.2.18
 
 Linux C++ Library
 =================
diff -u --recursive --new-file v1.3.96/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.96/linux/Documentation/Configure.help	Sat Apr 27 15:19:44 1996
+++ linux/Documentation/Configure.help	Mon Apr 29 10:58:40 1996
@@ -2831,20 +2831,6 @@
   via a removable medium like magneto-optical or ZIP drives. If you
   don't know what all this is about, say N.
 
-AFFS filesystem support
-CONFIG_AFFS_FS
-  AFFS is the filesystem used on Commodore Amiga computers. People
-  running Linux on other systems can say N here.
-
-UFS filesystem support (read only)
-CONFIG_UFS_FS
-  UFS (Unix File System) is the way files are organized on Sun Solaris
-  harddisks and floppies. If you want to be able to read those, say Y
-  here. Note that this option is generally not needed for floppies,
-  since a good portable way to transport files and directories between
-  unixes (and even other operating systems) is given by the tar
-  program ("man tar").
-
 SMB filesystem support (to mount WfW shares etc..)
 CONFIG_SMB_FS
   SMB (Server Message Buffer) is the protocol Windows for Workgroups
diff -u --recursive --new-file v1.3.96/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v1.3.96/linux/Documentation/cdrom/aztcd	Fri Apr 12 15:51:43 1996
+++ linux/Documentation/cdrom/aztcd	Sun Apr 28 12:09:28 1996
@@ -1,16 +1,19 @@
-$Id: README.aztcd,v 2.20 1996/03/12 18:31:33 root Exp root $
+$Id: README.aztcd,v 2.30 1996/04/26 05:32:23 root Exp root $
           Readme-File /usr/src/Documentation/cdrom/aztcd
-           for Aztech CD-ROM CDA268-01A, ORCHID CD-3110,
-                  OKANO/WEARNES CDD110, Conrad TXC
-                           CD-ROM Driver 
-                       Version 2.2 and newer
+           			for 
+	     AZTECH CD-ROM CDA268-01A, ORCHID CD-3110,
+      OKANO/WEARNES CDD110, CONRAD TXC, CyCDROM CR520, CR540
+                           CD-ROM Drives 
+                       Version 2.3 and newer
                    (for other drives see 6.-8.)
 
 NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
       A PROPRIETARY INTERFACE (implemented on a sound card or on an
       ISA-AT-bus card). 
       IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
-      such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
+      such as the Aztech CDA269-031SE !!! (The only known exceptions are
+      'faked' IDE drives like the CyCDROM CR520ie which work with aztcd
+      under certain conditions, see 7.). IF YOU'RE USING A CD-ROM DRIVE
       WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE 
       USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !
       THE STANDARD-KERNEL 1.2.x NOW ALSO SUPPORTS IDE-CDROM-DRIVES, SEE THE
@@ -267,9 +270,7 @@
 AZTECH driver work with these drives. 
 
 Unfortunately I do not have any of these drives available, so I couldn't test
-it myself. But I've got reports, that it works with ORCHID CDS3110 and Game-
-Wave32 sound cards and also with WEARNES CDD110 and Conrad TXC in some different
-combinations. In some installations, it seems necessary to initialize the drive 
+it myself. In some installations, it seems necessary to initialize the drive 
 with the DOS driver before (especially if combined with a sound card) and then 
 do a warm boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
 
@@ -278,6 +279,12 @@
 Sorry for the inconvenience, but it is difficult to develop for hardware, 
 which you don't have available for testing. So if you like, please help us.
 
+If you do have a CyCDROM CR520ie thanks to Hilmar Berger's help your chances
+are good, that it will work with aztcd. The CR520ie is sold as an IDE-drive
+and really is connected to the IDE interface (primary at 0x1F0 or secondary
+at 0x170, configured as slave, not as master). Nevertheless it is not ATAPI
+compatible but still uses Aztech's command codes.
+
 
 8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
 -reread the complete README file
@@ -290,9 +297,9 @@
  drive. By appropriately configuring the drive and the DOS driver you can
  check, whether your drive does operate in this mode correctly under DOS. If
  it does not operate under DOS, it won't under Linux.
- If your drive's base address is something like 0x170 or 0x1F0 you most
- likely are having a IDE/ATAPI-compatible drive, which is not supported by
- aztcd.c, use ide-cd.c instead.
+ If your drive's base address is something like 0x170 or 0x1F0 (and it is
+ not a CyCDROM CR520ie or CR 940ie) you most likely are having an IDE/ATAPI-
+ compatible drive, which is not supported by aztcd.c, use ide-cd.c instead.
  Make sure the Base Address is configured correctly in aztcd.h, also make
  sure, that /dev/aztcd0 exists with the correct major number (compare it with
  the entry in file /usr/include/linux/major.h for the Aztech drive). 
@@ -462,6 +469,8 @@
 
 Jochen Kunz and Olaf Kaluza delivered the information for supporting Conrad's 
 TXC drive.
+
+Hilmar Berger delivered the patches for supporting CyCDROM CR520ie.
 
 Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
 directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
diff -u --recursive --new-file v1.3.96/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.96/linux/MAINTAINERS	Tue Apr 23 13:57:01 1996
+++ linux/MAINTAINERS	Mon Apr 29 07:23:09 1996
@@ -1,41 +1,47 @@
-		Maintainers And Source Submission Procedures
+	List of maintainers and how to submit kernel changes
 
-	In order to keep things easy for the maintainers please try to
-follow the guidelines given. Not all of these guidelines matter for every
+Please try to follow the guidelines below.  This will make things
+easier on the maintainers.  Not all of these guidelines matter for every
 trivial patch so apply some common sense.
 
-1.	Always _test_ your changes however small on at least 4 or 5 people,
-preferably many more.
+1.	Always _test_ your changes, however small, on at least 4 or
+	5 people, preferably many more.
 
-2.	Try and release a few ALPHA test versions to the net. Announce them
-onto the kernel channel and await results. This is especially important
-for device drivers because often thats the only way you will find things
-like the fact version 3 firmware needs a magic fix you didn't know about, or
-some clown changed the chips on a board and not its name (Don't laugh look
-at the SMC etherpower for that).
+2.	Try and release a few ALPHA test versions to the net. Announce
+	them onto the kernel channel and await results. This is especially
+	important for device drivers, because often thats the only way
+	you will find things like the fact version 3 firmware needs
+	a magic fix you didn't know about, or some clown changed the
+	chips on a board and not its name.  (Don't laugh!  Look at the
+	SMC etherpower for that).
 
-3.	Make sure your changes compile correctly in multiple configurations.
+3.	Make sure your changes compile correctly in multiple
+	configurations.
 
 4.	When you are happy with a change make it generally available for
-testing and await feedback. 
+	testing and await feedback.
 
 5.	Make a patch available to the relevant maintainer in the list. Use
-'diff -u' to make the patch easy to merge. Be prepared to get your changes
-sent back with seemingly silly requests about formatting and variable names.
-These aren't as silly as they seem, one job the maintainers (and especially
-Linus) do is to keep things looking the same. Sometimes this means that
-the clever hack in your driver to get around a problem actual needs to
-become a generalised kernel feature ready for next time.
+	'diff -u' to make the patch easy to merge. Be prepared to get your
+	changes sent back with seemingly silly requests about formatting
+	and variable names.  These aren't as silly as they seem. One
+	job the maintainers (and especially Linus) do is to keep things
+	looking the same. Sometimes this means that the clever hack in
+	your driver to get around a problem actual needs to become a
+	generalised kernel feature ready for next time.
+
 	PLEASE try and include any credit lines you want added with the
-patch. It avoids people being missed off by mistake and makes it easier to
-know who wants adding and who doesn't.
-	PLEASE Document known bugs. If it doesn't work for everything or
-does something very odd once a month document it.
+	patch. It avoids people being missed off by mistake and makes
+	it easier to know who wants adding and who doesn't.
+
+	PLEASE document known bugs. If it doesn't work for everything
+	or does something very odd once a month document it.
 
 6.	Make sure you have the right to send any changes you make. If you
-do changes at work you may find your employer owns the patch not you.
+	do changes at work you may find your employer owns the patch
+	not you.
 
-7.	Happy hacking
+7.	Happy hacking.
 
 
 [This file is new: I've just put the existing network contacts in, other
@@ -49,7 +55,8 @@
 M: Mail patches to
 L: Mailing list that is relevant to this area
 W: Web-page with status/info
-S: Status
+S: Status, one of the following:
+
 	Supported:	Someone is actually paid to look after this (wildly
 			improbable).
 	Maintained:	Someone actually looks after it.
@@ -57,9 +64,10 @@
 			much other than throw the odd patch in. See below..
 	Orphan:		No current maintainer [but maybe you could take the 
 			role as you write your new code].
-	Obsolete:	Ex code. Something tagged obsolete generally means
+	Obsolete:	Old code. Something tagged obsolete generally means
 			its been replaced by a better system and you should
 			be using that.
+
 3C501 NETWORK DRIVER
 P:	Alan Cox
 M:	net-patches@lxorguk.ukuu.org.uk
diff -u --recursive --new-file v1.3.96/linux/Makefile linux/Makefile
--- v1.3.96/linux/Makefile	Sat Apr 27 15:19:44 1996
+++ linux/Makefile	Sat Apr 27 20:44:30 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 96
+SUBLEVEL = 97
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.96/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v1.3.96/linux/arch/i386/kernel/signal.c	Sun Apr 21 19:21:59 1996
+++ linux/arch/i386/kernel/signal.c	Mon Apr 29 07:32:54 1996
@@ -151,6 +151,9 @@
 
 static struct _fpstate * save_i387(struct _fpstate * buf)
 {
+	if (!current->used_math)
+		return NULL;
+
 #ifndef CONFIG_MATH_EMULATION
 	return save_i387_hard(buf);
 #else
diff -u --recursive --new-file v1.3.96/linux/drivers/block/README.fd linux/drivers/block/README.fd
--- v1.3.96/linux/drivers/block/README.fd	Sun Apr 21 12:39:00 1996
+++ linux/drivers/block/README.fd	Sun Apr 28 18:47:40 1996
@@ -27,7 +27,7 @@
  If you give options both in the lilo config file and on the boot
 prompt, the option strings of both places are concatenated, the boot
 prompt options coming last. That's why there are also options to
-restore the default behaviour.
+restore the default behavior.
 
  If you use the floppy driver as a module, use the following syntax:
  insmod floppy floppy="<options>"
@@ -49,7 +49,7 @@
 	Obsolete. Use the floppy=<drive>,<type>,cmos option instead
 
  floppy=asus_pci
-	Sets the bitmask to allow only units 0 and 1. (The default)
+	Sets the bit mask to allow only units 0 and 1. (The default)
 
  floppy=daring
 	Tells the floppy driver that you have a well behaved floppy controller.
@@ -81,34 +81,71 @@
  floppy=nodma
 	Tells the floppy driver not to use Dma for data transfers.
 	This is needed on HP Omnibooks, which don't have a workable
-	DMA channel for the floppy driver.
-
+	DMA channel for the floppy driver. This option is also useful
+	if you frequently get "Unable to allocate DMA memory" messages.
+	Indeed, dma memory needs to be continuous in physical, and is
+	thus harder to find, whereas non-dma buffers may be allocated
+	in virtual memory. However, I advise against this if you have
+	an FDC without a FIFO (8272A or 82072). 82072A and later are
+	OK. You also need at least a 486 to use nodma.
+	If you use nodma mode, I suggest you also set the FIFO
+	threshold to 10 or lower, in order to limit the number of data
+	transfer interrupts.
+	
  floppy=dma
 	Tells the floppy driver that a workable DMA channel is available
 	(the default).
 
+floppy=nofifo
+	Disables the FIFO entirely. This is needed if you get "Bus
+	master arbitration error" messages from your ethernet card (or
+	from other devices) while accessing the foppy.
+
+floppy=fifo
+	Enables the FIFO (default)
+
+ floppy=<threshold>,fifo_depth
+	Sets the FIFO threshold. This is mostly relevant in DMA
+	mode. If this is higher, the floppy driver tolerates more
+	interrupt latency, but it triggers more interrupts (i.e. it
+	imposes more load on the rest of the system). If this is
+	lower, the interrupt latency should be lower too (faster
+	processor). The benefit of a lower threshold is less
+	interrupts.
+	To tune the fifo threshold, switch on over/underrun messages
+	using 'floppycontrol --messages'. Then access a floppy
+	disk. If you get a huge amount of "Over/Underrun - retrying"
+	messages, then the fifo threshold is too low. Try with a
+	higher value, until you only get an occasional Over/Underrun.
+	It is a good idea to compile the floppy driver as a module
+	when doing this tuning. Indeed, it allows to try different
+	fifo values whithout rebooting the machine for each test. Note
+	that you need to do 'floppycontrol --messages' every time you
+	re-inseert the module.
+	Usually, tuning the fifo threshold should not be needed, as
+	the default (0xa) is reasonable.
+
  floppy=<drive>,<type>,cmos
-	Sets the cmos type of <drive> to <type>. This is mandatory if
+	Sets the CMOS type of <drive> to <type>. This is mandatory if
 	you have more than two floppy drives (only two can be
-	described in the physical cmos), or if your BIOS uses
+	described in the physical CMOS), or if your BIOS uses
 	non-standard CMOS types. The CMOS types are:
-		0 - unknown or not installed
+		0 - Use the value of the physical CMOS
 		1 - 5 1/4 DD
 		2 - 5 1/4 HD
 		3 - 3 1/2 DD
 		4 - 3 1/2 HD
 		5 - 3 1/2 ED
 		6 - 3 1/2 ED
+	       16 - unknown or not installed
 	(Note: there are two valid types for ED drives. This is because 5 was
 	initially chosen to represent floppy *tapes*, and 6 for ED drives.
 	AMI ignored this, and used 5 for ED drives. That's why the floppy
 	driver handles both)
-	Setting the CMOS to 0 for the first two drives (default) makes the
-	floppy driver read the physical cmos for those drives.
 
  floppy=unexpected_interrupts
 	Print a warning message when an unexpected interrupt is received 
-	(default behaviour)
+	(default behavior)
 
  floppy=no_unexpected_interrupts
  floppy=L40SX
diff -u --recursive --new-file v1.3.96/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.96/linux/drivers/block/floppy.c	Sat Apr 27 15:19:50 1996
+++ linux/drivers/block/floppy.c	Sun Apr 28 18:47:40 1996
@@ -50,7 +50,7 @@
 
 /* 1992/9/20
  * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
- * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
+ * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
  * Christoph H. Hochst\"atter.
  * I have fixed the shift values to the ones I always use. Maybe a new
  * ioctl() should be created to be able to modify them.
@@ -132,7 +132,7 @@
 #include <linux/fd.h>
 
 
-#define OLDFDRAWCMD 0x020d /* send a raw command to the fdc */
+#define OLDFDRAWCMD 0x020d /* send a raw command to the FDC */
 
 struct old_floppy_raw_cmd {
   void *data;
@@ -164,8 +164,9 @@
 
 static int use_virtual_dma=0; /* virtual DMA for Intel */
 static unsigned short virtual_dma_port=0x3f0;
-static void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
 static int set_dor(int fdc, char mask, char data);
+static inline int __get_order(unsigned long size);
 #include <asm/floppy.h>
 
 
@@ -179,10 +180,6 @@
 #define FLOPPY_MOTOR_MASK 0xf0
 #endif
 
-#ifndef fd_eject
-#define fd_eject(x) -EINVAL
-#endif
-
 #ifndef fd_get_dma_residue
 #define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
 #endif
@@ -203,12 +200,13 @@
 	return order;
 }
 
-static unsigned long dma_mem_alloc(int size)
-{
-	int order = __get_order(size);
+#ifndef fd_dma_mem_free
+#define fd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
+#endif
 
-	return __get_dma_pages(GFP_KERNEL,order);
-}
+#ifndef fd_dma_mem_alloc
+#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
+#endif
 
 /* End dma memory related stuff */
 
@@ -532,6 +530,22 @@
 static long current_count_sectors = 0;
 static unsigned char sector_t; /* sector in track */
 
+
+#ifndef fd_eject
+#ifdef __sparc__
+static int fd_eject(int drive)
+{
+	set_dor(0, ~0, 0x90);
+	udelay(500);
+	set_dor(0, ~0x80, 0);
+	udelay(500);
+}
+#else
+#define fd_eject(x) -EINVAL
+#endif
+#endif
+
+
 #ifdef DEBUGT
 static long unsigned debugtimer;
 #endif
@@ -1163,7 +1177,8 @@
 	}
 } /* perpendicular_mode */
 
-static int fifo = 0xa;
+static int fifo_depth = 0xa;
+static int no_fifo = 0;
 
 static int fdc_configure(void)
 {
@@ -1176,10 +1191,9 @@
 	if(need_more_output() != MORE_OUTPUT)
 		return 0;
 	output_byte(0);
-	output_byte(0x10 | (fifo & 0xf)); /* FIFO on, polling off,
-					     10 byte threshold */
+	output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
 #endif
-	output_byte(0);	/* precompensation from track 
+	output_byte(0);	/* pre-compensation from track 
 			   0 upwards */
 	return 1;
 }	
@@ -1214,7 +1228,7 @@
 	int hlt_max_code = 0x7f;
 	int hut_max_code = 0xf;
 
-	if (FDCS->need_configure && FDCS->has_fifo) {
+	if (FDCS->need_configure && FDCS->version >= FDC_82072A) {
 		fdc_configure();
 		FDCS->need_configure = 0;
 		/*DPRINT("FIFO enabled\n");*/
@@ -1313,7 +1327,7 @@
 
 
 /*
- * Ok, this error interpreting routine is called after a
+ * OK, this error interpreting routine is called after a
  * DMA read/write has succeeded
  * or failed, so we check the results, and copy any buffers.
  * hhb: Added better error reporting.
@@ -1392,7 +1406,7 @@
 
 /*
  * This routine is called when everything should be correctly set up
- * for the transfer (ie floppy motor is on, the correct floppy is
+ * for the transfer (i.e. floppy motor is on, the correct floppy is
  * selected, and the head is sitting on the right track).
  */
 static void setup_rw_floppy(void)
@@ -1648,6 +1662,7 @@
 			for (i=0; i<inr; i++)
 				printk("%d %x\n", i, reply_buffer[i]);
 	}
+	FDCS->reset = 0;        /* Allow SENSEI to be sent. */
 	while(1){
 		output_byte(FD_SENSEI);
 		inr=result();
@@ -1666,7 +1681,7 @@
 { 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
 
 /* interrupt handler */
-static void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
 	void (*handler)(void) = DEVICE_INTR;
 
@@ -1719,6 +1734,9 @@
 #ifdef DEBUGT
 	debugt("reset interrupt:");
 #endif
+#ifdef __sparc__
+	fdc_specify();  /* P3: It gives us "sector not found" without this. */
+#endif
 	result();		/* get the status ready for set_fdc */
 	if (FDCS->reset) {
 		printk("reset set in interrupt, calling %p\n", cont->error);
@@ -1728,8 +1746,8 @@
 }
 
 /*
- * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
- * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
+ * or by setting the self clearing bit 7 of STATUS (newer FDCs)
  */
 static void reset_fdc(void)
 {
@@ -1901,7 +1919,7 @@
  * here ends the bottom half. Exported routines are:
  * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
  * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
- * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * Initialization also uses output_byte, result, set_dor, floppy_interrupt
  * and set_dor.
  * ========================================================================
  */
@@ -2970,8 +2988,8 @@
 	*ptr = 0;
 	while(this) {
 		if (this->buffer_length) {
-			free_pages((unsigned long)this->kernel_data,
-				   __get_order(this->buffer_length));
+			fd_dma_mem_free((unsigned long)this->kernel_data,
+					this->buffer_length);
 			this->buffer_length = 0;
 		}
 		next = this->next;
@@ -3036,7 +3054,7 @@
 		if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
 			if (ptr->length <= 0)
 				return -EINVAL;
-			ptr->kernel_data =(char*)dma_mem_alloc(ptr->length);
+			ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
 			if (!ptr->kernel_data)
 				return -ENOMEM;
 			ptr->buffer_length = ptr->length;
@@ -3292,7 +3310,7 @@
 	/* 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 */
+	if(cmd == CDROMEJECT || /* CD-ROM eject */
 	   cmd == 0x6470 /* SunOS floppy eject */) {
 		DPRINT("obsolete eject ioctl\n");
 		DPRINT("please use floppycontrol --eject\n");
@@ -3330,12 +3348,7 @@
 
 			/* do the actual eject. Fails on
 			 * non-Sparc architectures */
-			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);
+			ret=fd_eject(UNIT(drive));
 
 			USETF(FD_DISK_CHANGED);
 			USETF(FD_VERIFY);
@@ -3452,7 +3465,7 @@
 	int first=1;
 	int drive;
 
-	/* read drive info out of physical cmos */
+	/* read drive info out of physical CMOS */
 	drive=0;
 	if (!UDP->cmos)
 		UDP->cmos= FLOPPY0_TYPE;
@@ -3464,6 +3477,8 @@
 	/* additional physical CMOS drive detection should go here */
 
 	for (drive=0; drive < N_DRIVE; drive++){
+		if (UDP->cmos >= 16)
+			UDP->cmos = 0;
 		if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
 			memcpy((char *) UDP,
 			       (char *) (&default_drive_params[(int)UDP->cmos].params),
@@ -3594,19 +3609,19 @@
 		else
 			try = 32; /* Only 24 actually useful */
 
-		tmp=(char *)dma_mem_alloc(1024 * try);
+		tmp=(char *)fd_dma_mem_alloc(1024 * try);
 		if (!tmp) {
 			try >>= 1; /* buffer only one side */
 			INFBOUND(try, 16);
-			tmp= (char *)dma_mem_alloc(1024*try);
+			tmp= (char *)fd_dma_mem_alloc(1024*try);
 		}
 		if (!tmp) {
 			DPRINT("Unable to allocate DMA memory\n");
 			RETERR(ENXIO);
 		}
-		if (floppy_track_buffer){
-			free_pages((unsigned long)tmp,__get_order(try*1024));
-		}else {
+		if (floppy_track_buffer)
+			fd_dma_mem_free((unsigned long)tmp,try*1024);
+		else {
 			buffer_min = buffer_max = -1;
 			floppy_track_buffer = tmp;
 			max_buffer_sectors = try;
@@ -3741,7 +3756,7 @@
 };
 
 /*
- * Floppy Driver initialisation
+ * Floppy Driver initialization
  * =============================
  */
 
@@ -3751,7 +3766,6 @@
 {
 	int r;
 
-	FDCS->has_fifo = 0;
 	output_byte(FD_DUMPREGS);	/* 82072 and better know DUMPREGS */
 	if (FDCS->reset)
 		return FDC_NONE;
@@ -3766,18 +3780,12 @@
 		       fdc, r);
 		return FDC_UNKNOWN;
 	}
-	output_byte(FD_VERSION);
-	r = result();
-	if ((r == 1) && (reply_buffer[0] == 0x80)){
+
+	if(!fdc_configure()) {
 		printk(KERN_INFO "FDC %d is an 82072\n",fdc);
-		return FDC_82072;		/* 82072 doesn't know VERSION */
-	}
-	if ((r != 1) || (reply_buffer[0] != 0x90)) {
-		printk("FDC %d init: VERSION: unexpected return of %d bytes.\n",
-		       fdc, r);
-		return FDC_UNKNOWN;
+		return FDC_82072;      	/* 82072 doesn't know CONFIGURE */
 	}
-	FDCS->has_fifo = fdc_configure();
+
 	output_byte(FD_PERPENDICULAR);
 	if(need_more_output() == MORE_OUTPUT) {
 		output_byte(0);
@@ -3889,7 +3897,8 @@
 	}
 	if (current_drive >= 4 && !FDC2)
 		FDC2 = 0x370;
-	if (ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
+	if (ints[2] <= 0 || 
+	    (ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){
 		DPRINT1("bad cmos code %d\n", ints[2]);
 		return;
 	}
@@ -3918,7 +3927,9 @@
 	{ "omnibook", 0, &use_virtual_dma, 1 },
 	{ "dma", 0, &use_virtual_dma, 0 },
 
-	{ "fifo", 0, &fifo, 0xa },
+	{ "fifo_depth", 0, &fifo_depth, 0xa },
+	{ "nofifo", 0, &no_fifo, 0x20 },
+	{ "usefifo", 0, &no_fifo, 0 },
 
 	{ "cmos", set_cmos, 0, 0 },
 
@@ -4054,8 +4065,7 @@
 	if (have_no_fdc) {
 		DPRINT("no floppy controllers found\n");
 		unregister_blkdev(MAJOR_NR,"fd");
-	} else
-		virtual_dma_init();
+	}
 	return have_no_fdc;
 }
 
@@ -4103,7 +4113,7 @@
 	int drive;
 #endif
 	long tmpsize;
-	void *tmpaddr;
+	unsigned long tmpaddr;
 
 	cli();
 	if (--usage_count){
@@ -4125,11 +4135,11 @@
 
 	if (floppy_track_buffer && max_buffer_sectors) {
 		tmpsize = max_buffer_sectors*1024;
-		tmpaddr = (void *)floppy_track_buffer;
+		tmpaddr = (unsigned long)floppy_track_buffer;
 		floppy_track_buffer = 0;
 		max_buffer_sectors = 0;
 		buffer_min = buffer_max = -1;
-		free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
+		fd_dma_mem_free(tmpaddr, tmpsize);
 	}
 
 #ifdef FLOPPY_SANITY_CHECK
@@ -4248,4 +4258,17 @@
 }
 #endif
 
+#else
+/* eject the boot floppy (if we need the drive for a different root floppy) */
+/* This should only be called at boot time when we're sure that there's no
+ * resource contention. */
+void floppy_eject(void)
+{
+	int dummy;
+	floppy_grab_irq_and_dma();
+	lock_fdc(0,0);
+	dummy=fd_eject(0);
+	process_fd_request();
+	floppy_release_irq_and_dma();
+}
 #endif
diff -u --recursive --new-file v1.3.96/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v1.3.96/linux/drivers/block/rd.c	Sat Apr 27 15:19:51 1996
+++ linux/drivers/block/rd.c	Sun Apr 28 18:47:40 1996
@@ -48,6 +48,7 @@
 #include <linux/mman.h>
 #include <linux/malloc.h>
 #include <linux/ioctl.h>
+#include <linux/fd.h>
 #include <linux/module.h>
 
 #include <asm/system.h>
@@ -90,11 +91,12 @@
  * architecture-specific setup routine (from the stored bootsector
  * information). 
  */
+int rd_size = 4096;		/* Size of the ramdisks */
+
 #ifndef MODULE
 int rd_doload = 0;		/* 1 = load ramdisk, 0 = don't load */
 int rd_prompt = 1;		/* 1 = prompt for ramdisk, 0 = don't prompt */
 int rd_image_start = 0;		/* starting block # of image */
-int rd_size = 4096;		/* Size of the ramdisks */
 #ifdef CONFIG_BLK_DEV_INITRD
 unsigned long initrd_start,initrd_end;
 int mount_initrd = 1;		/* zero if initrd should not be mounted */
@@ -489,7 +491,7 @@
 		outfile.f_op->write(outfile.f_inode, &outfile, buf,
 				    BLOCK_SIZE);
 		if (!(i % 16)) {
-			printk(KERN_NOTICE "%c\b", rotator[rotate & 0x3]);
+			printk("%c\b", rotator[rotate & 0x3]);
 			rotate++;
 		}
 	}
@@ -515,6 +517,9 @@
 	if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
 
 	if (rd_prompt) {
+#ifdef CONFIG_BLK_DEV_FD
+		floppy_eject();
+#endif
 		printk(KERN_NOTICE
 		       "VFS: Insert root floppy disk to be loaded into ramdisk and press ENTER\n");
 		wait_for_keypress();
diff -u --recursive --new-file v1.3.96/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in
--- v1.3.96/linux/drivers/cdrom/Config.in	Tue Feb 13 10:30:25 1996
+++ linux/drivers/cdrom/Config.in	Sun Apr 28 12:09:28 1996
@@ -14,7 +14,7 @@
     fi
   fi
 fi
-tristate 'Aztech/Orchid/Okano/Wearnes/TXC (non IDE) CDROM support' CONFIG_AZTCD
+tristate 'Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support' CONFIG_AZTCD
 tristate 'Sony CDU535 CDROM support' CONFIG_CDU535
 tristate 'Goldstar R420 CDROM support' CONFIG_GSCD
 tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206
diff -u --recursive --new-file v1.3.96/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.96/linux/drivers/cdrom/aztcd.c	Fri Apr 12 15:51:51 1996
+++ linux/drivers/cdrom/aztcd.c	Sun Apr 28 12:09:28 1996
@@ -1,6 +1,6 @@
-#define AZT_VERSION "2.2"
-/*      $Id: aztcd.c,v 2.20 1996/03/12 18:31:23 root Exp root $
-	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
+#define AZT_VERSION "2.30"
+/*      $Id: aztcd.c,v 2.30 1996/04/26 05:32:15 root Exp root $
+	linux/drivers/block/aztcd.c - Aztech CD268 CDROM driver
 
 	Copyright (C) 1994,95,96 Werner Zimmermann(zimmerma@rz.fht-esslingen.de)
 
@@ -23,7 +23,7 @@
 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 	HISTORY
-	V0.0    Adaption to Adaptec CD268-01A Version 1.3
+	V0.0    Adaption to Aztech CD268-01A Version 1.3
 		Version is PRE_ALPHA, unresolved points:
 		1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
 		   thus driver causes CPU overhead and is very slow 
@@ -143,8 +143,12 @@
         V2.10   Started to modify azt_poll to prevent reading beyond end of
                 tracks.
                 Werner Zimmermann, December 3, 95
-	NOTE: 
-	Points marked with ??? are questionable !
+        V2.20   Changed some comments
+                Werner Zimmermann, April 1, 96
+        V2.30   Implemented support for CyCDROM CR520, CR940, Code for CR520 
+        	delivered by H.Berger with preworks by E.Moenkeberg.
+                Werner Zimmermann, April 29, 96
+
 */
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -178,6 +182,13 @@
 #define RETURN(message)        {printk("aztcd: Warning: %s failed\n",message);\
                                 return;}
 
+/* Macros to switch the IDE-interface to the slave device and back to the master*/
+#define SWITCH_IDE_SLAVE  outb_p(0xa0,azt_port+6); \
+	                  outb_p(0x10,azt_port+6); \
+	                  outb_p(0x00,azt_port+7); \
+	                  outb_p(0x10,azt_port+6); 
+#define SWITCH_IDE_MASTER outb_p(0xa0,azt_port+6);
+
 static int aztPresent = 0;
 
 #if 0
@@ -411,6 +422,10 @@
 #ifdef AZT_DEBUG
    printk("aztcd: Executing command %x\n",cmd);
 #endif
+
+   if ((azt_port==0x1f0)||(azt_port==0x170))  
+      SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration*/
+   
    aztCmd=cmd;
    outb(POLLED,MODE_PORT);
    do { if (inb(STATUS_PORT)&AFL_STATUS) break;
@@ -778,7 +793,7 @@
 		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
 		if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc();
 		if (entry.cdte_track == CDROM_LEADOUT)
-		  tocPtr = &Toc[DiskInfo.last + 1];   /* ??? */
+		  tocPtr = &Toc[DiskInfo.last + 1];
 		else if (entry.cdte_track > DiskInfo.last
 				|| entry.cdte_track < DiskInfo.first)
 		{ return -EINVAL;
@@ -866,7 +881,7 @@
 		  STEN_LOW_WAIT;
 		}
 		if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1);
-		STEN_LOW_WAIT; /*???*/
+		STEN_LOW_WAIT;
 		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
 		break;
 	case CDROMEJECT_SW:
@@ -1041,7 +1056,7 @@
     int loop_ctl = 1;
     int skip = 0;
 
-    if (azt_error) {                             /* ???*/
+    if (azt_error) {                            
 	if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1");
 	STEN_LOW;
 	azt_error=inb(DATA_PORT)&0xFF;
@@ -1128,13 +1143,13 @@
 	      end_request(0);
 	    return;
 	  }
-					/*???*/
+				
 /*	  if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3");
 	  outb(0x01, DATA_PORT);          
 	  PA_OK;
 	  STEN_LOW;
 */        if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4");
-	  STEN_LOW; /*???*/
+	  STEN_LOW;
 	  azt_mode = 1;
 	  azt_state = AZT_S_READ;
 	  AztTimeout = 3000;
@@ -1234,7 +1249,7 @@
 	  }
 #endif
 
-	  st = inb(STATUS_PORT) & AFL_STATUSorDATA;   /*???*/
+	  st = inb(STATUS_PORT) & AFL_STATUSorDATA; 
 
 	  switch (st) {
 
@@ -1341,7 +1356,7 @@
 	      }
 	      AztTimeout = READ_TIMEOUT;   
 	      if (azt_read_count==0) {
-		azt_state = AZT_S_STOP;   /*???*/
+		azt_state = AZT_S_STOP; 
 		loop_ctl = 1;
 		break;           
 	      } 
@@ -1357,7 +1372,7 @@
 	    printk("AZT_S_STOP\n");
 	  }
 #endif
-	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);  /*???*/
+	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);
 	  while (azt_read_count!=0) {
 	    int i;
 	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
@@ -1545,13 +1560,17 @@
 	  printk("aztcd: no Aztech CD-ROM Initialization");
           return -EIO;
 	}
-	printk("aztcd: Aztech,Orchid,Okano,Wearnes,Txc CD-ROM Driver (C) 1994,95,96 W.Zimmermann\n");
+	printk("aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM CD-ROM Driver\n"); 
+	printk("aztcd: (C) 1994-96 W.Zimmermann\n");
 	printk("aztcd: DriverVersion=%s BaseAddress=0x%x  For IDE/ATAPI-drives use ide-cd.c\n",AZT_VERSION,azt_port);
 	printk("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n");
 
-	if (check_region(azt_port, 4)) {
-	  printk("aztcd: conflict, I/O port (%X) already used\n",
-		 azt_port);
+        if ((azt_port==0x1f0)||(azt_port==0x170))  
+          st = check_region(azt_port, 8);  /*IDE-interfaces need 8 bytes*/
+        else
+          st = check_region(azt_port, 4);  /*proprietary interfaces need 4 bytes*/
+	if (st) 
+	{ printk("aztcd: conflict, I/O port (%X) already used\n",azt_port);
           return -EIO;
 	}
 
@@ -1570,7 +1589,11 @@
 #endif	
 
 	/* check for presence of drive */
-	outb(POLLED,MODE_PORT);                 /*???*/
+	
+        if ((azt_port==0x1f0)||(azt_port==0x170))  
+            SWITCH_IDE_SLAVE;  /*switch IDE interface to slave configuration*/
+
+	outb(POLLED,MODE_PORT);              
 	inb(CMD_PORT);
 	inb(CMD_PORT);
 	outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
@@ -1600,7 +1623,7 @@
 	            { inb(STATUS_PORT);    /*removing all data from earlier tries*/
 	              inb(DATA_PORT);
 	            }
-	          outb(POLLED,MODE_PORT);           /*???*/
+	          outb(POLLED,MODE_PORT);          
 	          inb(CMD_PORT);
 	          inb(CMD_PORT);
 		  getAztStatus(); 		    /*trap errors*/
@@ -1621,7 +1644,7 @@
 #ifdef AZT_DEBUG
 	          printk("aztcd: Status = %x\n",st);
 #endif
-	          outb(POLLED,MODE_PORT);              /*???*/
+	          outb(POLLED,MODE_PORT);            
 	          inb(CMD_PORT);
 	          inb(CMD_PORT);
 	          outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
@@ -1654,9 +1677,9 @@
 	 { printk("ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
 	 }
 	else if ((result[1]==0x03)&&(result[2]=='5'))
-	 { printk("TXC drive detected\n"); /*Conrad TXC*/
+	 { printk("TXC or CyCDROM drive detected\n"); /*Conrad TXC, CyCDROM*/
 	 }
-	else                                               /*OTHERS or none*/
+	else                                             /*OTHERS or none*/
 	 { printk("\nunknown drive or firmware version detected\n");
 	   printk("aztcd may not run stable, if you want to try anyhow,\n");
 	   printk("boot with: aztcd=<BaseAddress>,0x79\n");
@@ -1677,8 +1700,11 @@
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 	read_ahead[MAJOR_NR] = 4;
 
-	request_region(azt_port, 4, "aztcd");
-
+        if ((azt_port==0x1f0)||(azt_port==0x170))  
+	   request_region(azt_port, 8, "aztcd");  /*IDE-interface*/
+        else
+	   request_region(azt_port, 4, "aztcd");  /*proprietary inferface*/
+        
 	azt_invalidate_buffers();
 	aztPresent = 1;
 	aztCloseDoor();
@@ -2038,7 +2064,7 @@
 
   if (!multi)
      { azt_mode = 0x05;
-       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); /*???*/
+       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1);
        STEN_LOW_WAIT;
      }
   for (limit = 300; limit > 0; limit--)
@@ -2097,7 +2123,12 @@
     { printk("What's that: can't unregister aztcd\n");
       return;
     }
-   release_region(azt_port,4);
-   printk(KERN_INFO "aztcd module released.\n");
+  if ((azt_port==0x1f0)||(azt_port==0x170))  
+    { SWITCH_IDE_MASTER;
+      release_region(azt_port,8);  /*IDE-interface*/
+    }
+  else
+      release_region(azt_port,4);  /*proprietary interface*/
+  printk(KERN_INFO "aztcd module released.\n");
 }   
 #endif MODULE
diff -u --recursive --new-file v1.3.96/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.3.96/linux/drivers/char/serial.c	Fri Apr 19 10:07:58 1996
+++ linux/drivers/char/serial.c	Sun Apr 28 18:47:41 1996
@@ -2448,7 +2448,7 @@
  */
 static void show_serial_version(void)
 {
- 	printk("%s version %s with", serial_name, serial_version);
+ 	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
 #define SERIAL_OPT
@@ -2797,7 +2797,7 @@
 		autoconfig(info);
 		if (info->type == PORT_UNKNOWN)
 			continue;
-		printk("tty%02d%s at 0x%04x (irq = %d)", info->line, 
+		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line, 
 		       (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 		       info->port, info->irq);
 		switch (info->type) {
@@ -2867,7 +2867,7 @@
 		printk("register_serial(): autoconfig failed\n");
 		return -1;
 	}
-	printk("tty%02d at 0x%04x (irq = %d)", info->line, 
+	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line, 
 	       info->port, info->irq);
 	switch (info->type) {
 	case PORT_8250:
@@ -2895,7 +2895,7 @@
 	if (info->tty)
 		tty_hangup(info->tty);
 	info->type = PORT_UNKNOWN;
-	printk("tty%02d unloaded\n", info->line);
+	printk(KERN_INFO "tty%02d unloaded\n", info->line);
 	restore_flags(flags);
 }
 
@@ -2909,6 +2909,7 @@
 {
 	unsigned long flags;
 	int e1, e2;
+	int i;
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
 	save_flags(flags);
@@ -2923,5 +2924,10 @@
 		printk("SERIAL: failed to unregister callout driver (%d)\n", 
 		       e2);
 	restore_flags(flags);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		if (rs_table[i].type != PORT_UNKNOWN)
+			release_region(rs_table[i].port, 8);
+	}
 }
 #endif /* MODULE */
diff -u --recursive --new-file v1.3.96/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v1.3.96/linux/drivers/char/vt.c	Sat Feb 17 10:19:07 1996
+++ linux/drivers/char/vt.c	Sun Apr 28 18:47:13 1996
@@ -713,7 +713,8 @@
 		/* the frsig is ignored, so we set it to 0 */
 		vt_cons[console]->vt_mode.frsig = 0;
 		vt_cons[console]->vt_pid = current->pid;
-		vt_cons[console]->vt_newvt = 0;
+		/* no switch is required -- saw@shade.msu.ru */
+		vt_cons[console]->vt_newvt = -1; 
 		return 0;
 	}
 
diff -u --recursive --new-file v1.3.96/linux/drivers/isdn/teles/card.c linux/drivers/isdn/teles/card.c
--- v1.3.96/linux/drivers/isdn/teles/card.c	Sun Apr 21 19:22:07 1996
+++ linux/drivers/isdn/teles/card.c	Mon Apr 29 11:02:03 1996
@@ -652,6 +652,7 @@
 		  break;
 	  case (12):
 	  case (13):
+	  	  ph_command(sp, 8);
 		  sp->ph_active = 5;
 		  isac_sched_event(sp, ISAC_PHCHANGE);
 		  if (!sp->xmtibh)
diff -u --recursive --new-file v1.3.96/linux/drivers/net/depca.c linux/drivers/net/depca.c
--- v1.3.96/linux/drivers/net/depca.c	Wed Apr 24 17:00:40 1996
+++ linux/drivers/net/depca.c	Mon Apr 29 17:11:39 1996
@@ -201,11 +201,12 @@
 			  <pchen@woodruffs121.residence.gatech.edu>.
 			  Add new multicasting code.
       0.421   22-Apr-96	  Fix alloc_device() bug <jari@markkus2.fimr.fi>
+      0.422   29-Apr-96	  Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
 
     =========================================================================
 */
 
-static const char *version = "depca.c:v0.421 96/4/22 davies@wanton.lkg.dec.com\n";
+static const char *version = "depca.c:v0.422 96/4/29 davies@wanton.lkg.dec.com\n";
 
 #include <linux/module.h>
 
@@ -483,8 +484,8 @@
 
   if (inw(DEPCA_DATA) == STOP) {
     if (mem == 0) {
-      for (; mem_base[mem_chkd]; mem_chkd++) {
-	mem_start = mem_base[mem_chkd];
+      while (mem_base[mem_chkd]) {
+	mem_start = mem_base[mem_chkd++];
 	DepcaSignature(name, mem_start);
 	if (*name != '\0') break;
       }
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v1.3.96/linux/drivers/scsi/NCR5380.c	Fri Apr 12 15:51:59 1996
+++ linux/drivers/scsi/NCR5380.c	Mon Apr 29 17:14:19 1996
@@ -3224,7 +3224,7 @@
 
 
 /* 
- * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
  * 
  * Purpose : reset the SCSI bus.
  *
@@ -3235,7 +3235,7 @@
 #ifndef NCR5380_reset
 static
 #endif
-int NCR5380_reset (Scsi_Cmnd *cmd) {
+int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int dummy) {
     NCR5380_local_declare();
     NCR5380_setup(cmd->host);
 
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h
--- v1.3.96/linux/drivers/scsi/NCR5380.h	Sat Apr 27 15:19:56 1996
+++ linux/drivers/scsi/NCR5380.h	Mon Apr 29 17:14:19 1996
@@ -298,7 +298,7 @@
 #ifndef NCR5380_reset
 static
 #endif
-int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int);
+int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags);
 #ifndef NCR5380_queue_command
 static 
 #endif
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/dtc.h linux/drivers/scsi/dtc.h
--- v1.3.96/linux/drivers/scsi/dtc.h	Wed Apr 10 17:02:25 1996
+++ linux/drivers/scsi/dtc.h	Mon Apr 29 17:14:19 1996
@@ -79,7 +79,7 @@
 int dtc_biosparam(Disk *, kdev_t, int*);
 int dtc_detect(Scsi_Host_Template *);
 int dtc_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int dtc_reset(Scsi_Cmnd *);
+int dtc_reset(Scsi_Cmnd *, unsigned int reset_flags);
 int dtc_proc_info (char *buffer, char **start, off_t offset,
 		   int length, int hostno, int inout);
 
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h
--- v1.3.96/linux/drivers/scsi/g_NCR5380.h	Wed Apr 10 17:02:25 1996
+++ linux/drivers/scsi/g_NCR5380.h	Mon Apr 29 17:14:19 1996
@@ -46,7 +46,7 @@
 int generic_NCR5380_detect(Scsi_Host_Template *);
 int generic_NCR5380_release_resources(struct Scsi_Host *);
 int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int generic_NCR5380_reset(Scsi_Cmnd *);
+int generic_NCR5380_reset(Scsi_Cmnd *, unsigned int);
 int notyet_generic_proc_info (char *buffer ,char **start, off_t offset,
                      int length, int hostno, int inout);
 const char* generic_NCR5380_info(struct Scsi_Host *);
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v1.3.96/linux/drivers/scsi/in2000.c	Fri Mar  1 07:50:52 1996
+++ linux/drivers/scsi/in2000.c	Sun Apr 28 12:13:45 1996
@@ -1,731 +1,2375 @@
 /*
- *  This file is in2000.c, written and
- *  Copyright (C) 1993  Brad McLean
- *	Last edit 1/19/95 TZ
- * Disclaimer:
- * Note:  This is ugly.  I know it, I wrote it, but my whole
- * focus was on getting the damn thing up and out quickly.
- * Future stuff that would be nice:  Command chaining, and
- * a local queue of commands would speed stuff up considerably.
- * Disconnection needs some supporting code.  All of this
- * is beyond the scope of what I wanted to address, but if you
- * have time and patience, more power to you.
- * Also, there are some constants scattered throughout that
- * should have defines, and I should have built functions to
- * address the registers on the WD chip.
- * Oh well, I'm out of time for this project.
- * The one good thing to be said is that you can use the card.
- */
-
-/*
- * This module was updated by Shaun Savage first on 5-13-93
- * At that time the write was fixed, irq detection, and some
- * timing stuff.  since that time other problems were fixed.
- * On 7-20-93 this file was updated for patch level 11
- * There are still problems with it but it work on 95% of
- * the machines.  There are still problems with it working with
- * IDE drives, as swap drive and HD that support reselection.
- * But for most people it will work.
- */
-/* More changes by Bill Earnest, wde@aluxpo.att.com
- * through 4/07/94. Includes rewrites of FIFO routines,
- * length-limited commands to make swap partitions work.
- * Merged the changes released by Larry Doolittle, based on input
- * from Jon Luckey, Roger Sunshine, John Shifflett. The FAST_FIFO
- * doesn't work for me. Scatter-gather code from Eric. The change to
- * an IF stmt. in the interrupt routine finally made it stable.
- * Limiting swap request size patch to ll_rw_blk.c not needed now.
- * Please ignore the clutter of debug stmts., pretty can come later.
- */
-/* Merged code from Matt Postiff improving the auto-sense validation
- * for all I/O addresses. Some reports of problems still come in, but
- * have been unable to reproduce or localize the cause. Some are from
- * LUN > 0 problems, but that is not host specific. Now 6/6/94.
- */
-/* Changes for 1.1.28 kernel made 7/19/94, code not affected. (WDE)
- */
-/* Changes for 1.1.43+ kernels made 8/25/94, code added to check for
- * new BIOS version, derived by jshiffle@netcom.com. (WDE)
+ *    in2000.c -  Linux device driver for the
+ *                Always IN2000 ISA SCSI card.
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ *    john@geolog.com
+ *    jshiffle@netcom.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Drew Eckhardt's excellent 'Generic NCR5380' sources provided
+ * much of the inspiration and some of the code for this driver.
+ * The Linux IN2000 driver distributed in the Linux kernels through
+ * version 1.2.13 was an extremely valuable reference on the arcane
+ * (and still mysterious) workings of the IN2000's fifo. It also
+ * is where I lifted in2000_biosparam(), the gist of the card
+ * detection scheme, and other bits of code. Many thanks to the
+ * talented and courageous people who wrote, contributed to, and
+ * maintained that driver (including Brad McLean, Shaun Savage,
+ * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey,
+ * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric
+ * Youngdale). I should also mention the driver written by
+ * Hamish Mcdonald for the (GASP!) Amiga A2091 card, included
+ * in the Linux-m68k distribution; it gave me a good initial
+ * understandng of the proper way to run a WD33c93 chip, and I
+ * ended up stealing lots of code from it.
+ *
+ * _This_ driver is (I feel) an improvement over the old one in
+ * several respects:
+ *    -  All problems relating to the data size of a SCSI request are
+ *          gone (as far as I know). The old driver couldn't handle
+ *          swapping to partitions because that involved 4k blocks, nor
+ *          could it deal with the st.c tape driver unmodified, because
+ *          that usually involved 4k - 32k blocks. The old driver never
+ *          quite got away from a morbid dependence on 2k block sizes -
+ *          which of course is the size of the card's fifo.
+ *
+ *    -  Target Disconnection/Reconnection is now supported. Any
+ *          system with more than one device active on the SCSI bus
+ *          will benefit from this. The driver defaults to what I'm
+ *          calling 'adaptive disconnect' - meaning that each command
+ *          is evaluated individually as to whether or not it should
+ *          be run with the option to disconnect/reselect (if the
+ *          device chooses), or as a "SCSI-bus-hog".
+ *
+ *    -  Synchronous data transfers are now supported. Because there
+ *          are a few devices (and many improperly terminated systems)
+ *          that choke when doing sync, the default is sync DISABLED
+ *          for all devices. This faster protocol can (and should!)
+ *          be enabled on selected devices via the command-line.
+ *
+ *    -  Runtime operating parameters can now be specified through
+ *       either the LILO or the 'insmod' command line. For LILO do:
+ *          "in2000=blah,blah,blah"
+ *       and with insmod go like:
+ *          "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah"
+ *       The defaults should be good for most people. See the comment
+ *       for 'setup_strings' below for more details.
+ *
+ *    -  The old driver relied exclusively on what the Western Digital
+ *          docs call "Combination Level 2 Commands", which are a great
+ *          idea in that the CPU is relieved of a lot of interrupt
+ *          overhead. However, by accepting a certain (user-settable)
+ *          amount of additional interrupts, this driver achieves
+ *          better control over the SCSI bus, and data transfers are
+ *          almost as fast while being much easier to define, track,
+ *          and debug.
+ *
+ *    -  You can force detection of a card whose BIOS has been disabled.
  *
- * 1/7/95 Fix from Peter Lu (swift@world.std.com) for datalen vs. dataptr
- * logic, much more stable under load.
+ *    -  Multiple IN2000 cards might almost be supported. I've tried to
+ *       keep it in mind, but have no way to test...
+ *
+ *
+ * TODO:
+ *       proc interface. tagged queuing. multiple cards.
+ *
+ *
+ * NOTE:
+ *       When using this or any other SCSI driver as a module, you'll
+ *       find that with the stock kernel, at most _two_ SCSI hard
+ *       drives will be linked into the device list (ie, usable).
+ *       If your IN2000 card has more than 2 disks on its bus, you
+ *       might want to change the define of 'SD_EXTRA_DEVS' in the
+ *       'hosts.h' file from 2 to whatever is appropriate. It took
+ *       me a while to track down this surprisingly obscure and
+ *       undocumented little "feature".
+ *
+ *
+ * People with bug reports, wish-lists, complaints, comments,
+ * or improvements are asked to pah-leeez email me (John Shifflett)
+ * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
+ * this thing into as good a shape as possible, and I'm positive
+ * there are lots of lurking bugs and "Stupid Places".
  *
- * 1/19/95 (zerucha@shell.portal.com) Added module and biosparam support for
- * larger SCSI hard drives (untested).
  */
 
-#ifdef MODULE
-#include <linux/module.h>
-#endif
 
-#include <linux/kernel.h>
-#include <linux/head.h>
-#include <linux/types.h>
-#include <linux/string.h>
+
+#include <asm/system.h>
 #include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/delay.h>
 #include <linux/proc_fs.h>
-#include <asm/dma.h>
-#include <asm/system.h>
 #include <asm/io.h>
-#include <linux/blk.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+
 #include "scsi.h"
-#include "hosts.h"
 #include "sd.h"
-
+#include "hosts.h"
 #include "in2000.h"
-#include<linux/stat.h>
 
-struct proc_dir_entry proc_scsi_in2000 = {
-    PROC_SCSI_IN2000, 6, "in2000",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+
+#define PROC_INTERFACE     /* add code for /proc/scsi/in2000/xxx interface */
+
+#define FAST_READ_IO       /* No problems with these on my machine */
+#define FAST_WRITE_IO
 
-/*#define FAST_FIFO_IO*/
+#define SYNC_DEBUG         /* extra info on sync negotiation printed */
+#define DEBUGGING_ON       /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0   /* default bitmask - change from command-line */
 
-/*#define DEBUG*/
-#ifdef DEBUG
-#define DEB(x) x
+#define IN2000_VERSION    "1.28"
+#define IN2000_DATE       "27/Apr/1996"
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));}
 #else
-#define DEB(x)
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
+
+#define IS_DIR_OUT(cmd) ((cmd)->cmnd[0] == WRITE_6  || \
+                         (cmd)->cmnd[0] == WRITE_10 || \
+                         (cmd)->cmnd[0] == WRITE_12)
+
+
+/*
+ * setup_strings is an array of strings that define some of the operating
+ * parameters and settings for this driver. It is used unless a LILO
+ * or insmod command line has been specified, in which case those settings
+ * are combined with the ones here. The driver recognizes the following
+ * keywords (lower case required) and arguments:
+ *
+ * -  ioport:addr    -Where addr is IO address of a (usually ROM-less) card.
+ * -  noreset        -No optional args. Prevents SCSI bus reset at boot time.
+ * -  nosync:x       -x is a bitmask where the 1st 7 bits correspond with
+ *                    the 7 possible SCSI devices (bit 0 for device #0, etc).
+ *                    Set a bit to PREVENT sync negotiation on that device.
+ *                    The driver default is sync DISABLED on all devices.
+ * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer
+ *                    period. Default is 500; acceptable values are 250 - 1000.
+ * -  disconnect:x   -x = 0 to never allow disconnects, 2 to always allow them.
+ *                    x = 1 does 'adaptive' disconnects, which is the default
+ *                    and generally the best choice.
+ * -  debug:x        -If 'DEBUGGING_ON' is defined, x is a bitmask that causes
+ *                    various types of debug output to printed - see the DB_xxx
+ *                    defines in in2000.h
+ * -  proc:x         -If 'PROC_INTERFACE' is defined, x is a bitmask that
+ *                    determines how the /proc interface works and what it
+ *                    does - see the PR_xxx defines in in2000.h
+ *
+ * Syntax Notes:
+ * -  Numeric arguments can be decimal or the '0x' form of hex notation. There
+ *    _must_ be a colon between a keyword and its numeric argument, with no
+ *    spaces.
+ * -  Keywords are separated by commas, no spaces, in the standard kernel
+ *    command-line manner, except in the case of 'setup_strings[]' (see
+ *    below), which is simply a C array of pointers to char. Each element
+ *    in the array is a string comprising one keyword & argument.
+ * -  A keyword in the 'nth' comma-separated command-line member will overwrite
+ *    the 'nth' element of setup_strings[]. A blank command-line member (in
+ *    other words, a comma with no preceding keyword) will _not_ overwrite
+ *    the corresponding setup_strings[] element.
+ *
+ * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
+ * -  in2000=ioport:0x220,noreset
+ * -  in2000=period:250,disconnect:2,nosync:0x03
+ * -  in2000=debug:0x1e
+ * -  in2000=proc:3
+ */
+
+static char *setup_strings[] =
+      {"","","","","","","","","","","",""};
+
+static struct Scsi_Host *instance_list = 0;
+
+#ifdef PROC_INTERFACE
+unsigned long disc_allowed_total;
+unsigned long disc_taken_total;
 #endif
 
-/* These functions are based on include/asm/io.h */
-#ifndef inw
-inline static unsigned short inw( unsigned short port )
+
+#define read1_io(a)     (inb(hostdata->io_base+(a)))
+#define read2_io(a)     (inw(hostdata->io_base+(a)))
+#define write1_io(b,a)  (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a)  (outw((w),hostdata->io_base+(a)))
+
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO()    \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   insw %%dx              \n \
+1: "                       \
+   : "=D" (sp)                   /* output */   \
+   : "d" (f), "D" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "edi" )       /* trashed */
+
+#define FAST_WRITE2_IO()   \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   outsw %%dx             \n \
+1: "                       \
+   : "=S" (sp)                   /* output */   \
+   : "d" (f), "S" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "esi" )       /* trashed */
+
+
+inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
 {
-   unsigned short _v;
-   
-   __asm__ volatile ("inw %1,%0"
-		     :"=a" (_v):"d" ((unsigned short) port));
-   return _v;
+   write1_io(reg_num,IO_WD_ADDR);
+   return read1_io(IO_WD_DATA);
 }
-#endif
 
-#ifndef outw
-inline static void outw( unsigned short value, unsigned short port )
-{
-   __asm__ volatile ("outw %0,%1"
-			: /* no outputs */
-			:"a" ((unsigned short) value),
-			"d" ((unsigned short) port));
+
+#define READ_AUX_STAT() read1_io(IO_WD_ASR)
+
+
+inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
+{
+   write1_io(reg_num,IO_WD_ADDR);
+   write1_io(value,IO_WD_DATA);
 }
-#endif
 
-/* These functions are lifted from drivers/block/hd.c */
-
-#define port_read(port,buf,nr) \
-__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di")
-
-#define port_write(port,buf,nr) \
-__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si")
-
-static unsigned int base;
-static unsigned int ficmsk;
-static unsigned char irq_level;
-static int in2000_datalen;
-static unsigned int in2000_nsegment;
-static unsigned int in2000_current_segment;
-static unsigned short *in2000_dataptr;
-static char	in2000_datawrite;
-static struct scatterlist * in2000_scatter;
-static Scsi_Cmnd *in2000_SCptr = 0;
-
-static void (*in2000_done)(Scsi_Cmnd *);
-
-static int in2000_test_port(int index)
-{
-    static const int *bios_tab[] = {
-	(int *) 0xc8000, (int *) 0xd0000, (int *) 0xd8000 };
-    int	i;
-    char    tmp;
-
-    tmp = inb(INFLED);
-	/* First, see if the DIP switch values are valid */
-	/* The test of B7 may fail on some early boards, mine works. */
-    if ( ((~tmp & 0x3) != index ) || (tmp & 0x80) || !(tmp & 0x4) )
-    	return 0;
-    printk("IN-2000 probe got dip setting of %02X\n", tmp);
-    tmp = inb(INVERS);
-/* Add some extra sanity checks here */
-    for(i=0; i < 3; i++)
-	if(*(bios_tab[i]+0x04) == 0x41564f4e ||
-		*(bios_tab[i]+0xc) == 0x61776c41) {
-	  printk("IN-2000 probe found hdw. vers. %02x, BIOS at %06x\n",
-		tmp, (unsigned int)bios_tab[i]);
-		return 1;
-	}
-    printk("in2000 BIOS not found.\n");
-    return 0;
+
+inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
+{
+/*   while (READ_AUX_STAT() & ASR_CIP)
+      printk("|");*/
+   write1_io(WD_COMMAND,IO_WD_ADDR);
+   write1_io(cmd,IO_WD_DATA);
 }
 
 
-/*
- * retrieve the current transaction counter from the WD
- */
+uchar read_1_byte(struct IN2000_hostdata *hostdata)
+{
+uchar asr, x = 0;
+
+   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+   write_3393_cmd(hostdata,WD_CMD_TRANS_INFO|0x80);
+   do {
+      asr = READ_AUX_STAT();
+      if (asr & ASR_DBR)
+         x = read_3393(hostdata,WD_DATA);
+      } while (!(asr & ASR_INT));
+   return x;
+}
 
-static unsigned in2000_txcnt(void)
+
+void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
 {
-    unsigned total=0;
+   write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
+   write1_io((value >> 16),IO_WD_DATA);
+   write1_io((value >> 8),IO_WD_DATA);
+   write1_io(value,IO_WD_DATA);
+}
+
+
+unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
+{
+unsigned long value;
+
+   write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
+   value = read1_io(IO_WD_DATA) << 16;
+   value |= read1_io(IO_WD_DATA) << 8;
+   value |= read1_io(IO_WD_DATA);
+   return value;
+}
+
+
+
+static struct sx_period sx_table[] = {
+   {  1, 0x20},
+   {252, 0x20},
+   {376, 0x30},
+   {500, 0x40},
+   {624, 0x50},
+   {752, 0x60},
+   {876, 0x70},
+   {1000,0x00},
+   {0,   0} };
+
+int round_period(unsigned int period)
+{
+int x;
+
+   for (x=1; sx_table[x].period_ns; x++) {
+      if ((period <= sx_table[x-0].period_ns) &&
+          (period >  sx_table[x-1].period_ns)) {
+         return x;
+         }
+      }
+   return 7;
+}
+
+uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+uchar result;
+
+   period *= 4;   /* convert SDTR code to ns */
+   result = sx_table[round_period(period)].reg_value;
+   result |= (offset < OPTIMUM_SX_OFF)?offset:OPTIMUM_SX_OFF;
+   return result;
+}
+
+
+
+void in2000_execute(struct Scsi_Host *instance);
+
+int in2000_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *tmp;
+unsigned long flags;
+
+
+   hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
+
+DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
+
+/* Set up a few fields in the Scsi_Cmnd structure for our own use:
+ *  - host_scribble is the pointer to the next cmd in the input queue
+ *  - scsi_done points to the routine we call when a cmd is finished
+ *  - result is what you'd expect
+ */
+
+   cmd->host_scribble = NULL;
+   cmd->scsi_done = done;
+   cmd->result = 0;
+
+/* We use the Scsi_Pointer structure that's included with each command
+ * as a scratchpad (as it's intended to be used!). The handy thing about
+ * the SCp.xxx fields is that they're always associated with a given
+ * cmd, and are preserved across disconnect-reconnect. This means we
+ * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
+ * if we keep all the critical pointers and counters in SCp:
+ *  - SCp.ptr is the pointer into the RAM buffer
+ *  - SCp.this_residual is the size of that buffer
+ *  - SCp.buffer points to the current scatter-gather buffer
+ *  - SCp.buffers_residual tells us how many S.G. buffers there are
+ *  - SCp.have_data_in helps keep track of >2048 byte transfers
+ *  - SCp.sent_command is not used
+ *  - SCp.phase records this command's SRCID_ER bit setting
+ */
+
+   if (cmd->use_sg) {
+      cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+      cmd->SCp.buffers_residual = cmd->use_sg - 1;
+      cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+      cmd->SCp.this_residual = cmd->SCp.buffer->length;
+      }
+   else {
+      cmd->SCp.buffer = NULL;
+      cmd->SCp.buffers_residual = 0;
+      cmd->SCp.ptr = (char *)cmd->request_buffer;
+      cmd->SCp.this_residual = cmd->request_bufflen;
+      }
+   cmd->SCp.have_data_in = 0;
+
+/* We don't set SCp.phase here - that's done in in2000_execute() */
+
+/* Preset the command status to GOOD, since that's the normal case */
+
+   cmd->SCp.Status = GOOD;
+
+   save_flags(flags);
+   cli();
+
+   /*
+    * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE
+    * commands are added to the head of the queue so that the desired
+    * sense data is not lost before REQUEST_SENSE executes.
+    */
+
+   if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+      cmd->host_scribble = (uchar *)hostdata->input_Q;
+      hostdata->input_Q = cmd;
+      }
+   else {   /* find the end of the queue */
+      for (tmp=(Scsi_Cmnd *)hostdata->input_Q; tmp->host_scribble;
+            tmp=(Scsi_Cmnd *)tmp->host_scribble)
+         ;
+      tmp->host_scribble = (uchar *)cmd;
+      }
+
+/* We know that there's at least one command in 'input_Q' now.
+ * Go see if any of them are runnable!
+ */
 
-    if(inb(INSTAT) & 0x20) return 0xffffff;	/* not readable now */
-    outb(TXCNTH,INSTAT);	/* then autoincrement */
-    total =  (inb(INDATA) & 0xff) << 16;
-    outb(TXCNTM,INSTAT);
-    total += (inb(INDATA) & 0xff) << 8;
-    outb(TXCNTL,INSTAT);
-    total += (inb(INDATA) & 0xff);
-    return total;
+   in2000_execute(cmd->host);
+
+DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
+
+   restore_flags(flags);
+   return 0;
 }
 
+
+
 /*
- * Note: the FIFO is screwy, and has a counter granularity of 16 bytes, so
- * we have to reconcile the FIFO counter, the transaction byte count from the
- * WD chip, and of course, our desired transaction size.  It may look strange,
- * and could probably use improvement, but it works, for now.
- */
-
-static void in2000_fifo_out(void)	/* uses FIFOCNTR */
-{
-    unsigned count, infcnt, txcnt;
-
-    infcnt = inb(INFCNT)& 0xfe;	/* FIFO counter */
-    do {
-	txcnt = in2000_txcnt();
-/*DEB(printk("FIw:%d %02x %d\n", in2000_datalen, infcnt, txcnt));*/
-	count = (infcnt << 3) - 32;	/* don't fill completely */
-	if ( count > in2000_datalen )
-	    count = in2000_datalen;	/* limit to actual data on hand */
-	count >>= 1;		/* Words, not bytes */
-#ifdef FAST_FIFO_IO
-	if ( count ) {
-		port_write(INFIFO, in2000_dataptr, count);
-		in2000_datalen -= (count<<1);
-	}
-#else
-	while ( count-- )
-	    {
-		outw(*in2000_dataptr++, INFIFO);
-		in2000_datalen -= 2;
-	    }
-#endif
-    } while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) );
-    /* If scatter-gather, go on to next segment */
-    if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
-      {
-      in2000_scatter++;
-      in2000_datalen = in2000_scatter->length;
-      in2000_dataptr = (unsigned short*)in2000_scatter->address;
-      }
-    if ( in2000_datalen <= 0 )
-    {
-	ficmsk = 0;
-	count = 32;	/* Always says to use this much flush */
-	while ( count-- )
-	    outw(0, INFIFO);
-	outb(2, ININTR); /* Mask FIFO Interrupts when done */
-    }
-}
-
-static void in2000_fifo_in(void)	/* uses FIFOCNTR */
-{
-    unsigned fic, count, count2;
-
-    count = inb(INFCNT) & 0xe1;
-    do{
-	count2 = count;
-	count = (fic = inb(INFCNT)) & 0xe1;
-    } while ( count != count2 );
-DEB(printk("FIir:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
-    do {
-	count2 = in2000_txcnt();	/* bytes yet to come over SCSI bus */
-DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in2000_dataptr));
-	if(count2 > 65536) count2 = 0;
-	if(fic > 128) count = 1024;
-	  else if(fic > 64) count = 512;
-	    else if (fic > 32) count = 256;
-	      else if ( count2 < in2000_datalen ) /* if drive has < what we want */
-		count = in2000_datalen - count2;	/* FIFO has the rest */
-	if ( count > in2000_datalen )	/* count2 is lesser of FIFO & rqst */
-	    count2 = in2000_datalen >> 1;	/* converted to word count */
-	else
-	    count2 = count >> 1;
-	count >>= 1;		/* also to words */
-	count -= count2;	/* extra left over in FIFO */
-#ifdef FAST_FIFO_IO
-	if ( count2 ) {
-		port_read(INFIFO, in2000_dataptr, count2);
-		in2000_datalen -= (count2<<1);
-	}
-#else
-	while ( count2-- )
-	{
-	    *in2000_dataptr++ = inw(INFIFO);
-	    in2000_datalen -=2;
-	}
-#endif
-    } while((in2000_datalen > 0) && (fic = inb(INFCNT)) );
-DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
-/*    while ( count-- )
-    	inw(INFIFO);*/	/* Throw away some extra stuff */
-    if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
-      {
-      in2000_scatter++;
-      in2000_datalen = in2000_scatter->length;
-      in2000_dataptr = (unsigned short*)in2000_scatter->address;
-      }
-    if ( ! in2000_datalen ){
-	outb(2, ININTR); /* Mask FIFO Interrupts when done */
-	ficmsk = 0;}
-}
-
-static void in2000_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
-{
-    int result=0;
-    unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte;
-    int action=0;
-    Scsi_Cmnd *SCptr;
-
-  DEB(printk("INT:%d %02x %08x\n", in2000_datalen, inb(INFCNT),(unsigned int)in2000_dataptr));
-
-    if (( (ficmsk & (count = inb(INFCNT))) == 0xfe ) ||
-		( (inb(INSTAT) & 0x8c) == 0x80))
-	{	/* FIFO interrupt or WD interrupt */
-   	auxstatus = inb(INSTAT);	/* need to save now */
-   	outb(SCSIST,INSTAT);
-   	scsistatus = inb(INDATA); /* This clears the WD intrpt bit */
-   	outb(TARGETU,INSTAT);	/* then autoincrement */
-   	scsibyte = inb(INDATA);	/* Get the scsi status byte */
-   	outb(CMDPHAS,INSTAT);
-   	cmdphase = inb(INDATA);
-   	DEB(printk("(int2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
-		scsistatus,cmdphase,scsibyte));
-
-	/* Why do we assume that we need to send more data here??? ERY */
-   	if ( in2000_datalen )	/* data xfer pending */
-   	    {
-   	    if ( in2000_dataptr == NULL )
-		printk("int2000: dataptr=NULL datalen=%d\n",
-			in2000_datalen);
-	    else if ( in2000_datawrite )
-		in2000_fifo_out();
-	    else
-		in2000_fifo_in();
-   	    } 
-	if ( (auxstatus & 0x8c) == 0x80 )
-	    {	/* There is a WD Chip interrupt & register read good */
-	    outb(2,ININTR);	/* Disable fifo interrupts */
-	    ficmsk = 0;
-	    result = DID_OK << 16;
-	    /* 16=Select & transfer complete, 85=got disconnect */
-	    if ((scsistatus != 0x16) && (scsistatus != 0x85)
-		&& (scsistatus != 0x42)){
-/*	   	printk("(WDi2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
-			scsistatus,cmdphase,scsibyte);*/
-/*		printk("QDAT:%d %08x %02x\n",
-		in2000_datalen,(unsigned int)in2000_dataptr,ficmsk);*/
-		;
-	    }
-		switch ( scsistatus & 0xf0 )
-		    {
-		    case	0x00:	/* Card Reset Completed */
-			action = 3;
-			break;
-		    case	0x10:	/* Successful Command Completion */
-			if ( scsistatus & 0x8 )
-		    	    action = 1;
-			break;
-		    case	0x20:	/* Command Paused or Aborted */
-			if ( (scsistatus & 0x8) )
-		    	    action = 1;
-			else if ( (scsistatus & 7) < 2 )
-		    		action = 2;
-			     else
-		    		result = DID_ABORT << 16;
-			break;
-		    case	0x40:	/* Terminated early */
-			if ( scsistatus & 0x8 )
-		     	    action = 1;
-			else if ( (scsistatus & 7) > 2 )
-		     		action = 2;
-			     else
-		    		result = DID_TIME_OUT << 16;
-			break;
-		    case	0x80:	/* Service Required from SCSI bus */
-			if ( scsistatus & 0x8 )
-			    action = 1;
-			else
-			    action = 2;
-			break;
-		    }		/* end switch(scsistatus) */
-		outb(0,INFLED);
-		switch ( action )
-		    {
-		    case	0x02:	/* Issue an abort */
-			outb(COMMAND,INSTAT);
-			outb(1,INDATA); 	/* ABORT COMMAND */
-			result = DID_ABORT << 16;
-		    case	0x00:	/* Basically all done */
-			if ( ! in2000_SCptr )
-			    return;
-			in2000_SCptr->result = result | scsibyte;
-			SCptr = in2000_SCptr;
-			in2000_SCptr = 0;
-			if ( in2000_done )
-		     	    (*in2000_done)(SCptr);
-			break;
-		    case	0x01:	/* We need to reissue a command */
-			outb(CMDPHAS,INSTAT);
-			switch ( scsistatus & 7 )
-			    {
-			    case	0:	/* Data out phase */
-		    	    case	1:	/* Data in phase */
-		    	    case	4:	/* Unspec info out phase */
-		    	    case	5:	/* Unspec info in phase */
-		    	    case	6:	/* Message in phase */
-		    	    case	7:	/* Message in phase */
-				outb(0x41,INDATA); /* rdy to disconn */
-				break;
-		    	    case	2:	/* command phase */
-				outb(0x30,INDATA); /* rdy to send cmd bytes */
-				break;
-		    	    case	3:	/* status phase */
-				outb(0x45,INDATA); /* To go to status phase,*/
-				outb(TXCNTH,INSTAT); /* elim. data, autoinc */
-				outb(0,INDATA);
-				outb(0,INDATA);
-				outb(0,INDATA);
-				in2000_datalen = 0;
-				in2000_dataptr = 0;
-				break;
-			    }	/* end switch(scsistatus) */
-			outb(COMMAND,INSTAT);
-			outb(8,INDATA);	 /* RESTART THE COMMAND */
-			break;
-		    case	0x03:	/* Finish up a Card Reset */
-			outb(TIMEOUT,INSTAT);	/* I got these values */
-						/* by reverse Engineering */
-			outb(IN2000_TMOUT,INDATA); /* the Always' bios. */
-			outb(CONTROL,INSTAT);
-			outb(0,INDATA);
-			outb(SYNCTXR,INSTAT);
-			outb(0x40,INDATA);	/* async, 4 cyc xfer per. */
-			break;
-		    }		/* end switch(action) */
-	    }			/* end if auxstatus for WD int */
-	}			/* end while intrpt active */
-}
-
-int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
-{
-    unchar direction;
-    unchar *cmd = (unchar *) SCpnt->cmnd;
-    unchar target = SCpnt->target;
-    void *buff = SCpnt->request_buffer;
-    unsigned long flags;
-    int bufflen = SCpnt->request_bufflen;
-    int timeout, size, loop;
-    int i;
+ * This routine attempts to start a scsi command. If the host_card is
+ * already connected, we give up immediately. Otherwise, look through
+ * the input_Q, using the first command we find that's intended
+ * for a currently non-busy target/lun.
+ */
+void in2000_execute (struct Scsi_Host *instance)
+{
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *cmd, *prev;
+unsigned long flags;
+int i;
+unsigned short *sp;
+unsigned short f;
+unsigned short flushbuf[16];
 
-    /*
-     * This SCSI command has no data phase, but unfortunately the mid-level
-     * SCSI drivers ask for 256 bytes of data xfer.  Our card hangs if you
-     * do this, so we protect against it here.  It would be nice if the mid-
-     * level could be changed, but who knows if that would break other host
-     * adapter drivers.
-     */
-    if ( *cmd == TEST_UNIT_READY )
-	bufflen = 0;
+
+   save_flags(flags);
+   cli();
+   hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+DB(DB_EXECUTE,printk("EX("))
+
+   if (hostdata->selecting || hostdata->connected) {
+
+DB(DB_EXECUTE,printk(")EX-0 "))
+
+      restore_flags(flags);
+      return;
+      }
 
     /*
-     * What it looks like.  Boy did I get tired of reading its output.
-     */
-    if (*cmd == READ_10 || *cmd == WRITE_10) {
-	i = xscsi2int((cmd+1));
-    } else if (*cmd == READ_6 || *cmd == WRITE_6) {
-	i = scsi2int((cmd+1));
-    } else {
-	i = -1;
-    }
-#ifdef DEBUG
-    printk("in2000qcmd: pos %d len %d ", i, bufflen);
-    printk("scsi cmd:");
-    for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
-    printk("\n");
-#endif
-    direction = 1;	/* assume for most commands */
-    if (*cmd == WRITE_10 || *cmd == WRITE_6)
-	direction = 0;
-    size = SCpnt->cmd_len;	/* CDB length */ 
-    /*
-     * Setup our current pointers
-     * This is where you would allocate a control structure in a queue,
-     * If you were going to upgrade this to do multiple issue.
-     * Note that datalen and dataptr exist because we can change the
-     * values during the course of the operation, while managing the
-     * FIFO.
-     * Note the nasty little first clause.  In theory, the mid-level
-     * drivers should never hand us more than one command at a time,
-     * but just in case someone gets cute in configuring the driver,
-     * we'll protect them, although not very politely.
-     */
-    if ( in2000_SCptr )
-    {
-	printk("in2000_queue_command waiting for free command block!\n");
-	while ( in2000_SCptr )
-	    barrier();
-    }
-    for ( timeout = jiffies + 5; timeout > jiffies; )
-    {
-	if ( ! ( inb(INSTAT) & 0xb0 ) )
-	{
-	    timeout = 0;
-	    break;
-	}
-	else
-	{
-	    inb(INSTAT);
-	    outb(SCSIST,INSTAT);
-	    inb(INDATA);
-	    outb(TARGETU,INSTAT); 	/* then autoinc */
-	    inb(INDATA);
-	    inb(INDATA);
-	}
-    }
-    if ( timeout )
-    {
-	printk("in2000_queue_command timeout!\n");
-	SCpnt->result = DID_TIME_OUT << 16;
-	(*done)(SCpnt);
-	return 1;
-    }
-    /* Added for scatter-gather support */
-    in2000_nsegment = SCpnt->use_sg;
-    in2000_current_segment = 0;
-    if(SCpnt->use_sg){
-      in2000_scatter = (struct scatterlist *) buff;
-      in2000_datalen = in2000_scatter->length;
-      in2000_dataptr = (unsigned short*)in2000_scatter->address;
-    } else {
-      in2000_scatter = NULL;
-      in2000_datalen = bufflen;
-      in2000_dataptr = (unsigned short*) buff;
-    };
-    in2000_done = done;
-    in2000_SCptr = SCpnt;
-    /*
-     * Write the CDB to the card, then the LUN, the length, and the target.
-     */
-    outb(TOTSECT, INSTAT);	/* start here then autoincrement */
-    for ( loop=0; loop < size; loop++ )
-	outb(cmd[loop],INDATA);
-    outb(TARGETU,INSTAT);
-    outb(SCpnt->lun & 7,INDATA);
-    SCpnt->host_scribble = NULL;
-    outb(TXCNTH,INSTAT);	/* then autoincrement */
-    outb(bufflen>>16,INDATA);
-    outb(bufflen>>8,INDATA);
-    outb(bufflen,INDATA);
-    outb(target&7,INDATA);
-    /*
-     * Set up the FIFO
-     */
-    save_flags(flags);
-    cli();		/* so FIFO init waits till WD set */
-    outb(0,INFRST);
-    if ( direction == 1 )
-    {
-	in2000_datawrite = 0;
-	outb(0,INFWRT);
-    }
-    else
-    {
-	in2000_datawrite = 1;
-	for ( loop=16; --loop; ) /* preload the outgoing fifo */
-	    {
-		outw(*in2000_dataptr++,INFIFO);
-		if(in2000_datalen > 0) in2000_datalen-=2;
-	    }
-    }
-    ficmsk = 0xff;
-    /*
-     * Start it up
+     * Search through the input_Q for a command destined
+     * for an idle target/lun.
      */
-    outb(CONTROL,INSTAT);	/* WD BUS Mode */
-    outb(0x4C,INDATA);
-    if ( in2000_datalen )		/* if data xfer cmd */
-	outb(0,ININTR);		/* Enable FIFO intrpt some boards? */
-    outb(COMMAND,INSTAT);
-    outb(0,INNLED);
-    outb(8,INDATA);		/* Select w/ATN & Transfer */
-    restore_flags(flags);			/* let the intrpt rip */
-    return 0;
+
+   cmd = (Scsi_Cmnd *)hostdata->input_Q;
+   prev = 0;
+   while (cmd) {
+      if (!(hostdata->busy[cmd->target] & (1 << cmd->lun)))
+         break;
+      prev = cmd;
+      cmd = (Scsi_Cmnd *)cmd->host_scribble;
+      }
+
+   /* quit if queue empty or all possible targets are busy */
+
+   if (!cmd) {
+
+DB(DB_EXECUTE,printk(")EX-1 "))
+
+      restore_flags(flags);
+      return;
+      }
+
+   /*  remove command from queue */
+   
+   if (prev)
+      prev->host_scribble = cmd->host_scribble;
+   else
+      hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
+
+/*
+ * Start the selection process
+ */
+
+   if (IS_DIR_OUT(cmd))
+      write_3393(hostdata,WD_DESTINATION_ID, cmd->target);
+   else
+      write_3393(hostdata,WD_DESTINATION_ID, cmd->target | DSTID_DPD);
+
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ *   DIS_NEVER (this holds for tape drives also), and ALWAYS
+ *   disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ *   for a different target/lun. In this case, the other commands
+ *   should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+   cmd->SCp.phase = 0;  /* assume no disconnect */
+   if (hostdata->disconnect == DIS_NEVER)
+      goto no;
+   if (hostdata->disconnect == DIS_ALWAYS)
+      goto yes;
+   if (cmd->device->type == 1)   /* tape drive? */
+      goto yes;
+   if (hostdata->disconnected_Q) /* other commands disconnected? */
+      goto yes;
+   if (!(hostdata->input_Q))     /* input_Q empty? */
+      goto no;
+   for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+         prev=(Scsi_Cmnd *)prev->host_scribble) {
+      if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) {
+         for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+               prev=(Scsi_Cmnd *)prev->host_scribble)
+            prev->SCp.phase = 1;
+         goto yes;
+         }
+      }
+   goto no;
+
+yes:
+   cmd->SCp.phase = 1;
+
+#ifdef PROC_INTERFACE
+   disc_allowed_total++;
+#endif
+
+no:
+   write_3393(hostdata,WD_SOURCE_ID,((cmd->SCp.phase)?SRCID_ER:0));
+
+   write_3393(hostdata,WD_TARGET_LUN, cmd->lun);
+   write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
+   hostdata->busy[cmd->target] |= (1 << cmd->lun);
+
+   if ((hostdata->level2 <= L2_NONE) ||
+       (hostdata->sync_stat[cmd->target] == SS_UNSET)) {
+
+         /*
+          * Do a 'Select-With-ATN' command. This will end with
+          * one of the following interrupts:
+          *    CSR_RESEL_AM:  failure - can try again later.
+          *    CSR_TIMEOUT:   failure - give up.
+          *    CSR_SELECT:    success - proceed.
+          */
+
+      hostdata->selecting = cmd;
+
+/* Every target has its own synchronous transfer setting, kept in
+ * the sync_xfer array, and a corresponding status byte in sync_stat[].
+ * Each target's sync_stat[] entry is initialized to SS_UNSET, and its
+ * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
+ * means that the parameters are undetermined as yet, and that we
+ * need to send an SDTR message to this device after selection is
+ * complete. We set SS_FIRST to tell the interrupt routine to do so,
+ * unless we don't want to even _try_ synchronous transfers: In this
+ * case we set SS_SET to make the defaults final.
+ */
+      if (hostdata->sync_stat[cmd->target] == SS_UNSET) {
+         if (hostdata->sync_off & (1 << cmd->target))
+            hostdata->sync_stat[cmd->target] = SS_SET;
+         else
+            hostdata->sync_stat[cmd->target] = SS_FIRST;
+         }
+      hostdata->state = S_SELECTING;
+      write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */
+      write_3393_cmd(hostdata,WD_CMD_SEL_ATN);
+      }
+
+   else {
+
+         /*
+          * Do a 'Select-With-ATN-Xfer' command. This will end with
+          * one of the following interrupts:
+          *    CSR_RESEL_AM:  failure - can try again later.
+          *    CSR_TIMEOUT:   failure - give up.
+          *    anything else: success - proceed.
+          */
+
+      hostdata->connected = cmd;
+      write_3393(hostdata,WD_COMMAND_PHASE, 0);
+
+   /* copy command_descriptor_block into WD chip
+    * (take advantage of auto-incrementing)
+    */
+
+      write1_io(WD_CDB_1, IO_WD_ADDR);
+      for (i=0; i<cmd->cmd_len; i++)
+         write1_io(cmd->cmnd[i], IO_WD_DATA);
+
+   /* The wd33c93 only knows about Group 0, 1, and 5 commands when
+    * it's doing a 'select-and-transfer'. To be safe, we write the
+    * size of the CDB into the OWN_ID register for every case. This
+    * way there won't be problems with vendor-unique, audio, etc.
+    */
+
+      write_3393(hostdata, WD_OWN_ID, cmd->cmd_len);
+
+   /* When doing a non-disconnect command, we can save ourselves a DATA
+    * phase interrupt later by setting everything up now. With writes we
+    * need to pre-fill the fifo; if there's room for the 32 flush bytes,
+    * put them in there too - that'll avoid a fifo interrupt. Reads are
+    * somewhat simpler.
+    * KLUDGE NOTE: It seems that you can't completely fill the fifo here:
+    * This results in the IO_FIFO_COUNT register rolling over to zero,
+    * and apparently the gate array logic sees this as empty, not full,
+    * so the 3393 chip is never signalled to start reading from the
+    * fifo. Or maybe it's seen as a permanent fifo interrupt condition.
+    * Regardless, we fix this by temporarily pretending that the fifo
+    * is 16 bytes smaller. (I see now that the old driver has a comment
+    * about "don't fill completely" in an analogous place - must be the
+    * same deal.) This results in CDROM, swap partitions, and tape drives
+    * needing an extra interrupt per write command - I think we can live
+    * with that!
+    */
+
+      if (!(cmd->SCp.phase)) {
+         write_3393_count(hostdata, cmd->SCp.this_residual);
+         write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+         write1_io(0, IO_FIFO_WRITE);  /* clear fifo counter, write mode */
+
+         if (IS_DIR_OUT(cmd)) {
+            hostdata->fifo = FI_FIFO_WRITING;
+            if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) )
+               i = IN2000_FIFO_SIZE - 16;
+            cmd->SCp.have_data_in = i;    /* this much data in fifo */
+            i >>= 1;                      /* Gulp. Assumimg modulo 2. */
+            sp = (unsigned short *)cmd->SCp.ptr;
+            f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+            FAST_WRITE2_IO();
+#else
+            while (i--)
+               write2_io(*sp++,IO_FIFO);
+
+#endif
+
+      /* Is there room for the flush bytes? */
+
+            if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) {
+               sp = flushbuf;
+               i = 16;
+
+#ifdef FAST_WRITE_IO
+
+               FAST_WRITE2_IO();
+#else
+               while (i--)
+                  write2_io(0,IO_FIFO);
+
+#endif
+
+               }
+            }
+
+         else {
+            write1_io(0, IO_FIFO_READ);   /* put fifo in read mode */
+            hostdata->fifo = FI_FIFO_READING;
+            cmd->SCp.have_data_in = 0;    /* nothing transfered yet */
+            }
+
+         }
+      else {
+         write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */
+         }
+      hostdata->state = S_RUNNING_LEVEL2;
+      write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+      }
+
+   /*
+    * Since the SCSI bus can handle only 1 connection at a time,
+    * we get out of here now. If the selection fails, or when
+    * the command disconnects, we'll come back to this routine
+    * to search the input_Q again...
+    */
+      
+DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
+
+   restore_flags(flags);
 }
 
-static volatile int internal_done_flag = 0;
-static volatile int internal_done_errcode = 0;
 
-static void internal_done(Scsi_Cmnd * SCpnt)
+
+void transfer_pio(uchar *buf, int cnt,
+                  int data_in_dir, struct IN2000_hostdata *hostdata)
 {
-    internal_done_errcode = SCpnt->result;
-    ++internal_done_flag;
+uchar asr;
+
+DB(DB_TRANSFER,printk("(%p,%d,%s)",buf,cnt,data_in_dir?"in":"out"))
+
+   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+   write_3393_count(hostdata,cnt);
+   write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+   if (data_in_dir) {
+      do {
+         asr = READ_AUX_STAT();
+         if (asr & ASR_DBR)
+            *buf++ = read_3393(hostdata,WD_DATA);
+         } while (!(asr & ASR_INT));
+      }
+   else {
+      do {
+         asr = READ_AUX_STAT();
+         if (asr & ASR_DBR)
+            write_3393(hostdata,WD_DATA, *buf++);
+         } while (!(asr & ASR_INT));
+      }
+
+   /* Note: we are returning with the interrupt UN-cleared.
+   * Since (presumably) an entire I/O operation has
+   * completed, the bus phase is probably different, and
+   * the interrupt routine will discover this when it
+   * responds to the uncleared int.
+   */
+
 }
 
-int in2000_command(Scsi_Cmnd * SCpnt)
+
+
+void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
 {
-    in2000_queuecommand(SCpnt, internal_done);
+struct IN2000_hostdata *hostdata;
+unsigned short *sp;
+unsigned short f;
+int i;
+
+   hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
+
+/* Normally, you'd expect 'this_residual' to be non-zero here.
+ * In a series of scatter-gather transfers, however, this
+ * routine will usually be called with 'this_residual' equal
+ * to 0 and 'buffers_residual' non-zero. This means that a
+ * previous transfer completed, clearing 'this_residual', and
+ * now we need to setup the next scatter-gather buffer as the
+ * source or destination for THIS transfer.
+ */
+   if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+      ++cmd->SCp.buffer;
+      --cmd->SCp.buffers_residual;
+      cmd->SCp.this_residual = cmd->SCp.buffer->length;
+      cmd->SCp.ptr = cmd->SCp.buffer->address;
+      }
+
+/* Set up hardware registers */
+
+   write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
+   write_3393_count(hostdata,cmd->SCp.this_residual);
+   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+   write1_io(0,IO_FIFO_WRITE); /* zero counter, assume write */
+
+/* Reading is easy. Just issue the command and return - we'll
+ * get an interrupt later when we have actual data to worry about.
+ */
+
+   if (data_in_dir) {
+      write1_io(0,IO_FIFO_READ);
+      if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+         write_3393(hostdata,WD_COMMAND_PHASE,0x45);
+         write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+         hostdata->state = S_RUNNING_LEVEL2;
+         }
+      else
+         write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+      hostdata->fifo = FI_FIFO_READING;
+      cmd->SCp.have_data_in = 0;
+      return;
+      }
+
+/* Writing is more involved - we'll start the WD chip and write as
+ * much data to the fifo as we can right now. Later interrupts will
+ * write any bytes that don't make it at this stage.
+ */
+
+   if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+      write_3393(hostdata,WD_COMMAND_PHASE,0x45);
+      write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+      hostdata->state = S_RUNNING_LEVEL2;
+      }
+   else
+      write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+   hostdata->fifo = FI_FIFO_WRITING;
+   sp = (unsigned short *)cmd->SCp.ptr;
+
+   if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE)
+      i = IN2000_FIFO_SIZE;
+   cmd->SCp.have_data_in = i;
+   i >>= 1;    /* Gulp. We assume this_residual is modulo 2 */
+   f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+   FAST_WRITE2_IO();
+#else
+   while (i--)
+      write2_io(*sp++,IO_FIFO);
+
+#endif
 
-    while (!internal_done_flag);
-    internal_done_flag = 0;
-    return internal_done_errcode;
 }
 
-int in2000_detect(Scsi_Host_Template * tpnt)
+
+/* It appears that the Linux interrupt dispatcher calls this
+ * function in a non-reentrant fashion. What that means to us
+ * is that we can use an SA_INTERRUPT type of interrupt (which
+ * is faster), and do an sti() right away to let timer, serial,
+ * etc. ints happen.
+ *
+ * WHOA! Wait a minute, pardner! Does this hold when more than
+ * one card has been detected?? I doubt it. Maybe better
+ * re-think the multiple card capability....
+ */
+
+#if LINUX_VERSION_CODE >= 0x010346   /* 1.3.70 */
+void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
+#else
+void in2000_intr (int irqnum, struct pt_regs *ptregs)
+#endif
 {
-/* Order chosen to reduce conflicts with some multi-port serial boards */
-    int base_tab[] = { 0x220,0x200,0x110,0x100 };
-    int int_tab[] = { 15,14,11,10 };
-    struct Scsi_Host * shpnt;
-    int loop, tmp;
-
-    DEB(printk("in2000_detect: \n"));
-
-    tpnt->proc_dir = &proc_scsi_in2000;
-
-    for ( loop=0; loop < 4; loop++ )
-    {
-	base = base_tab[loop];
-	if ( in2000_test_port(loop))  break;
-    }
-    if ( loop == 4 )
-	return 0;
-
-  /* Read the dip switch values again for miscellaneous checking and
-     informative messages */
-  tmp = inb(INFLED);
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *patch, *cmd;
+unsigned long flags;
+uchar asr, sr, phs, id, lun, *ucp, msg;
+int i,j;
+unsigned long length;
+unsigned short *sp;
+unsigned short f;
+
+   for (instance = instance_list; instance; instance = instance->next) {
+      if (instance->irq == irqnum)
+         break;
+      }
+   if (!instance) {
+      printk("*** Hmm... interrupts are screwed up! ***\n");
+      return;
+      }
+   hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+/* OK - it should now be safe to re-enable system interrupts */
+
+   save_flags(flags);
+   sti();
+
+/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
+ * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
+ * with a big logic array, so it's a little different than what you might
+ * expect). As far as I know, there's no reason that BOTH can't be active
+ * at the same time, but there's a problem: while we can read the 3393
+ * to tell if _it_ wants an interrupt, I don't know of a way to ask the
+ * fifo the same question. The best we can do is check the 3393 and if
+ * it _isn't_ the source of the interrupt, then we can be pretty sure
+ * that the fifo is the culprit.
+ *  UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the
+ *          IO_FIFO_COUNT register mirrors the fifo interrupt state. I
+ *          assume that bit clear means interrupt active. As it turns
+ *          out, the driver really doesn't need to check for this after
+ *          all, so my remarks above about a 'problem' can safely be
+ *          ignored. The way the logic is set up, there's no advantage
+ *          (that I can see) to worrying about it.
+ *
+ * It seems that the fifo interrupt signal is negated when we extract
+ * bytes during read or write bytes during write.
+ *  - fifo will interrupt when data is moving from it to the 3393, and
+ *    there are 31 (or less?) bytes left to go. This is sort of short-
+ *    sighted: what if you don't WANT to do more? In any case, our
+ *    response is to push more into the fifo - either actual data or
+ *    dummy bytes if need be. Note that we apparently have to write at
+ *    least 32 additional bytes to the fifo after an interrupt in order
+ *    to get it to release the ones it was holding on to - writing fewer
+ *    than 32 will result in another fifo int.
+ *  UPDATE: Again, info from Bill Earnest makes this more understandable:
+ *          32 bytes = two counts of the fifo counter register. He tells
+ *          me that the fifo interrupt is a non-latching signal derived
+ *          from a straightforward boolean interpretation of the 7
+ *          highest bits of the fifo counter and the fifo-read/fifo-write
+ *          state. Who'd a thought?
+ */
+
+   write1_io(0, IO_LED_ON);
+   asr = READ_AUX_STAT();
+   if (!(asr & ASR_INT)) {    /* no WD33c93 interrupt? */
+
+/* Ok. This is definitely a FIFO-only interrupt.
+ *
+ * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read,
+ * maybe more to come from the SCSI bus. Read as many as we can out of the
+ * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and
+ * update have_data_in afterwards.
+ *
+ * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move
+ * into the WD3393 chip (I think the interrupt happens when there are 31
+ * bytes left, but it may be fewer...). The 3393 is still waiting, so we
+ * shove some more into the fifo, which gets things moving again. If the
+ * original SCSI command specified more than 2048 bytes, there may still
+ * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]).
+ * Don't forget to update have_data_in. If we've already written out the
+ * entire buffer, feed 32 dummy bytes to the fifo - they're needed to
+ * push out the remaining real data.
+ *    (Big thanks to Bill Earnest for getting me out of the mud in here.)
+ */
+
+      cmd = (Scsi_Cmnd *)hostdata->connected;   /* assume we're connected */
+CHECK_NULL(cmd,"fifo_int")
+
+      if (hostdata->fifo == FI_FIFO_READING) {
+
+DB(DB_FIFO,printk("{R:%02x} ",read1_io(IO_FIFO_COUNT)))
+
+         sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+         i = read1_io(IO_FIFO_COUNT) & 0xfe;
+         i <<= 2;    /* # of words waiting in the fifo */
+         f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+         FAST_READ2_IO();
+#else
+         while (i--)
+            *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+         i = sp - (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+         i <<= 1;
+         cmd->SCp.have_data_in += i;
+         }
+
+      else if (hostdata->fifo == FI_FIFO_WRITING) {
+
+DB(DB_FIFO,printk("{W:%02x} ",read1_io(IO_FIFO_COUNT)))
+
+/* If all bytes have been written to the fifo, flush out the stragglers.
+ * Note that while writing 16 dummy words seems arbitrary, we don't
+ * have another choice that I can see. What we really want is to read
+ * the 3393 transfer count register (that would tell us how many bytes
+ * needed flushing), but the TRANSFER_INFO command hasn't completed
+ * yet (not enough bytes!) and that register won't be accessible. So,
+ * we use 16 words - a number obtained through trial and error.
+ *  UPDATE: Bill says this is exactly what Always does, so there.
+ *          More thanks due him for help in this section.
+ */
+
+         if (cmd->SCp.this_residual == cmd->SCp.have_data_in) {
+            i = 16;
+            while (i--)          /* write 32 dummy bytes */
+               write2_io(0,IO_FIFO);
+            }
+
+/* If there are still bytes left in the SCSI buffer, write as many as we
+ * can out to the fifo.
+ */
+
+         else {
+            sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+            i = cmd->SCp.this_residual - cmd->SCp.have_data_in;   /* bytes yet to go */
+            j = read1_io(IO_FIFO_COUNT) & 0xfe;
+            j <<= 2;    /* how many words the fifo has room for */
+            if ((j << 1) > i)
+               j = (i >> 1);
+            while (j--)
+               write2_io(*sp++,IO_FIFO);
+
+            i = sp - (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+            i <<= 1;
+            cmd->SCp.have_data_in += i;
+            }
+         }
+
+      else {
+            printk("*** Spurious FIFO interrupt ***");
+            }
+
+      write1_io(0, IO_LED_OFF);
+      restore_flags(flags);
+      return;
+      }
+
+/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt
+ * may also be asserted, but we don't bother to check it: we get more
+ * detailed info from FIFO_READING and FIFO_WRITING (see below).
+ */
+
+   cmd = (Scsi_Cmnd *)hostdata->connected;   /* assume we're connected */
+   sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear the interrupt */
+   phs = read_3393(hostdata,WD_COMMAND_PHASE);
+
+   if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
+      printk("\nNR:wd-intr-1\n");
+      write1_io(0, IO_LED_OFF);
+      restore_flags(flags);
+      return;
+      }
+
+DB(DB_INTR,printk("{%02x:%02x-",asr,sr))
+
+/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is
+ * guarenteed to be in response to the completion of the transfer.
+ * If we were reading, there's probably data in the fifo that needs
+ * to be copied into RAM - do that here. Also, we have to update
+ * 'this_residual' and 'ptr' based on the contents of the
+ * TRANSFER_COUNT register, in case the device decided to do an
+ * intermediate disconnect (a device may do this if it has to
+ * do a seek,  or just to be nice and let other devices have
+ * some bus time during long transfers).
+ * After doing whatever is necessary with the fifo, we go on and
+ * service the WD3393 interrupt normally.
+ */
+
+   if (hostdata->fifo == FI_FIFO_READING) {
+
+/* buffer index = start-of-buffer + #-of-bytes-already-read */
+
+      sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+
+/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */
+
+      i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in;
+      i >>= 1;    /* Gulp. We assume this will always be modulo 2 */
+      f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+      FAST_READ2_IO();
+#else
+      while (i--)
+         *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+      hostdata->fifo = FI_FIFO_UNUSED;
+      length = cmd->SCp.this_residual;
+      cmd->SCp.this_residual = read_3393_count(hostdata);
+      cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+DB(DB_TRANSFER,printk("(%p,%d)",cmd->SCp.ptr,cmd->SCp.this_residual))
+
+      }
+
+   else if (hostdata->fifo == FI_FIFO_WRITING) {
+      hostdata->fifo = FI_FIFO_UNUSED;
+      length = cmd->SCp.this_residual;
+      cmd->SCp.this_residual = read_3393_count(hostdata);
+      cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+DB(DB_TRANSFER,printk("(%p,%d)",cmd->SCp.ptr,cmd->SCp.this_residual))
+
+      }
+
+/* Respond to the specific WD3393 interrupt - there are quite a few! */
+
+   switch (sr) {
+
+      case CSR_TIMEOUT:
+DB(DB_INTR,printk("TIMEOUT"))
+
+         cli();
+         if (hostdata->state == S_RUNNING_LEVEL2)
+            hostdata->connected = NULL;
+         else {
+            cmd = (Scsi_Cmnd *)hostdata->selecting;   /* get a valid cmd */
+CHECK_NULL(cmd,"csr_timeout")
+            hostdata->selecting = NULL;
+            }
+
+         cmd->result = DID_NO_CONNECT << 16;
+         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+         hostdata->state = S_UNCONNECTED;
+         cmd->scsi_done(cmd);
+
+/* We are not connected to a target - check to see if there
+ * are commands waiting to be executed.
+ */
+
+         sti();
+         in2000_execute(instance);
+         break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+      case CSR_SELECT:
+         cli();
+DB(DB_INTR,printk("SELECT"))
+         hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting;
+CHECK_NULL(cmd,"csr_select")
+         hostdata->selecting = NULL;
+
+      /* construct an IDENTIFY message with correct disconnect bit */
+
+         hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->lun);
+         if (cmd->SCp.phase)
+            hostdata->outgoing_msg[0] |= 0x40;
+
+         if (hostdata->sync_stat[cmd->target] == SS_FIRST) {
+#ifdef SYNC_DEBUG
+printk(" sending SDTR ");
+#endif
+
+            hostdata->sync_stat[cmd->target] = SS_WAITING;
+
+      /* tack on a 2nd message to ask about synchronous transfers */
+
+            hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
+            hostdata->outgoing_msg[2] = 3;
+            hostdata->outgoing_msg[3] = EXTENDED_SDTR;
+            hostdata->outgoing_msg[4] = OPTIMUM_SX_PER/4;
+            hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
+            hostdata->outgoing_len = 6;
+            }
+         else
+            hostdata->outgoing_len = 1;
+
+         hostdata->state = S_CONNECTED;
+         break;
+
+
+      case CSR_XFER_DONE|PHS_DATA_IN:
+      case CSR_UNEXP    |PHS_DATA_IN:
+      case CSR_SRV_REQ  |PHS_DATA_IN:
+DB(DB_INTR,printk("IN-%d.%d",cmd->SCp.this_residual,cmd->SCp.buffers_residual))
+         transfer_bytes(cmd, DATA_IN_DIR);
+         if (hostdata->state != S_RUNNING_LEVEL2)
+            hostdata->state = S_CONNECTED;
+         break;
+
+
+      case CSR_XFER_DONE|PHS_DATA_OUT:
+      case CSR_UNEXP    |PHS_DATA_OUT:
+      case CSR_SRV_REQ  |PHS_DATA_OUT:
+DB(DB_INTR,printk("OUT-%d.%d",cmd->SCp.this_residual,cmd->SCp.buffers_residual))
+         transfer_bytes(cmd, DATA_OUT_DIR);
+         if (hostdata->state != S_RUNNING_LEVEL2)
+            hostdata->state = S_CONNECTED;
+         break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+      case CSR_XFER_DONE|PHS_COMMAND:
+      case CSR_UNEXP    |PHS_COMMAND:
+      case CSR_SRV_REQ  |PHS_COMMAND:
+DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid))
+         transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
+         hostdata->state = S_CONNECTED;
+         break;
+
+
+      case CSR_XFER_DONE|PHS_STATUS:
+      case CSR_UNEXP    |PHS_STATUS:
+      case CSR_SRV_REQ  |PHS_STATUS:
+DB(DB_INTR,printk("STATUS"))
+
+         cmd->SCp.Status = read_1_byte(hostdata);
+         if (hostdata->level2 >= L2_BASIC) {
+            sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
+            hostdata->state = S_RUNNING_LEVEL2;
+            write_3393(hostdata,WD_COMMAND_PHASE, 0x50);
+            write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+            }
+         else {
+DB(DB_INTR,printk("=%02x",cmd->SCp.Status))
+            hostdata->state = S_CONNECTED;
+            }
+         break;
+
+
+      case CSR_XFER_DONE|PHS_MESS_IN:
+      case CSR_UNEXP    |PHS_MESS_IN:
+      case CSR_SRV_REQ  |PHS_MESS_IN:
+DB(DB_INTR,printk("MSG_IN="))
+
+         cli();
+         msg = read_1_byte(hostdata);
+         sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
+
+         hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
+         if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
+            msg = EXTENDED_MESSAGE;
+         else
+            hostdata->incoming_ptr = 0;
+
+         cmd->SCp.Message = msg;
+         switch (msg) {
+
+            case COMMAND_COMPLETE:
+DB(DB_INTR,printk("CCMP-%ld",cmd->pid))
+               write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+               hostdata->state = S_PRE_CMP_DISC;
+               break;
+
+            case SAVE_POINTERS:
+DB(DB_INTR,printk("SDP"))
+               write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+               hostdata->state = S_CONNECTED;
+               break;
+
+            case RESTORE_POINTERS:
+DB(DB_INTR,printk("RDP"))
+               if (hostdata->level2 >= L2_BASIC) {
+                  write_3393(hostdata,WD_COMMAND_PHASE, 0x45);
+                  write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+                  hostdata->state = S_RUNNING_LEVEL2;
+                  }
+               else {
+                  write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+                  hostdata->state = S_CONNECTED;
+                  }
+               break;
+
+            case DISCONNECT:
+DB(DB_INTR,printk("DIS"))
+               cmd->device->disconnect = 1;
+               write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+               hostdata->state = S_PRE_TMP_DISC;
+               break;
+
+            case MESSAGE_REJECT:
+DB(DB_INTR,printk("REJ"))
+#ifdef SYNC_DEBUG
+printk("-REJ-");
+#endif
+               if (hostdata->sync_stat[cmd->target] == SS_WAITING)
+                  hostdata->sync_stat[cmd->target] = SS_SET;
+               write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+               hostdata->state = S_CONNECTED;
+               break;
+
+            case EXTENDED_MESSAGE:
+DB(DB_INTR,printk("EXT"))
+
+               ucp = hostdata->incoming_msg;
+
+#ifdef SYNC_DEBUG
+printk("%02x",ucp[hostdata->incoming_ptr]);
+#endif
+         /* Is this the last byte of the extended message? */
+
+               if ((hostdata->incoming_ptr >= 2) &&
+                   (hostdata->incoming_ptr == (ucp[1] + 1))) {
+
+                  switch (ucp[2]) {   /* what's the EXTENDED code? */
+                     case EXTENDED_SDTR:
+                        id = calc_sync_xfer(ucp[3],ucp[4]);
+                        if (hostdata->sync_stat[cmd->target] != SS_WAITING) {
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
+                           write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+                           hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+                           hostdata->outgoing_msg[1] = 3;
+                           hostdata->outgoing_msg[2] = EXTENDED_SDTR;
+                           hostdata->outgoing_msg[3] = hostdata->default_sx_per/4;
+                           hostdata->outgoing_msg[4] = 0;
+                           hostdata->outgoing_len = 5;
+                           hostdata->sync_xfer[cmd->target] =
+                                       calc_sync_xfer(hostdata->default_sx_per/4,0);
+                           }
+                        else {
+                           hostdata->sync_xfer[cmd->target] = id;
+                           }
+#ifdef SYNC_DEBUG
+printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
+#endif
+                        hostdata->sync_stat[cmd->target] = SS_SET;
+                        write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+                        hostdata->state = S_CONNECTED;
+                        break;
+                     case EXTENDED_WDTR:
+                        write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+                        printk("sending WDTR ");
+                        hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+                        hostdata->outgoing_msg[1] = 2;
+                        hostdata->outgoing_msg[2] = EXTENDED_WDTR;
+                        hostdata->outgoing_msg[3] = 0;   /* 8 bit transfer width */
+                        hostdata->outgoing_len = 4;
+                        write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+                        hostdata->state = S_CONNECTED;
+                        break;
+                     default:
+                        write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+                        printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
+                        hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+                        hostdata->outgoing_len = 1;
+                        write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+                        hostdata->state = S_CONNECTED;
+                        break;
+                     }
+                  hostdata->incoming_ptr = 0;
+                  }
+
+         /* We need to read more MESS_IN bytes for the extended message */
+
+               else {
+                  hostdata->incoming_ptr++;
+                  write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+                  hostdata->state = S_CONNECTED;
+                  }
+               break;
+
+            default:
+               printk("Rejecting Unknown Message(%02x) ",msg);
+               write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+               hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+               hostdata->outgoing_len = 1;
+               write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+               hostdata->state = S_CONNECTED;
+            }
+         break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+      case CSR_SEL_XFER_DONE:
+         cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+         if (phs == 0x60) {
+DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
+            cmd->SCp.Message = COMMAND_COMPLETE;
+            lun = read_3393(hostdata,WD_TARGET_LUN);
+            if (cmd->SCp.Status == GOOD)
+               cmd->SCp.Status = lun;
+            hostdata->connected = NULL;
+            if (cmd->cmnd[0] != REQUEST_SENSE)
+               cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+            else if (cmd->SCp.Status != GOOD)
+               cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+            hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+            hostdata->state = S_UNCONNECTED;
+            cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+            sti();
+            in2000_execute(instance);
+            }
+         else {
+            printk("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---",asr,sr,phs,cmd->pid);
+            }
+         break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+      case CSR_SDP:
+DB(DB_INTR,printk("SDP"))
+            hostdata->state = S_RUNNING_LEVEL2;
+            write_3393(hostdata,WD_COMMAND_PHASE, 0x41);
+            write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+         break;
+
+
+      case CSR_XFER_DONE|PHS_MESS_OUT:
+      case CSR_UNEXP    |PHS_MESS_OUT:
+      case CSR_SRV_REQ  |PHS_MESS_OUT:
+DB(DB_INTR,printk("MSG_OUT="))
+
+/* To get here, we've probably requested MESSAGE_OUT and have
+ * already put the correct bytes in outgoing_msg[] and filled
+ * in outgoing_len. We simply send them out to the SCSI bus.
+ * Sometimes we get MESSAGE_OUT phase when we're not expecting
+ * it - like when our SDTR message is rejected by a target. Some
+ * targets send the REJECT before receiving all of the extended
+ * message, and then seem to go back to MESSAGE_OUT for a byte
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
+ * NOP messages in these situations results in no harm and
+ * makes everyone happy.
+ */
+
+         if (hostdata->outgoing_len == 0) {
+            hostdata->outgoing_len = 1;
+            hostdata->outgoing_msg[0] = NOP;
+            }
+         transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len,
+                      DATA_OUT_DIR, hostdata);
+DB(DB_INTR,printk("%02x",hostdata->outgoing_msg[0]))
+         hostdata->outgoing_len = 0;
+         hostdata->state = S_CONNECTED;
+         break;
+
+ 
+      case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+         cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+         if (cmd == NULL) {
+            printk(" - Already disconnected! ");
+            hostdata->state = S_UNCONNECTED;
+            return;
+            }
+DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
+         hostdata->connected = NULL;
+         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+         hostdata->state = S_UNCONNECTED;
+         if (cmd->cmnd[0] != REQUEST_SENSE)
+            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+         else if (cmd->SCp.Status != GOOD)
+            cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+         cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+         sti();
+         in2000_execute(instance);
+         break;
+
+
+      case CSR_DISC:
+         cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+DB(DB_INTR,printk("DISC-%ld",cmd->pid))
+         if (cmd == NULL) {
+            printk(" - Already disconnected! ");
+            hostdata->state = S_UNCONNECTED;
+            }
+         switch (hostdata->state) {
+            case S_PRE_CMP_DISC:
+               hostdata->connected = NULL;
+               hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+               hostdata->state = S_UNCONNECTED;
+               if (cmd->cmnd[0] != REQUEST_SENSE)
+                  cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+               else if (cmd->SCp.Status != GOOD)
+                  cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+               cmd->scsi_done(cmd);
+               break;
+            case S_PRE_TMP_DISC:
+            case S_RUNNING_LEVEL2:
+               cmd->host_scribble = (uchar *)hostdata->disconnected_Q;
+               hostdata->disconnected_Q = cmd;
+               hostdata->connected = NULL;
+               hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_INTERFACE
+               disc_taken_total++;
+#endif
+
+               break;
+            default:
+               printk("*** Unexpected DISCONNECT interrupt! ***");
+               hostdata->state = S_UNCONNECTED;
+            }
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+         sti();
+         in2000_execute(instance);
+         break;
+
+
+      case CSR_RESEL_AM:
+DB(DB_INTR,printk("RESEL"))
+
+         cli();
+
+   /* First we have to make sure this reselection didn't */
+   /* happen during Arbitration/Selection of some other device. */
+   /* If yes, put losing command back on top of input_Q. */
+
+         if (hostdata->level2 <= L2_NONE) {
+
+            if (hostdata->selecting) {
+               cmd = (Scsi_Cmnd *)hostdata->selecting;
+               hostdata->selecting = NULL;
+               hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+               cmd->host_scribble = (uchar *)hostdata->input_Q;
+               hostdata->input_Q = cmd;
+               }
+            }
+
+         else {
+
+            if (cmd) {
+               if (phs == 0x00) {
+                  hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+                  cmd->host_scribble = (uchar *)hostdata->input_Q;
+                  hostdata->input_Q = cmd;
+                  }
+               else {
+                  printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---",asr,sr,phs);
+                  while (1)
+                     printk("\r");
+                  }
+               }
+
+            }
+
+   /* OK - find out which device reselected us. */
+
+         id = read_3393(hostdata,WD_SOURCE_ID);
+         id &= SRCID_MASK;
+
+   /* and extract the lun from the ID message. (Note that we don't
+    * bother to check for a valid message here - I guess this is
+    * not the right way to go, but....)
+    */
+
+         lun = read_3393(hostdata,WD_DATA);
+         if (hostdata->level2 < L2_RESELECT)
+            write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+         lun &= 7;
+
+   /* Now we look for the command that's reconnecting. */
+
+         cmd = (Scsi_Cmnd *)hostdata->disconnected_Q;
+         patch = NULL;
+         while (cmd) {
+            if (id == cmd->target && lun == cmd->lun)
+               break;
+            patch = cmd;
+            cmd = (Scsi_Cmnd *)cmd->host_scribble;
+            }
+
+   /* Hmm. Couldn't find a valid command.... What to do? */
+
+         if (!cmd) {
+            printk("---TROUBLE: target %d.%d not in disconnect queue---",id,lun);
+            break;
+            }
+
+   /* Ok, found the command - now start it up again. */
+
+         if (patch)
+            patch->host_scribble = cmd->host_scribble;
+         else
+            hostdata->disconnected_Q = (Scsi_Cmnd *)cmd->host_scribble;
+         hostdata->connected = cmd;
+
+   /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
+    * because these things are preserved over a disconnect.
+    * But we DO need to fix the DPD bit so it's correct for this command.
+    */
+
+         if (IS_DIR_OUT(cmd))
+            write_3393(hostdata,WD_DESTINATION_ID,cmd->target);
+         else
+            write_3393(hostdata,WD_DESTINATION_ID,cmd->target | DSTID_DPD);
+         if (hostdata->level2 >= L2_RESELECT) {
+            write_3393_count(hostdata,0); /* we want a DATA_PHASE interrupt */
+            write_3393(hostdata,WD_COMMAND_PHASE, 0x45);
+            write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+            hostdata->state = S_RUNNING_LEVEL2;
+            }
+         else
+            hostdata->state = S_CONNECTED;
+
+DB(DB_INTR,printk("-%ld",cmd->pid))
+         break;
+
+      default:
+         printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--",asr,sr,phs);
+      }
+
+   write1_io(0, IO_LED_OFF);
+   restore_flags(flags);
+
+DB(DB_INTR,printk("} "))
 
-  /* Bit 2 tells us if interrupts are disabled */
-  if ( (tmp & 0x4) == 0 ) {
-    printk("The IN-2000 is not configured for interrupt operation\n");
-    printk("Change the DIP switch settings to enable interrupt operation\n");
-  }
-
-  /* Bit 6 tells us about floppy controller */
-  printk("IN-2000 probe found floppy controller on IN-2000 ");
-  if ( (tmp & 0x40) == 0)
-    printk("enabled\n");
-  else
-    printk("disabled\n");
-
-  /* Bit 5 tells us about synch/asynch mode */
-  printk("IN-2000 probe found IN-2000 in ");
-  if ( (tmp & 0x20) == 0)
-    printk("synchronous mode\n");
-  else
-    printk("asynchronous mode\n");
-
-    irq_level = int_tab [ ((~inb(INFLED)>>3)&0x3) ];
-
-    printk("Configuring IN2000 at IO:%x, IRQ %d"
-#ifdef FAST_FIFO_IO
-		" (using fast FIFO I/O code)"
-#endif
-		"\n",base, irq_level);
-
-    outb(2,ININTR);	/* Shut off the FIFO first, so it won't ask for data.*/
-    if (request_irq(irq_level,in2000_intr_handle, 0, "in2000", NULL))
-    {
-	printk("in2000_detect: Unable to allocate IRQ.\n");
-	return 0;
-    }
-    outb(0,INFWRT);	/* read mode so WD can intrpt */
-    outb(SCSIST,INSTAT);
-    inb(INDATA);	/* free status reg, clear WD intrpt */
-    outb(OWNID,INSTAT);
-    outb(0x7,INDATA);	/* we use addr 7 */
-    outb(COMMAND,INSTAT);
-    outb(0,INDATA);	/* do chip reset */
-    shpnt = scsi_register(tpnt, 0);
-    /* Set these up so that we can unload the driver properly. */
-    shpnt->io_port = base;
-    shpnt->n_io_port = 12;
-    shpnt->irq = irq_level;
-    request_region(base, 12,"in2000");  /* Prevent other drivers from using this space */
-    return 1;
 }
 
-int in2000_abort(Scsi_Cmnd * SCpnt)
+
+
+#define RESET_CARD         0
+#define RESET_CARD_AND_BUS 1
+#define B_FLAG 0x80
+
+int reset_hardware(struct Scsi_Host *instance, int type)
 {
-    DEB(printk("in2000_abort\n"));
-    /*
-     * Ask no stupid questions, just order the abort.
-     */
-    outb(COMMAND,INSTAT);
-    outb(1,INDATA);	/* Abort Command */
-    return 0;
+struct IN2000_hostdata *hostdata;
+int qt,x;
+unsigned long flags;
+
+   hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+   write1_io(0, IO_LED_ON);
+   if (type == RESET_CARD_AND_BUS) {
+      write1_io(0,IO_CARD_RESET);
+      x = read1_io(IO_HARDWARE);
+      }
+   x = read_3393(hostdata,WD_SCSI_STATUS);   /* clear any WD intrpt */
+   write_3393(hostdata,WD_OWN_ID, instance->this_id |
+                           OWNID_EAF | OWNID_RAF | OWNID_FS_8);
+   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+   write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,
+              calc_sync_xfer(hostdata->default_sx_per/4,DEFAULT_SX_OFF));
+   save_flags(flags);
+   cli();
+   write1_io(0,IO_FIFO_WRITE);            /* clear fifo counter */
+   write1_io(0,IO_FIFO_READ);             /* start fifo out in read mode */
+   write_3393(hostdata,WD_COMMAND, WD_CMD_RESET);
+   while (!(READ_AUX_STAT() & ASR_INT))
+      ;                                   /* wait for RESET to complete */
+
+   x = read_3393(hostdata,WD_SCSI_STATUS);   /* clear interrupt */
+   restore_flags(flags);
+   write_3393(hostdata,WD_QUEUE_TAG,0xa5);   /* any random number */
+   qt = read_3393(hostdata,WD_QUEUE_TAG);
+   if (qt == 0xa5) {
+      x |= B_FLAG;
+      write_3393(hostdata,WD_QUEUE_TAG,0);
+      }
+   write_3393(hostdata,WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
+   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+   write1_io(0, IO_LED_OFF);
+   return x;
 }
 
-static inline void delay( unsigned how_long )
+
+
+#if LINUX_VERSION_CODE >= 0x010359        /* 1.3.89 */
+int in2000_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+#else
+int in2000_reset(Scsi_Cmnd *cmd)
+#endif
 {
-    unsigned long time = jiffies + how_long;
-    while (jiffies < time) ;
+unsigned long flags;
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+int x;
+
+   instance = cmd->host;
+   hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+   printk("scsi%d: Reset. ", instance->host_no);
+   save_flags(flags);
+   cli();
+
+   /* do scsi-reset here */
+
+   reset_hardware(instance, RESET_CARD_AND_BUS);
+   for (x = 0; x < 8; x++) {
+      hostdata->busy[x] = 0;
+      hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+      hostdata->sync_stat[x] = SS_UNSET;  /* using default sync values */
+      }
+   hostdata->input_Q = NULL;
+   hostdata->selecting = NULL;
+   hostdata->connected = NULL;
+   hostdata->disconnected_Q = NULL;
+   hostdata->state = S_UNCONNECTED;
+   hostdata->fifo = FI_FIFO_UNUSED;
+   hostdata->incoming_ptr = 0;
+   hostdata->outgoing_len = 0;
+
+   cmd->result = DID_RESET << 16;
+   restore_flags(flags);
+   return 0;
 }
 
-int in2000_reset(Scsi_Cmnd * SCpnt)
+
+
+int in2000_abort (Scsi_Cmnd *cmd)
 {
-    DEB(printk("in2000_reset called\n"));
-    /*
-     * Note: this is finished off by an incoming interrupt
-     */
-    outb(0,INFWRT);	/* read mode so WD can intrpt */
-    outb(SCSIST,INSTAT);
-    inb(INDATA);
-    outb(OWNID,INSTAT);
-    outb(0x7,INDATA);	/* ID=7,noadv, no parity, clk div=2 (8-10Mhz clk) */
-    outb(COMMAND,INSTAT);
-    outb(0,INDATA);	/* reset WD chip */
-    delay(2);
-#ifdef SCSI_RESET_PENDING
-    return SCSI_RESET_PENDING;
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *tmp, *prev;
+unsigned long flags;
+uchar sr, asr;
+unsigned long timeout;
+
+   save_flags (flags);
+   cli();
+
+   instance = cmd->host;
+   hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+   printk ("scsi%d: Abort-", instance->host_no);
+   printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ",
+            READ_AUX_STAT(),read_3393_count(hostdata),cmd->SCp.this_residual,cmd->SCp.buffers_residual,
+            cmd->SCp.have_data_in,read1_io(IO_FIFO_COUNT));
+
+/*
+ * Case 1 : If the command hasn't been issued yet, we simply remove it
+ *     from the inout_Q.
+ */
+
+   tmp = (Scsi_Cmnd *)hostdata->input_Q;
+   prev = 0;
+   while (tmp) {
+      if (tmp == cmd) {
+         if (prev)
+            prev->host_scribble = cmd->host_scribble;
+         cmd->host_scribble = NULL;
+         cmd->result = DID_ABORT << 16;
+         printk("scsi%d: Abort - removing command %ld from input_Q. ",
+           instance->host_no, cmd->pid);
+         cmd->scsi_done(cmd);
+         restore_flags(flags);
+         return SCSI_ABORT_SUCCESS;
+         }
+      prev = tmp;
+      tmp = (Scsi_Cmnd *)tmp->host_scribble;
+      }
+
+/*
+ * Case 2 : If the command is connected, we're going to fail the abort
+ *     and let the high level SCSI driver retry at a later time or
+ *     issue a reset.
+ *
+ *     Timeouts, and therefore aborted commands, will be highly unlikely
+ *     and handling them cleanly in this situation would make the common
+ *     case of noresets less efficient, and would pollute our code.  So,
+ *     we fail.
+ */
+
+   if (hostdata->connected == cmd) {
+
+      printk("scsi%d: Aborting connected command %ld - ",
+              instance->host_no, cmd->pid);
+
+      printk("sending wd33c93 ABORT command - ");
+      write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+      write_3393_cmd(hostdata, WD_CMD_ABORT);
+
+/* Now we have to attempt to flush out the FIFO... */
+
+      printk("flushing fifo - ");
+      timeout = 1000000;
+      do {
+         asr = READ_AUX_STAT();
+         if (asr & ASR_DBR)
+            read_3393(hostdata, WD_DATA);
+         } while (!(asr & ASR_INT) && timeout-- > 0);
+      sr = read_3393(hostdata, WD_SCSI_STATUS);
+      printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
+             asr, sr, read_3393_count(hostdata), timeout);
+
+   /*
+    * Abort command processed.
+    * Still connected.
+    * We must disconnect.
+    */
+
+      printk("sending wd33c93 DISCONNECT command - ");
+      write_3393_cmd(hostdata, WD_CMD_DISCONNECT);
+
+      timeout = 1000000;
+      asr = READ_AUX_STAT();
+      while ((asr & ASR_CIP) && timeout-- > 0)
+         asr = READ_AUX_STAT();
+      sr = read_3393(hostdata, WD_SCSI_STATUS);
+      printk("asr=%02x, sr=%02x.",asr,sr);
+
+      hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+      hostdata->connected = NULL;
+      hostdata->state = S_UNCONNECTED;
+      cmd->result = DID_ABORT << 16;
+      cmd->scsi_done(cmd);
+
+/*      sti();*/
+      in2000_execute (instance);
+
+      restore_flags(flags);
+      return SCSI_ABORT_SUCCESS;
+      }
+
+/*
+ * Case 3: If the command is currently disconnected from the bus,
+ * we're not going to expend much effort here: Let's just return
+ * an ABORT_SNOOZE and hope for the best...
+ */
+
+   for (tmp=(Scsi_Cmnd *)hostdata->disconnected_Q; tmp;
+         tmp=(Scsi_Cmnd *)tmp->host_scribble)
+      if (cmd == tmp) {
+         restore_flags(flags);
+         printk("Sending ABORT_SNOOZE. ");
+         return SCSI_ABORT_SNOOZE;
+         }
+
+/*
+ * Case 4 : If we reached this point, the command was not found in any of
+ *     the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case somethign really
+ * broke.
+ */
+
+/*   sti();*/
+   in2000_execute (instance);
+
+   restore_flags(flags);
+   printk("scsi%d: warning : SCSI command probably completed successfully"
+      "         before abortion. ", instance->host_no);
+   return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+
+#define MAX_IN2000_HOSTS 3
+#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_STRINGS];
+
+void in2000_setup (char *str, int *ints)
+{
+int i;
+char *p1,*p2;
+
+   strncpy(setup_buffer,str,SETUP_BUFFER_SIZE);
+   setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+   p1 = setup_buffer;
+   i = 0;
+   while (*p1 && (i < MAX_SETUP_STRINGS)) {
+      p2 = strchr(p1, ',');
+      if (p2) {
+         *p2 = '\0';
+         if (p1 != p2)
+            setup_strings[i] = p1;
+         p1 = p2 + 1;
+         i++;
+         }
+      else {
+         setup_strings[i] = p1;
+         break;
+         }
+      }
+   for (i=0; i<MAX_SETUP_STRINGS; i++)
+      setup_used[i] = 0;
+}
+
+
+/* check_setup_strings() returns index if key found, 0 if not
+ */
+
+int check_setup_strings(char *key, int *flags, int *val, char *buf)
+{
+int x;
+char *cp;
+
+   for  (x=0; x<MAX_SETUP_STRINGS; x++) {
+      if (setup_used[x])
+         continue;
+      if (!strncmp(setup_strings[x], key, strlen(key)))
+         break;
+      }
+   if (x == MAX_SETUP_STRINGS)
+      return 0;
+   setup_used[x] = 1;
+   cp = setup_strings[x] + strlen(key);
+   *val = -1;
+   if (*cp != ':')
+      return ++x;
+   cp++;
+   if ((*cp >= '0') && (*cp <= '9')) {
+      *val = simple_strtoul(cp,NULL,0);
+      }
+   return ++x;
+}
+
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h>
+struct proc_dir_entry proc_scsi_in2000 = {
+   PROC_SCSI_IN2000, 6, "in2000",
+   S_IFDIR | S_IRUGO | S_IXUGO, 2
+   };
+#endif
+
+
+const unsigned int *bios_tab[] = {
+   (unsigned int *)0xc8000,
+   (unsigned int *)0xd0000,
+   (unsigned int *)0xd8000,
+   0
+   };
+
+const unsigned short base_tab[] = {
+   0x220,
+   0x200,
+   0x110,
+   0x100,
+   };
+
+const int int_tab[] = {
+   15,
+   14,
+   11,
+   10
+   };
+
+int in2000_detect(Scsi_Host_Template * tpnt)
+{
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+int detect_count;
+int bios;
+int x;
+unsigned short base;
+uchar switches;
+uchar hrev;
+int flags;
+int val;
+char buf[32];
+
+/* Thanks to help from Bill Earnest, probing for IN2000 cards is a
+ * pretty straightforward and fool-proof operation. We do require
+ * that cards have their BIOS enabled, although I hope to be able
+ * to detect and use BIOS-less cards in the future. There are 3
+ * possible locations for the IN2000 EPROM in memory space - if we
+ * find a BIOS signature, we can read the dip switch settings from
+ * the byte at BIOS+32 (shadowed in by logic on the card). From 2
+ * of the switch bits we get the card's address in IO space. There's
+ * an image of the dip switch there, also, so we have a way to back-
+ * check that this really is an IN2000 card. Very nifty.
+ *
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
+
+   detect_count = 0;
+   for (bios = 0; bios_tab[bios]; bios++) {
+      if (check_setup_strings("ioport",&flags,&val,buf)) {
+         base = val;
+         switches = ~inb(base + IO_SWITCHES) & 0xff;
+         printk("Forcing detection at IOport 0x%x.\n",base);
+         bios = 2;
+         }
+      else if (*(bios_tab[bios]+0x04) == 0x41564f4e ||
+          *(bios_tab[bios]+0x0c) == 0x61776c41) {
+         printk("Found IN2000 BIOS at 0x%x.\n",(unsigned int)bios_tab[bios]);
+
+/* Read the switch image that's mapped into EPROM space */
+
+         switches = ~((*(bios_tab[bios]+0x08) & 0xff));
+
+/* Find out where the IO space is */
+
+         x = switches & (SW_ADDR0 | SW_ADDR1);
+         base = base_tab[x];
+
+/* Check for the IN2000 signature in IO space. */
+
+         x = ~inb(base + IO_SWITCHES) & 0xff;
+         if (x != switches) {
+            printk("Bad IO signature: %02x vs %02x\n",x,switches);
+            continue;
+            }
+         }
+      else
+         continue;
+
+/* OK. We have a base address for the IO ports - run a few safety checks */
+
+      if (!(switches & SW_BIT7)) {        /* I _think_ all cards do this */
+         printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n",base);
+         continue;
+         }
+
+/* Let's expect only known legal hardware version here. There
+ * can't be THAT many of them, and it's easy to add new ones
+ * as we hear about them.
+ */
+
+      hrev = inb(base + IO_HARDWARE);
+      if ((hrev != 0x27) && (hrev != 0x26) && (hrev != 0x25)) {
+         printk("The IN-2000 SCSI card at IOport 0x%03x ",base);
+         printk("has unknown version %02x hardware - ",hrev);
+         printk("Sorry, cancelling detection.\n");
+         continue;
+         }
+
+  /* Bit 2 tells us if interrupts are disabled */
+      if (switches & SW_DISINT) {
+         printk("The IN-2000 SCSI card at IOport 0x%03x ",base);
+         printk("is not configured for interrupt operation!\n");
+         printk("This driver requires an interrupt: cancelling detection.\n");
+         continue;
+         }
+
+/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now
+ * initialize it.
+ */
+
+#if LINUX_VERSION_CODE >= 0x010300
+      tpnt->proc_dir = &proc_scsi_in2000; /* done more than once? harmless. */
+#endif
+
+      detect_count++;
+      instance  = scsi_register(tpnt, sizeof(struct IN2000_hostdata));
+      if (!instance_list)
+         instance_list = instance;
+      hostdata = (struct IN2000_hostdata *)instance->hostdata;
+      instance->io_port = hostdata->io_base = base;
+      hostdata->dip_switch = switches;
+      hostdata->hrev = hrev;
+
+      write1_io(0,IO_FIFO_WRITE);            /* clear fifo counter */
+      write1_io(0,IO_FIFO_READ);             /* start fifo out in read mode */
+      write1_io(0,IO_INTR_MASK);    /* allow all ints */
+      x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT];
+
+#if LINUX_VERSION_CODE >= 0x010346   /* 1.3.70 */
+      if (request_irq(x, in2000_intr, SA_INTERRUPT, "in2000", NULL)) {
 #else
-    if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
-    return 0;
+      if (request_irq(x, in2000_intr, SA_INTERRUPT, "in2000")) {
+#endif
+         printk("in2000_detect: Unable to allocate IRQ.\n");
+         detect_count--;
+         continue;
+         }
+      instance->irq = x;
+      instance->n_io_port = 13;
+      request_region(base, 13, "in2000"); /* lock in this IO space for our use */
+
+      for (x = 0; x < 8; x++) {
+         hostdata->busy[x] = 0;
+         hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+         hostdata->sync_stat[x] = SS_UNSET;  /* using default sync values */
+         }
+      hostdata->input_Q = NULL;
+      hostdata->selecting = NULL;
+      hostdata->connected = NULL;
+      hostdata->disconnected_Q = NULL;
+      hostdata->state = S_UNCONNECTED;
+      hostdata->fifo = FI_FIFO_UNUSED;
+      hostdata->level2 = L2_BASIC;
+      hostdata->disconnect = DIS_ADAPTIVE;
+      hostdata->args = DEBUG_DEFAULTS;
+      hostdata->incoming_ptr = 0;
+      hostdata->outgoing_len = 0;
+      hostdata->default_sx_per = DEFAULT_SX_PER;
+
+/* Older BIOS's had a 'sync on/off' switch - use its setting */
+
+      if (*(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
+         hostdata->sync_off = 0x00;    /* sync defaults to on */
+      else
+         hostdata->sync_off = 0xff;    /* sync defaults to off */
+
+      hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+                       PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
+                       PR_STOP;
+
+#ifdef PROC_INTERFACE
+      disc_allowed_total = 0;
+      disc_taken_total = 0;
+#endif
+
+
+      if (check_setup_strings("nosync",&flags,&val,buf))
+         hostdata->sync_off = val;
+
+      if (check_setup_strings("period",&flags,&val,buf))
+         hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
+
+      if (check_setup_strings("disconnect",&flags,&val,buf)) {
+         if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+            hostdata->disconnect = val;
+         else
+            hostdata->disconnect = DIS_ADAPTIVE;
+         }
+
+      if (check_setup_strings("noreset",&flags,&val,buf))
+         hostdata->args ^= A_NO_SCSI_RESET;
+
+      if (check_setup_strings("debug",&flags,&val,buf))
+         hostdata->args = (val & DB_MASK);
+
+      while (check_setup_strings("proc",&flags,&val,buf))
+         hostdata->proc = val;
+
+      x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS);
+
+      hostdata->microcode = read_3393(hostdata,WD_CDB_1);
+      if (x & 0x01) {
+         if (x & B_FLAG)
+            hostdata->chip = C_WD33C93B;
+         else
+            hostdata->chip = C_WD33C93A;
+         }
+      else
+         hostdata->chip = C_WD33C93;
+
+      printk("in2000-%d: dip_switch=%02x: irq=%d ioport=%02x floppy=%s sync/DOS5=%s\n",
+                  instance->host_no,(switches & 0x7f),
+                  instance->irq,hostdata->io_base,
+                  (switches & SW_FLOPPY)?"Yes":"No",
+                  (switches & SW_SYNC_DOS5)?"Yes":"No");
+      printk("in2000-%d: hardware_ver=%02x chip=%s microcode=%02x\n",
+                  instance->host_no,hrev,
+                  (hostdata->chip==C_WD33C93)?"WD33c93":
+                  (hostdata->chip==C_WD33C93A)?"WD33c93A":
+                  (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
+                  hostdata->microcode);
+#ifdef DEBUGGING_ON
+      printk("in2000-%d: setup_strings = ",instance->host_no);
+      for (x=0; x<8; x++)
+         printk("%s,",setup_strings[x]);
+      printk("\n");
 #endif
+      if (hostdata->sync_off == 0xff)
+         printk("in2000-%d: Sync-transfer DISABLED on all devices: ENABLE from command-line\n",instance->host_no);
+      printk("in2000-%d: driver version %s - %s\n",instance->host_no,
+                        IN2000_VERSION,IN2000_DATE);
+      }
+
+   return detect_count;
 }
 
-int in2000_biosparam(Disk * disk, kdev_t dev, int* iinfo)
-	{
-	  int size = disk->capacity;
-    DEB(printk("in2000_biosparam\n"));
-    iinfo[0] = 64;
-    iinfo[1] = 32;
-    iinfo[2] = size >> 11;
+
+/* NOTE: I lifted this function straight out of the old driver,
+ *       and have not tested it. Presumably it does what it's
+ *       supposed to do...
+ */
+
+#if LINUX_VERSION_CODE >= 0x010300
+int in2000_biosparam(Disk *disk, kdev_t dev, int *iinfo)
+#else
+int in2000_biosparam(Disk *disk, int dev, int *iinfo)
+#endif
+{
+int size;
+
+   size  = disk->capacity;
+   iinfo[0] = 64;
+   iinfo[1] = 32;
+   iinfo[2] = size >> 11;
+
 /* This should approximate the large drive handling that the DOS ASPI manager
    uses.  Drives very near the boundaries may not be handled correctly (i.e.
    near 2.0 Gb and 4.0 Gb) */
-    if (iinfo[2] > 1024) {
-	iinfo[0] = 64;
-	iinfo[1] = 63;
-	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-	}
-    if (iinfo[2] > 1024) {
-	iinfo[0] = 128;
-	iinfo[1] = 63;
-	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-	}
-    if (iinfo[2] > 1024) {
-	iinfo[0] = 255;
-	iinfo[1] = 63;
-	iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-	if (iinfo[2] > 1023)
-	    iinfo[2] = 1023;
-	}
+
+   if (iinfo[2] > 1024) {
+      iinfo[0] = 64;
+      iinfo[1] = 63;
+      iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+      }
+   if (iinfo[2] > 1024) {
+      iinfo[0] = 128;
+      iinfo[1] = 63;
+      iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+      }
+   if (iinfo[2] > 1024) {
+      iinfo[0] = 255;
+      iinfo[1] = 63;
+      iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+      if (iinfo[2] > 1023)
+         iinfo[2] = 1023;
+      }
     return 0;
-    }
+}
+
+
+int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+char *bp;
+char tbuf[128];
+unsigned long flags;
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hd;
+Scsi_Cmnd *cmd;
+int x,i;
+static int stop = 0;
+
+   for (instance=instance_list; instance; instance=instance->next) {
+      if (instance->host_no == hn)
+         break;
+      }
+   if (!instance) {
+      printk("*** Hmm... Can't find host #%d!\n",hn);
+      return (-ESRCH);
+      }
+   hd = (struct IN2000_hostdata *)instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ *    debug
+ *    disconnect
+ *    period
+ *    resync
+ *    proc
+ */
+
+   if (in) {
+      buf[len] = '\0';
+      bp = buf;
+      if (!strncmp(bp,"debug:",6)) {
+         bp += 6;
+         hd->args = simple_strtoul(bp,NULL,0) & DB_MASK;
+         }
+      else if (!strncmp(bp,"disconnect:",11)) {
+         bp += 11;
+         x = simple_strtoul(bp,NULL,0);
+         if (x < DIS_NEVER || x > DIS_ALWAYS)
+            x = DIS_ADAPTIVE;
+         hd->disconnect = x;
+         }
+      else if (!strncmp(bp,"period:",7)) {
+         bp += 7;
+         x = simple_strtoul(bp,NULL,0);
+         hd->default_sx_per = sx_table[round_period((unsigned int)x)].period_ns;
+         }
+      else if (!strncmp(bp,"resync:",7)) {
+         bp += 7;
+         x = simple_strtoul(bp,NULL,0);
+         for (i=0; i<7; i++)
+            if (x & (1<<i))
+               hd->sync_stat[i] = SS_UNSET;
+         }
+      else if (!strncmp(bp,"proc:",5)) {
+         bp += 5;
+         hd->proc = simple_strtoul(bp,NULL,0);
+         }
+      return len;
+      }
+
+   save_flags(flags);
+   cli();
+   bp = buf;
+   *bp = '\0';
+   if (hd->proc & PR_VERSION) {
+      sprintf(tbuf,"\nVersion %s - %s. Compiled %s %s",
+            IN2000_VERSION,IN2000_DATE,__DATE__,__TIME__);
+      strcat(bp,tbuf);
+      }
+   if (hd->proc & PR_INFO) {
+      sprintf(tbuf,"\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s",
+                  (hd->dip_switch & 0x7f), instance->irq, hd->io_base,
+                  (hd->dip_switch & 0x40)?"Yes":"No",
+                  (hd->dip_switch & 0x20)?"Yes":"No");
+      strcat(bp,tbuf);
+      }
+   if (hd->proc & PR_TOTALS) {
+      sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
+            disc_allowed_total,disc_taken_total);
+      strcat(bp,tbuf);
+      }
+   if (hd->proc & PR_CONNECTED) {
+      strcat(bp,"\nconnected:     ");
+      if (hd->connected) {
+         cmd = (Scsi_Cmnd *)hd->connected;
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         }
+      }
+   if (hd->proc & PR_INPUTQ) {
+      strcat(bp,"\ninput_Q:       ");
+      cmd = (Scsi_Cmnd *)hd->input_Q;
+      while (cmd) {
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         cmd = (Scsi_Cmnd *)cmd->host_scribble;
+         }
+      }
+   if (hd->proc & PR_DISCQ) {
+      strcat(bp,"\ndisconnected_Q:");
+      cmd = (Scsi_Cmnd *)hd->disconnected_Q;
+      while (cmd) {
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         cmd = (Scsi_Cmnd *)cmd->host_scribble;
+         }
+      }
+   if (hd->proc & PR_TEST) {
+      ;  /* insert your own custom function here */
+      }
+   strcat(bp,"\n");
+   restore_flags(flags);
+   *start = buf;
+   if (stop) {
+      stop = 0;
+      return 0;         /* return 0 to signal end-of-file */
+      }
+   if (off > 0x40000)   /* ALWAYS stop after 256k bytes have been read */
+      stop = 1;;
+   if (hd->proc & PR_STOP)    /* stop every other time */
+      stop = 1;
+   return strlen(bp);
+
+#else    /* PROC_INTERFACE */
+
+   return 0;
+
+#endif   /* PROC_INTERFACE */
+
+}
+
 
 #ifdef MODULE
-/* Eventually this will go into an include file, but this will be later */
+
 Scsi_Host_Template driver_template = IN2000;
 
 #include "scsi_module.c"
+
 #endif
 
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v1.3.96/linux/drivers/scsi/in2000.h	Mon Sep 18 08:54:09 1995
+++ linux/drivers/scsi/in2000.h	Sun Apr 28 12:13:45 1996
@@ -1,122 +1,396 @@
-#ifndef _IN2000_H
-
-/* $Id: in2000.h,v 1.2 1994/08/25 06:27:38 root Exp root $
+/*
+ *    in2000.h -  Linux device driver definitions for the
+ *                Always IN2000 ISA SCSI card.
+ *
+ *    IMPORTANT: This file is for version 1.28 - 27/Apr/1996
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ *    john@geolog.com
+ *    jshiffle@netcom.com
  *
- * Header file for the Always IN 2000 driver for Linux
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  *
  */
 
-#include <linux/types.h>
-#include <linux/ioport.h>
+#ifndef IN2000_H
+#define IN2000_H
 
-/* The IN-2000 is based on a WD33C93 */
+#include <asm/io.h>
+
+/* We include version.h to get 'LINUX_VERSION_CODE' - a define used here
+ * and there in the source to get around various compatibility problems:
+ * -  pre-1.3.xx kernels didn't have 'kdev_t' or proc, and their
+ *    <blk.h> was in a different place.
+ * -  1.3.70 introduced an additional argument for interrupt functions
+ * -  1.3.89 added an argument to in2000_reset(), which we don't really
+ *    use at the moment. But for completeness...
+ */
+#include <linux/version.h>
+
+
+#define uchar unsigned char
+
+
+/* IN2000 io_port offsets */
+#define IO_WD_ASR       0x00     /* R - 3393 auxstat reg */
+#define     ASR_INT        0x80
+#define     ASR_LCI        0x40
+#define     ASR_BSY        0x20
+#define     ASR_CIP        0x10
+#define     ASR_PE         0x02
+#define     ASR_DBR        0x01
+#define IO_WD_ADDR      0x00     /* W - 3393 address reg */
+#define IO_WD_DATA      0x01     /* R/W - rest of 3393 regs */
+#define IO_FIFO         0x02     /* R/W - in2000 dual-port fifo (16 bits) */
+#define IN2000_FIFO_SIZE   2048  /*    fifo capacity in bytes */
+#define IO_CARD_RESET   0x03     /* W - in2000 start master reset */
+#define IO_FIFO_COUNT   0x04     /* R - in2000 fifo counter */
+#define IO_FIFO_WRITE   0x05     /* W - clear fifo counter, start write */
+#define IO_FIFO_READ    0x07     /* W - start fifo read */
+#define IO_LED_OFF      0x08     /* W - turn off in2000 activity LED */
+#define IO_SWITCHES     0x08     /* R - read in2000 dip switch */
+#define     SW_ADDR0       0x01  /*    bit 0 = bit 0 of index to io addr */
+#define     SW_ADDR1       0x02  /*    bit 1 = bit 1 of index io addr */
+#define     SW_DISINT      0x04  /*    bit 2 true if ints disabled */
+#define     SW_INT0        0x08  /*    bit 3 = bit 0 of index to interrupt */
+#define     SW_INT1        0x10  /*    bit 4 = bit 1 of index to interrupt */
+#define     SW_INT_SHIFT   3     /*    shift right this amount to right justify int bits */
+#define     SW_SYNC_DOS5   0x20  /*    bit 5 used by Always BIOS */
+#define     SW_FLOPPY      0x40  /*    bit 6 true if floppy enabled */
+#define     SW_BIT7        0x80  /*    bit 7 hardwired true (ground) */
+#define IO_LED_ON       0x09     /* W - turn on in2000 activity LED */
+#define IO_HARDWARE     0x0a     /* R - read in2000 hardware rev, stop reset */
+#define IO_INTR_MASK    0x0c     /* W - in2000 interrupt mask reg */
+#define     IMASK_WD       0x01  /*    WD33c93 interrupt mask */
+#define     IMASK_FIFO     0x02  /*    FIFO interrupt mask */
+
+/* wd register names */
+#define WD_OWN_ID    0x00
+#define WD_CONTROL   0x01
+#define WD_TIMEOUT_PERIOD  0x02
+#define WD_CDB_1     0x03
+#define WD_CDB_2     0x04
+#define WD_CDB_3     0x05
+#define WD_CDB_4     0x06
+#define WD_CDB_5     0x07
+#define WD_CDB_6     0x08
+#define WD_CDB_7     0x09
+#define WD_CDB_8     0x0a
+#define WD_CDB_9     0x0b
+#define WD_CDB_10    0x0c
+#define WD_CDB_11    0x0d
+#define WD_CDB_12    0x0e
+#define WD_TARGET_LUN      0x0f
+#define WD_COMMAND_PHASE   0x10
+#define WD_SYNCHRONOUS_TRANSFER  0x11
+#define WD_TRANSFER_COUNT_MSB 0x12
+#define WD_TRANSFER_COUNT  0x13
+#define WD_TRANSFER_COUNT_LSB 0x14
+#define WD_DESTINATION_ID  0x15
+#define WD_SOURCE_ID    0x16
+#define WD_SCSI_STATUS     0x17
+#define WD_COMMAND      0x18
+#define WD_DATA      0x19
+#define WD_QUEUE_TAG    0x1a
+#define WD_AUXILIARY_STATUS   0x1f
+
+/* WD commands */
+#define WD_CMD_RESET    0x00
+#define WD_CMD_ABORT    0x01
+#define WD_CMD_ASSERT_ATN  0x02
+#define WD_CMD_NEGATE_ACK  0x03
+#define WD_CMD_DISCONNECT  0x04
+#define WD_CMD_RESELECT    0x05
+#define WD_CMD_SEL_ATN     0x06
+#define WD_CMD_SEL      0x07
+#define WD_CMD_SEL_ATN_XFER   0x08
+#define WD_CMD_SEL_XFER    0x09
+#define WD_CMD_RESEL_RECEIVE  0x0a
+#define WD_CMD_RESEL_SEND  0x0b
+#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
+#define WD_CMD_TRANS_ADDR  0x18
+#define WD_CMD_TRANS_INFO  0x20
+#define WD_CMD_TRANSFER_PAD   0x21
+#define WD_CMD_SBT_MODE    0x80
+
+/* SCSI Bus Phases */
+#define PHS_DATA_OUT    0x00
+#define PHS_DATA_IN     0x01
+#define PHS_COMMAND     0x02
+#define PHS_STATUS      0x03
+#define PHS_MESS_OUT    0x06
+#define PHS_MESS_IN     0x07
+
+/* Command Status Register definitions */
+
+  /* reset state interrupts */
+#define CSR_RESET    0x00
+#define CSR_RESET_AF    0x01
+
+  /* successful completion interrupts */
+#define CSR_RESELECT    0x10
+#define CSR_SELECT      0x11
+#define CSR_SEL_XFER_DONE  0x16
+#define CSR_XFER_DONE      0x18
+
+  /* paused or aborted interrupts */
+#define CSR_MSGIN    0x20
+#define CSR_SDP         0x21
+#define CSR_SEL_ABORT      0x22
+#define CSR_RESEL_ABORT    0x25
+#define CSR_RESEL_ABORT_AM 0x27
+#define CSR_ABORT    0x28
+
+  /* terminated interrupts */
+#define CSR_INVALID     0x40
+#define CSR_UNEXP_DISC     0x41
+#define CSR_TIMEOUT     0x42
+#define CSR_PARITY      0x43
+#define CSR_PARITY_ATN     0x44
+#define CSR_BAD_STATUS     0x45
+#define CSR_UNEXP    0x48
+
+  /* service required interrupts */
+#define CSR_RESEL    0x80
+#define CSR_RESEL_AM    0x81
+#define CSR_DISC     0x85
+#define CSR_SRV_REQ     0x88
+
+   /* Own ID/CDB Size register */
+#define OWNID_EAF    0x08
+#define OWNID_EHP    0x10
+#define OWNID_RAF    0x20
+#define OWNID_FS_8   0x00
+#define OWNID_FS_12  0x40
+#define OWNID_FS_16  0x80
+
+   /* Control register */
+#define CTRL_HSP     0x01
+#define CTRL_HA      0x02
+#define CTRL_IDI     0x04
+#define CTRL_EDI     0x08
+#define CTRL_HHP     0x10
+#define CTRL_POLLED  0x00
+#define CTRL_BURST   0x20
+#define CTRL_BUS     0x40
+#define CTRL_DMA     0x80
+
+   /* Timeout Period register */
+#define TIMEOUT_PERIOD_VALUE  20    /* results in 200 ms. */
+
+   /* Synchronous Transfer Register */
+#define STR_FSS      0x80
+
+   /* Destination ID register */
+#define DSTID_DPD    0x40
+#define DATA_OUT_DIR 0
+#define DATA_IN_DIR  1
+#define DSTID_SCC    0x80
+
+   /* Source ID register */
+#define SRCID_MASK   0x07
+#define SRCID_SIV    0x08
+#define SRCID_DSP    0x20
+#define SRCID_ES     0x40
+#define SRCID_ER     0x80
+
+
+
+#define DEFAULT_SX_PER     500   /* (ns) fairly safe */
+#define DEFAULT_SX_OFF     0     /* aka async */
+
+#define OPTIMUM_SX_PER     252   /* (ns) best we can do (mult-of-4) */
+#define OPTIMUM_SX_OFF     12    /* size of in2000 fifo */
+
+struct sx_period {
+   unsigned int   period_ns;
+   uchar          reg_value;
+   };
+
+
+struct IN2000_hostdata {
+    struct Scsi_Host *next;
+    uchar            chip;             /* what kind of wd33c93 chip? */
+    uchar            microcode;        /* microcode rev if 'B' */
+    unsigned short   io_base;          /* IO port base */
+    unsigned int     dip_switch;       /* dip switch settings */
+    unsigned int     hrev;             /* hardware revision of card */
+    volatile uchar   busy[8];          /* index = target, bit = lun */
+    volatile Scsi_Cmnd *input_Q;       /* commands waiting to be started */
+    volatile Scsi_Cmnd *selecting;     /* trying to select this command */
+    volatile Scsi_Cmnd *connected;     /* currently connected command */
+    volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
+    uchar            state;            /* what we are currently doing */
+    uchar            fifo;             /* what the FIFO is up to */
+    uchar            level2;           /* extent to which Level-2 commands are used */
+    uchar            disconnect;       /* disconnect/reselect policy */
+    unsigned int     args;             /* set from command-line argument */
+    uchar            incoming_msg[8];  /* filled during message_in phase */
+    int              incoming_ptr;     /* mainly used with EXTENDED messages */
+    uchar            outgoing_msg[8];  /* send this during next message_out */
+    int              outgoing_len;     /* length of outgoing message */
+    unsigned int     default_sx_per;   /* default transfer period for SCSI bus */
+    uchar            sync_xfer[8];     /* sync_xfer reg settings per target */
+    uchar            sync_stat[8];     /* status of sync negotiation per target */
+    uchar            sync_off;         /* bit mask: don't use sync with these targets */
+    uchar            proc;             /* bit mask: what's in proc output */
+    };
+
+
+/* defines for hostdata->chip */
+
+#define C_WD33C93       0
+#define C_WD33C93A      1
+#define C_WD33C93B      2
+#define C_UNKNOWN_CHIP  100
+
+/* defines for hostdata->state */
+
+#define S_UNCONNECTED         0
+#define S_SELECTING           1
+#define S_RUNNING_LEVEL2      2
+#define S_CONNECTED           3
+#define S_PRE_TMP_DISC        4
+#define S_PRE_CMP_DISC        5
+
+/* defines for hostdata->fifo */
+
+#define FI_FIFO_UNUSED        0
+#define FI_FIFO_READING       1
+#define FI_FIFO_WRITING       2
+
+/* defines for hostdata->level2 */
+/* NOTE: only the first 3 are trustworthy at this point -
+ * having trouble when more than 1 device is reading/writing
+ * at the same time...
+ */
+
+#define L2_NONE      0  /* no combination commands - we get lots of ints */
+#define L2_SELECT    1  /* start with SEL_ATN_XFER, but never resume it */
+#define L2_BASIC     2  /* resume after STATUS ints & RDP messages */
+#define L2_DATA      3  /* resume after DATA_IN/OUT ints */
+#define L2_MOST      4  /* resume after anything except a RESELECT int */
+#define L2_RESELECT  5  /* resume after everything, including RESELECT ints */
+#define L2_ALL       6  /* always resume */
+
+/* defines for hostdata->disconnect */
+
+#define DIS_NEVER    0
+#define DIS_ADAPTIVE 1
+#define DIS_ALWAYS   2
+
+/* defines for hostdata->args */
+
+#define DB_TEST               1<<0
+#define DB_FIFO               1<<1
+#define DB_QUEUE_COMMAND      1<<2
+#define DB_EXECUTE            1<<3
+#define DB_INTR               1<<4
+#define DB_TRANSFER           1<<5
+#define DB_MASK               0x3f
+
+#define A_NO_SCSI_RESET       1<<15
+
+
+/* defines for hostdata->sync_xfer[] */
+
+#define SS_UNSET     0
+#define SS_FIRST     1
+#define SS_WAITING   2
+#define SS_SET       3
+
+/* defines for hostdata->proc */
+
+#define PR_VERSION   1<<0
+#define PR_INFO      1<<1
+#define PR_TOTALS    1<<2
+#define PR_CONNECTED 1<<3
+#define PR_INPUTQ    1<<4
+#define PR_DISCQ     1<<5
+#define PR_TEST      1<<6
+#define PR_STOP      1<<7
 
-#define	INSTAT	(base + 0x0)	/* R: Auxiliary Status; W: register select */
-#define	INDATA	(base + 0x1)	/* R/W: Data port */
-#define	INFIFO	(base + 0x2)	/* R/W FIFO, Word access only */
-#define	INREST	(base + 0x3)	/* W: Reset everything */
-#define	INFCNT	(base + 0x4)	/* R: FIFO byte count */
-#define	INFRST	(base + 0x5)	/* W: Reset Fifo count and to write */
-#define	INFWRT	(base + 0x7)	/* W: Set FIFO to read */
-#define	INFLED	(base + 0x8)	/* W: Set LED; R: Dip Switch settings */
-#define	INNLED	(base + 0x9)	/* W: reset LED */
-#define	INVERS	(base + 0xa)	/* R: Read hw version, end-reset */
-#define	ININTR	(base + 0xc)	/* W: Interrupt Mask Port */
-#define G2CNTRL_HRDY	0x20		/* Sets HOST ready */
-
-/* WD33C93 defines */
-#define	OWNID	0
-#undef	CONTROL
-#define	CONTROL	1
-#define	TIMEOUT	2
-#define	TOTSECT	3
-#define	TOTHEAD	4
-#define	TOTCYLH 5
-#define	TOTCYLL	6
-#define	LADRSHH	7
-#define	LADRSHL	8
-#define	LADRSLH	9
-#define	LADRSLL	10
-#define	SECTNUM	11
-#define	HEADNUM	12
-#define	CYLNUMH	13
-#define	CYLNUML	14
-#define	TARGETU	15
-#define	CMDPHAS	16
-#define	SYNCTXR	17
-#define	TXCNTH	18
-#define	TXCNTM	19
-#define TXCNTL	20
-#define DESTID	21
-#define	SRCID	22
-#define	SCSIST	23
-#define	COMMAND	24
-#define	WDDATA	25
-#define	AUXSTAT	31
-
-/* OWNID Register Bits */
-#define	OWN_EAF	0x08
-#define	OWN_EHP	0x10
-#define	OWN_FS0	0x40
-#define	OWN_FS1	0x80
-/* AUX Register Bits */
-#define	AUX_DBR	0
-#define	AUX_PE	1
-#define	AUX_CIP	0x10
-#define	AUX_BSY	0x20
-#define	AUX_LCI	0x40
-#define	AUX_INT	0x80
-
-/* Select timeout const, 1 count = 8ms */
-#define IN2000_TMOUT 0x1f
-
-/* These belong in scsi.h also */
-#undef any2scsi
-#define any2scsi(up, p)				\
-(up)[0] = (((unsigned long)(p)) >> 16);		\
-(up)[1] = (((unsigned long)(p)) >> 8);		\
-(up)[2] = ((unsigned long)(p));
-
-#undef scsi2int
-#define scsi2int(up) ( ((((long)*(up))&0x1f) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
-
-#undef xany2scsi
-#define xany2scsi(up, p)	\
-(up)[0] = ((long)(p)) >> 24;	\
-(up)[1] = ((long)(p)) >> 16;	\
-(up)[2] = ((long)(p)) >> 8;	\
-(up)[3] = ((long)(p));
-
-#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
-		      + (((long)(up)[2]) <<  8) +  ((long)(up)[3]) )
-
-#define MAX_CDB 12
-#define MAX_SENSE 14
-#define MAX_STATUS 32
 
 int in2000_detect(Scsi_Host_Template *);
-int in2000_command(Scsi_Cmnd *);
 int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int in2000_abort(Scsi_Cmnd *);
+void in2000_setup(char *, int *);
+int in2000_proc_info(char *, char **, off_t, int, int, int);
+struct proc_dir_entry proc_scsi_in2000;
+
+#if LINUX_VERSION_CODE >= 0x010300
+int in2000_biosparam(struct scsi_disk *, kdev_t, int *);
+#else
+int in2000_biosparam(Disk *, int, int *);
+#endif
+#if LINUX_VERSION_CODE >= 0x010359           /* 1.3.89 */
+int in2000_reset(Scsi_Cmnd *, unsigned int);
+#else
 int in2000_reset(Scsi_Cmnd *);
-int in2000_biosparam(Disk *, kdev_t, int*);
-
-#ifndef NULL
-	#define NULL 0
 #endif
 
 
-/* next may be "SG_NONE" or "SG_ALL" or nr. of (1k) blocks per R/W Cmd. */
-#define IN2000_SG SG_ALL
-#define IN2000 {NULL, NULL,  \
-                NULL, NULL, \
-		"Always IN2000", in2000_detect, NULL,	\
-		NULL, in2000_command,		\
-		in2000_queuecommand,		\
-		in2000_abort,			\
-		in2000_reset,			\
-		NULL,				\
-		in2000_biosparam,               \
-		1, 7, IN2000_SG, 1, 0, 0}
-
+#define IN2000_CAN_Q    16
+#define IN2000_SG       SG_ALL
+#define IN2000_CPL      2
+#define IN2000_HOST_ID  7
+
+#if LINUX_VERSION_CODE >= 0x010300
+#define IN2000 {  NULL,                /* link pointer for modules */ \
+                  NULL,                /* usage_count for modules */ \
+                  &proc_scsi_in2000,   /* pointer to /proc/scsi directory entry */ \
+                  in2000_proc_info,    /* pointer to proc info function */ \
+                  "Always IN2000",     /* device name */ \
+                  in2000_detect,       /* returns number of in2000's found */ \
+                  NULL,                /* optional unload function for modules */ \
+                  NULL,                /* optional misc info function */ \
+                  NULL,                /* send scsi command, wait for completion */ \
+                  in2000_queuecommand, /* queue scsi command, don't wait */ \
+                  in2000_abort,        /* abort current command */ \
+                  in2000_reset,        /* reset scsi bus */ \
+                  NULL,                /* slave_attach - unused */ \
+                  in2000_biosparam,    /* figures out BIOS parameters for lilo, etc */ \
+                  IN2000_CAN_Q,        /* max commands we can queue up */ \
+                  IN2000_HOST_ID,      /* host-adapter scsi id */ \
+                  IN2000_SG,           /* scatter-gather table size */ \
+                  IN2000_CPL,          /* commands per lun */ \
+                  0,                   /* board counter */ \
+                  0,                   /* unchecked dma */ \
+                  DISABLE_CLUSTERING \
+               }
+
+#else
+#define IN2000 {  NULL,                /* link pointer for modules */ \
+                  NULL,                /* usage_count for modules */ \
+/*                  NULL,*/                /* pointer to /proc/scsi directory entry */ \
+/*                  NULL,*/                /* pointer to proc info function */ \
+                  "Always IN2000",     /* device name */ \
+                  in2000_detect,       /* returns number of in2000's found */ \
+                  NULL,                /* optional unload function for modules */ \
+                  NULL,                /* optional misc info function */ \
+                  NULL,                /* send scsi command, wait for completion */ \
+                  in2000_queuecommand, /* queue scsi command, don't wait */ \
+                  in2000_abort,        /* abort current command */ \
+                  in2000_reset,        /* reset scsi bus */ \
+                  NULL,                /* slave_attach - unused */ \
+                  in2000_biosparam,    /* figures out BIOS parameters for lilo, etc */ \
+                  IN2000_CAN_Q,        /* max commands we can queue up */ \
+                  IN2000_HOST_ID,      /* host-adapter scsi id */ \
+                  IN2000_SG,           /* scatter-gather table size */ \
+                  IN2000_CPL,          /* commands per lun */ \
+                  0,                   /* board counter */ \
+                  0,                   /* unchecked dma */ \
+                  DISABLE_CLUSTERING \
+               }
 #endif
+
+
+#endif /* IN2000_H */
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/in2000.readme linux/drivers/scsi/in2000.readme
--- v1.3.96/linux/drivers/scsi/in2000.readme	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/in2000.readme	Sun Apr 28 12:13:45 1996
@@ -0,0 +1,171 @@
+
+UPDATE NEWS: version 1.28 - 27 Apr 96
+
+   Tightened up the "interrupts enabled/disabled" discipline
+   in 'in2000_queuecommand()' and maybe 1 or 2 other places.
+   I _think_ it may have been a little too lax, causing an
+   occasional crash during full moon. A fully functional
+   /proc interface is now in place - if you want to play
+   with it, start by doing 'cat /proc/scsi/in2000/0'. You
+   can also use it to change a few run-time parameters on
+   the fly, but it's mostly for debugging. The curious
+   should take a good look at 'in2000_proc_info()' in the
+   in2000.c file to get an understanding of what it's all
+   about; I figure that people who are really into it will
+   want to add features suited to their own needs...
+   Also, sync is now DISABLED by default.
+
+UPDATE NEWS: version 1.27 - 10 Apr 96
+
+   Fixed a well-hidden bug in the adapative-disconnect code
+   that would show up every now and then during extreme
+   heavy loads involving 2 or more simultaneously active
+   devices. Thanks to Joe Mack for keeping my nose to the
+   grindstone on this one.
+
+UPDATE NEWS: version 1.26 - 07 Mar 96
+
+   1.25 had a nasty bug that bit people with swap partitions
+   and tape drives. Also, in my attempt to guess my way
+   through Intel assembly language, I made an error in the
+   inline code for IO writes. Made a few other changes and
+   repairs - this version (fingers crossed) should work well.
+
+UPDATE NEWS: version 1.25 - 05 Mar 96
+
+   Kernel 1.3.70 interrupt mods added; old kernels still OK.
+   Big help from Bill Earnest and David Willmore on speed
+   testing and optimizing: I think there's a real improvement
+   in this area.
+   New! User-friendly command-line interface for LILO and
+   module loading - the old method is gone, so you'll need
+   to read the comments for 'setup_strings' near the top
+   of in2000.c. For people with CDROM's or other devices
+   that have a tough time with sync negotiation, you can
+   now selectively disable sync on individual devices -
+   search for the 'nosync' keyword in the command-line
+   comments. Some of you disable the BIOS on the card, which
+   caused the auto-detect function to fail; there is now a
+   command-line option to force detection of a ROM-less card.
+
+UPDATE NEWS: version 1.24a - 24 Feb 96
+
+   There was a bug in the synchronous transfer code. Only
+   a few people downloaded before I caught it - could have
+   been worse.
+
+UPDATE NEWS: version 1.24 - 23 Feb 96
+
+   Lots of good changes. Advice from Bill Earnest resulted
+   in much better detection of cards, more efficient usage
+   of the fifo, and (hopefully) faster data transfers. The
+   jury is still out on speed - I hope it's improved some.
+   One nifty new feature is a cool way of doing disconnect/
+   reselect. The driver defaults to what I'm calling
+   'adaptive disconnect' - meaning that each command is
+   evaluated individually as to whether or not it should be
+   run with the option to disconnect/reselect (if the device
+   chooses), or as a "SCSI-bus-hog". When several devices
+   are operating simultaneously, disconnects are usually an
+   advantage. In a single device system, or if only 1 device
+   is being accessed, transfers usually go faster if disconnects
+   are not allowed.
+   Hackers -> the positions of some of the 'setup_default' flags
+              have changed, so check your command-line args if
+              you've been using them with a previous version.
+
+
+Hi everyone:
+
+   A revamped IN2000 SCSI driver is available for download and
+   testing at my ftp site:
+
+            ftp.netcom.com/pub/js/jshiffle
+
+   in the 'in2000' directory. It has what I think are a few
+   improvements over the stock driver, including disconnect/
+   reselect, synchronous transfer, easier debugging, command-
+   line arguments for setting run-time parameters and debug
+   output, and better FIFO handling. Swap partitions work now,
+   as do tape drives and anything else that used to butt heads
+   with the old driver's 2k block size limit. See the top
+   of the 'in2000.c' file for more details.
+
+   This is a rewrite of the in2000.[ch] files for Linux. They
+   are drop-in replacements for the originals in linux/drivers/scsi
+   - just copy them over the old ones. The driver has been tested
+   in 1.2.13 with and without ELF, and in the 1.3 series somewhere
+   up into the 60's. I don't expect any problems with newer kernels.
+   It is "modular-ised" for those who prefer that route.
+
+-------  Hackers take note:   ------------------
+Anyone who wants to use LILO to pass a command line to this driver
+will have to make 2 small changes to the file 'init/main.c'. Find
+the area between lines 60-80 where all the 'xxxxx_setup()' extern
+declarations are and insert a new one like the following:
+
+extern void in2000_setup(char *str, int *ints);
+
+Then do a search for "bootsetups" - add a new entry to this array
+(somewhere in the middle) that looks like this:
+
+#ifdef CONFIG_SCSI_IN2000
+   { "in2000=", in2000_setup },
+#endif
+
+[You can skip the above if you're only loading the driver as a module
+ or if the driver defaults are OK.]
+
+The default arguments (you get these when you don't give an 'in2000'
+command-line argument, or you give a blank argument) will cause
+the driver to do adaptive disconnect, synchronous transfers, and a
+minimum of debug messages. If you want to fool with the options,
+search for 'setup_strings' near the top of the in2000.c file and
+check the 'hostdata->args' section in in2000.h - but be warned! Not
+everything is working yet (some things will never work, probably).
+I believe that disabling disconnects (DIS_NEVER) will allow you
+to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't
+spent a lot of time testing this. You might try 'ENABLE_CLUSTERING'
+to see what happens: my tests showed little difference either way.
+There's also a define called 'DEFAULT_SX_PER'; this sets the data
+transfer speed for the asynchronous mode. I've put it at 500 ns
+despite the fact that the card could handle settings of 376 or
+252, because I'm not really sure if certain devices or maybe bad
+cables might have trouble at higher speeds. I couldn't find any
+info in my various SCSI references that talk about this in language
+I could understand, so decided to compromise with 500. This is still
+faster than the old driver was set at (I think). Can someone explain
+the significance of the bus transfer speed setting? Do devices on
+the bus ever care what it is? Is cable quality a factor here?
+Regardless, you can choose your own default through the command-
+line with the 'period' keyword.
+
+------------------------------------------------
+
+   I have run a LOT of tests on this driver, and it seems very solid,
+   including with up to 3 simultaneous large copy or tar commands
+   running between 6 devices at once. Synchronous transfers are
+   working fine for the devices I have to test, although others have
+   reported some failures (CDROM drives, mostly). Tape drives work
+   well (finally!) and so do CD-ROM drives.
+
+   I should mention that Drew Eckhardt's 'Generic NCR5380' sources
+   were my main inspiration, with lots of reference to the IN2000
+   driver currently distributed in the kernel source. I also owe
+   much to a driver written by Hamish Mcdonald for Linux-m68k(!).
+   And to Eric Wright for being an ALPHA guinea pig. And to Bill
+   Earnest for 2 tons of great input and information. And to David
+   Willmore for extensive 'bonnie' testing.
+
+   Be forewarned that while I've had good luck with it, this
+   is the first time it's been loose out in the wide world.
+   It wouldn't suprise me if people uncover problems that
+   I haven't caught....
+
+   Please try the driver out. Test it, beat on it. And PLEASE get back
+   to me - I really need to hear about bugs, stupid or bad code,
+   and any ideas for enhancements.
+   Thanks very much...
+
+            John Shifflett    jshiffle@netcom.com
+
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/pas16.h linux/drivers/scsi/pas16.h
--- v1.3.96/linux/drivers/scsi/pas16.h	Wed Apr 10 17:02:25 1996
+++ linux/drivers/scsi/pas16.h	Mon Apr 29 17:14:19 1996
@@ -118,7 +118,7 @@
 int pas16_biosparam(Disk *, kdev_t, int*);
 int pas16_detect(Scsi_Host_Template *);
 int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int pas16_reset(Scsi_Cmnd *);
+int pas16_reset(Scsi_Cmnd *, unsigned int);
 int pas16_proc_info (char *buffer ,char **start, off_t offset,
 		     int length, int hostno, int inout);
 
diff -u --recursive --new-file v1.3.96/linux/drivers/scsi/t128.h linux/drivers/scsi/t128.h
--- v1.3.96/linux/drivers/scsi/t128.h	Wed Apr 10 17:02:25 1996
+++ linux/drivers/scsi/t128.h	Mon Apr 29 17:14:19 1996
@@ -95,7 +95,7 @@
 int t128_biosparam(Disk *, kdev_t, int*);
 int t128_detect(Scsi_Host_Template *);
 int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int t128_reset(Scsi_Cmnd *);
+int t128_reset(Scsi_Cmnd *, unsigned int reset_flags);
 int t128_proc_info (char *buffer, char **start, off_t offset,
 		   int length, int hostno, int inout);
 
diff -u --recursive --new-file v1.3.96/linux/drivers/sound/Readme.modules linux/drivers/sound/Readme.modules
--- v1.3.96/linux/drivers/sound/Readme.modules	Fri Apr 12 15:52:02 1996
+++ linux/drivers/sound/Readme.modules	Mon Apr 29 09:23:31 1996
@@ -72,28 +72,13 @@
 	file (sndscape.co[012]) to the card. Using large buffers doesn't
 	increase performance in these cases.
 
-Configuring device parameters when loading the driver
------------------------------------------------------
-
-The loadable version of the sound driver accepts now the same
-sound= parameter that has been available in the LILO command line.
-In this way it's possible to change I/O port, IRQ and DMA addresses
-and to enable/disable various cards at load time. Normally
-the driver uses the configuration parameters entered when compiling
-and configuring the driver.
-Look at Readme.linux for more info.
-
-NOTE!	This method is not normally required. You should use it only when
-	you have to use different configuration than normally. The sound=
-	command line parameter is error prone and not recommended.
-
 Debugging and tracing
 ---------------------
 
 Modularized sound driver doesn't display messages during initialization as
 the kernel compiled one does. This feature can be turned on by adding
-init_trace=1 to the insmod command line.
+trace_init=1 to the insmod command line.
 
 For example:
 
-	insmod sound init_trace=1
+	insmod sound trace_init=1
diff -u --recursive --new-file v1.3.96/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v1.3.96/linux/drivers/sound/pss.c	Fri Apr 12 15:52:03 1996
+++ linux/drivers/sound/pss.c	Mon Apr 29 08:00:29 1996
@@ -520,10 +520,11 @@
 
     case SNDCTL_COPR_SENDMSG:
       {
+	/* send buf->len words from buf->data to DSP */
+
 	copr_msg       *buf;
 	unsigned long   flags;
 	unsigned short *data;
-	unsigned short  tmp;
 	int             i;
 
 	buf = (copr_msg *) kmalloc (sizeof (copr_msg), GFP_KERNEL);
@@ -534,19 +535,17 @@
 
 	data = (unsigned short *) (buf->data);
 
-	/* printk( "SNDCTL_COPR_SENDMSG: data = %d", data ); */
-
 	save_flags (flags);
 	cli ();
 
 	for (i = 0; i < buf->len; i++)
 	  {
-	    tmp = *data++;
-	    if (!pss_put_dspword (devc, tmp))
+	    if (!pss_put_dspword (devc, *data++))
 	      {
 		restore_flags (flags);
-		buf->len = i;	/* feed back number of WORDs sent */
-		memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
+		/* feed back number of WORDs sent */
+		memcpy_tofs( (char *)(&(((copr_msg *)arg)->len)),
+				(char *)(&i), sizeof(buf->len));
 		kfree (buf);
 		return -EIO;
 	      }
@@ -562,6 +561,7 @@
 
     case SNDCTL_COPR_RCVMSG:
       {
+	/* try to read as much words as possible from DSP into buf */
 	copr_msg       *buf;
 	unsigned long   flags;
 	unsigned short *data;
@@ -571,27 +571,31 @@
 	buf = (copr_msg *) kmalloc (sizeof (copr_msg), GFP_KERNEL);
 	if (buf == NULL)
 	  return -ENOSPC;
-
+#if 0
 	memcpy_fromfs ((char *) buf, &(((char *) arg)[0]), sizeof (*buf));
+#endif
 
 	data = (unsigned short *) buf->data;
 
 	save_flags (flags);
 	cli ();
 
-	for (i = 0; i < buf->len; i++)
+	for (i = 0; i < sizeof(buf->data); i++)
 	  {
 	    if (!pss_get_dspword (devc, data++))
 	      {
 		buf->len = i;	/* feed back number of WORDs read */
-		err = -EIO;
+		err = (i==0)? -EIO : 0;	   /* EIO only if no word read */
 		break;
 	      }
 	  }
 
+	if( i==sizeof(buf->data) )
+		buf->len = i;
+
 	restore_flags (flags);
 
-	memcpy_tofs ((&((char *) arg)[0]), &buf, sizeof (buf));
+	memcpy_tofs ((&((char *) arg)[0]), buf, sizeof (*buf));
 	kfree (buf);
 
 	return err;
diff -u --recursive --new-file v1.3.96/linux/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- v1.3.96/linux/fs/ext2/ioctl.c	Wed Jan 17 07:31:58 1996
+++ linux/fs/ext2/ioctl.c	Sun Apr 28 11:28:10 1996
@@ -26,12 +26,16 @@
 
 	switch (cmd) {
 	case EXT2_IOC_GETFLAGS:
-		if ((err = verify_area (VERIFY_WRITE, (long *) arg, sizeof(long))))
+		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+		if (err)
 			return err;
-		put_fs_long (inode->u.ext2_i.i_flags, (long *) arg);
+		put_user(inode->u.ext2_i.i_flags, (int *) arg);
 		return 0;
 	case EXT2_IOC_SETFLAGS:
-		flags = get_fs_long ((long *) arg);
+		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+		if (err)
+			return err;
+		flags = get_user((int *) arg);
 		/*
 		 * The IMMUTABLE flag can only be changed by the super user
 		 * when the security level is zero.
@@ -59,16 +63,20 @@
 		inode->i_dirt = 1;
 		return 0;
 	case EXT2_IOC_GETVERSION:
-		if ((err = verify_area (VERIFY_WRITE, (long *) arg, sizeof(long))))
+		err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+		if (err)
 			return err;
-		put_fs_long (inode->u.ext2_i.i_version, (long *) arg);
+		put_user(inode->u.ext2_i.i_version, (int *) arg);
 		return 0;
 	case EXT2_IOC_SETVERSION:
 		if ((current->fsuid != inode->i_uid) && !fsuser())
 			return -EPERM;
 		if (IS_RDONLY(inode))
 			return -EROFS;
-		inode->u.ext2_i.i_version = get_fs_long ((long *) arg);
+		err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+		if (err)
+			return err;
+		inode->u.ext2_i.i_version = get_user((int *) arg);
 		inode->i_ctime = CURRENT_TIME;
 		inode->i_dirt = 1;
 		return 0;
diff -u --recursive --new-file v1.3.96/linux/fs/super.c linux/fs/super.c
--- v1.3.96/linux/fs/super.c	Tue Apr 23 13:57:11 1996
+++ linux/fs/super.c	Mon Apr 29 11:21:07 1996
@@ -30,6 +30,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/mm.h>
+#include <linux/fd.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -965,6 +966,7 @@
 
 #ifdef CONFIG_BLK_DEV_FD
 	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
+		floppy_eject();
 		printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
 		wait_for_keypress();
 	}
diff -u --recursive --new-file v1.3.96/linux/fs/ufs/ufs_super.c linux/fs/ufs/ufs_super.c
--- v1.3.96/linux/fs/ufs/ufs_super.c	Sat Apr 27 15:19:59 1996
+++ linux/fs/ufs/ufs_super.c	Mon Apr 29 11:02:59 1996
@@ -104,6 +104,7 @@
 	 *   and s_type when we return.
 	 */
 
+	MOD_INC_USE_COUNT;
 	lock_super (sb);
 
 	/* XXX - make everything read only for testing */
@@ -119,7 +120,8 @@
 	        }
 	        printk ("ufs_read_super: unable to read superblock\n");
 
-	        return 0;
+		MOD_DEC_USE_COUNT;
+	        return(NULL);
 	}
 	/* XXX - redo this so we can free it later... */
 	usb = (struct ufs_superblock *)__get_free_page(GFP_KERNEL); 
@@ -147,6 +149,7 @@
 	                printk ("ufs_read_super: bad magic number 0x%8.8x on dev %d/%d\n",
 	                        usb->fs_magic, MAJOR(sb->s_dev),
 	                        MINOR(sb->s_dev));
+		MOD_DEC_USE_COUNT;
 	        return(NULL);
 	}
 
@@ -238,7 +241,8 @@
 
        ufs_read_super_lose:
 	/* XXX - clean up */
-	return(0);
+	MOD_DEC_USE_COUNT;
+	return(NULL);
 }
 
 void ufs_put_super (struct super_block * sb)
diff -u --recursive --new-file v1.3.96/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v1.3.96/linux/include/asm-i386/floppy.h	Sun Apr 21 12:39:02 1996
+++ linux/include/asm-i386/floppy.h	Sun Apr 28 18:47:40 1996
@@ -33,6 +33,8 @@
 					       "floppy", NULL)
 #define fd_free_irq()		free_irq(FLOPPY_IRQ, NULL)
 #define fd_get_dma_residue()    SW._get_dma_residue(FLOPPY_DMA)
+#define fd_dma_mem_alloc(size)	SW._dma_mem_alloc(size)
+#define fd_dma_mem_free(addr,size)	SW._dma_mem_free(addr,size)
 
 static int virtual_dma_count=0;
 static int virtual_dma_residue=0;
@@ -193,6 +195,25 @@
 
 }
 
+static unsigned long dma_mem_alloc(unsigned long size)
+{
+	return __get_dma_pages(GFP_KERNEL,__get_order(size));
+}
+
+static void dma_mem_free(unsigned long addr, unsigned long size)
+{
+	free_pages(addr, __get_order(size));
+}
+
+static unsigned long vdma_mem_alloc(unsigned long size)
+{
+	return (unsigned long) vmalloc(size);
+}
+
+static void vdma_mem_free(unsigned long addr, unsigned long size)
+{
+	return vfree((void *)addr);
+}
 
 struct fd_routine_l {
 	void (*_enable_dma)(unsigned int dummy);
@@ -209,6 +230,8 @@
 			   unsigned long flags, 
 			   const char *device,
 			   void *dev_id);
+	unsigned long (*_dma_mem_alloc) (unsigned long size);
+	void (*_dma_mem_free)(unsigned long addr, unsigned long size);
 } fd_routine[] = {
 	{
 		enable_dma,
@@ -221,6 +244,8 @@
 		set_dma_count,
 		get_dma_residue,
 		request_irq,
+		dma_mem_alloc,
+		dma_mem_free
 	},
 	{
 		vdma_enable_dma,
@@ -232,7 +257,9 @@
 		vdma_set_dma_addr,
 		vdma_set_dma_count,
 		vdma_get_dma_residue,
-		vdma_request_irq
+		vdma_request_irq,
+		vdma_mem_alloc,
+		vdma_mem_free
 	}
 };
 
@@ -258,6 +285,6 @@
  * driver otherwise. It doesn't matter much for performance anyway, as most
  * floppy accesses go through the track buffer.
  */
-#define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
+#define CROSS_64KB(a,s) (((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64) && ! (use_virtual_dma & 1))
 
 #endif /* __ASM_I386_FLOPPY_H */
diff -u --recursive --new-file v1.3.96/linux/include/asm-mips/floppy.h linux/include/asm-mips/floppy.h
--- v1.3.96/linux/include/asm-mips/floppy.h	Fri Apr 12 15:52:05 1996
+++ linux/include/asm-mips/floppy.h	Sun Apr 28 18:47:40 1996
@@ -37,12 +37,33 @@
 #define fd_free_irq()           free_irq(FLOPPY_IRQ, NULL);
 
 #define MAX_BUFFER_SECTORS 24
-#define virtual_dma_init()                                              \
-        if (boot_info.machtype == MACH_ACER_PICA_61 ||                  \
-            boot_info.machtype == MACH_MIPS_MAGNUM_4000 ||              \
-            boot_info.machtype == MACH_OLIVETTI_M700)                   \
-		vdma_alloc(PHYSADDR(floppy_track_buffer),               \
-			   512*2*MAX_BUFFER_SECTORS);
+
+static unsigned long mips_dma_mem_alloc(unsigned long size)
+{
+	int order = __get_order(size);
+	unsigned long mem;
+
+	mem = __get_dma_pages(GFP_KERNEL,order);
+	if(!mem)
+		return 0;
+        if (boot_info.machtype == MACH_ACER_PICA_61 ||
+            boot_info.machtype == MACH_MIPS_MAGNUM_4000 ||
+            boot_info.machtype == MACH_OLIVETTI_M700)
+		vdma_alloc(PHYSADDR(mem), size);
+	return mem;
+}
+
+static void mips_dma_mem_free(unsigned long addr, unsigned long size)
+{       
+        if (boot_info.machtype == MACH_ACER_PICA_61 ||
+            boot_info.machtype == MACH_MIPS_MAGNUM_4000 ||
+            boot_info.machtype == MACH_OLIVETTI_M700)
+		vdma_free(PHYSADDR(addr));
+	free_pages(addr, __get_order(size));	
+}
+
+#define fd_dma_mem_alloc(mem,size) mips_dma_mem_alloc(mem,size)
+#define fd_dma_mem_free(mem) mips_dma_mem_free(mem)
 
 /*
  * And on Mips's the CMOS info fails also ...
diff -u --recursive --new-file v1.3.96/linux/include/asm-sparc/floppy.h linux/include/asm-sparc/floppy.h
--- v1.3.96/linux/include/asm-sparc/floppy.h	Wed Apr 24 17:00:42 1996
+++ linux/include/asm-sparc/floppy.h	Sun Apr 28 18:47:40 1996
@@ -344,4 +344,15 @@
 	return -1;
 }
 
+static int sparc_eject(void)
+{
+	set_dor(0, ~0, 0x90);
+	udelay(500);
+	set_dor(0, ~0x90, 0);
+	udelay(500);
+	return 0;
+}
+
+#define fd_eject(drive) sparc_eject()
+
 #endif /* !(__ASM_SPARC_FLOPPY_H) */
diff -u --recursive --new-file v1.3.96/linux/include/linux/aztcd.h linux/include/linux/aztcd.h
--- v1.3.96/linux/include/linux/aztcd.h	Tue Apr  2 13:32:22 1996
+++ linux/include/linux/aztcd.h	Sun Apr 28 12:09:28 1996
@@ -1,4 +1,4 @@
-/* $Id: aztcd.h,v 2.20 1996/03/12 18:31:30 root Exp root $
+/* $Id: aztcd.h,v 2.30 1996/04/26 05:33:56 root Exp root $
  *
  * Definitions for a AztechCD268 CD-ROM interface
  *	Copyright (C) 1994, 1995  Werner Zimmermann
@@ -22,7 +22,6 @@
  *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
  *		October 1994 Email: zimmerma@rz.fht-esslingen.de
  */
-
 
 /* *** change this to set the I/O port address of your CD-ROM drive*/
 #define AZT_BASE_ADDR		0x320
diff -u --recursive --new-file v1.3.96/linux/include/linux/fd.h linux/include/linux/fd.h
--- v1.3.96/linux/include/linux/fd.h	Fri Apr 12 15:52:07 1996
+++ linux/include/linux/fd.h	Sun Apr 28 18:47:40 1996
@@ -368,4 +368,10 @@
 #define FDEJECT _IO(2, 0x5a)
 /* eject the disk */
 
+
+#ifdef __KERNEL__
+/* eject the boot floppy (if we need the drive for a different root floppy) */
+void floppy_eject(void);
+#endif
+
 #endif
diff -u --recursive --new-file v1.3.96/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.96/linux/include/linux/fs.h	Wed Apr 24 17:00:43 1996
+++ linux/include/linux/fs.h	Mon Apr 29 17:26:03 1996
@@ -325,7 +325,7 @@
 	loff_t f_pos;
 	unsigned short f_flags;
 	unsigned short f_count;
-	unsigned long f_reada, f_ramax, f_rapos, f_ralen, f_rawin;
+	unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
 	struct file *f_next, *f_prev;
 	int f_owner;		/* pid or -pgrp where SIGIO should be sent */
 	struct inode * f_inode;
diff -u --recursive --new-file v1.3.96/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.96/linux/include/linux/skbuff.h	Sat Apr 27 15:20:07 1996
+++ linux/include/linux/skbuff.h	Mon Apr 29 17:26:12 1996
@@ -18,6 +18,7 @@
 #include <linux/time.h>
 
 #include <asm/atomic.h>
+#include <asm/types.h>
 
 #define CONFIG_SKB_CHECK 0
 
diff -u --recursive --new-file v1.3.96/linux/init/main.c linux/init/main.c
--- v1.3.96/linux/init/main.c	Sat Apr 27 15:20:07 1996
+++ linux/init/main.c	Sun Apr 28 12:13:26 1996
@@ -80,6 +80,7 @@
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
+extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void ppa_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
@@ -307,6 +308,9 @@
 #endif
 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
 	{ "fdomain=", fdomain_setup},
+#endif
+#ifdef CONFIG_SCSI_IN2000
+	{ "in2000=", in2000_setup},
 #endif
 #ifdef CONFIG_SCSI_PPA
         { "ppa=", ppa_setup },
diff -u --recursive --new-file v1.3.96/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.96/linux/kernel/ksyms.c	Fri Apr 12 15:52:09 1996
+++ linux/kernel/ksyms.c	Sun Apr 28 00:45:54 1996
@@ -120,6 +120,7 @@
 	X(do_munmap),
 	X(insert_vm_struct),
 	X(merge_segments),
+	X(exit_mm),
 
 	/* internal kernel memory management */
 	X(__get_free_pages),
diff -u --recursive --new-file v1.3.96/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.96/linux/mm/filemap.c	Sat Apr 27 15:20:09 1996
+++ linux/mm/filemap.c	Mon Apr 29 08:31:26 1996
@@ -356,8 +356,8 @@
 			total_rawin/total_reada,
 			(total_async*100)/total_reada);
 #ifdef DEBUG_READAHEAD
-		printk("Readahead snapshot: max=%ld, len=%ld, win=%ld, rapos=%ld\n",
-			filp->f_ramax, filp->f_ralen, filp->f_rawin, filp->f_rapos);
+		printk("Readahead snapshot: max=%ld, len=%ld, win=%ld, raend=%ld\n",
+			filp->f_ramax, filp->f_ralen, filp->f_rawin, filp->f_raend);
 #endif
 
 		total_reada	= 0;
@@ -375,7 +375,7 @@
  * Read-ahead context:
  * -------------------
  * The read ahead context fields of the "struct file" are the following:
- * - f_rapos : position of the first byte after the last page we tried to
+ * - f_raend : position of the first byte after the last page we tried to
  *             read ahead.
  * - f_ramax : current read-ahead maximum size.
  * - f_ralen : length of the current IO read block we tried to read-ahead.
@@ -389,14 +389,15 @@
  * ------------------
  * MIN_READAHEAD   : minimum read-ahead size when read-ahead.
  * MAX_READAHEAD   : maximum read-ahead size when read-ahead.
- * MAX_READWINDOW  : maximum read window length.
  *
  * Synchronous read-ahead benefits:
  * --------------------------------
  * Using reasonable IO xfer length from peripheral devices increase system 
  * performances.
  * Reasonable means, in this context, not too large but not too small.
- * The actual maximum value is MAX_READAHEAD + PAGE_SIZE = 32k
+ * The actual maximum value is:
+ *	MAX_READAHEAD + PAGE_SIZE = 76k is CONFIG_READA_SMALL is undefined
+ *      and 32K if defined.
  *
  * Asynchronous read-ahead benefits:
  * ---------------------------------
@@ -421,110 +422,117 @@
  * - The number of effective pending IO read requests.
  *   ONE seems to be the only reasonable value.
  * - The total memory pool usage for the file access stream.
- *   We try to have a limit of MAX_READWINDOW = 48K.
+ *   This maximum memory usage is implicitely 2 IO read chunks:
+ *   2*(MAX_READAHEAD + PAGE_SIZE) = 156K if CONFIG_READA_SMALL is undefined,
+ *   64k if defined.
  */
 
-#define MAX_READWINDOW (PAGE_SIZE*12)
+#if 0 /* small readahead */
 #define MAX_READAHEAD (PAGE_SIZE*7)
-#define MIN_READAHEAD (PAGE_SIZE)
+#define MIN_READAHEAD (PAGE_SIZE*2)
+#else
+#define MAX_READAHEAD (PAGE_SIZE*18)
+#define MIN_READAHEAD (PAGE_SIZE*3)
+#endif
 
-static inline unsigned long generic_file_readahead(struct file * filp, struct inode * inode,
-	int try_async, unsigned long pos, struct page * page,
+static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode,
+	unsigned long pos, struct page * page,
 	unsigned long page_cache)
 {
 	unsigned long max_ahead, ahead;
-	unsigned long rapos, ppos;
+	unsigned long raend, ppos;
 
 	ppos = pos & PAGE_MASK;
-	rapos = filp->f_rapos & PAGE_MASK;
+	raend = filp->f_raend & PAGE_MASK;
 	max_ahead = 0;
 
 /*
- * If the current page is locked, and if the current position is outside the
- * previous read IO request, try some synchronous read-ahead in order
- * to avoid too small IO requests.
+ * The current page is locked.
+ * If the current position is inside the previous read IO request, do not
+ * try to reread previously read ahead pages.
+ * Otherwise decide or not to read ahead some pages synchronously.
+ * If we are not going to read ahead, set the read ahead context for this 
+ * page only.
  */
 	if (PageLocked(page)) {
-	        if (!rapos || ppos >= rapos || ppos + filp->f_ralen < rapos) {
-			rapos = ppos;
-			if (rapos < inode->i_size)
+		if (!filp->f_ralen || ppos >= raend || ppos + filp->f_ralen < raend) {
+			raend = ppos;
+			if (raend < inode->i_size)
 				max_ahead = filp->f_ramax;
 			filp->f_rawin = 0;
 			filp->f_ralen = PAGE_SIZE;
+			if (!max_ahead) {
+				filp->f_raend  = ppos + filp->f_ralen;
+				filp->f_rawin += filp->f_ralen;
+			}
 		}
 	}
 /*
- * The current page is not locked
- * If the current position is inside the last read-ahead IO request,
- * it is the moment to try asynchronous read-ahead.
- * try_async = 2 means that we have to force unplug of the device in
- * order to force read IO asynchronously.
+ * The current page is not locked.
+ * If we were reading ahead and,
+ * if the current max read ahead size is not zero and,
+ * if the current position is inside the last read-ahead IO request,
+ *   it is the moment to try to read ahead asynchronously.
+ * We will later force unplug device in order to force asynchronous read IO.
  */
-	else if (try_async == 1 && rapos >= PAGE_SIZE &&
-	         ppos <= rapos && ppos + filp->f_ralen >= rapos) {
+	else if (reada_ok && filp->f_ramax && raend >= PAGE_SIZE &&
+	         ppos <= raend && ppos + filp->f_ralen >= raend) {
 /*
  * Add ONE page to max_ahead in order to try to have about the same IO max size
  * as synchronous read-ahead (MAX_READAHEAD + 1)*PAGE_SIZE.
- * Compute the position of the last page we have tried to read.
+ * Compute the position of the last page we have tried to read in order to 
+ * begin to read ahead just at the next page.
  */
-		rapos -= PAGE_SIZE;
-		if (rapos < inode->i_size)
+		raend -= PAGE_SIZE;
+		if (raend < inode->i_size)
 			max_ahead = filp->f_ramax + PAGE_SIZE;
 
 		if (max_ahead) {
 			filp->f_rawin = filp->f_ralen;
 			filp->f_ralen = 0;
-			try_async = 2;
+			reada_ok      = 2;
 		}
 	}
 /*
- * Try to read pages.
- * We hope that ll_rw_blk() plug/unplug, coalescence and sort will work fine
- * enough to avoid too bad actuals IO requests.
+ * Try to read ahead pages.
+ * We hope that ll_rw_blk() plug/unplug, coalescence, requests sort and the
+ * scheduler, will work enough for us to avoid too bad actuals IO requests.
  */
 	ahead = 0;
 	while (ahead < max_ahead) {
 		ahead += PAGE_SIZE;
-		page_cache = try_to_read_ahead(inode, rapos + ahead, page_cache);
+		page_cache = try_to_read_ahead(inode, raend + ahead, page_cache);
 	}
 /*
- * If we tried to read some pages,
+ * If we tried to read ahead some pages,
+ * If we tried to read ahead asynchronously,
+ *   Try to force unplug of the device in order to start an asynchronous
+ *   read IO request.
  * Update the read-ahead context.
  * Store the length of the current read-ahead window.
- * Add PAGE_SIZE to the max read ahead size each time we have read-ahead
- *   That recipe avoid to do some large IO for files that are not really
+ * Double the current max read ahead size.
+ *   That heuristic avoid to do some large IO for files that are not really
  *   accessed sequentially.
- * Do that only if the read ahead window is lower that MAX_READWINDOW
- * in order to limit the amount of pages used for this file access context.
- * If asynchronous,
- *    Try to force unplug of the device in order to start an asynchronous
- *    read IO request.
  */
 	if (ahead) {
+		if (reada_ok == 2) {
+			run_task_queue(&tq_disk);
+		}
+
 		filp->f_ralen += ahead;
 		filp->f_rawin += filp->f_ralen;
-		filp->f_rapos = rapos + ahead + PAGE_SIZE;
+		filp->f_raend = raend + ahead + PAGE_SIZE;
 
-		if (filp->f_rawin < MAX_READWINDOW)
-			filp->f_ramax += PAGE_SIZE;
-		else if (filp->f_rawin > MAX_READWINDOW && filp->f_ramax > PAGE_SIZE)
-			filp->f_ramax -= PAGE_SIZE;
+		filp->f_ramax += filp->f_ramax;
 
 		if (filp->f_ramax > MAX_READAHEAD)
 			filp->f_ramax = MAX_READAHEAD;
+
 #ifdef PROFILE_READAHEAD
-		profile_readahead((try_async == 2), filp);
+		profile_readahead((reada_ok == 2), filp);
 #endif
-		if (try_async == 2) {
-			run_task_queue(&tq_disk);
-		}
-	}
-/*
- * Wait on the page if necessary
- */
-	if (PageLocked(page)) {
-		__wait_on_page(page);
 	}
+
 	return page_cache;
 }
 
@@ -542,7 +550,7 @@
 {
 	int error, read;
 	unsigned long pos, ppos, page_cache;
-	int try_async;
+	int reada_ok;
 
 	if (count <= 0)
 		return 0;
@@ -554,47 +562,41 @@
 	pos = filp->f_pos;
 	ppos = pos & PAGE_MASK;
 /*
- * Check if the current position is inside the previous read-ahead window.
- * If that's true, We assume that the file accesses are sequential enough to
- * continue asynchronous read-ahead.
- * Do minimum read-ahead at the beginning of the file since some tools
- * only read the beginning of files.
- * Break read-ahead if the file position is outside the previous read ahead
- * window or if read-ahead position is 0.
- */
-/*
- * Will not try asynchronous read-ahead.
- * Reset to zero, read-ahead context.
- */
-	if (pos+count < MIN_READAHEAD || !filp->f_rapos ||
-	    ppos > filp->f_rapos || ppos + filp->f_rawin < filp->f_rapos) {
-		try_async = 0;
-		filp->f_rapos = 0;
+ * If the current position is outside the previous read-ahead window, 
+ * we reset the current read-ahead context and set read ahead max to zero
+ * (will be set to just needed value later),
+ * otherwise, we assume that the file accesses are sequential enough to
+ * continue read-ahead.
+ */
+	if (ppos > filp->f_raend || ppos + filp->f_rawin < filp->f_raend) {
+		reada_ok = 0;
+		filp->f_raend = 0;
 		filp->f_ralen = 0;
 		filp->f_ramax = 0;
 		filp->f_rawin = 0;
-/*
- * Will try asynchronous read-ahead.
- */
 	} else {
-		try_async = 1;
+		reada_ok = 1;
 	}
 /*
  * Adjust the current value of read-ahead max.
  * If the read operation stay in the first half page, force no readahead.
- * Otherwise try first some value near count.
- *      do at least MIN_READAHEAD and at most MAX_READAHEAD.
+ * Otherwise try to increase read ahead max just enough to do the read request.
+ * Then, at least MIN_READAHEAD if read ahead is ok,
+ * and at most MAX_READAHEAD in all cases.
  */
 	if (pos + count <= (PAGE_SIZE >> 1)) {
-		try_async = 0;
 		filp->f_ramax = 0;
 	} else {
-		if (filp->f_ramax < count)
-			filp->f_ramax = count & PAGE_MASK;
+		unsigned long needed;
 
-		if (filp->f_ramax < MIN_READAHEAD)
-			filp->f_ramax = MIN_READAHEAD;
-		else if (filp->f_ramax > MAX_READAHEAD)
+		needed = ((pos + count) & PAGE_MASK) - (pos & PAGE_MASK);
+
+		if (filp->f_ramax < needed)
+			filp->f_ramax = needed;
+
+		if (reada_ok && filp->f_ramax < MIN_READAHEAD)
+				filp->f_ramax = MIN_READAHEAD;
+		if (filp->f_ramax > MAX_READAHEAD)
 			filp->f_ramax = MAX_READAHEAD;
 	}
 
@@ -640,17 +642,19 @@
 		if (nr > count)
 			nr = count;
 /*
- * Do not try to readahead if the current page is not filled or being filled.
- * If our goal was to try asynchronous read-ahead, we were quite wrong.
- * Set max readahead to some shorter value in order to fix a little
- * this mistake.
+ * Try to read ahead only if the current page is filled or being filled.
+ * Otherwise, if we were reading ahead, decrease max read ahead size to
+ * the minimum value.
+ * In this context, that seems to may happen only on some read error or if 
+ * the page has been rewritten.
  */
 		if (PageUptodate(page) || PageLocked(page))
-			page_cache = generic_file_readahead(filp, inode, try_async, pos, page, page_cache);
-		else if (try_async) {
-			if (filp->f_ramax > MIN_READAHEAD)
-				filp->f_ramax -= PAGE_SIZE;
-		}
+			page_cache = generic_file_readahead(reada_ok, filp, inode, pos, page, page_cache);
+		else if (reada_ok && filp->f_ramax > MIN_READAHEAD)
+				filp->f_ramax = MIN_READAHEAD;
+
+		if (PageLocked(page))
+			__wait_on_page(page);
 
 		if (!PageUptodate(page))
 			goto read_page;
@@ -685,6 +689,16 @@
 		 * page or not..
 		 */
 read_page:
+/*
+ * We have to read the page.
+ * If we were reading ahead, we had previously tried to read this page,
+ * That means that the page has probably been removed from the cache before 
+ * the application process needs it, or has been rewritten.
+ * Decrease max readahead size to the minimum value in that situation.
+ */
+		if (reada_ok && filp->f_ramax > MIN_READAHEAD)
+			filp->f_ramax = MIN_READAHEAD;
+
 		error = inode->i_op->readpage(inode, page);
 		if (!error) {
 			if (!PageError(page))
diff -u --recursive --new-file v1.3.96/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v1.3.96/linux/net/ipv4/Config.in	Sat Apr 27 15:20:10 1996
+++ linux/net/ipv4/Config.in	Sat Apr 27 20:01:37 1996
@@ -21,9 +21,9 @@
 if [ "$CONFIG_NET_ALIAS" = "y" ]; then
 	tristate 'IP: aliasing support' CONFIG_IP_ALIAS
 fi
-if [ "$CONFIG_KERNELD" = "y" ]; then
+#if [ "$CONFIG_KERNELD" = "y" ]; then
 #	bool 'IP: ARP daemon support (experimental)' CONFIG_ARPD
-fi
+#fi
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
