diff -u --recursive --new-file linux-1.1.55+new_quota/ANNOUNCE.eata linux/ANNOUNCE.eata
--- linux-1.1.55+new_quota/ANNOUNCE.eata	Wed Dec 31 18:00:00 1969
+++ linux/ANNOUNCE.eata	Thu Oct 20 18:15:32 1994
@@ -0,0 +1,39 @@
+This is the announcement for the EATA SCSI driver
+
+The driver supports all EATA-DMA Protocol compliant SCSI 
+ISA and EISA controllers (PCI rsn.)
+
+Those are for example:
+        DPT Smartcache    : PM2011 (ISA), PM2012A (EISA), 
+                            PM2012B (EISA) 
+            Smartcache III: PM2021 (ISA), PM2022 (EISA), 
+                            PM2122 (EISA), PM2322 (EISA)
+        controllers from NEC and AT&T.
+
+The driver has been tested on the PM2011, PM2021 and PM2022 
+
+I call it still ALPHA software but the driver is pretty stable
+and has been used for some months now. There are just some  
+features missing (PCI/multiple HAs) which I want to implement
+in the next time.
+
+The installation is basically a plug and play installation.
+Just "cd" to your linux directory and extract the files from the 
+TAR archive. Then you will find the diffile eata_patch.
+Apply that with  "patch -p1 <patch.eata" against your linux source 
+tree. The diffs were made against Linux 1.1.51
+
+The (hopefully) only problem that might appear is that you have
+to configure the disk geometry of your first two drives. This 
+problem only appears for people who were already able to use
+their controller under Linux in IDE emulation mode and are sharing
+the drives with DOS or some other OS that operates the controller
+in IDE emulation mode (ie. doesn't use a SCSI driver).
+
+If you've got problems, questions or whatever, send me a mail 
+
+neuffer@goofy.zdv.uni-mainz.de
+Michael_Neuffer@wi2.maus.de (Mails<16KB & <48KB per day)
+
+
+Mike
diff -u --recursive --new-file linux-1.1.55+new_quota/README-Slackware linux/README-Slackware
--- linux-1.1.55+new_quota/README-Slackware	Wed Dec 31 18:00:00 1969
+++ linux/README-Slackware	Thu Oct 20 18:15:32 1994
@@ -0,0 +1,14 @@
+The following changes were made to this kernel:
+
+Added nec260-0_3 patches (NEC CD-ROM on the IDE interface)
+Added sony535-0.6 patches (Sony 531/535 CD-ROM drives)
+Added dpt_EATA_0.3a patches (EATA compliant ISA and EISA SCSI)
+Added Adaptec 274x SCSI patches.
+Added Always in2000 patches.
+
+NOTE: IN2000 and Adaptec 1742 drivers should not be compiled into the same
+kernel.  Do not compile the NEC CD-ROM driver into your kernel unless you
+have an NEC CD-ROM driver attached as the slave device on your IDE controller.
+I have no idea what it does if it finds a hard drive there...
+
+Pat
diff -u --recursive --new-file linux-1.1.55+new_quota/arch/i386/config.in linux/arch/i386/config.in
--- linux-1.1.55+new_quota/arch/i386/config.in	Thu Oct 20 17:09:39 1994
+++ linux/arch/i386/config.in	Thu Oct 20 18:17:15 1994
@@ -12,8 +12,9 @@
 bool 'Filesystem quota support' CONFIG_QUOTA n
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'System V IPC' CONFIG_SYSVIPC y
-bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
+bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+bool 'Do you wish to be offered ALPHA drivers added by Slackware?' ALPHA_SLACK y
 
 if [ "$CONFIG_NET" = "y" ]; then
 comment 'Networking options'
@@ -52,11 +53,15 @@
 bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
 bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
 bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
+if [ "$ALPHA_SLACK" = "y" ]; then
+  bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X y
+  bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
+  bool 'EATA-DMA (rev. 2.0b) (DPT,NEC,AT&T) support' CONFIG_SCSI_EATA n
+fi
 bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC y
 bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
 bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
 bool 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx y
-#bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
 bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
 bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
 bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
@@ -134,6 +139,10 @@
 comment 'CD-ROM drivers'
 
 bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
+if [ "$ALPHA_SLACK" = "y" ]; then
+  bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
+  bool 'NEC CDR-260 CDROM driver support' CONFIG_NEC260 n
+fi
 bool 'Mitsumi CDROM driver support' CONFIG_MCD n
 bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
 if [ "$CONFIG_SBPCD" = "y" ]; then
diff -u --recursive --new-file linux-1.1.55+new_quota/config.in linux/config.in
--- linux-1.1.55+new_quota/config.in	Thu Oct 20 17:09:26 1994
+++ linux/config.in	Thu Oct 20 18:17:01 1994
@@ -12,8 +12,9 @@
 bool 'Filesystem quota support' CONFIG_QUOTA n
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'System V IPC' CONFIG_SYSVIPC y
-bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
+bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+bool 'Do you wish to be offered ALPHA drivers added by Slackware?' ALPHA_SLACK y
 
 if [ "$CONFIG_NET" = "y" ]; then
 comment 'Networking options'
@@ -52,11 +53,15 @@
 bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
 bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
 bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
+if [ "$ALPHA_SLACK" = "y" ]; then
+  bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X y
+  bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
+  bool 'EATA-DMA (rev. 2.0b) (DPT,NEC,AT&T) support' CONFIG_SCSI_EATA n
+fi
 bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC y
 bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
 bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
 bool 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx y
-#bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
 bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
 bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
 bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
@@ -134,6 +139,10 @@
 comment 'CD-ROM drivers'
 
 bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
+if [ "$ALPHA_SLACK" = "y" ]; then
+  bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
+  bool 'NEC CDR-260 CDROM driver support' CONFIG_NEC260 n
+fi
 bool 'Mitsumi CDROM driver support' CONFIG_MCD n
 bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
 if [ "$CONFIG_SBPCD" = "y" ]; then
diff -u --recursive --new-file linux-1.1.55+new_quota/config.old linux/config.old
--- linux-1.1.55+new_quota/config.old	Thu Oct 20 17:09:06 1994
+++ linux/config.old	Wed Dec 31 18:00:00 1969
@@ -1,209 +0,0 @@
-#
-# For a description of the syntax of this configuration file,
-# see the Configure script.
-#
-
-comment 'General setup'
-
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
-bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
-bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
-bool 'Networking support' CONFIG_NET y
-bool 'Filesystem quota support' CONFIG_QUOTA n
-bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
-bool 'System V IPC' CONFIG_SYSVIPC y
-bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
-bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
-
-if [ "$CONFIG_NET" = "y" ]; then
-comment 'Networking options'
-bool 'TCP/IP networking' CONFIG_INET y
-if [ "$CONFIG_INET" "=" "y" ]; then
-bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n
-comment '(it is safe to leave these untouched)'
-bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n
-bool 'Reverse ARP' CONFIG_INET_RARP n
-bool 'Assume subnets are local' CONFIG_INET_SNARL y
-bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
-fi
-bool 'The IPX protocol' CONFIG_IPX n
-#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
-fi
-
-comment 'SCSI support'
-
-bool 'SCSI support?' CONFIG_SCSI y
-
-if [ "$CONFIG_SCSI" = "n" ]; then
-
-comment 'Skipping SCSI configuration options...'
-
-else
-
-comment 'SCSI support type (disk, tape, CDrom)'
-
-bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
-bool 'Scsi tape support' CONFIG_CHR_DEV_ST y
-bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y
-bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
-
-comment 'SCSI low-level drivers'
-
-bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
-bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
-bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
-bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC y
-bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
-bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
-bool 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx y
-#bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
-bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
-bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
-bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
-bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR y
-bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST y
-#bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
-fi
-
-
-if [ "$CONFIG_NET" = "y" ]; then
-
-comment 'Network device support'
-
-bool 'Network device support?' CONFIG_NETDEVICES y
-if [ "$CONFIG_NETDEVICES" = "n" ]; then
-
-comment 'Skipping network driver configuration options...'
-
-else
-bool 'Dummy net driver support' CONFIG_DUMMY n
-bool 'SLIP (serial line) support' CONFIG_SLIP y
-if [ "$CONFIG_SLIP" = "y" ]; then
-  bool ' CSLIP compressed headers' SL_COMPRESSED y
-#  bool ' SLIP debugging on' SL_DUMP y
-fi
-bool 'PPP (point-to-point) support' CONFIG_PPP y
-bool 'PLIP (parallel port) support' CONFIG_PLIP n
-bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
-bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
-bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC y
-if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
-	bool 'WD80*3 support' CONFIG_WD80x3 y
-	bool 'SMC Ultra support' CONFIG_ULTRA y
-fi
-bool '3COM cards' CONFIG_NET_VENDOR_3COM y
-if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
-	bool '3c501 support' CONFIG_EL1 y
-	bool '3c503 support' CONFIG_EL2 y
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool '3c505 support' CONFIG_ELPLUS n
-		bool '3c507 support' CONFIG_EL16 n
-	fi
-	bool '3c509/3c579 support' CONFIG_EL3 y
-fi
-bool 'Other ISA cards' CONFIG_NET_ISA y
-if [ "$CONFIG_NET_ISA" = "y" ]; then
-	bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE y
-	bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 y
-	bool 'DEPCA support' CONFIG_DEPCA y
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool 'EtherExpress support' CONFIG_EEXPRESS n
-		bool 'AT1700 support' CONFIG_AT1700 n
-		bool 'NI5210 support' CONFIG_NI52 n
-		bool 'NI6510 support' CONFIG_NI65 n
-	fi
-	bool 'HP PCLAN support' CONFIG_HPLAN y
-	bool 'NE2000/NE1000 support' CONFIG_NE2000 y
-	bool 'SK_G16 support' CONFIG_SK_G16 y
-fi
-bool 'EISA and on board controllers' CONFIG_NET_EISA y
-	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
-	fi
-	bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT y
-bool 'Pocket and portable adaptors' CONFIG_NET_POCKET y
-if [ "$CONFIG_NET_POCKET" = "y" ]; then
-	bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 y
-	bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 y
-	bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP y
-	bool 'Zenith Z-Note support' CONFIG_ZNET y
-fi
-fi
-fi
-
-comment 'CD-ROM drivers'
-
-bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
-bool 'Mitsumi CDROM driver support' CONFIG_MCD n
-bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
-if [ "$CONFIG_SBPCD" = "y" ]; then
-  bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
-  if [ "$CONFIG_SBPCD2" = "y" ]; then
-    bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
-    if [ "$CONFIG_SBPCD3" = "y" ]; then
-      bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
-    fi
-  fi
-fi
-
-comment 'Filesystems'
-
-bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
-bool 'Extended fs support' CONFIG_EXT_FS n
-bool 'Second extended fs support' CONFIG_EXT2_FS y
-bool 'xiafs filesystem support' CONFIG_XIA_FS y
-bool 'msdos fs support' CONFIG_MSDOS_FS y
-if [ "$CONFIG_MSDOS_FS" = "y" ]; then
-bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS y
-fi
-bool '/proc filesystem support' CONFIG_PROC_FS y
-if [ "$CONFIG_INET" = "y" ]; then
-bool 'NFS filesystem support' CONFIG_NFS_FS y
-fi
-bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
-bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS y
-bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
-
-comment 'character devices'
-
-bool 'Parallel printer support' CONFIG_PRINTER y
-bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
-bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
-if [ "$CONFIG_PSMOUSE" = "y" ]; then
-bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
-fi
-bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
-bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
-bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION y
-
-bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
-if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
-if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
-
-comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
-
-else
-
-comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
-comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
-
-fi
-fi
-
-bool 'QIC-117 tape support' CONFIG_FTAPE y
-if [ "$CONFIG_FTAPE" = "y" ]; then
-int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
-fi
-
-comment 'Sound'
-
-bool 'Sound card support' CONFIG_SOUND n
-
-comment 'Kernel hacking'
-
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
-bool 'Kernel profiling support' CONFIG_PROFILE n
-if [ "$CONFIG_SCSI" = "y" ]; then
-bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS n
-fi
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/Makefile linux/drivers/block/Makefile
--- linux-1.1.55+new_quota/drivers/block/Makefile	Sat Sep 17 21:44:49 1994
+++ linux/drivers/block/Makefile	Thu Oct 20 18:15:33 1994
@@ -64,7 +64,16 @@
 SRCS := $(SRCS) xd.c
 endif
 
-all: block.a
+ifdef CONFIG_CDU535
+OBJS := $(OBJS) sonycd535.o
+SRCS := $(SRCS) sonycd535.c
+endif
+
+ifdef CONFIG_NEC260
+OBJS := $(OBJS) nec260.o
+SRCS := $(SRCS) nec260.c
+CFLAGS := $(CFLAGS) -DLINKED_IN_KERNEL -DNO_IRQACTION
+endif
 
 block.a: $(OBJS)
 	rm -f block.a
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/blk.h linux/drivers/block/blk.h
--- linux-1.1.55+new_quota/drivers/block/blk.h	Thu Oct 13 21:31:06 1994
+++ linux/drivers/block/blk.h	Thu Oct 20 18:15:33 1994
@@ -40,6 +40,9 @@
 extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
 extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
 extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
+#ifdef CONFIG_CDU535
+extern unsigned long init_sony535(unsigned long mem_start, unsigned long mem_end);
+#endif
 #ifdef CONFIG_SBPCD
 extern unsigned long sbpcd_init(unsigned long, unsigned long);
 #endif CONFIG_SBPCD
@@ -135,6 +138,14 @@
 
 #define DEVICE_NAME "CDU31A"
 #define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/hd.c linux/drivers/block/hd.c
--- linux-1.1.55+new_quota/drivers/block/hd.c	Thu Oct 20 15:14:27 1994
+++ linux/drivers/block/hd.c	Thu Oct 20 18:18:47 1994
@@ -43,6 +43,10 @@
 #define MAJOR_NR HD_MAJOR
 #include "blk.h"
 
+#ifdef CONFIG_NEC260
+int the_nec260_major = 0;
+#endif
+
 #define HD_IRQ 14
 
 static int revalidate_hddisk(int, int);
@@ -659,6 +663,9 @@
 	reset = 1;
 	dev = DEVICE_NR(CURRENT->dev);
 	printk("hd%c: timeout\n", dev+'a');
+#ifdef CONFIG_NEC260
+	if (CURRENT->dev < 0) return;
+#endif
 	if (++CURRENT->errors >= MAX_ERRORS) {
 #ifdef DEBUG
 		printk("hd%c: too many errors\n", dev+'a');
@@ -717,6 +724,14 @@
 		return;
 repeat:
 	timer_active &= ~(1<<HD_TIMER);
+#ifdef CONFIG_NEC260
+	if (!CURRENT && the_nec260_major > 0 &&
+	    blk_dev[the_nec260_major].current_request) {
+	  cli ();
+	  (blk_dev[the_nec260_major].request_fn) ();
+	}
+	if (CURRENT && CURRENT->dev < 0) return;
+#endif
 	sti();
 	INIT_REQUEST;
 	if (reset) {
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-1.1.55+new_quota/drivers/block/ll_rw_blk.c	Thu Oct 20 17:01:16 1994
+++ linux/drivers/block/ll_rw_blk.c	Thu Oct 20 18:15:34 1994
@@ -34,6 +34,10 @@
 
 int read_ahead[MAX_BLKDEV] = {0, };
 
+#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A)
+	int sonycd_save_mem_start;
+#endif
+
 /* blk_dev_struct is:
  *	do_request-address
  *	next-request
@@ -504,15 +508,36 @@
 #ifdef CONFIG_BLK_DEV_XD
 	mem_start = xd_init(mem_start,mem_end);
 #endif
+#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A)
+	{  /* since controllers for 535 and 31A can be at same location
+	    * we have to be careful.
+	    */
+		sonycd_save_mem_start = mem_start;
+		mem_start = cdu31a_init(mem_start,mem_end);
+		if ( mem_start == sonycd_save_mem_start ) {  /* CDU31A not found */
+			mem_start = init_sony535(mem_start,mem_end);
+		}
+	}
+#else
 #ifdef CONFIG_CDU31A
 	mem_start = cdu31a_init(mem_start,mem_end);
 #endif
+#ifdef CONFIG_CDU535
+	mem_start = init_sony535(mem_start,mem_end);
+#endif
+#endif  /* CONFIG_CDU31A && CONFIG_CDU535 */
 #ifdef CONFIG_MCD
 	mem_start = mcd_init(mem_start,mem_end);
 #endif
 #ifdef CONFIG_SBPCD
 	mem_start = sbpcd_init(mem_start, mem_end);
 #endif CONFIG_SBPCD
+#ifdef CONFIG_NEC260
+        {
+          extern unsigned long nec260_init (unsigned long, unsigned long);
+	  mem_start = nec260_init(mem_start, mem_end);
+	}
+#endif
 	if (ramdisk_size)
 		mem_start += rd_init(mem_start, ramdisk_size*1024);
 	return mem_start;
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/mcd.c linux/drivers/block/mcd.c
--- linux-1.1.55+new_quota/drivers/block/mcd.c	Sat Sep 17 21:48:13 1994
+++ linux/drivers/block/mcd.c	Thu Oct 20 18:15:34 1994
@@ -1182,7 +1182,6 @@
 
 	mcd_invalidate_buffers();
 	mcdPresent = 1;
-	printk("\n");
 	return mem_start;
 }
 
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/nec260.c linux/drivers/block/nec260.c
--- linux-1.1.55+new_quota/drivers/block/nec260.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/block/nec260.c	Thu Oct 20 18:15:34 1994
@@ -0,0 +1,852 @@
+/*
+ * nec260.c - NEC CDR-260 driver
+ *  some pieces derived from mcd.c.
+ *
+ * scott snyder  <snyder@fnald0.fnal.gov>
+ *
+ *   0.1  May 31, 1994  Initial alpha release.
+ *   0.2  Sep  4, 1994  Converted into a loadable kernel module.
+ *                      Made more flexible wrt. hardware configurations.
+ *                      Sense disk changes properly.
+ *   0.3  Sep. 11, 1994 Add LINKED_IN_KERNEL #ifdef's.
+ *
+ *   Copyright (c) 1994  scott snyder <snyder@fnald0.fnal.gov>
+ *
+ *   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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   (for example /usr/src/linux/COPYING); if not, write to the Free
+ *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define REALLY_SLOW_IO
+#include <asm/io.h>
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <linux/hdreg.h>
+#include <linux/signal.h>
+#include <linux/autoconf.h>
+
+#ifdef LINKED_IN_KERNEL
+# define MAYBE_STATIC static
+#else
+# include <linux/module.h>
+# define MAYBE_STATIC
+#endif
+
+#include "version.h"
+
+/***************************************************************************/
+
+/* Configuration options which may be set when loading the driver. */
+
+/* Major device number. */
+MAYBE_STATIC int nec260_major = 26;
+
+/* IRQ level used.  Only matters if nec260_no_hd is turned on. */
+MAYBE_STATIC int nec260_irq = 14;
+
+/* Base I/O address for the IDE adapter registers. */
+MAYBE_STATIC int nec260_base = HD_DATA;
+
+/* Set this to 1 if the device is configured as the master on the bus. */
+MAYBE_STATIC int nec260_master = 0;
+ 
+/* Set this to 1 if the cdrom drive is on the bus by itself. */
+MAYBE_STATIC int nec260_no_hd = 0;
+
+/***************************************************************************/
+
+#define HD_DATA_OFFSET    (HD_DATA   - HD_DATA)
+#define HD_STATUS_OFFSET  (HD_STATUS - HD_DATA)
+#define HD_ERROR_OFFSET   (HD_ERROR  - HD_DATA)
+#define HD_LCYL_OFFSET    (HD_LCYL   - HD_DATA)
+#define HD_HCYL_OFFSET    (HD_HCYL   - HD_DATA)
+#define HD_CURRENT_OFFSET (HD_CURRENT - HD_DATA)
+#define HD_COMMAND_OFFSET (HD_COMMAND - HD_DATA)
+
+#define MAJOR_NR nec260_major
+#define DEVICE_NAME "NEC IDE CD-ROM"
+#define DEVICE_REQUEST do_nec260_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_INTR do_nec260
+
+#include "blk.h"
+
+/* The call interface for binding interrupts changed in 1.1.43 :-( */
+
+#if (KERNEL_MAJOR >= 1) && \
+    (KERNEL_MINOR >= 1) && \
+    (KERNEL_PATCH >= 43)
+# define NO_IRQACTION
+#endif
+
+/***************************************************************************/
+
+/* special command codes for strategy routine. */
+#define CHECK_STATUS 4314
+
+#define CD_BLOCK_SIZE 2048
+
+static void nec260_timeout (unsigned long x);
+static void do_read_callback (void);
+static void do_read_callback_2 (void);
+static int test_read (unsigned int dev, unsigned int block,
+		      unsigned int nsect);
+
+
+/* Values to write to the controller registers to start a transaction. */
+
+static char regimage[7] = {
+  0,                          /* HD_PRECOMP */
+  0,                          /* HD_NSECTOR */
+  0,                          /* HD_SECTOR  */
+  (CD_BLOCK_SIZE & 0xff),     /* HD_LCYL   # of bytes to read  (lo) */
+  (CD_BLOCK_SIZE >> 8),       /* HD_HCYL   # of bytes to read  (hi) */
+  0xf0,                       /* HD_CURRENT   (device select) (slave)     */
+  0xa0                        /* HD_COMMAND                         */
+};
+
+
+/* Image of command to be sent to the device for a read.
+   Portions of this will be overwritten to set the sector number.
+   If i actually had documentation for this device, i could probably
+   give these values some meaningful names... */
+
+static char cmdimage[12] = {0x28, 0, 0, 0, 0x0f, 0x54, 0, 0, 1, 0, 0, 0};
+
+static int nec260_request_in_progress = 0;
+static int nec260_waiting_for_int = 0;
+static int nec260_open_count = 0;
+static int nec260_media_changed = 0;
+static void (**nec260_handler_ptr)(void);
+static int the_hd_major;
+static int retrying = 0;
+
+static struct timer_list nec260_timer = {NULL, NULL, 0, 0, nec260_timeout};
+
+/* The transfer buffer. */
+static char nec260_buf[CD_BLOCK_SIZE];
+static int  nec260_bn = -1;
+
+/* The dummy request which we use to plug up the HD queue. */
+static struct request plug;
+
+/* Symbols from hd.c. */
+extern int the_nec260_major;
+extern void (*do_hd)(void);
+#ifdef CONFIG_BLK_DEV_HD1
+extern void (*do_hd1)(void);
+#endif
+
+
+/***************************************************************************/
+
+/*
+ * Wait until the controller is idle.
+ * Returns the status field; if it has the bit BUSY_STAT set,
+ * we timed out.
+ */
+static int controller_busy (void)
+{
+  int retries = 100000;
+  unsigned char status;
+
+  do 
+    {
+      status = inb_p (nec260_base + HD_STATUS_OFFSET);
+    } while ((status & BUSY_STAT) && --retries);
+#ifdef SHY_DEVICE
+  {
+    static doneonce = 0;
+    if (status == 0xff && !doneonce)
+      status = 0;
+    doneonce = 1;
+  }
+#endif
+  return status;
+}
+
+
+/*
+ * Wait for the controller to be ready to receive a command.
+ * Returns 0 if successful, -1 if we timed out.
+ */
+static inline int wait_DRQ (void)
+{
+  int retries = 100000;
+
+  while (--retries > 0)
+    if (inb_p (nec260_base + HD_STATUS_OFFSET) & DRQ_STAT)
+      return 0;
+  return -1;
+}
+
+
+/*
+ * Stick a dummy request at the head of the HD request queue
+ * to prevent any HD activity while we're using the controller.
+ * The HD queue must be empty.
+ */
+static void plug_hd (void)
+{
+  if (nec260_no_hd)
+    printk ("nec260: spurious call to plug_hd\n");
+  else
+    { 
+      cli ();  /* safety */
+      if (blk_dev[the_hd_major].current_request != NULL)
+	{
+	  printk ("nec260 (plug_hd): hd already active!\n");
+	  return;
+	}
+      blk_dev[the_hd_major].current_request = &plug;
+      plug.dev = -1;
+      plug.next = NULL;
+      
+      /* exits with ints clear */
+    }
+}
+
+
+/*
+ * Remove the dummy request from the start of the HD queue.
+ */
+static void unplug_hd (void)
+{
+  if (nec260_no_hd)
+    printk ("nec260: spurious call to unplug_hd\n");
+  else
+    { 
+      cli ();  /* safety */
+      if (blk_dev[the_hd_major].current_request != &plug) 
+	{
+	  printk ("nec260 (unplug_hd): hd not plugged!\n");
+	  return;
+	}
+      blk_dev[the_hd_major].current_request = plug.next;
+      (blk_dev[the_hd_major].request_fn) ();
+    }
+}
+
+
+/*
+ * Unplug the HD queue and end a idecd request.
+ */
+static void end_read_request (int flag)
+{
+  if (! nec260_no_hd)
+    unplug_hd ();
+  end_request (flag);
+  nec260_request_in_progress = 0;
+}
+
+
+/*
+ * Transfer as much data as we can from NEC260_BUF to the output buffer.
+ */
+static void
+nec260_transfer(void)
+{
+  long offs;
+
+  while (CURRENT -> nr_sectors > 0 && nec260_bn == CURRENT -> sector / 4)
+    {
+      offs = (CURRENT -> sector & 3) * 512;
+      memcpy (CURRENT -> buffer, nec260_buf + offs, 512);
+      CURRENT -> nr_sectors--;
+      CURRENT -> sector++;
+      CURRENT -> buffer += 512;
+    }
+}
+
+
+/*
+ * Invalidate any data saved in our internal buffer
+ */
+static void
+nec260_invalidate_buffers (void)
+{
+  nec260_bn = -1;
+}
+
+
+/*
+ * Complete a read request with status STAT, and call the request routine
+ * to start off the next one.
+ */
+static void complete_read_request (int stat)
+{
+  del_timer (&nec260_timer);	/* Cancel the timeout */
+  end_read_request (stat);
+  cli ();
+  do_nec260_request ();
+}
+
+
+/*
+ * Called when our timer goes off.
+ */
+static void nec260_timeout (unsigned long x)
+{
+  /* Ignore if we're not waiting for an interrupt. */
+  if (! nec260_waiting_for_int) return;
+
+  /* Complete the request with error status. */
+  nec260_waiting_for_int = 0;
+  printk ("nec260: request timed out\n");
+  if (*nec260_handler_ptr == do_read_callback ||
+      *nec260_handler_ptr == do_read_callback_2)
+    *nec260_handler_ptr = NULL;
+  else
+    printk ("nec260: funny value for interrupt handler\n");
+  complete_read_request (0);
+}
+
+/*
+ * Interrupt routine to swallow the extra interrupt from the device.
+ */
+static void do_read_callback_2 (void)
+{
+  int stat;
+
+  *nec260_handler_ptr = NULL;
+
+  if (! nec260_waiting_for_int) 
+    {
+      printk ("nec260 (do_read_callback_2): spurious call?\n");
+      return;
+    }
+
+  nec260_waiting_for_int = 0;
+
+  /* Check error flag and complete the I/O. */
+  stat = inb (nec260_base + HD_ERROR_OFFSET);
+  stat = ((stat & ERR_STAT) == 0);
+  complete_read_request (stat);
+}
+
+
+/*
+ * Interrupt routine.  Called when a read request has completed.
+ */
+static void do_read_callback (void)
+{
+  int stat, len, thislen;
+
+  if (! nec260_waiting_for_int) 
+    {
+      printk ("nec260 (do_read_callback): spurious call?\n");
+      return;
+    }
+
+  nec260_waiting_for_int = 0;
+
+  /* Check for errors. */
+  stat = inb (nec260_base + HD_ERROR_OFFSET);
+
+  if (stat & ERR_STAT)
+    stat = 0;
+  else
+    {
+      if (CURRENT->cmd != CHECK_STATUS)
+	{
+	  /* Error bit not set.
+	     Read the device registers to see how much data is waiting. */
+	  len = inb_p (nec260_base + HD_LCYL_OFFSET) +
+	           256 * inb_p (nec260_base + HD_HCYL_OFFSET);
+
+	  /* Read the data into our buffer. */
+	  thislen = len;
+	  if (thislen > sizeof (nec260_buf)) thislen = sizeof (nec260_buf);
+	  insw (nec260_base + HD_DATA_OFFSET, nec260_buf, thislen/2);
+	  len -= thislen;
+
+	  /* Warn if the size of the data from the device is
+	     larger than the buffer. */
+	  if (len > 0)
+	    {
+	      printk ("nec260: discarding %x bytes\n", len);
+	      while (len > 0) 
+		{
+		  (void) inw_p (nec260_base + HD_DATA_OFFSET);
+		  len -= 2;
+		}
+	    }
+	}
+
+      /* Check for tray open/disk changed.
+	 For some reason, these don't set the `error' bit. */
+      if (stat == 0x24)
+	{
+	  /* Tray open.  The request fails. */
+	  printk ("nec260: tray open\n");
+	  nec260_invalidate_buffers ();
+	  nec260_media_changed = 1;
+	  stat = 0;
+	}
+      else if (stat == 0x64)
+	{
+	  /* Media changed.  Try the operation again. */
+	  printk ("nec260: media change\n");
+	  nec260_invalidate_buffers ();
+	  nec260_media_changed = 1;
+	  if (CURRENT->cmd != CHECK_STATUS && !retrying)
+	    {
+	      del_timer (&nec260_timer);
+	      retrying = 1;
+	      test_read (MINOR (CURRENT->dev),
+			 CURRENT->sector, CURRENT->nr_sectors);
+	      return;
+	    }
+	  stat = 0;
+	}
+      else if (CURRENT->cmd == CHECK_STATUS)
+	stat = 1;
+      else
+	{
+	  /* Copy as much as we can into the output buffer. */
+	  nec260_bn = CURRENT->sector / 4;
+	  nec260_transfer ();
+
+	  stat = 1;
+	}
+    }
+
+  /* If there was an error, complete the request now with an error.
+     But if the read is successful, the device is going to be sending
+     us another interrupt.  It likes long goodbyes, i guess.  Anyway,
+     wait until we see this extra interrupt before ending the request. */
+  if (stat && CURRENT->cmd != CHECK_STATUS)
+    {
+      *nec260_handler_ptr = do_read_callback_2;
+      nec260_waiting_for_int = 1;
+    }
+  else
+    complete_read_request (stat);
+}
+ 
+
+/*
+ * Start a read request from the CD-ROM.
+ * Returns 0 if the request was started successfully,
+ *  -1 if there was an error.
+ * Note: The NSECT arg is presently ignored; we always read exactly
+ *       one block.
+ */
+static int do_read (unsigned int dev, unsigned int block, unsigned int nsect)
+{
+  int i;
+  char *the_regimage;
+  char check_status_regimage[sizeof (regimage)];
+  char *the_cmdimage;
+  char check_status_cmdimage[sizeof (cmdimage)];
+
+  /* Wait for the controller to be idle. */
+  if (controller_busy () & BUSY_STAT) return -1;
+
+  if (CURRENT->cmd == CHECK_STATUS)
+    {
+      the_regimage = check_status_regimage;
+      for (i=0; i<sizeof (check_status_regimage); i++)
+	check_status_regimage[i] = 0;
+      check_status_regimage[HD_CURRENT_OFFSET-1]=regimage[HD_CURRENT_OFFSET-1];
+      check_status_regimage[HD_COMMAND_OFFSET-1]=regimage[HD_COMMAND_OFFSET-1];
+    }
+  else
+    the_regimage = regimage;
+
+  /* Set up the controller registers. */
+  for (i=0; i<sizeof (regimage); i++)
+    outb_p (the_regimage[i], nec260_base + HD_ERROR_OFFSET + i);
+
+  /* Wait for the controller to be ready to receive the comand. */
+  if (controller_busy () & BUSY_STAT) {
+    printk ("nec260: controller busy\n"); return -1;
+  }
+  if (wait_DRQ ()) {
+    printk ("nec260: controller not ready\n"); return -1;
+  }
+  if (controller_busy () & BUSY_STAT) {
+    printk ("nec260: controller busy (2)\n"); return -1;
+  }
+
+  if (CURRENT->cmd == CHECK_STATUS)
+    {
+      the_cmdimage = check_status_cmdimage;
+      for (i=0; i<sizeof (check_status_cmdimage); i++)
+	check_status_cmdimage[i] = 0;
+    }
+  else
+    {
+      /* Write the sector address into the command image. */
+      {
+	union {
+	  struct {unsigned char b0, b1, b2, b3;} b;
+	  struct {unsigned long l0;} l;
+	} conv;
+	conv.l.l0 = CURRENT->sector / 4;
+	cmdimage[2] = conv.b.b3;
+	cmdimage[3] = conv.b.b2;
+	cmdimage[4] = conv.b.b1;
+	cmdimage[5] = conv.b.b0;
+      }
+      the_cmdimage = cmdimage;
+    }
+
+  /* Send the command to the device. */
+  outsw (nec260_base + HD_DATA_OFFSET, the_cmdimage, sizeof (cmdimage)/2);
+
+  /* Set up our interrupt handler and return. */
+  *nec260_handler_ptr = do_read_callback;
+  nec260_waiting_for_int = 1;
+
+  /* Set up a timeout. */
+  nec260_timer.expires = 500;
+  add_timer (&nec260_timer);
+
+  return 0;
+}
+
+
+/*
+ * Start a read request.
+ * If there is an error starting it, terminate the current request
+ * immediately with an error.
+ */
+static int test_read (unsigned int dev, unsigned int block,
+		      unsigned int nsect)
+{
+  int stat;
+  stat = do_read (dev, block, nsect);
+
+  if (stat)
+    end_read_request (0);
+  return 1;
+}
+
+
+/*
+ * I/O request routine called from kernel.
+ */
+static void do_nec260_request (void)
+{
+  unsigned int block,dev;
+  unsigned int nsect;
+
+  /* Don't do anything if we're waiting for a request to complete.
+     (Can this actually happen?) */
+  if (nec260_request_in_progress) return;
+
+ repeat:
+  cli (); /* safety */
+  nec260_request_in_progress = 0;
+
+  /* Return if our queue is plugged. */
+  if (!(CURRENT) || CURRENT->dev < 0) return;
+
+  /* Get the next request on the queue. */
+  INIT_REQUEST;
+  dev = MINOR (CURRENT->dev);
+  block = CURRENT->sector;
+  nsect = CURRENT->nr_sectors;
+
+  /* Return if there wasn't one. */
+  if (CURRENT == NULL || CURRENT -> sector == -1)
+    return;
+
+  /* We can only read. */
+  if (CURRENT -> cmd != READ && CURRENT->cmd != CHECK_STATUS)
+    {
+      printk ("nec260: bad cmd %d\n", CURRENT -> cmd);
+      end_request (0);
+      goto repeat;
+    }
+
+  if (CURRENT->cmd != CHECK_STATUS)
+    {
+      /* Try to satisfy the request from the buffer. */
+      nec260_transfer ();
+
+      /* If we got the entire request, we're done. */
+      if (CURRENT->nr_sectors == 0) 
+	{
+	  end_request (1);
+	  goto repeat;
+	}
+    }
+
+  /* If the HD is currently active,  return - we must wait for it to finish.
+     If it is idle (no requests in the queue), plug up the queue with a dummy
+     request until we're done using the controller. */
+  if (! nec260_no_hd)
+    {
+      if (blk_dev[the_hd_major].current_request) return;
+      plug_hd ();
+    }
+  nec260_request_in_progress = 1;
+
+  retrying = 0;
+  if (!test_read (dev, block, nsect))
+    goto repeat;
+
+  sti ();
+  return;
+}
+
+
+/*
+ * Open the device.
+ */
+
+static int
+nec260_open (struct inode *ip, struct file *fp)
+{
+  /* should check that h/w is available... */
+
+  /* no write access */
+  if (fp->f_mode & 2) return -EROFS;
+
+  if (!nec260_open_count)
+    {
+      nec260_invalidate_buffers ();
+
+      /* should check that there's a disk in the drive? */
+    }
+
+  /* Opened ok.  Count this access and return success. */
+  ++nec260_open_count;
+#ifndef LINKED_IN_KERNEL
+  MOD_INC_USE_COUNT;
+#endif
+  return 0;
+}
+
+
+/*
+ * Close down the device.  Invalidate all cached blocks.
+ */
+
+static void
+nec260_release (struct inode *inode, struct file *file)
+{
+  --nec260_open_count;
+  if (nec260_open_count < 0)
+    {
+      printk ("nec260: inconsistent open count %d\n", nec260_open_count);
+      nec260_open_count = 0;
+    }
+
+  if (nec260_open_count == 0)
+    {
+      nec260_invalidate_buffers ();
+      sync_dev (inode->i_rdev);
+      invalidate_buffers (inode->i_rdev);
+    }
+#ifndef LINKED_IN_KERNEL
+  MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static
+void nec260_do_check_status (dev_t full_dev)
+{
+  struct request check_status_req;
+  struct semaphore sem = MUTEX_LOCKED;
+
+  check_status_req.dev = full_dev;
+  check_status_req.cmd = CHECK_STATUS;
+  check_status_req.errors = 0;
+  check_status_req.sector = 0;
+  check_status_req.nr_sectors = 0;
+  check_status_req.current_nr_sectors = 0;
+  check_status_req.buffer = NULL;
+  check_status_req.sem = &sem;
+  check_status_req.bh = NULL;
+  check_status_req.bhtail = NULL;
+  check_status_req.next = NULL;
+
+  cli ();
+  if (CURRENT == NULL)
+    {
+      CURRENT = &check_status_req;
+      do_nec260_request ();
+    }
+  else
+    {
+      check_status_req.next = CURRENT;
+      CURRENT->next = &check_status_req;
+    }
+  sti ();
+
+  down (&sem);
+}
+
+
+static
+int nec260_disk_change (dev_t full_dev)
+{
+  int retval, target;
+
+  target = MINOR (full_dev);
+
+  if (target > 0)
+    {
+      printk ("nec260 (nec260_disk_change): invalid device %d\n", target);
+      return 0;
+    }
+
+  if (nec260_media_changed == 0)
+    nec260_do_check_status (full_dev);
+
+  retval = nec260_media_changed;
+  nec260_media_changed = 0;
+
+  printk ("nec260: disk change %d\n", retval);
+
+  return retval;
+}
+
+
+static void nec260_interrupt (int unused)
+{
+  void (*handler)(void) = DEVICE_INTR;
+	
+  DEVICE_INTR = NULL;
+  if (!handler)
+    printk ("nec260: unexpected interrupt\n");
+  else
+    handler();
+  sti();
+}
+
+
+#ifndef NO_IRQACTION
+/*
+ * This is the cdrom IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled.
+ */
+static struct sigaction nec260_sigaction = {
+	nec260_interrupt,
+	0,
+	SA_INTERRUPT,
+	NULL
+};
+#endif /* ! NO_IRQACTION */
+
+
+
+static struct file_operations nec260_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	NULL, 			/* ioctl */
+	NULL,			/* mmap */
+	nec260_open,		/* open */
+        nec260_release,		/* release */
+        NULL,	                /* fsync */  
+        NULL,	                /* fasync */  
+        nec260_disk_change,	/* check_media_change */  
+        NULL,	                /* revalidate */  
+};
+
+
+MAYBE_STATIC int init_module (void)
+{
+  /* Find the major numbe of the HD on our IDE bus. */
+  the_hd_major = HD_MAJOR;
+#ifdef CONFIG_BLK_DEV_HD1
+  if (nec260_base == HD1_DATA)
+    the_hd_major = HD1_MAJOR;
+#endif
+
+  /* Register ourselves with the kernel. */
+  if (register_blkdev (nec260_major, "nec260", &nec260_fops) != 0) 
+    {
+      printk("nec260: Unable to get major %d for NEC CDR-260\n", nec260_major);
+      return -EIO;
+    }
+
+  printk ("nec260: registered with major #%d\n", nec260_major);
+
+  /* Install our device request routine. */
+  blk_dev[nec260_major].request_fn = DEVICE_REQUEST;
+  read_ahead[nec260_major] = 4;
+
+  /* Set the proper device select code. */
+  if (nec260_master)
+    regimage[5] = 0xe0;
+  else
+    regimage[5] = 0xf0;
+
+  if (nec260_no_hd)
+    {
+      /* Set up the interrupt. */
+      do_nec260 = NULL;
+      nec260_handler_ptr = &do_nec260;
+#ifdef NO_IRQACTION
+      if (request_irq (nec260_irq, nec260_interrupt, SA_INTERRUPT, "nec260"))
+#else
+      if (irqaction (nec260_irq, &nec260_sigaction))
+#endif
+	{
+	  printk ("Unable to get IRQ%d for NEC CDR-260 CD-ROM\n", nec260_irq);
+	  unregister_blkdev (nec260_major, "nec260");
+	  return -EIO;
+	}
+    }
+  else
+    {
+      /* tell hd.c our major number */
+      the_nec260_major = nec260_major;
+
+      /* Set up nec260_handler_ptr to point to the HD interrupt vector. */
+      nec260_handler_ptr = &do_hd;
+#ifdef CONFIG_BLK_DEV_HD1
+      if (nec260_base == HD1_DATA)
+	the_hd_major = &do_hd1;
+#endif
+    }
+
+  return 0;
+}
+
+#ifdef LINKED_IN_KERNEL
+
+unsigned long nec260_init (unsigned long mem_start, unsigned long mem_end)
+{
+  init_module ();
+  return mem_start;
+}
+
+#else
+
+void cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk("nec260: device busy, remove delayed\n");
+
+  if (unregister_blkdev (nec260_major, "nec260") != 0)
+    printk ("nec260: cleanup_module failed\n");
+  else 
+    {
+      printk ("nec260: cleanup_module succeeded\n");
+      if (nec260_no_hd)
+	free_irq (nec260_irq);
+      else
+	the_nec260_major = 0;
+    }
+}
+
+#endif
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c
--- linux-1.1.55+new_quota/drivers/block/sonycd535.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/block/sonycd535.c	Thu Oct 20 18:15:34 1994
@@ -0,0 +1,1537 @@
+/*
+ * Sony CDU-535 interface device driver
+ *
+ * This is a modified version of the CDU-31A device driver (see below).
+ * Changes were made using documentation for the CDU-531 (which Sony
+ * assures me is very similar to the 535) and partial disassembly of the
+ * DOS driver.  I used Minyard's driver and replaced the the CDU-31A 
+ * commands with the CDU-531 commands.  This was complicated by a different
+ * interface protocol with the drive.  The driver is still polled.
+ *
+ * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
+ * I tried polling without the sony_sleep during the data transfers but
+ * it did not speed things up any.
+ *
+ *  5/23/93 (rgj) changed the major number to 21 to get rid of conflict
+ * with CDU-31A driver.  This is the also the number from the Linux
+ * Device Driver Registry for the Sony Drive.  Hope nobody else is using it.
+ *
+ *  8/29/93 (rgj) remove the configuring of the interface board address
+ * from the top level configuration, you have to modify it in this file.
+ *
+ * Things to do:
+ *	- handle errors and status better, put everything into a single word
+ *	- use interrupts, DMA
+ *
+ *  Known Bugs:
+ *	-
+ *
+ *   Ron Jeppesen (ronj.an@site007.saic.com)
+ *
+ *
+ *------------------------------------------------------------------------
+ * Sony CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to ronj above)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunatly) a polled interface.  This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables.  Some (like mine) do not even have the capability to
+ * handle interrupts or DMA.  For this reason you will see a lot of
+ * the following:
+ *
+ *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ *   while ((retry_count > jiffies) && (! <some condition to wait for))
+ *   {
+ *      while (handle_sony_cd_attention())
+ *         ;
+ *
+ *      sony_sleep();
+ *   }
+ *   if (the condition not met)
+ *   {
+ *      return an error;
+ *   }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try.  it also handles attentions, which are
+ * asyncronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk.  The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal.  A lot of conversion goes on.
+ *
+ *  Copyright (C) 1993  Corey Minyard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#ifdef CONFIG_CDU535
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+#include <linux/sonycd535.h>
+
+#define MAJOR_NR CDU535_CDROM_MAJOR
+#include "blk.h"
+
+/*
+ * this is the base address of the interface card for the Sony CDU535
+ * CDROM drive.  If your jumpers are set for an address other than
+ * this one (the default), change the following line to the
+ * proper address.
+ */
+#define CDU535_ADDRESS	(0x340)
+
+#define DEBUG
+
+/*
+ *  SONY535_BUFFER_SIZE determines the size of internal buffer used
+ *  by the drive.  It must be at least 2K and the larger the buffer
+ *  the better the transfer rate.  It does however take system memory.
+ *   On my system I get the following transfer rates using dd to read
+ *  10 Mb off /dev/cdrom.
+ *
+ *      8K buffer		 43 Kb/sec
+ *	16K buffer		 66 Kb/sec
+ *	32K buffer		 91 Kb/sec
+ *	64K buffer		111 Kb/sec
+ *	128K buffer		123 Kb/sec
+ *	512K buffer		123 Kb/sec
+ */
+#define SONY535_BUFFER_SIZE	(64*1024)
+
+/*
+ *  if LOCK_DOORS is defined then the eject button is disabled while
+ * the device is open.
+ */
+#define LOCK_DOORS
+
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int cdu_open( struct inode *inode, struct file *filp );
+static inline unsigned int int_to_bcd(unsigned int val);
+static unsigned int bcd_to_int(unsigned int bcd);
+static int do_sony_cmd( Byte *cmd, int nCmd, Byte status[2], 
+                 Byte *response, int nResponse, int ignoreStatusBit7 );
+
+/* The base I/O address of the Sony Interface.  This is a variable (not a
+   #define) so it can be easily changed via some future ioctl() */
+static unsigned short sony_cd_base_io = CDU535_ADDRESS;
+
+/*
+ * The following are I/O addresses of the various registers for the drive.  The
+ * comment for the base address also applies here.
+ */
+static unsigned short select_unit_reg;
+static unsigned short result_reg;
+static unsigned short command_reg;
+static unsigned short read_status_reg;
+static unsigned short data_reg;
+
+static int initialized = 0;                /* Has the drive been initialized? */
+static int sony_disc_changed = 1;          /* Has the disk been changed
+                                              since the last check? */
+static int sony_toc_read = 0;              /* Has the table of contents been
+                                              read? */
+static unsigned int sony_buffer_size;      /* Size in bytes of the read-ahead
+                                              buffer. */
+static unsigned int sony_buffer_sectors;   /* Size (in 2048 byte records) of
+                                              the read-ahead buffer. */
+static unsigned int sony_usage = 0;        /* How many processes have the
+                                              drive open. */
+
+static int sony_first_block = -1;          /* First OS block (512 byte) in
+                                              the read-ahead buffer */
+static int sony_last_block = -1;           /* Last OS block (512 byte) in
+                                              the read-ahead buffer */
+
+static struct s535_sony_toc *sony_toc;           /* Points to the table of
+                                                    contents. */
+static struct s535_sony_subcode *last_sony_subcode; /* Points to the last
+                                                    subcode address read */
+static unsigned char *sony_buffer;               /* Points to the read-ahead
+                                                    buffer */
+static int sony_inuse = 0;		/* is the drive in use? Only one
+					   open at a time allowed */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play.  The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over.  This holds the
+ * position during a pause so a resume can restart it.  It uses the
+ * audio status variable above to tell if it is paused.
+ *   I just kept the CDU-31A driver behavior rather than using the PAUSE
+ * command on the CDU-535.
+ */
+static unsigned char cur_pos_msf[3] = { 0, 0, 0 };
+static unsigned char final_pos_msf[3] = { 0, 0, 0 };
+
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.
+ */
+static int
+cdu535_check_media_change(dev_t full_dev)
+{
+   int retval, target;
+
+   target = MINOR(full_dev);
+
+   if (target > 0) {
+      printk("Sony CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   /* if driver is not initialized, always return 0 */
+   retval = ( initialized ? sony_disc_changed : 0 );
+   sony_disc_changed = 0;
+   return retval;
+}
+
+
+/*
+ * Wait a little while (used for polling the drive).  If in initialization,
+ * setting a timeout doesn't work, so just loop for a while  (we trust
+ * that the sony_sleep() call is protected by a test for proper jiffies count).
+ */
+static inline void
+sony_sleep(void)
+{
+   current->state = TASK_INTERRUPTIBLE;
+   current->timeout = jiffies;
+   schedule();
+}
+
+/*------------------start of SONY CDU535 very specific ---------------------*/
+
+/****************************************************************************
+ * void select_unit( int unit_no )
+ *
+ *  Select the specified unit (0-3) so that subsequent commands reference it
+ ****************************************************************************/
+static void 
+select_unit( int unit_no )
+{
+   unsigned int select_mask = ~(1 << unit_no);
+   outb( select_mask, select_unit_reg );
+}  /* select_unit() */
+
+/***************************************************************************
+ * int read_result_reg( unsigned char *data_ptr )
+ *
+ *  Read a result byte from the Sony CDU controller, store in location pointed
+ * to by data_ptr.  Return zero on success, TIME_OUT if we did not receive
+ * data.
+ ***************************************************************************/
+static int 
+read_result_reg( unsigned char *data_ptr )
+{
+   int retry_count;
+   int read_status;
+  
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ( retry_count > jiffies ) 
+   {
+      if ( ((read_status=inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0 ) 
+      {
+#if 0
+         printk("read_result_reg(): readStatReg = 0x%x\n", read_status );
+#endif
+         *data_ptr = inb(result_reg);
+         return( 0 );
+      }
+      else 
+      {
+         sony_sleep();
+      }
+   }
+   printk(" Sony CDROM read_result_reg: TIME OUT!\n");
+   return( TIME_OUT );
+}  /* read_result_reg() */
+
+/****************************************************************************
+ * int read_exec_status( Byte status[2] )
+ *
+ *  Read the execution status of the last command and put into status. 
+ * Handles reading second status word if available.  Returns 0 on success,
+ * TIME_OUT on failure.
+ ****************************************************************************/
+static int 
+read_exec_status( Byte status[2] )
+{
+   status[1] = 0;
+   if ( read_result_reg( &(status[0]) ) != 0 ) return( TIME_OUT );
+   if ( (status[0] & 0x80) != 0 )  /* byte two follows */
+   {
+      if ( read_result_reg( &(status[1]) ) != 0 ) return( TIME_OUT );
+   }
+#if 0
+   printk("read_exec_status: read 0x%x\n", status[0] );
+   if (status[0] & 0x80) printk(" and 0x%x\n", status[1] );
+   printk("\n");
+#endif
+   return( 0 );
+}  /* read_exec_status() */
+
+/****************************************************************************
+ * int check_drive_status( void )
+ *
+ *  Check the current drive status.  Using this before executing a command
+ * takes care of the problem of unsolicited drive status-2 messages.
+ * Add a check of the audio status if we think the disk is playing.
+ ****************************************************************************/
+static int 
+check_drive_status( void )
+{
+   Byte status, e_status[2];
+   int CDD, ATN;
+   unsigned char cmd;
+  
+   select_unit(0);
+   if ( sony_audio_status == CDROM_AUDIO_PLAY )  /* check status */
+   {
+      outb( SONY535_REQUEST_AUDIO_STATUS, command_reg );
+      if ( read_result_reg( &status ) == 0 ) 
+      {
+         switch( status ) 
+         {
+            case 0x0:  break;			/* play in progress */
+            case 0x1:  break;			/* paused */
+            case 0x3:				/* audio play completed */
+            case 0x5:				/* play not requested */
+                       sony_audio_status = CDROM_AUDIO_COMPLETED;
+                       read_subcode();
+                       break;
+            case 0x4:				/* error during play */
+                       sony_audio_status = CDROM_AUDIO_ERROR;
+                       break;
+         }
+      }
+   }
+    /* now check drive status */
+   outb( SONY535_REQUEST_DRIVE_STATUS_2, command_reg );
+   if ( read_result_reg( &status ) != 0 ) 
+      return( TIME_OUT );
+
+#if 0
+   printk("--check_drive_status() got 0x%x\n", status );
+#endif
+  
+   if ( status == 0 ) return( 0 );
+  
+   ATN = status & 0xf;
+   CDD = (status >> 4) & 0xf;
+  
+   switch ( ATN ) 
+   {
+      case 0x0:
+                 break;  /* go on to CDD stuff */
+      case SONY535_ATN_BUSY:
+                 if ( initialized )
+                    printk("Sony CDROM error, drive busy\n");
+                 return( CD_BUSY);
+      case SONY535_ATN_EJECT_IN_PROGRESS:
+                 printk("Sony CDROM error, eject in progress\n");
+                 sony_audio_status = CDROM_AUDIO_INVALID;
+                 return( CD_BUSY);
+      case SONY535_ATN_RESET_OCCURRED:
+      case SONY535_ATN_DISC_CHANGED:
+      case SONY535_ATN_RESET_AND_DISC_CHANGED:
+#ifdef DEBUG
+                 printk("Sony CDROM, reset occurred or disc changed\n");
+#endif
+                 sony_disc_changed = 1;
+                 sony_toc_read = 0;
+                 sony_audio_status = CDROM_AUDIO_NO_STATUS;
+                 sony_first_block = -1;
+                 sony_last_block = -1;
+                 if (initialized)
+                 {
+                    cmd = SONY535_SPIN_UP;
+                    do_sony_cmd( &cmd,1, e_status, NULL,0, 0 );
+                    sony_get_toc();
+                 }
+                 return( 0 );
+      default:
+                 printk("Sony CDROM error, drive busy (ATN=0x%x)\n", ATN );
+                 return( CD_BUSY );
+   }
+   switch ( CDD ) 
+   {  /* the 531 docs are not helpful in decoding this */
+      case 0x0:       /* just use the values from the DOS driver */
+      case 0x2:
+      case 0xa:
+                 break;  /* no error */
+      case 0xc:
+                 printk("check_drive_status(): CDD = 0xc! Not properly handled!\n");
+                 return( CD_BUSY );  /* ? */
+      default:
+                 return( CD_BUSY );
+   }
+   return( 0 );
+}  /* check_drive_status() */
+
+/*****************************************************************************
+ * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], 
+ *                Byte *response, int n_response, int ignore_status_bit7 )
+ *
+ *  Generic routine for executing commands.  The command and its parameters
+ *  should be placed in the cmd[] array, number of bytes in the command is
+ *  stored in nCmd.  The response from the command will be stored in the
+ *  response array.  The number of bytes you expect back (excluding status)
+ *  should be passed in nReponse.  Finally, some
+ *  commands set bit 7 of the return status even when there is no second
+ *  status byte, on these commands set ignoreStatusBit7 TRUE.
+ *    If the command was sent and data recieved back, then we return 0,
+ *  else we return TIME_OUT.  You still have to check the status yourself.
+ *    You should call check_drive_status() before calling this routine
+ *  so that you do not lose notifications of disk changes, etc.
+ ****************************************************************************/
+static int 
+do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], 
+             Byte *response, int n_response, int ignore_status_bit7 )
+{
+   int i;
+  
+    /* write out the command */
+   for ( i=0; i < n_cmd; i++ )
+      outb( cmd[i], command_reg );
+  
+    /* read back the status */
+   if ( read_result_reg( status ) != 0 )
+      return( TIME_OUT );
+   if ( !ignore_status_bit7 && ((status[0] & 0x80) != 0) ) /* get second status byte */
+   {
+      if ( read_result_reg( status+1 ) != 0 )
+         return( TIME_OUT );
+   }
+   else
+   {
+      status[1] = 0;
+   }
+    
+   /* do not know about when I should read set of data and when not to */
+   if ( (status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0 )
+      return( 0 );
+  
+    /* else, read in rest of data */
+   for ( i=0; n_response > 0; n_response--, i++ )
+      if ( read_result_reg( response+i ) != 0 ) return(TIME_OUT);
+   return( 0 );
+}  /* do_sony_cmd() */
+
+/**************************************************************************
+ * int set_drive_mode( int mode, Byte status[2] )
+ *
+ *  Set the drive mode to the specified value (mode=0 is audio, mode=e0
+ * is mode-1 CDROM
+ **************************************************************************/
+static int 
+set_drive_mode( int mode, Byte status[2] )
+{
+   Byte cmd_buff[2], ret_buff[1];
+  
+   cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+   cmd_buff[1] = mode;
+   return( do_sony_cmd( cmd_buff,2, status, ret_buff,1, 1 ) );
+}  /* set_drive_mode() */
+
+/***************************************************************************
+ * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], 
+ *                             Byte *data_buff, int buff_size )
+ *
+ *  Read n_blocks of data from the CDROM starting at position params[0:2],
+ *  number of blocks in stored in params[3:5] -- both these are already
+ *  int bcd format.
+ *  Transfer the data into the buffer pointed at by data_buff.  buff_size
+ *  gives the number of bytes available in the buffer.
+ *    The routine returns number of bytes read in if successful, otherwise
+ *  it returns one of the standard error returns.
+ ***************************************************************************/
+static int 
+seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
+                        Byte *data_buff, int buf_size )
+{
+   int i;
+   const int block_size = 2048;
+   Byte cmd_buff[7];
+   int read_status;
+   Byte *start_pos = data_buff;
+   int retry_count;
+  
+   if ( ((long)block_size)*n_blocks > buf_size )
+      return( NO_ROOM );
+  
+   set_drive_mode( SONY535_CDROM_DRIVE_MODE, status );
+  
+    /* send command to read the data */
+   cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
+   for ( i=0; i < 6; i++ )
+      cmd_buff[i+1] = params[i];
+   for ( i=0; i < 7; i++ )
+      outb( cmd_buff[i], command_reg );
+  
+    /* read back the data one block at a time */
+   while ( n_blocks-- > 0 ) 
+   {
+       /* wait for data to be ready */
+      retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+      while ( retry_count > jiffies ) 
+      {
+         read_status = inb(read_status_reg);
+         if ( (read_status & SONY535_RESULT_NOT_READY_BIT) == 0 ) 
+         {
+            read_exec_status( status );
+            return( BAD_STATUS );
+         }
+         if ( (read_status & SONY535_DATA_NOT_READY_BIT) == 0 ) 
+         {
+             /* data is ready, read it */
+            for ( i=0; i < block_size; i++ )
+               *data_buff++ = inb( data_reg );  /* unrolling this loop does not seem to help */
+            break;  /* exit the timeout loop */
+         }
+         sony_sleep();  /* data not ready, sleep a while */
+      }
+      if ( retry_count <= jiffies )
+      return( TIME_OUT );  /* if we reach this stage */
+   }
+  
+    /* read all the data, now read the status */
+   if ( (i=read_exec_status( status )) != 0 )
+      return( i );
+   return( data_buff - start_pos );
+}  /* seek_and_read_N_blocks() */
+
+/****************************************************************************
+ * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
+ *
+ *  Read in the table of contents data.  Converts all the bcd data
+ * into integers in the toc structure.
+ ****************************************************************************/
+static int 
+request_toc_data( Byte status[2], struct s535_sony_toc *toc )
+{
+   int to_status;
+   int i, j, n_tracks, track_no;
+   Byte cmd_no = 0xb2;
+   Byte track_address_buffer[5];
+   int first_track_num, last_track_num;
+
+    /* read the fixed portion of the table of contents */
+   if ((to_status=do_sony_cmd( &cmd_no,1, status, (Byte *)toc,15, 1 )) != 0 )
+      return( to_status );
+  
+    /* convert the data into integers so we can use them */
+   first_track_num = bcd_to_int(toc->first_track_num);
+   last_track_num = bcd_to_int(toc->last_track_num);
+   n_tracks = last_track_num - first_track_num + 1;
+  
+    /* read each of the track address descriptors */
+   for ( i=0; i < n_tracks; i++ ) 
+   {
+       /* read the descriptor into a temporary buffer */
+      for ( j=0; j < 5; j++ ) 
+      {
+         if ( read_result_reg( track_address_buffer+j ) != 0 )
+            return( TIME_OUT );
+         if ( j == 1 )  /* need to convert from bcd */
+            track_no = bcd_to_int(track_address_buffer[j]);
+      }
+       /* copy the descriptor to proper location - sonycd.c just fills */
+      memcpy( toc->tracks+i, track_address_buffer, 5 );
+   }
+   return( 0 );
+}  /* request_toc_data() */
+
+/***************************************************************************
+ * int spin_up_drive( Byte status[2] )
+ *
+ *  Spin up the drive (unless it is already spinning).
+ ***************************************************************************/
+static int 
+spin_up_drive( Byte status[2] )
+{
+   Byte cmd_buff[1];
+  
+    /* first see if the drive is already spinning */
+   cmd_buff[0] = SONY535_REQUEST_DRIVE_STATUS_1;
+   if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 )
+      return(TIME_OUT );
+   if ( (status[0] & SONY535_STATUS1_NOT_SPINNING) == 0 )
+      return( 0 );  /* its already spinning */
+  
+    /* else, give the spin-up command */
+   cmd_buff[0] = SONY535_SPIN_UP;
+   return( do_sony_cmd( cmd_buff, 1, status, NULL,0, 0 )  );
+}  /* spin_up_drive() */
+
+/*--------------------end of SONY CDU535 very specific ---------------------*/
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+   int retval;
+
+
+   retval = (val / 10) << 4;
+   retval = retval | val % 10;
+   return(retval);
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+   return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, unsigned char *msf)
+{
+   log = log + LOG_START_OFFSET;
+   msf[0] = int_to_bcd(log / 4500);
+   log = log % 4500;
+   msf[1] = int_to_bcd(log / 75);
+   msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(unsigned char *msf)
+{
+   unsigned int log;
+
+
+   log = bcd_to_int(msf[2]);
+   log += bcd_to_int(msf[1]) * 75;
+   log += bcd_to_int(msf[0]) * 4500;
+   log = log - LOG_START_OFFSET;
+
+   return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size,
+            unsigned char *buf)
+{
+   buf[0] = size / 65536;
+   size = size % 65536;
+   buf[1] = size / 256;
+   buf[2] = size % 256;
+}
+
+
+/*
+ * The OS calls this to perform a read or write operation to the drive.
+ * Write obviously fail.  Reads to a read ahead of sony_buffer_size
+ * bytes to help speed operations.  This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu535_request(void)
+{
+   int block;
+   unsigned int dev;
+   int nsect;
+   unsigned char params[10];
+   int copyoff;
+   int spin_up_retry;
+   unsigned int read_size;
+   unsigned char status[2], cmd[2];
+
+
+   if ( !sony_inuse )
+   {
+      cdu_open( NULL, NULL );
+   }
+   
+   while (1)
+   {
+      /*
+       * The beginning here is stolen from the hard disk driver.  I hope
+       * its right.
+       */
+      if (!(CURRENT) || CURRENT->dev < 0)
+      {
+         return;
+      }
+
+      INIT_REQUEST;
+      dev = MINOR(CURRENT->dev);
+      block = CURRENT->sector;
+      nsect = CURRENT->nr_sectors;
+      if (dev != 0)
+      {
+         end_request(0);
+         continue;
+      }
+
+      switch(CURRENT->cmd)
+      {
+      case READ:
+         /*
+          * If the block address is invalid or the request goes beyond the end of
+          * the media, return an error.
+          */
+         if ((block / 4) >= sony_toc->lead_out_start_lba)
+         {
+            end_request(0);
+            return;
+         }
+         if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
+         {
+            end_request(0);
+            return;
+         }
+
+         while (nsect > 0)
+         {
+            /*
+             * If the requested sector is not currently in the read-ahead buffer,
+             * it must be read in.
+             */
+            if ((block < sony_first_block) || (block > sony_last_block))
+            {
+               sony_first_block = (block / 4) * 4;
+               log_to_msf(block/4, params);
+
+               /*
+                * If the full read-ahead would go beyond the end of the media, trim
+                * it back to read just till the end of the media.
+                */
+               if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
+               {
+                  sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+                  read_size = sony_toc->lead_out_start_lba - (block / 4);
+               }
+               else
+               {
+                  sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+                  read_size = sony_buffer_sectors;
+               }
+               size_to_buf(read_size, &params[3]);
+
+               /*
+                * Read the data.  If the drive was not spinning, spin it up and try
+                * once more.  I know, the goto is ugly, but I am too lazy to fix it.
+                */
+               spin_up_retry = 0;
+try_read_again:
+#if 0
+               if ( check_drive_status() != 0 ) {  /* drive not ready */
+                  sony_first_block = -1;
+                  sony_last_block = -1;
+                  end_request(0);
+                  return;
+                  }
+#endif
+               if ( seek_and_read_N_blocks( params, read_size, status, sony_buffer,
+                         (read_size * 2048) ) < 0 ) {
+                  if ((status[0] & SONY535_STATUS1_NOT_SPINNING) && (!spin_up_retry))
+                  {
+                     printk(" Sony535 Debug -- calling spin up when reading data!\n");
+                     cmd[0] = SONY535_SPIN_UP;
+                     do_sony_cmd( cmd,1, status, NULL,0, 0);
+                     spin_up_retry = 1;
+                     goto try_read_again;
+                  }
+
+                  printk("Sony CDROM Read error: 0x%.2x\n", status[0]);
+                  sony_first_block = -1;
+                  sony_last_block = -1;
+                  end_request(0);
+                  return;
+               }
+            }
+   
+            /*
+             * The data is in memory now, copy it to the buffer and advance to the
+             * next block to read.
+             */
+            copyoff = (block - sony_first_block) * 512;
+            memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
+               
+            block += 1;
+            nsect -= 1;
+            CURRENT->buffer += 512;
+         }
+               
+         end_request(1);
+         break;
+            
+      case WRITE:
+         end_request(0);
+         break;
+            
+      default:
+         panic("Unkown SONY CD cmd");
+      }
+   }
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+   unsigned char status[2];
+  
+   if ( !sony_toc_read ) 
+   {
+       /* do not call check_drive_status() from here since it can call this routine */
+      if ( request_toc_data( status, sony_toc ) < 0 )
+         return;
+      sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+      sony_toc_read = 1;
+   }
+}
+
+
+/*
+ * Search for a specific track in the table of contents.  track is
+ * passed in bcd format
+ */
+static int
+find_track(int track)
+{
+   int i;
+   int num_tracks;
+
+
+   num_tracks = bcd_to_int(sony_toc->last_track_num) - 
+                   bcd_to_int(sony_toc->first_track_num) + 1;
+   for (i = 0; i < num_tracks; i++)
+   {
+      if (sony_toc->tracks[i].track == track)
+      {
+         return i;
+      }
+   }
+
+   return -1;
+}
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+   Byte cmd = SONY535_REQUEST_SUB_Q_DATA, status[2];
+   int dsc_status;
+   
+   if ( check_drive_status() != 0 )
+      return( -EIO );
+     
+   if ( (dsc_status=do_sony_cmd( &cmd,1, status, (Byte *)last_sony_subcode, 
+                      sizeof(struct s535_sony_subcode), 1 )) != 0 ) 
+   {
+      printk("Sony CDROM error 0x%.2x, %d (read_subcode)\n", status[0], 
+              dsc_status );
+      return( -EIO );
+   }
+   return(0);
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing).  If the drive is paused or completed, the subcode information has
+ * already been stored, just use that.  The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+   struct cdrom_subchnl schi;
+
+
+   /* Get attention stuff */
+   if ( check_drive_status() != 0 )
+      return( -EIO );
+
+   sony_get_toc();
+   if (!sony_toc_read)
+   {
+      return -EIO;
+   }
+
+   verify_area(VERIFY_WRITE /* and read */, (char *) arg, sizeof(schi));
+
+   memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
+   
+   switch (sony_audio_status)
+   {
+   case CDROM_AUDIO_PLAY:
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      break;
+
+   case CDROM_AUDIO_PAUSED:
+   case CDROM_AUDIO_COMPLETED:
+      break;
+
+   case CDROM_AUDIO_NO_STATUS:
+      schi.cdsc_audiostatus = sony_audio_status;
+      memcpy_tofs((char *) arg, &schi, sizeof(schi));
+      return 0;
+      break;
+
+   case CDROM_AUDIO_INVALID:
+   case CDROM_AUDIO_ERROR:
+   default:
+      return -EIO;
+   }
+
+   schi.cdsc_audiostatus = sony_audio_status;
+   schi.cdsc_adr = last_sony_subcode->address;
+   schi.cdsc_ctrl = last_sony_subcode->control;
+   schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
+   schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
+   if (schi.cdsc_format == CDROM_MSF)
+   {
+      schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
+      schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
+      schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
+
+      schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
+      schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
+      schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
+   }
+   else if (schi.cdsc_format == CDROM_LBA)
+   {
+      schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
+      schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
+   }
+   
+   memcpy_tofs((char *) arg, &schi, sizeof(schi));
+   return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+cdu_ioctl(struct inode *inode,
+          struct file  *file,
+          unsigned int cmd,
+          unsigned long arg)
+{
+   unsigned int dev;
+   unsigned char status[2];
+   unsigned char cmd_buff[10], params[10];
+   int i, dsc_status;
+
+
+   if (!inode)
+   {
+      return -EINVAL;
+   }
+   dev = MINOR(inode->i_rdev) >> 6;
+   if (dev != 0)
+   {
+      return -EINVAL;
+   }
+   
+   if ( check_drive_status() != 0 )
+      return( -EIO );
+
+   switch (cmd)
+   {
+   case CDROMSTART:     /* Spin up the drive */
+      if ( spin_up_drive( status ) < 0 )
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMSTART)\n", status[0]);
+         return -EIO;
+      }
+      return 0;
+      break;
+      
+   case CDROMSTOP:      /* Spin down the drive */
+      cmd_buff[0] = SONY535_HOLD;
+      do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+
+      /*
+       * Spin the drive down, ignoring the error if the disk was
+       * already not spinning.
+       */
+      sony_audio_status = CDROM_AUDIO_NO_STATUS;
+      cmd_buff[0] = SONY535_SPIN_DOWN;
+      dsc_status = do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+      if ( (( dsc_status < 0 ) && (dsc_status != BAD_STATUS)) ||
+           ( (status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0) ) 
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMSTOP)\n", status[0]);
+         return -EIO;
+      }
+      
+      return 0;
+      break;
+
+   case CDROMPAUSE:     /* Pause the drive */
+      cmd_buff[0] = SONY535_HOLD;  /* CDU-31 driver uses AUDIO_STOP, not pause */
+      if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 ) 
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMPAUSE)\n", status[0]);
+         return -EIO;
+      }
+
+      /* Get the current position and save it for resuming */
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+      cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+      cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+      sony_audio_status = CDROM_AUDIO_PAUSED;
+      return 0;
+      break;
+
+   case CDROMRESUME:    /* Start the drive after being paused */
+      set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status );
+      
+      if (sony_audio_status != CDROM_AUDIO_PAUSED)
+      {
+         return -EINVAL;
+      }
+      
+      spin_up_drive( status );
+      
+      /* Start the drive at the saved position. */
+      cmd_buff[0] = SONY535_PLAY_AUDIO;
+      cmd_buff[1] = 0;  /* play back starting at this address */
+      cmd_buff[2] = cur_pos_msf[0];
+      cmd_buff[3] = cur_pos_msf[1];
+      cmd_buff[4] = cur_pos_msf[2];
+      cmd_buff[5] = SONY535_PLAY_AUDIO;
+      cmd_buff[6] = 2;  /* set ending address */
+      cmd_buff[7] = final_pos_msf[0];
+      cmd_buff[8] = final_pos_msf[1];
+      cmd_buff[9] = final_pos_msf[2];
+      if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) ||
+           (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) )
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMRESUME)\n", status[0]);
+         return -EIO;
+      }
+      sony_audio_status = CDROM_AUDIO_PLAY;
+      return 0;
+      break;
+
+   case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+      verify_area(VERIFY_READ, (char *) arg, 6);
+      spin_up_drive( status );
+      set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status );
+      memcpy_fromfs(params, (void *) arg, 6);
+      
+      /* The parameters are given in int, must be converted */
+      for (i=0; i<3; i++)
+      {
+         cmd_buff[2+i] = int_to_bcd(params[i]);
+         cmd_buff[7+i] = int_to_bcd(params[i+3]);
+      }
+      cmd_buff[0] = SONY535_PLAY_AUDIO;
+      cmd_buff[1] = 0;  /* play back starting at this address */
+       /* cmd_buff[2-4] are filled in for loop above */
+      cmd_buff[5] = SONY535_PLAY_AUDIO;
+      cmd_buff[6] = 2;  /* set ending address */
+       /* cmd_buff[7-9] are filled in for loop above */
+      if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) ||
+           (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) )
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMPLAYMSF)\n", status[0]);
+         return -EIO;
+      }
+      
+      /* Save the final position for pauses and resumes */
+      final_pos_msf[0] = cmd_buff[7];
+      final_pos_msf[1] = cmd_buff[8];
+      final_pos_msf[2] = cmd_buff[9];
+      sony_audio_status = CDROM_AUDIO_PLAY;
+      return 0;
+      break;
+
+   case CDROMREADTOCHDR:        /* Read the table of contents header */
+      {
+         struct cdrom_tochdr *hdr;
+         struct cdrom_tochdr loc_hdr;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         hdr = (struct cdrom_tochdr *) arg;
+         verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
+         loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+         loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+         memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
+      }
+      return 0;
+      break;
+
+   case CDROMREADTOCENTRY:      /* Read a given table of contents entry */
+      {
+         struct cdrom_tocentry *entry;
+         struct cdrom_tocentry loc_entry;
+         int track_idx;
+         unsigned char *msf_val = NULL;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         entry = (struct cdrom_tocentry *) arg;
+         verify_area(VERIFY_WRITE /* and read */, entry, sizeof(*entry));
+         
+         memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
+         
+         /* Lead out is handled separately since it is special. */
+         if (loc_entry.cdte_track == CDROM_LEADOUT)
+         {
+            loc_entry.cdte_adr = 0 /*sony_toc->address2*/;
+            loc_entry.cdte_ctrl = sony_toc->control2;
+            msf_val = sony_toc->lead_out_start_msf;
+         }
+         else
+         {
+            track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+            if (track_idx < 0)
+            {
+               return -EINVAL;
+            }
+            
+            loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address*/;
+            loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+            msf_val = sony_toc->tracks[track_idx].track_start_msf;
+         }
+         
+         /* Logical buffer address or MSF format requested? */
+         if (loc_entry.cdte_format == CDROM_LBA)
+         {
+            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+         }
+         else if (loc_entry.cdte_format == CDROM_MSF)
+         {
+            loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
+            loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
+            loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
+         }
+         memcpy_tofs(entry, &loc_entry, sizeof(*entry));
+      }
+      return 0;
+      break;
+
+   case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+      {
+         struct cdrom_ti ti;
+         int track_idx;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         verify_area( VERIFY_READ, (char *) arg, sizeof(ti));
+         
+         memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
+         if (   (ti.cdti_trk0 < sony_toc->first_track_num)
+             || (ti.cdti_trk0 > sony_toc->last_track_num)
+             || (ti.cdti_trk1 < ti.cdti_trk0))
+         {
+            return -EINVAL;
+         }
+         
+         track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+         if (track_idx < 0)
+         {
+            return -EINVAL;
+         }
+         params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+         params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+         params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
+         /*
+          * If we want to stop after the last track, use the lead-out
+          * MSF to do that.
+          */
+         if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
+         {
+            log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
+                       &(params[4]));
+         }
+         else
+         {
+            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
+            if (track_idx < 0)
+            {
+               return -EINVAL;
+            }
+            log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
+                       &(params[4]));
+         }
+         params[0] = 0x03;
+         
+         spin_up_drive( status );
+         
+         set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status );
+      
+         /* Start the drive at the saved position. */
+         cmd_buff[0] = SONY535_PLAY_AUDIO;
+         cmd_buff[1] = 0;  /* play back starting at this address */
+         cmd_buff[2] = params[1];
+         cmd_buff[3] = params[2];
+         cmd_buff[4] = params[3];
+         cmd_buff[5] = SONY535_PLAY_AUDIO;
+         cmd_buff[6] = 2;  /* set ending address */
+         cmd_buff[7] = params[4];
+         cmd_buff[8] = params[5];
+         cmd_buff[9] = params[6];
+         if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) ||
+              (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) )
+         {
+            printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
+                   params[2], params[3], params[4], params[5], params[6]);
+            printk("Sony CDROM error 0x%.2x (CDROMPLAYTRKIND)\n", status[0]);
+            return -EIO;
+         }
+         
+         /* Save the final position for pauses and resumes */
+         final_pos_msf[0] = params[4];
+         final_pos_msf[1] = params[5];
+         final_pos_msf[2] = params[6];
+         sony_audio_status = CDROM_AUDIO_PLAY;
+         return 0;
+      }
+     
+   case CDROMSUBCHNL:   /* Get subchannel info */
+      return sony_get_subchnl_info(arg);
+
+   case CDROMVOLCTRL:   /* Volume control.  What volume does this change, anyway? */
+      {
+         struct cdrom_volctrl volctrl;
+         
+         verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
+         
+         memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+         cmd_buff[0] = SONY535_SET_VOLUME;
+         cmd_buff[1] = volctrl.channel0;
+         cmd_buff[2] = volctrl.channel1;
+         if ( do_sony_cmd( cmd_buff,3, status, NULL,0, 0 ) != 0 )
+         {
+            printk("Sony CDROM error 0x%.2x (CDROMVOLCTRL)\n", status[0]);
+            return -EIO;
+         }
+      }
+      return 0;
+
+   case CDROMEJECT:     /* Eject the drive */
+      cmd_buff[0] = SONY535_STOP;
+      do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+      cmd_buff[0] = SONY535_SPIN_DOWN;
+      do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+
+      sony_audio_status = CDROM_AUDIO_INVALID;
+      cmd_buff[0] = SONY535_EJECT_CADDY;
+      if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 )
+      {
+         printk("Sony CDROM error 0x%.2x (CDROMEJECT)\n", status[0]);
+         return -EIO;
+      }
+      return 0;
+      break;
+     
+   default:
+      return -EINVAL;
+   }
+}
+
+
+/*
+ * Open the drive for operations.  Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+cdu_open(struct inode *inode,
+         struct file *filp)
+{
+   unsigned char status[2], cmd_buff[2];
+
+
+   if ( sony_inuse )
+   {
+      return( -EBUSY );
+   }
+   
+   if ( check_drive_status() != 0 )
+   {
+      return( -EIO );
+   }
+      
+   sony_inuse = 1;
+   
+   if ( spin_up_drive( status ) != 0 )
+   {
+      printk("Sony CDROM error 0x%.2x (cdu_open, spin up)\n", status[0]);
+      sony_inuse = 0;
+      return -EIO;
+   }
+      
+#if 0  /* what is this doing - CDU-535 does not have separate READ and REQ_TOC */
+   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
+   if ((res_size < 2) || (res_reg[0] != 0))
+   {
+      /* If the drive is already playing, its ok. */
+      if (res_reg[1] == SONY_AUDIO_PLAYING_ERR)
+      {
+         goto drive_spinning;
+      }
+
+      printk("Sony CDROM error 0x%.2x (cdu_open, read toc)\n", res_reg[1]);
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+      
+      sony_inuse = 0;
+      return -EIO;
+   }
+#endif
+
+   sony_get_toc();
+   if (!sony_toc_read)
+   {
+      cmd_buff[0] = SONY535_SPIN_DOWN;
+      do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+      sony_inuse = 0;
+      return -EIO;
+   }
+      
+#if 0  /* not used without stuff read toc above */
+drive_spinning:
+#endif
+
+   if ( inode ) 
+   {
+      check_disk_change(inode->i_rdev);
+   }
+
+   sony_usage++;
+   
+#ifdef LOCK_DOORS
+    /* disable the eject button while mounted */
+   cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
+   do_sony_cmd( cmd_buff,1, status, NULL,0, 0 );
+#endif
+
+   return 0;
+}
+
+
+/*
+ * Close the drive.  Spin it down if no task is using it.  The spin
+ * down will fail if playing audio, so audio play is OK.
+ */
+static void
+cdu_release(struct inode *inode,
+         struct file *filp)
+{
+   unsigned char status[2], cmd_no;
+
+   sony_inuse = 0;
+
+   if (sony_usage > 0)
+   {
+      sony_usage--;
+   }
+   if (sony_usage == 0)
+   {
+      sync_dev(inode->i_rdev);
+      check_drive_status();
+
+      if ( sony_audio_status != CDROM_AUDIO_PLAY ) {
+         cmd_no = SONY535_SPIN_DOWN;
+         do_sony_cmd( &cmd_no,1, status, NULL,0, 0 );
+         }
+#ifdef LOCK_DOORS
+       /* enable the eject button after umount */
+      cmd_no = SONY535_ENABLE_EJECT_BUTTON;
+      do_sony_cmd( &cmd_no,1, status, NULL,0, 0 );
+#endif
+   }
+}
+
+
+static struct file_operations cdu_fops = {
+   NULL,                   /* lseek - default */
+   block_read,             /* read - general block-dev read */
+   block_write,            /* write - general block-dev write */
+   NULL,                   /* readdir - bad */
+   NULL,                   /* select */
+   cdu_ioctl,              /* ioctl */
+   NULL,                   /* mmap */
+   cdu_open,               /* open */
+   cdu_release,            /* release */
+   NULL,                   /* fsync */
+   NULL,                   /* fasync */
+   cdu535_check_media_change, /* check_media_change */
+   NULL                    /* revalidate */
+};
+
+
+/*
+ * Initialize the driver.
+ */
+unsigned long
+init_sony535(unsigned long mem_start, unsigned long mem_end)
+{
+   struct s535_sony_drive_config drive_config;
+   unsigned char cmd_buff[3], ret_buff[2];
+   unsigned char status[2];
+   int retry_count;
+
+
+   /* Set up all the register locations */
+   result_reg = sony_cd_base_io;
+   command_reg = sony_cd_base_io;
+   data_reg = sony_cd_base_io + 1;
+   read_status_reg = sony_cd_base_io + 2;
+   select_unit_reg = sony_cd_base_io + 3;
+
+    /* look for the CD-ROM, follows the procedure in the DOS driver */
+   inb( select_unit_reg );
+   retry_count = jiffies + 2*HZ;
+   while ( retry_count > jiffies )
+      sony_sleep();		/* wait for 40 18 Hz ticks (from DOS driver) */
+   inb( result_reg );
+   
+   outb( 0, read_status_reg );	/* does a reset? */
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ( retry_count > jiffies ) 
+   {
+      select_unit(0);
+      if ( inb( result_reg ) != 0xff )
+         break;
+      sony_sleep();  /* about 1-2 ms on my machine */
+   }
+   
+   if ( (retry_count > jiffies) && (check_drive_status() != TIME_OUT) ) 
+   {  
+       /* CD-ROM drive responded --  get the drive configuration */
+      cmd_buff[0] = SONY535_INQUIRY;
+      if ( do_sony_cmd( cmd_buff,1, status, (Byte *)&drive_config, 28, 1) == 0 ) 
+      {
+          /* was able to get the configuration, set drive mode as rest of init */
+         if ( (status[0] & 0x7f) != 0 )
+            printk("Inquiry command returned status = 0x%x\n",status[0]);
+         cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+         cmd_buff[1] = 0x0;  /* default audio */
+         if (do_sony_cmd( cmd_buff,2, status, ret_buff,1, 1 ) == 0 ) 
+         {
+             /* set the drive mode successful, we are set! */
+            sony_buffer_size = SONY535_BUFFER_SIZE;
+            sony_buffer_sectors = sony_buffer_size / 2048;
+
+            printk("Sony I/F CDROM : %8.8s %16.16s %4.4s",
+                   drive_config.vendor_id,
+                   drive_config.product_id,
+                   drive_config.product_rev_level );
+            printk("  using %d byte buffer\n", sony_buffer_size);
+
+	    if (register_blkdev(MAJOR_NR,"cdu",&cdu_fops)) {
+		printk("Unable to get major %d for sony CDU-535 cd\n",MAJOR_NR);
+		return( mem_start );
+	    }
+            blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+            read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
+
+            sony_toc = (struct s535_sony_toc *) mem_start;
+            mem_start += sizeof(*sony_toc);
+            last_sony_subcode = (struct s535_sony_subcode *) mem_start;
+            mem_start += sizeof(*last_sony_subcode);
+            sony_buffer = (unsigned char *) mem_start;
+            mem_start += sony_buffer_size;
+
+            initialized = 1;
+         }
+      }
+   }
+
+   if ( !initialized )
+      printk( "Did not find a Sony CDU-535 drive\n");
+
+   return mem_start;
+}
+
+#endif  /* CONFIG_CDU535 */
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/version.h linux/drivers/block/version.h
--- linux-1.1.55+new_quota/drivers/block/version.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/block/version.h	Thu Oct 20 18:15:34 1994
@@ -0,0 +1,7 @@
+#define UTS_RELEASE "1.1.53"
+#define UTS_VERSION "#8 Wed Oct 12 21:48:39 CDT 1994"
+#define LINUX_COMPILE_TIME "21:48:39"
+#define LINUX_COMPILE_BY "root"
+#define LINUX_COMPILE_HOST "fuzzy"
+#define LINUX_COMPILE_DOMAIN "is.a.good.cat"
+#define LINUX_COMPILER "gcc version 2.5.8"
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/53c8xx_d.h linux/drivers/scsi/53c8xx_d.h
--- linux-1.1.55+new_quota/drivers/scsi/53c8xx_d.h	Sat Sep 17 21:48:35 1994
+++ linux/drivers/scsi/53c8xx_d.h	Thu Oct 20 18:15:34 1994
@@ -296,7 +296,7 @@
 ABSOLUTE reselected_tag = 0
 
 ; Request sense command pointer, its a 6 byte command, should
-; be constant for all commands since we allays want 16 bytes of 
+; be constant for all commands since we always want 16 bytes of 
 ; sense and we don't need to change any fields as we did under 
 ; SCSI-I when we actually cared about the LUN field.
 ;EXTERNAL NCR53c7xx_sense		; Request sense command
@@ -314,7 +314,7 @@
 ;
 ; MODIFIES : SCRATCH, reconnect_dsa_head
 ; 
-; EXITS : allays passes control to schedule
+; EXITS : always passes control to schedule
 
 ENTRY dsa_schedule
 dsa_schedule:
@@ -1378,7 +1378,7 @@
     ; XXX the ALU is only eight bits wide, and the assembler
     ; wont do the dirt work for us.  As long as dsa_check_reselect
     ; is negative, we need to sign extend with 1 bits to the full
-    ; 32 bit width of the address.
+    ; 32 bit width os the address.
     ;
     ; A potential work around would be to have a known alignment 
     ; of the DSA structure such that the base address plus 
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-1.1.55+new_quota/drivers/scsi/Makefile	Sat Sep 17 21:47:29 1994
+++ linux/drivers/scsi/Makefile	Thu Oct 20 18:15:35 1994
@@ -62,6 +62,11 @@
 SCSI_SRCS := $(SCSI_SRCS) aha1740.c
 endif
 
+ifdef CONFIG_SCSI_AHA274X
+SCSI_OBJS := $(SCSI_OBJS) aha274x.o
+SCSI_SRCS := $(SCSI_SRCS) aha274x.c
+endif
+
 ifdef CONFIG_SCSI_BUSLOGIC
 SCSI_OBJS := $(SCSI_OBJS) buslogic.o
 SCSI_SRCS := $(SCSI_SRCS) buslogic.c
@@ -70,6 +75,11 @@
 ifdef CONFIG_SCSI_DEBUG
 SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
 SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c
+endif
+
+ifdef CONFIG_SCSI_EATA
+SCSI_OBJS := $(SCSI_OBJS) eata.o
+SCSI_SRCS := $(SCSI_SRCS) eata.c
 endif
 
 ifdef CONFIG_SCSI_FUTURE_DOMAIN
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.c linux/drivers/scsi/aha274x.c
--- linux-1.1.55+new_quota/drivers/scsi/aha274x.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/aha274x.c	Thu Oct 20 18:15:36 1994
@@ -0,0 +1,1449 @@
+/*
+ *  @(#)aha274x.c 1.28 94/10/04 jda
+ *
+ *  Adaptec 274x device driver for Linux.
+ *  Copyright (c) 1994 The University of Calgary Department of Computer Science.
+ *  
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Sources include the Adaptec 1740 driver (aha1740.c), the
+ *  Ultrastor 24F driver (ultrastor.c), various Linux kernel
+ *  source, the Adaptec EISA config file (!adp7771.cfg), the
+ *  Adaptec AHA-2740A Series User's Guide, the Linux Kernel
+ *  Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ *  the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
+ *  overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
+ *  Technical Reference Manual, the Adaptec AIC-7770 Data
+ *  Book, the ANSI SCSI specification, the ANSI SCSI-2
+ *  specification (draft 10c), ...
+ *
+ *  On a twin-bus adapter card, channel B is ignored.  Rationale:
+ *  it would greatly complicate the sequencer and host driver code,
+ *  and both busses are multiplexed on to the EISA bus anyway.  So
+ *  I don't really see any technical advantage to supporting both.
+ *
+ *  As well, multiple adapter card using the same IRQ level are
+ *  not supported.  It doesn't make sense to configure the cards
+ *  this way from a performance standpoint.  Not to mention that
+ *  the kernel would have to support two devices per registered IRQ.
+ */
+
+#include <stdarg.h>
+#include <asm/io.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include "../block/blk.h"
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "aha274x.h"
+
+/*
+ *  There should be a specific return value for this in scsi.h, but
+ *  it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW	DID_ERROR
+
+/* EISA stuff */
+
+#define MINEISA		1
+#define MAXEISA		15
+#define SLOTBASE(x)	((x) << 12)
+
+#define MAXIRQ		15
+
+/* AIC-7770 offset definitions */
+
+#define O_MINREG(x)	((x) + 0xc00)		/* i/o range to reserve */
+#define O_MAXREG(x)	((x) + 0xcbf)
+
+#define O_SCSISEQ(x)	((x) + 0xc00)		/* scsi sequence control */
+#define O_SCSISIGI(x)	((x) + 0xc03)		/* scsi control signal read */
+#define O_SCSISIGO(x)	((x) + 0xc03)		/* scsi control signal write */
+#define O_SCSIID(x)	((x) + 0xc05)		/* scsi id */
+#define O_SSTAT0(x)	((x) + 0xc0b)		/* scsi status register 0 */
+#define O_CLRSINT1(x)	((x) + 0xc0c)		/* clear scsi interrupt 1 */
+#define O_SSTAT1(x)	((x) + 0xc0c)		/* scsi status register 1 */
+#define O_SELID(x)	((x) + 0xc19)		/* [re]selection id */
+#define O_SBLKCTL(x)	((x) + 0xc1f)		/* scsi block control */
+#define O_SEQCTL(x)	((x) + 0xc60)		/* sequencer control */
+#define O_SEQRAM(x)	((x) + 0xc61)		/* sequencer ram data */
+#define O_SEQADDR(x)	((x) + 0xc62)		/* sequencer address (W) */
+#define O_BIDx(x)	((x) + 0xc80)		/* board id */
+#define O_BCTL(x)	((x) + 0xc84)		/* board control */
+#define O_HCNTRL(x)	((x) + 0xc87)		/* host control */
+#define O_SCBPTR(x)	((x) + 0xc90)		/* scb pointer */
+#define O_INTSTAT(x)	((x) + 0xc91)		/* interrupt status */
+#define O_ERROR(x)	((x) + 0xc92)		/* hard error */
+#define O_CLRINT(x)	((x) + 0xc92)		/* clear interrupt status */
+#define O_SCBCNT(x)	((x) + 0xc9a)		/* scb auto increment */
+#define O_QINFIFO(x)	((x) + 0xc9b)		/* queue in fifo */
+#define O_QINCNT(x)	((x) + 0xc9c)		/* queue in count */
+#define O_QOUTFIFO(x)	((x) + 0xc9d)		/* queue out fifo */
+#define O_QOUTCNT(x)	((x) + 0xc9e)		/* queue out count */
+#define O_SCBARRAY(x)	((x) + 0xca0)		/* scb array start */
+
+/* host adapter offset definitions */
+
+#define HA_REJBYTE(x)	((x) + 0xc31)		/* 1st message in byte */
+#define HA_MSG_FLAGS(x)	((x) + 0xc35)		/* outgoing message flag */
+#define HA_MSG_LEN(x)	((x) + 0xc36)		/* outgoing message length */
+#define HA_MSG_START(x)	((x) + 0xc37)		/* outgoing message body */
+#define HA_ARG_1(x)	((x) + 0xc4c)		/* sdtr <-> rate parameters */
+#define HA_ARG_2(x)	((x) + 0xc4d)
+#define HA_RETURN_1(x)	((x) + 0xc4c)
+#define HA_RETURN_2(x)	((x) + 0xc4d)
+#define HA_SIGSTATE(x)	((x) + 0xc4e)		/* value in SCSISIGO */
+#define HA_NEEDSDTR(x)	((x) + 0xc4f)		/* synchronous negotiation? */
+
+#define HA_SCSICONF(x)	((x) + 0xc5a)		/* SCSI config register */
+#define HA_INTDEF(x)	((x) + 0xc5c)		/* interrupt def'n register */
+#define HA_HOSTCONF(x)	((x) + 0xc5d)		/* host config def'n register */
+
+/* debugging code */
+
+#define AHA274X_DEBUG
+
+/*
+ *  If a parity error occurs during a data transfer phase, run the
+ *  command to completion - it's easier that way - making a note
+ *  of the error condition in this location.  This then will modify
+ *  a DID_OK status into a DID_PARITY one for the higher-level SCSI
+ *  code.
+ */
+#define aha274x_parity(cmd)	((cmd)->SCp.Status)
+
+/*
+ *  Since the sequencer code DMAs the scatter-gather structures
+ *  directly from memory, we use this macro to assert that the
+ *  kernel structure hasn't changed.
+ */
+#define SG_STRUCT_CHECK(sg) \
+	((char *)&(sg).address - (char *)&(sg) != 0 ||	\
+	 (char *)&(sg).length  - (char *)&(sg) != 8 ||	\
+	 sizeof((sg).address) != 4 ||			\
+	 sizeof((sg).length)  != 4 ||			\
+	 sizeof(sg)	      != 12)
+
+/*
+ *  "Static" structures.  Note that these are NOT initialized
+ *  to zero inside the kernel - we have to initialize them all
+ *  explicitly.
+ *
+ *  We support a maximum of one adapter card per IRQ level (see the
+ *  rationale for this above).  On an interrupt, use the IRQ as an
+ *  index into aha274x_boards[] to locate the card information.
+ */
+static struct Scsi_Host *aha274x_boards[MAXIRQ + 1];
+
+struct aha274x_host {
+	int base;					/* card base address */
+	int startup;					/* intr type check */
+	volatile int unpause;				/* value for HCNTRL */
+	volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB];	/* active commands */
+};
+
+struct aha274x_scb {
+	unsigned char control;
+	unsigned char target_channel_lun;		/* 4/1/3 bits */
+	unsigned char SG_segment_count;
+	unsigned char SG_list_pointer[4];
+	unsigned char SCSI_cmd_pointer[4];
+	unsigned char SCSI_cmd_length;
+	unsigned char RESERVED[2];			/* must be zero */
+	unsigned char target_status;
+	unsigned char residual_data_count[3];
+	unsigned char residual_SG_segment_count;
+	unsigned char data_pointer[4];
+	unsigned char data_count[3];
+#if 0
+	/*
+	 *  No real point in transferring this to the
+	 *  SCB registers.
+	 */
+	unsigned char RESERVED[6];
+#endif
+};
+
+/*
+ *  NB.  This table MUST be ordered shortest period first.
+ */
+static struct {
+	short period;
+	short rate;
+	char *english;
+} aha274x_synctab[] = {
+	100,	0,	"10.0",
+	125,	1,	"8.0",
+	150,	2,	"6.67",
+	175,	3,	"5.7",
+	200,	4,	"5.0",
+	225,	5,	"4.4",
+	250,	6,	"4.0",
+	275,	7,	"3.6"
+};
+
+static int aha274x_synctab_max =
+	sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]);
+
+enum aha_type {
+	T_NONE,
+	T_274X,
+	T_284X,
+	T_MAX
+};
+
+#ifdef AHA274X_DEBUG
+
+	extern int vsprintf(char *, const char *, va_list);
+
+	static
+	void debug(const char *fmt, ...)
+	{
+		va_list ap;
+		char buf[256];
+
+		va_start(ap, fmt);
+		  vsprintf(buf, fmt, ap);
+		  printk(buf);
+		va_end(ap);
+	}
+
+	static
+	void debug_config(enum aha_type type, int base)
+	{
+		int ioport2, ioport3, ioport4;
+
+		static char *BRT[T_MAX][16] = {
+			{ },					/* T_NONE */
+			{
+				"2",   "???", "???", "12",	/* T_274X */
+				"???", "???", "???", "28",
+				"???", "???", "???", "44",
+				"???", "???", "???", "60"
+			},
+			{
+				"2",  "4",  "8",  "12",		/* T_284X */
+				"16", "20", "24", "28",
+				"32", "36", "40", "44",
+				"48", "52", "56", "60"
+			}
+		};
+		static int DFT[4] = {
+			0, 50, 75, 100
+		};
+		static int SST[4] = {
+			256, 128, 64, 32
+		};
+
+		ioport2 = inb(HA_HOSTCONF(base));
+		ioport3 = inb(HA_SCSICONF(base));
+		ioport4 = inb(HA_INTDEF(base));
+
+		if (type == T_284X)
+			printk("AHA284X AT SLOT %d:\n", base >> 12);
+		else
+			printk("AHA274X AT EISA SLOT %d:\n", base >> 12);
+
+		printk("    irq %d\n"
+		       "    bus release time %s bclks\n"
+		       "    data fifo threshold %d%%\n",
+		       ioport4 & 0xf,
+		       BRT[type][(ioport2 >> 2) & 0xf],
+		       DFT[(ioport2 >> 6) & 0x3]);
+
+		printk("    SCSI CHANNEL A:\n"
+		       "        scsi id %d\n"
+		       "        scsi bus parity check %sabled\n"
+		       "        scsi selection timeout %d ms\n"
+		       "        scsi bus reset at power-on %sabled\n",
+		       ioport3 & 0x7,
+		       (ioport3 & 0x20) ? "en" : "dis",
+		       SST[(ioport3 >> 3) & 0x3],
+		       (ioport3 & 0x40) ? "en" : "dis");
+
+		if (type == T_274X) {
+			printk("        scsi bus termination %sabled\n",
+			       (ioport3 & 0x80) ? "en" : "dis");
+		}
+	}
+
+	static
+	void debug_rate(int base, int rate)
+	{
+		int target = inb(O_SCSIID(base)) >> 4;
+
+		if (rate) {
+			printk("aha274x: target %d now synchronous at %sMb/s\n",
+			       target,
+			       aha274x_synctab[(rate >> 4) & 0x7].english);
+		} else {
+			printk("aha274x: target %d using asynchronous mode\n",
+			       target);
+		}
+	}
+
+#else
+
+#	define debug(fmt, args...)
+#	define debug_config(x)
+#	define debug_rate(x,y)
+
+#endif AHA274X_DEBUG
+
+static
+void aha274x_getscb(int base, struct aha274x_scb *scb)
+{
+	/*
+	 *  This is almost identical to aha274x_putscb().
+	 */
+	outb(0x80, O_SCBCNT(base));	/* SCBAUTO */
+
+	asm volatile("cld\n\t"
+		     "rep\n\t"
+		     "insb"
+		     : /* no output */
+		     :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
+		     :"di", "cx", "dx");
+
+	outb(0, O_SCBCNT(base));
+}
+
+/*
+ *  How much data should be transferred for this SCSI command?  Stop
+ *  at segment sg_last if it's a scatter-gather command so we can
+ *  compute underflow easily.
+ */
+static
+unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last)
+{
+	int i, segments;
+	unsigned length;
+	struct scatterlist *sg;
+
+	segments = cmd->use_sg - sg_last;
+	sg = (struct scatterlist *)cmd->buffer;
+
+	if (cmd->use_sg) {
+		for (i = length = 0;
+		     i < cmd->use_sg && i < segments;
+		     i++)
+		{
+			length += sg[i].length;
+		}
+	} else
+		length = cmd->request_bufflen;
+
+	return(length);
+}
+
+static
+void aha274x_sg_check(Scsi_Cmnd *cmd)
+{
+	int i;
+	struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
+
+	if (cmd->use_sg) {
+		for (i = 0; i < cmd->use_sg; i++)
+			if ((unsigned)sg[i].length > 0xffff)
+				panic("aha274x_sg_check: s/g segment > 64k\n");
+	}
+}
+
+static
+void aha274x_to_scsirate(unsigned char *rate,
+			 unsigned char transfer,
+			 unsigned char offset)
+{
+	int i;
+
+	transfer *= 4;
+
+	for (i = 0; i < aha274x_synctab_max-1; i++) {
+
+		if (transfer == aha274x_synctab[i].period) {
+			*rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf);
+			return;
+		}
+
+		if (transfer > aha274x_synctab[i].period &&
+		    transfer < aha274x_synctab[i+1].period)
+		{
+			*rate = (aha274x_synctab[i+1].rate << 4) |
+				(offset & 0xf);
+			return;
+		}
+	}
+	*rate = 0;
+}
+
+/*
+ *  Pause the sequencer and wait for it to actually stop - this
+ *  is important since the sequencer can disable pausing for critical
+ *  sections.
+ */
+#define PAUSE_SEQUENCER(p)	\
+	do {								\
+		outb(0xe, O_HCNTRL(p->base));	/* IRQMS|PAUSE|INTEN */	\
+									\
+		while ((inb(O_HCNTRL(p->base)) & 0x4) == 0)		\
+			;						\
+	} while (0)
+
+/*
+ *  Unpause the sequencer.  Unremarkable, yet done often enough to
+ *  warrant an easy way to do it.
+ */
+#define UNPAUSE_SEQUENCER(p)	\
+	outb(p->unpause, O_HCNTRL(p->base))	/* IRQMS|INTEN */
+
+/*
+ *  See comments in aha274x_loadram() wrt this.
+ */
+#define RESTART_SEQUENCER(p)	\
+	do {						\
+		do {					\
+			outb(0x2, O_SEQCTL(p->base));	\
+		} while (inw(O_SEQADDR(p->base)) != 0);	\
+							\
+		UNPAUSE_SEQUENCER(p);			\
+	} while (0)
+
+/*
+ *  Since we declared this using SA_INTERRUPT, interrupts should
+ *  be disabled all through this function unless we say otherwise.
+ */
+static
+void aha274x_isr(int irq)
+{
+	int base, intstat;
+	struct aha274x_host *p;
+	
+	p = (struct aha274x_host *)aha274x_boards[irq]->hostdata;
+	base = p->base;
+
+	/*
+	 *  Check the startup flag - if no commands have been queued,
+	 *  we probably have the interrupt type set wrong.  Reverse
+	 *  the stored value and the active one in the host control
+	 *  register.
+	 */
+	if (p->startup) {
+		p->unpause ^= 0x8;
+		outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
+		return;
+	}
+
+	/*
+	 *  Handle all the interrupt sources - especially for SCSI
+	 *  interrupts, we won't get a second chance at them.
+	 */
+	intstat = inb(O_INTSTAT(base));
+
+	if (intstat & 0x8) {				/* BRKADRINT */
+
+		panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
+		      inb(O_ERROR(base)), inw(O_SEQADDR(base)));
+	}
+
+	if (intstat & 0x4) {				/* SCSIINT */
+
+		int scbptr = inb(O_SCBPTR(base));
+		int status = inb(O_SSTAT1(base));
+		Scsi_Cmnd *cmd;
+
+		cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
+		if (!cmd) {
+			printk("aha274x_isr: no command for scb (scsiint)\n");
+			/*
+			 *  Turn off the interrupt and set status
+			 *  to zero, so that it falls through the
+			 *  reset of the SCSIINT code.
+			 */
+			outb(status, O_CLRSINT1(base));
+			UNPAUSE_SEQUENCER(p);
+			outb(0x4, O_CLRINT(base));	/* undocumented */
+			status = 0;
+		}
+		p->SCB_array[scbptr] = NULL;
+
+		/*
+		 *  Only the SCSI Status 1 register has information
+		 *  about exceptional conditions that we'd have a
+		 *  SCSIINT about; anything in SSTAT0 will be handled
+		 *  by the sequencer.  Note that there can be multiple
+		 *  bits set.
+		 */
+		if (status & 0x80) {			/* SELTO */
+			/*
+			 *  Hardware selection timer has expired.  Turn
+			 *  off SCSI selection sequence.
+			 */
+			outb(0, O_SCSISEQ(base));
+			cmd->result = DID_TIME_OUT << 16;
+
+			/*
+			 *  If there's an active message, it belongs to the
+			 *  command that is getting punted - remove it.
+			 */
+			outb(0, HA_MSG_FLAGS(base));
+
+			/*
+			 *  Shut off the offending interrupt sources, reset
+			 *  the sequencer address to zero and unpause it,
+			 *  then call the high-level SCSI completion routine.
+			 *
+			 *  WARNING!  This is a magic sequence!  After many
+			 *  hours of guesswork, turning off the SCSI interrupts
+			 *  in CLRSINT? does NOT clear the SCSIINT bit in
+			 *  INTSTAT.  By writing to the (undocumented, unused
+			 *  according to the AIC-7770 manual) third bit of
+			 *  CLRINT, you can clear INTSTAT.  But, if you do it
+			 *  while the sequencer is paused, you get a BRKADRINT
+			 *  with an Illegal Host Address status, so the
+			 *  sequencer has to be restarted first.
+			 */
+			outb(0x80, O_CLRSINT1(base));	/* CLRSELTIMO */
+			RESTART_SEQUENCER(p);
+
+			outb(0x4, O_CLRINT(base));	/* undocumented */
+			cmd->scsi_done(cmd);
+		}
+
+		if (status & 0x4) {			/* SCSIPERR */
+			/*
+			 *  A parity error has occurred during a data
+			 *  transfer phase.  Flag it and continue.
+			 */
+			printk("aha274x: parity error on target %d, lun %d\n",
+			       cmd->target,
+			       cmd->lun);
+			aha274x_parity(cmd) = DID_PARITY;
+
+			/*
+			 *  Clear interrupt and resume as above.
+			 */
+			outb(0x4, O_CLRSINT1(base));	/* CLRSCSIPERR */
+			UNPAUSE_SEQUENCER(p);
+
+			outb(0x4, O_CLRINT(base));	/* undocumented */
+		}
+
+		if ((status & (0x8|0x4)) == 0 && status) {
+			/*
+			 *  We don't know what's going on.  Turn off the
+			 *  interrupt source and try to continue.
+			 */
+			printk("aha274x_isr: sstat1 = 0x%x\n", status);
+			outb(status, O_CLRSINT1(base));
+			UNPAUSE_SEQUENCER(p);
+			outb(0x4, O_CLRINT(base));	/* undocumented */
+		}
+	}
+
+	if (intstat & 0x2) {				/* CMDCMPLT */
+
+		int complete, old_scbptr;
+		struct aha274x_scb scb;
+		unsigned actual;
+		Scsi_Cmnd *cmd;
+
+		/*
+		 *  The sequencer will continue running when it
+		 *  issues this interrupt.  There may be >1 commands
+		 *  finished, so loop until we've processed them all.
+		 */
+		do {
+			complete = inb(O_QOUTFIFO(base));
+
+			cmd = (Scsi_Cmnd *)p->SCB_array[complete];
+			if (!cmd) {
+				printk("aha274x warning: "
+				       "no command for scb (cmdcmplt)\n");
+				continue;
+			}
+			p->SCB_array[complete] = NULL;
+			
+			PAUSE_SEQUENCER(p);
+
+			/*
+			 *  After pausing the sequencer (and waiting
+			 *  for it to stop), save its SCB pointer, then
+			 *  write in our completed one and read the SCB
+			 *  registers.  Afterwards, restore the saved
+			 *  pointer, unpause the sequencer and call the
+			 *  higher-level completion function - unpause
+			 *  first since we have no idea how long done()
+			 *  will take.
+			 */
+			old_scbptr = inb(O_SCBPTR(base));
+			outb(complete, O_SCBPTR(base));
+
+			aha274x_getscb(base, &scb);
+			outb(old_scbptr, O_SCBPTR(base));
+
+			UNPAUSE_SEQUENCER(p);
+
+			cmd->result = scb.target_status |
+				     (aha274x_parity(cmd) << 16);
+
+			/*
+			 *  Did we underflow?  At this time, there's only
+			 *  one other driver that bothers to check for this,
+			 *  and cmd->underflow seems to be set rather half-
+			 *  heartedly in the higher-level SCSI code.
+			 */
+			actual = aha274x_length(cmd,
+						scb.residual_SG_segment_count);
+
+			actual -= ((scb.residual_data_count[2] << 16) |
+				   (scb.residual_data_count[1] <<  8) |
+				   (scb.residual_data_count[0]));
+
+			if (actual < cmd->underflow) {
+				printk("aha274x: target %d underflow - "
+				       "wanted (at least) %u, got %u\n",
+				       cmd->target, cmd->underflow, actual);
+
+				cmd->result = scb.target_status |
+					     (DID_UNDERFLOW << 16);
+			}
+
+			cmd->scsi_done(cmd);
+
+			/*
+			 *  Clear interrupt status before checking
+			 *  the output queue again.  This eliminates
+			 *  a race condition whereby a command could
+			 *  complete between the queue poll and the
+			 *  interrupt clearing, so notification of the
+			 *  command being complete never made it back
+			 *  up to the kernel.
+			 */
+			outb(0x2, O_CLRINT(base));	/* CLRCMDINT */
+
+		} while (inb(O_QOUTCNT(base)));
+	}
+
+	if (intstat & 0x1) {				/* SEQINT */
+
+		unsigned char transfer, offset, rate;
+
+		/*
+		 *  Although the sequencer is paused immediately on
+		 *  a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
+		 *  condition will have unpaused the sequencer before
+		 *  this point.
+		 */
+		PAUSE_SEQUENCER(p);
+
+		switch (intstat & 0xf0) {
+		    case 0x00:
+			panic("aha274x_isr: unknown scsi bus phase\n");
+		    case 0x10:
+			debug("aha274x_isr warning: "
+			      "issuing message reject, 1st byte 0x%x\n",
+			      inb(HA_REJBYTE(base)));
+			break;
+		    case 0x20:
+			panic("aha274x_isr: reconnecting target %d "
+			      "didn't issue IDENTIFY message\n",
+			      (inb(O_SELID(base)) >> 4) & 0xf);
+		    case 0x30:
+			debug("aha274x_isr: sequencer couldn't find match "
+			      "for reconnecting target %d - issuing ABORT\n",
+			      (inb(O_SELID(base)) >> 4) & 0xf);
+			break;
+		    case 0x40:
+			transfer = inb(HA_ARG_1(base));
+			offset = inb(HA_ARG_2(base));
+			aha274x_to_scsirate(&rate, transfer, offset);
+			outb(rate, HA_RETURN_1(base));
+			debug_rate(base, rate);
+			break;
+		    default:
+			debug("aha274x_isr: seqint, "
+			      "intstat = 0x%x, scsisigi = 0x%x\n",
+			      intstat, inb(O_SCSISIGI(base)));
+			break;
+		}
+
+		outb(0x1, O_CLRINT(base));		/* CLRSEQINT */
+		UNPAUSE_SEQUENCER(p);
+	}
+}
+
+/*
+ *  Probing for EISA boards: it looks like the first two bytes
+ *  are a manufacturer code - three characters, five bits each:
+ *
+ *		 BYTE 0   BYTE 1   BYTE 2   BYTE 3
+ *		?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ *  The characters are baselined off ASCII '@', so add that value
+ *  to each to get the real ASCII code for it.  The next two bytes
+ *  appear to be a product and revision number, probably vendor-
+ *  specific.  This is what is being searched for at each port,
+ *  and what should probably correspond to the ID= field in the
+ *  ECU's .cfg file for the card - if your card is not detected,
+ *  make sure your signature is listed in the array.
+ *
+ *  The fourth byte's lowest bit seems to be an enabled/disabled
+ *  flag (rest of the bits are reserved?).
+ */
+
+static
+enum aha_type aha274x_probe(int slot, int s_base)
+{
+	int i;
+	unsigned char buf[4];
+
+	static struct {
+		int n;
+		unsigned char signature[sizeof(buf)];
+		enum aha_type type;
+	} S[] = {
+		4, { 0x04, 0x90, 0x77, 0x71 }, T_274X,	/* host adapter 274x */
+		4, { 0x04, 0x90, 0x77, 0x70 }, T_274X,	/* motherboard 274x  */
+		4, { 0x04, 0x90, 0x77, 0x56 }, T_284X,	/* 284x, BIOS enabled */
+	};
+
+	for (i = 0; i < sizeof(buf); i++) {
+		/*
+		 *  The VL-bus cards need to be primed by
+		 *  writing before a signature check.
+		 */
+		outb(0x80 + i, s_base);
+		buf[i] = inb(s_base + i);
+	}
+
+	for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
+		if (!memcmp(buf, S[i].signature, S[i].n)) {
+			/*
+			 *  Signature match on enabled card?
+			 */
+			if (inb(s_base + 4) & 1)
+				return(S[i].type);
+			printk("aha274x disabled at slot %d, ignored\n", slot);
+		}
+	}
+	return(T_NONE);
+}
+
+/*
+ *  Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
+ *  wide channel, '?' for anything else.
+ */
+
+static
+char aha274x_type(int base)
+{
+	/*
+	 *  The AIC-7770 can be wired so that, on chip reset,
+	 *  the SCSI Block Control register indicates how many
+	 *  busses the chip is configured for.
+	 */
+	switch (inb(O_SBLKCTL(base))) {
+	    case 0:
+		return(' ');
+	    case 2:
+		return('W');
+	    case 8:
+		return('T');
+	    default:
+		printk("aha274x has unknown bus configuration\n");
+		return('?');
+	}
+}
+
+static
+void aha274x_loadram(int base)
+{
+	static unsigned char seqprog[] = {
+		/*
+		 *  Each sequencer instruction is 29 bits
+		 *  long (fill in the excess with zeroes)
+		 *  and has to be loaded from least -> most
+		 *  significant byte, so this table has the
+		 *  byte ordering reversed.
+		 */
+#		include "aha274x_seq.h"
+	};
+
+	/*
+	 *  When the AIC-7770 is paused (as on chip reset), the
+	 *  sequencer address can be altered and a sequencer
+	 *  program can be loaded by writing it, byte by byte, to
+	 *  the sequencer RAM port - the Adaptec documentation
+	 *  recommends using REP OUTSB to do this, hence the inline
+	 *  assembly.  Since the address autoincrements as we load
+	 *  the program, reset it back to zero afterward.  Disable
+	 *  sequencer RAM parity error detection while loading, and
+	 *  make sure the LOADRAM bit is enabled for loading.
+	 */
+	outb(0x83, O_SEQCTL(base));	/* PERRORDIS|SEQRESET|LOADRAM */
+
+	asm volatile("cld\n\t"
+		     "rep\n\t"
+		     "outsb"
+		     : /* no output */
+		     :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
+		     :"si", "cx", "dx");
+
+	/*
+	 *  WARNING!  This is a magic sequence!  After extensive
+	 *  experimentation, it seems that you MUST turn off the
+	 *  LOADRAM bit before you play with SEQADDR again, else
+	 *  you will end up with parity errors being flagged on
+	 *  your sequencer program.  (You would also think that
+	 *  turning off LOADRAM and setting SEQRESET to reset the
+	 *  address to zero would work, but you need to do it twice
+	 *  for it to take effect on the address.  Timing problem?)
+	 */
+	outb(0, O_SEQCTL(base));
+	do {
+		/*
+		 *  Actually, reset it until
+		 *  the address shows up as
+		 *  zero just to be safe..
+		 */
+		outb(0x2, O_SEQCTL(base));	/* SEQRESET */
+
+	} while (inw(O_SEQADDR(base)) != 0);
+}
+
+static
+int aha274x_register(Scsi_Host_Template *template,
+		     enum aha_type type,
+		     int base)
+{
+	int i, irq, scsi_id;
+	struct Scsi_Host *host;
+	struct aha274x_host *p;
+
+	/*
+	 *  Give the AIC-7770 a reset - reading the 274x's registers
+	 *  returns zeroes unless you do.  This forces a pause of the
+	 *  Sequencer.
+	 */
+	outb(1, O_HCNTRL(base));	/* CHIPRST */
+
+	/*
+	 *  The IRQ level in i/o port 4 maps directly onto the real
+	 *  IRQ number.  If it's ok, register it with the kernel.
+	 *
+	 *  NB. the Adaptec documentation says the IRQ number is only
+	 *	in the lower four bits; the ECU information shows the
+	 *	high bit being used as well.  Which is correct?
+	 */
+	irq = inb(HA_INTDEF(base)) & 0xf;
+	if (irq < 9 || irq > 15) {
+		printk("aha274x uses unsupported IRQ level, ignoring\n");
+		return(0);
+	}
+	
+	/*
+	 *  Lock out other contenders for our i/o space.
+	 */
+	snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base));
+
+	/*
+	 *  Any card-type-specific adjustments before we register
+	 *  the scsi host(s).
+	 */
+
+	scsi_id = inb(HA_SCSICONF(base)) & 0x7;
+
+	switch (aha274x_type(base)) {
+	    case 'T':
+		printk("aha274x warning: ignoring channel B of 274x-twin\n");
+		break;
+	    case ' ':
+		break;
+	    default:
+		printk("aha274x is an unsupported type, ignoring\n");
+		return(0);
+	}
+
+	/*
+	 *  Before registry, make sure that the offsets of the
+	 *  struct scatterlist are what the sequencer will expect,
+	 *  otherwise disable scatter-gather altogether until someone
+	 *  can fix it.  This is important since the sequencer will
+	 *  DMA elements of the SG array in while executing commands.
+	 */
+	if (template->sg_tablesize != SG_NONE) {
+		struct scatterlist sg;
+
+		if (SG_STRUCT_CHECK(sg)) {
+			printk("aha274x warning: kernel scatter-gather "
+			       "structures changed, disabling it\n");
+			template->sg_tablesize = SG_NONE;
+		}
+	}
+	
+	/*
+	 *  Register each "host" and fill in the returned Scsi_Host
+	 *  structure as best we can.  Some of the parameters aren't
+	 *  really relevant for EISA, and none of the high-level SCSI
+	 *  code looks at it anyway.. why are the fields there?  Also
+	 *  save the pointer so that we can find the information when
+	 *  an IRQ is triggered.
+	 */
+	host = scsi_register(template, sizeof(struct aha274x_host));
+	host->this_id = scsi_id;
+	host->irq = irq;
+
+	aha274x_boards[irq] = host;
+	
+	p = (struct aha274x_host *)host->hostdata;
+	for (i = 0; i < AHA274X_MAXSCB; i++)
+		p->SCB_array[i] = NULL;
+	p->base = base;
+
+	/*
+	 *  The interrupt trigger is different depending
+	 *  on whether the card is EISA or VL-bus - sometimes.
+	 *  The startup variable will be cleared once the first
+	 *  command is queued, and is checked in the isr to
+	 *  try and detect when the interrupt type is set
+	 *  incorrectly, triggering an interrupt immediately.
+	 */
+	p->unpause = (type != T_274X ? 0x2 : 0xa);
+	p->startup = !0;
+
+	/*
+	 *  Register IRQ with the kernel _after_ the host information
+	 *  is set up, in case we take an interrupt right away, due to
+	 *  the interrupt type being set wrong.
+	 */
+	if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) {
+		printk("aha274x couldn't register irq %d, ignoring\n", irq);
+		return(0);
+	}
+
+	/*
+	 *  Print out debugging information before re-enabling
+	 *  the card - a lot of registers on it can't be read
+	 *  when the sequencer is active.
+	 */
+	debug_config(type, base);
+
+	/*
+	 *  Load the sequencer program, then re-enable the board -
+	 *  resetting the AIC-7770 disables it, leaving the lights
+	 *  on with nobody home.
+	 */
+	aha274x_loadram(base);
+	outb(1, O_BCTL(base));		/* ENABLE */
+
+	/*
+	 *  Set the host adapter registers to indicate that synchronous
+	 *  negotiation should be attempted the first time the targets
+	 *  are communicated with.  Also initialize the active message
+	 *  flag to indicate that there is no message.
+	 */
+	outb(0xff, HA_NEEDSDTR(base));
+	outb(0, HA_MSG_FLAGS(base));
+
+	/*
+	 *  Unpause the sequencer before returning and enable
+	 *  interrupts - we shouldn't get any until the first
+	 *  command is sent to us by the high-level SCSI code.
+	 */
+	UNPAUSE_SEQUENCER(p);
+	return(1);
+}
+
+int aha274x_detect(Scsi_Host_Template *template)
+{
+	enum aha_type type;
+	int found = 0, slot, base;
+
+	for (slot = MINEISA; slot <= MAXEISA; slot++) {
+
+		base = SLOTBASE(slot);
+		
+		if (check_region(O_MINREG(base),
+				 O_MAXREG(base)-O_MINREG(base)))
+		{
+			/*
+			 *  Some other driver has staked a
+			 *  claim to this i/o region already.
+			 */
+			continue;
+		}
+
+		type = aha274x_probe(slot, O_BIDx(base));
+
+		if (type != T_NONE) {
+			/*
+			 *  We "find" a 274x if we locate the card
+			 *  signature and we can set it up and register
+			 *  it with the kernel without incident.
+			 */
+			found += aha274x_register(template, type, base);
+		}
+	}
+	template->name = (char *)aha274x_info();
+	return(found);
+}
+
+const char *aha274x_info(void)
+{
+	return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) "
+	       AHA274X_SEQ_VERSION "/"
+	       AHA274X_H_VERSION "/"
+	       "1.28");
+}
+
+int aha274x_command(Scsi_Cmnd *cmd)
+{
+	/*
+	 *  This is a relic of non-interrupt-driven SCSI
+	 *  drivers.  With the can_queue variable set, this
+	 *  should never be called.
+	 */
+	panic("aha274x_command was called\n");
+}
+
+static
+void aha274x_buildscb(struct aha274x_host *p,
+		      Scsi_Cmnd *cmd,
+		      struct aha274x_scb *scb)
+{
+	void *addr;
+	unsigned length;
+
+	memset(scb, 0, sizeof(*scb));
+
+	/*
+	 *  NB. channel selection (bit 3) is always zero.
+	 */
+	scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
+				   (cmd->lun & 0x7);
+
+	/*
+	 *  The interpretation of request_buffer and request_bufflen
+	 *  changes depending on whether or not use_sg is zero; a
+	 *  non-zero use_sg indicates the number of elements in the
+	 *  scatter-gather array.
+	 *
+	 *  The AIC-7770 can't support transfers of any sort larger
+	 *  than 2^24 (three-byte count) without backflips.  For what
+	 *  the kernel is doing, this shouldn't occur.  I hope.
+	 */
+	length = aha274x_length(cmd, 0);
+
+	/*
+	 *  The sequencer code cannot yet handle scatter-gather segments
+	 *  larger than 64k (two-byte length).  The 1.1.x kernels, however,
+	 *  have a four-byte length field in the struct scatterlist, so
+	 *  make sure we don't exceed 64k on these kernels for now.
+	 */
+	aha274x_sg_check(cmd);
+
+	if (length > 0xffffff) {
+		panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n");
+	}
+
+	/*
+	 *  XXX - this relies on the host data being stored in a
+	 *	  little-endian format.
+	 */
+	addr = cmd->cmnd;
+	scb->SCSI_cmd_length = COMMAND_SIZE(cmd->cmnd[0]);
+	memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+
+	if (cmd->use_sg) {
+#if 0
+		debug("aha274x_buildscb: SG used, %d segments, length %u\n",
+		      cmd->use_sg,
+		      length);
+#endif
+		scb->SG_segment_count = cmd->use_sg;
+		memcpy(scb->SG_list_pointer,
+		       &cmd->request_buffer,
+		       sizeof(scb->SG_list_pointer));
+	} else {
+		scb->SG_segment_count = 0;
+		memcpy(scb->data_pointer,
+		       &cmd->request_buffer,
+		       sizeof(scb->data_pointer));
+		memcpy(scb->data_count,
+		       &cmd->request_bufflen,
+		       sizeof(scb->data_count));
+	}
+}
+
+static
+void aha274x_putscb(int base, struct aha274x_scb *scb)
+{
+	/*
+	 *  By turning on the SCB auto increment, any reference
+	 *  to the SCB I/O space postincrements the SCB address
+	 *  we're looking at.  So turn this on and dump the relevant
+	 *  portion of the SCB to the card.
+	 */
+	outb(0x80, O_SCBCNT(base));	/* SCBAUTO */
+
+	asm volatile("cld\n\t"
+		     "rep\n\t"
+		     "outsb"
+		     : /* no output */
+		     :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
+		     :"si", "cx", "dx");
+
+	outb(0, O_SCBCNT(base));
+}
+
+int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+	long flags;
+	int empty, old_scbptr;
+	struct aha274x_host *p;
+	struct aha274x_scb scb;
+
+#if 0
+	debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n",
+	      cmd->cmnd[0],
+	      COMMAND_SIZE(cmd->cmnd[0]),
+	      cmd->target,
+	      cmd->lun);
+#endif
+
+	p = (struct aha274x_host *)cmd->host->hostdata;
+
+	/*
+	 *  Construct the SCB beforehand, so the sequencer is
+	 *  paused a minimal amount of time.
+	 */
+	aha274x_buildscb(p, cmd, &scb);
+
+	/*
+	 *  Clear the startup flag - we can now legitimately
+	 *  expect interrupts.
+	 */
+	p->startup = 0;
+
+	/*
+	 *  This is a critical section, since we don't want the
+	 *  interrupt routine mucking with the host data or the
+	 *  card.  Since the kernel documentation is vague on
+	 *  whether or not we are in a cli/sti pair already, save
+	 *  the flags to be on the safe side.
+	 */
+	save_flags(flags);
+	cli();
+
+	/*
+	 *  Find a free slot in the SCB array to load this command
+	 *  into.  Since can_queue is set to AHA274X_MAXSCB, we
+	 *  should always find one.
+	 */
+	for (empty = 0; empty < AHA274X_MAXSCB; empty++)
+		if (!p->SCB_array[empty])
+			break;
+	if (empty == AHA274X_MAXSCB)
+		panic("aha274x_queue: couldn't find a free scb\n");
+
+	/*
+	 *  Pause the sequencer so we can play with its registers -
+	 *  wait for it to acknowledge the pause.
+	 *
+	 *  XXX - should the interrupts be left on while doing this?
+	 */
+	PAUSE_SEQUENCER(p);
+
+	/*
+	 *  Save the SCB pointer and put our own pointer in - this
+	 *  selects one of the four banks of SCB registers.  Load
+	 *  the SCB, then write its pointer into the queue in FIFO
+	 *  and restore the saved SCB pointer.
+	 */
+	old_scbptr = inb(O_SCBPTR(p->base));
+	outb(empty, O_SCBPTR(p->base));
+	
+	aha274x_putscb(p->base, &scb);
+
+	outb(empty, O_QINFIFO(p->base));
+	outb(old_scbptr, O_SCBPTR(p->base));
+
+	/*
+	 *  Make sure the Scsi_Cmnd pointer is saved, the struct it
+	 *  points to is set up properly, and the parity error flag
+	 *  is reset, then unpause the sequencer and watch the fun
+	 *  begin.
+	 */
+	cmd->scsi_done = fn;
+	p->SCB_array[empty] = cmd;
+	aha274x_parity(cmd) = DID_OK;
+
+	UNPAUSE_SEQUENCER(p);
+
+	restore_flags(flags);
+	return(0);
+}
+
+/* return values from aha274x_kill */
+
+enum k_state {
+	k_ok,				/* scb found and message sent */
+	k_busy,				/* message already present */
+	k_absent,			/* couldn't locate scb */
+	k_disconnect,			/* scb found, but disconnected */
+};
+
+/*
+ *  This must be called with interrupts disabled - it's going to
+ *  be messing around with the host data, and an interrupt being
+ *  fielded in the middle could get ugly.
+ *
+ *  Since so much of the abort and reset code is shared, this
+ *  function performs more magic than it really should.  If the
+ *  command completes ok, then it will call scsi_done with the
+ *  result code passed in.  The unpause parameter controls whether
+ *  or not the sequencer gets unpaused - the reset function, for
+ *  instance, may want to do something more aggressive.
+ *
+ *  Note that the command is checked for in our SCB_array first
+ *  before the sequencer is paused, so if k_absent is returned,
+ *  then the sequencer is NOT paused.
+ */
+
+static
+enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message,
+			  unsigned int result, int unpause)
+{
+	struct aha274x_host *p;
+	int i, scb, found, queued;
+	unsigned char scbsave[AHA274X_MAXSCB];
+
+	p = (struct aha274x_host *)cmd->host->hostdata;
+
+	/*
+	 *  If we can't find the command, assume it just completed
+	 *  and shrug it away.
+	 */
+	for (scb = 0; scb < AHA274X_MAXSCB; scb++)
+		if (p->SCB_array[scb] == cmd)
+			break;
+
+	if (scb == AHA274X_MAXSCB)
+		return(k_absent);
+
+	PAUSE_SEQUENCER(p);
+
+	/*
+	 *  This is the best case, really.  Check to see if the
+	 *  command is still in the sequencer's input queue.  If
+	 *  so, simply remove it.  Reload the queue afterward.
+	 */
+	queued = inb(O_QINCNT(p->base));
+	
+	for (i = found = 0; i < queued; i++) {
+		scbsave[i] = inb(O_QINFIFO(p->base));
+
+		if (scbsave[i] == scb) {
+			found = 1;
+			i -= 1;
+		}
+	}
+
+	queued -= found;
+	for (i = 0; i < queued; i++)
+		outb(scbsave[i], O_QINFIFO(p->base));
+
+	if (found)
+		goto complete;
+
+	/*
+	 *  Check the current SCB bank.  If it's not the one belonging
+	 *  to the command we want to kill, assume that the command
+	 *  is disconnected.  It's rather a pain to force a reconnect
+	 *  and send a message to the target, so we abdicate responsibility
+	 *  in this case.
+	 */
+	if (inb(O_SCBPTR(p->base)) != scb) {
+		if (unpause)
+			UNPAUSE_SEQUENCER(p);
+		return(k_disconnect);
+	}
+
+	/*
+	 *  Presumably at this point our target command is active.  Check
+	 *  to see if there's a message already in effect.  If not, place
+	 *  our message in and assert ATN so the target goes into MESSAGE
+	 *  OUT phase.
+	 */
+	if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
+		if (unpause)
+			UNPAUSE_SEQUENCER(p);
+		return(k_busy);
+	}
+
+	outb(0x80, HA_MSG_FLAGS(p->base));		/* active message */
+	outb(1, HA_MSG_LEN(p->base));			/* length = 1 */
+	outb(message, HA_MSG_START(p->base));		/* message body */
+
+	/*
+	 *  Assert ATN.  Use the value of SCSISIGO saved by the
+	 *  sequencer code so we don't alter its contents radically
+	 *  in the middle of something critical.
+	 */
+	outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
+
+	/*
+	 *  The command has been killed.  Do the bookkeeping, unpause
+	 *  the sequencer, and notify the higher-level SCSI code.
+	 */
+complete:
+	p->SCB_array[scb] = NULL;
+	if (unpause)
+		UNPAUSE_SEQUENCER(p);
+
+	cmd->result = result << 16;
+	cmd->scsi_done(cmd);
+	return(k_ok);
+}
+
+int aha274x_abort(Scsi_Cmnd *cmd)
+{
+	int rv;
+	long flags;
+
+	save_flags(flags);
+	cli();
+
+	switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) {
+	    case k_ok:		rv = SCSI_ABORT_SUCCESS;	break;
+	    case k_busy:	rv = SCSI_ABORT_BUSY;		break;
+	    case k_absent:	rv = SCSI_ABORT_NOT_RUNNING;	break;
+	    case k_disconnect:	rv = SCSI_ABORT_SNOOZE;		break;
+	    default:
+		panic("aha274x_do_abort: internal error\n");
+	}
+
+	restore_flags(flags);
+	return(rv);
+}
+
+/*
+ *  Resetting the bus always succeeds - is has to, otherwise the
+ *  kernel will panic!  Try a surgical technique - sending a BUS
+ *  DEVICE RESET message - on the offending target before pulling
+ *  the SCSI bus reset line.
+ */
+
+int aha274x_reset(Scsi_Cmnd *cmd)
+{
+	int i;
+	long flags;
+	Scsi_Cmnd *reset;
+	struct aha274x_host *p;
+
+	p = (struct aha274x_host *)cmd->host->hostdata;
+	save_flags(flags);
+	cli();
+
+	switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
+
+	    case k_ok:
+		/*
+		 *  The RESET message was sent to the target
+		 *  with no problems.  Flag that target as
+		 *  needing a SDTR negotiation on the next
+		 *  connection and restart the sequencer.
+		 */
+		outb((1 << cmd->target), HA_NEEDSDTR(p->base));
+		UNPAUSE_SEQUENCER(p);
+		break;
+
+	    case k_absent:
+		/*
+		 *  The sequencer will not be paused if aha274x_kill()
+		 *  couldn't find the command.
+		 */
+		PAUSE_SEQUENCER(p);
+		/* falls through */
+
+	    case k_busy:
+	    case k_disconnect:
+		/*
+		 *  Do a hard reset of the SCSI bus.  According to the
+		 *  SCSI-2 draft specification, reset has to be asserted
+		 *  for at least 25us.  I'm invoking the kernel delay
+		 *  function for 30us since I'm not totally trusting of
+		 *  the busy loop timing.
+		 *
+		 *  XXX - I'm not convinced this works.  I tried resetting
+		 *	  the bus before, trying to get the devices on the
+		 *	  bus to revert to asynchronous transfer, and it
+		 *	  never seemed to work.
+		 */
+		debug("aha274x: attempting to reset scsi bus and card\n");
+
+		outb(1, O_SCSISEQ(p->base));		/* SCSIRSTO */
+		udelay(30);
+		outb(0, O_SCSISEQ(p->base));		/* !SCSIRSTO */
+
+		outb(0xff, HA_NEEDSDTR(p->base));
+		UNPAUSE_SEQUENCER(p);
+
+		/*
+		 *  Locate the command and return a "reset" status
+		 *  for it.  This is not completely correct and will
+		 *  probably return to haunt me later.
+		 */
+		for (i = 0; i < AHA274X_MAXSCB; i++) {
+			if (cmd == p->SCB_array[i]) {
+				reset = (Scsi_Cmnd *)p->SCB_array[i];
+				p->SCB_array[i] = NULL;
+				reset->result = DID_RESET << 16;
+				reset->scsi_done(reset);
+				break;
+			}
+		}
+		break;
+
+	    default:
+		panic("aha274x_reset: internal error\n");
+	}
+
+	restore_flags(flags);
+	return(SCSI_RESET_SUCCESS);
+}
+
+int aha274x_biosparam(Disk *disk, int devno, int geom[])
+{
+	/*
+	 *  XXX - when I find the EISA configuration information,
+	 *	  this should change to handle the "extended translation
+	 *	  for drives >1G" option, which uses 255 heads and
+	 *	  63 sectors/track for drives >1G.  Right now, just
+	 *	  assume it's turned off.
+	 */
+	debug("aha274x_biosparam warning: don't know translation config\n");
+
+	geom[0] = 64;
+	geom[1] = 32;
+	geom[2] = disk->capacity / (64 * 32);
+
+	return(0);
+}
+
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.h linux/drivers/scsi/aha274x.h
--- linux-1.1.55+new_quota/drivers/scsi/aha274x.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/aha274x.h	Thu Oct 20 18:15:36 1994
@@ -0,0 +1,62 @@
+/* @(#)aha274x.h 1.11 94/09/06 jda */
+
+/*
+ * Adaptec 274x device driver for Linux.
+ * Copyright (c) 1994 The University of Calgary Department of Computer Science.
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef aha274x_h
+#define aha274x_h
+
+#define	AHA274X_MAXSCB		4
+#define AHA274X_H_VERSION	"1.11"
+
+/*
+ *  Scsi_Host_Template (see hosts.h) for 274x - some fields
+ *  to do with card config are filled in after the card is
+ *  detected.
+ */
+#define AHA274X	{						\
+	NULL,							\
+	"",							\
+	aha274x_detect,						\
+	NULL,							\
+	aha274x_info,						\
+	aha274x_command,					\
+	aha274x_queue,						\
+	aha274x_abort,						\
+	aha274x_reset,						\
+	NULL,							\
+	aha274x_biosparam,					\
+	AHA274X_MAXSCB,		/* max simultaneous cmds      */\
+	-1,			/* scsi id of host adapter    */\
+	SG_ALL,			/* max scatter-gather cmds    */\
+	1,			/* cmds per lun (linked cmds) */\
+	0,			/* number of 274x's present   */\
+	0,			/* no memory DMA restrictions */\
+	DISABLE_CLUSTERING					\
+}
+
+extern int aha274x_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+extern int aha274x_biosparam(Disk *, int, int[]);
+extern int aha274x_detect(Scsi_Host_Template *);
+extern int aha274x_command(Scsi_Cmnd *);
+extern int aha274x_abort(Scsi_Cmnd *);
+extern int aha274x_reset(Scsi_Cmnd *);
+extern const char *aha274x_info(void);
+
+#endif
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.seq linux/drivers/scsi/aha274x.seq
--- linux-1.1.55+new_quota/drivers/scsi/aha274x.seq	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/aha274x.seq	Thu Oct 20 18:15:36 1994
@@ -0,0 +1,1021 @@
+# @(#)aha274x.seq 1.28 94/10/04 jda
+#
+# Adaptec 274x device driver for Linux.
+# Copyright (c) 1994 The University of Calgary Department of Computer Science.
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+VERSION AHA274X_SEQ_VERSION 1.28
+
+MAXSCB		= 4
+
+SCSISEQ		= 0x00
+SXFRCTL0	= 0x01
+SXFRCTL1	= 0x02
+SCSISIGI	= 0x03
+SCSISIGO	= 0x03
+SCSIRATE	= 0x04
+SCSIID		= 0x05
+SCSIDATL	= 0x06
+STCNT		= 0x08
+STCNT+0		= 0x08
+STCNT+1		= 0x09
+STCNT+2		= 0x0a
+SSTAT0		= 0x0b
+CLRSINT1	= 0x0c
+SSTAT1		= 0x0c
+SIMODE1		= 0x11
+SCSIBUSL	= 0x12
+SHADDR		= 0x14
+SELID		= 0x19
+SBLKCTL		= 0x1f
+SEQCTL		= 0x60
+A		= 0x64				# == ACCUM
+SINDEX		= 0x65
+DINDEX		= 0x66
+ALLZEROS	= 0x6a
+NONE		= 0x6a
+SINDIR		= 0x6c
+DINDIR		= 0x6d
+FUNCTION1	= 0x6e
+HADDR		= 0x88
+HCNT		= 0x8c
+HCNT+0		= 0x8c
+HCNT+1		= 0x8d
+HCNT+2		= 0x8e
+SCBPTR		= 0x90
+INTSTAT		= 0x91
+DFCNTRL		= 0x93
+DFSTATUS	= 0x94
+DFDAT		= 0x99
+QINFIFO		= 0x9b
+QINCNT		= 0x9c
+QOUTFIFO	= 0x9d
+
+SCSICONF	= 0x5a
+
+#  The two reserved bytes at SCBARRAY+1[23] are expected to be set to
+#  zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
+#  to indicate whether or not to reload scatter-gather parameters after
+#  a disconnect.
+#
+SCBARRAY+0	= 0xa0
+SCBARRAY+1	= 0xa1
+SCBARRAY+2	= 0xa2
+SCBARRAY+3	= 0xa3
+SCBARRAY+7	= 0xa7
+SCBARRAY+11	= 0xab
+SCBARRAY+14	= 0xae
+SCBARRAY+15	= 0xaf
+SCBARRAY+16	= 0xb0
+SCBARRAY+17	= 0xb1
+SCBARRAY+18	= 0xb2
+SCBARRAY+19	= 0xb3
+SCBARRAY+20	= 0xb4
+SCBARRAY+21	= 0xb5
+SCBARRAY+22	= 0xb6
+SCBARRAY+23	= 0xb7
+SCBARRAY+24	= 0xb8
+SCBARRAY+25	= 0xb9
+
+SIGNAL_0	= 0x01				# unknown scsi bus phase
+SIGNAL_1	= 0x11				# message reject
+SIGNAL_2	= 0x21				# no IDENTIFY after reconnect
+SIGNAL_3	= 0x31				# no cmd match for reconnect
+SIGNAL_4	= 0x41				# SDTR -> SCSIRATE conversion
+
+#  The host adapter card (at least the BIOS) uses 20-2f for SCSI
+#  device information, 32-33 and 5a-5f as well.  Since we don't support
+#  wide or twin-bus SCSI, 28-2f can be reclaimed.  As it turns out, the
+#  BIOS trashes 20-27 anyway, writing the synchronous negotiation results
+#  on top of the BIOS values, so we re-use those for our per-target
+#  scratchspace (actually a value that can be copied directly into
+#  SCSIRATE).  This implies, since we can't get the BIOS config values,
+#  that all targets will be negotiated with for synchronous transfer.
+#  NEEDSDTR has one bit per target indicating if an SDTR message is
+#  needed for that device - this will be set initially, as well as
+#  after a bus reset condition.
+#
+#  The high bit of DROPATN is set if ATN should be dropped before the ACK
+#  when outb is called.  REJBYTE contains the first byte of a MESSAGE IN
+#  message, so the driver can report an intelligible error if a message is
+#  rejected.
+#
+#  RESELECT's high bit is true if we are currently handling a reselect;
+#  its next-highest bit is true ONLY IF we've seen an IDENTIFY message
+#  from the reselecting target.  If we haven't had IDENTIFY, then we have
+#  no idea what the lun is, and we can't select the right SCB register
+#  bank, so force a kernel panic if the target attempts a data in/out or
+#  command phase instead of corrupting something.
+#
+#  Note that SG_NEXT occupies four bytes.
+#
+SYNCNEG		= 0x20
+DISC_DSB_A	= 0x32
+
+DROPATN		= 0x30
+REJBYTE		= 0x31
+RESELECT	= 0x34
+
+MSG_FLAGS	= 0x35
+MSG_LEN		= 0x36
+MSG_START+0	= 0x37
+MSG_START+1	= 0x38
+MSG_START+2	= 0x39
+MSG_START+3	= 0x3a
+MSG_START+4	= 0x3b
+MSG_START+5	= 0x3c
+-MSG_START+0	= 0xc9				# 2's complement of MSG_START+0
+
+ARG_1		= 0x4c				# sdtr conversion args & return
+ARG_2		= 0x4d
+RETURN_1	= 0x4c
+
+SIGSTATE	= 0x4e				# value written to SCSISIGO
+NEEDSDTR	= 0x4f				# send SDTR message, 1 bit/trgt
+
+SG_SIZEOF	= 12				# sizeof(struct scatterlist)
+SG_NOLOAD	= 0x50				# load SG pointer/length?
+SG_COUNT	= 0x51				# working value of SG count
+SG_NEXT		= 0x52				# working value of SG pointer
+SG_NEXT+0	= 0x52
+SG_NEXT+1	= 0x53
+SG_NEXT+2	= 0x54
+SG_NEXT+3	= 0x55
+
+#  Poll QINCNT for work - the lower three bits contain
+#  the number of entries in the Queue In FIFO.
+#
+start:
+	test	SCSISIGI,0x4	jnz reselect	# BSYI
+	test	QINCNT,0x7	jz start
+
+#  We have at least one queued SCB now.  Set the SCB pointer
+#  from the FIFO so we see the right bank of SCB registers,
+#  then set SCSI options and set the initiator and target
+#  SCSI IDs.
+#
+	mov	SCBPTR,QINFIFO
+	mov	SCBARRAY+1	call initialize
+	clr	SG_NOLOAD
+	clr	RESELECT
+
+#  As soon as we get a successful selection, the target should go
+#  into the message out phase since we have ATN asserted.  Prepare
+#  the message to send, locking out the device driver.  If the device
+#  driver hasn't beaten us with an ABORT or RESET message, then tack
+#  on a SDTR negotation if required.
+#
+#  Messages are stored in scratch RAM starting with a flag byte (high bit
+#  set means active message), one length byte, and then the message itself.
+#
+	mov	SCBARRAY+1	call disconnect	# disconnect ok?
+
+	and	SINDEX,0x7,SCBARRAY+1		# lun
+	or	SINDEX,A			# return value from disconnect
+	or	SINDEX,0x80	call mk_mesg	# IDENTIFY message
+
+	mov	A,SINDEX
+	cmp	MSG_START+0,A	jne !message	# did driver beat us?
+	mvi	MSG_START+1	call mk_sdtr	# build SDTR message if needed
+
+!message:
+
+#  Enable selection phase as an initiator, and do automatic ATN
+#  after the selection.
+#
+	mvi	SCSISEQ,0x48			# ENSELO|ENAUTOATNO
+
+#  Wait for successful arbitration.  The AIC-7770 documentation says
+#  that SELINGO indicates successful arbitration, and that it should
+#  be used to look for SELDO.  However, if the sequencer is paused at
+#  just the right time - a parallel fsck(8) on two drives did it for
+#  me - then SELINGO can flip back to false before we've seen it.  This
+#  makes the sequencer sit in the arbitration loop forever.  This is
+#  Not Good.
+#
+#  Therefore, I've added a check in the arbitration loop for SELDO
+#  too.  This could arguably be made a critical section by disabling
+#  pauses, but I don't want to make a potentially infinite loop a CS.
+#  I suppose you could fold it into the select loop, too, but since
+#  I've been hunting this bug for four days it's kinda like a trophy.
+#
+arbitrate:
+	test	SSTAT0,0x40	jnz *select	# SELDO
+	test	SSTAT0,0x10	jz arbitrate	# SELINGO
+
+#  Wait for a successful selection.  If the hardware selection
+#  timer goes off, then the driver gets the interrupt, so we don't
+#  need to worry about it.
+#
+select:
+	test	SSTAT0,0x40	jz select	# SELDO
+	jmp	*select
+
+#  Reselection is being initiated by a target - we've seen the BSY
+#  line driven active, and we didn't do it!  Enable the reselection
+#  hardware, and wait for it to finish.  Make a note that we've been
+#  reselected, but haven't seen an IDENTIFY message from the target
+#  yet.
+#
+reselect:
+	mvi	SCSISEQ,0x10			# ENRSELI
+
+reselect1:
+	test	SSTAT0,0x20	jz reselect1	# SELDI
+	mov	SELID		call initialize
+
+	mvi	RESELECT,0x80			# reselected, no IDENTIFY
+
+#  After the [re]selection, make sure that the [re]selection enable
+#  bit is off.  This chip is flaky enough without extra things
+#  turned on.  Also clear the BUSFREE bit in SSTAT1 since we'll be
+#  using it shortly.
+#
+*select:
+	clr	SCSISEQ
+	mvi	CLRSINT1,0x8			# CLRBUSFREE
+
+#  Main loop for information transfer phases.  If BSY is false, then
+#  we have a bus free condition, expected or not.  Otherwise, wait
+#  for the target to assert REQ before checking MSG, C/D and I/O
+#  for the bus phase.
+#
+#  We can't simply look at the values of SCSISIGI here (if we want
+#  to do synchronous data transfer), because the target won't assert
+#  REQ if it's already sent us some data that we haven't acknowledged
+#  yet.
+#
+ITloop:
+	test	SSTAT1,0x8	jnz p_busfree	# BUSFREE
+	test	SSTAT1,0x1	jz ITloop	# REQINIT
+
+	and	A,0xe0,SCSISIGI			# CDI|IOI|MSGI
+
+	cmp	ALLZEROS,A	je p_dataout
+	cmp	A,0x40		je p_datain
+	cmp	A,0x80		je p_command
+	cmp	A,0xc0		je p_status
+	cmp	A,0xa0		je p_mesgout
+	cmp	A,0xe0		je p_mesgin
+
+	mvi	INTSTAT,SIGNAL_0		# unknown - signal driver
+
+p_dataout:
+	mvi	0		call scsisig	# !CDO|!IOO|!MSGO
+	call	assert
+	call	sg_load
+
+	mvi	A,3
+	mvi	DINDEX,HCNT
+	mvi	SCBARRAY+23	call bcopy
+
+	mvi	A,3
+	mvi	DINDEX,STCNT
+	mvi	SCBARRAY+23	call bcopy
+
+	mvi	A,4
+	mvi	DINDEX,HADDR
+	mvi	SCBARRAY+19	call bcopy
+
+	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
+						#   DIRECTION|FIFORESET
+
+#  After a DMA finishes, save the final transfer pointer and count
+#  back into the SCB, in case a device disconnects in the middle of
+#  a transfer.  Use SHADDR and STCNT instead of HADDR and HCNT, since
+#  it's a reflection of how many bytes were transferred on the SCSI
+#  (as opposed to the host) bus.
+#
+	mvi	A,3
+	mvi	DINDEX,SCBARRAY+23
+	mvi	STCNT		call bcopy
+
+	mvi	A,4
+	mvi	DINDEX,SCBARRAY+19
+	mvi	SHADDR		call bcopy
+
+	call	sg_advance
+	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+
+	jmp	ITloop
+
+p_datain:
+	mvi	0x40		call scsisig	# !CDO|IOO|!MSGO
+	call	assert
+	call	sg_load
+
+	mvi	A,3
+	mvi	DINDEX,HCNT
+	mvi	SCBARRAY+23	call bcopy
+
+	mvi	A,3
+	mvi	DINDEX,STCNT
+	mvi	SCBARRAY+23	call bcopy
+
+	mvi	A,4
+	mvi	DINDEX,HADDR
+	mvi	SCBARRAY+19	call bcopy
+
+	mvi	0x39		call dma	# SCSIEN|SDMAEN|HDMAEN|
+						#   !DIRECTION|FIFORESET
+	mvi	A,3
+	mvi	DINDEX,SCBARRAY+23
+	mvi	STCNT		call bcopy
+
+	mvi	A,4
+	mvi	DINDEX,SCBARRAY+19
+	mvi	SHADDR		call bcopy
+
+	call	sg_advance
+	mov	SCBARRAY+18,SG_COUNT		# residual S/G count
+
+	jmp	ITloop
+
+#  Command phase.  Set up the DMA registers and let 'er rip - the
+#  two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
+#  so we can copy those three bytes directly into HCNT.
+#
+p_command:
+	mvi	0x80		call scsisig	# CDO|!IOO|!MSGO
+	call	assert
+
+	mvi	A,3
+	mvi	DINDEX,HCNT
+	mvi	SCBARRAY+11	call bcopy
+
+	mvi	A,3
+	mvi	DINDEX,STCNT
+	mvi	SCBARRAY+11	call bcopy
+
+	mvi	A,4
+	mvi	DINDEX,HADDR
+	mvi	SCBARRAY+7	call bcopy
+
+	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
+						#   DIRECTION|FIFORESET
+	jmp	ITloop
+
+#  Status phase.  Wait for the data byte to appear, then read it
+#  and store it into the SCB.
+#
+p_status:
+	mvi	0xc0		call scsisig	# CDO|IOO|!MSGO
+
+	mvi	SCBARRAY+14	call inb
+	jmp	ITloop
+
+#  Message out phase.  If there is no active message, but the target
+#  took us into this phase anyway, build a no-op message and send it.
+#
+p_mesgout:
+	mvi	0xa0		call scsisig	# CDO|!IOO|MSGO
+	mvi	0x8		call mk_mesg	# build NOP message
+
+#  Set up automatic PIO transfer from MSG_START.  Bit 3 in
+#  SXFRCTL0 (SPIOEN) is already on.
+#
+	mvi	SINDEX,MSG_START+0
+	mov	DINDEX,MSG_LEN
+	clr	A
+
+#  When target asks for a byte, drop ATN if it's the last one in
+#  the message.  Otherwise, keep going until the message is exhausted.
+#  (We can't use outb for this since it wants the input in SINDEX.)
+#
+#  Keep an eye out for a phase change, in case the target issues
+#  a MESSAGE REJECT.
+#
+p_mesgout2:
+	test	SSTAT0,0x2	jz p_mesgout2	# SPIORDY
+	test	SSTAT1,0x10	jnz p_mesgout6	# PHASEMIS
+
+	cmp	DINDEX,1	jne p_mesgout3	# last byte?
+	mvi	CLRSINT1,0x40			# CLRATNO - drop ATN
+
+#  Write a byte to the SCSI bus.  The AIC-7770 refuses to automatically
+#  send ACKs in automatic PIO or DMA mode unless you make sure that the
+#  "expected" bus phase in SCSISIGO matches the actual bus phase.  This
+#  behaviour is completely undocumented and caused me several days of
+#  grief.
+#
+#  After plugging in different drives to test with and using a longer
+#  SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
+#  especially when transferring >1 byte.  It seems to be much more stable
+#  if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
+#  polled for transfer completion - for both output _and_ input.  The
+#  only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
+#  is accessed (like the documentation says it does), and that on a longer
+#  cable run, the sequencer code was fast enough to loop back and see
+#  an SPIORDY that hadn't dropped yet.
+#
+p_mesgout3:
+	call	one_stcnt
+	mov	SCSIDATL,SINDIR
+
+p_mesgout4:
+	test	SSTAT0,0x4	jz p_mesgout4	# SDONE
+	dec	DINDEX
+	inc	A
+	cmp	MSG_LEN,A	jne p_mesgout2
+
+#  If the next bus phase after ATN drops is a message out, it means
+#  that the target is requesting that the last message(s) be resent.
+#
+p_mesgout5:
+	test	SSTAT1,0x8	jnz p_mesgout6	# BUSFREE
+	test	SSTAT1,0x1	jz p_mesgout5	# REQINIT
+
+	and	A,0xe0,SCSISIGI			# CDI|IOI|MSGI
+	cmp	A,0xa0		jne p_mesgout6
+	mvi	0x10		call scsisig	# ATNO - re-assert ATN
+
+	jmp	ITloop
+
+p_mesgout6:
+	mvi	CLRSINT1,0x40			# CLRATNO - in case of PHASEMIS
+	clr	MSG_FLAGS			# no active msg
+	jmp	ITloop
+
+#  Message in phase.  Bytes are read using Automatic PIO mode, but not
+#  using inb.  This alleviates a race condition, namely that if ATN had
+#  to be asserted under Automatic PIO mode, it had to beat the SCSI
+#  circuitry sending an ACK to the target.  This showed up under heavy
+#  loads and really confused things, since ABORT commands wouldn't be
+#  seen by the drive after an IDENTIFY message in until it had changed
+#  to a data I/O phase.
+#
+p_mesgin:
+	mvi	0xe0		call scsisig	# CDO|IOO|MSGO
+	mvi	A		call inb_first	# read the 1st message byte
+	mvi	REJBYTE,A			# save it for the driver
+
+	cmp	ALLZEROS,A	jne p_mesgin1
+
+#  We got a "command complete" message, so put the SCB pointer
+#  into the Queue Out, and trigger a completion interrupt.
+#
+	mov	QOUTFIFO,SCBPTR
+	mvi	INTSTAT,0x2			# CMDCMPLT
+	jmp	p_mesgin_done
+
+#  Is it an extended message?  We only support the synchronous data
+#  transfer request message, which will probably be in response to
+#  an SDTR message out from us.  If it's not an SDTR, reject it -
+#  apparently this can be done after any message in byte, according
+#  to the SCSI-2 spec.
+#
+#  XXX - we should really reject this if we didn't initiate the SDTR
+#	 negotiation; this may cause problems with unusual devices.
+#
+p_mesgin1:
+	cmp	A,1		jne p_mesgin2	# extended message code?
+	
+	mvi	A		call inb_next
+	cmp	A,3		jne p_mesginN	# extended mesg length = 3
+	mvi	A		call inb_next
+	cmp	A,1		jne p_mesginN	# SDTR code
+
+	mvi	ARG_1		call inb_next	# xfer period
+	mvi	ARG_2		call inb_next	# REQ/ACK offset
+	mvi	INTSTAT,SIGNAL_4		# call driver to convert
+
+	call	ndx_sdtr			# index sync config for target
+	mov	DINDEX,SINDEX
+	mov	DINDIR,RETURN_1			# save returned value
+
+	not	A				# turn off "need sdtr" flag
+	and	NEEDSDTR,A
+
+#  Even though the SCSI-2 specification says that a device responding
+#  to our SDTR message should honor our parameters for transmitting
+#  to us, it doesn't seem to work too well in real life.  In particular,
+#  a lot of CD-ROM and tape units don't function: try using the SDTR
+#  parameters the device sent us for both transmitting and receiving.
+#
+	mov	SCSIRATE,RETURN_1
+	jmp	p_mesgin_done
+
+#  Is it a disconnect message?  Set a flag in the SCB to remind us
+#  and await the bus going free.
+#
+p_mesgin2:
+	cmp	A,4		jne p_mesgin3	# disconnect code?
+
+	or	SCBARRAY+0,0x4			# set "disconnected" bit
+	jmp	p_mesgin_done
+
+#  Save data pointers message?  Copy working values into the SCB,
+#  usually in preparation for a disconnect.
+#
+p_mesgin3:
+	cmp	A,2		jne p_mesgin4	# save data pointers code?
+
+	call	sg_ram2scb
+	jmp	p_mesgin_done
+
+#  Restore pointers message?  Data pointers are recopied from the
+#  SCB anyway at the start of any DMA operation, so the only thing
+#  to copy is the scatter-gather values.
+#
+p_mesgin4:
+	cmp	A,3		jne p_mesgin5	# restore pointers code?
+
+	call	sg_scb2ram
+	jmp	p_mesgin_done
+
+#  Identify message?  For a reconnecting target, this tells us the lun
+#  that the reconnection is for - find the correct SCB and switch to it,
+#  clearing the "disconnected" bit so we don't "find" it by accident later.
+#
+p_mesgin5:
+	test	A,0x80		jz p_mesgin6	# identify message?
+
+	test	A,0x78		jnz p_mesginN	# !DiscPriv|!LUNTAR|!Reserved
+
+	mov	A		call findSCB	# switch to correct SCB
+
+#  If a active message is present after calling findSCB, then either it
+#  or the driver is trying to abort the command.  Either way, something
+#  untoward has happened and we should just leave it alone.
+#
+	test	MSG_FLAGS,0x80	jnz p_mesgin_done
+
+	xor	SCBARRAY+0,0x4			# clear disconnect bit in SCB
+	mvi	RESELECT,0xc0			# make note of IDENTIFY
+
+	call	sg_scb2ram			# implied restore pointers
+						#   required on reselect
+	jmp	p_mesgin_done
+
+#  Message reject?  If we have an outstanding SDTR negotiation, assume
+#  that it's a response from the target selecting asynchronous transfer,
+#  otherwise just ignore it since we have no clue what it pertains to.
+#
+#  XXX - I don't have a device that responds this way.  Does this code
+#	 actually work?
+#
+p_mesgin6:
+	cmp	A,7		jne p_mesgin7	# message reject code?
+
+	and	FUNCTION1,0x70,SCSIID		# outstanding SDTR message?
+	mov	A,FUNCTION1
+	test	NEEDSDTR,A	jz p_mesgin_done  # no - ignore rejection
+
+	call	ndx_sdtr			# note use of asynch xfer
+	mov	DINDEX,SINDEX
+	clr	DINDIR
+
+	not	A				# turn off "active sdtr" flag
+	and	NEEDSDTR,A
+
+	clr	SCSIRATE			# select asynch xfer
+	jmp	p_mesgin_done
+
+#  [ ADD MORE MESSAGE HANDLING HERE ]
+#
+p_mesgin7:
+
+#  We have no idea what this message in is, and there's no way
+#  to pass it up to the kernel, so we issue a message reject and
+#  hope for the best.  Since we're now using manual PIO mode to
+#  read in the message, there should no longer be a race condition
+#  present when we assert ATN.  In any case, rejection should be a
+#  rare occurrence - signal the driver when it happens.
+#
+p_mesginN:
+	or	SINDEX,0x10,SIGSTATE		# turn on ATNO
+	call	scsisig
+	mvi	INTSTAT,SIGNAL_1		# let driver know
+
+	mvi	0x7		call mk_mesg	# MESSAGE REJECT message
+
+p_mesgin_done:
+	call	inb_last			# ack & turn auto PIO back on
+	jmp	ITloop
+
+#  Bus free phase.  It might be useful to interrupt the device
+#  driver if we aren't expecting this.  For now, make sure that
+#  ATN isn't being asserted and look for a new command.
+#
+p_busfree:
+	mvi	CLRSINT1,0x40			# CLRATNO
+	clr	SIGSTATE
+	jmp	start
+
+#  Bcopy: number of bytes to transfer should be in A, DINDEX should
+#  contain the destination address, and SINDEX should contain the
+#  source address.  All input parameters are trashed on return.
+#
+bcopy:
+	mov	DINDIR,SINDIR
+	dec	A
+	cmp	ALLZEROS,A	jne bcopy
+	ret
+
+#  Locking the driver out, build a one-byte message passed in SINDEX
+#  if there is no active message already.  SINDEX is returned intact.
+#
+mk_mesg:
+	mvi	SEQCTL,0x40			# PAUSEDIS
+	test	MSG_FLAGS,0x80	jnz mk_mesg1	# active message?
+
+	mvi	MSG_FLAGS,0x80			# if not, there is now
+	mvi	MSG_LEN,1			# length = 1
+	mov	MSG_START+0,SINDEX		# 1-byte message
+
+mk_mesg1:
+	clr	SEQCTL				# !PAUSEDIS
+	ret
+
+#  Input byte in Automatic PIO mode.  The address to store the byte
+#  in should be in SINDEX.  DINDEX will be used by this routine.
+#
+inb:
+	test	SSTAT0,0x2	jz inb		# SPIORDY
+	mov	DINDEX,SINDEX
+	call	one_stcnt			# xfer one byte
+	mov	DINDIR,SCSIDATL
+inb1:
+	test	SSTAT0,0x4	jz inb1		# SDONE - wait to "finish"
+	ret
+
+#  Carefully read data in Automatic PIO mode.  I first tried this using
+#  Manual PIO mode, but it gave me continual underrun errors, probably
+#  indicating that I did something wrong, but I feel more secure leaving
+#  Automatic PIO on all the time.
+#
+#  According to Adaptec's documentation, an ACK is not sent on input from
+#  the target until SCSIDATL is read from.  So we wait until SCSIDATL is
+#  latched (the usual way), then read the data byte directly off the bus
+#  using SCSIBUSL.  When we have pulled the ATN line, or we just want to
+#  acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
+#  spec guarantees that the target will hold the data byte on the bus until
+#  we send our ACK.
+#
+#  The assumption here is that these are called in a particular sequence,
+#  and that REQ is already set when inb_first is called.  inb_{first,next}
+#  use the same calling convention as inb.
+#
+inb_first:
+	mov	DINDEX,SINDEX
+	mov	DINDIR,SCSIBUSL	ret		# read byte directly from bus
+
+inb_next:
+	mov	DINDEX,SINDEX			# save SINDEX
+
+	call	one_stcnt			# xfer one byte
+	mov	NONE,SCSIDATL			# dummy read from latch to ACK
+inb_next1:
+	test	SSTAT0,0x4	jz inb_next1	# SDONE
+inb_next2:
+	test	SSTAT0,0x2	jz inb_next2	# SPIORDY - wait for next byte
+	mov	DINDIR,SCSIBUSL	ret		# read byte directly from bus
+
+inb_last:
+	call	one_stcnt			# ACK with dummy read
+	mov	NONE,SCSIDATL
+inb_last1:
+	test	SSTAT0,0x4	jz inb_last1	# wait for completion
+	ret
+
+#  Output byte in Automatic PIO mode.  The byte to output should be
+#  in SINDEX.  If DROPATN's high bit is set, then ATN will be dropped
+#  before the byte is output.
+#
+outb:
+	test	SSTAT0,0x2	jz outb		# SPIORDY
+	call	one_stcnt			# xfer one byte
+
+	test	DROPATN,0x80	jz outb1
+	mvi	CLRSINT1,0x40			# CLRATNO
+	clr	DROPATN
+outb1:
+	mov	SCSIDATL,SINDEX
+outb2:
+	test	SSTAT0,0x4	jz outb2	# SDONE
+	ret
+
+#  Write the value "1" into the STCNT registers, for Automatic PIO
+#  transfers.
+#
+one_stcnt:
+	clr	STCNT+2
+	clr	STCNT+1
+	mvi	STCNT+0,1	ret
+
+#  DMA data transfer.  HADDR and HCNT must be loaded first, and
+#  SINDEX should contain the value to load DFCNTRL with - 0x3d for
+#  host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
+#  during initialization.
+#
+dma:
+	mov	DFCNTRL,SINDEX
+dma1:
+dma2:
+	test	SSTAT0,0x1	jnz dma3	# DMADONE
+	test	SSTAT1,0x10	jz dma1		# PHASEMIS, ie. underrun
+
+#  We will be "done" DMAing when the transfer count goes to zero, or
+#  the target changes the phase (in light of this, it makes sense that
+#  the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
+#  doing a SCSI->Host transfer, flush the data FIFO.
+#
+dma3:
+	test	SINDEX,0x4	jnz dma5	# DIRECTION
+	and	SINDEX,0xfe			# mask out FIFORESET
+	or	DFCNTRL,0x2,SINDEX		# FIFOFLUSH
+dma4:
+	test	DFCNTRL,0x2	jnz dma4	# FIFOFLUSHACK
+
+#  Now shut the DMA enables off, and copy STCNT (ie. the underrun
+#  amount, if any) to the SCB registers; SG_COUNT will get copied to
+#  the SCB's residual S/G count field after sg_advance is called.  Make
+#  sure that the DMA enables are actually off first lest we get an ILLSADDR.
+#
+dma5:
+	clr	DFCNTRL				# disable DMA
+dma6:
+	test	DFCNTRL,0x38	jnz dma6	# SCSIENACK|SDMAENACK|HDMAENACK
+
+	mvi	A,3
+	mvi	DINDEX,SCBARRAY+15
+	mvi	STCNT		call bcopy
+
+	ret
+
+#  Common SCSI initialization for selection and reselection.  Expects
+#  the target SCSI ID to be in the upper four bits of SINDEX, and A's
+#  contents are stomped on return.
+#
+initialize:
+	clr	SBLKCTL				# channel A, !wide
+	and	SCSIID,0xf0,SINDEX		# target ID
+	and	A,0x7,SCSICONF			# SCSI_ID_A[210]
+	or	SCSIID,A
+
+#  Esundry initialization.
+#
+	clr	DROPATN
+	clr	SIGSTATE
+
+#  Turn on Automatic PIO mode now, before we expect to see an REQ
+#  from the target.  It shouldn't hurt anything to leave it on.  Set
+#  CLRCHN here before the target has entered a data transfer mode -
+#  with synchronous SCSI, if you do it later, you blow away some
+#  data in the SCSI FIFO that the target has already sent to you.
+#
+	mvi	SXFRCTL0,0xa			# SPIOEN|CLRCHN
+
+#  Set SCSI bus parity checking and the selection timeout value,
+#  and enable the hardware selection timer.  Set the SELTO interrupt
+#  to signal the driver.
+#
+	and	A,0x38,SCSICONF			# PARITY_ENB_A|SEL_TIM_A[10]
+	or	SXFRCTL1,0x4,A			# ENSTIMER
+	mvi	SIMODE1,0x84			# ENSELTIMO|ENSCSIPERR
+	
+#  Initialize scatter-gather pointers by setting up the working copy
+#  in scratch RAM.
+#
+	call	sg_scb2ram
+
+#  Initialize SCSIRATE with the appropriate value for this target.
+#
+	call	ndx_sdtr
+	mov	SCSIRATE,SINDIR
+	ret
+
+#  Assert that if we've been reselected, then we've seen an IDENTIFY
+#  message.
+#
+assert:
+	test	RESELECT,0x80	jz assert1	# reselected?
+	test	RESELECT,0x40	jnz assert1	# seen IDENTIFY?
+
+	mvi	INTSTAT,SIGNAL_2		# no - cause a kernel panic
+
+assert1:
+	ret
+
+#  Find out if disconnection is ok from the information the BIOS has left
+#  us.  The target ID should be in the upper four bits of SINDEX; A will
+#  contain either 0x40 (disconnection ok) or 0x00 (diconnection not ok)
+#  on exit.
+#
+#  This is the only place the target ID is limited to three bits, so we
+#  can use the FUNCTION1 register.
+#
+disconnect:
+	and	FUNCTION1,0x70,SINDEX		# strip off extra just in case
+	mov	A,FUNCTION1
+	test	DISC_DSB_A,A	jz disconnect1	# bit nonzero if DISabled
+
+	clr	A		ret
+disconnect1:
+	mvi	A,0x40		ret
+
+#  Locate the SCB matching the target ID in SELID and the lun in the lower
+#  three bits of SINDEX, and switch the SCB to it.  Have the kernel print
+#  a warning message if it can't be found - this seems to happen occasionally
+#  under high loads.  Also, if not found, generate an ABORT message to the
+#  target.
+#
+findSCB:
+	and	A,0x7,SINDEX			# lun in lower three bits
+	or	A,A,SELID			# can I do this?
+	and	A,0xf7				# only channel A implemented
+
+	clr	SINDEX
+
+findSCB1:
+	mov	SCBPTR,SINDEX			# switch to new SCB
+	cmp	SCBARRAY+1,A	jne findSCB2	# target ID/channel/lun match?
+	test	SCBARRAY+0,0x4	jz findSCB2	# should be disconnected
+
+	ret
+
+findSCB2:
+	inc	SINDEX
+	cmp	SINDEX,MAXSCB	jne findSCB1
+
+	mvi	INTSTAT,SIGNAL_3		# not found - signal kernel
+	mvi	0x6		call mk_mesg	# ABORT message
+
+	or	SINDEX,0x10,SIGSTATE		# assert ATNO
+	call	scsisig
+	ret
+
+#  Make a working copy of the scatter-gather parameters in the SCB.
+#
+sg_scb2ram:
+	mov	SG_COUNT,SCBARRAY+2
+
+	mvi	A,4
+	mvi	DINDEX,SG_NEXT
+	mvi	SCBARRAY+3	call bcopy
+
+	mvi	SG_NOLOAD,0x80
+	test	SCBARRAY+0,0x10	jnz sg_scb2ram1	# don't reload s/g?
+	clr	SG_NOLOAD
+
+sg_scb2ram1:
+	ret
+
+#  Copying RAM values back to SCB, for Save Data Pointers message.
+#
+sg_ram2scb:
+	mov	SCBARRAY+2,SG_COUNT
+
+	mvi	A,4
+	mvi	DINDEX,SCBARRAY+3
+	mvi	SG_NEXT		call bcopy
+
+	and	SCBARRAY+0,0xef,SCBARRAY+0
+	test	SG_NOLOAD,0x80	jz sg_ram2scb1	# reload s/g?
+	or	SCBARRAY+0,0x10
+
+sg_ram2scb1:
+	ret
+
+#  Load a struct scatter if needed and set up the data address and
+#  length.  If the working value of the SG count is nonzero, then
+#  we need to load a new set of values.
+#
+#  This, like the above DMA, assumes a little-endian host data storage.
+#
+sg_load:
+	test	SG_COUNT,0xff	jz sg_load3	# SG being used?
+	test	SG_NOLOAD,0x80	jnz sg_load3	# don't reload s/g?
+
+	clr	HCNT+2
+	clr	HCNT+1
+	mvi	HCNT+0,SG_SIZEOF
+
+	mvi	A,4
+	mvi	DINDEX,HADDR
+	mvi	SG_NEXT		call bcopy
+
+	mvi	DFCNTRL,0xd			# HDMAEN|DIRECTION|FIFORESET
+
+#  Wait for DMA from host memory to data FIFO to complete, then disable
+#  DMA and wait for it to acknowledge that it's off.
+#
+sg_load1:
+	test	DFSTATUS,0x8	jz sg_load1	# HDONE
+
+	clr	DFCNTRL				# disable DMA
+sg_load2:
+	test	DFCNTRL,0x8	jnz sg_load2	# HDMAENACK
+
+#  Copy data from FIFO into SCB data pointer and data count.  This assumes
+#  that the struct scatterlist has this structure (this and sizeof(struct
+#  scatterlist) == 12 are asserted in aha274x.c):
+#
+#	struct scatterlist {
+#		char *address;		/* four bytes, little-endian order */
+#		...			/* four bytes, ignored */
+#		unsigned short length;	/* two bytes, little-endian order */
+#	}
+#
+	mov	SCBARRAY+19,DFDAT		# new data address
+	mov	SCBARRAY+20,DFDAT
+	mov	SCBARRAY+21,DFDAT
+	mov	SCBARRAY+22,DFDAT
+
+	mov	NONE,DFDAT			# throw away four bytes
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+	mov	NONE,DFDAT
+
+	mov	SCBARRAY+23,DFDAT
+	mov	SCBARRAY+24,DFDAT
+	clr	SCBARRAY+25
+
+sg_load3:
+	ret
+
+#  Advance the scatter-gather pointers only IF NEEDED.  If SG is enabled,
+#  and the SCSI transfer count is zero (note that this should be called
+#  right after a DMA finishes), then move the working copies of the SG
+#  pointer/length along.  If the SCSI transfer count is not zero, then
+#  presumably the target is disconnecting - do not reload the SG values
+#  next time.
+#
+sg_advance:
+	test	SG_COUNT,0xff	jz sg_advance2	# s/g enabled?
+
+	test	STCNT+0,0xff	jnz sg_advance1	# SCSI transfer count nonzero?
+	test	STCNT+1,0xff	jnz sg_advance1
+	test	STCNT+2,0xff	jnz sg_advance1
+
+	clr	SG_NOLOAD			# reload s/g next time
+	dec	SG_COUNT			# one less segment to go
+
+	clr	A				# add sizeof(struct scatter)
+	add	SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
+	adc	SG_NEXT+1,A,SG_NEXT+1
+	adc	SG_NEXT+2,A,SG_NEXT+2
+	adc	SG_NEXT+3,A,SG_NEXT+3
+
+	ret
+
+sg_advance1:
+	mvi	SG_NOLOAD,0x80			# don't reload s/g next time
+sg_advance2:
+	ret
+
+#  Add the array base SYNCNEG to the target offset (the target address
+#  is in SCSIID), and return the result in SINDEX.  The accumulator
+#  contains the 3->8 decoding of the target ID on return.
+#
+ndx_sdtr:
+	shr	A,SCSIID,4
+	and	A,0x7
+	add	SINDEX,SYNCNEG,A
+
+	and	FUNCTION1,0x70,SCSIID		# 3-bit target address decode
+	mov	A,FUNCTION1	ret
+
+#  If we need to negotiate transfer parameters, build the SDTR message
+#  starting at the address passed in SINDEX.  DINDEX is modified on return.
+#
+mk_sdtr:
+	mov	DINDEX,SINDEX			# save SINDEX
+
+	call	ndx_sdtr
+	test	NEEDSDTR,A	jnz mk_sdtr1	# do we need negotiation?
+	ret
+
+mk_sdtr1:
+	mvi	DINDIR,1			# extended message
+	mvi	DINDIR,3			# extended message length = 3
+	mvi	DINDIR,1			# SDTR code
+	mvi	DINDIR,25			# REQ/ACK transfer period
+	mvi	DINDIR,15			# REQ/ACK offset
+
+	add	MSG_LEN,-MSG_START+0,DINDEX	# update message length
+	ret
+
+#  Set SCSI bus control signal state.  This also saves the last-written
+#  value into a location where the higher-level driver can read it - if
+#  it has to send an ABORT or RESET message, then it needs to know this
+#  so it can assert ATN without upsetting SCSISIGO.  The new value is
+#  expected in SINDEX.  Change the actual state last to avoid contention
+#  from the driver.
+#
+scsisig:
+	mov	SIGSTATE,SINDEX
+	mov	SCSISIGO,SINDEX	ret
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x_seq.h linux/drivers/scsi/aha274x_seq.h
--- linux-1.1.55+new_quota/drivers/scsi/aha274x_seq.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/aha274x_seq.h	Thu Oct 20 18:15:36 1994
@@ -0,0 +1,336 @@
+#define AHA274X_SEQ_VERSION "1.26"
+	0x04, 0x03, 0x12, 0x1a,
+	0x07, 0x9c, 0x00, 0x1e,
+	0xff, 0x9b, 0x90, 0x02,
+	0x00, 0xa1, 0xe1, 0x16,
+	0xff, 0x6a, 0x54, 0x02,
+	0xff, 0x6a, 0x34, 0x02,
+	0x00, 0xa1, 0xf3, 0x16,
+	0x07, 0xa1, 0x65, 0x02,
+	0x00, 0x65, 0x65, 0x00,
+	0x80, 0x65, 0xad, 0x16,
+	0xff, 0x65, 0x64, 0x02,
+	0x00, 0x37, 0x0d, 0x18,
+	0x38, 0x6a, 0x42, 0x17,
+	0x48, 0x6a, 0x00, 0x00,
+	0x40, 0x0b, 0x16, 0x1a,
+	0x10, 0x0b, 0x0e, 0x1e,
+	0x40, 0x0b, 0x10, 0x1e,
+	0x00, 0x65, 0x16, 0x10,
+	0x10, 0x6a, 0x00, 0x00,
+	0x20, 0x0b, 0x13, 0x1e,
+	0x00, 0x19, 0xe1, 0x16,
+	0x80, 0x6a, 0x34, 0x00,
+	0xff, 0x6a, 0x00, 0x02,
+	0x08, 0x6a, 0x0c, 0x00,
+	0x08, 0x0c, 0xa6, 0x1a,
+	0x01, 0x0c, 0x18, 0x1e,
+	0xe0, 0x03, 0x64, 0x02,
+	0x00, 0x6a, 0x22, 0x1c,
+	0x40, 0x64, 0x32, 0x1c,
+	0x80, 0x64, 0x42, 0x1c,
+	0xc0, 0x64, 0x4f, 0x1c,
+	0xa0, 0x64, 0x52, 0x1c,
+	0xe0, 0x64, 0x68, 0x1c,
+	0x01, 0x6a, 0x91, 0x00,
+	0x00, 0x6a, 0x4d, 0x17,
+	0x00, 0x65, 0xef, 0x16,
+	0x00, 0x65, 0x17, 0x17,
+	0x03, 0x6a, 0x64, 0x00,
+	0x8c, 0x6a, 0x66, 0x00,
+	0xb7, 0x6a, 0xa9, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0x08, 0x6a, 0x66, 0x00,
+	0xb7, 0x6a, 0xa9, 0x16,
+	0x04, 0x6a, 0x64, 0x00,
+	0x88, 0x6a, 0x66, 0x00,
+	0xb3, 0x6a, 0xa9, 0x16,
+	0x3d, 0x6a, 0xd1, 0x16,
+	0x00, 0x65, 0x2f, 0x17,
+	0xff, 0x55, 0xb2, 0x02,
+	0x00, 0x65, 0x18, 0x10,
+	0x40, 0x6a, 0x4d, 0x17,
+	0x00, 0x65, 0xef, 0x16,
+	0x00, 0x65, 0x17, 0x17,
+	0x03, 0x6a, 0x64, 0x00,
+	0x8c, 0x6a, 0x66, 0x00,
+	0xb7, 0x6a, 0xa9, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0x08, 0x6a, 0x66, 0x00,
+	0xb7, 0x6a, 0xa9, 0x16,
+	0x04, 0x6a, 0x64, 0x00,
+	0x88, 0x6a, 0x66, 0x00,
+	0xb3, 0x6a, 0xa9, 0x16,
+	0x39, 0x6a, 0xd1, 0x16,
+	0x00, 0x65, 0x2f, 0x17,
+	0xff, 0x55, 0xb2, 0x02,
+	0x00, 0x65, 0x18, 0x10,
+	0x80, 0x6a, 0x4d, 0x17,
+	0x00, 0x65, 0xef, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0x8c, 0x6a, 0x66, 0x00,
+	0xab, 0x6a, 0xa9, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0x08, 0x6a, 0x66, 0x00,
+	0xab, 0x6a, 0xa9, 0x16,
+	0x04, 0x6a, 0x64, 0x00,
+	0x88, 0x6a, 0x66, 0x00,
+	0xa7, 0x6a, 0xa9, 0x16,
+	0x3d, 0x6a, 0xd1, 0x16,
+	0x00, 0x65, 0x18, 0x10,
+	0xc0, 0x6a, 0x4d, 0x17,
+	0xae, 0x6a, 0xb4, 0x16,
+	0x00, 0x65, 0x18, 0x10,
+	0xa0, 0x6a, 0x4d, 0x17,
+	0x08, 0x6a, 0xad, 0x16,
+	0x37, 0x6a, 0x65, 0x00,
+	0xff, 0x36, 0x66, 0x02,
+	0xff, 0x6a, 0x64, 0x02,
+	0x02, 0x0b, 0x57, 0x1e,
+	0x01, 0x66, 0x5a, 0x18,
+	0x40, 0x6a, 0x0c, 0x00,
+	0x00, 0x65, 0xce, 0x16,
+	0xff, 0x6c, 0x06, 0x02,
+	0x04, 0x0b, 0x5c, 0x1e,
+	0xff, 0x66, 0x66, 0x06,
+	0x01, 0x64, 0x64, 0x06,
+	0x00, 0x36, 0x57, 0x18,
+	0x08, 0x0c, 0x66, 0x1a,
+	0x01, 0x0c, 0x60, 0x1e,
+	0xe0, 0x03, 0x64, 0x02,
+	0xa0, 0x64, 0x66, 0x18,
+	0x10, 0x6a, 0x4d, 0x17,
+	0x00, 0x65, 0x18, 0x10,
+	0xff, 0x6a, 0x35, 0x02,
+	0x00, 0x65, 0x18, 0x10,
+	0xe0, 0x6a, 0x4d, 0x17,
+	0x64, 0x6a, 0xba, 0x16,
+	0x00, 0x6a, 0x31, 0x00,
+	0x00, 0x6a, 0x6f, 0x18,
+	0xff, 0x90, 0x9d, 0x02,
+	0x02, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0xa4, 0x10,
+	0x01, 0x64, 0x7e, 0x18,
+	0x64, 0x6a, 0xbc, 0x16,
+	0x03, 0x64, 0xa0, 0x18,
+	0x64, 0x6a, 0xbc, 0x16,
+	0x01, 0x64, 0xa0, 0x18,
+	0x4c, 0x6a, 0xbc, 0x16,
+	0x4d, 0x6a, 0xbc, 0x16,
+	0x41, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x3d, 0x17,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x4c, 0x6d, 0x02,
+	0xff, 0x64, 0x64, 0x04,
+	0x00, 0x4f, 0x4f, 0x02,
+	0xff, 0x4c, 0x04, 0x02,
+	0x00, 0x65, 0xa4, 0x10,
+	0x04, 0x64, 0x81, 0x18,
+	0x04, 0xa0, 0xa0, 0x00,
+	0x00, 0x65, 0xa4, 0x10,
+	0x02, 0x64, 0x8a, 0x18,
+	0x04, 0x6a, 0x64, 0x00,
+	0xb3, 0x6a, 0x66, 0x00,
+	0x50, 0x6a, 0xa9, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0xb7, 0x6a, 0x66, 0x00,
+	0xaf, 0x6a, 0xa9, 0x16,
+	0x00, 0x65, 0x0f, 0x17,
+	0x00, 0x65, 0xa4, 0x10,
+	0x03, 0x64, 0x8d, 0x18,
+	0x00, 0x65, 0x07, 0x17,
+	0x00, 0x65, 0xa4, 0x10,
+	0x80, 0x64, 0x95, 0x1e,
+	0x78, 0x64, 0xa0, 0x1a,
+	0x00, 0x64, 0xf8, 0x16,
+	0x80, 0x35, 0xa4, 0x1a,
+	0x04, 0xa0, 0xa0, 0x04,
+	0xc0, 0x6a, 0x34, 0x00,
+	0x00, 0x65, 0x07, 0x17,
+	0x00, 0x65, 0xa4, 0x10,
+	0x07, 0x64, 0xa0, 0x18,
+	0x70, 0x05, 0x6e, 0x02,
+	0xff, 0x6e, 0x64, 0x02,
+	0x00, 0x4f, 0xa4, 0x1e,
+	0x00, 0x65, 0x3d, 0x17,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x6a, 0x6d, 0x02,
+	0xff, 0x64, 0x64, 0x04,
+	0x00, 0x4f, 0x4f, 0x02,
+	0xff, 0x6a, 0x04, 0x02,
+	0x00, 0x65, 0xa4, 0x10,
+	0x10, 0x4e, 0x65, 0x00,
+	0x00, 0x65, 0x4d, 0x17,
+	0x11, 0x6a, 0x91, 0x00,
+	0x07, 0x6a, 0xad, 0x16,
+	0x00, 0x65, 0xc2, 0x16,
+	0x00, 0x65, 0x18, 0x10,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x6a, 0x4e, 0x02,
+	0x00, 0x65, 0x00, 0x10,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x64, 0x64, 0x06,
+	0x00, 0x6a, 0xa9, 0x18,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x40, 0x6a, 0x60, 0x00,
+	0x80, 0x35, 0xb2, 0x1a,
+	0x80, 0x6a, 0x35, 0x00,
+	0x01, 0x6a, 0x36, 0x00,
+	0xff, 0x65, 0x37, 0x02,
+	0xff, 0x6a, 0x60, 0x02,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x02, 0x0b, 0xb4, 0x1e,
+	0xff, 0x65, 0x66, 0x02,
+	0x00, 0x65, 0xce, 0x16,
+	0xff, 0x06, 0x6d, 0x02,
+	0x04, 0x0b, 0xb8, 0x1e,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x12, 0x6d, 0x03,
+	0xff, 0x65, 0x66, 0x02,
+	0x00, 0x65, 0xce, 0x16,
+	0xff, 0x06, 0x6a, 0x02,
+	0x04, 0x0b, 0xbf, 0x1e,
+	0x02, 0x0b, 0xc0, 0x1e,
+	0xff, 0x12, 0x6d, 0x03,
+	0x00, 0x65, 0xce, 0x16,
+	0xff, 0x06, 0x6a, 0x02,
+	0x04, 0x0b, 0xc4, 0x1e,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x02, 0x0b, 0xc6, 0x1e,
+	0x00, 0x65, 0xce, 0x16,
+	0x80, 0x30, 0xcb, 0x1e,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x6a, 0x30, 0x02,
+	0xff, 0x65, 0x06, 0x02,
+	0x04, 0x0b, 0xcc, 0x1e,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x6a, 0x0a, 0x02,
+	0xff, 0x6a, 0x09, 0x02,
+	0x01, 0x6a, 0x08, 0x01,
+	0xff, 0x65, 0x93, 0x02,
+	0x01, 0x0b, 0xd4, 0x1a,
+	0x10, 0x0c, 0xd2, 0x1e,
+	0x04, 0x65, 0xd8, 0x1a,
+	0xfe, 0x65, 0x65, 0x02,
+	0x02, 0x65, 0x93, 0x00,
+	0x02, 0x93, 0xd7, 0x1a,
+	0xff, 0x6a, 0x93, 0x02,
+	0x38, 0x93, 0xd9, 0x1a,
+	0x04, 0x6a, 0x64, 0x00,
+	0x50, 0x6a, 0x66, 0x00,
+	0x14, 0x6a, 0xa9, 0x16,
+	0x03, 0x6a, 0x64, 0x00,
+	0xaf, 0x6a, 0x66, 0x00,
+	0x08, 0x6a, 0xa9, 0x16,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x6a, 0x1f, 0x02,
+	0xf0, 0x65, 0x05, 0x02,
+	0x07, 0x5a, 0x64, 0x02,
+	0x00, 0x05, 0x05, 0x00,
+	0xff, 0x6a, 0x30, 0x02,
+	0xff, 0x6a, 0x4e, 0x02,
+	0x0a, 0x6a, 0x01, 0x00,
+	0x38, 0x5a, 0x64, 0x02,
+	0x04, 0x64, 0x02, 0x00,
+	0x84, 0x6a, 0x11, 0x00,
+	0x00, 0x65, 0x07, 0x17,
+	0x00, 0x65, 0x3d, 0x17,
+	0xff, 0x6c, 0x04, 0x02,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x80, 0x34, 0xf2, 0x1e,
+	0x40, 0x34, 0xf2, 0x1a,
+	0x21, 0x6a, 0x91, 0x00,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x70, 0x65, 0x6e, 0x02,
+	0xff, 0x6e, 0x64, 0x02,
+	0x00, 0x32, 0xf7, 0x1e,
+	0xff, 0x6a, 0x64, 0x03,
+	0x40, 0x6a, 0x64, 0x01,
+	0x07, 0x65, 0x64, 0x02,
+	0x00, 0x19, 0x64, 0x00,
+	0xf7, 0x64, 0x64, 0x02,
+	0xff, 0x6a, 0x65, 0x02,
+	0xff, 0x65, 0x90, 0x02,
+	0x00, 0xa1, 0x00, 0x19,
+	0x04, 0xa0, 0x00, 0x1f,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x01, 0x65, 0x65, 0x06,
+	0x04, 0x65, 0xfc, 0x18,
+	0x31, 0x6a, 0x91, 0x00,
+	0x06, 0x6a, 0xad, 0x16,
+	0x10, 0x4e, 0x65, 0x00,
+	0x00, 0x65, 0x4d, 0x17,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0xa2, 0x55, 0x02,
+	0x04, 0x6a, 0x64, 0x00,
+	0x56, 0x6a, 0x66, 0x00,
+	0xa3, 0x6a, 0xa9, 0x16,
+	0x80, 0x6a, 0x54, 0x00,
+	0x10, 0xa0, 0x0e, 0x1b,
+	0xff, 0x6a, 0x54, 0x02,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x55, 0xa2, 0x02,
+	0x04, 0x6a, 0x64, 0x00,
+	0xa3, 0x6a, 0x66, 0x00,
+	0x56, 0x6a, 0xa9, 0x16,
+	0xef, 0xa0, 0xa0, 0x02,
+	0x80, 0x54, 0x16, 0x1f,
+	0x10, 0xa0, 0xa0, 0x00,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x55, 0x2e, 0x1f,
+	0x80, 0x54, 0x2e, 0x1b,
+	0xff, 0x6a, 0x8e, 0x02,
+	0xff, 0x6a, 0x8d, 0x02,
+	0x0c, 0x6a, 0x8c, 0x00,
+	0x04, 0x6a, 0x64, 0x00,
+	0x88, 0x6a, 0x66, 0x00,
+	0x56, 0x6a, 0xa9, 0x16,
+	0x0d, 0x6a, 0x93, 0x00,
+	0x08, 0x94, 0x20, 0x1f,
+	0xff, 0x6a, 0x93, 0x02,
+	0x08, 0x93, 0x22, 0x1b,
+	0xff, 0x99, 0xb3, 0x02,
+	0xff, 0x99, 0xb4, 0x02,
+	0xff, 0x99, 0xb5, 0x02,
+	0xff, 0x99, 0xb6, 0x02,
+	0xff, 0x99, 0x6a, 0x02,
+	0xff, 0x99, 0x6a, 0x02,
+	0xff, 0x99, 0x6a, 0x02,
+	0xff, 0x99, 0x6a, 0x02,
+	0xff, 0x99, 0xb7, 0x02,
+	0xff, 0x99, 0xb8, 0x02,
+	0xff, 0x6a, 0xb9, 0x02,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x55, 0x3c, 0x1f,
+	0xff, 0x08, 0x3b, 0x1b,
+	0xff, 0x09, 0x3b, 0x1b,
+	0xff, 0x0a, 0x3b, 0x1b,
+	0xff, 0x6a, 0x54, 0x02,
+	0xff, 0x55, 0x55, 0x06,
+	0xff, 0x6a, 0x64, 0x02,
+	0x0c, 0x56, 0x56, 0x06,
+	0x00, 0x57, 0x57, 0x08,
+	0x00, 0x58, 0x58, 0x08,
+	0x00, 0x59, 0x59, 0x08,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x80, 0x6a, 0x54, 0x00,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x4c, 0x05, 0x64, 0x0a,
+	0x07, 0x64, 0x64, 0x02,
+	0x20, 0x64, 0x65, 0x06,
+	0x70, 0x05, 0x6e, 0x02,
+	0xff, 0x6e, 0x64, 0x03,
+	0xff, 0x65, 0x66, 0x02,
+	0x00, 0x65, 0x3d, 0x17,
+	0x00, 0x4f, 0x46, 0x1b,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x01, 0x6a, 0x6d, 0x00,
+	0x03, 0x6a, 0x6d, 0x00,
+	0x01, 0x6a, 0x6d, 0x00,
+	0x19, 0x6a, 0x6d, 0x00,
+	0x0f, 0x6a, 0x6d, 0x00,
+	0xc9, 0x66, 0x36, 0x06,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x65, 0x4e, 0x02,
+	0xff, 0x65, 0x03, 0x03,
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- linux-1.1.55+new_quota/drivers/scsi/eata.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/eata.c	Thu Oct 20 18:15:36 1994
@@ -0,0 +1,621 @@
+/************************************************************
+ *                                                          *
+ *                  Linux EATA SCSI driver                  *
+ *                                                          *
+ *  based on the EATA proposal rev 2.0b, DPT DOS driver kit *
+ *  some proprietary source (DPT Unix driver), several      *
+ *  other linux scsi drivers and kernel documentation       *
+ *                                                          *
+ *  The driver currently:                                   *
+ *      -has ISA detection routines                         *
+ *      -has EISA detection routines (new)                  *
+ *      -doesn't support more than one HA and one channel   *
+ *      -gets sometimes timeouts when accessing more than   *
+ *       one device "simultaniously"                        *
+ *                                                          *
+ *  ECS_emulation_sense() doesn't work and I don't know why * 
+ *  Until this is working, the drive geometry has to be     * 
+ *  hardcoded into the driver :-( (Any ideas ?)             *
+ *                                                          *
+ *  (c)1993,94 Michael Neuffer                              *
+ *             Michael_Neuffer@wi2.maus.de (mails <16KB!)   *
+ *             neuffer@goofy.zdv.uni-mainz.de               *
+ *                                                          *
+ *  This program is free software; you can redistribute it  * 
+ *  and/or modify it under the terms of the GNU General     *
+ *  Public License as published by the Free Software        *
+ *  Foundation; either version 2 of the License, or         *
+ *  (at your option) any later version.                     *
+ *                                                          *
+ *  This program is distributed in the hope that it will be *
+ *  useful, but WITHOUT ANY WARRANTY; without even the      *
+ *  implied warranty of MERCHANTABILITY or FITNESS FOR A    *
+ *  PARTICULAR PURPOSE.  See the GNU General Public License *
+ *  for more details.                                       *
+ *                                                          *
+ *  You should have received a copy of the GNU General      * 
+ *  Public License along with this kernel; if not, write to *
+ *  the Free Software Foundation, Inc., 675 Mass Ave,       *
+ *  Cambridge, MA 02139, USA.                               *
+ *                                                          *
+ *                                                          *
+ * I have to thank DPT for their excellent support. I took  *
+ * me almost a year and a stopover at their HQ on my first  *
+ * trip to the USA to get it, but now they are very helpful *
+ * and try to give me all the infos and support I need....  *
+ *                                                          *
+ ************************************************************
+ *  last change: 02.10.94                                   *
+ ************************************************************/
+
+/* Look in eata.h for configuration information */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include "eata.h"
+#include "scsi.h"
+#include "sd.h"
+
+#define MAXISA  4
+#define MAXEISA 16  
+
+static unsigned int ISAbases[]={0x1F0,0x170,0x330,0x230};
+static unsigned int EISAbases[]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+static struct eata_register *EATA_base;
+
+static unsigned char dma_channel;
+static struct geom_emul geometry;       /* Drive 1 & 2 geometry              */
+
+static int eata_int_happened, eata_status, expecting_ints;
+
+struct eata_cmd_queue outstanding[64];  /* We should allocate this dynamicly */
+
+int cont_errs[]=
+{
+  DID_OK,         /* No error                    */
+  DID_NO_CONNECT, /* Selection Timeout           */
+  DID_TIME_OUT,   /* Command Timeout             */
+  DID_RESET,      /* SCSI Bus Reset Received     */
+  DID_ERROR,      /* Initial Controller Power up */
+  DID_ERROR,      /* Unexpected Bus Phase        */
+  DID_ERROR,      /* Unexpected Bus Free         */
+  DID_PARITY,     /* Bus Parity Error            */
+  DID_ERROR,      /* SCSI Hung                   */
+  DID_ERROR,      /* Unexpected Message Rejected */
+  DID_ERROR,      /* SCSI Bus Reset STUCK        */            
+  DID_ERROR,      /* Auto Request-Sense Failed   */
+  DID_PARITY,     /* Controller Ram Parity Error */
+};
+
+inline void end_of_command(void)
+{
+  Scsi_Cmnd *cmd;
+  struct eata_ccb *cp;
+  struct eata_sp  *sp;
+  int i, result;
+        /* Find out which CP caused the interrupt */
+  for(i=0; i<64; i++)
+    {
+      if(outstanding[i].used && outstanding[i].sp.cont_stat & 0x80)
+	break;
+    }
+  if(i==64)
+    {
+      printk("Got interrupt but no finished packet found!\n");
+      return;
+    }
+  DBG(DBG_AHMON,outb(i, 0x80));                   /* Ahmon's special */     
+  cmd=outstanding[i].cmd;
+  cp=&outstanding[i].cp;
+  sp=&outstanding[i].sp;
+  outstanding[i].used=0;
+  if(cmd->use_sg) scsi_free(cmd->host_scribble, 512);
+  result=sp->scsi_stat;                             
+  
+  DBG(DBG_INTR,printk("end_of_command: queuenr: %d message bytes: %x,%x,%x\n",
+		      i,sp->msg[0],sp->msg[1],sp->msg[2]));
+ 
+  result|=COMMAND_COMPLETE<<8; 
+  result|=cont_errs[sp->cont_stat&0x7f] << 16; /* Fixed the error array */
+  cmd->result=result;
+  
+  cmd->scsi_done(cmd);
+}
+
+void eata_int_handler(int irq)
+{
+  eata_status=inb(((int)EATA_base)+HA_RSTATUS); /* Acknowledge interrupt */
+  DBG(DBG_INTR, printk(" Interrupt %d received, expected: %d,Status: %x\n",irq,
+		       expecting_ints,eata_status));
+  if(expecting_ints==EXP_NOTHING)
+    {
+      printk("eata_int_handler: Unexpected interrupt!\n");
+      return;
+    }
+  if(expecting_ints==EXP_NORMAL)
+    {
+      eata_int_happened=1;
+      return;
+    }
+  end_of_command();
+}
+
+inline void eata_set_dma_address(int add)
+{
+  outb(add & 0x000000ff, ((int)EATA_base)+HA_WDMAADDR);
+  outb((add & 0x0000ff00) >> 8, ((int)EATA_base)+HA_WDMAADDR+1);
+  outb((add & 0x00ff0000) >> 16, ((int)EATA_base)+HA_WDMAADDR+2);
+  outb((add & 0xff000000) >> 24, ((int)EATA_base)+HA_WDMAADDR+3);
+}
+
+const char *eata_info(void)
+{
+  static char *information="EATA SCSI Controller Driver\n";
+  return information;
+}
+
+inline int eata_reverse(int arg)
+{
+  int result;
+  result=(arg & 0xff000000) >> 24;
+  result|=((arg & 0x00ff0000) >> 8);
+  result|=((arg & 0x0000ff00) << 8);
+  result|=((arg & 0x000000ff) << 24);
+  return result;
+}
+
+int eata_queue(Scsi_Cmnd *cmd, void *(done)(struct scsi_cmnd *))
+{
+  int i=64,status;
+  struct eata_sp  *sp;
+  struct eata_ccb *cp;
+  struct eata_sg_list *sglist;
+  struct scatterlist *sl;
+
+        /* Search for an open command slot.  If i==64, then
+	   there must be none free.  If this is the case, keep
+	   trying because an interrupt will eventually happen
+	   that will free up a slot. 
+	
+	   This is assuming that interrupts are enabled when this
+	   routine is called.  Is that true? 
+        */
+
+  i=64;
+  while(i==64) for(i=0; i<64 && outstanding[i].used; i++);
+  cli();
+  outstanding[i].used=1; /* Claim it    */
+  cmd->scsi_done=done;
+  sp=&outstanding[i].sp;
+  cp=&outstanding[i].cp;
+  sp->cont_stat=0; /* Clear this out because the interrupt handler
+		      checks it. Note that I clear it BEFORE I set the
+		      pointer in outstanding[] */
+  outstanding[i].cmd=cmd;
+  sti();
+  if(cmd->cmnd[0]==WRITE_6 || cmd->cmnd[0]==WRITE_10)
+    cp->cp_option.Byte=HA_DATA_OUT;                       /* Output mode */
+  else
+    cp->cp_option.Byte=HA_DATA_IN;                        /* Input mode  */
+  if(cmd->use_sg) {
+    cp->cp_option.Byte|=HA_SC_GA;                         /* SG mode     */
+    sglist=(struct eata_sg_list *)scsi_malloc(512);       /* Max needed 64 */
+    if(!sglist) 
+      {
+	panic("eata_queue: scsi_malloc failed. No memory for SG list\n");
+      }
+    cmd->host_scribble=(unsigned char *)sglist;
+    sl=(struct scatterlist *)cmd->request_buffer;
+    for(i=0; i<cmd->use_sg; i++, sglist++, sl++)
+      {
+	sglist->data=(void *)eata_reverse((int )sl->address);
+	sglist->len=eata_reverse((int )sl->length);
+      }
+    sglist=(struct eata_sg_list *)cmd->host_scribble;
+  }
+  cp->reqlen=38;
+  cp->cp_option2.Byte=0x00;
+  cp->cp_id=cmd->target;
+  cp->cp_msg0=cmd->lun+HA_IDENTIFY_MSG+HA_G_DISCO_RECO; /*Those bits should always be set*/
+  cp->cp_msg1=cp->cp_msg2=cp->cp_msg3=0;
+  memset(cp->cp_cdb, 0, 12);
+  memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
+  if(cmd->use_sg)
+    {
+      *((int *)cp->cp_dataDMA)=eata_reverse((int )sglist);
+      *((int *)cp->cp_datalen)=eata_reverse(cmd->use_sg*8);
+    }
+  else
+    {
+      *((int *)cp->cp_datalen)=eata_reverse(cmd->request_bufflen);
+      *((int *)cp->cp_dataDMA)=eata_reverse((int )cmd->request_buffer);
+    }
+  *((int *)cp->cp_statDMA)=eata_reverse((int )sp);
+  *((int *)cp->cp_viraddr)=i;      
+  DBG(DBG_QUEUE,printk("EATA_QUEUE pos: %d, com: %x, SG: %d\n",i,cmd->cmnd[0],
+		       cmd->use_sg));
+  status=inb(((int)EATA_base)+HA_RSTATUS);
+  while(status & HA_SBSY) status=inb(((int)EATA_base)+HA_RSTATUS);
+  expecting_ints=EXP_RETURN;
+  eata_set_dma_address((int )cp);
+  outb(EATA_CMD_DMA_SEND_CP, ((int)EATA_base)+HA_WCOMMAND); /* Let 'er rip */
+  return(0);
+}
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+    internal_done_errcode = SCpnt->result;
+    ++internal_done_flag;
+}
+
+int eata_command(Scsi_Cmnd *SCpnt)
+{
+
+  DBG(DBG_FUNC,printk("eata_command: calling eata_queue\n"));
+
+  eata_queue(SCpnt, internal_done);
+  
+  while (!internal_done_flag);
+  internal_done_flag = 0;
+  return internal_done_errcode;
+}
+
+int eata_abort(Scsi_Cmnd *cmd)
+{
+  int i;
+
+  /* Just act like we did something */
+  DBG(DBG_ABNORM,printk("eata_abort()\n"));
+  DELAY(200);
+  /*
+  eata_reset(cmd);
+  */
+  
+  for(i=0; i<64; i++) 
+    if((outstanding[i].cmd==cmd)&& outstanding[i].used)
+      return(SCSI_ABORT_BUSY);
+  return(SCSI_ABORT_NOT_RUNNING); /*SNOOZE*/
+}
+
+int eata_reset(Scsi_Cmnd *cmd)
+{
+  int i;
+  long time;
+  DBG(DBG_ABNORM,printk("eata_reset()\n"));
+  DELAY(200);
+  outb(EATA_CMD_RESET, ((int)EATA_base)+HA_WCOMMAND);
+/*  if(cmd)
+    cmd->flags|=NEEDS_JUMPSTART;                                         
+*/  
+  time=jiffies+75;
+  while(time>jiffies);
+  for(i=0; i<64; i++) outstanding[i].used=0;
+  return(SCSI_RESET_WAKEUP);
+}
+
+int eata_biosparam(Scsi_Disk * disk, int dev, int geo[])
+{
+  int id;
+  int size = disk->capacity;
+  
+  id=MINOR(dev);
+  if (id) id>>=4;
+
+  DBG(DBG_PROBE,printk("\nSize: %d, Device: %x, id: %x\n\n",size,dev,id));
+  
+  if(geometry.drv[0].id==id){
+    geo[0]=geometry.drv[0].heads;
+    geo[1]=geometry.drv[0].sectors;
+    geo[2]=geometry.drv[0].cylinder;
+  }else if(geometry.drv[1].id==id){
+    geo[0]=geometry.drv[1].heads;
+    geo[1]=geometry.drv[1].sectors;
+    geo[2]=geometry.drv[1].cylinder;
+  }else{
+    if(size<0x200000){
+      geo[0]=64;
+      geo[1]=32;
+    }else if(size<0x400000){
+      geo[0]=65;
+      geo[1]=63;
+    }else if(size<0x800000){
+      geo[0]=128;
+      geo[1]=63;
+    }else{
+      geo[0]=255;
+      geo[1]=63;
+    }    
+    geo[2]=size/(geo[0]*geo[1]);
+  }
+  return(0);
+}
+
+int get_conf_DMA(struct eata_register *base,struct get_conf *buf)
+{
+   long i;
+  
+  inb(((int)base)+HA_RSTATUS);               /* Clear any pending conditions */
+ 
+  i=jiffies;
+  expecting_ints=EXP_NORMAL;
+  eata_int_happened=0;
+
+  outb((unchar)((long)(buf)>>24),(int)base+HA_WDMAADDR+3);/*get config of    */
+  outb((unchar)((long)(buf)>>16),(int)base+HA_WDMAADDR+2);/*poss. controllers*/
+  outb((unchar)((long)(buf)>>8), (int)base+HA_WDMAADDR+1);
+  outb((unchar)((long)(buf)),    (int)base+HA_WDMAADDR); 
+  outb(EATA_CMD_DMA_READ_CONFIG, (int)base+HA_WCOMMAND);  /* set opcode      */
+
+  while(!eata_int_happened && jiffies<i+200);             /* wait for 2 sec. */
+  expecting_ints=EXP_NOTHING;
+
+  if(!eata_int_happened){
+    printk("eata.c get_conf_DMA: Controller timeout\n");
+    return (0);
+  }
+  
+  DBG(DBG_PROBE,printk("\nSignature: %c %c %c %c \n",(char)buf->gco_sig[0],
+           (char)buf->gco_sig[1],(char)buf->gco_sig[2],(char)buf->gco_sig[3]));
+  if((buf->gco_sig[0]=='E')&&(buf->gco_sig[1]== 'A')
+       &&(buf->gco_sig[2]== 'T')&&(buf->gco_sig[3]== 'A')) { 
+    DBG(DBG_PROBE,printk("EATA Controller found at %x EATA Level: %x\n",
+		    (unsigned int) base,(unsigned int)(buf->gco_version>>4)));
+    return(1);
+  }
+  return(0);
+}
+
+long find_EISA(int flag, struct get_conf *buf)
+{
+/* 
+   Is there anyone using more than 2 controllers
+   at the same time ? ie. more than one secondary ?
+   I don't think so.
+*/
+  struct eata_register *base;
+  int i;
+  unsigned char pal1,pal2,pal3,*p;
+
+  for(i=0; i<MAXEISA;i++){
+    if(EISAbases[i]){                      /* Still a possibility ?          */
+      base=(void *)0x1c88+(i*0x1000);
+      p=(char*)base;
+      pal1=*(p-8);
+      pal2=*(p-7);
+      pal3=*(p-6);
+      if(((pal1==0x12)&&(pal2==0x14))||    /* Check for id tags              */
+	 ((pal1==0x38)&&(pal2==0xa3)&&(pal3==0x82))||
+	 ((pal1==0x5d)&&(pal2==0xc3)&&(pal3==0x90))||        /* SCIII PM2022 */
+	 ((pal1==0x06)&&(pal2==0x94)&&(pal3==0x24)))
+	if(get_conf_DMA(base,buf)){
+	  if((!buf->bit2.SECOND)&&(buf->bit2.IRQ)&&(flag==-1)){
+	                                   /* We just found a primary EISA   */
+	    ISAbases[0]=0;                 /* so there is no prim. ISA cont. */
+	    EISAbases[i]=0;                /* and we don't need to look for  */
+	    return((long)base);            /* a sec. EISA controller here    */
+	  }
+	  else if((buf->bit2.SECOND)&&(buf->bit2.IRQ)&&(!flag)){ 
+	                                   /* We've found a EISA secondary   */
+	    ISAbases[1]=0;                 /* so there is no sec. ISA cont.  */
+	    EISAbases[i]=0;
+	    return((long)base);
+	  }
+	  else EISAbases[i]=0;
+	}
+    }
+  }
+  return(0l);                              /* Nothing found  :-(             */
+}
+
+long find_ISA(struct eata_register *base, struct get_conf *buf)
+{
+  int i,l;
+  long ret;
+
+  ret=0;
+  if(!base){
+    for (l=1;l<=MAXISA;l++){
+      if(ISAbases[l]){
+	i=get_conf_DMA(base,buf);
+	if (!i) ISAbases[l]=0;
+	else{
+	  ret=(int)base;
+	  break;
+	}
+      }
+    }
+  }else{
+    i=get_conf_DMA(base, buf);
+    if((i==1)&&((((long)base==0x1F0l)&&!buf->bit2.SECOND)
+             ||(((long)base!=0x1F0l)&&buf->bit2.SECOND))) 
+      ret=(long)base;
+    else ret=0;
+  }
+  return(ret);
+}
+
+int ECS_emulation_sense(long base,int drive)
+{
+ /*
+     I'm not sure what's going wrong here, because now I have 
+     documentation of the ECS command set but still I don't 
+     get it to work, I don't get a reponse from the 
+     controller (Ready + Seek complete...) :-(
+ */
+  int x,ret;
+  struct emul_sense *p;
+  char buff[256];
+ /* long time;*/
+
+  ret=0;
+
+  while(inb_p(base+HA_RSTATUS)&HA_SBSY);      /* Waiting to get not busy */
+
+  expecting_ints=EXP_NORMAL;
+  eata_int_happened=0;
+
+  x=drive<<5;
+  outb(base+1,0);
+  outb(base+2,0);
+  outb(base+3,255);
+  outb(base+4,0);
+  outb(base+5,0);
+  outb(base+HA_WCOMMAND-1,x);
+  outb(base+HA_WCOMMAND,ECS_EMULATE_SENSE);
+
+  DBG(DBG_WINTR,printk("ECS: Waiting for interrupt\n"));
+
+/* while(!eata_int_happened); */                /* wait for interrupt  */
+
+  expecting_ints=EXP_NOTHING;
+  while(!((x=inb_p(base+HA_RSTATUS))&HA_SDRQ)){
+    DBG(DBG_WINTR,printk("Controller status %x\n",x));
+    DELAY(50);
+  }
+  insw(base+HA_RDATA,buff,256);             /* Get the Data and    */
+  while(inb(base+HA_RSTATUS)&HA_SDRQ)       /* throw away the rest */
+    inb(base+HA_RDATA);
+  p=(struct emul_sense *)buff;
+  geometry.drv[drive].heads=p->Heads;
+  geometry.drv[drive].sectors=p->Sectors;
+  geometry.drv[drive].cylinder=(p->Cyls[0]<<8)+p->Cyls[1];
+  if(p->lunmap[drive]&0x80)
+    geometry.drv[drive].trans=0;
+  if(drive==0){
+    if(p->lunmap[2]&0x80) ret=0;
+    else ret=1;
+    geometry.drv[drive].id=p->lunmap[0];
+    geometry.drv[drive].lun=p->lunmap[1];
+  } else if(drive==1){
+    ret=1;
+    geometry.drv[drive].id=p->lunmap[2];
+    geometry.drv[drive].lun=p->lunmap[3];
+  }
+  return(ret);
+}
+
+void get_geo_trans(long base)
+{ 
+  outb(0x70,0x12);
+  if(inb(0x71)){
+    geometry.drv[0].trans=1;
+    if(ECS_emulation_sense(base,0)){
+      outb(0x70,0x12);
+      if(inb(0x71)&0xF){
+	geometry.drv[1].trans=1;
+	ECS_emulation_sense(base,1);
+      }
+    }
+  }
+}
+
+/* int eata_detect(int index) */
+int eata_detect(Scsi_Host_Template * tpnt)
+{
+  struct get_conf gc;
+  int i;
+  int err;
+  long base;
+  long time;
+  unsigned int irqlist[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+  geometry.drv[0].trans=geometry.drv[1].trans=0; 
+  base=0;
+  err=0;
+
+  
+  for(i=0;i<16;i++)                        /* Here we grab every available IRQ    */
+    if(!request_irq(i,eata_int_handler,SA_INTERRUPT,"EATA"))/* until we know which one we  */
+      irqlist[i]=1;                        /* will need                   */
+
+
+  base=find_EISA(-1,&gc);                  /* Check for primary EISA HA */
+  if(base) ISAbases[0]=0; /* If found, there can't be an ISA prim. here */
+  else base=find_ISA((void *)0x1F0,&gc);   /* Check for primary ISA HA  */
+  if (!base) {                             /* no primary found          */
+    base=find_EISA(0,&gc);                 /* Check for secondary EISA  */
+    if (!base) base=find_ISA((void *)0,&gc); 
+    if (!base) {                           /* No controller fould :-(   */
+      for(i=0;i<16;i++)                    /* release all interrupts    */ 
+	if(irqlist[i]){
+	  free_irq(i);
+	  irqlist[i]=0;
+	}
+      return(0);
+    }
+  }
+/*  else get_geo_trans(base); */           /* set emulation parameters  */
+
+  else if(IDE_EMULATION){                  /* Unfortionally,we have to  */
+    geometry.drv[0].heads=HEADS0;          /* hardcode the drive geometry */
+    geometry.drv[0].sectors=SECTORS0;      /* into the driver for now   */
+    geometry.drv[0].cylinder=CYLINDER0;
+    geometry.drv[0].id=ID0;
+    geometry.drv[1].heads=HEADS1;
+    geometry.drv[1].sectors=SECTORS1;
+    geometry.drv[1].cylinder=CYLINDER1;
+    geometry.drv[1].id=ID1;
+  } else {
+    geometry.drv[0].id=-1;
+    geometry.drv[1].id=-1;
+  }
+
+  dma_channel=(8-gc.bit2.DRQX)&7;          /* Which DMA channel ?      */
+  if (!gc.bit1.DRQ_valid) {
+    DBG(DBG_PROBE,printk("EISA EATA controller found.\n"));
+    err=0;
+  }else if (request_dma(dma_channel,"DPT_EATA")) {
+    printk("Unable to allocate DMA channel %d for EATA controller.\n",
+	   dma_channel);
+    err=1;
+  }
+
+  for(i=0;i<16;i++)                         /* Now we can release all  */
+    if(irqlist[i] && (i!=gc.bit2.IRQ)){     /* unused interrupts again */
+      free_irq(i);
+      irqlist[i]=0;
+    }
+  if (err) {
+    free_irq((unsigned int)gc.bit2.IRQ);
+    return(0);
+  }
+  if(!irqlist[(unsigned int) gc.bit2.IRQ]){
+    printk("eata_detect: Couldn't alloc. IRQ%d!\n",(unsigned int)gc.bit2.IRQ);
+    return(0);
+  }
+
+  tpnt->can_queue=((int)(gc.gco_queuesiz[0])<<8) + ((int)(gc.gco_queuesiz[1]));
+  DBG(DBG_PROBE, printk("Can queue %d commands \n",tpnt->can_queue));
+  if(tpnt->can_queue>64)                 /* This is only temporarily in here */
+    tpnt->can_queue=64;                   /* We'll allocate the needed space */
+
+  tpnt->this_id=(int)(xscsi2int(gc.gco_HAaddress));
+  tpnt->sg_tablesize=((short unsigned int)(gc.gco_SGsiz[0])<<8)
+                                      +((short unsigned int)(gc.gco_SGsiz[1]));
+  tpnt->cmd_per_lun=1;                    
+                               
+  if (!gc.bit1.DRQ_valid) tpnt->unchecked_isa_dma=0; /* This is an EISA contr.*/
+  else tpnt->unchecked_isa_dma=1;                    /* Here we have only ISA */
+
+  snarf_region(base, 9);
+  scsi_register(tpnt,0);
+  printk("EATA - DMA driver version: %d.%d%s\n",VER_MAJOR,VER_MINOR,VER_SUB);
+  printk("EATA compliant controller detected. EATA Level %x, SCSI ID is %d \n",
+	        (unsigned int)(gc.gco_version>>4),tpnt->this_id);
+  printk("Using IRQ %d, DMA channel %d, at %lx\n",(unsigned int)gc.bit2.IRQ,
+	        dma_channel,base); 
+  time=jiffies;
+  while(jiffies<time+300);                               /* Let 'em read 8-) */
+  for(i=0; i<64; i++) outstanding[i].used=0;             /* Do some setup    */
+  EATA_base=(void *)base;
+  return(1);
+
+}  
+  
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- linux-1.1.55+new_quota/drivers/scsi/eata.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/eata.h	Thu Oct 20 18:15:37 1994
@@ -0,0 +1,349 @@
+/***************************************************
+ *  Header file for eata.c Linux EATA SCSI driver  *
+ *  currently contains lots of unused stuff        *
+ *  (c) 1993,94 Michael Neuffer                    *
+ ***************************************************
+ * last change: 20.09.94                           *
+ ***************************************************/
+
+
+#ifndef _EATA_H
+#define _EATA_H
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+
+
+#define VER_MAJOR 0
+#define VER_MINOR 3
+#define VER_SUB   "a"
+
+/************************************************************************
+ * Here you can configure your drives that are running in IDE           *
+ * emulation mode                                                       *
+ * If all your drives are running in native mode (not emulated), set    *
+ * IDE_EMULATION to 0                                                   * 
+ * If you have only one drive (running in IDE emu. mode), set ID1 to -1 *
+ ************************************************************************/
+#define IDE_EMULATION 1          /* Here are drives running in emu. mode   */
+
+#define ID0           0          /* SCSI ID of "IDE" drive mapped to C:    */
+                                 /* If you're not sure check your config
+				  * utility that came with your controller
+				  */
+#define HEADS0       13          /* Number of emulated heads of this drive */  
+#define SECTORS0     38          /* Number of emulated sectors             */ 
+#define CYLINDER0   719          /* Number of emulated cylinders           */
+   
+#define ID1           1          /* SCSI ID of "IDE" drive mapped to D:    */
+#define HEADS1       16          /* Number of emulated heads of this drive */ 
+#define SECTORS1     62          /* Number of emulated sectors             */
+#define CYLINDER1  1024          /* Number of emulated cylinders           */
+
+/************************************************************************
+ * Debug options.                                                       * 
+ * Enable DEBUG and whichever options you require.                      *
+ ************************************************************************/
+#define DEBUG		0	/* Enable debug code. 			*/
+#define DBG_PROBE	1	/* Debug probe routines. 		*/
+#define	DBG_DUMP	1	/* Dump response to probes in hex. 	*/
+#define DBG_DELAY	0	/* Build in delays so debug messages can be
+				 * be read before they vanish of the top of
+				 * the screen!
+				 */
+#define DBG_FUNC	1	/* Trace function calls. 		*/
+#define DBG_TRAP	1	/* Trap errors from higher levels.	*/
+#define DBG_QUEUE	1	/* Trace command queueing. 		*/
+#define DBG_INTR	0       /* Trace interrupt service routine. 	*/
+#define DBG_WINTR       1       /* Trace Wait for interrupt             */
+#define DBG_ABNORM	1	/* Debug abnormal actions (reset, abort)*/
+#define DBG_AHMON       0       /* Ahmon's special  (-;                 */  
+
+#if DEBUG
+static char dummy;
+#define DBG(x, y)	if ((x)) {y;} else dummy=0
+#else
+#define DBG(x, y)
+#endif
+
+#if DEBUG && DBG_DELAY
+#define DELAY(x)	if (1) { int i; i = jiffies + x; while (jiffies < i); } else dummy=0
+#else
+#define DELAY(x)	
+#endif
+
+
+#define EATA {                       \
+	NULL,                        \
+        "Generic EATA - DMA (rev. 2.0b) driver", \
+        eata_detect,                 \
+        NULL,                        \
+        eata_info,                   \
+        eata_command,                \
+        eata_queue,                  \
+        eata_abort,                  \
+        eata_reset,                  \
+        NULL, /* Slave attach */     \
+        eata_biosparam,              \
+        1,      /* Canqueue */       \
+        7,      /* this_id */        \
+        64,     /* sg_tablesize */   \
+        1,      /* cmd_per_lun */    \
+        0,      /* present */        \
+        0,      /* unchecked_isa_dma */\
+	ENABLE_CLUSTERING }
+
+int eata_detect(Scsi_Host_Template *);
+const char *eata_info(void);
+int eata_command(Scsi_Cmnd *);
+int eata_queue(Scsi_Cmnd *, void *(done)(struct scsi_cmnd *));
+int eata_abort(Scsi_Cmnd *);
+int eata_reset(Scsi_Cmnd *);
+int eata_biosparam(Disk *, int, int []);
+
+
+typedef unsigned char byte;
+
+/***********************************************
+ *         EATA Register definitions           *
+ ***********************************************/
+
+#define EATA_CMD_PIO_READ_CONFIG 0xf0
+#define EATA_CMD_PIO_SET_CONFIG  0xf1
+#define EATA_CMD_PIO_SEND_CP     0xf2
+#define EATA_CMD_PIO_RECEIVE_SP  0xf3
+#define EATA_CMD_PIO_TRUNC       0xf4
+#define EATA_CMD_RESET           0xf9
+
+#define EATA_CMD_DMA_READ_CONFIG 0xfd
+#define EATA_CMD_DMA_SET_CONFIG  0xfe
+#define EATA_CMD_DMA_SEND_CP     0xff
+
+#define ECS_EMULATE_SENSE        0xd4
+
+#define HA_WCOMMAND 0x07        /* command register offset   */
+#define HA_WDMAADDR 0x02        /* DMA address LSB offset    */  
+#define HA_RAUXSTAT 0x08        /* aux status register offset*/
+#define HA_RSTATUS  0x07        /* status register offset    */
+#define HA_RDATA    0x00        /* data register (16bit)     */
+
+#define HA_ABUSY    0x01        /* aux busy bit              */
+#define HA_AIRQ     0x02        /* aux IRQ pending bit       */
+#define HA_SERROR   0x01        /* pr. command ended in error*/
+#define HA_SMORE    0x02        /* more data soon to come    */
+#define HA_SCORR    0x04        /* data corrected            */
+#define HA_SDRQ     0x08        /* data request active       */
+#define HA_SSC      0x10        /* seek complete             */
+#define HA_SFAULT   0x20        /* write fault               */
+#define HA_SRDY     0x40        /* drive ready               */
+#define HA_SBSY     0x80        /* drive busy                */
+#define HA_SDRDY    HA_SSC+HA_SRDY+HA_SDRQ 
+
+struct reg_bit {        /* reading this one will clear the interrupt 	*/
+  byte error:1;     /* previous command ended in an error           */
+  byte more:1;      /* more DATA comming soon, poll BSY & DRQ (PIO) */
+  byte corr:1;      /* data read was successfully corrected with ECC*/
+  byte drq:1;       /* data request aktive  */     
+  byte sc:1;        /* seek complete        */
+  byte fault:1;     /* write fault          */
+  byte ready:1;     /* drive ready          */
+  byte busy:1;      /* controller busy      */
+};
+
+struct reg_abit {       /* reading this won't clear the interrupt */
+  byte abusy:1;     /* auxiliary busy                         */
+  byte irq:1;       /* set when drive interrupt is asserted   */
+  byte dummy:6;
+};
+
+struct eata_register {      	    /* EATA register set */
+  byte data_reg[2];     	/* R, couldn't figure this one out          */
+  byte cp_addr[4];      	/* W, CP address register                   */
+  union { 
+    byte command;     	        /* W, command code: [read|set] conf, send CP*/
+    struct reg_bit status;	/* R, see register_bit1                     */
+    byte statusbyte;
+  } ovr;   
+  struct reg_abit aux_stat; 	/* R, see register_bit2               	    */
+};
+
+/**********************************************
+ *  Other (command) definitions               *
+ **********************************************/
+
+struct gco_bits1 {
+  byte OCS_enabled:1;	        /* Overlap Command Support enabled  	*/
+  byte TAR_support:1;	        /* SCSI Target Mode supported		*/
+  byte TRNXFR:1;		/* Truncate Transfer Cmd not necessary	*/
+                                /* Only used in PIO Mode 		*/
+  byte MORE_support:1;	        /* MORE supported (only PIO Mode) 	*/
+  byte DMA_support:1;	        /* DMA supported Driver uses only 	*/
+                        	/* this mode				*/
+  byte DRQ_valid:1;		/* DRQ value in Byte 30 is valid	*/
+  byte ATA:1;		        /* ATA device connected (no support)	*/
+  byte HAA_valid:1;		/* Hostadapter Address is valid     	*/
+};
+
+struct gco_bits2 {
+  byte IRQ:4;                 /* IRQ used this HA			*/
+  byte SECOND:1;              /* This is a secondary controller	        */
+  byte IRQ_TR:1;              /* IRQ Trigger: 0=edge, 1=level	        */
+  byte DRQX:2;                /* DRQ index, DRQ is 2comp of DRQX	*/
+};
+
+struct get_conf {               /* Read Configuration Array  */
+  byte gco_len[4];            /* Should return 0x1c 			*/
+  byte gco_sig[4];            /* Signature MUST be "EATA" 		*/
+  byte gco_version;           /* upper nibble contains Version      	*/
+  struct gco_bits1 bit1;				
+  byte gco_cppadlen[2];	      /* Number of pad bytes send after CD data */
+                              /* set to zero for DMA commands		*/
+  byte gco_HAaddress[4];      /* SCSI ID of controller if SCSI device   */
+                              /* if not, zero is returned 		*/
+  byte gco_cplen[4];	      /* CP length: number of valid cp bytes 	*/
+  byte gco_splen[4];	      /* Number of bytes returned after	        */ 
+                              /* Receive SP command			*/
+  byte gco_queuesiz[2];	      /* max number of queueable CPs		*/
+  byte gco_dummy[2];
+  byte gco_SGsiz[2];	      /* max number of SG table entries	        */
+  struct gco_bits2 bit2;
+  byte gco_sync;              /* device at ID 7 tru 0 is running in 	*/
+                              /* synchronous mode                       */
+  byte gco_unused[480];
+};
+
+struct sco_bits {
+  byte EATA_disable:1;	      /* Disable EATA interface :-(		*/
+  byte OC_enable:1;	      /* Enable overlapped commands		*/
+  byte MPD_enable:1;	      /* Enable sending of all Save, Restore    */
+                              /* and Modify Data Pointer Messages       */
+  byte TAR_enable:1;          /* Enable HA Target mode	       	        */
+}; 
+
+struct set_conf {	      /* Set Configuration Array */
+  byte sco_trlen[2];          /* Number of bytes following this field	*/
+  struct sco_bits bit;
+  byte sco_zero;              /* set this one to zero       		*/  
+  char   sco_unused[508];
+};
+
+#define HA_DATA_IN  0x80
+#define HA_DATA_OUT 0x40
+#define HA_SC_GA    0x08
+
+
+struct cp_bits {              /* Send Command Packet Bits           */
+  byte SCSI_Reset:1;          /* Cause a SCSI Bus reset on the cmd  */
+  byte HBA_Init:1;            /* Cause Controller to reInitialize   */
+  byte Auto_Req_Sen:1;        /* Do Auto Request Sense on errors    */
+  byte scatter:1;             /* Data Ptr points to a SG Packet     */
+  byte Resrvd:1;              /* RFU                                */
+  byte Interpret:1;           /* Interpret the SCSI cdb of own use  */
+  byte DataOut:1;             /* Data Out phase with command        */
+  byte DataIn:1;              /* Data In phase with command         */
+};
+
+struct cp_bits2 {
+  byte Phsunit:1;             /* physical unit on mirrored pair	    */
+  byte Iat:1;                 /* inhibit address translation        */
+  byte HBA_Cin:1;             /* HBA Inhibit caching                */
+};
+
+struct eata_ccb {             /* Send Command Packet structure      */
+  union {
+    struct cp_bits bit;       /* CP Bits			    */
+    byte Byte;
+  } cp_option;
+  byte reqlen;     	      /* Request Sense Length               */ 
+                              /* Valid if Auto_Req_Sen=1            */
+  byte unused[4];
+  union {
+    struct cp_bits2 bit;      /* Send command direct to physical unit   */   
+    byte Byte;
+  } cp_option2;
+  byte cp_id;                 /* SCSI Device ID of target       */ 
+  byte cp_msg0;               /* Message bytes 0-3              */
+  byte cp_msg1;
+  byte cp_msg2;
+  byte cp_msg3;
+  byte cp_cdb[12];    	      /* Command Descriptor Block       */
+  byte cp_datalen[4]; 	      /* Data Transfer Length           */
+                              /* If scatter=1 len of sg package */
+  byte cp_viraddr[4];         /* Virtual address of CP          */
+  byte cp_dataDMA[4]; 	      /* Data Address, if scatter=1     */
+                              /* address of scatter packet      */  
+  byte cp_statDMA[4];         /* address for Status Packet      */ 
+  byte cp_reqDMA[4];          /* Request Sense Address, used if */
+                              /* CP command ends with error     */
+};
+
+
+struct eata_sp
+{
+  byte cont_stat, scsi_stat, reserved[2];
+  byte residue_len[4], cp_id[4], msg[12];
+};
+
+struct eata_cmd_queue
+{
+  int used;
+  struct eata_sp  sp;
+  struct eata_ccb cp;
+  Scsi_Cmnd *cmd;
+};
+
+struct eata_sg_list
+{
+        void *data;
+        int len;
+};
+
+
+
+
+/* structure for max. 2 emulated drives */
+struct drive_geom_emul {
+  byte trans;                 /* translation flag 1=transl */
+  int  channel;               /* SCSI channel number       */
+  byte HBA;                   /* HBA number (prim/sec)     */
+  byte id;                    /* drive id                  */
+  byte lun;                   /* drive lun                 */
+  uint heads;                 /* number of heads           */
+  uint sectors;               /* number of sectors         */
+  uint cylinder;              /* number of cylinders       */
+};
+
+struct geom_emul {
+  int bios_drives;               /* number of emulated drives */
+  struct drive_geom_emul drv[2]; /* drive structures          */
+};
+
+struct emul_sense {
+  byte Byte0;
+  byte Cyls[2];
+  byte Heads;
+  byte Sectors;
+  byte padding;
+  byte drtype[2];
+  byte lunmap[8];
+};
+
+/**********************************************
+ * Message definitions                        *
+ **********************************************/
+#define HA_IDENTIFY_MSG 0x80       /* Sent identify message      */
+#define HA_G_DISCO_RECO 0x40       /* Grant disconnect privilege */
+
+/*********************************************
+ * Misc. definitions                         *
+ *********************************************/
+#define EXP_NOTHING 0
+#define EXP_RETURN  2
+#define EXP_NORMAL  1
+
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+                      + (((long)(up)[2]) <<  8) +  ((long)(up)[3]) )
+
+#endif /* _EATA_H */
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux-1.1.55+new_quota/drivers/scsi/hosts.c	Sat Sep 17 21:49:42 1994
+++ linux/drivers/scsi/hosts.c	Thu Oct 20 18:15:37 1994
@@ -39,10 +39,20 @@
 #include "aha1740.h"
 #endif
 
+#ifdef CONFIG_SCSI_AHA274X
+#include "aha274x.h"
+#endif
+
+
+
 #ifdef CONFIG_SCSI_BUSLOGIC
 #include "buslogic.h"
 #endif
 
+#ifdef CONFIG_SCSI_EATA            
+#include "eata.h"
+#endif
+
 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
 #include "fdomain.h"
 #endif
@@ -131,6 +141,12 @@
 #endif
 #ifdef CONFIG_SCSI_AHA1740
 	AHA1740,
+#endif
+#ifdef CONFIG_SCSI_AHA274X
+	AHA274X,
+#endif
+#ifdef CONFIG_SCSI_EATA 
+        EATA,
 #endif
 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
 	FDOMAIN_16X0,
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- linux-1.1.55+new_quota/drivers/scsi/in2000.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/in2000.c	Thu Oct 20 18:15:37 1994
@@ -0,0 +1,677 @@
+/*
+ *  This file is in2000.c, written and
+ *  Copyright (C) 1993  Brad McLean
+ *	Last edit 07/19/94 WDE
+ * 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)
+ */
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <linux/sched.h>
+#include <asm/dma.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "in2000.h"
+
+/*#define FAST_FIFO_IO*/
+
+/*#define DEBUG*/
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/* These functions are based on include/asm/io.h */
+#ifndef inw
+inline static unsigned short inw( unsigned short port )
+{
+   unsigned short _v;
+   
+   __asm__ volatile ("inw %1,%0"
+		     :"=a" (_v):"d" ((unsigned short) port));
+   return _v;
+}
+#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));
+}
+#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;
+struct scatterlist * in2000_scatter;
+static Scsi_Cmnd *in2000_SCptr = 0;
+
+void (*in2000_done)(Scsi_Cmnd *);
+
+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) {
+	  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;
+}
+
+
+/*
+ * retreive the current transaction counter from the WD
+ */
+
+unsigned in2000_txcnt(void)
+{
+    unsigned total=0;
+
+    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;
+}
+
+/*
+ * 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.
+ */
+
+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;	/* dont 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_current_segment++;
+      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 */
+    }
+}
+
+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_current_segment++;
+      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;}
+}
+
+const char *in2000_info(void)
+{
+    static char buffer[] = "";
+    return buffer;
+}
+
+void in2000_intr_handle(int foo)
+{
+    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 && in2000_dataptr )	/* data xfer pending */
+   	    {
+   	    if ( in2000_datawrite )
+		in2000_fifo_out();
+	    else
+		in2000_fifo_in();
+   	    } else ficmsk = 0;
+	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;
+    int bufflen = SCpnt->request_bufflen;
+    int timeout, size, loop;
+    int i;
+
+    /*
+     * 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;
+
+    /*
+     * What it looks like.  Boy did I get tired of reading it's 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 < (COMMAND_SIZE(*cmd)); i++) printk("%02x ", cmd[i]);
+    printk("\n");
+#endif
+    direction = 1;	/* assume for most commands */
+    if (*cmd == WRITE_10 || *cmd == WRITE_6)
+	direction = 0;
+    size = COMMAND_SIZE(*cmd);	/* 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 );
+    }
+    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
+     */
+    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
+     */
+    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 */
+    sti();			/* let the intrpt rip */
+    return 0;
+}
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+    internal_done_errcode = SCpnt->result;
+    ++internal_done_flag;
+}
+
+int in2000_command(Scsi_Cmnd * SCpnt)
+{
+    in2000_queuecommand(SCpnt, internal_done);
+
+    while (!internal_done_flag);
+    internal_done_flag = 0;
+    return internal_done_errcode;
+}
+
+int in2000_detect(Scsi_Host_Template * tpnt)
+{
+/* 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 };
+    int loop, tmp;
+
+    DEB(printk("in2000_detect: \n"));
+    
+    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);
+
+  /* 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, SA_INTERRUPT, "IN2000"))
+    {
+	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 */
+    return 1;
+}
+
+int in2000_abort(Scsi_Cmnd * SCpnt, int i)
+{
+    DEB(printk("in2000_abort\n"));
+    /*
+     * Ask no stupid questions, just order the abort.
+     */
+    outb(COMMAND,INSTAT);
+    outb(1,INDATA);	/* Abort Command */
+    return 0;
+}
+
+static inline void delay( unsigned how_long )
+{
+    unsigned long time = jiffies + how_long;
+    while (jiffies < time) ;
+}
+
+int in2000_reset(Scsi_Cmnd * SCpnt)
+{
+    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;
+#else
+    if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
+    return 0;
+#endif
+}
+
+int in2000_biosparam(int size, int dev, int* iinfo)
+	{
+    DEB(printk("in2000_biosparam\n"));
+    iinfo[0] = 64;
+    iinfo[1] = 32;
+    iinfo[2] = size >> 11;
+    return 0;
+    }
+ 
+
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- linux-1.1.55+new_quota/drivers/scsi/in2000.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/scsi/in2000.h	Thu Oct 20 18:15:37 1994
@@ -0,0 +1,124 @@
+#ifndef _IN2000_H
+
+/* $Id: in2000.h,v 1.1 1994/03/14 06:27:38 root Exp root $
+ *
+ * Header file for the Always IN 2000 driver for Linux
+ *
+ */
+
+#include <linux/types.h>
+
+/* The IN-2000 is based on a WD33C93 */
+
+#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
+#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
+
+#if 0
+/* This is used with scatter-gather */
+struct in2000_chain {
+  ulong  dataptr;		/* Location of data */
+  ulong  datalen;		/* Size of this part of chain */
+};
+#endif
+
+/* These belong in scsi.h also */
+#define any2scsi(up, p)				\
+(up)[0] = (((unsigned long)(p)) >> 16);		\
+(up)[1] = (((unsigned long)(p)) >> 8);		\
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( ((((long)*(up))&0x1f) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#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 *, int);
+const char *in2000_info(void);
+int in2000_reset(Scsi_Cmnd *);
+int in2000_biosparam(int, int, 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, "Always IN2000", in2000_detect, NULL,	\
+		in2000_info, in2000_command,	\
+		in2000_queuecommand,		\
+		in2000_abort,			\
+		in2000_reset,			\
+	        NULL,		                \
+		in2000_biosparam,               \
+		1, 7, IN2000_SG, 1, 0, 0}
+
+#endif
diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/sound/local.h linux/drivers/sound/local.h
--- linux-1.1.55+new_quota/drivers/sound/local.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/sound/local.h	Thu Oct 20 18:15:37 1994
@@ -0,0 +1,9 @@
+/*	Generated by configure. Don't edit!!!!	*/
+
+#undef CONFIGURE_SOUNDCARD
+#undef KERNEL_SOUNDCARD
+#define SOUND_VERSION_STRING "2.90-2"
+#define SOUND_CONFIG_DATE "Wed Oct 12 19:29:11 CDT 1994"
+#define SOUND_CONFIG_BY "root"
+#define SOUND_CONFIG_HOST "fuzzy"
+#define SOUND_CONFIG_DOMAIN ""
diff -u --recursive --new-file linux-1.1.55+new_quota/eata.lsm linux/eata.lsm
--- linux-1.1.55+new_quota/eata.lsm	Wed Dec 31 18:00:00 1969
+++ linux/eata.lsm	Thu Oct 20 18:15:37 1994
@@ -0,0 +1,29 @@
+Begin2
+Title        = EATA SCSI driver (DPT/NEC/AT&T)
+Version      = 0.3a
+Desc1        = Lowlevel device driver for all EATA compliant ISA and EISA 
+Desc2        = SCSI controllers
+Desc3        = Supported controllers are DPT PM2011, PM2012A, PM2012B
+Desc4        = PM2021, PM2022, PM2122, PM2322 and some OEMs from NEC and AT&T
+Author       = Michael Neuffer
+AuthorEmail  = Michael_Neuffer@wi2.maus.de, neuffer@goofy.zdv.uni-mainz.de
+Maintainer   = Michael Neuffer
+MaintEmail   = Michael_Neuffer@wi2.maus.de, neuffer@goofy.zdv.uni-mainz.de
+Site1        = tsx-11.mit.edu
+Path1        = /pub/linux/ALPHA/scsi/
+File1        = dpt_eata3a.tgz
+Site2        = sunsite.unc.edu
+Path2        = /pub/Linux/ALPHA/scsi
+File2        = dpt_eata03a.tgz
+Site3        = ftp.uni-mainz.de
+Path3        = /pub/Linux/Driver/SCSI
+File3        = dpt_eata03a.tgz
+Required1    = EATA compilant controller, Linux 1.1.51
+CopyPolicy1  = Code copyrighted be Michael Neuffer
+CopyPolicy2  = GNU Copyleft
+Keywords     = SCSI EATA DPT PM2011 PM2012A PM2012B PM2021 PM2022 PM2122 PM2322 NEC AT&T
+RelFiles1    = Slackboot_eata.gz Linux 1.1.51 Slackware bootdisk, kernel with many enabled features
+Entered      = 15AUG94
+EnteredBy    = Michael Neuffer
+End
+
diff -u --recursive --new-file linux-1.1.55+new_quota/include/linux/sonycd535.h linux/include/linux/sonycd535.h
--- linux-1.1.55+new_quota/include/linux/sonycd535.h	Wed Dec 31 18:00:00 1969
+++ linux/include/linux/sonycd535.h	Thu Oct 20 18:15:38 1994
@@ -0,0 +1,183 @@
+#ifndef SONYCD535_H
+#define SONYCD535_H
+
+/*
+ * define all the commands recognized by the CDU-531/5
+ */
+#define SONY535_REQUEST_DRIVE_STATUS_1		(0x80)
+#define SONY535_REQUEST_SENSE			(0x82)
+#define SONY535_REQUEST_DRIVE_STATUS_2		(0x84)
+#define SONY535_REQUEST_ERROR_STATUS		(0x86)
+#define SONY535_REQUEST_AUDIO_STATUS		(0x88)
+#define SONY535_INQUIRY				(0x8a)
+
+#define SONY535_SET_INACTIVITY_TIME		(0x90)
+
+#define SONY535_SEEK_AND_READ_N_BLOCKS_1	(0xa0)
+#define SONY535_SEEK_AND_READ_N_BLOCKS_2	(0xa4)
+#define SONY535_PLAY_AUDIO			(0xa6)
+
+#define SONY535_REQUEST_DISC_CAPACITY		(0xb0)
+#define SONY535_REQUEST_TOC_DATA		(0xb2)
+#define SONY535_REQUEST_SUB_Q_DATA		(0xb4)
+#define SONY535_REQUEST_ISRC			(0xb6)
+#define SONY535_REQUEST_UPC_EAN			(0xb8)
+
+#define SONY535_SET_DRIVE_MODE			(0xc0)
+#define SONY535_REQUEST_DRIVE_MODE		(0xc2)
+#define SONY535_SET_RETRY_COUNT			(0xc4)
+
+#define SONY535_DIAGNOSTIC_1			(0xc6)
+#define SONY535_DIAGNOSTIC_4			(0xcc)
+#define SONY535_DIAGNOSTIC_5			(0xce)
+
+#define SONY535_EJECT_CADDY			(0xd0)
+#define SONY535_DISABLE_EJECT_BUTTON		(0xd2)
+#define SONY535_ENABLE_EJECT_BUTTON		(0xd4)
+
+#define SONY535_HOLD				(0xe0)
+#define SONY535_AUDIO_PAUSE_ON_OFF		(0xe2)
+#define SONY535_SET_VOLUME			(0xe8)
+
+#define SONY535_STOP				(0xf0)
+#define SONY535_SPIN_UP				(0xf2)
+#define SONY535_SPIN_DOWN			(0xf4)
+
+#define SONY535_CLEAR_PARAMETERS		(0xf6)
+#define SONY535_CLEAR_ENDING_ADDRESS		(0xf8)
+
+/*
+ * define some masks
+ */
+#define SONY535_DATA_NOT_READY_BIT		(0x1)
+#define SONY535_RESULT_NOT_READY_BIT		(0x2)
+
+/*
+ *  drive status 1
+ */
+#define SONY535_STATUS1_COMMAND_ERROR		(0x1)
+#define SONY535_STATUS1_DATA_ERROR		(0x2)
+#define SONY535_STATUS1_SEEK_ERROR		(0x4)
+#define SONY535_STATUS1_DISC_TYPE_ERROR		(0x8)
+#define SONY535_STATUS1_NOT_SPINNING		(0x10)
+#define SONY535_STATUS1_EJECT_BUTTON_PRESSED	(0x20)
+#define SONY535_STATUS1_CADDY_NOT_INSERTED	(0x40)
+#define SONY535_STATUS1_BYTE_TWO_FOLLOWS	(0x80)
+
+/*
+ * drive status 2
+ */
+#define SONY535_CDD_LOADING_ERROR		(0x7)
+#define SONY535_CDD_NO_DISC			(0x8)
+#define SONY535_CDD_UNLOADING_ERROR		(0x9)
+#define SONY535_CDD_CADDY_NOT_INSERTED		(0xd)
+#define SONY535_ATN_RESET_OCCURRED		(0x2)
+#define SONY535_ATN_DISC_CHANGED		(0x4)
+#define SONY535_ATN_RESET_AND_DISC_CHANGED	(0x6)
+#define SONY535_ATN_EJECT_IN_PROGRESS		(0xe)
+#define SONY535_ATN_BUSY			(0xf)
+
+/*
+ * define some parameters
+ */
+#define SONY535_AUDIO_DRIVE_MODE		(0)
+#define SONY535_CDROM_DRIVE_MODE		(0xe0)
+
+#define SONY535_PLAY_OP_PLAYBACK		(0)
+#define SONY535_PLAY_OP_ENTER_HOLD		(1)
+#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR	(2)
+#define SONY535_PLAY_OP_SCAN_FORWARD		(3)
+#define SONY535_PLAY_OP_SCAN_BACKWARD		(4)
+
+/*
+ *  convert from msf format to block number 
+ */
+#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f))
+#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2])
+
+/*
+ *  error return values from the doSonyCmd() routines
+ */
+#define TIME_OUT			(-1)
+#define NO_CDROM			(-2)
+#define BAD_STATUS			(-3)
+#define CD_BUSY				(-4)
+#define NOT_DATA_CD			(-5)
+#define NO_ROOM				(-6)
+
+#define LOG_START_OFFSET        150     /* Offset of first logical sector */
+
+#define SONY_JIFFIES_TIMEOUT            500  /* Maximum number of jiffies (10ms)
+                                                  the drive will wait/try for an
+                                                  operation */
+#define SONY_READY_RETRIES      (50000)  /* How many times to retry a
+                                                  spin waiting for a register
+                                                  to come ready */
+#define SONY535_FAST_POLLS	(10000)   /* how many times recheck 
+                                                  status waiting for a data
+                                                  to become ready */
+
+typedef unsigned char Byte;
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s535_sony_drive_config
+{
+   char vendor_id[8];
+   char product_id[16];
+   char product_rev_level[4];
+};
+
+/* The following is returned from the request sub-q data command */
+struct s535_sony_subcode
+{
+   unsigned char address        :4;
+   unsigned char control        :4;
+   unsigned char track_num;
+   unsigned char index_num;
+   unsigned char rel_msf[3];
+   unsigned char abs_msf[3];
+};
+
+struct s535_sony_disc_capacity
+{
+   Byte mFirstTrack, sFirstTrack, fFirstTrack;
+   Byte mLeadOut, sLeadOut, fLeadOut;
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s535_sony_toc
+{
+   unsigned char reserved0      :4;
+   unsigned char control0       :4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char reserved0a;
+   unsigned char reserved0b;
+   unsigned char reserved1      :4;
+   unsigned char control1       :4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char reserved2      :4;
+   unsigned char control2       :4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char reserved    :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[100];
+
+   unsigned int lead_out_start_lba;
+};
+
+#endif /* SONYCD535_H */
diff -u --recursive --new-file linux-1.1.55+new_quota/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-1.1.55+new_quota/kernel/ksyms.c	Thu Oct 20 15:14:28 1994
+++ linux/kernel/ksyms.c	Thu Oct 20 18:15:38 1994
@@ -52,6 +52,15 @@
 
 extern void (* iABI_hook)(struct pt_regs * regs);
 
+#ifdef CONFIG_NEC260
+#include <linux/sched.h>
+extern int the_nec260_major;
+extern void (*do_hd) (void);
+#ifdef CONFIG_BLK_DEV_HD1
+extern void (*do_hd1) (void);
+#endif
+#endif
+
 struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
 	{
 	/* stackable module support */
@@ -187,6 +196,18 @@
 	X(dev_rint),
 	X(dev_tint),
 	X(irq2dev_map),
+#endif
+#ifdef CONFIG_NEC260
+	/* for the nec cdr-260 cdrom drive */
+	X (the_nec260_major),
+	X (do_hd),
+        X (read_ahead),
+	X (sync_dev),
+	X (invalidate_buffers),
+	X (__down),
+#ifdef CONFIG_BLK_DEV_HD1
+	X (do_hd1),
+#endif
 #endif
 
 	/********************************************************
diff -u --recursive --new-file linux-1.1.55+new_quota/patch.eata linux/patch.eata
--- linux-1.1.55+new_quota/patch.eata	Wed Dec 31 18:00:00 1969
+++ linux/patch.eata	Thu Oct 20 18:15:38 1994
@@ -0,0 +1,50 @@
+diff -u --recursive --new-file linux50/config.in linux/config.in
+--- config.old  Tue Sep 27 17:54:13 1994
++++ config.in   Tue Sep 27 17:53:40 1994
+@@ -51,6 +51,7 @@
+ bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n
+ bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n
+ bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n
++bool 'EATA-DMA (rev. 2.0b) (DPT,NEC,AT&T) support' CONFIG_SCSI_EATA y
+ bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n
+ bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
+ bool 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx n
+diff -u --recursive --new-file linux50/drivers/scsi/Makefile linux/drivers/scsi/Makefile
+--- linux50/drivers/scsi/Makefile	Tue Sep 27 18:02:02 1994
++++ linux/drivers/scsi/Makefile	Thu Sep  8 23:23:19 1994
+@@ -72,6 +72,11 @@
+ SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c
+ endif
+ 
++ifdef CONFIG_SCSI_EATA
++SCSI_OBJS := $(SCSI_OBJS) eata.o
++SCSI_SRCS := $(SCSI_SRCS) eata.c
++endif
++
+ ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ SCSI_OBJS := $(SCSI_OBJS) fdomain.o
+ SCSI_SRCS := $(SCSI_SRCS) fdomain.c
+diff -u --recursive --new-file linux50/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
+--- linux50/drivers/scsi/hosts.c	Tue Sep 27 18:03:15 1994
++++ linux/drivers/scsi/hosts.c	Thu Sep  8 23:23:19 1994
+@@ -43,6 +43,10 @@
+ #include "buslogic.h"
+ #endif
+ 
++#ifdef CONFIG_SCSI_EATA            
++#include "eata.h"
++#endif
++
+ #ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ #include "fdomain.h"
+ #endif
+@@ -131,6 +135,9 @@
+ #ifdef CONFIG_SCSI_AHA1740
+ 	AHA1740,
+ #endif
++#ifdef CONFIG_SCSI_EATA 
++        EATA,
++#endif
+ #ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ 	FDOMAIN_16X0,
+ #endif
diff -u --recursive --new-file linux-1.1.55+new_quota/tools/version.h linux/tools/version.h
--- linux-1.1.55+new_quota/tools/version.h	Wed Dec 31 18:00:00 1969
+++ linux/tools/version.h	Thu Oct 20 18:15:38 1994
@@ -0,0 +1,7 @@
+#define UTS_RELEASE "1.1.53"
+#define UTS_VERSION "#8 Wed Oct 12 21:48:39 CDT 1994"
+#define LINUX_COMPILE_TIME "21:48:39"
+#define LINUX_COMPILE_BY "root"
+#define LINUX_COMPILE_HOST "fuzzy"
+#define LINUX_COMPILE_DOMAIN "is.a.good.cat"
+#define LINUX_COMPILER "gcc version 2.5.8"