]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.31pre2 2.0.31pre2
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:27 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:27 +0000 (15:11 -0500)
61 files changed:
CREDITS
Documentation/Configure.help
Documentation/ide.txt
MAINTAINERS
Makefile
arch/alpha/kernel/ptrace.c
arch/alpha/kernel/signal.c
arch/i386/defconfig
arch/i386/kernel/bios32.c
arch/i386/kernel/ptrace.c
arch/i386/kernel/signal.c
drivers/block/Config.in
drivers/block/Makefile
drivers/block/genhd.c
drivers/block/ide-cd.c
drivers/block/ide-floppy.c [new file with mode: 0644]
drivers/block/ide.c
drivers/block/ide.h
drivers/net/auto_irq.c
drivers/net/lance32.c
drivers/net/tulip.c
drivers/pci/pci.c
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/aic7xxx.c
drivers/scsi/aic7xxx.h
drivers/scsi/aic7xxx.seq [deleted file]
drivers/scsi/aic7xxx/aic7xxx.reg [new file with mode: 0644]
drivers/scsi/aic7xxx/aic7xxx.seq [new file with mode: 0644]
drivers/scsi/aic7xxx/scsi_message.h [new file with mode: 0644]
drivers/scsi/aic7xxx/sequencer.h [new file with mode: 0644]
drivers/scsi/aic7xxx_asm.c [deleted file]
drivers/scsi/aic7xxx_proc.c
drivers/scsi/aic7xxx_reg.h
drivers/scsi/aic7xxx_seq.h [new file with mode: 0644]
drivers/scsi/hosts.c
drivers/scsi/ide-scsi.c [new file with mode: 0644]
drivers/scsi/ide-scsi.h [new file with mode: 0644]
fs/buffer.c
fs/ext2/namei.c
fs/smbfs/sock.c
include/linux/hdreg.h
include/linux/ipx.h
include/linux/pagemap.h
include/linux/pci.h
include/linux/proc_fs.h
include/linux/sched.h
include/net/sock.h
init/main.c
init/patches/acer-ess1688-support [deleted file]
ipc/shm.c
kernel/exit.c
kernel/sysctl.c
mm/filemap.c
mm/mlock.c
mm/page_alloc.c
mm/vmscan.c
net/core/dev.c
net/ipv4/arp.c
net/ipv4/tcp_input.c
net/ipx/af_ipx.c

diff --git a/CREDITS b/CREDITS
index 6d9eb5ddd5e8b4c6b0c1ca73d727760937004757..ad0d3271df616c4ba4b028cadaa90a7b1f71253a 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -466,7 +466,7 @@ S: Carnegie, Pennsylvania 15106-4304
 S: USA
 
 N: Philip Gladstone
-E: philipg@onsett.com
+E: philip@raptor.com
 D: Kernel / timekeeping stuff
 
 N: Michael A. Griffith
index 5817028e636dfc8c94e8fff70a6b20f25a706e4a..17db40aa897dd4e115ffd5c55b64400dcaa572b6 100644 (file)
@@ -124,18 +124,18 @@ CONFIG_BLK_DEV_LOOP
   nothing to do with the loopback device used for network connections
   from the machine to itself.  Most users will answer N here.
 
-Enhanced IDE/MFM/RLL disk/cdrom/tape support
+Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
 CONFIG_BLK_DEV_IDE 
-  This will use the full-featured IDE driver to control up to four IDE
-  interfaces, for a combination of up to eight IDE disk/cdrom/tape
-  drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, and other topics, is all contained in
-  Documentation/ide.txt.  If you have one or more IDE drives, say Y
-  here.  If your system has no IDE drives, or if memory requirements
-  are really tight, you could say N here, and select the Old harddisk
-  driver instead to save about 13kB of memory in the kernel.  To
-  fine-tune IDE drive/interface parameters for improved performance,
-  look for the hdparm package at
+  This will use the full-featured IDE driver to control up to four
+  IDE interfaces, for a combination of up to eight IDE
+  disk/cdrom/tape/floppy drives.  Useful information about large
+  (>540MB) IDE disks, soundcard IDE ports, and other topics, is all
+  contained in Documentation/ide.txt.  If you have one or more IDE
+  drives, say Y here.  If your system has no IDE drives, or if memory
+  requirements are really tight, you could say N here, and select
+  the Old harddisk driver instead to save about 13kB of memory in
+  the kernel.  To fine-tune IDE drive/interface parameters for
+  improved performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
 
 Old harddisk (MFM/RLL/IDE) driver
@@ -190,9 +190,32 @@ CONFIG_BLK_DEV_IDETAPE
   ATAPI is a new protocol used by IDE TAPE and ATAPI drives,
   similar to the SCSI protocol.  At boot time, the TAPE drive will
   be identified along with other IDE devices, as "hdb" or "hdc",
-  or something similar.  Be sure to consult the drivers/block/ide-tape.c
+  or something similar, and will be mapped to a character device
+  such as "ht0".  Be sure to consult the drivers/block/ide-tape.c
   and Documentation/ide.txt files for usage information.
 
+Include IDE/ATAPI FLOPPY support (new)
+CONFIG_BLK_DEV_IDEFLOPPY
+  If you have an IDE floppy which uses the ATAPI protocol, say Y.
+  ATAPI is a new protocol used by IDE cdrom/tape/floppy drives,
+  similar to the SCSI protocol.  IDE floppy drives include the
+  LS-120 and the ATAPI ZIP (ATAPI PD-CD drives are not supported
+  by this driver; support for PD-CD drives is available through
+  the SCSI emulation).  At boot time, the FLOPPY drive will be
+  identified along with other IDE devices, as "hdb" or "hdc", or
+  something similar.
+
+SCSI emulation support
+CONFIG_BLK_DEV_IDESCSI
+  This will provide SCSI host adapter emulation for IDE ATAPI devices,
+  and will allow you to use a SCSI device driver instead of a native
+  ATAPI driver. This is useful if you have an ATAPI device for which
+  no native driver has been written (for example, an ATAPI PD-CD
+  drive); you can then use this emulation together with an appropriate
+  SCSI device driver. If both this SCSI emulation and native ATAPI
+  support are compiled into the kernel, the native support will be
+  used.  Normally, say N.
+
 Support removable IDE interfaces (PCMCIA)
 CONFIG_BLK_DEV_IDE_PCMCIA
   This option adds code to the IDE driver to handle hot insertion
@@ -589,10 +612,11 @@ CONFIG_PCI_OPTIMIZE
 Intel 82371 PIIX (Triton I/II) DMA support
 CONFIG_BLK_DEV_TRITON
   If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
-  and includes the Intel 430FX PCI Triton chipset, you will want to
-  enable this option to allow use of bus-mastering DMA data transfers.
-  Read the comments at the beginning of drivers/block/triton.c.  Check
-  the file Documentation/Changes for location and latest version of
+  and includes the Intel Triton I/II IDE interface chipset (i82371FB,
+  i82371SB or i82371AB), you will want to enable this option to allow
+  use of bus-mastering DMA data transfers. Read the comments at the
+  beginning of drivers/block/triton.c and Documentation/ide.txt.
+  Check the file Documentation/Changes for location and latest version of
   the hdparm utility. It is safe to say Y to this question.
 
 System V IPC
index 277382b8c069c77e0be40b1cdddc02f75917cd7a..b388d7427dc4f0ac0809d5ada26bc5c29edd29a7 100644 (file)
@@ -14,6 +14,7 @@ See description later on below for handling BIG IDE drives with >1024 cyls.
 
 Major features of ide.c & ide-cd.c ("NEW!" marks changes since 1.2.13):
 
+NEW!   - support for IDE ATAPI *floppy* drives
 NEW!   - support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
                (re-run MAKEDEV.ide to create the tape device entries in /dev/)
 NEW!   - support for up to *four* IDE interfaces on one or more IRQs
@@ -111,12 +112,12 @@ shell script:   /usr/src/linux/scripts/MAKEDEV.ide
 Apparently many releases of Slackware 2.2/2.3 have incorrect entries
 in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
 
-ide.c automatically probes for the primary and secondary interfaces,
+ide.c automatically probes for the standard four IDE interfaces,
 for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
+IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10).
 
-Interfaces beyond the first two are not normally probed for, but may be
-specified using kernel "command line" options.  For example,
+For special cases, interfaces may be specified using kernel "command line"
+options.  For example,
 
        ide3=0x168,0x36e,10     /* ioports 0x168-0x16f,0x36e, irq 10 */
 
index c313f22393273cddb44df34054e86a08e1cedce3..54045c4dad35450d684089061051d147bb4b52e3 100644 (file)
@@ -212,6 +212,12 @@ M: andersee@debian.org
 L:     linux-kernel@vger.rutgers.edu
 S:     Maintained
 
+IDE/ATAPI TAPE/FLOPPY DRIVERS
+P:     Gadi Oxman
+M:     Gadi Oxman <gadio@netvision.net.il>
+L:     linux-kernel@vger.rutgers.edu
+S:     Maintained
+
 ISDN SUBSYSTEM
 P:     Fritz Elfert
 M:     fritz@wuemaus.franken.de
index e411f8defe5f670fd624ae5ab7e4236003227c9f..7b2c792e6dc2742e7a5506e432233aec43fdd8a1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -319,7 +319,6 @@ clean:      archclean
 mrproper: clean
        rm -f include/linux/autoconf.h include/linux/version.h
        rm -f drivers/sound/local.h drivers/sound/.defines
-       rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h
        rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
        rm -f .version .config* config.in config.old
        rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
index 1fd5942c9079db2aa66e25641d34b5ba542b89b2..a68e2b782fd060082643f64b0e6e6e757bc1e73c 100644 (file)
@@ -641,7 +641,7 @@ asmlinkage void syscall_trace(void)
                return;
        current->exit_code = SIGTRAP;
        current->state = TASK_STOPPED;
-       notify_parent(current);
+       notify_parent(current, SIGCHLD);
        schedule();
        /*
         * this isn't the same as continuing with a signal, but it will do
index c6cb6d88553978b898745d197579b30796b0e27c..91abaf4f0390afed51f48dcaca1bb11c41e75b17 100644 (file)
@@ -291,7 +291,7 @@ asmlinkage int do_signal(unsigned long oldmask,
                if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
                        current->exit_code = signr;
                        current->state = TASK_STOPPED;
-                       notify_parent(current);
+                       notify_parent(current, SIGCHLD);
                        schedule();
                        single_stepping |= ptrace_cancel_bpt(current);
                        if (!(signr = current->exit_code))
@@ -330,7 +330,7 @@ asmlinkage int do_signal(unsigned long oldmask,
                                current->exit_code = signr;
                                if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
                                                SA_NOCLDSTOP))
-                                       notify_parent(current);
+                                       notify_parent(current, SIGCHLD);
                                schedule();
                                single_stepping |= ptrace_cancel_bpt(current);
                                continue;
index bcecf23d1c239fddd0d058d87ba2eb0e65fa69d5..07227fdb3faee29f45bb06fce7370d208715dde3 100644 (file)
@@ -42,11 +42,13 @@ CONFIG_BLK_DEV_IDE=y
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_IDE_PCMCIA is not set
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
-# CONFIG_BLK_DEV_TRITON is not set
+CONFIG_BLK_DEV_TRITON=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
@@ -66,7 +68,10 @@ CONFIG_BLK_DEV_RZ1000=y
 CONFIG_INET=y
 # CONFIG_IP_FORWARD is not set
 # CONFIG_IP_MULTICAST is not set
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
 
 #
 # (it is safe to leave these untouched)
index bd209823b7966725d3fcd95c2e8ac9f76ecbe8ea..d4dbe3bcf45f1411fb6f38a495e5ccdb0ca9cc11 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * bios32.c - BIOS32, PCI BIOS functions.
  *
+ * $Id: bios32.c,v 1.3.2.4 1997/08/02 22:24:23 mj Exp $
+ *
  * Sponsored by
  *     iX Multiuser Multitasking Magazine
  *     Hannover, Germany
  * Feb 3, 1997  : Set internal functions to static, save/restore flags
  *     avoid dead locks reading broken PCI BIOS, werner@suse.de 
  *
+ * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
+ *     (mj@atrey.karlin.mff.cuni.cz)
+ *
+ * May 7,  1997 : Added some missing cli()'s. [mj]
+ * 
+ * Jun 20, 1997 : Corrected problems in "conf1" type accesses.
+ *      (paubert@iram.es)
  */
 
 #include <linux/config.h>
@@ -156,7 +165,7 @@ static unsigned long bios32_service(unsigned long service)
        unsigned long entry;            /* %edx */
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%edi)"
                : "=a" (return_code),
                  "=b" (address),
@@ -171,10 +180,10 @@ static unsigned long bios32_service(unsigned long service)
                case 0:
                        return address + entry;
                case 0x80:      /* Not present */
-                       printk("bios32_service(%ld) : not present\n", service);
+                       printk("bios32_service(0x%lx) : not present\n", service);
                        return 0;
                default: /* Shouldn't happen */
-                       printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
+                       printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
                                service, return_code);
                        return 0;
        }
@@ -187,7 +196,7 @@ static struct {
 } pci_indirect = { 0, KERNEL_CS };
 
 
-extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
+static int check_pcibios(void)
 {
        unsigned long signature;
        unsigned char present_status;
@@ -199,7 +208,7 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem
        if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
                pci_indirect.address = pcibios_entry;
 
-               save_flags(flags);
+               save_flags(flags); cli();
                __asm__("lcall (%%edi)\n\t"
                        "jc 1f\n\t"
                        "xor %%ah, %%ah\n"
@@ -230,9 +239,10 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem
                if (pcibios_entry) {
                        printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
                                major_revision, minor_revision, pcibios_entry);
+                       return 1;
                }
        }
-       return memory_start;
+       return 0;
 }
 
 
@@ -243,7 +253,7 @@ static int pci_bios_find_class (unsigned int class_code, unsigned short index,
        unsigned long ret;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__ ("lcall (%%edi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -268,7 +278,7 @@ static int pci_bios_find_device (unsigned short vendor, unsigned short device_id
        unsigned short ret;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%edi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -293,7 +303,7 @@ static int pci_bios_read_config_byte(unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -315,7 +325,7 @@ static int pci_bios_read_config_word (unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -337,7 +347,7 @@ static int pci_bios_read_config_dword (unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -359,7 +369,7 @@ static int pci_bios_write_config_byte (unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -381,7 +391,7 @@ static int pci_bios_write_config_word (unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -403,7 +413,7 @@ static int pci_bios_write_config_dword (unsigned char bus,
        unsigned long bx = (bus << 8) | device_fn;
        unsigned long flags;
 
-       save_flags(flags);
+       save_flags(flags); cli();
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -444,21 +454,17 @@ static int pci_direct_find_device (unsigned short vendor, unsigned short device_
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
        if (dev->vendor == vendor && dev->device == device_id) {
            if (curr == index) {
                *devfn = dev->devfn;
                *bus = dev->bus->number;
-               restore_flags(flags);
                return PCIBIOS_SUCCESSFUL;
            }
            ++curr;
        }
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -472,21 +478,17 @@ static int pci_direct_find_class (unsigned int class_code, unsigned short index,
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
        if (dev->class == class_code) {
            if (curr == index) {
                *devfn = dev->devfn;
                *bus = dev->bus->number;
-               restore_flags(flags);
                return PCIBIOS_SUCCESSFUL;
            }
            ++curr;
        }
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -500,18 +502,9 @@ static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    switch (where & 3) {
-       case 0: *value = inb(0xCFC);
-               break;
-       case 1: *value = inb(0xCFD);
-               break;
-       case 2: *value = inb(0xCFE);
-               break;
-       case 3: *value = inb(0xCFF);
-               break;
-    }
+    *value = inb(0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -521,12 +514,10 @@ static int pci_conf1_read_config_word (unsigned char bus,
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    if (where & 2)
-       *value = inw(0xCFE);
-    else
-       *value = inw(0xCFC);
+    *value = inw(0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;    
 }
@@ -536,7 +527,8 @@ static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     *value = inl(0xCFC);
     restore_flags(flags);
@@ -548,9 +540,9 @@ static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    outb(value, 0xCFC);
+    outb(value, 0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -560,9 +552,10 @@ static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    outw(value, 0xCFC);
+    outw(value, 0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -572,7 +565,8 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     outl(value, 0xCFC);
     restore_flags(flags);
@@ -608,7 +602,7 @@ static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn
 
     if (device_fn & 0x80)
        return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inb(IOADDR(device_fn,where));
@@ -624,7 +618,7 @@ static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_f
 
     if (device_fn & 0x80)
        return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inw(IOADDR(device_fn,where));
@@ -640,7 +634,7 @@ static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_
 
     if (device_fn & 0x80)
        return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inl (IOADDR(device_fn,where));    
@@ -654,7 +648,7 @@ static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outb (value, IOADDR(device_fn,where));
@@ -668,7 +662,7 @@ static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outw (value, IOADDR(device_fn,where));
@@ -682,7 +676,7 @@ static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outl (value, IOADDR(device_fn,where));    
@@ -714,7 +708,7 @@ static struct pci_access *check_direct_pci(void)
     unsigned int tmp;
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
 
     /*
      * check if configuration type 1 works
@@ -883,7 +877,9 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
         *
         */
 
-       for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
+       for (check = (union bios32 *) 0xe0000;
+            check <= (union bios32 *) 0xffff0;
+            ++check) {
                if (check->fields.signature != BIOS32_SIGNATURE)
                        continue;
                length = check->fields.length * 16;
@@ -908,13 +904,11 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
                                bios32_entry = check->fields.entry;
                                printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
                                bios32_indirect.address = bios32_entry;
-                               access_pci = &pci_bios_access;
                        }
                }
        }
-       if (bios32_entry) {
-               memory_start = check_pcibios (memory_start, memory_end);
-       }
+       if (bios32_entry && check_pcibios())
+               access_pci = &pci_bios_access;
 #endif
        return memory_start;
 }
index cd9543561c95ff699c53e0d8581d712f794a9738..435b4fd2ead4e60e9d4732e3b9d13d83cd7d7ed4 100644 (file)
@@ -535,7 +535,7 @@ asmlinkage void syscall_trace(void)
                return;
        current->exit_code = SIGTRAP;
        current->state = TASK_STOPPED;
-       notify_parent(current);
+       notify_parent(current, SIGCHLD);
        schedule();
        /*
         * this isn't the same as continuing with a signal, but it will do
index 9a82456104ecc077639dd1fc7ae23db724457d40..b5544a69a42c6660beb3142d2205582839b038d1 100644 (file)
@@ -286,7 +286,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
                if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
                        current->exit_code = signr;
                        current->state = TASK_STOPPED;
-                       notify_parent(current);
+                       notify_parent(current, SIGCHLD);
                        schedule();
                        if (!(signr = current->exit_code))
                                continue;
@@ -324,7 +324,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
                                current->exit_code = signr;
                                if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
                                                SA_NOCLDSTOP))
-                                       notify_parent(current);
+                                       notify_parent(current, SIGCHLD);
                                schedule();
                                continue;
 
index 050570a07214fa51f15bd328fb07470655c608ad..5f6ea38142c88bd64c46fca7c0552c94683653d4 100644 (file)
@@ -5,7 +5,7 @@ mainmenu_option next_comment
 comment 'Floppy, IDE, and other block devices'
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
-bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
+bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
 comment 'Please see Documentation/ide.txt for help/info on IDE drives'
 if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
   bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
@@ -13,6 +13,8 @@ else
   bool '   Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
   bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
   bool '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
+  bool '   Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY
+  bool '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI
   bool '   Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
   bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
   if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
index 2ebff1c0fc3000c93f47ace2a805e707a36857f1..a2ed99202c048a6af8217487b3c7e843f250d949 100644 (file)
@@ -97,6 +97,10 @@ ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
 L_OBJS += ide-tape.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 else
index 666058cdf5b4caa0ce801d7db53cd728c9831161..9215efbb0cb225581033474931959cf0c9220d42 100644 (file)
@@ -331,7 +331,7 @@ check_table:
                                   && (q->sector & 63) == 1
                                   && (q->end_sector & 63) == 63) {
                                        unsigned int heads = q->end_head + 1;
-                                       if (heads == 32 || heads == 64 || heads == 128) {
+                                       if (heads == 32 || heads == 64 || heads == 128 || heads == 255) {
 
                                                (void) ide_xlate_1024(dev, heads, " [PTBL]");
                                                break;
index e9cbd842e20d04e918f1129b4d58650e6882977b..6cabb54fb4643481fda72ea8ccfbc92f665209c8 100644 (file)
  *                          you must define IHAVEADOLPHIN)
  *                        Added identifier so new Sanyo CD-changer works
  *                        Better detection if door locking isn't supported 
- *                         
+ * 3.21  Jun 16,1997  -- Add work-around for GCD-R580B
+ *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
  *
@@ -1141,6 +1142,11 @@ static void cdrom_start_read_continuation (ide_drive_t *drive)
        /* Number of sectors to transfer. */
        nsect = rq->nr_sectors;
 
+#if !STANDARD_ATAPI
+       if (nsect > drive->cdrom_info.max_sectors)
+               nsect = drive->cdrom_info.max_sectors;
+#endif /* not STANDARD_ATAPI */
+
        /* Starting sector. */
        sector = rq->sector;
 
@@ -2674,6 +2680,8 @@ void ide_cdrom_setup (ide_drive_t *drive)
                CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
 #if ! STANDARD_ATAPI
+       drive->cdrom_info.max_sectors = 252;
+
        CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
        CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
        CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
@@ -2699,6 +2707,9 @@ void ide_cdrom_setup (ide_drive_t *drive)
                        CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
                }
 
+               else if (strcmp (drive->id->model, "GCD-R580B") == 0)
+                       drive->cdrom_info.max_sectors = 124;
+
                else if (strcmp (drive->id->model,
                                 "NEC CD-ROM DRIVE:260") == 0 &&
                         strcmp (drive->id->fw_rev, "1.01") == 0) {
diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c
new file mode 100644 (file)
index 0000000..cb39696
--- /dev/null
@@ -0,0 +1,1388 @@
+/*
+ * linux/drivers/block/ide-floppy.c    Version 0.7 - ALPHA     Aug   4, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2   Oct 31 96   Minor changes.
+ * Ver 0.3   Dec  2 96   Fixed error recovery bug.
+ * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5   Feb 21 97   Add partitions support.
+ *                       Use the minimum of the LBA and CHS capacities.
+ *                       Avoid hwgroup->rq == NULL on the last irq.
+ *                       Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.6   Jul  3 97   Limit max sectors per read/write command to 64.
+ * Ver 0.7   Aug  4 97   Increase irq timeout from 10 to 100 seconds.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ *     Main Linux ide driver include file
+ */
+#include "ide.h"
+
+/*
+ *     The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG            0
+#define IDEFLOPPY_DEBUG_INFO           0
+#define IDEFLOPPY_DEBUG_BUGS           1
+
+/*
+ *     After each failed packet command we issue a request sense command
+ *     and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES       3
+
+/*
+ *     With each packet command, we allocate a buffer of
+ *     IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE       256
+
+/*
+ *     In various places in the driver, we need to allocate storage
+ *     for packet commands and requests, which will remain valid while
+ *     we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK             (10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ *     Some drives fail read/write requests with 64 or more sectors.
+ */
+#define IDEFLOPPY_MAX_SECTORS          64
+
+/*
+ *     Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD             (10 * WAIT_CMD)
+
+/*
+ *     Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+       u8 c[12];                               /* Actual packet bytes */
+       int retries;                            /* On each retry, we increment retries */
+       int error;                              /* Error code */
+       int request_transfer;                   /* Bytes to transfer */
+       int actually_transferred;               /* Bytes actually transferred */
+       int buffer_size;                        /* Size of our data buffer */
+       char *b_data;                           /* Pointer which runs on the buffers */
+       int b_count;                            /* Missing/Available data on the current buffer */
+       struct request *rq;                     /* The corresponding request */
+       byte *buffer;                           /* Data buffer */
+       byte *current_position;                 /* Pointer into the above buffer */
+       void (*callback) (ide_drive_t *);       /* Called when this packet command is completed */
+       byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE];       /* Temporary buffer */
+       unsigned int flags;                     /* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ *     Packet command flag bits.
+ */
+#define        PC_ABORT                        0       /* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED             2       /* 1 when we prefer to use DMA if possible */
+#define        PC_DMA_IN_PROGRESS              3       /* 1 while DMA in progress */
+#define        PC_DMA_ERROR                    4       /* 1 when encountered problem during DMA */
+#define        PC_WRITING                      5       /* Data direction */
+
+/*
+ *     Removable Block Access Capabilities Page
+ */
+typedef struct {
+       unsigned        page_code       :6;     /* Page code - Should be 0x1b */
+       unsigned        reserved1_6     :1;     /* Reserved */
+       unsigned        ps              :1;     /* Should be 0 */
+       u8              page_length;            /* Page Length - Should be 0xa */
+       unsigned        reserved2       :6;
+       unsigned        srfp            :1;     /* Supports reporting progress of format */
+       unsigned        sflp            :1;     /* System floppy type device */
+       unsigned        tlun            :3;     /* Total logical units supported by the device */
+       unsigned        reserved3       :3;
+       unsigned        sml             :1;     /* Single / Multiple lun supported */
+       unsigned        ncd             :1;     /* Non cd optical device */
+       u8              reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ *     Flexible disk page.
+ */
+typedef struct {
+       unsigned        page_code       :6;     /* Page code - Should be 0x5 */
+       unsigned        reserved1_6     :1;     /* Reserved */
+       unsigned        ps              :1;     /* The device is capable of saving the page */
+       u8              page_length;            /* Page Length - Should be 0x1e */
+       u16             transfer_rate;          /* In kilobits per second */
+       u8              heads, sectors;         /* Number of heads, Number of sectors per track */
+       u16             sector_size;            /* Byes per sector */
+       u16             cyls;                   /* Number of cylinders */
+       u8              reserved10[10];
+       u8              motor_delay;            /* Motor off delay */
+       u8              reserved21[7];
+       u16             rpm;                    /* Rotations per minute */
+       u8              reserved30[2];
+} idefloppy_flexible_disk_page_t;
+/*
+ *     Format capacity
+ */
+typedef struct {
+       u8              reserved[3];
+       u8              length;                 /* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+       u32             blocks;                 /* Number of blocks */
+       unsigned        dc              :2;     /* Descriptor Code */
+       unsigned        reserved        :6;
+       u8              length_msb;             /* Block Length (MSB)*/
+       u16             length;                 /* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID       0x00
+#define CAPACITY_UNFORMATTED   0x01
+#define CAPACITY_CURRENT       0x02
+#define CAPACITY_NO_CARTRIDGE  0x03
+
+/*
+ *     Most of our global data which we need to save even as we leave the
+ *     driver due to an interrupt or a timer event is stored in a variable
+ *     of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+       ide_drive_t *drive;
+
+       idefloppy_pc_t *pc;                     /* Current packet command */
+       idefloppy_pc_t *failed_pc;              /* Last failed packet command */
+       idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+       int pc_stack_index;                     /* Next free packet command storage space */
+       struct request rq_stack[IDEFLOPPY_PC_STACK];
+       int rq_stack_index;                     /* We implement a circular array */
+
+       /*
+        *      Last error information
+        */
+       byte sense_key, asc, ascq;
+
+       /*
+        *      Device information
+        */
+       int blocks, block_size, bs_factor;                      /* Current format */
+       idefloppy_capacity_descriptor_t capacity;               /* Last format capacity */
+       idefloppy_flexible_disk_page_t flexible_disk_page;      /* Copy of the flexible disk page */
+
+       unsigned int flags;                     /* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ *     Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT                0       /* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED                1       /* Media may have changed */
+#define IDEFLOPPY_USE_READ12           2       /* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ *     ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD      0x04
+#define IDEFLOPPY_INQUIRY_CMD          0x12
+#define IDEFLOPPY_MODE_SELECT_CMD      0x55
+#define IDEFLOPPY_MODE_SENSE_CMD       0x5a
+#define IDEFLOPPY_READ10_CMD           0x28
+#define IDEFLOPPY_READ12_CMD           0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD    0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD    0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD  0x1e
+#define IDEFLOPPY_SEEK_CMD             0x2b
+#define IDEFLOPPY_START_STOP_CMD       0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD  0x00
+#define IDEFLOPPY_VERIFY_CMD           0x2f
+#define IDEFLOPPY_WRITE10_CMD          0x2a
+#define IDEFLOPPY_WRITE12_CMD          0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD     0x2e
+
+/*
+ *     Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT             0x00
+#define MODE_SENSE_CHANGEABLE          0x01
+#define MODE_SENSE_DEFAULT             0x02 
+#define MODE_SENSE_SAVED               0x03
+
+/*
+ *     Special requests for our block device strategy routine.
+ */
+#define        IDEFLOPPY_FIRST_RQ              90
+
+/*
+ *     IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define        IDEFLOPPY_PC_RQ                 90
+
+#define IDEFLOPPY_LAST_RQ              90
+
+/*
+ *     A macro which can be used to check if a given request command
+ *     originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd)          ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ *     Error codes which are returned in rq->errors to the higher part
+ *     of the driver.
+ */
+#define        IDEFLOPPY_ERROR_GENERAL         101
+
+/*
+ *     The ATAPI Status Register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned check          :1;     /* Error occurred */
+               unsigned idx            :1;     /* Reserved */
+               unsigned corr           :1;     /* Correctable error occurred */
+               unsigned drq            :1;     /* Data is request by the device */
+               unsigned dsc            :1;     /* Media access command finished */
+               unsigned reserved5      :1;     /* Reserved */
+               unsigned drdy           :1;     /* Ignored for ATAPI commands (ready to accept ATA command) */
+               unsigned bsy            :1;     /* The device has access to the command block */
+       } b;
+} idefloppy_status_reg_t;
+
+/*
+ *     The ATAPI error register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned ili            :1;     /* Illegal Length Indication */
+               unsigned eom            :1;     /* End Of Media Detected */
+               unsigned abrt           :1;     /* Aborted command - As defined by ATA */
+               unsigned mcr            :1;     /* Media Change Requested - As defined by ATA */
+               unsigned sense_key      :4;     /* Sense key of the last failed packet command */
+       } b;
+} idefloppy_error_reg_t;
+
+/*
+ *     ATAPI Feature Register
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned dma            :1;     /* Using DMA or PIO */
+               unsigned reserved321    :3;     /* Reserved */
+               unsigned reserved654    :3;     /* Reserved (Tag Type) */
+               unsigned reserved7      :1;     /* Reserved */
+       } b;
+} idefloppy_feature_reg_t;
+
+/*
+ *     ATAPI Byte Count Register.
+ */
+typedef union {
+       unsigned all                    :16;
+       struct {
+               unsigned low            :8;     /* LSB */
+               unsigned high           :8;     /* MSB */
+       } b;
+} idefloppy_bcount_reg_t;
+
+/*
+ *     ATAPI Interrupt Reason Register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned cod            :1;     /* Information transferred is command (1) or data (0) */
+               unsigned io             :1;     /* The device requests us to read (1) or write (0) */
+               unsigned reserved       :6;     /* Reserved */
+       } b;
+} idefloppy_ireason_reg_t;
+
+/*
+ *     ATAPI floppy Drive Select Register
+ */
+typedef union {        
+       unsigned all                    :8;
+       struct {
+               unsigned sam_lun        :3;     /* Logical unit number */
+               unsigned reserved3      :1;     /* Reserved */
+               unsigned drv            :1;     /* The responding drive will be drive 0 (0) or drive 1 (1) */
+               unsigned one5           :1;     /* Should be set to 1 */
+               unsigned reserved6      :1;     /* Reserved */
+               unsigned one7           :1;     /* Should be set to 1 */
+       } b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ *     ATAPI Device Control Register
+ */
+typedef union {                        
+       unsigned all                    :8;
+       struct {
+               unsigned zero0          :1;     /* Should be set to zero */
+               unsigned nien           :1;     /* Device interrupt is disabled (1) or enabled (0) */
+               unsigned srst           :1;     /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+               unsigned one3           :1;     /* Should be set to 1 */
+               unsigned reserved4567   :4;     /* Reserved */
+       } b;
+} idefloppy_control_reg_t;
+
+/*
+ *     The following is used to format the general configuration word of
+ *     the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {      
+       unsigned packet_size            :2;     /* Packet Size */
+       unsigned reserved234            :3;     /* Reserved */
+       unsigned drq_type               :2;     /* Command packet DRQ type */
+       unsigned removable              :1;     /* Removable media */
+       unsigned device_type            :5;     /* Device type */
+       unsigned reserved13             :1;     /* Reserved */
+       unsigned protocol               :2;     /* Protocol type */
+};
+
+/*
+ *     INQUIRY packet command - Data Format
+ */
+typedef struct {
+       unsigned        device_type     :5;     /* Peripheral Device Type */
+       unsigned        reserved0_765   :3;     /* Peripheral Qualifier - Reserved */
+       unsigned        reserved1_6t0   :7;     /* Reserved */
+       unsigned        rmb             :1;     /* Removable Medium Bit */
+       unsigned        ansi_version    :3;     /* ANSI Version */
+       unsigned        ecma_version    :3;     /* ECMA Version */
+       unsigned        iso_version     :2;     /* ISO Version */
+       unsigned        response_format :4;     /* Response Data Format */
+       unsigned        reserved3_45    :2;     /* Reserved */
+       unsigned        reserved3_6     :1;     /* TrmIOP - Reserved */
+       unsigned        reserved3_7     :1;     /* AENC - Reserved */
+       u8              additional_length;      /* Additional Length (total_length-4) */
+       u8              rsv5, rsv6, rsv7;       /* Reserved */
+       u8              vendor_id[8];           /* Vendor Identification */
+       u8              product_id[16];         /* Product Identification */
+       u8              revision_level[4];      /* Revision Level */
+       u8              vendor_specific[20];    /* Vendor Specific - Optional */
+       u8              reserved56t95[40];      /* Reserved - Optional */
+                                               /* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ *     REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+       unsigned        error_code      :7;     /* Current error (0x70) */
+       unsigned        valid           :1;     /* The information field conforms to SFF-8070i */
+       u8              reserved1       :8;     /* Reserved */
+       unsigned        sense_key       :4;     /* Sense Key */
+       unsigned        reserved2_4     :1;     /* Reserved */
+       unsigned        ili             :1;     /* Incorrect Length Indicator */
+       unsigned        reserved2_67    :2;
+       u32             information __attribute__ ((packed));
+       u8              asl;                    /* Additional sense length (n-7) */
+       u32             command_specific;       /* Additional command specific information */
+       u8              asc;                    /* Additional Sense Code */
+       u8              ascq;                   /* Additional Sense Code Qualifier */
+       u8              replaceable_unit_code;  /* Field Replaceable Unit Code */
+       u8              reserved[3];
+       u8              pad[2];                 /* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ *     Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define        IDEFLOPPY_CAPABILITIES_PAGE     0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE   0x05
+
+/*
+ *     Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+       u16             mode_data_length;       /* Length of the following data transfer */
+       u8              medium_type;            /* Medium Type */
+       unsigned        reserved3       :7;
+       unsigned        wp              :1;     /* Write protect */
+       u8              reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b)     ((a)<(b) ? (a):(b))
+#define        IDEFLOPPY_MAX(a,b)      ((a)>(b) ? (a):(b))
+
+/*
+ *     Too bad. The drive wants to send us data which we are not ready to accept.
+ *     Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+       while (bcount--)
+               IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+       while (bcount--)
+               OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ *     idefloppy_end_request is used to finish servicing a request.
+ *
+ *     For read/write requests, we will call ide_end_request to pass to the
+ *     next buffer.
+ */
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+       ide_drive_t *drive = hwgroup->drive;
+       idefloppy_floppy_t *floppy = drive->floppy;
+       struct request *rq = hwgroup->rq;
+       int error;
+
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       switch (uptodate) {
+               case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+               case 1: error = 0; break;
+               default: error = uptodate;
+       }
+       if (error)
+               floppy->failed_pc = NULL;
+       if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+               ide_end_request (uptodate, hwgroup);
+               return;
+       }
+       rq->errors = error;
+       ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+       struct request *rq = pc->rq;
+       struct buffer_head *bh = rq->bh;
+       int count;
+
+       while (bcount) {
+               if (pc->b_count == bh->b_size) {
+                       idefloppy_end_request (1, HWGROUP(drive));
+                       if ((bh = rq->bh) != NULL)
+                               pc->b_count = 0;
+               }
+               if (bh == NULL) {
+                       printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+                       idefloppy_discard_data (drive, bcount);
+                       return;
+               }
+               count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+               atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+               bcount -= count; pc->b_count += count;
+               if (pc->b_count == bh->b_size) {
+                       rq->sector += rq->current_nr_sectors;
+                       rq->nr_sectors -= rq->current_nr_sectors;
+               }
+       }
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+       struct request *rq = pc->rq;
+       struct buffer_head *bh = rq->bh;
+       int count;
+       
+       while (bcount) {
+               if (!pc->b_count) {
+                       idefloppy_end_request (1, HWGROUP(drive));
+                       if ((bh = rq->bh) != NULL) {
+                               pc->b_data = bh->b_data;
+                               pc->b_count = bh->b_size;
+                       }
+               }
+               if (bh == NULL) {
+                       printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+                       idefloppy_write_zeros (drive, bcount);
+                       return;
+               }
+               count = IDEFLOPPY_MIN (pc->b_count, bcount);
+               atapi_output_bytes (drive, pc->b_data, count);
+               bcount -= count; pc->b_data += count; pc->b_count -= count;
+               if (!pc->b_count) {
+                       rq->sector += rq->current_nr_sectors;
+                       rq->nr_sectors -= rq->current_nr_sectors;
+               }
+       }
+}
+
+#ifdef CONFIG_BLK_DEV_TRITON
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+       struct request *rq = pc->rq;
+       struct buffer_head *bh = rq->bh;
+
+       while ((bh = rq->bh) != NULL)
+               idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+/*
+ *     idefloppy_queue_pc_head generates a new packet command request in front
+ *     of the request queue, before the current request, so that it will be
+ *     processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+       ide_init_drive_cmd (rq);
+       rq->buffer = (char *) pc;
+       rq->cmd = IDEFLOPPY_PC_RQ;
+       (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+
+       if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+               floppy->pc_stack_index=0;
+       return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+
+       if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+               floppy->rq_stack_index=0;
+       return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ *     idefloppy_analyze_error is called on each failed packet command retry
+ *     to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+
+       floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+       if (floppy->failed_pc)
+               printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+       else
+               printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+       if (!floppy->pc->error) {
+               idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+               idefloppy_end_request (1,HWGROUP (drive));
+       } else {
+               printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+               idefloppy_end_request (0,HWGROUP (drive));
+       }
+}
+
+/*
+ *     General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ *     idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+       memset (pc->c, 0, 12);
+       pc->retries = 0;
+       pc->flags = 0;
+       pc->request_transfer = 0;
+       pc->buffer = pc->pc_buffer;
+       pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+       pc->b_data = NULL;
+       pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+       idefloppy_init_pc (pc); 
+       pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+       pc->c[4] = 255;
+       pc->request_transfer = 18;
+       pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ *     idefloppy_retry_pc is called when an error was detected during the
+ *     last packet command. We queue a request sense packet command in
+ *     the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+       idefloppy_pc_t *pc;
+       struct request *rq;
+       idefloppy_error_reg_t error;
+
+       error.all = IN_BYTE (IDE_ERROR_REG);
+       pc = idefloppy_next_pc_storage (drive);
+       rq = idefloppy_next_rq_storage (drive);
+       idefloppy_create_request_sense_cmd (pc);
+       idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ *     idefloppy_pc_intr is the usual interrupt handler which will be called
+ *     during a packet command.
+ */
+static void idefloppy_pc_intr (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_status_reg_t status;
+       idefloppy_bcount_reg_t bcount;
+       idefloppy_ireason_reg_t ireason;
+       idefloppy_pc_t *pc=floppy->pc;
+       struct request *rq = pc->rq;
+       unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */       
+
+#ifdef CONFIG_BLK_DEV_TRITON
+       if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+               if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+                       set_bit (PC_DMA_ERROR, &pc->flags);
+               } else {
+                       pc->actually_transferred=pc->request_transfer;
+                       idefloppy_update_buffers (drive, pc);
+               }
+               (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));    /* End DMA */
+#if IDEFLOPPY_DEBUG_LOG
+               printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+       }
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+       status.all = GET_STAT();                                        /* Clear the interrupt */
+
+       if (!status.b.drq) {                                            /* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+               printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+               clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+               sti();
+
+               if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {    /* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+                       printk (KERN_INFO "ide-floppy: %s: I/O error, ",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+                       rq->errors++;
+                       if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+                               printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+                               ide_do_reset (drive);
+                               return;
+                       }
+                       idefloppy_retry_pc (drive);                             /* Retry operation */
+                       return;
+               }
+               pc->error = 0;
+               if (floppy->failed_pc == pc)
+                       floppy->failed_pc=NULL;
+               pc->callback(drive);                    /* Command finished - Call the callback function */
+               return;
+       }
+#ifdef CONFIG_BLK_DEV_TRITON
+       if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+               printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+               printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
+               drive->using_dma=0;
+               ide_do_reset (drive);
+               return;
+       }
+#endif /* CONFIG_BLK_DEV_TRITON */
+       bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);                        /* Get the number of bytes to transfer */
+       bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);                 /* on this interrupt */
+       ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+       if (ireason.b.cod) {
+               printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+               ide_do_reset (drive);
+               return;
+       }
+       if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {        /* Hopefully, we will never get here */
+               printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+               printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+               ide_do_reset (drive);
+               return;
+       }
+       if (!test_bit (PC_WRITING, &pc->flags)) {                       /* Reading - Check that we have enough space */
+               temp = pc->actually_transferred + bcount.all;
+               if ( temp > pc->request_transfer) {
+                       if (temp > pc->buffer_size) {
+                               printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+                               idefloppy_discard_data (drive,bcount.all);
+                               ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
+                               return;
+                       }
+#if IDEFLOPPY_DEBUG_LOG
+                       printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+               }
+       }
+       if (test_bit (PC_WRITING, &pc->flags)) {
+               if (pc->buffer != NULL)
+                       atapi_output_bytes (drive,pc->current_position,bcount.all);     /* Write the current buffer */
+               else
+                       idefloppy_output_buffers (drive, pc, bcount.all);
+       } else {
+               if (pc->buffer != NULL)
+                       atapi_input_bytes (drive,pc->current_position,bcount.all);      /* Read the current buffer */
+               else
+                       idefloppy_input_buffers (drive, pc, bcount.all);
+       }
+       pc->actually_transferred+=bcount.all;                           /* Update the current position */
+       pc->current_position+=bcount.all;
+
+       ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);          /* And set the interrupt handler again */
+}
+
+static void idefloppy_transfer_pc (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_ireason_reg_t ireason;
+
+       if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+               printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+               return;
+       }
+       ireason.all=IN_BYTE (IDE_IREASON_REG);
+       if (!ireason.b.cod || ireason.b.io) {
+               printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+               ide_do_reset (drive);
+               return;
+       }
+       ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD);        /* Set the interrupt routine */
+       atapi_output_bytes (drive, floppy->pc->c, 12);          /* Send the actual packet */
+}
+
+/*
+ *     Issue a packet command
+ */
+static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_bcount_reg_t bcount;
+       int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+       if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+               printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+       }
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+       if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+               floppy->failed_pc=pc;
+       floppy->pc=pc;                                                  /* Set the current packet command */
+
+       if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+               /*
+                *      We will "abort" retrying a packet command in case
+                *      a legitimate error code was received.
+                */
+               if (!test_bit (PC_ABORT, &pc->flags)) {
+                       printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+                               drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+                       pc->error = IDEFLOPPY_ERROR_GENERAL;            /* Giving up */
+               }
+               floppy->failed_pc=NULL;
+               pc->callback(drive);
+               return;
+       }
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       pc->retries++;
+       pc->actually_transferred=0;                                     /* We haven't transferred any data yet */
+       pc->current_position=pc->buffer;
+       bcount.all=pc->request_transfer;                                /* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+       if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
+               printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
+               drive->using_dma=0;
+       }
+       if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+               dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+       OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+       OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);                        /* Use PIO/DMA */
+       OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+       OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+       OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_TRITON
+       if (dma_ok) {                                                   /* Begin DMA, if necessary */
+               set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+               (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+       }
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+       if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+               ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD);
+               OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);              /* Issue the packet command */
+       } else {
+               OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+               idefloppy_transfer_pc (drive);
+       }
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG        
+       printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       idefloppy_end_request(1, HWGROUP(drive));
+       return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       idefloppy_init_pc (pc);
+       pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+       pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+       idefloppy_init_pc (pc);
+       pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+       pc->c[7] = 255;
+       pc->c[8] = 255;
+}
+
+/*
+ *     A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+       unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+       
+       idefloppy_init_pc (pc);
+       pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+       pc->c[1] = 0;
+       pc->c[2] = page_code + (type << 6);
+
+       switch (page_code) {
+               case IDEFLOPPY_CAPABILITIES_PAGE:
+                       length += 12;
+                       break;
+               case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+                       length += 32;
+                       break;
+               default:
+                       printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+       }
+       put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+       pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+       idefloppy_init_pc (pc);
+       pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+       pc->c[4] = start;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+{
+       int block = sector / floppy->bs_factor;
+       int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS);
+
+#if IDEFLOPPY_DEBUG_LOG
+       printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+               2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       idefloppy_init_pc (pc);
+       if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+               pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+               put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+       } else {
+               pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+               put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+       }
+       put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+       pc->callback = &idefloppy_rw_callback;
+       pc->rq = rq;
+       pc->b_data = rq->buffer;
+       pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+       if (rq->cmd == WRITE)
+               set_bit (PC_WRITING, &pc->flags);
+       pc->buffer = NULL;
+       pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+       set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ *     idefloppy_do_request is our request handling function.  
+ */
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+       printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       if (rq->errors >= ERROR_MAX) {
+               if (floppy->failed_pc != NULL)
+                       printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+                               drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+               else
+                       printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
+               idefloppy_end_request (0, HWGROUP(drive));
+               return;
+       }
+       switch (rq->cmd) {
+               case READ:
+               case WRITE:
+                       if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+                               printk ("%s: unsupported r/w request size\n", drive->name);
+                               idefloppy_end_request (0, HWGROUP(drive));
+                               return;
+                       }
+                       pc = idefloppy_next_pc_storage (drive);
+                       idefloppy_create_rw_cmd (floppy, pc, rq, block);
+                       break;
+               case IDEFLOPPY_PC_RQ:
+                       pc = (idefloppy_pc_t *) rq->buffer;
+                       break;
+               default:
+                       printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+                       idefloppy_end_request (0,HWGROUP (drive));
+                       return;
+       }
+       pc->rq = rq;
+       idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ *     idefloppy_queue_pc_tail adds a special packet command request to the
+ *     tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+       struct request rq;
+
+       ide_init_drive_cmd (&rq);
+       rq.buffer = (char *) pc;
+       rq.cmd = IDEFLOPPY_PC_RQ;
+       return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *     Look at the flexible disk page parameters. We will ignore the CHS
+ *     capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_pc_t pc;
+       idefloppy_mode_parameter_header_t *header;
+       idefloppy_flexible_disk_page_t *page;
+       int capacity, lba_capacity;
+
+       idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+       if (idefloppy_queue_pc_tail (drive,&pc)) {
+               printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+               return 1;
+       }
+       header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+       page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+       page->transfer_rate = ntohs (page->transfer_rate);
+       page->sector_size = ntohs (page->sector_size);
+       page->cyls = ntohs (page->cyls);
+       page->rpm = ntohs (page->rpm);
+       capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+       if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
+               printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+                       drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+                       page->transfer_rate / 8, page->sector_size, page->rpm);
+
+       floppy->flexible_disk_page = *page;
+       drive->bios_cyl = page->cyls;
+       drive->bios_head = page->heads;
+       drive->bios_sect = page->sectors;
+       lba_capacity = floppy->blocks * floppy->block_size;
+       if (capacity != lba_capacity) {
+               printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+                       drive->name, capacity, lba_capacity);
+               capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+               floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
+       }
+       return 0;
+}
+
+/*
+ *     Determine if a media is present in the floppy drive, and if so,
+ *     its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_pc_t pc;
+       idefloppy_capacity_header_t *header;
+       idefloppy_capacity_descriptor_t *descriptor;
+       int i, descriptors, rc = 1, blocks, length;
+
+       drive->bios_cyl = 0;
+       drive->bios_head = drive->bios_sect = 0;
+       floppy->blocks = floppy->bs_factor = 0;
+       drive->part[0].nr_sects = 0;
+
+       idefloppy_create_read_capacity_cmd (&pc);
+       if (idefloppy_queue_pc_tail (drive, &pc)) {
+               printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+               return 1;
+       }
+       header = (idefloppy_capacity_header_t *) pc.buffer;
+       descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+       descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+       for (i = 0; i < descriptors; i++, descriptor++) {
+               blocks = descriptor->blocks = ntohl (descriptor->blocks);
+               length = descriptor->length = ntohs (descriptor->length);
+               if (!i && descriptor->dc == CAPACITY_CURRENT) {
+                       if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t)))
+                               printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length);
+                       floppy->capacity = *descriptor;
+                       if (!length || length % 512)
+                               printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+                       else {
+                               floppy->blocks = blocks;
+                               floppy->block_size = length;
+                               if ((floppy->bs_factor = length / 512) != 1)
+                                       printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+                               rc = 0;
+                       }
+               }
+#if IDEFLOPPY_DEBUG_INFO
+               if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+               printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+       }
+       (void) idefloppy_get_flexible_disk_page (drive);
+       drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
+       return rc;
+}
+
+/*
+ *     Our special ide-floppy ioctl's.
+ *
+ *     Currently there aren't any ioctl's.
+ */
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       return -EIO;
+}
+
+/*
+ *     Our open/release functions
+ */
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       idefloppy_pc_t pc;
+       
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       MOD_INC_USE_COUNT;
+       if (drive->usage == 1) {
+               idefloppy_create_start_stop_cmd (&pc, 1);
+               (void) idefloppy_queue_pc_tail (drive, &pc);
+               if (idefloppy_get_capacity (drive)) {
+                       drive->usage--;
+                       MOD_DEC_USE_COUNT;
+                       return -EIO;
+               }
+               set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+               idefloppy_create_prevent_cmd (&pc, 1);
+               (void) idefloppy_queue_pc_tail (drive, &pc);
+               check_disk_change(inode->i_rdev);
+       }
+       return 0;
+}
+
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+       idefloppy_pc_t pc;
+       
+#if IDEFLOPPY_DEBUG_LOG
+       printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+       if (!drive->usage) {
+               invalidate_buffers (inode->i_rdev);
+               idefloppy_create_prevent_cmd (&pc, 0);
+               (void) idefloppy_queue_pc_tail (drive, &pc);
+       }
+       MOD_DEC_USE_COUNT;
+}
+
+/*
+ *     Check media change. Use a simple algorithm for now.
+ */
+int idefloppy_media_change (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       
+       return clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ *     Return the current floppy capacity to ide.c.
+ */
+unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+       return capacity;
+}
+
+/*
+ *     idefloppy_identify_device checks if we can support a drive,
+ *     based on the ATAPI IDENTIFY command results.
+ */
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+       struct idefloppy_id_gcw gcw;
+       idefloppy_floppy_t *floppy;
+#if IDEFLOPPY_DEBUG_INFO
+       unsigned short mask,i;
+       char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+       *((unsigned short *) &gcw) = id->config;
+
+#if IDEFLOPPY_DEBUG_INFO
+       printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+       switch (gcw.protocol) {
+               case 0: case 1: sprintf (buffer, "ATA");break;
+               case 2: sprintf (buffer, "ATAPI");break;
+               case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+       }
+       printk (KERN_INFO "Protocol Type: %s\n", buffer);
+       switch (gcw.device_type) {
+               case 0: sprintf (buffer, "Direct-access Device");break;
+               case 1: sprintf (buffer, "Streaming Tape Device");break;
+               case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+               case 5: sprintf (buffer, "CD-ROM Device");break;
+               case 6: sprintf (buffer, "Reserved");
+               case 7: sprintf (buffer, "Optical memory Device");break;
+               case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+               default: sprintf (buffer, "Reserved");
+       }
+       printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+       printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");        
+       switch (gcw.drq_type) {
+               case 0: sprintf (buffer, "Microprocessor DRQ");break;
+               case 1: sprintf (buffer, "Interrupt DRQ");break;
+               case 2: sprintf (buffer, "Accelerated DRQ");break;
+               case 3: sprintf (buffer, "Reserved");break;
+       }
+       printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+       switch (gcw.packet_size) {
+               case 0: sprintf (buffer, "12 bytes");break;
+               case 1: sprintf (buffer, "16 bytes");break;
+               default: sprintf (buffer, "Reserved");break;
+       }
+       printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+       printk (KERN_INFO "Model: %s\n",id->model);
+       printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
+       printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+       printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+       printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+       printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+       printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+       printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+       printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+       printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+       printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+       printk (KERN_INFO "Single Word DMA supported modes:\n");
+       for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+               if (id->dma_1word & mask)
+                       printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+       }
+       printk (KERN_INFO "Multi Word DMA supported modes:\n");
+       for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+               if (id->dma_mword & mask)
+                       printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+       }
+       if (id->field_valid & 0x0002) {
+               printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+               if (id->eide_dma_min == 0)
+                       sprintf (buffer, "Not supported");
+               else
+                       sprintf (buffer, "%d ns",id->eide_dma_min);
+               printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+               if (id->eide_dma_time == 0)
+                       sprintf (buffer, "Not supported");
+               else
+                       sprintf (buffer, "%d ns",id->eide_dma_time);
+               printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+               if (id->eide_pio == 0)
+                       sprintf (buffer, "Not supported");
+               else
+                       sprintf (buffer, "%d ns",id->eide_pio);
+               printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+               if (id->eide_pio_iordy == 0)
+                       sprintf (buffer, "Not supported");
+               else
+                       sprintf (buffer, "%d ns",id->eide_pio_iordy);
+               printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+       } else
+               printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+       if (gcw.protocol != 2)
+               printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+       else if (gcw.device_type != 0)
+               printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+       else if (!gcw.removable)
+               printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+       else if (gcw.drq_type == 3)
+               printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+       else if (gcw.packet_size != 0)
+               printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+       else if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL)
+               printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+       else {
+               drive->floppy = floppy; 
+               return 1;
+       }
+       printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+       return 0;
+}
+
+/*
+ *     idefloppy_get_capabilities asks the floppy about its various
+ *     parameters.
+ */
+static void idefloppy_get_capabilities (ide_drive_t *drive)
+{
+       idefloppy_pc_t pc;
+       idefloppy_mode_parameter_header_t *header;
+       idefloppy_capabilities_page_t *capabilities;
+       
+       idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
+       if (idefloppy_queue_pc_tail (drive,&pc)) {
+               printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
+               return;
+       }
+       header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+       capabilities = (idefloppy_capabilities_page_t *) (header + 1);
+
+       if (!capabilities->sflp)
+               printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
+
+#if IDEFLOPPY_DEBUG_INFO
+       printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
+       printk (KERN_INFO "Mode Parameter Header:\n");
+       printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
+       printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
+       printk (KERN_INFO "WP - %d\n",header->wp);
+
+       printk (KERN_INFO "Capabilities Page:\n");
+       printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
+       printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
+       printk (KERN_INFO "PS - %d\n",capabilities->ps);
+       printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
+       printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
+       printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
+       printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
+       printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+}
+
+/*
+ *     Driver initialization.
+ */
+void idefloppy_setup (ide_drive_t *drive)
+{
+       idefloppy_floppy_t *floppy = drive->floppy;
+       struct idefloppy_id_gcw gcw;
+
+       *((unsigned short *) &gcw) = drive->id->config;
+       drive->ready_stat = 0;
+       memset (floppy, 0, sizeof (idefloppy_floppy_t));
+       floppy->drive = drive;
+       floppy->pc = floppy->pc_stack;
+       if (gcw.drq_type == 1)
+               set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+
+       idefloppy_get_capabilities (drive);
+       (void) idefloppy_get_capacity (drive);
+}
index a9803ba37390f1dbe93cdc2e363a10f1ed88218f..85b6018db59f9f4a4c47d94350376fa593f19592 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c  Version 5.52  Sep  24, 1996
+ *  linux/drivers/block/ide.c  Version 5.53  Jun  24, 1997
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
  *                     change delay_10ms() to delay_50ms() to fix problems
  * Version 5.52                fix incorrect invalidation of removable devices
  *                     add "hdx=slow" command line option
+ * Version 5.53                add ATAPI floppy drive support
+ *                     change default media for type 0 to floppy
+ *                     add support for Exabyte Nest
+ *                     add missing set_blocksize() in revalidate_disk()
+ *                     handle bad status bit sequencing in ide_wait_stat()
+ *                     support partition table translations with 255 heads
+ *                     probe all interfaces by default
+ *                     add probe for the i82371AB chipset
+ *                     acknowledge media change on removable drives
+ *                     add work-around for BMI drives
+ *                     remove "LBA" from boot messages
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -367,7 +378,6 @@ static void init_hwif_data (unsigned int index)
 
        /* fill in any non-zero initial values */
        hwif->index     = index;
-       hwif->noprobe   = (index > 1);
        hwif->io_base   = default_io_base[index];
        hwif->ctl_port  = hwif->io_base ? hwif->io_base+0x206 : 0x000;
 #ifdef CONFIG_BLK_DEV_HD
@@ -535,6 +545,29 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
        }
 }
 
+/*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+       ++bytecount;
+       ide_input_data (drive, buffer, bytecount / 4);
+       if ((bytecount & 0x03) >= 2)
+               insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+       ++bytecount;
+       ide_output_data (drive, buffer, bytecount / 4);
+       if ((bytecount & 0x03) >= 2)
+               outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
 /*
  * This should get invoked any time we exit the driver to
  * wait for an interrupt response from a drive.  handler() points
@@ -593,6 +626,10 @@ static unsigned long current_capacity (ide_drive_t  *drive)
 
        if (!drive->present)
                return 0;
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+       if (drive->media == ide_floppy)
+               return idefloppy_capacity(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
        if (drive->media != ide_disk)
                return 0x7fffffff;      /* cdrom or tape */
        drive->select.b.lba = 0;
@@ -625,8 +662,13 @@ static void ide_geninit (struct gendisk *gd)
                if (drive->present && drive->media == ide_tape)
                        idetape_setup(drive);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+               if (drive->present && drive->media == ide_floppy)
+                       idefloppy_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
                drive->part[0].nr_sects = current_capacity(drive);
-               if (!drive->present || drive->media != ide_disk) {
+               if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) ||
+                   !drive->part[0].nr_sects) {
                        drive->part[0].start_sect = -1; /* skip partition check */
                }
        }
@@ -1005,6 +1047,8 @@ void ide_error (ide_drive_t *drive, const char *msg, byte stat)
                                rq->errors = ERROR_MAX;
                        else if (err & TRK0_ERR)        /* help it find track zero */
                                rq->errors |= ERROR_RECAL;
+                       else if (err & MC_ERR)
+                               drive->special.b.mc = 1;
                }
                if ((stat & DRQ_STAT) && rq->cmd != WRITE)
                        try_to_flush_leftover_data(drive);
@@ -1017,9 +1061,20 @@ void ide_error (ide_drive_t *drive, const char *msg, byte stat)
                if (drive->media == ide_tape) {
                        rq->errors = 0;
                        idetape_end_request(0, HWGROUP(drive));
-               }
-               else
+               } else
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+               if (drive->media == ide_floppy) {
+                       rq->errors = 0;
+                       idefloppy_end_request(0, HWGROUP(drive));
+               } else
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+               if (drive->media == ide_scsi) {
+                       rq->errors = 0;
+                       idescsi_end_request(0, HWGROUP(drive));
+               } else
+#endif /* CONFIG_BLK_DEV_IDESCSI */
                ide_end_request(0, HWGROUP(drive));
        }
        else {
@@ -1229,6 +1284,19 @@ static void recal_intr (ide_drive_t *drive)
                ide_error(drive, "recal_intr", stat);
 }
 
+/*
+ * mc_intr() is invoked on completion of a WIN_ACKMC cmd.
+ */
+static void mc_intr (ide_drive_t *drive)
+{
+       byte stat = GET_STAT();
+
+       sti();
+       if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+               ide_error(drive, "mc_intr", stat);
+       drive->special.b.mc = 0;
+}
+
 /*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
@@ -1265,7 +1333,7 @@ static inline void do_special (ide_drive_t *drive)
 #endif
        if (s->b.set_geometry) {
                s->b.set_geometry = 0;
-               if (drive->media == ide_disk) {
+               if (drive->media == ide_disk && !drive->no_geom) {
                        OUT_BYTE(drive->sect,IDE_SECTOR_REG);
                        OUT_BYTE(drive->cyl,IDE_LCYL_REG);
                        OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
@@ -1291,6 +1359,10 @@ static inline void do_special (ide_drive_t *drive)
                                ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
                } else
                        drive->mult_req = 0;
+       } else if (s->b.mc) {
+               s->b.mc = 0;
+               if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
+                       ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr);
        } else if (s->all) {
                int special = s->all;
                s->all = 0;
@@ -1314,27 +1386,24 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
        byte stat;
        unsigned long flags;
 
-test:
-       udelay(1);      /* spec allows drive 400ns to change "BUSY" */
-       if (OK_STAT((stat = GET_STAT()), good, bad))
-               return 0;       /* fast exit for most frequent case */
-       if (!(stat & BUSY_STAT)) {
-               ide_error(drive, "status error", stat);
-               return 1;
-       }
-
-       save_flags(flags);
-       sti();
-       timeout += jiffies;
-       do {
-               if (!((stat = GET_STAT()) & BUSY_STAT)) {
-                       restore_flags(flags);
-                       goto test;
+       udelay(1);      /* spec allows drive 400ns to assert "BUSY" */
+       if ((stat = GET_STAT()) & BUSY_STAT) {
+               save_flags(flags);
+               sti();
+               timeout += jiffies;
+               while ((stat = GET_STAT()) & BUSY_STAT) {
+                       if (jiffies > timeout) {
+                               restore_flags(flags);
+                               ide_error(drive, "status timeout", stat);
+                               return 1;
+                       }
                }
-       } while (jiffies <= timeout);
-
-       restore_flags(flags);
-       ide_error(drive, "status timeout", GET_STAT());
+               restore_flags(flags);
+       }
+       udelay(1);      /* allow status to settle, then read it again */
+       if (OK_STAT((stat = GET_STAT()), good, bad))
+               return 0;
+       ide_error(drive, "status error", stat);
        return 1;
 }
 
@@ -1532,6 +1601,16 @@ static inline void do_request (ide_hwif_t *hwif, struct request *rq)
                                idetape_do_request (drive, rq, block);
                                return;
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+                       case ide_floppy:
+                               idefloppy_do_request (drive, rq, block);
+                               return;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+                       case ide_scsi:
+                               idescsi_do_request (drive, rq, block);
+                               return;
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
                        default:
                                printk("%s: media type %d not supported\n",
@@ -1588,6 +1667,7 @@ void ide_do_request (ide_hwgroup_t *hwgroup)
                                if (rq != NULL && rq->rq_status != RQ_INACTIVE)
                                        goto got_rq;
                        } while ((hwif = hwif->next) != hwgroup->next_hwif);
+                       hwgroup->active = 0;
                        return;         /* no work left for this hwgroup */
                }
        got_rq: 
@@ -1612,6 +1692,7 @@ static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
        if (hwgroup->handler == NULL) {
                ide_hwif_t *hgif = hwgroup->hwif;
                ide_hwif_t *hwif = hgif;
+               hwgroup->active = 1;
                do {
                        disable_irq(hwif->irq);
                } while ((hwif = hwif->next) != hgif);
@@ -1859,13 +1940,8 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
        if (cur_rq == NULL || action == ide_preempt) {
                rq->next = cur_rq;
                bdev->current_request = rq;
-               if (action == ide_preempt) {
+               if (action == ide_preempt)
                        HWGROUP(drive)->rq = NULL;
-               } else
-               if (HWGROUP(drive)->rq == NULL) {  /* is this necessary (?) */
-                       bdev->request_fn();
-                       cli();
-               }
        } else {
                if (action == ide_wait || action == ide_end) {
                        while (cur_rq->next != NULL)    /* find end of list */
@@ -1874,6 +1950,10 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
                rq->next = cur_rq->next;
                cur_rq->next = rq;
        }
+       if (!HWGROUP(drive)->active) {
+               do_hwgroup_request(HWGROUP(drive));
+               cli();
+       }
        if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
                down(&sem);     /* wait for it to be serviced */
        restore_flags(flags);
@@ -1901,6 +1981,14 @@ static int ide_open(struct inode * inode, struct file * filp)
        if (drive->media == ide_tape)
                return idetape_blkdev_open (inode, filp, drive);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+       if (drive->media == ide_floppy)
+               return idefloppy_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+       if (drive->media == ide_scsi)
+               return idescsi_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
        if (drive->removable && drive->usage == 1) {
                byte door_lock[] = {WIN_DOORLOCK,0,0,0};
                struct request rq;
@@ -1940,6 +2028,18 @@ static void ide_release(struct inode * inode, struct file * file)
                        return;
                }
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+               if (drive->media == ide_floppy) {
+                       idefloppy_release (inode, file, drive);
+                       return;
+               }
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+               if (drive->media == ide_scsi) {
+                       idescsi_ide_release (inode, file, drive);
+                       return;
+               }
+#endif /* CONFIG_BLK_DEV_IDESCSI */
                if (drive->removable && !drive->usage) {
                        byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
                        struct request rq;
@@ -1985,13 +2085,14 @@ static int revalidate_disk(kdev_t i_rdev)
                        fsync_dev          (devp);
                        invalidate_inodes  (devp);
                        invalidate_buffers (devp);
+                       set_blocksize(devp, 1024);
                }
                drive->part[p].start_sect = 0;
                drive->part[p].nr_sects   = 0;
        };
 
        drive->part[0].nr_sects = current_capacity(drive);
-       if (drive->media != ide_disk)
+       if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects)
                drive->part[0].start_sect = -1;
        resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
@@ -2029,7 +2130,7 @@ static int ide_ioctl (struct inode *inode, struct file *file,
                case HDIO_GETGEO:
                {
                        struct hd_geometry *loc = (struct hd_geometry *) arg;
-                       if (!loc || drive->media != ide_disk) return -EINVAL;
+                       if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
                        err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
                        if (err) return err;
                        put_user(drive->bios_head, (byte *) &loc->heads);
@@ -2223,6 +2324,14 @@ static int ide_ioctl (struct inode *inode, struct file *file,
                        if (drive->media == ide_tape)
                                return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+                       if (drive->media == ide_floppy)
+                               return idefloppy_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+                       if (drive->media == ide_scsi)
+                               return idescsi_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
                        return -EPERM;
        }
 }
@@ -2237,6 +2346,10 @@ static int ide_check_media_change (kdev_t i_rdev)
        if (drive->media == ide_cdrom)
                return ide_cdrom_check_media_change (drive);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+       if (drive->media == ide_floppy)
+               return idefloppy_media_change (drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
        if (drive->removable) /* for disks */
                return 1;       /* always assume it was changed */
        return 0;
@@ -2307,6 +2420,9 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
        ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
        ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
 
+       if (strstr(id->model, "E X A B Y T E N E S T"))
+               return;
+
 #ifdef CONFIG_BLK_DEV_IDEATAPI
        /*
         * Check for an ATAPI device
@@ -2322,7 +2438,22 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
                }
 #endif /* CONFIG_BLK_DEV_PROMISE */
                switch (type) {
-                       case 0:         /* Early cdrom models used zero */
+                       case 0:
+                               if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+                                       printk("cdrom or floppy?, assuming ");
+                               if (drive->media != ide_cdrom) {
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+                                       printk("FLOPPY drive\n");
+                                       drive->media = ide_floppy;
+                                       if (idefloppy_identify_device(drive, id))
+                                               drive->present = 1;
+                                       return;
+#else
+                                       printk("FLOPPY ");
+                                       break;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+                               }
+                               /* Early cdrom models used zero */
                        case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
                                printk ("CDROM drive\n");
@@ -2361,8 +2492,15 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
                                printk("Type %d - Unknown device\n", type);
                                return;
                }
+#ifdef CONFIG_BLK_DEV_IDESCSI
+               printk("drive - enabling SCSI emulation\n");
+               drive->media = ide_scsi;
+               drive->present = 1;
+               idescsi_setup(drive);
+#else
                drive->present = 0;
                printk("- not supported by this kernel\n");
+#endif /* CONFIG_BLK_DEV_IDESCSI */
                return;
        }
 #endif /* CONFIG_BLK_DEV_IDEATAPI */
@@ -2381,7 +2519,7 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
                    return;
                }
        }
-       
+
        drive->media = ide_disk;
        /* Extract geometry if we did not already have one for the drive */
        if (!drive->present) {
@@ -2431,9 +2569,13 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
 
        (void) current_capacity (drive); /* initialize LBA selection */
 
-       printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
+       if (!strncmp(id->model, "BMI ", 4) &&
+           strstr(id->model, " ENHANCED IDE ") &&
+           drive->select.b.lba)
+               drive->no_geom = 1;
+
+       printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
         drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
-        drive->select.b.lba ? "LBA, " : "",
         drive->bios_cyl, drive->bios_head, drive->bios_sect);
 
        drive->mult_count = 0;
@@ -2607,6 +2749,34 @@ static int do_probe (ide_drive_t *drive, byte cmd)
        return rc;
 }
 
+static void enable_nest (ide_drive_t *drive)
+{
+       unsigned long timeout;
+
+       printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
+       SELECT_DRIVE(HWIF(drive), drive);
+       delay_50ms();
+       OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
+       timeout = jiffies + WAIT_WORSTCASE;
+       do {
+               if (jiffies > timeout) {
+                       printk("failed (timeout)\n");
+                       return;
+               }
+               delay_50ms();
+       } while (GET_STAT() & BUSY_STAT);
+       delay_50ms();
+       if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
+               printk("failed (status = 0x%02x)\n", GET_STAT());
+       else
+               printk("success\n");
+       if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+               (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+       }
+}
+
 /*
  * probe_for_drive() tests for existence of a given drive using do_probe().
  *
@@ -2622,6 +2792,8 @@ static inline byte probe_for_drive (ide_drive_t *drive)
                (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
 #endif /* CONFIG_BLK_DEV_IDEATAPI */
        }
+       if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
+               enable_nest(drive);
        if (!drive->present)
                return 0;                       /* drive not found */
        if (drive->id == NULL) {                /* identification failed? */
@@ -3250,6 +3422,7 @@ static int init_irq (ide_hwif_t *hwif)
                else
                        hwgroup->drive = &hwif->drives[1];
                hwgroup->poll_timeout = 0;
+               hwgroup->active = 0;
                init_timer(&hwgroup->timer);
                hwgroup->timer.function = &timer_expiry;
                hwgroup->timer.data = (unsigned long) hwgroup;
@@ -3353,6 +3526,7 @@ static void probe_for_hwifs (void)
                 */
                ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
                ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
+               ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
 #endif /* CONFIG_BLK_DEV_TRITON */
        }
 #endif /* CONFIG_PCI */
index 98ec3d939870d5ccff4da9394b1d8c905e010db6..5e349a6ba1d93b34498617d232f8711405b93b2f 100644 (file)
@@ -51,7 +51,8 @@ void cmd640_dump_regs (void);
 #endif
 #endif  /* CONFIG_BLK_DEV_CMD640 */
 
-#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
+#if    defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) || \
+       defined(CONFIG_BLK_DEV_IDEFLOPPY) || defined(CONFIG_BLK_DEV_IDESCSI)
 #define CONFIG_BLK_DEV_IDEATAPI 1
 #endif
 
@@ -110,6 +111,9 @@ typedef unsigned char       byte;   /* used everywhere */
 #define IDE_FEATURE_REG                IDE_ERROR_REG
 #define IDE_COMMAND_REG                IDE_STATUS_REG
 #define IDE_ALTSTATUS_REG      IDE_CONTROL_REG
+#define IDE_IREASON_REG                IDE_NSECTOR_REG
+#define IDE_BCOUNTL_REG                IDE_LCYL_REG
+#define IDE_BCOUNTH_REG                IDE_HCYL_REG
 
 #ifdef REALLY_FAST_IO
 #define OUT_BYTE(b,p)          outb((b),(p))
@@ -126,7 +130,7 @@ typedef unsigned char       byte;   /* used everywhere */
 #define BAD_W_STAT             (BAD_R_STAT  | WRERR_STAT)
 #define BAD_STAT               (BAD_R_STAT  | DRQ_STAT)
 #define DRIVE_READY            (READY_STAT  | SEEK_STAT)
-#define DATA_READY             (DRIVE_READY | DRQ_STAT)
+#define DATA_READY             (DRQ_STAT)
 
 /*
  * Some more useful definitions
@@ -285,6 +289,8 @@ struct cdrom_info {
        /* The result of the last successful request sense command
           on this device. */
        struct atapi_request_sense sense_data;
+
+       int max_sectors;
 };
 
 #endif /* CONFIG_BLK_DEV_IDECD */
@@ -293,7 +299,7 @@ struct cdrom_info {
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
 
-typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
+typedef enum {ide_disk, ide_cdrom, ide_tape, ide_floppy, ide_scsi} ide_media_t;
 
 typedef union {
        unsigned all                    : 8;    /* all of the bits together */
@@ -302,7 +308,8 @@ typedef union {
                unsigned recalibrate    : 1;    /* seek to cyl 0      */
                unsigned set_multmode   : 1;    /* set multmode count */
                unsigned set_tune       : 1;    /* tune interface for drive */
-               unsigned reserved       : 4;    /* unused */
+               unsigned mc             : 1;    /* acknowledge media change */
+               unsigned reserved       : 3;    /* unused */
                } b;
        } special_t;
 
@@ -335,7 +342,8 @@ typedef struct ide_drive_s {
 #if FAKE_FDISK_FOR_EZDRIVE
        unsigned remap_0_to_1   : 1;    /* flag: partitioned with ezdrive */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
-       ide_media_t     media;          /* disk, cdrom, tape */
+       unsigned no_geom        : 1;    /* flag: do not set geometry */
+       ide_media_t     media;          /* disk, cdrom, tape, floppy */
        select_t        select;         /* basic drive/head select reg value */
        byte            ctl;            /* "normal" value for IDE_CONTROL_REG */
        byte            ready_stat;     /* min status value for drive ready */
@@ -363,6 +371,12 @@ typedef struct ide_drive_s {
 #ifdef CONFIG_BLK_DEV_IDETAPE
        idetape_tape_t  tape;           /* for ide-tape.c */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+       void *floppy;                   /* for ide-floppy.c */
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+       void *scsi;                     /* for ide-scsi.c */
+#endif /* CONFIG_BLK_DEV_IDESCSI */
        } ide_drive_t;
 
 /*
@@ -465,6 +479,7 @@ typedef struct hwgroup_s {
        struct timer_list       timer;  /* failsafe timer */
        struct request          wrq;    /* local copy of current write rq */
        unsigned long           poll_timeout;   /* timeout value during long polls */
+       int                     active; /* set when servicing requests */
        } ide_hwgroup_t;
 
 /*
@@ -502,6 +517,12 @@ void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
  */
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
 
+/*
+ * This is used for (nearly) all ATAPI data transfers from/to the IDE interface
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+
 /*
  * This is used on exit from the driver, to designate the next irq handler
  * and also to start the safety timer.
@@ -693,6 +714,28 @@ void idetape_register_chrdev (void);
 
 #endif /* CONFIG_BLK_DEV_IDETAPE */
 
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+void idefloppy_setup (ide_drive_t *drive);
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg);
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+int idefloppy_media_change (ide_drive_t *drive);
+unsigned long idefloppy_capacity (ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+
+#ifdef CONFIG_BLK_DEV_IDESCSI
+void idescsi_setup (ide_drive_t *drive);
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
+
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
 #endif /* CONFIG_BLK_DEV_TRITON */
index c91da9c5227c74702f04e7cd7d6265e9022de3d9..6ac1531ea7a190cb206c76c43c021c87404824fb 100644 (file)
@@ -54,17 +54,23 @@ static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs)
 {
        irq_number = irq;
        set_bit(irq, (void *)&irq_bitmap);      /* irq_bitmap |= 1 << irq; */
-       disable_irq(irq);
+       /* This code used to disable the irq. However, the interrupt stub
+        * would then re-enable the interrupt with (potentially) disastrous
+        * consequences
+        */
+       free_irq(irq, dev_id);
        return;
 }
 
 int autoirq_setup(int waittime)
 {
-       int i, mask;
+       int i;
        int timeout = jiffies + waittime;
        int boguscount = (waittime*loops_per_sec) / 100;
 
        irq_handled = 0;
+       irq_bitmap = 0;
+
        for (i = 0; i < 16; i++) {
                if (test_bit(i, &irqs_busy) == 0
                        && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
@@ -72,22 +78,15 @@ int autoirq_setup(int waittime)
        }
        /* Update our USED lists. */
        irqs_used |= ~irq_handled;
-       irq_number = 0;
-       irq_bitmap = 0;
 
        /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
        while (timeout > jiffies  &&  --boguscount > 0)
                ;
 
-       for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
-               if (irq_bitmap & irq_handled & mask) {
-                       irq_handled &= ~mask;
-#ifdef notdef
-                       printk(" Spurious interrupt on IRQ %d\n", i);
-#endif
-                       free_irq(i, NULL);
-               }
-       }
+       irq_handled &= ~irq_bitmap;
+
+       irq_number = 0; /* We are interested in new interrupts from now on */
+
        return irq_handled;
 }
 
@@ -103,6 +102,8 @@ int autoirq_report(int waittime)
                if (irq_number)
                        break;
 
+       irq_handled &= ~irq_bitmap;     /* This eliminates the already reset handlers */
+
        /* Retract the irq handlers that we installed. */
        for (i = 0; i < 16; i++) {
                if (test_bit(i, (void *)&irq_handled))
index 0395606bc36baa08b9f11cfc0461d241851287bb..6e34cd6b58654f2e3d7f2f948780039ce9c81191 100644 (file)
@@ -273,6 +273,9 @@ void lance32_probe1(struct device *dev, char *chipname, int pci_irq_line)
         outw(0x0002, ioaddr+LANCE_ADDR);
        outw(0x0002, ioaddr+LANCE_BUS_IF);
 
+       /* Reset the LANCE - this should prevent any more interrupts from arriving */
+       inw(ioaddr+LANCE_RESET);
+
        if (lance32_debug > 0)
                printk(version);
 
@@ -451,7 +454,7 @@ lance32_start_xmit(struct sk_buff *skb, struct device *dev)
        /* Transmitter timeout, serious problems. */
        if (dev->tbusy) {
                int tickssofar = jiffies - dev->trans_start;
-               if (tickssofar < 20)
+               if (tickssofar < 60)            /* It can take this long to run through the 16 retries */
                        return 1;
                outw(0, ioaddr+LANCE_ADDR);
                printk("%s: transmit timed out, status %4.4x, resetting.\n",
@@ -469,8 +472,9 @@ lance32_start_xmit(struct sk_buff *skb, struct device *dev)
                                           lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
                                           lp->rx_ring[i].msg_length);
                        for (i = 0 ; i < TX_RING_SIZE; i++)
-                               printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+                               printk("%s %08x %04x %04x %08x  ", i & 0x1 ? "" : "\n ",
                                           lp->tx_ring[i].base, -lp->tx_ring[i].length,
+                                          lp->tx_ring[i].status,
                                           lp->tx_ring[i].misc);
                        printk("\n");
                }
@@ -634,6 +638,9 @@ lance32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                        }
 #endif
 
+                       if (dev->tbusy)
+                               dev->trans_start = jiffies; /* We are just starting the next transmit */
+
                        if (lp->tx_full && dev->tbusy
                                && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
                                /* The ring is no longer full, clear tbusy. */
index 324262c2ad938d0f885e179d29e7c0a838337afa..60a22b6e5ab7715798b060254e62f69871c63db9 100644 (file)
@@ -1,7 +1,6 @@
-/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for linux. */
 /*
-   NOTICE: this version works with kernels 1.1.82 and later only!
-       Written 1994,1995 by Donald Becker.
+       Written 1994-1997 by Donald Becker.
 
        This software may be used and distributed according to the terms
        of the GNU Public License, incorporated herein by reference.
        Center of Excellence in Space Data and Information Sciences
           Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-       Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov
-       for late breaking news and exciting develovements.
+       Support and updates available at
+       http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static char *version =
-"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n"
-"        +0.72 4/17/96 "
-"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n"
-"        +0.02 12/15/96 mjacob@feral.com (2.0.27)\n";
+static const char *version = "tulip.c:v0.78 7/25/97 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
-/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */
-#define        TULIP_10TP_PORT         0
-#define        TULIP_100TP_PORT        1
-#define        TULIP_AUI_PORT          1
-#define        TULIP_BNC_PORT          2
-#define        TULIP_MAX_PORT          3
-#define        TULIP_AUTO_PORT         -1
+/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
+#ifdef REVERSE_PROBE_ORDER
+static int reverse_probe = 1;
+#else
+static int reverse_probe = 0;
+#endif
+
+/* Keep the ring sizes a power of two for efficiency.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE   16
+#define RX_RING_SIZE   16
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+#define SKBUFF_RX_COPYBREAK 200
 
-#ifndef        TULIP_PORT
-#define        TULIP_PORT                      TULIP_10TP_PORT
+/* The following example shows how to always use the 10base2 port. */
+#ifdef notdef
+#define TULIP_DEFAULT_MEDIA 1          /* 1 == 10base2 */
+#define TULIP_NO_MEDIA_SWITCH          /* Don't switch from this port */
 #endif
 
 /* Define to force full-duplex operation on all Tulip interfaces. */
 /* #define  TULIP_FULL_DUPLEX 1 */
 
-/* Define to fix port. */
-/* #define  TULIP_FIX_PORT 1 */
-
-/* Define to probe only first detected device */
-/*#define      TULIP_MAX_CARDS 1*/
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((2000*HZ)/1000)
 
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/timer.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -58,8 +73,7 @@ static char *version =
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/delay.h>
-#include <asm/byteorder.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -68,35 +82,116 @@ static char *version =
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>             /* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)                  /* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#ifdef SA_SHIRQ
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
+/* This my implementation of shared IRQs, now only used for 1.2.13. */
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
 /* The total size is unusually large: The 21040 aligns each of its 16
    longword-wide registers on a quadword boundary. */
 #define TULIP_TOTAL_SIZE 0x80
 
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
 /*
                                Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the DECchip 21040 "Tulip", Digital's
-single-chip ethernet controller for PCI, as used on the SMC EtherPower
-ethernet adapter.  It also works with boards based the 21041 (new/experimental)
-and 21140 (10/100mbps).
+This device driver is designed for the DECchip "Tulip", Digital's
+single-chip ethernet controllers for PCI.  Supported members of the family
+are the 21040, 21041, 21140, 21140A and 21142.  These chips are used on
+many PCI boards including the SMC EtherPower series.
 
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-physically possible to shared PCI interrupt lines, the kernel doesn't
-support it. 
+need to be set on the board.  The system BIOS preferably should assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
 
 III. Driver operation
 
 IIIa. Ring buffers
+
 The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-The current driver uses a statically allocated Rx ring of descriptors and
-buffers, and a list of the Tx buffers.
+This driver uses statically allocated rings of Rx and Tx descriptors, set at
+compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
+for the Rx ring buffers at open() time and passes the skb->data field to the
+Tulip as receive data buffers.  When an incoming frame is less than
+SKBUFF_RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+copied to the new skbuff.  When the incoming frame is larger, the skbuff is
+passed directly up the protocol stack and replaced by a newly allocated
+skbuff.
+
+The SKBUFF_RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  For small frames the copying cost is negligible (esp. considering
+that we are pre-loading the cache with immediately useful header
+information).  For large frames the copying cost is non-trivial, and the
+larger copy might flush the cache of useful data.  A subtle aspect of this
+choice is that the Tulip only receives into longword aligned buffers, thus
+the IP header at offset 14 isn't longword aligned for further processing.
+Copied frames are put into the new skbuff at an offset of "+2", thus copying
+has the beneficial effect of aligning the IP header and preloading the
+cache.
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -120,592 +215,807 @@ IV. Notes
 
 Thanks to Duke Kamstra of SMC for providing an EtherPower board.
 
+IVb. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
+http://www.national.com/pf/DP/DP83840.html
+
+IVc. Errata
+
 The DEC databook doesn't document which Rx filter settings accept broadcast
 packets.  Nor does it document how to configure the part to configure the
 serial subsystem for normal (vs. loopback) operation or how to have it
 autoswitch between internal 10baseT, SIA and AUI transceivers.
 
-The databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.   Hmmm, now how is that possible?
-*/
+The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
+
 
 /* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE   4
-#define RX_RING_SIZE   4
 #define PKT_BUF_SZ             1536                    /* Size of each temporary Rx buffer.*/
 
-/* This is a mysterious value that can be written to CSR11 in the 21040
-   to detect a full-duplex frame.  No one knows what it should be, but if
-   left at its default value some 10base2(!) packets trigger a
-   full-duplex-request interrupt. */
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
 #define FULL_DUPLEX_MAGIC      0x6969
 
+#ifndef PCI_VENDOR_ID_DEC              /* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_DEC                      0x1011
+#define PCI_DEVICE_ID_TULIP                    0x0002          /* 21040. */
+#define PCI_DEVICE_ID_TULIP_FAST       0x0009          /* 21140. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS   0x0014          /* 21041. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
+#define PCI_DEVICE_ID_DEC_TULIP_21142  0x0019
+#endif
+
 /* The rest of these values should never change. */
 
-#define PCI_DEVICE_ID_NONE     0xFFFF
-#define        ETHNAMSIZ       8
-#define        ROUND_UP(size, n)       ((size + n - 1) & ~(n - 1))
+static void tulip_timer(unsigned long data);
+
+/* A table describing the chip types. */
+static struct tulip_chip_table {
+  int device_id;
+  char *chip_name;
+  int flags;
+  void (*media_timer)(unsigned long data);
+} tulip_tbl[] = {
+  { PCI_DEVICE_ID_DEC_TULIP, "DS21040 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/
+  { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */
+  {0, 0, 0, 0},
+};
+/* This matches the table above. */
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, };
+
+static const char * const medianame[] = {
+  "10baseT", "10base2", "AUI", "100baseTx",
+  "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+  "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+  "", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
+/* A full-duplex map for above. */
+static const char media_fd[] =
+{0,0,0,0,  0xff,0xff,0,0,  0xff,0,0xff,0x01, 0,0,0xff,0 };
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
+static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, };
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
 enum tulip_offsets {
-                               /* 21040                21041           21140           */
-       CSR0=0,         /* BUS mode                                                             */
-    CSR1=0x08, /* TX poll demand                                               */
-       CSR2=0x10,      /* RX poll demand                                               */
-       CSR3=0x18,      /* RX ring base addr                                    */
-       CSR4=0x20,      /* TX ring base addr                                    */
-       CSR5=0x28,      /* Status                                                               */
-       CSR6=0x30,      /* Command mode                                                 */
-       CSR7=0x38,      /* Interrupt Mask                                               */
-       CSR8=0x40,      /* Missed frame counter                                 */
-       CSR9=0x48,      /* Eth.addrROM  SROM mii        SROM mii        */
-       CSR10=0x50,     /* Diagn.               boot ROM        -                       */
-       CSR11=0x58,     /* Full duplex  G.P. timer      G.P. timer      */
-       CSR12=0x60,     /* SIA status                           G.P.            */
-       CSR13=0x68,     /* SIA connectivity                     -                       */
-       CSR14=0x70,     /* SIA TX/RX                            -                       */
-       CSR15=0x78      /* SIA general                          watchdog        */
-};
+       CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+       CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+       CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
 
-/* description of CSR0 bus mode register */
-#define        TBMOD_RESERVED          0xfff80000      /* I don't know */
-#define        TBMOD_RESET                     0x00000001
-#define        TBMOD_BIGENDIAN         0x00000080
-/*
-          Cache alignment bits 15:14        Burst length 13:8
-       0000    No alignment  0x00000000 unlimited              0800 8 longwords
-               4000    8  longwords            0100 1 longword         1000 16 longwords
-               8000    16 longwords            0200 2 longwords        2000 32 longwords
-               C000    32  longwords           0400 4 longwords
-*/
-#define        TBMOD_ALIGN0            0x00000000      /* no cache alignment */
-#define        TBMOD_ALIGN8            0x00004000      /* 8 longwords */
-#define        TBMOD_ALIGN16           0x00008000
-#define        TBMOD_ALIGN32           (TBMOD_ALIGN8|TBMOD_ALIGN16)
-#define        TBMOD_BURST0            0x00000000      /* unlimited=rx buffer size */
-#define        TBMOD_BURST1            0x00000100      /* 1 longwords */
-#define        TBMOD_BURST2            0x00000200
-#define        TBMOD_BURST4            0x00000400
-#define        TBMOD_BURST8            0x00000800
-#define        TBMOD_BURST16           0x00001000
-#define        TBMOD_BURST32           0x00002000
-
-/* description of CSR1 Tx poll demand register */
-/* description of CSR2 Rx poll demand register */
-#define        TPOLL_START                     0x00000001      /* ? */
-#define        TPOLL_TRIGGER           0x00000000      /* ? */
-
-/* description of CSR5 status register from de4x5.h */
-#define        TSTAT_BUSERROR          0x03800000
-#define        TSTAT_SYSERROR          0x00002000
-#define        TSTAT_TxSTAT            0x00700000
-#define        TSTAT_RxSTAT            0x000e0000
-#define        TSTAT_LKFAIL            0x00001000
-#define        TSTAT_NORINTR           0x00010000      /* Normal interrupt */
-#define        TSTAT_ABNINTR           0x00008000      /* Abnormal interrupt */
-#define        TSTAT_RxMISSED          0x00000100      /* Rx frame missed */
-#define        TSTAT_RxUNABL           0x00000080
-#define        TSTAT_RxINTR            0x00000040
-#define        TSTAT_LKPASS            0x00000010
-#define        TSTAT_TEXPIRED          0x00000800      /* Timer Expired */
-#define        TSTAT_TxTOUT            0x00000008
-#define        TSTAT_TxUNABL           0x00000004
-#define        TSTAT_TxINTR            0x00000001
-#define        TSTAT_CLEARINTR         0x0001ffff      /* clear all interrupt sources */
-
-/* description of CSR6 command mode register */
-#define        TCMOD_SCRM                      0x01000000      /* scrambler mode */
-#define        TCMOD_PCS                       0x00800000      /* PCS function */
-#define        TCMOD_TxTHMODE          0x00400000      /* Tx threshold mode */
-#define        TCMOD_SW100TP           0x00040000  /* 21140: 100MB */
-#define        TCMOD_CAPTURE           0x00020000      /* capture effect */
-#define        TCMOD_FULLDUPLEX        0x00000200
-#define TCMOD_TH128                    0x00008000      /* 10 - 128 bytes threshold */
-#define        TCMOD_TxSTART           0x00002000
-#define        TCMOD_RxSTART           0x00000002
-#define        TCMOD_ALLMCAST          0x00000080      /* pass all multicast */
-#define        TCMOD_PROMISC           0x00000040      /* promisc */
-#define        TCMOD_BOFFCOUNTER       0x00000020      /* backoff counter */
-#define        TCMOD_INVFILTER         0x00000010      /* invert filtering */
-#define        TCMOD_HONLYFILTER       0x00000004      /* hash only filtering */
-#define TCMOD_HPFILTER         0x00000001      /* hash/perfect Rx filtering */
-#define        TCMOD_MODEMASK          (TCMOD_ALLMCAST|TCMOD_PROMISC)
-#define        TCMOD_FILTERMASK        (TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER)
-#define        TCMOD_TRxSTART          (TCMOD_TxSTART|TCMOD_RxSTART)
-#define        TCMOD_BASE                      (TCMOD_CAPTURE|TCMOD_BOFFCOUNTER)
-#define        TCMOD_10TP                      (TCMOD_TxTHMODE|TCMOD_BASE)
-#define        TCMOD_100TP                     (TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE)
-#define        TCMOD_AUTO                      (TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP)
-
-/* description of CSR7 interrupt mask register */
-#define        TINTR_ENABLE            0xFFFFFFFF
-#define        TINTR_DISABLE           0x00000000
-
-/* description of CSR11 G.P. timer (21041/21140) register */
-#define        TGEPT_COUNT                     0x0001FFFF
-
-/* description of CSR12 SIA status(2104x)/GP(21140) register */
-#define        TSIAS_CONERROR          0x00000002      /* connection error */
-#define        TSIAS_LNKERROR          0x00000004      /* link error */
-#define TSIAS_ACTERROR         0x00000200  /* port Rx activity */
-#define TSIAS_RxACTIVE         0x00000100  /* port Rx activity */
-
-#define        TGEPR_LK10NG            0x00000080      /* 10Mbps N.G. (R) */
-#define        TGEPR_LK100NG           0x00000040      /* 100Mbps N.G. (R) */
-#define        TGEPR_DETECT            0x00000020      /* detect signal (R) */
-#define        TGEPR_HALFDUPLEX        0x00000008      /* half duplex (W) */
-#define        TGEPR_PHYLOOPBACK       0x00000004      /* PHY loopback (W) */
-#define        TGEPR_FORCEALED         0x00000002      /* force activity LED on (W) */
-#define        TGEPR_FORCE100          0x00000001      /* force 100Mbps mode */
-
-/* description of CSR13 SIA connectivity register */
-#define        TSIAC_OUTEN                     0x0000e000      /* 21041: Output enable */
-#define TSIAC_SELED                    0x00000f00      /* 21041: AUI or TP with LEDs */
-#define        TSIAC_INEN                      0x00001000      /* 21041: Input enable */
-#define        TSIAC_NO10TP            0x00000008      /* 10baseT(0) or not(1) */
-#define TSIAC_CONFIG           0x00000004      /* Configuration */
-#define        TSIAC_SWRESET           0x00000001      /* 21041: software reset */
-#define        TSIAC_RESET                     0x00000000      /* reset */
-#define        TSIAC_C21041            (TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET)
-#define        TSIAC_C21040            TSIAC_CONFIG
-
-/* description of CSR14 SIA TX/RX register */
-#define        TSIAX_NO10TP            0x0000f73d
-#define        TSIAX_10TP                      0x0000ff3f
-
-/* description of CSR15 SIA general register */
-#define        TSIAG_SWBNCAUI          0x00000008 /* BNC(0) or AUI(1) */
-#define        TSIAG_BNC                       0x00000006
-#define        TSIAG_AUI                       (TSIAG_BNC|TSIAG_SWBNCAUI)
-#define        TSIAG_10TP                      0x00000000
-
-/* description of rx_ring.status */
-#define        TRING_OWN                       0x80000000      /* Owned by chip */
-#define        TRING_CLEAR                     0x00000000      /* clear */
-#define        TRING_ERROR                     0x00008000      /* error summary */
-#define        TRING_ETxTO                     0x00004000      /* Tx time out */
-#define        TRING_ELCOLL            0x00000200      /* late collision */
-#define        TRING_EFCOLL            0x00000100      /* fatal collision */
-#define        TRING_ELCARR            0x00000800      /* carrier lost */
-#define        TRING_ENCARR            0x00000400      /* no carrier */
-#define        TRING_ENOHB                     0x00000080      /* heartbeat fail */
-#define        TRING_ELINK                     0x00000004      /* link fail */
-#define        TRING_EUFLOW            0x00000002      /* underflow */
-
-#define        TRING_ELEN                      0x00004000      /* length error */
-#define        TRING_FDESC                     0x00000200      /* first descriptor */
-#define        TRING_LDESC                     0x00000100      /* last descriptor */
-#define        TRING_ERUNT                     0x00000800      /* runt frame */
-#define        TRING_ELONG                     0x00000080      /* frame too long */
-#define        TRING_EWATCHDOG         0x00000010      /* receive watchdog */
-#define        TRING_EDRBIT            0x00000004      /* dribble bit */
-#define        TRING_ECRC                      0x00000002      /* CRC error */
-#define        TRING_EOVERFLOW         0x00000001      /* overflow */
-
-#define        TRING_RxDESCMASK        (TRING_FDESC|TRING_LDESC)
-#define        TRING_RxLENGTH          (TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG)
-#define        TRING_RxFRAME           (TRING_EDRBIT)
-#define        TRING_RxCRC                     (TRING_ECRC)
-#define        TRING_RxFIFO            (TRING_EOVERFLOW)
-#define        TRING_TxABORT           (TRING_ETxTO|TRING_EFCOLL|TRING_ELINK)
-#define        TRING_TxCARR            (TRING_ELCARR|TRING_ENCARR)
-#define        TRING_TxWINDOW          (TRING_ELCOLL)
-#define        TRING_TxFIFO            (TRING_EUFLOW)
-#define        TRING_TxHEARTBEAT       (TRING_ENOHB)
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
        s32 status;
        s32 length;
-       u32 buffer1, buffer2;                   /* We use only buffer 1.  */
+       u32 buffer1, buffer2;
 };
 
 struct tulip_tx_desc {
        s32 status;
        s32 length;
-       u32 buffer1, buffer2;                   /* We use only buffer 1.  */
+       u32 buffer1, buffer2;                           /* We use only buffer 1.  */
+};
+
+struct medialeaf {
+       u8 type;
+       u8 media;
+       unsigned char *leafdata;
+};
+
+struct mediatable {
+       u16 defaultmedia;
+       u8 leafcount, csr12dir;                         /* General purpose pin directions. */
+       unsigned has_mii:1;
+       struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+       struct mediainfo *next;
+       int info_type;
+       int index;
+       struct non_mii { char media; unsigned char csr12val; char bitnum, flags;}  non_mii;
+       unsigned char *info;
 };
 
 struct tulip_private {
+       char devname[8];                        /* Used only for kernel debugging. */
+       const char *product_name;
+       struct device *next_module;
        struct tulip_rx_desc rx_ring[RX_RING_SIZE];
        struct tulip_tx_desc tx_ring[TX_RING_SIZE];
        /* The saved address of a sent-in-place packet/buffer, for skfree(). */
        struct sk_buff* tx_skbuff[TX_RING_SIZE];
-       char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ];
-       /* temporary Rx buffers. */
+       /* The addresses of receive-in-place skbuffs. */
+       struct sk_buff* rx_skbuff[RX_RING_SIZE];
+       char *rx_buffs;                         /* Address of temporary Rx buffers. */
+       int setup_frame[48];            /* Pseudo-Tx frame to init address table. */
+       int chip_id;
+       int revision;
        struct enet_statistics stats;
-       int setup_frame[48];    /* Pseudo-Tx frame to init address table. */
-       void (*port_select)(struct device *dev);
-       int (*port_fail)(struct device *dev);
-       struct device *next_module;
-       char *signature;
+       struct timer_list timer;        /* Media selection timer. */
        unsigned int cur_rx, cur_tx;            /* The next free ring entry */
        unsigned int dirty_rx, dirty_tx;        /* The ring entries to be free()ed. */
        unsigned int tx_full:1;                         /* The Tx queue is full. */
        unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
-       unsigned int port_fix:1;                        /* Fix if_port to specified port. */
+       unsigned int default_port:4;            /* Last dev->if_port value. */
+       unsigned int media2:4;                          /* Secondary monitored media port. */
+       unsigned int medialock:1;                       /* Don't sense media type. */
+       unsigned int mediasense:1;                      /* Media sensing in progress. */
+       unsigned int csr6;                                      /* Current CSR6 control settings. */
+       unsigned char eeprom[128];                      /* Serial EEPROM contents. */
+       struct mediatable *mtable;
+       int cur_index;                                          /* Current media index. */
+       int pad0, pad1;                                         /* Used for 8-byte alignment */
 };
 
-struct eeprom {
-    union {
-               struct { /* broken EEPROM structure */
-                       u_char addr[ETH_ALEN];
-               } ng;
-               struct { /* DEC EtherWorks
-                                       and other cards which have correct eeprom structure */
-                       u_char dum1[20];
-                       u_char addr[ETH_ALEN];
-               } ok;
-    } hw;
-#define        ng_addr hw.ng.addr
-#define        ok_addr hw.ok.addr
-#define        EE_SIGNLEN      14              /* should be 102 ? */
-       u_char sign[EE_SIGNLEN];
-};
+#ifdef MODULE
+/* Used to pass the full-duplex flag, etc. */
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#endif
 
-static int read_eeprom(int ioaddr, struct eeprom *eepp);
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+                                                                  int chip_id, int options);
+static void parse_eeprom(struct device *dev);
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
+static void tulip_timer(unsigned long data);
+static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
 static struct enet_statistics *tulip_get_stats(struct device *dev);
+#ifdef NEW_MULTICAST
 static void set_multicast_list(struct device *dev);
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 
-#define        generic21140_fail       NULL
-static void generic21040_select(struct device *dev);
-static void generic21140_select(struct device *dev);
-static void generic21041_select(struct device *dev);
-static void auto21140_select(struct device *dev);
-static void cogent21140_select(struct device *dev);
-static int generic21040_fail(struct device *dev);
-static int generic21041_fail(struct device *dev);
+\f
 
 #ifdef MODULE
 /* A list of all installed Tulip devices, for removing the driver module. */
 static struct device *root_tulip_dev = NULL;
 #endif
 
-static struct {
-       void (*port_select)(struct device *dev);
-       int (*port_fail)(struct device *dev);
-       unsigned int vendor_id, device_id;
-       char *signature;
-       unsigned int array:1;
-} cardVendor[] = {
-       {generic21140_select, generic21140_fail,
-                0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0},
-       {generic21041_select, generic21041_fail,
-                0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0},
-       {generic21040_select, generic21040_fail,
-                0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0},
-       {auto21140_select, generic21140_fail,
-                0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0},
-       {cogent21140_select, generic21140_fail,
-                0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0},
-       {generic21040_select, generic21040_fail,
-               0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1},
-       {generic21140_select, generic21140_fail,
-                0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0},
-       {generic21041_select, generic21041_fail,
-                0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0},
-       {generic21040_select, generic21040_fail,
-                0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0},
-       {generic21040_select, generic21040_fail,
-                0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0},
-       {generic21040_select, generic21040_fail,
-                0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1},
-       {generic21040_select, generic21040_fail,
-                0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0},
-       {generic21040_select, generic21040_fail,
-                0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0},
-       {generic21140_select, generic21140_fail,
-                0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0},
-       {generic21041_select, generic21041_fail,
-                0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0},
-       {NULL, NULL, 0, 0, "Unknown", 0}
-};
+/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
+   but now receives directly into full-sized skbuffs that are allocated
+   at open() time.
+   This allows the probe routine to use the old driver initialization
+   interface. */
 
-\f
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK   0x02    /* EEPROM shift clock. */
-#define EE_CS                  0x01    /* EEPROM chip select. */
-#define EE_DATA_WRITE  0x04    /* EEPROM chip data in. */
-#define EE_WRITE_0             0x01
-#define EE_WRITE_1             0x05
-#define EE_DATA_READ   0x08    /* EEPROM chip data out. */
-#define EE_ENB                 (0x4800 | EE_CS)
+int tulip_probe(struct device *dev)
+{
+       int cards_found = 0;
+       static int pci_index = 0;       /* Static, for multiple probe calls. */
+
+       /* Ideally we would detect all network cards in slot order.  That would
+          be best done a central PCI probe dispatch, which wouldn't work
+          well with the current structure.  So instead we detect just the
+          Tulip cards in slot order. */
+
+       if (pcibios_present()) {
+               unsigned char pci_bus, pci_device_fn;
+
+               for (;pci_index < 0xff; pci_index++) {
+                       unsigned char pci_irq_line, pci_latency;
+                       unsigned short pci_command, vendor, device;
+                       unsigned int pci_ioaddr, chip_idx = 0;
+
+                       if (pcibios_find_class
+                               (PCI_CLASS_NETWORK_ETHERNET << 8,
+                                reverse_probe ? 0xfe - pci_index : pci_index,
+                                &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+                               if (reverse_probe)
+                                       continue;
+                               else
+                                       break;
+                       pcibios_read_config_word(pci_bus, pci_device_fn,
+                                                                        PCI_VENDOR_ID, &vendor);
+                       pcibios_read_config_word(pci_bus, pci_device_fn,
+                                                                        PCI_DEVICE_ID, &device);
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                        PCI_INTERRUPT_LINE, &pci_irq_line);
+                       pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
+                       /* Remove I/O space marker in bit 0. */
+                       pci_ioaddr &= ~3;
+
+                       if (vendor != PCI_VENDOR_ID_DEC)
+                               continue;
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD   (5 << 6)
-#define EE_READ_CMD            (6 << 6)
-#define EE_ERASE_CMD   (7 << 6)
+                       for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
+                               if (device == tulip_tbl[chip_idx].device_id)
+                                       break;
+                       if (tulip_tbl[chip_idx].chip_name == 0) {
+                               printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
+                                          " not configured.\n", device);
+                               continue;
+                       }
+                       if (tulip_debug > 2)
+                               printk("Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
+                                          pci_ioaddr, pci_irq_line);
+
+                       if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+                               continue;
 
 #ifdef MODULE
-static int if_port=TULIP_AUTO_PORT;
-#ifdef TULIP_FULL_DUPLEX
-static int full_duplex=1;
+                       dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+                                                options[cards_found]);
 #else
-static int full_duplex=0;
-#endif
+                       dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+                                                dev ? dev->mem_start : 0);
 #endif
 
-#define        tio_write(val, port)    outl(val, ioaddr + port)
-#define        tio_read(port)                  inl(ioaddr + port)
+                       if (dev) {
+                         /* Get and check the bus-master and latency values. */
+                         pcibios_read_config_word(pci_bus, pci_device_fn,
+                                                                          PCI_COMMAND, &pci_command);
+                         if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                               printk("  PCI Master Bit has not been set! Setting...\n");
+                               pci_command |= PCI_COMMAND_MASTER;
+                               pcibios_write_config_word(pci_bus, pci_device_fn,
+                                                                                 PCI_COMMAND, pci_command);
+                         }
+                         pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                          PCI_LATENCY_TIMER, &pci_latency);
+                         if (pci_latency < 10) {
+                               printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+                                          "  Setting to 64 clocks.\n", pci_latency);
+                               pcibios_write_config_byte(pci_bus, pci_device_fn,
+                                                                                 PCI_LATENCY_TIMER, 64);
+                         } else if (tulip_debug > 1)
+                               printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+                         /* Bring the 21143 out power-down mode. */
+                         if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+                               pcibios_write_config_dword(pci_bus, pci_device_fn,
+                                                                                       0x40, 0x40000000);
+                         dev = 0;
+                         cards_found++;
+                       }
+               }
+       }
 
-static void inline
-tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15)
-{
-       tio_write(0,CSR13);
-       tio_write(val15,CSR15);
-       tio_write(val14,CSR14);
-       tio_write(val13,CSR13);
+#if defined (MODULE)
+       return cards_found;
+#else
+       return 0;
+#endif
 }
 
-/*
-   card_type returns 1 if the card is 'etherarray'
-*/
-
-static int
-card_type(struct tulip_private *tp, int device_id, int vendor_id)
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+                                                                  int chip_id, int options)
 {
-       int n;
-
-       for (n = 0; cardVendor[n].device_id; n ++)
-               if (cardVendor[n].device_id == device_id
-                       && (cardVendor[n].vendor_id == vendor_id
-                               || cardVendor[n].vendor_id == 0)) break;
-       tp->port_select = cardVendor[n].port_select;
-       tp->port_fail = cardVendor[n].port_fail;
-       tp->signature = cardVendor[n].signature;
-       return(cardVendor[n].array ? 1: 0);
-}
+       static int did_version = 0;                     /* Already printed version info. */
+       struct tulip_private *tp;
+       /* See note below on the multiport cards. */
+       static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+       static int last_irq = 0;
+       int i;
+       unsigned short sum;
 
-static int
-read_eeprom(int ioaddr, struct eeprom *eepp)
-{
-    int i, n;
-    unsigned short val = 0;
-    int read_cmd = EE_READ_CMD;
-    u_char *p=(u_char *)eepp;
-
-       for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) {
-               tio_write(EE_ENB & ~EE_CS, CSR9);
-               tio_write(EE_ENB, CSR9);
-
-               /* Shift the read command bits out. */
-               for (i = 10; i >= 0; i--) {
-                       short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-                       tio_write(EE_ENB | dataval, CSR9);
-                       udelay(100);
-                       tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9);
-                       udelay(150);
-                       tio_write(EE_ENB | dataval, CSR9);
-                       udelay(250);
+       if (tulip_debug > 0  &&  did_version++ == 0)
+               printk(version);
+
+       dev = init_etherdev(dev, 0);
+
+       printk("%s: DEC %s at %#3x,",
+                  dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
+
+       /* Stop the chip's Tx and Rx processes. */
+       outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+       /* Clear the missed-packet counter. */
+       (volatile)inl(ioaddr + CSR8);
+
+       if (chip_id == DC21041) {
+               if (inl(ioaddr + CSR9) & 0x8000) {
+                       printk(" 21040 compatible mode,");
+                       chip_id = DC21040;
+               } else {
+                       printk(" 21041 mode,");
+               }
+       }
+
+       /* The station address ROM is read byte serially.  The register must
+          be polled, waiting for the value to be read bit serially from the
+          EEPROM.
+          */
+       sum = 0;
+       if (chip_id == DC21040) {
+               outl(0, ioaddr + CSR9);         /* Reset the pointer with a dummy write. */
+               for (i = 0; i < 6; i++) {
+                       int value, boguscnt = 100000;
+                       do
+                               value = inl(ioaddr + CSR9);
+                       while (value < 0  && --boguscnt > 0);
+                       dev->dev_addr[i] = value;
+                       sum += value & 0xff;
                }
-               tio_write(EE_ENB, CSR9);
-
-               for (i = 16; i > 0; i--) {
-                       tio_write(EE_ENB | EE_SHIFT_CLK, CSR9);
-                       udelay(100);
-                       val = (val << 1)
-                               | ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0);
-                       tio_write(EE_ENB, CSR9);
-                       udelay(100);
+       } else {        /* Must be a new chip, with a serial EEPROM interface. */
+               /* We read the whole EEPROM, and sort it out later.  DEC has a
+                  specification _Digital Semiconductor 21X4 Serial ROM Format_
+                  but early vendor boards just put the address in the first six
+                  EEPROM locations. */
+               unsigned char ee_data[128];
+               int sa_offset = 0;
+
+               for (i = 0; i < sizeof(ee_data)/2; i++)
+                       ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+
+               /* Detect the simple EEPROM format by the duplicated station addr. */
+               for (i = 0; i < 8; i ++)
+                       if (ee_data[i] != ee_data[16+i])
+                               sa_offset = 20;
+               for (i = 0; i < 6; i ++) {
+                       dev->dev_addr[i] = ee_data[i + sa_offset];
+                       sum += ee_data[i + sa_offset];
                }
+       }
+       /* On the Zynx 315 Etherarray and other multiport boards only the
+          first Tulip has an EEPROM.
+          The addresses of the subsequent ports are derived from the first.
+          Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+          that here as well. */
+       if (sum == 0  || sum == 6*0xff) {
+               printk(" EEPROM not present,");
+               for (i = 0; i < 5; i++)
+                       dev->dev_addr[i] = last_phys_addr[i];
+               dev->dev_addr[i] = last_phys_addr[i] + 1;
+               irq = last_irq;
+       }
+       for (i = 0; i < 6; i++)
+               printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
+       printk(", IRQ %d\n", irq);
+       last_irq = irq;
 
-               /* Terminate the EEPROM access. */
-               tio_write(EE_ENB & ~EE_CS, CSR9);
-               *p ++ = val;
-               *p ++ = val >> 8;
-    }
-       /* broken eeprom ? */
-       p = (u_char *)eepp;
-       for (i = 0; i < 8; i ++)
-               if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0);
-       return(-1); /* broken */
-}
+       /* We do a request_region() only to register /proc/ioports info. */
+       request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
 
-/* Is this required ? */
-static int
-generic21040_fail(struct device *dev)
-{
-       int ioaddr = dev->base_addr;
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
 
-       return(tio_read(CSR12) & TSIAS_CONERROR);
-}
+       /* Make certain the data structures are quadword aligned. */
+       tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+       memset(tp, 0, sizeof(*tp));
+       dev->priv = tp;
 
-static int
-generic21041_fail(struct device *dev)
-{
-       int ioaddr = dev->base_addr;
-       u32 csr12 = tio_read(CSR12);
+#ifdef MODULE
+       tp->next_module = root_tulip_dev;
+       root_tulip_dev = dev;
+#endif
 
-       return((!(csr12 & TSIAS_CONERROR)
-                       || !(csr12 & TSIAS_LNKERROR)) ? 0: 1);
-}
+       tp->chip_id = chip_id;
 
-static void
-generic21040_select(struct device *dev)
-{
-       int ioaddr = dev->base_addr;
-       const char *media;
+#ifdef TULIP_FULL_DUPLEX
+       tp->full_duplex = 1;
+#endif
+#ifdef TULIP_DEFAULT_MEDIA
+       tp->default_port = TULIP_DEFAULT_MEDIA;
+#endif
+#ifdef TULIP_NO_MEDIA_SWITCH
+       tp->medialock = 1;
+#endif
 
-       dev->if_port &= 3;
-       switch (dev->if_port)
-       {
-       case TULIP_10TP_PORT:
-               media = "10baseT";
-               break;
-       case TULIP_AUI_PORT:
-               media = "AUI";
+       /* The lower four bits are the media type. */
+       if (options > 0) {
+               tp->full_duplex = (options & 16) ? 1 : 0;
+               tp->default_port = options & 15;
+               if (tp->default_port)
+                 tp->medialock = 1;
+       }
+
+       /* The Tulip-specific entries in the device structure. */
+       dev->open = &tulip_open;
+       dev->hard_start_xmit = &tulip_start_xmit;
+       dev->stop = &tulip_close;
+       dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_MULTICAST
+       dev->set_multicast_list = &set_multicast_list;
+#endif
+
+       /* This is logically part of probe1(), but too complex to write inline. */
+       if (chip_id != DC21040)
+               parse_eeprom(dev);
+
+       /* Reset the xcvr interface and turn on heartbeat. */
+       switch (chip_id) {
+       case DC21041:
+               outl(0x00000000, ioaddr + CSR13);
+               outl(0xFFFFFFFF, ioaddr + CSR14);
+               outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+               outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6);
+               outl(0x0000EF05, ioaddr + CSR13);
                break;
-       case TULIP_BNC_PORT:
-               media = "BNC";
+       case DC21140:  case DC21142:
+               if (tp->mtable)
+                       outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
                break;
-       default:
-               media = "unknown type";
+       case DC21040:
+               outl(0x00000000, ioaddr + CSR13);
+               outl(0x00000004, ioaddr + CSR13);
                break;
        }
-       printk("%s: enabling %s port.\n", dev->name, media);
-       /* Set the full duplex match frame. */
-       tio_write(FULL_DUPLEX_MAGIC, CSR11);
-       tio_write(TSIAC_RESET, CSR13);
-       /* Reset the serial interface */
-       tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13);
-}
 
-#if 0
-static void
-generic_timer(struct device *dev, u32 count)
+       return dev;
+}
+\f
+/* Serial EEPROM section. */
+/* The main routine to parse the very complicated SROM structure.
+   Search www.digital.com for "21X4 SROM" to get details.
+   This code is very complex, and will require changes to support
+   additional cards, so I'll be verbose about what is going on.
+   */
+
+/* Known cards that have old-style EEPROMs. */
+static struct fixups {
+  char *name;
+  unsigned char addr0, addr1, addr2;
+  u16 newtable[32];                            /* Max length below. */
+} eeprom_fixups[] = {
+  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+                                                 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+                                                          0x0000, 0x009E, /* 10baseT */
+                                                          0x0903, 0x006D, /* 100baseTx */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+                                                                0x0103, 0x006D, /* 100baseTx */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+                                                          0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+                                                          0x0000, 0x009E, /* 10baseT */
+                                                          0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
+                                                       0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+                                                       0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
+                                                       0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+   }},
+  {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 non-MII PHY", "21142 MII PHY", };
+
+#define EEPROM_SIZE 128
+static void parse_eeprom(struct device *dev)
 {
+       /* The last media info list parsed, for multiport boards.  */
+       static struct mediatable *last_mediatable = NULL;
+       static unsigned char *last_ee_data = NULL;
+       static controller_index = 0;
+       struct tulip_private *tp = (struct tulip_private *)dev->priv;
        int ioaddr = dev->base_addr;
+       unsigned char *ee_data = tp->eeprom;
+       int i;
 
-       tio_write(count, CSR11);
-       while (tio_read(CSR11) & TGEPT_COUNT);
-}
-#endif
+       {
+         static int done_did_that = 0;
+         if (done_did_that++ == 0)
+               printk("\n  THIS IS AN ALPHA TEST DRIVER.\n"
+                          "  The following verbose information is emitted for\n"
+                          "  bug reports on media selection.\n");
+       }
+       tp->mtable = 0;
+       for (i = 0; i < EEPROM_SIZE/2; i++)
+         ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
 
-static void
-generic21041_select(struct device *dev)
-{
-       int ioaddr = dev->base_addr;
-       u32 tsiac = TSIAC_C21041;
-       u32 tsiax = TSIAX_10TP;
-       u32 tsiag = TSIAG_10TP;
-
-       switch(dev->if_port) {
-       case TULIP_AUI_PORT:
-               tsiac |= TSIAC_NO10TP;
-               tsiax = TSIAX_NO10TP;
-               tsiag = TSIAG_AUI;
-               break;
-       case TULIP_BNC_PORT:
-               tsiac |= TSIAC_NO10TP;
-               tsiax = TSIAX_NO10TP;
-               tsiag = TSIAG_BNC;
-               break;
-       default:
-               dev->if_port = TULIP_10TP_PORT;
+       /* Detect an old-style (SA only) EEPROM layout:
+          memcmp(eedata, eedata+16, 8). */
+       for (i = 0; i < 8; i ++)
+         if (ee_data[i] != ee_data[16+i])
                break;
+       if (i >= 8) {
+         if (ee_data[0] == 0xff) {
+               if (last_mediatable) {
+                 controller_index++;
+                 printk("%s: Controller %d of multiport board.\n",
+                                dev->name, controller_index);
+                 tp->mtable = last_mediatable;
+                 ee_data = last_ee_data;
+                 goto subsequent_board;
+               } else
+               printk("%s:  Missing EEPROM, this device may not work correctly!\n",
+                          dev->name);
+               return;
+         }
+         /* Do a fix-up based on the vendor half of the station address prefix. */
+         for (i = 0; eeprom_fixups[i].name; i++) {
+               if (dev->dev_addr[0] == eeprom_fixups[i].addr0
+                       &&  dev->dev_addr[1] == eeprom_fixups[i].addr1
+                       &&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+                 if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
+                         i++;                  /* An Accton EN1207, not an outlaw Maxtech. */
+                 memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+                                sizeof(eeprom_fixups[i].newtable));
+                 printk("\n%s: Old format EEPROM on '%s' board.  Using substitute"
+                                " media control info.\n",
+                                dev->name, eeprom_fixups[i].name);
+                 break;
+               }
+         }
+         if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+               printk("\n %s: Old style EEPROM -- no media selection information.\n",
+                          dev->name);
+               return;
+         }
+       }
+       if (tulip_debug > 1) {
+         printk("\nread_eeprom:");
+         for (i = 0; i < 64; i++) {
+               printk("%c%4.4x", (i & 7) == 0 ? '\n':' ',
+                          read_eeprom(ioaddr, i));
+         }
+         printk("\n");
+       }
+       
+       controller_index = 0;
+       if (ee_data[19] > 1) {          /* Multiport board. */
+               last_ee_data = ee_data;
+       }
+subsequent_board:
+
+       if (tp->chip_id == DC21041) {
+               unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
+               short media = *(u16 *)p;
+               int count = p[2];
+
+               printk("%s:21041 Media information at %d, default media %4.4x"
+                          " (%s).\n", dev->name, ee_data[27], media,
+                          media & 0x0800 ? "Autosense" : medianame[media & 15]);
+               for (i = 0; i < count; i++) {
+                       unsigned char media_code = p[3 + i*7];
+                       unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1];
+                       printk("%s:  21041 media %2.2x (%s),"
+                                  " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
+                                  dev->name, media_code & 15, medianame[media_code & 15],
+                                  csrvals[0], csrvals[1], csrvals[2]);
+               }
+       } else {
+               unsigned char *p = (void *)ee_data + ee_data[27];
+               unsigned char csr12dir = 0;
+               int count;
+               struct mediatable *mtable;
+               short media = *((u16 *)p)++;
+
+               if (tp->chip_id == DC21140)
+                       csr12dir = *p++;
+               count = *p++;
+               mtable = (struct mediatable *)
+                       kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
+                                       GFP_KERNEL);
+               if (mtable == NULL)
+                       return;                         /* Horrible, impossible failure. */
+               last_mediatable = tp->mtable = mtable;
+               mtable->defaultmedia = media;
+               mtable->leafcount = count;
+               mtable->csr12dir = csr12dir;
+               mtable->has_mii = 0;
+
+               printk("%s: EEPROM default media type %s.\n", dev->name,
+                          media & 0x0800 ? "Autosense" : medianame[media & 15]);
+               for (i = 0; i < count; i++) {
+                       struct medialeaf *leaf = &mtable->mleaf[i];
+                       
+                       if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+                               leaf->type = 0;
+                               leaf->media = p[0] & 0x3f;
+                               leaf->leafdata = p;
+                               p += 4;
+                       } else {
+                               leaf->type = p[1];
+                               if (p[1] & 1) {
+                                       mtable->has_mii = 1;
+                                       leaf->media = 11;
+                               } else
+                                       leaf->media = p[2] & 0x0f;
+                               leaf->leafdata = p + 2;
+                               p += (p[0] & 0x3f) + 1;
+                       }
+                       if (tulip_debug > 1  &&  leaf->media == 11) {
+                               unsigned char *bp = leaf->leafdata;
+                               printk("%s:  MII interface PHY %d, setup/reset sequences"
+                                          " %d/%d long, capabilities %2.2x %2.2x.\n",
+                                          dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+                                          bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+                               if (tulip_debug > 2) { /* DEBUG only, should be > 3 */
+                                       int mii_reg;
+                                       printk("%s:  MII xcvr control registers:", dev->name);
+                                       for (mii_reg = 0; mii_reg < 32; mii_reg++)
+                                               printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg));
+                                       printk(".\n");
+                               }
+                       }
+
+                       printk("%s: Index #%d - Media %s (#%d) described by a %s (%d) block.\n",
+                                  dev->name, i, medianame[leaf->media], leaf->media,
+                                  block_name[leaf->type], leaf->type);
+               }
        }
-       tio_sia_write(ioaddr, tsiac, tsiax, tsiag);
-       if (dev->start)
-               printk("%s: enabling %s port.\n", dev->name,
-                          (dev->if_port == TULIP_AUI_PORT) ? "AUI":
-                          (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP");
 }
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
 
-static void
-auto21140_select(struct device *dev)
-{
-       int i, ioaddr = dev->base_addr;
-       struct tulip_private *tp = (struct tulip_private *)dev->priv;
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK   0x02    /* EEPROM shift clock. */
+#define EE_CS                  0x01    /* EEPROM chip select. */
+#define EE_DATA_WRITE  0x04    /* EEPROM chip data in. */
+#define EE_WRITE_0             0x01
+#define EE_WRITE_1             0x05
+#define EE_DATA_READ   0x08    /* EEPROM chip data out. */
+#define EE_ENB                 (0x4800 | EE_CS)
 
-       /* kick port */
-       tio_write(TPOLL_TRIGGER, CSR1);
-       tio_write(TINTR_ENABLE, CSR7);
-       tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6);
-       dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED);
-       printk("%s: probed %s port.\n",
-                  dev->name, dev->if_port ? "100TX" : "10TP");
-       tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-                         | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);     
-       tio_write(TINTR_DISABLE, CSR7);
-       i = tio_read(CSR8) & 0xffff;
-       tio_write(TCMOD_AUTO, CSR6);
-}
+/* Delay between EEPROM clock transitions.
+   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
+   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
+#ifdef _LINUX_DELAY_H
+#define eeprom_delay(nanosec)  udelay((nanosec + 999)/1000)
+#else
+#define eeprom_delay(nanosec)  do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
 
-static void
-cogent21140_select(struct device *dev)
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD   (5 << 6)
+#define EE_READ_CMD            (6 << 6)
+#define EE_ERASE_CMD   (7 << 6)
+
+static int read_eeprom(int ioaddr, int location)
 {
-       int ioaddr = dev->base_addr, csr6;
-       struct tulip_private *tp = (struct tulip_private *)dev->priv;
-       dev->if_port &= 1;
-       csr6 = tio_read(CSR6) &
-               ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
-       /* Stop the transmit process. */
-       tio_write(csr6 | TCMOD_RxSTART, CSR6);
-       printk("%s: enabling %s port.\n",
-                  dev->name, dev->if_port ? "100baseTx" : "10baseT");
-       /* Turn on the output drivers */
-       tio_write(0x0000013F, CSR12);
-       tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-                         | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
-       tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-                         | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
+       int i;
+       unsigned short retval = 0;
+       int ee_addr = ioaddr + CSR9;
+       int read_cmd = location | EE_READ_CMD;
+       
+       outl(EE_ENB & ~EE_CS, ee_addr);
+       outl(EE_ENB, ee_addr);
+       
+       /* Shift the read command bits out. */
+       for (i = 10; i >= 0; i--) {
+               short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+               outl(EE_ENB | dataval, ee_addr);
+               eeprom_delay(100);
+               outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+               eeprom_delay(150);
+               outl(EE_ENB | dataval, ee_addr);        /* Finish EEPROM a clock tick. */
+               eeprom_delay(250);
+       }
+       outl(EE_ENB, ee_addr);
+       
+       for (i = 16; i > 0; i--) {
+               outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+               eeprom_delay(100);
+               retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+               outl(EE_ENB, ee_addr);
+               eeprom_delay(100);
+       }
+
+       /* Terminate the EEPROM access. */
+       outl(EE_ENB & ~EE_CS, ee_addr);
+       return retval;
 }
 
-static void
-generic21140_select(struct device *dev)
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK 0x10000
+#define MDIO_DATA_WRITE 0x20000
+#define MDIO_ENB               0x40000
+#define MDIO_DATA_READ 0x80000
+static int mdio_read(int ioaddr, int phy_id, int location)
 {
-       int ioaddr = dev->base_addr, csr6;
-       struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-       dev->if_port &= 1;
-       csr6 = tio_read(CSR6) &
-               ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
+       int i;
+       int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       unsigned short retval = 0;
+       int ee_addr = ioaddr + CSR9;
+
+       /* Shift the read command bits out. */
+       for (i = 18; i >= 0; i--) {
+               int dataval =
+                 (read_cmd & (1 << i)) ? MDIO_DATA_WRITE : 0;
+
+               outl(MDIO_ENB | dataval, ee_addr);
+               eeprom_delay(100);
+               outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, ee_addr);
+               eeprom_delay(250);
+               outl(MDIO_ENB | dataval, ee_addr);
+               eeprom_delay(150);
+       }
 
-       /* Stop the transmit process. */
-       tio_write(csr6 | TCMOD_RxSTART, CSR6);
-       if (dev->start)
-               printk("%s: enabling %s port.\n",
-                          dev->name, dev->if_port ? "100TX" : "10TP");
-       tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-                         | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-       tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-                         | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
+       for (i = 16; i > 0; i--) {
+               outl(MDIO_SHIFT_CLK, ee_addr);
+               eeprom_delay(250);
+               retval = (retval << 1) | ((inl(ee_addr) & MDIO_DATA_READ) ? 1 : 0);
+               outl(0, ee_addr);
+               eeprom_delay(250);
+       }
+       return retval;
 }
 
+\f
 static int
 tulip_open(struct device *dev)
 {
        struct tulip_private *tp = (struct tulip_private *)dev->priv;
        int ioaddr = dev->base_addr;
-       int i;
+       int i = 0;
+
+       /* On some chip revs we must set the MII/SYM port before the reset!? */
+       if (tp->mtable  &&  tp->mtable->has_mii)
+               outl(0x00040000, ioaddr + CSR6);
 
-       /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
-       tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0);
-       udelay(1000);
-       /* Deassert reset.  Set 8 longword cache alignment, 8 longword burst.
-          -> Set 32 longword cache alignment, unlimited longword burst ?
+       /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+       outl(0x00000001, ioaddr + CSR0);
+#ifdef _LINUX_DELAY_H
+       udelay(2);
+#else
+       SLOW_DOWN_IO;
+#endif
+       /* Deassert reset.
+          486: Set 8 longword cache alignment, 8 longword burst.
+          586: Set 16 longword cache alignment, no burst limit.
+          Cache alignment bits 15:14        Burst length 13:8
+               0000    No alignment  0x00000000 unlimited              0800 8 longwords
+               4000    8  longwords            0100 1 longword         1000 16 longwords
+               8000    16 longwords            0200 2 longwords        2000 32 longwords
+               C000    32  longwords           0400 4 longwords
           Wait the specified 50 PCI cycles after a reset by initializing
           Tx and Rx queues and the address filter list. */
-       tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);
+#if defined(__alpha)
+       /* ToDo: Alpha setting could be better. */
+       outl(0x00200000 | 0xE000, ioaddr + CSR0);
+#else
+#if defined(MODULE)
+       /* When a module we don't have 'x86' to check. */
+       outl(0x00200000 | 0x4800, ioaddr + CSR0);
+#else
+       outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+       if (x86 <= 4)
+         printk("This is a 386/486 PCI system, setting cache alignment to %x.\n",
+                        0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
+#endif
+#endif
 
-       if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ,
-                                       tp->signature, dev))
+#ifdef SA_SHIRQ
+       if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
+                                       tulip_tbl[tp->chip_id].chip_name, dev)) {
+               return -EAGAIN;
+       }
+#else
+       if (irq2dev_map[dev->irq] != NULL
+               || (irq2dev_map[dev->irq] = dev) == NULL
+               || dev->irq == 0
+               || request_irq(dev->irq, &tulip_interrupt, 0,
+                                          tulip_tbl[tp->chip_id].chip_name)) {
                return -EAGAIN;
+       }
+#endif
+
+       if (tulip_debug > 1)
+               printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+       MOD_INC_USE_COUNT;
 
        tulip_init_ring(dev);
 
+       /* This is set_rx_mode(), but without starting the transmitter. */
        /* Fill the whole address filter table with our physical address. */
-       { 
-               unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+       {
+               u16 *eaddrs = (u16 *)dev->dev_addr;
                int *setup_frm = tp->setup_frame, i;
 
                /* You must add the broadcast address when doing perfect filtering! */
@@ -721,57 +1031,463 @@ tulip_open(struct device *dev)
                /* Put the setup frame on the Tx list. */
                tp->tx_ring[0].length = 0x08000000 | 192;
                tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
-               tp->tx_ring[0].buffer2 = 0;
-               tp->tx_ring[0].status = TRING_OWN;
-               barrier();
-               tp->cur_tx++, tp->dirty_tx++;
+               tp->tx_ring[0].status = 0x80000000;
+
+               tp->cur_tx++;
        }
 
-       tio_write(virt_to_bus(tp->rx_ring), CSR3);
-       tio_write(virt_to_bus(tp->tx_ring), CSR4);
+       outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
+       outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
+
+       if (dev->if_port == 0)
+         dev->if_port = tp->default_port;
+       if (tp->chip_id == DC21041  &&  dev->if_port > 4)
+               /* Invalid: Select initial TP, autosense, autonegotiate.  */
+               dev->if_port = 4;
+
+       /* Allow selecting a default media. */
+       if (tp->mtable == NULL)
+               goto media_picked;
+       if (dev->if_port)
+               for (i = 0; i < tp->mtable->leafcount; i++)
+                 if (tp->mtable->mleaf[i].media ==
+                         (dev->if_port == 12 ? 0 : dev->if_port)) {
+                       printk("%s: Using user-specified media %s.\n",
+                                  dev->name, medianame[dev->if_port]);
+                       goto media_picked;
+                 }
+       if ((tp->mtable->defaultmedia & 0x0800) == 0)
+               for (i = 0; i < tp->mtable->leafcount; i++)
+                 if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
+                       printk("%s: Using EEPROM-set media %s.\n",
+                                  dev->name, medianame[tp->mtable->mleaf[i].media]);
+                       goto media_picked;
+                 }
+       for (i = tp->mtable->leafcount - 1;
+                (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--)
+         ;
+media_picked:
+
+       tp->cur_index = i;
+       tp->csr6 = 0;
+       select_media(dev, 1);
+
+       /* Start the chip's Tx to process setup frame. */
+       outl(tp->csr6, ioaddr + CSR6);
+       outl(tp->csr6 | 0x2000, ioaddr + CSR6);
 
        dev->tbusy = 0;
        dev->interrupt = 0;
        dev->start = 1;
-       /*
-        * process setup frame completely prior to fiddling with media.
-        */
-       tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6);
-       tio_write(TPOLL_TRIGGER, CSR1);
-       sti();
-       for (i = 0; i < 1000; i++) {
-               if (tp->tx_ring[0].status >= 0) {
+
+
+       /* Enable interrupts by setting the interrupt mask. */
+       outl(0x0001fbff, ioaddr + CSR7);
+       outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+       outl(0, ioaddr + CSR2);         /* Rx poll demand */
+
+       if (tulip_debug > 2) {
+               printk("%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+                          dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
+                          inl(ioaddr + CSR13));
+       }
+       /* Set the timer to switch to check for link beat and perhaps switch
+          to an alternate media type. */
+       init_timer(&tp->timer);
+       tp->timer.expires = RUN_AT((24*HZ)/10);                 /* 2.4 sec. */
+       tp->timer.data = (unsigned long)dev;
+       tp->timer.function = &tulip_timer;                              /* timer handler */
+       add_timer(&tp->timer);
+
+       return 0;
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct device *dev, int startup)
+{
+       int ioaddr = dev->base_addr;
+       struct tulip_private *tp = (struct tulip_private *)dev->priv;
+       struct mediatable *mtable = tp->mtable;
+       u32 new_csr6;
+       int i;
+
+       if (mtable) {
+               struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+               unsigned char *p = mleaf->leafdata;
+               switch (mleaf->type) {
+               case 0:                                 /* 21140 non-MII xcvr. */
+                       if (tulip_debug > 1)
+                               printk("%s: Using a 21140 non-MII transceiver with control"
+                                          " setting %2.2x.\n",
+                                          dev->name, p[1]);
+                       dev->if_port = p[0];
+                       if (startup)
+                               outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+                       outl(p[1], ioaddr + CSR12);
+                       new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+                       break;
+               case 1:
+                       if (startup) {
+                               outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+                               dev->if_port = 11;
+                               if (tulip_debug > 2)
+                                       printk("%s:  Doing a reset sequence of length %d.\n",
+                                                  dev->name, p[2 + p[1]]);
+                               for (i = 0; i < p[2 + p[1]]; i++)
+                                       outl(p[3 + p[1] + i], ioaddr + CSR12);
+                               if (tulip_debug > 2)
+                                       printk("%s  Doing a transceiver setup sequence of length %d.\n",
+                                                  dev->name, p[1]);
+                               for (i = 0; i < p[1]; i++)
+                                       outl(p[2 + i], ioaddr + CSR12);
+                       }
+                       new_csr6 = 0x020C0000;
+                       break;
+               case 2: case 4: {
+                       u16 *setup = (u16*)&p[1];
+                       dev->if_port = p[0] & 15;
+                       if (tulip_debug > 1)
+                               printk("%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+                                          dev->name, medianame[dev->if_port], setup[0], setup[1]);
+                       if (p[0] & 0x40) {      /* SIA (CSR13-15) setup values are provided. */
+                               outl(0, ioaddr + CSR13);
+                               outl(setup[1], ioaddr + CSR14);
+                               outl(setup[2], ioaddr + CSR15);
+                               outl(setup[0], ioaddr + CSR13);
+                               setup += 3;
+                       } else {
+                               outl(0, ioaddr + CSR13);
+                               outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
+                               outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+                               outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+                       }
+                       outl(setup[0]<<16, ioaddr + CSR15);     /* Direction */
+                       outl(setup[1]<<16, ioaddr + CSR15);     /* Data */
+                       new_csr6 = 0x02000000;
                        break;
                }
-               udelay(1000);
+               case 3: {
+                       int init_length = p[1];
+                       u16 * init_sequence = (u16*)(p + 2);
+                       int reset_length = p[2 + init_length*2];
+                       u16 * reset_sequence = (u16*)&p[3 + init_length*2];
+
+                       dev->if_port = 11;
+                       if (startup) {
+                               if (tulip_debug > 2)
+                                       printk("%s:  Doing a 21142 reset sequence of length %d.\n",
+                                                  dev->name, reset_length);
+                               for (i = 0; i < reset_length; i++)
+                                       outl(reset_sequence[i] << 16, ioaddr + CSR15);
+                       }
+                       if (tulip_debug > 2)
+                               printk("%s: Doing a 21142 xcvr setup sequence of length %d.\n",
+                                          dev->name, init_length);
+                       for (i = 0; i < init_length; i++)
+                               outl(init_sequence[i] << 16, ioaddr + CSR15);
+                       new_csr6 = 0x020C0000 | (tp->full_duplex ? 0x0200 : 0);
+                       break;
+               }
+               default:
+                 new_csr6 = 0x020C0000;
+               }
+               if (tulip_debug > 1)
+                       printk("%s: Using media type %s, CSR12 is %2.2x.\n",
+                                  dev->name, medianame[dev->if_port],
+                                  inl(ioaddr + CSR12) & 0xff);
+       } else if (tp->chip_id == DC21140) {
+               /* Set media type to MII @ 100mbps: 0x020C0000 */
+               new_csr6 = 0x020C0000;
+               dev->if_port = 11;
+               if (tulip_debug > 1) {
+                       printk("%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
+                                  dev->name, inl(ioaddr + CSR12) & 0xff);
+               }
+       } else if (tp->chip_id == DC21041) {
+               if (tulip_debug > 1)
+                       printk("%s: 21041 using media %s, CSR12 is %4.4x.\n",
+                                  dev->name, medianame[dev->if_port & 15],
+                                  inl(ioaddr + CSR12) & 0xffff);
+               outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+               outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+               outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+               outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+               new_csr6 = 0x80020000;
+       } else {                                        /* 21040 */
+               /* Turn on the xcvr interface. */
+               int csr12 = inl(ioaddr + CSR12);
+               if (tulip_debug > 1)
+                       printk("%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+                                  dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
+               new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+               /* Set the full duplux match frame. */
+               outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+               outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+               outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
        }
-       if (i == 500) {
-               printk("%s: initial setup frame didn't complete.\n", dev->name);
-               dev->start = 0;
-               dev->tbusy = 1;
-               tio_write(TINTR_DISABLE, CSR7);
-               tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-               tio_write(TSIAC_CONFIG, CSR13);
-               tio_write(0, CSR13);
-               free_irq(dev->irq, dev);
-               return (-EIO);
+
+       tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+       return;
+}
+
+static void tulip_timer(unsigned long data)
+{
+       struct device *dev = (struct device *)data;
+       struct tulip_private *tp = (struct tulip_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       u32 csr12 = inl(ioaddr + CSR12);
+       int next_tick = 0;
+
+       if (tulip_debug > 3) {
+               printk("%s: Media selection tick, status %8.8x mode %8.8x "
+                          "SIA %8.8x %8.8x %8.8x %8.8x.\n",
+                          dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
+                          csr12, inl(ioaddr + CSR13),
+                          inl(ioaddr + CSR14), inl(ioaddr + CSR15));
        }
-       /*
-        * Whack the chip to stop it and *then* do initial media setup.
-        */
-       tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6);
-       if (tp->port_select)
-               tp->port_select(dev);
-       /* Start the chip's Tx and Rx processes. */
-       tio_write(tio_read(CSR6) | TCMOD_TRxSTART
-                         | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6);
-       /* Enable interrupts by setting the interrupt mask. */
-       tio_write(TINTR_ENABLE, CSR7);
+       switch (tp->chip_id) {
+       case DC21040:
+               if (csr12 & 0x0002) { /* Network error */
+                       printk("%s: No 10baseT link beat found, switching to %s media.\n",
+                                  dev->name, dev->if_port ? "10baseT" : "AUI");
+                       dev->if_port ^= 1;
+                       outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+                       dev->trans_start = jiffies;
+               }
+               break;
+       case DC21041:
+               if (tulip_debug > 2)
+                       printk("%s: 21041 media tick  CSR12 %8.8x.\n",
+                                  dev->name, csr12);
+               switch (dev->if_port) {
+               case 0: case 3: case 4:
+                 if (csr12 & 0x0004) { /*LnkFail */
+                       /* 10baseT is dead.  Check for activity on alternate port. */
+                       tp->mediasense = 1;
+                       if (csr12 & 0x0200)
+                               dev->if_port = 2;
+                       else
+                               dev->if_port = 1;
+                       printk("%s: No 21041 10baseT link beat, Media switched to %s.\n",
+                                  dev->name, medianame[dev->if_port]);
+                       outl(0, ioaddr + CSR13); /* Reset */
+                       outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+                       outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+                       outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+                       next_tick = 10*HZ;                      /* 2.4 sec. */
+                 } else
+                       next_tick = 30*HZ;
+                 break;
+               case 1:                                 /* 10base2 */
+               case 2:                                 /* AUI */
+                 if (csr12 & 0x0100) {
+                       next_tick = (30*HZ);                    /* 30 sec. */
+                       tp->mediasense = 0;
+                 } else if ((csr12 & 0x0004) == 0) {
+                       printk("%s: 21041 media switched to 10baseT.\n", dev->name);
+                       dev->if_port = 0;
+                       select_media(dev, 0);
+                       next_tick = (24*HZ)/10;                         /* 2.4 sec. */
+                 } else if (tp->mediasense || (csr12 & 0x0002)) {
+                       dev->if_port = 3 - dev->if_port; /* Swap ports. */
+                       select_media(dev, 0);
+                       next_tick = 20*HZ;
+                 } else {
+                       next_tick = 20*HZ;
+                 }
+                 break;
+               }
+               break;
+       case DC21140:  case DC21142: {
+               struct medialeaf *mleaf;
+               unsigned char *p;
+               if (tp->mtable == NULL) {       /* No EEPROM info, use generic code. */
+                       /* Assume this is like a SMC card, and check its link beat bit. */
+                       if ((dev->if_port == 0 && (csr12 & 0x0080)) ||
+                               (dev->if_port == 1 && (csr12 & 0x0040) == 0)) {
+                               dev->if_port ^= 1;
+                               /* Stop the transmit process. */
+                               tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
+                               outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+                               printk("%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
+                                          " %s media.\n", dev->name,
+                                          csr12 & 0xff,
+                                          dev->if_port ? "100baseTx" : "10baseT");
+                               outl(tp->csr6 | 0xA002, ioaddr + CSR6);
+                               dev->trans_start = jiffies;
+                               next_tick = (24*HZ)/10;
+                       } else {
+                               next_tick = 10*HZ;
+                               if (tulip_debug > 2)
+                                       printk("%s: network media monitor 0x%2.2x, link"
+                                                  " beat detected as %s.\n", dev->name,
+                                                  csr12 & 0xff,
+                                                  dev->if_port ? "100baseTx" : "10baseT");
+                       }
+                       break;
+               }
+         mleaf = &tp->mtable->mleaf[tp->cur_index];
+         p = mleaf->leafdata;
+         switch (mleaf->type) {
+         case 0: case 4: {
+               /* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
+                 s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
+                 if (tulip_debug > 2)
+                         printk("%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
+                                        " %d, expecting %d.\n",
+                                        dev->name, csr12, (bitnum >> 1) & 7,
+                                        (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+                                        (bitnum >= 0));
+                 /* Check that the specified bit has the proper value. */
+                 if ((bitnum < 0) !=
+                         ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+                         if (tulip_debug > 1)
+                                 printk("%s: Link beat detected for %s.\n", dev->name,
+                                                medianame[mleaf->media]);
+                         break;
+                 }
+                 if (tp->medialock)
+                       break;
+         select_next_media:
+                 if (--tp->cur_index < 0) {
+                       /* We start again, but should instead look for default. */
+                       tp->cur_index = tp->mtable->leafcount - 1;
+                 }
+                 dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+                 if (media_fd[dev->if_port])
+                       goto select_next_media; /* Skip FD entries. */
+                 if (tulip_debug > 1)
+                         printk("%s: No link beat on media %s,"
+                                        " trying transceiver type %s.\n",
+                                        dev->name, medianame[mleaf->media & 15],
+                                        medianame[tp->mtable->mleaf[tp->cur_index].media]);
+                 select_media(dev, 0);
+                 /* Restart the transmit process. */
+                 outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+                 outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+                 next_tick = (24*HZ)/10;
+                 break;
+         }
+         case 1:
+                 printk(" %s: MII monitoring tick: CSR12 status %2.2x.\n",
+                                dev->name, csr12);
+                 /* Hack for D-Link: Full duplex indication is on bit 3. */
+                 if (dev->dev_addr[0] == 0  &&  dev->dev_addr[1] == 0x80
+                         && dev->dev_addr[2] == 0xC8) {
+                         /* The first message is for information only. */
+                         if (tp->full_duplex) {
+                                 printk("%s: D-Link card in full-duplex mode, csr6 setting"
+                                                " %8.8x.\n", dev->name, tp->csr6);
+                         } else if (csr12 & 0x08) {
+                                 tp->full_duplex = 0;
+                                 tp->csr6 &= ~0x0200;
+                                 outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+                                 outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+                         } else {
+                                 tp->full_duplex = 1;
+                                 tp->csr6 |= 0x0200;
+                                 printk("%s: Switching D-Link card to full-duplex.\n", dev->name);
+                                 outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+                                 outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+                         }
+                 }
+                 break;
+         case 2:                                       /* 21142 non-MII */
+         case 3:                                       /* 21142 MII */
+                 next_tick = (24*HZ)/10;
+                 break;
+         default:
+                 break;
+         }
+       }
+       default:                                        /* Invalid chip type. */
+         break;
+       }
+       if (next_tick) {
+               tp->timer.expires = RUN_AT(next_tick);
+               add_timer(&tp->timer);
+       }
+}
 
-       MOD_INC_USE_COUNT;
-       return 0;
+static void tulip_tx_timeout(struct device *dev)
+{
+  struct tulip_private *tp = (struct tulip_private *)dev->priv;
+  int ioaddr = dev->base_addr;
+  int i;
+
+  if (tp->mtable && tp->mtable->has_mii) {
+       /* Do nothing -- the media monitor should handle this. */
+       if (tulip_debug > 1)
+         printk("%s: Transmit timeout using MII device.\n", dev->name);
+  } else if (tp->chip_id == DC21040) {
+         if (inl(ioaddr + CSR12) & 0x0002) {
+                 printk("%s: transmit timed out, switching to %s media.\n",
+                                dev->name, dev->if_port ? "10baseT" : "AUI");
+                 dev->if_port ^= 1;
+                 outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+         }
+         dev->trans_start = jiffies;
+         return;
+  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) {
+       /* Stop the transmit process. */
+       outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+       dev->if_port ^= 1;
+       printk("%s: 21140 transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+                  dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+                  inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+       printk("%s: transmit timed out, switching to %s media.\n",
+                  dev->name, dev->if_port ? "100baseTx" : "10baseT");
+       outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+       tp->stats.tx_errors++;
+       dev->trans_start = jiffies;
+       return;
+  } else if (tp->chip_id == DC21041) {
+       u32 csr12 = inl(ioaddr + CSR12);
+
+       printk("%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
+                  " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+                  dev->name, inl(ioaddr + CSR5), csr12,
+                  inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+       tp->mediasense = 1;
+       if (dev->if_port == 1 || dev->if_port == 2)
+               if (csr12 & 0x0004) {
+                       dev->if_port = 2 - dev->if_port;
+               } else
+                       dev->if_port = 0;
+       else
+               dev->if_port = 1;
+       select_media(dev, 0);
+       tp->stats.tx_errors++;
+       dev->trans_start = jiffies;
+       return;
+  } else
+       printk("%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
+                  " resetting...\n",
+                  dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+#ifndef __alpha__
+  printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
+  for (i = 0; i < RX_RING_SIZE; i++)
+       printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+  printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
+  for (i = 0; i < TX_RING_SIZE; i++)
+       printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+  printk("\n");
+#endif
+
+  /* Perhaps we should reinitialize the hardware here. */
+  dev->if_port = 0;
+  /* Stop and restart the chip's Tx processes . */
+  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+  /* Trigger an immediate transmit demand. */
+  outl(0, ioaddr + CSR1);
+
+  dev->trans_start = jiffies;
+  tp->stats.tx_errors++;
+  return;
 }
 
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void
 tulip_init_ring(struct device *dev)
@@ -784,85 +1500,62 @@ tulip_init_ring(struct device *dev)
        tp->dirty_rx = tp->dirty_tx = 0;
 
        for (i = 0; i < RX_RING_SIZE; i++) {
-               tp->rx_ring[i].status = TRING_OWN;
+               tp->rx_ring[i].status = 0x80000000;     /* Owned by Tulip chip */
                tp->rx_ring[i].length = PKT_BUF_SZ;
-               tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]);
+               {
+                       /* Note the receive buffer must be longword aligned.
+                          dev_alloc_skb() provides 16 byte alignment.  But do *not*
+                          use skb_reserve() to align the IP header! */
+                       struct sk_buff *skb;
+                       skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+                       tp->rx_skbuff[i] = skb;
+                       if (skb == NULL)
+                               break;                  /* Bad news!  */
+                       skb->dev = dev;                 /* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+                       tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+#else
+                       tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
+#endif
+               }
                tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
        }
-       /* Mark the last entry as wrapping the ring. */ 
+       /* Mark the last entry as wrapping the ring. */
        tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
        tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
        /* The Tx buffer descriptor is filled in as needed, but we
           do need to clear the ownership bit. */
        for (i = 0; i < TX_RING_SIZE; i++) {
+               tp->tx_skbuff[i] = 0;
                tp->tx_ring[i].status = 0x00000000;
+               tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
        }
+       tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
 
 static int
 tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 {
        struct tulip_private *tp = (struct tulip_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-       int entry, len;
-       unsigned long daddr;
-
-       /* Transmitter timeout, serious problems. */
-       if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) {
-               int tickssofar = jiffies - dev->trans_start;
-               int i;
-               if (tickssofar < 40) return(1);
-               if (tp->port_select) {
-                       if (!tp->port_fix) dev->if_port ++;
-                       tp->port_select(dev);
-                       dev->trans_start = jiffies;
-                       dev_kfree_skb(skb, FREE_WRITE);
-                       return(0);
-               }
-               printk("%s: transmit timed out, status %8.8x,"
-                          "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-                          dev->name, tio_read(CSR5), tio_read(CSR12),
-                          tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
-#ifndef        __alpha__
-               printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-#endif
-               for (i = 0; i < RX_RING_SIZE; i++)
-                       printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-#ifndef        __alpha__
-               printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-#endif
-               for (i = 0; i < TX_RING_SIZE; i++)
-                       printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-               printk("\n");
-
-               tp->stats.tx_errors++;
-               /* Perhaps we should reinitialize the hardware here. */
-               dev->if_port = 0;
-               tio_write(TSIAC_CONFIG, CSR13);
-               /* Start the chip's Tx and Rx processes . */
-               tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6);
-               /* Trigger an immediate transmit demand. */
-               tio_write(TPOLL_TRIGGER, CSR1);
-
-               dev->tbusy=0;
-               dev->trans_start = jiffies;
-               dev_kfree_skb(skb, FREE_WRITE);
-               return(0);
-       }
+       int entry;
+       u32 flag;
 
-       if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) {
+#ifndef final_version
+       if (skb == NULL || skb->len <= 0) {
                printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
                           dev->name);
                dev_tint(dev);
-               return(0);
+               return 0;
        }
+#endif
 
        /* Block a timer-based transmit from overlapping.  This could better be
-          done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-          If this ever occurs the queue layer is doing something evil! */
-       if (set_bit(0, (void*)&dev->tbusy) != 0) {
-               printk("%s: Transmitter access conflict.\n", dev->name);
+          done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+       if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+               if (jiffies - dev->trans_start < TX_TIMEOUT)
+                       return 1;
+               tulip_tx_timeout(dev);
                return 1;
        }
 
@@ -872,43 +1565,49 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
        /* Calculate the next Tx descriptor entry. */
        entry = tp->cur_tx % TX_RING_SIZE;
 
-       tp->tx_full = 1;
-       /*
-        * If skb is == -1, then this is a funky setup_frame redo.
-        */
-       if (skb == (struct sk_buff *) -1) {
-               daddr = virt_to_bus((char *)tp->setup_frame);
-               len = 192;
-               skb = NULL;
+       tp->tx_skbuff[entry] = skb;
+       tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+
+       if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+         flag = 0x60000000; /* No interrupt */
+         dev->tbusy = 0;
+       } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+         flag = 0xe0000000; /* Tx-done intr. */
+         dev->tbusy = 0;
+       } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+         flag = 0x60000000; /* No Tx-done intr. */
+         dev->tbusy = 0;
        } else {
-               daddr = virt_to_bus(skb->data);
-               len = skb->len;
+         /* Leave room for set_rx_mode() to fill entries. */
+         flag = 0xe0000000; /* Tx-done intr. */
+         tp->tx_full = 1;
        }
-       tp->tx_skbuff[entry] = skb;
-       tp->tx_ring[entry].length = len |
-               (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
-       tp->tx_ring[entry].buffer1 = daddr;
-       tp->tx_ring[entry].buffer2 = 0;
-       tp->tx_ring[entry].status = TRING_OWN;  /* Pass ownership to the chip. */
-       barrier();
+       if (entry == TX_RING_SIZE-1)
+               flag |= 0xe2000000;
 
+       tp->tx_ring[entry].length = skb->len | flag;
+       tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */
        tp->cur_tx++;
-
        /* Trigger an immediate transmit demand. */
-       tio_write(TPOLL_TRIGGER, CSR1);
+       outl(0, dev->base_addr + CSR1);
 
        dev->trans_start = jiffies;
 
-       return(0);
+       return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
 {
-       struct device *dev = (struct device *)dev_id;
+#ifdef SA_SHIRQ                /* Use the now-standard shared IRQ implementation. */
+       struct device *dev = (struct device *)dev_instance;
+#else
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+
        struct tulip_private *lp;
-       int csr5, ioaddr, boguscnt=10;
+       int csr5, ioaddr, boguscnt = 10;
 
        if (dev == NULL) {
                printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
@@ -923,33 +1622,46 @@ static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        dev->interrupt = 1;
 
        do {
-               csr5 = tio_read(CSR5);
+               csr5 = inl(ioaddr + CSR5);
                /* Acknowledge all of the current interrupt sources ASAP. */
-               tio_write(csr5 & TSTAT_CLEARINTR, CSR5);
-               /* check interrupt ? */
-               if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break;
+               outl(csr5 & 0x0001ffff, ioaddr + CSR5);
 
-               if (csr5 & TSTAT_RxINTR)                        /* Rx interrupt */
+               if (tulip_debug > 4)
+                       printk("%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+                                  dev->name, csr5, inl(dev->base_addr + CSR5));
+
+               if ((csr5 & 0x00018000) == 0)
+                       break;
+
+               if (csr5 & 0x0040)                      /* Rx interrupt */
                        tulip_rx(dev);
 
-               if (csr5 & TSTAT_TxINTR) {              /* Tx-done interrupt */
-                       int dirty_tx = lp->dirty_tx;
+               if (csr5 & 0x0007) {            /* Tx-done interrupt */
+                       int dirty_tx;
 
-                       while (dirty_tx < lp->cur_tx) {
+                       for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
                                int entry = dirty_tx % TX_RING_SIZE;
                                int status = lp->tx_ring[entry].status;
 
                                if (status < 0)
                                        break;                  /* It still hasn't been Txed */
+                               /* Check for Rx filter setup frames. */
+                               if (lp->tx_skbuff[entry] == NULL)
+                                 continue;
 
-                               if (status & TRING_ERROR) {
+                               if (status & 0x8000) {
                                        /* There was an major error, log it. */
+#ifndef final_version
+                                       if (tulip_debug > 1)
+                                               printk("%s: Transmit error, Tx status %8.8x.\n",
+                                                          dev->name, status);
+#endif
                                        lp->stats.tx_errors++;
-                                       if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++;
-                                       if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++;
-                                       if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++;
-                                       if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++;
-                                       if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex)
+                                       if (status & 0x4104) lp->stats.tx_aborted_errors++;
+                                       if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+                                       if (status & 0x0200) lp->stats.tx_window_errors++;
+                                       if (status & 0x0002) lp->stats.tx_fifo_errors++;
+                                       if ((status & 0x0080) && lp->full_duplex == 0)
                                                lp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
                                        if (status & 0x0100) lp->stats.collisions16++;
@@ -963,15 +1675,14 @@ static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                }
 
                                /* Free the original skb. */
-                               if (lp->tx_skbuff[entry] != NULL)
-                                       dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
-                               dirty_tx++;
+                               dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+                               lp->tx_skbuff[entry] = 0;
                        }
 
 #ifndef final_version
-                       if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-                               printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-                                          dirty_tx, lp->cur_tx, lp->tx_full);
+                       if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+                               printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+                                          dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
                                dirty_tx += TX_RING_SIZE;
                        }
 #endif
@@ -988,34 +1699,50 @@ static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                }
 
                /* Log errors. */
-               if (csr5 & TSTAT_ABNINTR) {     /* Abnormal error summary bit. */
-                       if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */
-                       if (csr5 & TSTAT_RxMISSED) {            /* Missed a Rx frame. */
+               if (csr5 & 0x8000) {    /* Abnormal error summary bit. */
+                       if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+                       if (csr5 & 0x0020) { /* Tx FIFO underflow. */
+                         lp->csr6 |= 0x00200000;  /* Reconfigure to store-n-forward. */
+                         /* Restart the transmit process. */
+                         outl(lp->csr6 | 0x0002, ioaddr + CSR6);
+                         outl(lp->csr6 | 0x2002, ioaddr + CSR6);
+                       }
+                       if (csr5 & 0x0100) {            /* Missed a Rx frame. */
                                lp->stats.rx_errors++;
-                               lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+                               lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
                        }
-                       if (csr5 & TSTAT_TEXPIRED) {
+                       if (csr5 & 0x0800) {
                                printk("%s: Something Wicked happened! %8.8x.\n",
                                           dev->name, csr5);
                                /* Hmmmmm, it's not clear what to do here. */
                        }
+                       /* Clear all error sources, included undocumented ones! */
+                       outl(0x000f7ba, ioaddr + CSR5);
                }
                if (--boguscnt < 0) {
                        printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
                                   dev->name, csr5);
                        /* Clear all interrupt sources. */
-                       tio_write(TSTAT_CLEARINTR, CSR5);
+                       outl(0x0001ffff, ioaddr + CSR5);
                        break;
                }
        } while (1);
 
-       /* Special code for testing *only*. */
+       if (tulip_debug > 3)
+               printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+                          dev->name, inl(ioaddr + CSR5));
+
+       /* Code that should never be run!  Perhaps remove after testing.. */
        {
                static int stopit = 10;
                if (dev->start == 0  &&  --stopit < 0) {
                        printk("%s: Emergency stop, looping startup interrupt.\n",
                                   dev->name);
+#ifdef SA_SHIRQ
                        free_irq(irq, dev);
+#else
+                       free_irq(irq);
+#endif
                }
        }
 
@@ -1028,59 +1755,100 @@ tulip_rx(struct device *dev)
 {
        struct tulip_private *lp = (struct tulip_private *)dev->priv;
        int entry = lp->cur_rx % RX_RING_SIZE;
-       int i;
 
+       if (tulip_debug > 4)
+               printk(" In tulip_rx(), entry %d %8.8x.\n", entry,
+                          lp->rx_ring[entry].status);
        /* If we own the next entry, it's a new packet. Send it up. */
        while (lp->rx_ring[entry].status >= 0) {
                int status = lp->rx_ring[entry].status;
 
-               if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
-                       printk("%s: Ethernet frame spanned multiple buffers,"
-                                  "status %8.8x!\n", dev->name, status);
-               } else if (status & TRING_ERROR) {
+               if (tulip_debug > 4)
+                       printk("  tulip_rx() status was %8.8x.\n", status);
+               if ((status & 0x0300) != 0x0300) {
+                       if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
+                         printk("%s: Oversized Ethernet frame spanned multiple buffers,"
+                                        " status %8.8x!\n", dev->name, status);
+                         lp->stats.rx_length_errors++;
+                       }
+               } else if (status & 0x8000) {
                        /* There was a fatal error. */
                        lp->stats.rx_errors++; /* end of a packet.*/
-                       if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++;
-                       if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++;
-                       if (status & TRING_RxCRC) lp->stats.rx_crc_errors++;
-                       if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++;
+                       if (status & 0x0890) lp->stats.rx_length_errors++;
+                       if (status & 0x0004) lp->stats.rx_frame_errors++;
+                       if (status & 0x0002) lp->stats.rx_crc_errors++;
+                       if (status & 0x0001) lp->stats.rx_fifo_errors++;
                } else {
                        /* Malloc up new buffer, compatible with net-2e. */
                        /* Omit the four octet CRC from the length. */
                        short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
                        struct sk_buff *skb;
-
-                       skb = dev_alloc_skb(pkt_len + 2);
+                       int rx_in_place = 0;
+
+                       /* Check if the packet is long enough to just accept without
+                          copying to a properly sized skbuff. */
+                       if (pkt_len > SKBUFF_RX_COPYBREAK) {
+                               struct sk_buff *newskb;
+                               char *temp;
+
+                               /* Get a fresh skbuff to replace the filled one. */
+                               newskb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+                               if (newskb == NULL) {
+                                       skb = 0;                /* No memory, drop the packet. */
+                                       goto memory_squeeze;
+                               }
+                               /* Pass up the skb already on the Rx ring. */
+                               skb = lp->rx_skbuff[entry];
+                               temp = skb_put(skb, pkt_len);
+                               if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp)
+                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                                  " in tulip_rx: %p vs. %p / %p.\n", dev->name,
+                                                  bus_to_virt(lp->rx_ring[entry].buffer1),
+                                                  skb->head, temp);
+                               rx_in_place = 1;
+                               lp->rx_skbuff[entry] = newskb;
+                               newskb->dev = dev;
+                               /* Longword alignment required: do not skb_reserve(2)! */
+                               lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail);
+                       } else
+                               skb = DEV_ALLOC_SKB(pkt_len + 2);
+                       memory_squeeze:
                        if (skb == NULL) {
-                               printk("%s: Memory squeeze, deferring packet.\n",
-                                          dev->name);
+                               int i;
+                               printk("%s: Memory squeeze, deferring packet.\n", dev->name);
                                /* Check that at least two ring entries are free.
                                   If not, free one and mark stats->rx_dropped++. */
-                               for (i=0; i < RX_RING_SIZE; i++)
+                               for (i = 0; i < RX_RING_SIZE; i++)
                                        if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
                                                break;
 
                                if (i > RX_RING_SIZE -2) {
                                        lp->stats.rx_dropped++;
-                                       lp->rx_ring[entry].status = TRING_OWN;
+                                       lp->rx_ring[entry].status = 0x80000000;
                                        lp->cur_rx++;
                                }
                                break;
                        }
                        skb->dev = dev;
-                       skb_reserve(skb, 2);
-                       memcpy(skb_put(skb, pkt_len),
-                                  bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
-                       /* Needed for 1.3.x */
-                       skb->protocol = eth_type_trans(skb,dev);
+                       if (! rx_in_place) {
+                               skb_reserve(skb, 2);    /* 16 byte align the data fields */
+                               memcpy(skb_put(skb, pkt_len),
+                                          bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
+                       }
+#if LINUX_VERSION_CODE > 0x10300
+                       skb->protocol = eth_type_trans(skb, dev);
+#else
+                       skb->len = pkt_len;
+#endif
                        netif_rx(skb);
                        lp->stats.rx_packets++;
                }
 
-               lp->rx_ring[entry].status = TRING_OWN;
+               lp->rx_ring[entry].status = 0x80000000;
                entry = (++lp->cur_rx) % RX_RING_SIZE;
        }
-       return(0);
+
+       return 0;
 }
 
 static int
@@ -1088,342 +1856,223 @@ tulip_close(struct device *dev)
 {
        int ioaddr = dev->base_addr;
        struct tulip_private *tp = (struct tulip_private *)dev->priv;
+       int i;
 
        dev->start = 0;
        dev->tbusy = 1;
 
+       if (tulip_debug > 1)
+               printk("%s: Shutting down ethercard, status was %2.2x.\n",
+                          dev->name, inl(ioaddr + CSR5));
+
        /* Disable interrupts by clearing the interrupt mask. */
-       tio_write(TINTR_DISABLE, CSR7);
+       outl(0x00000000, ioaddr + CSR7);
        /* Stop the chip's Tx and Rx processes. */
-       tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-       /* Leave the card in 10baseT state. */
-       tio_write(TSIAC_CONFIG, CSR13);
+       outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+       /* 21040 -- Leave the card in 10baseT state. */
+       if (tp->chip_id == DC21040)
+               outl(0x00000004, ioaddr + CSR13);
 
-       tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+       tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-       tio_write(0, CSR13);
-/*     tio_write(0, CSR8);     wake up chip ? */
+       del_timer(&tp->timer);
 
+#ifdef SA_SHIRQ
        free_irq(dev->irq, dev);
+#else
+       free_irq(dev->irq);
+       irq2dev_map[dev->irq] = 0;
+#endif
 
-       MOD_DEC_USE_COUNT;
-       return(0);
-}
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = tp->rx_skbuff[i];
+               tp->rx_skbuff[i] = 0;
+               tp->rx_ring[i].status = 0;              /* Not owned by Tulip chip. */
+               tp->rx_ring[i].length = 0;
+               tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+               if (skb) {
+#if LINUX_VERSION_CODE < 0x20100
+                       skb->free = 1;
+#endif
+                       dev_kfree_skb(skb, FREE_WRITE);
+               }
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (tp->tx_skbuff[i])
+                       dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+               tp->tx_skbuff[i] = 0;
+       }
 
-static int
-tulip_config(struct device *dev, struct ifmap *map)
-{
-       struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-       if (map->port == 0xff) return(-EINVAL);
-       dev->if_port = map->port;
-       tp->port_fix = 1;
-       if (tp->port_select) tp->port_select(dev);
-       return(0);
+       MOD_DEC_USE_COUNT;
+
+       return 0;
 }
 
 static struct enet_statistics *
 tulip_get_stats(struct device *dev)
 {
        struct tulip_private *tp = (struct tulip_private *)dev->priv;
-       /*      short ioaddr = dev->base_addr;*/
+       int ioaddr = dev->base_addr;
+
+       if (dev->start)
+               tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-       return(&tp->stats);
+       return &tp->stats;
 }
 
-/*
- *     Set or clear the multicast filter for this adaptor.
- */
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling tp->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+       unsigned int crc = 0xffffffff;  /* Initial value. */
+       while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 8; --bit >= 0; current_octet >>= 1) {
+                       if ((crc ^ current_octet) & 1) {
+                               crc >>= 1;
+                               crc ^= ethernet_polynomial_le;
+                       } else
+                               crc >>= 1;
+               }
+       }
+       return crc;
+}
 
-static void set_multicast_list(struct device *dev)
+
+static void
+#ifdef NEW_MULTICAST
+set_multicast_list(struct device *dev)
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 {
-       short ioaddr = dev->base_addr;
-       int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
+       int ioaddr = dev->base_addr;
+       int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+       struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-       if (dev->flags&IFF_PROMISC) 
-       {                       /* Set promiscuous. why ALLMULTI ? */
-               tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6);
-               /* Log any net taps. */
+       tp->csr6 &= ~0x00D5;
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               outl(csr6 | 0x00C0, ioaddr + CSR6);
+               /* Unconditionally log net taps. */
                printk("%s: Promiscuous mode enabled.\n", dev->name);
-       }
-       else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) 
-       {
+               tp->csr6 |= 0xC0;
+       } else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
                /* Too many to filter perfectly -- accept all multicasts. */
-               tio_write(csr6 | TCMOD_ALLMCAST, CSR6);
-       }
-       else
-       {
-               struct tulip_private *tp = (struct tulip_private *)dev->priv;
-               struct dev_mc_list *dmi=dev->mc_list;
-               int *setup_frm = tp->setup_frame;
-               unsigned short *eaddrs;
+               outl(csr6 | 0x0080, ioaddr + CSR6);
+               tp->csr6 |= 0x80;
+       } else {
+               u32 *setup_frm = tp->setup_frame;
+               struct dev_mc_list *mclist;
+               u16 *eaddrs;
+               u32 tx_flags;
                int i;
 
-               /* We have < 15 addresses that we can use the wonderful
-                  16 address perfect filtering of the Tulip.  Note that only
-                  the low shortword of setup_frame[] is valid. */
-               tio_write(csr6 | 0x0000, CSR6);
-               for (i = 0; i < dev->mc_count; i ++) {
-                       eaddrs=(unsigned short *)dmi->dmi_addr;
-                       dmi=dmi->next;
+               if (dev->mc_count > 14) { /* Must use a multicast hash table. */
+                 u16 hash_table[32];
+                 memset(hash_table, 0, sizeof(hash_table));
+                 for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                          i++, mclist = mclist->next)
+                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+                                       hash_table);
+                 /* Copy the hash table to the setup frame.
+                        NOTE that only the LOW SHORTWORD of setup_frame[] is valid!
+                        This code may require tweaking for non-x86 architectures!  */
+                 for (i = 0; i < 32; i++)
+                       *setup_frm++ = hash_table[i];
+                 setup_frm += 7;
+                 tx_flags = 0x08400000 | 192;
+                 /* Too clever: i > 15 for fall-though. */
+               } else {
+                 /* We have <= 15 addresses so we can use the wonderful
+                        16 address perfect filtering of the Tulip. */
+                 for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+                          i++, mclist = mclist->next) {
+                       /* Note that only the low shortword of setup_frame[] is valid!
+                          This code may require tweaking for non-x86 architectures! */
+                       eaddrs = (u16 *)mclist->dmi_addr;
                        *setup_frm++ = *eaddrs++;
                        *setup_frm++ = *eaddrs++;
                        *setup_frm++ = *eaddrs++;
+                 }
+                 /* Fill the rest of the table with our physical address.
+                        Once again, only the low shortword or setup_frame[] is valid! */
+                 *setup_frm++ = 0xffff;
+                 *setup_frm++ = 0xffff;
+                 *setup_frm++ = 0xffff;
+                 tx_flags = 0x08000000 | 192;
                }
-               /* Fill the rest of the table with our physical address. */
-               eaddrs = (unsigned short *)dev->dev_addr;
-               /* Always accept broadcast packets */
-               *setup_frm++ = 0xffff;
-               *setup_frm++ = 0xffff;
-               *setup_frm++ = 0xffff;
+               eaddrs = (u16 *)dev->dev_addr;
                do {
-                       *setup_frm++ = eaddrs[0];
-                       *setup_frm++ = eaddrs[1];
-                       *setup_frm++ = eaddrs[2];
+                 *setup_frm++ = eaddrs[0];
+                 *setup_frm++ = eaddrs[1];
+                 *setup_frm++ = eaddrs[2];
                } while (++i < 15);
-
                /* Now add this frame to the Tx list. */
-               tulip_start_xmit((struct sk_buff *) -1, dev);
-       }
-}
-
-int
-tulip_hwinit(struct device *dev, int ioaddr,
-                        int irq, int device_id)
-{
-       /* See note below on the Znyx 315 etherarray. */
-       static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
-       static int last_irq;
-       char detect_mesg[80], *mesgp=detect_mesg;
-       struct tulip_private *tp = (struct tulip_private *)dev->priv;
-       int i;
-       unsigned short sum, bitsum;
-
-       if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
-               printk("tulip_hwinit: region already allocated at %#3x.\n",
-                          ioaddr);
-               return(-1);
-       }
-
-       mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip",
-                                        device_id == PCI_DEVICE_ID_DEC_TULIP_FAST,
-                                        device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS);
-
-       /* Stop the chip's Tx and Rx processes. */
-       tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6);
-       /* Clear the missed-packet counter. */
-       i = tio_read(CSR8) & 0xffff;
-
-       if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS
-           && (tio_read(CSR9) & 0x8000)) {
-               mesgp += sprintf(mesgp, " treat as 21040");
-           device_id = PCI_DEVICE_ID_DEC_TULIP;
-       }
-       
-       /* The station address ROM is read byte serially.  The register must
-          be polled, waiting for the value to be read bit serially from the
-          EEPROM.
-          */
-       sum = 0;
-       if (device_id == PCI_DEVICE_ID_DEC_TULIP) {
-               tio_write(0, CSR9);
-           /* Reset the pointer with a dummy write. */
-           bitsum = 0xff;
-           for (i = 0; i < 6; i++) {
-                       int value, boguscnt = 100000;
-                       do
-                               value = tio_read(CSR9);
-                       while (value < 0  && --boguscnt > 0);
-                       dev->dev_addr[i] = value;
-                       sum += value & 0xFF;
-                       bitsum &= value;
-           }
-       } else {
-           /* Must be a 21140/21041, with a serial EEPROM interface. */
-           struct eeprom eep;
-           u_char *addr;
-
-           if (read_eeprom(ioaddr, &eep) < 0) {
-                       addr = eep.ng_addr;/* broken EEPROM structure */
-           } else {
-                       addr = eep.ok_addr;/* DEC EtherWorks */
-           }
-           for (i = 0; i < ETH_ALEN; i++) {
-                       sum += addr[i];
-                       dev->dev_addr[i] = addr[i];
-           }
-       }
-       /* Make certain the data structures are quadword aligned. */
-
-       mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr);
-
-       /* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
-          The addresses of the subsequent ports are derived from the first. */
-       if (sum == 0) {
-               for (i = 0; i < ETH_ALEN - 1; i++)
-                       dev->dev_addr[i] = last_phys_addr[i];
-               dev->dev_addr[i] = last_phys_addr[i] + 1;
-               irq = last_irq;
-       }
-       for (i = 0; i < ETH_ALEN - 1; i++)
-               mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]);
-       mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n",
-                                        last_phys_addr[i] = dev->dev_addr[i], irq);
-       last_irq = irq;
-
-       /* copy ethernet address */
-       if (card_type(tp, device_id,
-                                 htonl((*(int*)dev->dev_addr) & 0xFFFFFF)))
-               for (i = 0; i < ETH_ALEN - 1; i++)
-                       last_phys_addr[i] = dev->dev_addr[i];
-       /* We do a request_region() only to register /proc/ioports info. */
-       request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature);
-
-       dev->base_addr = ioaddr;
-       dev->irq = irq;
-
-       /* The Tulip-specific entries in the device structure. */
-       dev->open = &tulip_open;
-       dev->hard_start_xmit = &tulip_start_xmit;
-       dev->stop = &tulip_close;
-       dev->get_stats = &tulip_get_stats;
-       dev->set_config = &tulip_config;
-       dev->set_multicast_list = &set_multicast_list;
-
-#ifdef MODULE
-       if (if_port == TULIP_AUTO_PORT)
-               if_port = TULIP_PORT;
-       else
-               tp->port_fix = 1;
-       dev->if_port = if_port;
-       tp->full_duplex = full_duplex;
-       tp->next_module = root_tulip_dev;
-       root_tulip_dev = dev;
-#else
-#ifdef TULIP_FULL_DUPLEX
-       tp->full_duplex = 1;
-#endif
-       dev->if_port = TULIP_PORT;
-#endif
-#ifdef TULIP_FIX_PORT
-       tp->port_fix = 1;
-#endif
-
-       printk("%s: %s %s", dev->name, tp->signature, detect_mesg);
-
-       /* Reset the xcvr interface and turn on heartbeat. */
-       tio_write(TSIAC_RESET, CSR13);
-       tio_write(TSIAC_CONFIG, CSR13);
-
-       return(0);
-}
-
-int tulip_probe(struct device *dev)
-{
-       static struct device *tulip_head=NULL;
-       u_char pci_bus, pci_device_fn, pci_latency, pci_irq;
-       u_int pci_ioaddr;
-       u_short pci_command, vendor_id, device_id;
-       u_int pci_chips[] = {
-               PCI_DEVICE_ID_DEC_TULIP,
-               PCI_DEVICE_ID_DEC_TULIP_FAST,
-               PCI_DEVICE_ID_DEC_TULIP_PLUS,
-               PCI_DEVICE_ID_NONE
-       };
-       int num=0, cno;
-       static int pci_index = 0;
-
-    if (!pcibios_present()) return(-ENODEV);
-
-       for (; pci_index < 0xff; pci_index++) {
-               if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
-                                                          &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
-                               break;
-
-               /* get vendor id */
-               pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
-                                                                &vendor_id);
-               /* get IRQ */
-               pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
-                                                                &pci_irq);
-
-               /* get device id */
-               pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
-                                                                &device_id);
-
-               /* get IO address */
-               pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-                                                                 &pci_ioaddr);
-
-               /* Remove I/O space marker in bit 0. */
-               pci_ioaddr &= ~3;
-               if (vendor_id != PCI_VENDOR_ID_DEC)
-                               continue;
-
-               for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++)
-                               if (device_id == pci_chips[cno])
-                                               break;
-               if (pci_chips[cno] == PCI_DEVICE_ID_NONE) {
-                       printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
-                                  " not configured.\n", device_id);
-                       continue;
-               }
-               dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) +
-                                                                                  sizeof (struct tulip_private) +
-                                                                                  ETHNAMSIZ, 8));
-               if (dev == NULL)
-                               break;
-
-               if (!tulip_head) {
-                       printk(version);
-                       tulip_head = dev;
-               }
-
-               /* Get and check the bus-master and latency values. */
-               pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-                                                                &pci_command);
-               if ((pci_command & PCI_COMMAND_MASTER) == 0) {
-                       printk("  PCI Master Bit has not been set!"
-                                  " Setting...\n");
-                       pci_command |= PCI_COMMAND_MASTER;
-                       pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-                                                                         pci_command);
-               }
-
-               pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER,
-                                                                &pci_latency);
-               
-               if (pci_latency < 10) {
-                       printk("  PCI latency timer (CFLT) is"
-                                  " unreasonably low at %d."
-                                  "  Setting to 100 clocks.\n", pci_latency);
-                       pcibios_write_config_byte(pci_bus, pci_device_fn,
-                                                                         PCI_LATENCY_TIMER, 100);
-               }
+               if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+                       /* Same setup recently queued, we need not add it. */
+               } else {
+                       unsigned long flags;
+                       unsigned int entry;
+                       
+                       save_flags(flags); cli();
+                       entry = tp->cur_tx++ % TX_RING_SIZE;
+
+                       if (entry != 0) {
+                         /* Avoid a chip errata by prefixing a dummy entry. */
+                         tp->tx_skbuff[entry] = 0;
+                         tp->tx_ring[entry].length =
+                               (entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+                         tp->tx_ring[entry].buffer1 = 0;
+                         tp->tx_ring[entry].status = 0x80000000;
+                         entry = tp->cur_tx++ % TX_RING_SIZE;
+                       }
 
-               if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) {
-                       continue;
+                       tp->tx_skbuff[entry] = 0;
+                       /* Put the setup frame on the Tx list. */
+                       if (entry == TX_RING_SIZE-1)
+                         tx_flags |= 0x02000000;               /* Wrap ring. */
+                       tp->tx_ring[entry].length = tx_flags;
+                       tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+                       tp->tx_ring[entry].status = 0x80000000;
+                       if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+                               dev->tbusy = 1;
+                               tp->tx_full = 1;
+                       }
+                       restore_flags(flags);
+                       /* Trigger an immediate transmit demand. */
+                       outl(0, ioaddr + CSR1);
                }
-               num++;
-#ifdef TULIP_MAX_CARDS
-               if (num >= TULIP_MAX_CARDS) return(0);
-#endif
+               outl(csr6 | 0x0000, ioaddr + CSR6);
        }
-       return(num > 0 ? 0: -ENODEV);
 }
-
+\f
 #ifdef MODULE
 
-/* The parameters that may be passed in... */
-/* This driver does nothing with options yet.  It will later be used to
-   pass the full-duplex flag, etc. */
-int debug = -1;
+/* An additional parameter that may be passed in... */
+static int debug = -1;
 
 int
 init_module(void)
 {
+       int cards_found;
+
+       if (debug >= 0)
+               tulip_debug = debug;
+
        root_tulip_dev = NULL;
-       return tulip_probe(NULL);
+       cards_found = tulip_probe(0);
+
+       return cards_found ? 0 : -ENODEV;
 }
 
 void
@@ -1433,21 +2082,21 @@ cleanup_module(void)
 
        /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
        while (root_tulip_dev) {
-               next_dev =
-                  ((struct tulip_private *) root_tulip_dev->priv)->next_module;
+               next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
                unregister_netdev(root_tulip_dev);
                release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
                kfree(root_tulip_dev);
                root_tulip_dev = next_dev;
        }
 }
-#endif /* MODULE */
 
+#endif  /* MODULE */
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
index 80bf86bddcb2788ed6b918ea72b63b9a6a417b5e..74dca22f53afcc06bf61b449af23eca2f0ae083d 100644 (file)
@@ -241,6 +241,7 @@ struct pci_dev_info dev_info[] = {
        DEVICE( INTEL,          INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"),
        DEVICE( INTEL,          INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
        DEVICE( INTEL,          INTEL_82437VX,  "82437VX Triton II"),
+       DEVICE( INTEL,          INTEL_82371AB,  "82371AB 430TX PIIX4"),
        DEVICE( INTEL,          INTEL_P6,       "Orion P6"),
        DEVICE( ADAPTEC,        ADAPTEC_7850,   "AIC-7850"),
        DEVICE( ADAPTEC,        ADAPTEC_7855,   "AIC-7855"),
index 90186fc7d197829435bae485bde347b29c7497b4..816cfe532c4a16c0515a6f272a36f4ad3ada53b6 100644 (file)
@@ -18,7 +18,14 @@ dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
 dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+    bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
+    int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+    bool '   Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
+    bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
+    int  '   delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+fi
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
index b251fc3f1c9ba6150abb6625a747a1993fa20378..caeb342eaae5134e8e10f3a26dba6a248bed0340 100644 (file)
@@ -348,6 +348,11 @@ else
   endif
 endif
 
+
+ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
+L_OBJS += ide-scsi.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 BusLogic.o: BusLogic.c FlashPoint.c
@@ -359,12 +364,8 @@ BusLogic.o: BusLogic.c FlashPoint.c
 aha152x.o: aha152x.c
        $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
 
-aic7xxx_asm:   aic7xxx_asm.c
-       $(HOSTCC) -o $@ aic7xxx_asm.c
-
-aic7xxx.c: aic7xxx_seq.h
-aic7xxx_seq.h: aic7xxx_asm aic7xxx.seq
-       ./aic7xxx_asm -o $@ aic7xxx.seq
+aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
+       $(CC) $(CFLAGS) -c -o $@ aic7xxx.c
 
 seagate.o: seagate.c
        $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
index f3982aa91258c1705d4082b850650d1557c5b04c..005c889d7503a8e1d7f4bc49abeb4213d0067070 100644 (file)
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
 /*+M*************************************************************************
  * Adaptec AIC7xxx device driver for Linux.
  *
  * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
  * ANSI SCSI-2 specification (draft 10c), ...
  *
- * ----------------------------------------------------------------
- *  Modified to include support for wide and twin bus adapters,
- *  DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
  *  SCB paging, and other rework of the code.
  *
- *  Parts of this driver are based on the FreeBSD driver by Justin
- *  T. Gibbs.
+ *  Parts of this driver were also based on the FreeBSD driver by
+ *  Justin T. Gibbs.  His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
  *
  *  A Boot time option was also added for not resetting the scsi bus.
  *
- *    Form:  aic7xxx=extended,no_reset
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
  *
- *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
  *
- *  $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
 #include "scsi.h"
 #include "hosts.h"
 #include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
 #include <linux/stat.h>
 #include <linux/malloc.h>      /* for kmalloc() */
 
 
 struct proc_dir_entry proc_scsi_aic7xxx = {
     PROC_SCSI_AIC7XXX, 7, "aic7xxx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+    S_IFDIR | S_IRUGO | S_IXUGO, 2,
+    0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b)        ((a < b) ? a : b)
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
 #define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -107,11 +165,8 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *     support because all PCI dependent code is bracketed with
  *     "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
  *
- *   o Twin bus support - this has been tested and does work.
- *
- *   o DMAing of SCBs - thanks to Kai Makisara, this now works.
- *     This define is now taken out and DMAing of SCBs is always
- *     performed (8/12/95 - DE).
+ *   o Twin bus support - this has been tested and does work.  It is
+ *     not an option anymore.
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *     LUN using its own heuristic based on the number of available
  *     SCBs.
  *
- *   o 3985 support - The 3985 adapter is much like the 3940, but
- *     has three 7870 controllers as opposed to two for the 3940.
- *     It will get probed and recognized as three different adapters,
- *     but all three controllers can share the same external bank of
- *     255 SCBs.  If you enable AIC7XXX_SHARE_SCBS, then the driver
- *     will attempt to share the common bank of SCBs between the three
- *     controllers of the 3985.  This is experimental and hasn't
- *     been tested.  By default, we do not share the bank of SCBs,
- *     and force the controllers to use their own internal bank of
- *     16 SCBs.  Please let us know if sharing the SCB array works.
+ *   o 3985 support - The 3985 adapter is much like the 3940, but has
+ *     three 7870 controllers as opposed to two for the 3940.  It will
+ *     be probed and recognized as three different adapters, but all
+ *     three controllers can share the same external bank of 255 SCBs.
+ *     If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ *     to use and share the common bank of SCBs between the three
+ *     controllers of the 3985.  This is experimental and hasn't been
+ *     been tested.  By default, we do not use external SCB RAM, and
+ *     force the controllers to use their own internal bank of 16 SCBs.
+ *     Please let us know if using the external SCB array works.
  *
  *   o SCB paging support - SCB paging is enabled by defining
  *     AIC7XXX_PAGE_ENABLE.  Support for this was taken from the
@@ -162,43 +217,54 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *  Note that sharing of IRQs is not an option any longer.  Linux supports
  *  it so we support it.
  *
- *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
  */
 
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
 /* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
 
 /*
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
 #define AIC7XXX_RESET_DELAY 15
+#endif
 
 /*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
  *
  * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
  * NOTE: This does affect performance since it has to maintain statistics.
  */
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
 
 /*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
  */
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
 
 /*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs.  For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
  */
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
 
 /*
  * For debugging the abort/reset code.
@@ -210,6 +276,87 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  */
 #define AIC7XXX_DEBUG
 
+/*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis.  The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on.  These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases.  The tag_commands is an array of
+ * 16 to allow for wide and twin adapters.  Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ * 
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN.  If SCB paging is
+ * enabled, the commands/LUN is 8.  When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+  char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system.  By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above).  When -1, the driver will
+ * not enable tagged queueing for that particular device.  When positive
+ * (> 0) the values in the array are used for the queue_depth.  Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+  {DEFAULT_TAG_COMMANDS},
+  {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler.  The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts.  Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts.  Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
 /*
  * Controller type and options
  */
@@ -232,14 +379,15 @@ typedef enum {
   AIC_7882,    /* PCI  aic7882 on 3940 Ultra */
   AIC_7883,    /* PCI  aic7883 on 3985 Ultra */
   AIC_7884     /* PCI  aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
 
 typedef enum {
   AIC_777x,    /* AIC-7770 based */
-  AIC_785x,    /* AIC-7850 based */
+  AIC_785x,    /* AIC-7850 based (3 SCBs)*/
+  AIC_786x,    /* AIC-7860 based (7850 ultra) */
   AIC_787x,    /* AIC-7870 based */
-  AIC_788x     /* AIC-7880 based */
-} aha_chip_type;
+  AIC_788x     /* AIC-7880 based (ultra) */
+} aha_chip_class_type;
 
 typedef enum {
   AIC_SINGLE,  /* Single Channel */
@@ -269,24 +417,24 @@ typedef enum {
  * Don't forget to change this when changing the types!
  */
 static const char *board_names[] = {
-  "<AIC-7xxx Unknown>",                /* AIC_NONE */
-  "AIC-7770",                  /* AIC_7770 */
-  "AHA-2740",                  /* AIC_7771 */
-  "AHA-2840",                  /* AIC_284x */
-  "AIC-7850",                  /* AIC_7850 */
-  "AIC-7855",                  /* AIC_7855 */
-  "AIC-7850 Ultra",            /* AIC_7860 */
-  "AHA-2940A Ultra",           /* AIC_7861 */
-  "AIC-7870",                  /* AIC_7870 */
-  "AHA-2940",                  /* AIC_7871 */
-  "AHA-3940",                  /* AIC_7872 */
-  "AHA-3985",                  /* AIC_7873 */
-  "AHA-2940 Differential",     /* AIC_7874 */
-  "AIC-7880 Ultra",            /* AIC_7880 */
-  "AHA-2940 Ultra",            /* AIC_7881 */
-  "AHA-3940 Ultra",            /* AIC_7882 */
-  "AHA-3985 Ultra",            /* AIC_7883 */
-  "AHA-2940 Ultra Differential"        /* AIC_7884 */
+  "AIC-7xxx Unknown",                                  /* AIC_NONE */
+  "Adaptec AIC-7770 SCSI host adapter",                        /* AIC_7770 */
+  "Adaptec AHA-274X SCSI host adapter",                        /* AIC_7771 */
+  "Adaptec AHA-284X SCSI host adapter",                        /* AIC_284x */
+  "Adaptec AIC-7850 SCSI host adapter",                        /* AIC_7850 */
+  "Adaptec AIC-7855 SCSI host adapter",                        /* AIC_7855 */
+  "Adaptec AIC-7860 Ultra SCSI host adapter",          /* AIC_7860 */
+  "Adaptec AHA-2940A Ultra SCSI host adapter",         /* AIC_7861 */
+  "Adaptec AIC-7870 SCSI host adapter",                        /* AIC_7870 */
+  "Adaptec AHA-294X SCSI host adapter",                        /* AIC_7871 */
+  "Adaptec AHA-394X SCSI host adapter",                        /* AIC_7872 */
+  "Adaptec AHA-398X SCSI host adapter",                        /* AIC_7873 */
+  "Adaptec AHA-2944 SCSI host adapter",                        /* AIC_7874 */
+  "Adaptec AIC-7880 Ultra SCSI host adapter",          /* AIC_7880 */
+  "Adaptec AHA-294X Ultra SCSI host adapter",          /* AIC_7881 */
+  "Adaptec AHA-394X Ultra SCSI host adapter",          /* AIC_7882 */
+  "Adaptec AHA-398X Ultra SCSI host adapter",          /* AIC_7883 */
+  "Adaptec AHA-2944 Ultra SCSI host adapter"           /* AIC_7884 */
 };
 
 /*
@@ -310,12 +458,17 @@ static const char *board_names[] = {
  */
 #define DID_RETRY_COMMAND DID_ERROR
 
+#define HSCSIID        0x07
+#define HWSCSIID       0x0F
+#define SCSI_RESET     0x040
+
 /*
  * EISA/VL-bus stuff
  */
 #define MINSLOT                1
 #define MAXSLOT                15
 #define SLOTBASE(x)    ((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
 
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
@@ -333,14 +486,6 @@ static const char *board_names[] = {
 
 #define INTDEF         0x5C            /* Interrupt Definition Register */
 
-/*
- * Some defines for the HCNTRL register.
- */
-#define        REQ_PAUSE       IRQMS | INTEN | PAUSE
-#define        UNPAUSE_274X    IRQMS | INTEN
-#define        UNPAUSE_284X    INTEN
-#define        UNPAUSE_294X    IRQMS | INTEN
-
 /*
  * AIC-78X0 PCI registers
  */
@@ -377,7 +522,7 @@ static const char *board_names[] = {
  * each word, while the C56 and C66 (4096 bits) use 8 bits to
  * address each word.
  */
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
 
 /*
  *
@@ -417,15 +562,15 @@ struct seeprom_config {
 /*
  * Host Adapter Control Bits
  */
-/* UNUSED               0x0001 */
+#define CFAUTOTERM      0x0001          /* Perform Auto termination */
 #define CFULTRAEN       0x0002          /* Ultra SCSI speed enable (Ultra cards) */
 #define CF284XSELTO     0x0003          /* Selection timeout (284x cards) */
 #define CF284XFIFO      0x000C          /* FIFO Threshold (284x cards) */
-#define CFSTERM         0x0004          /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM         0x0004          /* SCSI low byte termination */
 #define CFWSTERM        0x0008          /* SCSI high byte termination (wide card) */
 #define CFSPARITY      0x0010          /* SCSI parity */
 #define CF284XSTERM    0x0020          /* SCSI low byte termination (284x cards) */
-#define CFRESETB       0x0040          /* reset SCSI bus at IC initialization */
+#define CFRESETB       0x0040          /* reset SCSI bus at boot */
 /* UNUSED              0xFF80 */
   unsigned short adapter_control;      /* word 17 */
 
@@ -448,35 +593,17 @@ struct seeprom_config {
   unsigned short checksum;             /* word 31 */
 };
 
+#define SELBUS_MASK            0x0a
+#define        SELNARROW       0x00
+#define        SELBUSB         0x08
+#define SINGLE_BUS             0x00
 
-#define SCSI_RESET 0x040
-
-/*
- * 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) \
-  outb(p->pause, HCNTRL + p->base);                    \
-  while ((inb(HCNTRL + p->base) & PAUSE) == 0)         \
-    ;                                                  \
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
-  outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
-  do {                                                 \
-    outb(SEQRESET | FASTMODE, SEQCTL + p->base);       \
-  } while (inb(SEQADDR0 + p->base) != 0 &&             \
-          inb(SEQADDR1 + p->base) != 0);               \
-  UNPAUSE_SEQUENCER(p);
+#define SCB_TARGET(scb)         \
+       (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb)            \
+       ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb)   \
+       (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
 
 /*
  * If an error occurs during a data transfer phase, run the command
@@ -539,12 +666,6 @@ static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1];
  */
 static int aic7xxx_spurious_count;
 
-/*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
 /*
  * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
  * in the scatter-gather lists.  We need to convert the virtual
@@ -558,20 +679,28 @@ struct hw_scatterlist {
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define        MAX_SG 256
+#define        AIC7XXX_MAX_SG 27
 
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+
+struct aic7xxx_hwscb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /* 0*/  unsigned char control;
 /* 1*/  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /* 2*/  unsigned char target_status;
 /* 3*/  unsigned char SG_segment_count;
-/* 4*/  unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/  unsigned int  SG_list_pointer;
 /* 8*/  unsigned char residual_SG_segment_count;
-/* 9*/  unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/  unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/  unsigned int  data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/  unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/  unsigned char residual_data_count[3];
+/*12*/  unsigned int  data_pointer;
+/*16*/  unsigned int  data_count;
+/*20*/  unsigned int  SCSI_cmd_pointer;
 /*24*/  unsigned char SCSI_cmd_length;
 /*25*/ u_char tag;                     /* Index into our kernel SCB array.
                                         * Also used as the tag for tagged I/O
@@ -579,29 +708,47 @@ struct aic7xxx_scb {
 #define SCB_PIO_TRANSFER_SIZE  26      /* amount we need to upload/download
                                         * via PIO to initialize a transaction.
                                         */
-/*26*/  u_char next;                    /* Used to thread SCBs awaiting selection
+/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
                                          * or disconnected down in the sequencer.
                                          */
-       /*-----------------end of hardware supported fields----------------*/
-       Scsi_Cmnd          *cmd;        /* Scsi_Cmnd for this scb */
-        struct aic7xxx_scb *q_next;     /* next scb in queue */
-#define SCB_FREE               0x00
-#define SCB_ACTIVE             0x01
-#define SCB_ABORTED            0x02
-#define SCB_DEVICE_RESET       0x04
-#define SCB_IMMED              0x08
-#define SCB_SENSE              0x10
-#define SCB_QUEUED_FOR_DONE    0x40
-#define SCB_PAGED_OUT          0x80
-#define SCB_WAITINGQ           0x100
-#define SCB_ASSIGNEDQ          0x200
-#define SCB_SENTORDEREDTAG     0x400
-#define SCB_IN_PROGRESS        (SCB_ACTIVE | SCB_PAGED_OUT | \
-                                SCB_WAITINGQ | SCB_ASSIGNEDQ)
-       int                 state;          /* current state of scb */
-       unsigned int        position;       /* Position in scb array */
-       struct hw_scatterlist  sg_list[MAX_SG]; /* SG list in adapter format */
-       unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+/*27*/  unsigned char prev;
+/*28*/  unsigned int pad;               /*
+                                         * Unused by the kernel, but we require
+                                         * the padding so that the array of
+                                         * hardware SCBs is alligned on 32 byte
+                                         * boundaries so the sequencer can index
+                                         */
+};
+
+typedef enum {
+       SCB_FREE                = 0x0000,
+       SCB_ACTIVE              = 0x0001,
+       SCB_ABORTED             = 0x0002,
+       SCB_DEVICE_RESET        = 0x0004,
+       SCB_SENSE               = 0x0008,
+       SCB_TIMEDOUT            = 0x0010,
+       SCB_QUEUED_FOR_DONE     = 0x0020,
+       SCB_RECOVERY_SCB        = 0x0040,
+       SCB_WAITINGQ            = 0x0080,
+       SCB_ASSIGNEDQ           = 0x0100,
+       SCB_SENTORDEREDTAG      = 0x0200,
+       SCB_MSGOUT_SDTR         = 0x0400,
+       SCB_MSGOUT_WDTR         = 0x0800,
+       SCB_ABORT               = 0x1000,
+       SCB_QUEUED_ABORT        = 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+        struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
+       Scsi_Cmnd             *cmd;           /* Scsi_Cmnd for this scb */
+        struct aic7xxx_scb    *q_next;        /* next scb in queue */
+       scb_flag_type          flags;         /* current state of scb */
+       struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          sg_count;
+       unsigned char          sense_cmd[6];  /*
+                                               * Allocate 6 characters for
+                                               * sense command.
+                                               */
 };
 
 /*
@@ -626,20 +773,17 @@ static unsigned char
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
+  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
-  int            numscbs;          /* current number of scbs */
-  int            activescbs;       /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB 255
+  unsigned char  numscbs;          /* current number of scbs */
+  unsigned char  maxhscbs;         /* hardware scbs */
+  unsigned char  maxscbs;          /* max scbs including pageable scbs */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  unsigned int   reserve[100];
+} scb_data_type;
 
 /*
  * Define a structure used for each host adapter, only one per IRQ.
@@ -647,17 +791,26 @@ typedef struct {
 struct aic7xxx_host {
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /*   channel B for twin adapters */
+  int                      irq;              /* IRQ for this adapter */
   int                      base;             /* card base address */
-  int                      maxhscbs;         /* hardware SCBs */
-  int                      maxscbs;          /* max SCBs (including pageable) */
-#define A_SCANNED              0x0001
-#define B_SCANNED              0x0002
-#define EXTENDED_TRANSLATION   0x0004
-#define HAVE_SEEPROM           0x0008
-#define ULTRA_ENABLED          0x0010
-#define PAGE_ENABLED           0x0020
-#define IN_ISR                 0x0040
-#define USE_DEFAULTS           0x0080
+  unsigned int             mbase;            /* I/O memory address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
+#define A_SCANNED               0x0001
+#define B_SCANNED               0x0002
+#define EXTENDED_TRANSLATION    0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL           0x0010
+#define ULTRA_ENABLED           0x0020
+#define PAGE_ENABLED            0x0040
+#define USE_DEFAULTS            0x0080
+#define BIOS_ENABLED            0x0100
+#define IN_ISR                  0x0200
+#define IN_TIMEOUT              0x0400
+#define SHARED_SCBDATA          0x0800
+#define HAVE_SEEPROM            0x1000
   unsigned int             flags;
   unsigned int             isr_count;        /* Interrupt count */
   unsigned short           needsdtr_copy;    /* default config */
@@ -668,36 +821,22 @@ struct aic7xxx_host {
   unsigned short           wdtr_pending;
   unsigned short           orderedtag;
   unsigned short           discenable;      /* Targets allowed to disconnect */
-  aha_type                 type;             /* card type */
-  aha_chip_type            chip_type;        /* chip base type */
+  aha_chip_type            chip_type;        /* card type */
+  aha_chip_class_type      chip_class;
   aha_bus_type             bus_type;         /* normal/twin/wide bus */
-  char *                   mbase;            /* I/O memory address */
-  unsigned char            chan_num;         /* for 3940/3985, channel number */
+  unsigned char            chan_num;         /* for 39xx, channel number */
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   unsigned char            qcntmask;
-  struct seeprom_config    seeprom;
+  unsigned char            qfullcount;
+  unsigned char            curqincnt;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
-  struct aic7xxx_scb      *scb_array[AIC7XXX_MAXSCB];  /* active commands */
-  struct aic7xxx_scb      *pagedout_ntscbs[16];  /*
-                                                  * paged-out, non-tagged scbs
-                                                  * indexed by target.
-                                                  */
-  scb_queue_type           page_scbs;        /*
-                                              * SCBs that will require paging
-                                              * before use (no assigned slot)
-                                              */
+  unsigned char            activescbs;       /* active scbs */
   scb_queue_type           waiting_scbs;     /*
-                                              * SCBs waiting to be paged and
-                                              * started.
+                                              * SCBs waiting for space in
+                                              * the QINFIFO.
                                               */
-  scb_queue_type           assigned_scbs;    /*
-                                              * SCBs that were waiting but have
-                                              * have now been assigned a slot
-                                              * by aic7xxx_free_scb
-                                              */
-  scb_usage_type           scb_usage;
-  scb_usage_type          *scb_link;
+  scb_data_type           *scb_data;
 
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
@@ -709,6 +848,7 @@ struct aic7xxx_host {
 #define  BUS_DEVICE_RESET_PENDING       0x02
     int  flags;
     int  commands_sent;
+    int  active_cmds;
   } device_status[16];
 #ifdef AIC7XXX_PROC_STATS
   /*
@@ -734,34 +874,10 @@ struct aic7xxx_host {
 #endif /* AIC7XXX_PROC_STATS */
 };
 
-struct aic7xxx_host_config {
-  int              irq;        /* IRQ number */
-  int              mbase;      /* memory base address*/
-  int              base;       /* I/O base address*/
-  int              maxhscbs;   /* hardware SCBs */
-  int              maxscbs;    /* max SCBs (including pageable) */
-  int              unpause;    /* unpause value for HCNTRL */
-  int              pause;      /* pause value for HCNTRL */
-  int              scsi_id;    /* host SCSI ID */
-  int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
-  unsigned int     flags;      /* used the same as struct aic7xxx_host flags */
-  int              chan_num;   /* for 3940/3985, channel number */
-  unsigned char    busrtime;   /* bus release time */
-  unsigned char    bus_speed;  /* bus speed */
-  unsigned char    qcntmask;
-  aha_type         type;       /* card type */
-  aha_chip_type    chip_type;  /* chip base type */
-  aha_bus_type     bus_type;   /* normal/twin/wide bus */
-  aha_status_type  bios;       /* BIOS is enabled/disabled */
-  aha_status_type  parity;     /* bus parity enabled/disabled */
-  aha_status_type  low_term;   /* bus termination low byte */
-  aha_status_type  high_term;  /* bus termination high byte (wide cards only) */
-};
-
 /*
  * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
  */
 static struct {
   short period;
@@ -770,17 +886,17 @@ static struct {
   short rate;
   const char *english;
 } aic7xxx_syncrates[] = {
-  {  50,  0x100,  "20.0"  },
-  {  62,  0x110,  "16.0"  },
-  {  75,  0x120,  "13.4"  },
-  { 100,  0x000,  "10.0"  },
-  { 125,  0x010,   "8.0"  },
-  { 150,  0x020,   "6.67" },
-  { 175,  0x030,   "5.7"  },
-  { 200,  0x040,   "5.0"  },
-  { 225,  0x050,   "4.4"  },
-  { 250,  0x060,   "4.0"  },
-  { 275,  0x070,   "3.6"  }
+  { 12,  0x100,  "20.0"  },
+  { 15,  0x110,  "16.0"  },
+  { 18,  0x120,  "13.4"  },
+  { 25,  0x000,  "10.0"  },
+  { 31,  0x010,   "8.0"  },
+  { 37,  0x020,   "6.67" },
+  { 43,  0x030,   "5.7"  },
+  { 50,  0x040,   "5.0"  },
+  { 56,  0x050,   "4.4"  },
+  { 62,  0x060,   "4.0"  },
+  { 68,  0x070,   "3.6"  }
 };
 
 static int num_aic7xxx_syncrates =
@@ -789,166 +905,51 @@ static int num_aic7xxx_syncrates =
 #ifdef CONFIG_PCI
 static int number_of_3940s = 0;
 static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
 
 #ifdef AIC7XXX_DEBUG
 
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
-  int scsi_conf;
-  unsigned char brelease;
-  unsigned char dfthresh;
-
-  static int DFT[] = { 0, 50, 75, 100 };
-  static int SST[] = { 256, 128, 64, 32 };
-  static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
-  scsi_conf = inb(SCSICONF + p->base);
-
-  /*
-   * Scale the Data FIFO Threshhold and the Bus Release Time; they are
-   * stored in formats compatible for writing to sequencer registers.
-   */
-  dfthresh = p->bus_speed  >> 6;
-
-  if (p->chip_type == AIC_777x)
-  {
-    brelease = p->busrtime >> 2;
-  }
-  else
-  {
-    brelease = p->busrtime;
-  }
-  if (brelease == 0)
-  {
-    brelease = 2;
-  }
-
-  switch (p->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_284x:
-      printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
-             BUSW[p->bus_type], p->base, p->mbase);
-      break;
-
-    default:
-      panic("aic7xxx: (debug_config) internal error.\n");
-  }
-
-  printk("    irq %d\n"
-        "    bus release time %d bclks\n"
-        "    data fifo threshold %d%%\n",
-        p->irq,
-        brelease,
-        DFT[dfthresh]);
-
-  printk("    SCSI CHANNEL A:\n"
-        "        scsi id %d\n"
-        "        scsi selection timeout %d ms\n"
-        "        scsi bus reset at power-on %sabled\n",
-        (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07),
-        SST[(scsi_conf >> 3) & 0x03],
-        (scsi_conf & 0x40) ? "en" : "dis");
-
-  if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
-  {
-    /*
-     * Set the parity for 7770 based cards.
-     */
-    p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->parity != AIC_UNKNOWN)
-  {
-    printk("        scsi bus parity %sabled\n",
-          (p->parity == AIC_ENABLED) ? "en" : "dis");
-  }
-
-  if ((p->type == AIC_7770) || (p->type == AIC_7771))
-  {
-    p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->low_term != AIC_UNKNOWN)
-  {
-    printk("        scsi bus termination (low byte) %sabled\n",
-         (p->low_term == AIC_ENABLED) ? "en" : "dis");
-  }
-  if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
-  {
-    printk("        scsi bus termination (high byte) %sabled\n",
-         (p->high_term == AIC_ENABLED) ? "en" : "dis");
-  }
-}
-
 #if 0
 static void
 debug_scb(struct aic7xxx_scb *scb)
 {
-  printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
-         scb->control, scb->target_channel_lun, scb->SG_segment_count,
-         (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
-         (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
-         (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
-         (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
-         scb->SCSI_cmd_length);
-  printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
-         (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
-         scb->residual_SG_segment_count,
-         ((scb->residual_data_count[2] << 16) |
-          (scb->residual_data_count[1] <<  8) |
-          (scb->residual_data_count[0]));
-  printk("data ptr 0x%x, data count %d, next waiting %d\n",
-         (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
-         (scb->data_pointer[1] << 8) | scb->data_pointer[0],
-         scb->data_count, scb->next_waiting);
-  printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
-         (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
-         scb->position);
+  struct aic7xxx_hwscb *hscb = scb->hscb;
+
+  printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+    scb,
+    hscb->control,
+    hscb->target_channel_lun,
+    hscb->SCSI_cmd_length,
+    hscb->SCSI_cmd_pointer );
+  printk("        datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+    hscb->data_count,
+    hscb->data_pointer,
+    hscb->SG_segment_count,
+    hscb->SG_list_pointer);
+  printk("        sg_addr:%lx sg_len:%ld\n",
+    hscb->sg_list[0].address,
+    hscb->sg_list[0].length);
 }
 #endif
 
 #else
-#  define debug_config(x)
 #  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
-#define TCL_OF_SCB(x)  (((x)->target_channel_lun >> 4) & 0xf),  \
-                       (((x)->target_channel_lun >> 3) & 0x01), \
-                       ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+                        ((scb->hscb)->target_channel_lun & 0x07)
 
-#define TARGET_INDEX(x)  ((x)->target | ((x)->channel << 3))
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                       (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system. This should be fixed, but then,
- *       does anyone really have more than one in a machine?
+ *       cards in the system.  This should be fixed.
  */
 static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
 static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
@@ -958,6 +959,53 @@ static int aic7xxx_irq_trigger = -1;         /*
                                               *  1 use level triggered
                                               */
 static int aic7xxx_enable_ultra = 0;         /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0;                     /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ ***************************************************************************/
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+  if (p->maddr != NULL)
+    return (p->maddr[port]);
+  else
+    return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+  if (p->maddr != NULL)
+    p->maddr[port] = val;
+  else
+    outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+  if (p->maddr != NULL)
+  {
+    __asm __volatile("
+      cld;
+    1:  lodsb;
+      movb %%al,(%0);
+      loop 1b"      :
+              :
+      "r" ((p)->maddr + (port)),
+      "S" ((valp)), "c" ((size))  :
+      "%esi", "%ecx", "%eax");
+  }
+  else
+  {
+    outsb(p->base + port, valp, size);
+  }
+}
 
 /*+F*************************************************************************
  * Function:
@@ -982,6 +1030,7 @@ aic7xxx_setup(char *s, int *dummy)
     { "no_reset",    &aic7xxx_no_reset },
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "ultra",       &aic7xxx_enable_ultra },
+    { "verbose",     &aic7xxx_verbose },
     { NULL,          NULL }
   };
 
@@ -1007,150 +1056,328 @@ aic7xxx_setup(char *s, int *dummy)
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_loadseq
+ *   pause_sequencer
  *
  * Description:
- *   Load the sequencer code into the controller memory.
+ *   Pause the sequencer and wait for it to actually stop - this
+ *   is important since the sequencer can disable pausing for critical
+ *   sections.
  *-F*************************************************************************/
-static void
-aic7xxx_loadseq(int base)
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
 {
-  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 "aic7xxx_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(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
-
-  outsb(SEQRAM + base, seqprog, sizeof(seqprog));
-
-  /*
-   * 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?)
-   */
-  do {
-    /*
-     * Actually, reset it until
-     * the address shows up as
-     * zero just to be safe..
-     */
-    outb(SEQRESET | FASTMODE, SEQCTL + base);
-  } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+  outb(p->pause, p->base + HCNTRL);
+  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+  {
+    ;
+  }
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_delay
+ *   unpause_sequencer
  *
  * Description:
- *   Delay for specified amount of time.
+ *   Unpause the sequencer. Unremarkable, yet done often enough to
+ *   warrant an easy way to do it.
  *-F*************************************************************************/
-static void
-aic7xxx_delay(int seconds)
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
 {
-  unsigned long i;
-
-  i = jiffies + (seconds * HZ);  /* compute time to stop */
-
-  while (jiffies < i)
+  if (unpause_always ||
+      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
   {
-    ;  /* Do nothing! */
+    outb(p->unpause, p->base + HCNTRL);
   }
 }
 
 /*+F*************************************************************************
  * Function:
- *   rcs_version
+ *   restart_sequencer
  *
  * Description:
- *   Return a string containing just the RCS version number from either
- *   an Id or Revision RCS clause.
+ *   Restart the sequencer program from address zero.  This assumes
+ *   that the sequencer is already paused.
  *-F*************************************************************************/
-const char *
-rcs_version(const char *version_info)
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
 {
-  static char buf[10];
-  char *bp, *ep;
+  /* Set the sequencer address to 0. */
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
 
-  bp = NULL;
-  strcpy(buf, "????");
-  if (!strncmp(version_info, "$Id: ", 5))
-  {
-    if ((bp = strchr(version_info, ' ')) != NULL)
-    {
-      bp++;
-      if ((bp = strchr(bp, ' ')) != NULL)
-      {
-       bp++;
-      }
-    }
-  }
-  else
+  /*
+   * Reset and unpause the sequencer.  The reset is suppose to
+   * start the sequencer running, but we do an unpause to make
+   * sure.
+   */
+  outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+  unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_next_patch
+ *
+ * Description:
+ *   Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+  while (cur_patch != NULL)
   {
-    if (!strncmp(version_info, "$Revision: ", 11))
+    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+      || (instrptr >= cur_patch->end))
     {
-      if ((bp = strchr(version_info, ' ')) != NULL)
+      /*
+       * Either we want to keep this section of code, or we have consumed
+       * this patch.  Skip to the next patch.
+       */
+      cur_patch++;
+      if (cur_patch->options == 0)
       {
-       bp++;
+        /* Out of patches. */
+        cur_patch = NULL;
       }
     }
-  }
-
-  if (bp != NULL)
-  {
-    if ((ep = strchr(bp, ' ')) != NULL)
+    else
     {
-      register int len = ep - bp;
-
-      strncpy(buf, bp, len);
-      buf[len] = '\0';
+      /* Found an OK patch. */
+      break;
     }
   }
-
-  return buf;
+  return (cur_patch);
 }
 
+
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_info
+ *   aic7xxx_download_instr
  *
  * Description:
- *   Return a string describing the driver.
+ *   Find the next patch to download.
  *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
 {
-  static char buffer[128];
+  unsigned char opcode;
+  struct ins_format3 *instr;
 
-  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
-  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+  instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+  /* Pull the opcode */
+  opcode = instr->opcode_addr >> 1;
+  switch (opcode)
+  {
+    case AIC_OP_JMP:
+    case AIC_OP_JC:
+    case AIC_OP_JNC:
+    case AIC_OP_CALL:
+    case AIC_OP_JNE:
+    case AIC_OP_JNZ:
+    case AIC_OP_JE:
+    case AIC_OP_JZ:
+    {
+      int address_offset;
+      struct ins_format3 new_instr;
+      unsigned int address;
+      struct patch *patch;
+      int i;
+
+      address_offset = 0;
+      new_instr = *instr;  /* Strucure copy */
+      address = new_instr.address;
+      address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+      for (i = 0; i < NUMBER(patches); i++)
+      {
+        patch = &patches[i];
+        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        {
+          if (address >= patch->end)
+          {
+            address_offset += patch->end - patch->begin;
+          }
+        }
+      }
+      address -= address_offset;
+      new_instr.address = address &0xFF;
+      new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+      new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+      break;
+    }
+
+    case AIC_OP_OR:
+    case AIC_OP_AND:
+    case AIC_OP_XOR:
+    case AIC_OP_ADD:
+    case AIC_OP_ADC:
+    case AIC_OP_ROL:
+      outsb(p->base + SEQRAM, &instr->immediate, 4);
+      break;
+
+    default:
+      panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+      break;
+  }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_loadseq
+ *
+ * Description:
+ *   Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+  int options;
+  struct patch *cur_patch;
+  int i;
+  int downloaded;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+  }
+  options = 1;  /* Code for all options. */
+  downloaded = 0;
+  if ((p->flags & ULTRA_ENABLED) != 0)
+    options |= ULTRA;
+  if (p->bus_type == AIC_TWIN)
+    options |= TWIN_CHANNEL;
+  if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+    options |= SCB_PAGING;
+
+  cur_patch = patches;
+  outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  for (i = 0; i < sizeof(seqprog) / 4;  i++)
+  {
+    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    {
+      /* Skip this instruction for this configuration. */
+      continue;
+    }
+    aic7xxx_download_instr(p, options, i);
+    downloaded++;
+  }
+
+  outb(FASTMODE, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  if (aic7xxx_verbose)
+  {
+     printk(" %d instructions downloaded\n", downloaded);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_delay
+ *
+ * Description:
+ *   Delay for specified amount of time.  We use udelay because the timer
+ *   interrupt is not guaranteed to be enabled.  This will cause an
+ *   infinite loop since jiffies (clock ticks) is not updated.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+  int i;
+
+  /*                        
+   * Call udelay() for 1 millisecond inside a loop for  
+   * the requested amount of seconds.
+   */
+  for (i=0; i < seconds*1000; i++)
+  {
+    udelay(1000);  /* Delay for 1 millisecond. */
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   rcs_version
+ *
+ * Description:
+ *   Return a string containing just the RCS version number from either
+ *   an Id or Revision RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+  static char buf[10];
+  char *bp, *ep;
+
+  bp = NULL;
+  strcpy(buf, "????");
+  if (!strncmp(version_info, "$Id: ", 5))
+  {
+    if ((bp = strchr(version_info, ' ')) != NULL)
+    {
+      bp++;
+      if ((bp = strchr(bp, ' ')) != NULL)
+      {
+       bp++;
+      }
+    }
+  }
+  else
+  {
+    if (!strncmp(version_info, "$Revision: ", 11))
+    {
+      if ((bp = strchr(version_info, ' ')) != NULL)
+      {
+       bp++;
+      }
+    }
+  }
+
+  if (bp != NULL)
+  {
+    if ((ep = strchr(bp, ' ')) != NULL)
+    {
+      register int len = ep - bp;
+
+      strncpy(buf, bp, len);
+      buf[len] = '\0';
+    }
+  }
+
+  return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
   strcat(buffer, "/");
   strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
   strcat(buffer, "/");
   strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
 
   return buffer;
 }
@@ -1160,9 +1387,12 @@ aic7xxx_info(struct Scsi_Host *notused)
  *   aic7xxx_length
  *
  * Description:
- *   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.
+ *   How much data should be transferred for this SCSI command?  Assume
+ *   all segments are to be transferred except for the last sg_last
+ *   segments.  This will allow us to compute underflow easily.  To
+ *   calculate the total length of the command, use sg_last = 0.  To
+ *   calculate the length of all but the last 2 SG segments, use
+ *   sg_last = 2.
  *-F*************************************************************************/
 static unsigned
 aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1176,7 +1406,7 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
 
   if (cmd->use_sg)
   {
-    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    for (i = length = 0; i < segments; i++)
     {
       length += sg[i].length;
     }
@@ -1198,9 +1428,9 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
  *-F*************************************************************************/
 static void
 aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    short period, unsigned char offset, int target, char channel)
+    unsigned char *period, unsigned char *offset, int target, char channel)
 {
-  int i;
+  int i = num_aic7xxx_syncrates;
   unsigned long ultra_enb_addr;
   unsigned char ultra_enb, sxfrctl0;
 
@@ -1208,11 +1438,11 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
    * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if (offset != 0)
+  if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
-      if ((aic7xxx_syncrates[i].period - period) >= 0)
+      if (*period <= aic7xxx_syncrates[i].period)
       {
         /*
          * Watch out for Ultra speeds when ultra is not enabled and
@@ -1228,99 +1458,57 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
            */
           continue;
         }
-        *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+        *period = aic7xxx_syncrates[i].period;
 
-        /*
-         * Ensure Ultra mode is set properly for this target.
-         */
-        ultra_enb_addr = ULTRA_ENB;
-        if ((channel == 'B') || (target > 7))
-        {
-          ultra_enb_addr++;
-        }
-        ultra_enb = inb(p->base + ultra_enb_addr);
-        sxfrctl0 = inb(p->base + SXFRCTL0);
-        if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
-        {
-          ultra_enb |= 0x01 << (target & 0x07);
-          sxfrctl0 |= ULTRAEN;
-        }
-        else
+        if (aic7xxx_verbose)
         {
-          ultra_enb &= ~(0x01 << (target & 0x07));
-          sxfrctl0 &= ~ULTRAEN;
+          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, target, channel,
+                 aic7xxx_syncrates[i].english, *offset);
         }
-        outb(ultra_enb, p->base + ultra_enb_addr);
-        outb(sxfrctl0, p->base + SXFRCTL0);
-
-        printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-               "offset %d.\n", p->host_no, target, channel,
-               aic7xxx_syncrates[i].english, offset);
-        return;
+        break;
       }
     }
   }
 
-  /*
-   * Default to asynchronous transfer
-   */
-  *scsirate = 0;
-  printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
-         p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_putscb
- *
- * Description:
- *   Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  outb(SCBAUTO, SCBCNT + base);
+  if (i >= num_aic7xxx_syncrates)
+  {
+    /*
+     * Use asynchronous transfers.
+     */
+    *scsirate = 0;
+    *period = 0;
+    *offset = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+             p->host_no, target, channel);
+    }
+  }
 
   /*
-   * 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.
-   *
-   * We can do 16bit transfers on all but 284x.
+   * Ensure Ultra mode is set properly for this target.
    */
-  if (p->type == AIC_284x)
+  ultra_enb_addr = ULTRA_ENB;
+  if ((channel == 'B') || (target > 7))
+  {
+    ultra_enb_addr++;
+  }
+  ultra_enb = inb(p->base + ultra_enb_addr);
+  sxfrctl0 = inb(p->base + SXFRCTL0);
+  if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
   {
-    outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+    ultra_enb |= 0x01 << (target & 0x07);
+    sxfrctl0 |= FAST20;
   }
   else
   {
-    outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+    ultra_enb &= ~(0x01 << (target & 0x07));
+    sxfrctl0 &= ~FAST20;
   }
-
-  outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_getscb
- *
- * Description:
- *   Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  /*
-   * This is almost identical to aic7xxx_putscb().
-   */
-  outb(SCBAUTO, SCBCNT + base);
-  insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
-  outb(0, SCBCNT + base);
+  outb(ultra_enb, p->base + ultra_enb_addr);
+  outb(sxfrctl0, p->base + SXFRCTL0);
 }
 
 /*+F*************************************************************************
@@ -1372,6 +1560,47 @@ scbq_remove_head(scb_queue_type *queue)
     queue->tail = NULL;
 }
 
+/*+F*************************************************************************
+ * Function:
+ *   scbq_remove
+ *
+ * Description:
+ *   Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+  if (queue->head == scb)
+  {
+    /* At beginning of queue, remove from head. */
+    scbq_remove_head(queue);
+  }
+  else
+  {
+    struct aic7xxx_scb *curscb = queue->head;
+
+    /*
+     * Search until the next scb is the one we're looking for, or
+     * we run out of queue.
+     */
+    while ((curscb != NULL) && (curscb->q_next != scb))
+    {
+      curscb = curscb->q_next;
+    }
+    if (curscb != NULL)
+    {
+      /* Found it. */
+      curscb->q_next = scb->q_next;
+      if (scb->q_next == NULL)
+      {
+        /* Update the tail when removing the tail. */
+        queue->tail = curscb;
+      }
+    }
+  }
+}
+
 /*+F*************************************************************************
  * Function:
  *   scbq_insert_tail
@@ -1403,23 +1632,87 @@ scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
  *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
 static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+    int lun, unsigned char tag)
 {
-  int targ = (scb->target_channel_lun >> 4) & 0x0F;
-  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int slun = scb->hscb->target_channel_lun & 0x07;
+  int match;
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
-         target, channel, targ, chan);
+  printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+         scb->cmd->device->host->host_no, target, channel, targ, chan);
 #endif
-  if (target == ALL_TARGETS)
+  match = ((chan == channel) || (channel == ALL_CHANNELS));
+  if (match != 0)
+    match = ((targ == target) || (target == ALL_TARGETS));
+  if (match != 0)
+    match = ((lun == slun) || (lun == ALL_LUNS));
+  if (match != 0)
+    match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+  return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ *   Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+  /*
+   * Invalidate the tag so that aic7xxx_find_scb doesn't think
+   * it's active
+   */
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+  outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+  outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ *   Removes the current SCB from the disconnected list and adds it
+ *   to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+  unsigned char next;
+  unsigned char prev;
+
+  outb(scbptr, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
+  prev = inb(p->base + SCB_PREV);
+
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
+
+  if (prev != SCB_LIST_NULL)
   {
-    return (chan == channel);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   else
   {
-    return ((chan == channel) && (targ == target));
+    outb(next, p->base + DISCONNECTED_SCBH);
   }
+
+  if (next != SCB_LIST_NULL)
+  {
+    outb(next, p->base + SCBPTR);
+    outb(prev, p->base + SCB_PREV);
+  }
+  return next;
 }
 
 /*+F*************************************************************************
@@ -1427,51 +1720,93 @@ aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
  *   aic7xxx_busy_target
  *
  * Description:
- *   Set the specified target active.
+ *   Set the specified target busy.
  *-F*************************************************************************/
 static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, unsigned char scbid)
+{
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
+
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  outb(scbid, p->base + scb_offset);
+  outb(active_scb, p->base + SCBPTR);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_index_busy_target
+ *
+ * Description:
+ *   Returns the index of the busy target, and optionally sets the
+ *   target inactive.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, int unbusy)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned char busy_scbid;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
 
-  if ((target > 0x07) || (channel == 'B'))
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  busy_scbid = inb(p->base + scb_offset);
+  if (unbusy)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(SCB_LIST_NULL, p->base + scb_offset);
   }
-  active = inb(active_port);
-  active |= (0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(active_scb, p->base + SCBPTR);
+  return (busy_scbid);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_unbusy_target
+ *   aic7xxx_find_scb
  *
  * Description:
- *   Set the specified target inactive.
+ *   Look through the SCB array of the card and attempt to find the
+ *   hardware SCB that corresponds to the passed in SCB.  Return
+ *   SCB_LIST_NULL if unsuccessful.  This routine assumes that the
+ *   card is already paused.
  *-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char saved_scbptr;
+  unsigned char curindex;
 
-  if ((target > 0x07) || (channel == 'B'))
+  saved_scbptr = inb(p->base + SCBPTR);
+  curindex = 0;
+  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(curindex, p->base + SCBPTR);
+    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+    {
+      break;
+    }
   }
-  active = inb(active_port);
-  active &= ~(0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(saved_scbptr, p->base + SCBPTR);
+  if (curindex >= p->scb_data->maxhscbs)
+  {
+    curindex = SCB_LIST_NULL;
+  }
+
+  return (curindex);
 }
 
 /*+F*************************************************************************
@@ -1479,68 +1814,60 @@ aic7xxx_unbusy_target(unsigned char target, char channel, int base)
  *   aic7xxx_allocate_scb
  *
  * Description:
- *   Get a free SCB either from one already assigned to a hardware
- *   slot, or one that will require an SCB to be paged out before
- *   use.  If there are none, attempt to allocate a new one.
+ *   Get an SCB from the free list or by allocating a new one.
  *-F*************************************************************************/
 static struct aic7xxx_scb *
 aic7xxx_allocate_scb(struct aic7xxx_host *p)
 {
-  struct aic7xxx_scb *scbp = NULL;
-  int maxscbs;
+  struct aic7xxx_scb   *scbp = NULL;
+  struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+  long processor_flags;
 
-  scbp = p->scb_link->free_scbs.head;
+  save_flags(processor_flags);
+  cli();
+#endif
+
+  scbp = p->scb_data->free_scbs.head;
   if (scbp != NULL)
   {
-    scbq_remove_head(&p->scb_link->free_scbs);
+    scbq_remove_head(&p->scb_data->free_scbs);
   }
   else
   {
-    /*
-     * This should always be NULL if paging is not enabled.
-     */
-    scbp = p->page_scbs.head;
-    if (scbp != NULL)
-    {
-      scbq_remove_head(&p->page_scbs);
-    }
-    else
+    if (p->scb_data->numscbs < p->scb_data->maxscbs)
     {
-      /*
-       * Set limit the SCB allocation to the maximum number of
-       * hardware SCBs if paging is not enabled; otherwise use
-       * the maximum (255).
-       */
-      if (p->flags & PAGE_ENABLED)
-        maxscbs = p->maxscbs;
-      else
-        maxscbs = p->maxhscbs;
-      if (p->scb_link->numscbs < maxscbs)
-      {
-        int scb_index = p->scb_link->numscbs;
-        int scb_size = sizeof(struct aic7xxx_scb);
+      int scb_index = p->scb_data->numscbs;
+      int scb_size = sizeof(struct aic7xxx_scb) +
+                     sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
 
-        p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
-        scbp = (p->scb_array[scb_index]);
-        if (scbp != NULL)
-        {
-          memset(scbp, 0, sizeof(*scbp));
-          scbp->tag = scb_index;
-          if (scb_index < p->maxhscbs)
-            scbp->position = scb_index;
-          else
-           scbp->position = SCB_LIST_NULL;
-          p->scb_link->numscbs++;
-        }
+      scbp = kmalloc(scb_size, GFP_ATOMIC);
+      if (scbp != NULL)
+      {
+        memset(scbp, 0, sizeof(struct aic7xxx_scb));
+        hscbp = &p->scb_data->hscbs[scb_index];
+        scbp->hscb = hscbp;
+        scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+        memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+        hscbp->tag = scb_index;
+        p->scb_data->numscbs++;
+        /*
+         * Place in the scb array; never is removed
+         */
+        p->scb_data->scb_array[scb_index] = scbp;
       }
     }
   }
+#ifdef AIC7XXX_DEBUG
   if (scbp != NULL)
   {
-#ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs++;
-#endif
+    p->activescbs++;
   }
+#endif
+
+#ifdef AGRESSIVE
+  restore_flags(processor_flags);
+#endif
   return (scbp);
 }
 
@@ -1580,6 +1907,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
+    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
     cmd->scsi_done(cmd);
   }
   p->completeq.tail = NULL;
@@ -1590,53 +1918,29 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
  *   aic7xxx_free_scb
  *
  * Description:
- *   Free the scb and update the page, waiting, free scb lists.
+ *   Free the scb and insert into the free scb list.
  *-F*************************************************************************/
 static void
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_scb *wscb;
+  struct aic7xxx_hwscb *hscb;
+  long flags;
+
+  hscb = scb->hscb;
+  save_flags(flags);
+  cli();
 
-  scb->state = SCB_FREE;
+  scb->flags = SCB_FREE;
   scb->cmd = NULL;
-  scb->control = 0;
-  scb->state = 0;
+  hscb->control = 0;
+  hscb->target_status = 0;
 
-  if (scb->position == SCB_LIST_NULL)
-  {
-    scbq_insert_head(&p->page_scbs, scb);
-  }
-  else
-  {
-    /*
-     * If there are any SCBS on the waiting queue, assign the slot of this
-     * "freed" SCB to the first one.  We'll run the waiting queues after
-     * all command completes for a particular interrupt are completed or
-     * when we start another command.
-     */
-    wscb = p->waiting_scbs.head;
-    if (wscb != NULL)
-    {
-      scbq_remove_head(&p->waiting_scbs);
-      wscb->position = scb->position;
-      scbq_insert_tail(&p->assigned_scbs, wscb);
-      wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
-      /* 
-       * The "freed" SCB will need to be assigned a slot before being
-       * used, so put it in the page_scbs queue.
-       */
-      scb->position = SCB_LIST_NULL;
-      scbq_insert_head(&p->page_scbs, scb);
-    }
-    else
-    {
-      scbq_insert_head(&p->scb_link->free_scbs, scb);
-    }
+  scbq_insert_head(&p->scb_data->free_scbs, scb);
 #ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs--;  /* For debugging purposes. */
+  p->activescbs--;  /* For debugging purposes. */
 #endif
-  }
+
+  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1651,68 +1955,113 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   Scsi_Cmnd *cmd = scb->cmd;
 
+  if (scb->flags & SCB_RECOVERY_SCB)
+  {
+    p->flags &= ~IN_TIMEOUT;
+  }
+  if (cmd->result == DID_OK)
+  {
+    if (scb->flags & SCB_ABORTED)
+    {
+      cmd->result = (DID_RESET << 16);
+    }
+  }
+  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  {
+    unsigned short mask;
+
+    mask = 0x01 << TARGET_INDEX(scb->cmd);
+    if (scb->flags & SCB_MSGOUT_WDTR)
+    {
+      p->wdtr_pending &= ~mask;
+    }
+    if (scb->flags & SCB_MSGOUT_SDTR)
+    {
+      p->sdtr_pending &= ~mask;
+    }
+  }
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
+#ifdef AIC7XXX_PROC_STATS
+  {
+    int actual;
+
+    /*
+     * XXX: we should actually know how much actually transferred
+     * XXX: for each command, but apparently that's too difficult.
+     */
+    actual = aic7xxx_length(cmd, 0);
+    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+        && (aic7xxx_error(cmd) == 0))
+    {
+      struct aic7xxx_xferstats *sp;
+      long *ptr;
+      int x;
+
+      sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+      sp->xfers++;
+
+      if (cmd->request.cmd == WRITE)
+      {
+        sp->w_total++;
+        sp->w_total512 += (actual >> 9);
+        ptr = sp->w_bins;
+      }
+      else
+      {
+        sp->r_total++;
+        sp->r_total512 += (actual >> 9);
+        ptr = sp->r_bins;
+      }
+      for (x = 9; x <= 17; x++)
+      {
+        if (actual < (1 << x))
+        {
+          ptr[x - 9]++;
+          break;
+        }
+      }
+      if (x > 17)
+      {
+        ptr[x - 9]++;
+      }
+    }
+  }
+#endif /* AIC7XXX_PROC_STATS */
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_done_aborted_scbs
+ *   aic7xxx_run_done_queue
  *
  * Description:
- *   Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- *   aborted list, and adds each scb to the free list.
+ *   Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ *   aborted list, and adds each scb to the free list.  If complete
+ *   is TRUE, we also process the commands complete list.
  *-F*************************************************************************/
 static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
-  Scsi_Cmnd *cmd;
   struct aic7xxx_scb *scb;
   int i;
 
-  for (i = 0; i < p->scb_link->numscbs; i++)
+  for (i = 0; i < p->scb_data->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if (scb->state & SCB_QUEUED_FOR_DONE)
+    scb = p->scb_data->scb_array[i];
+    if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
 #ifdef AIC7XXX_DEBUG_ABORT
-      printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
-      scb->position, TCL_OF_SCB(scb));
+      printk("(scsi%d:%d:%d) Aborting scb %d\n",
+             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
 #endif
-      /*
-       * Process the command after marking the scb as free
-       * and adding it to the free list.
-       */
-      cmd = scb->cmd;
-      p->device_status[TARGET_INDEX(cmd)].flags = 0;
-      aic7xxx_free_scb(p, scb);
-      cmd->scsi_done(cmd);  /* call the done function */
+      aic7xxx_done(p, scb);
     }
   }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_add_waiting_scb
- *
- * Description:
- *   Add this SCB to the head of the "waiting for selection" list.
- *-F*************************************************************************/
-static void
-aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
-{
-  unsigned char next;
-  unsigned char curscb;
-
-  curscb = inb(SCBPTR + base);
-  next = inb(WAITING_SCBH + base);
-
-  outb(scb->position, SCBPTR + base);
-  outb(next, SCB_NEXT + base);
-  outb(scb->position, WAITING_SCBH + base);
-
-  outb(curscb, SCBPTR + base);
+  if (complete)
+  {
+    aic7xxx_done_cmds_complete(p);
+  }
 }
 
 /*+F*************************************************************************
@@ -1725,26 +2074,23 @@ aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
  *-F*************************************************************************/
 static unsigned char
 aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    unsigned char prev)
+    unsigned char scbpos, unsigned char prev)
 {
   unsigned char curscb, next;
-  int target = (scb->target_channel_lun >> 4) & 0x0F;
-  char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
-  int base = p->base;
 
   /*
    * Select the SCB we want to abort and pull the next pointer out of it.
    */
-  curscb = inb(SCBPTR + base);
-  outb(scb->position, SCBPTR + base);
-  next = inb(SCB_NEXT + base);
+  curscb = inb(p->base + SCBPTR);
+  outb(scbpos, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
 
   /*
    * Clear the necessary fields
    */
-  outb(0, SCB_CONTROL + base);
-  outb(SCB_LIST_NULL, SCB_NEXT + base);
-  aic7xxx_unbusy_target(target, channel, base);
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
 
   /*
    * Update the waiting list
@@ -1754,27 +2100,97 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
     /*
      * First in the list
      */
-    outb(next, WAITING_SCBH + base);
+    outb(next, p->base + WAITING_SCBH);
   }
   else
   {
     /*
      * Select the scb that pointed to us and update its next pointer.
      */
-    outb(prev, SCBPTR + base);
-    outb(next, SCB_NEXT + base);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   /*
    * Point us back at the original scb position and inform the SCSI
    * system that the command has been aborted.
    */
-  outb(curscb, SCBPTR + base);
-  scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  outb(curscb, p->base + SCBPTR);
+  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  scb->flags &= ~SCB_ACTIVE;
   scb->cmd->result = (DID_RESET << 16);
 
   return (next);
 }
 
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_search_qinfifo
+ *
+ * Description:
+ *   Search the queue-in FIFO for matching SCBs and conditionally
+ *   requeue.  Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+    int lun, unsigned char tag, int flags, int requeue)
+{
+  unsigned char saved_queue[AIC7XXX_MAXSCB];
+  int      queued = inb(p->base + QINCNT) & p->qcntmask;
+  int      i;
+  int      found;
+  struct aic7xxx_scb *scbp;
+  scb_queue_type removed_scbs;
+
+  found = 0;
+  scbq_init (&removed_scbs);
+  for (i = 0; i < (queued - found); i++)
+  {
+    saved_queue[i] = inb(p->base + QINFIFO);
+    scbp = p->scb_data->scb_array[saved_queue[i]];
+    if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+       /*
+        * We found an scb that needs to be removed.
+        */
+       if (requeue)
+       {
+         scbq_insert_head(&removed_scbs, scbp);
+       }
+       else
+       {
+         scbp->flags = flags;
+         scbp->flags &= ~SCB_ACTIVE;
+         /*
+          * XXX - Don't know what error to use here.
+          */
+         aic7xxx_error(scbp->cmd) = DID_RESET;
+       }
+       i--;
+       found++;
+    }
+  }
+  /* Now put the saved scbs back. */
+  for (queued = 0; queued < i; queued++)
+    outb(saved_queue[queued], p->base + QINFIFO);
+
+  if (requeue)
+  {
+    scbp = removed_scbs.head;
+    while (scbp != NULL)
+    {
+      scbq_remove_head(&removed_scbs);
+      /*
+       * XXX - Shouldn't we be adding this to the free list?
+       */
+      scbq_insert_head(&p->waiting_scbs, scbp);
+      scbp->flags |= SCB_WAITINGQ;
+      scbp = removed_scbs.head;
+    }
+  }
+
+  return (found);
+}
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_reset_device
@@ -1784,118 +2200,250 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
  *   all active and queued scbs for that target/channel.
  *-F*************************************************************************/
 static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     int lun, unsigned char tag)
 {
-  int base = p->base;
-  struct aic7xxx_scb *scb;
+  struct aic7xxx_scb *scbp;
   unsigned char active_scb;
   int i = 0;
-  int found = 0;
+  int found;
 
   /*
    * Restore this when we're done
    */
-  active_scb = inb(SCBPTR + base);
+  active_scb = inb(p->base + SCBPTR);
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
-         target, channel, active_scb);
+  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+         p->host_no, target, CHAN_TO_INT(channel), active_scb);
 #endif
+
   /*
-   * Search the QINFIFO.
+   * Deal with the busy target and linked next issues.
    */
   {
-    int saved_queue[AIC7XXX_MAXSCB];
-    int queued = inb(QINCNT + base) & p->qcntmask;
+    int min_target, max_target;
+    unsigned char busy_scbid;
 
-    for (i = 0; i < (queued - found); i++)
+    /* Make all targets 'relative' to bus A. */
+    if (target == ALL_TARGETS)
     {
-      saved_queue[i] = inb(QINFIFO + base);
-      outb(saved_queue[i], SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      if (aic7xxx_match_scb(scb, target, channel))
+      switch (channel)
       {
-        /*
-         * We found an scb that needs to be aborted.
-         */
-#ifdef AIC7XXX_DEBUG_ABORT
-        printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
-               saved_queue[i], TCL_OF_SCB(scb));
-#endif
-        scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-        scb->cmd->result = (DID_RESET << 16);
-        outb(0, SCB_CONTROL + base);
-        i--;
-        found++;
+        case 'A':
+         min_target = 0;
+         max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+         break;
+        case 'B':
+         min_target = 8;
+         max_target = 15;
+         break;
+        case ALL_CHANNELS:
+        default:
+         min_target = 0;
+         max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+         break;
       }
     }
-    /*
-     * Now put the saved scbs back.
-     */
-    for (queued = 0; queued < i; queued++)
+    else
+    { 
+      min_target = target + channel == 'B' ? 8 : 0;
+      max_target = min_target;
+    }
+
+    for (i = min_target; i <= max_target; i++)
     {
-      outb(saved_queue[queued], QINFIFO + base);
+      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+      if (busy_scbid < p->scb_data->numscbs)
+      {
+       struct aic7xxx_scb *busy_scb;
+       struct aic7xxx_scb *next_scb;
+       unsigned char next_scbid;
+
+       busy_scb = p->scb_data->scb_array[busy_scbid];
+  
+       next_scbid = busy_scb->hscb->data_count >> 24;
+
+       if (next_scbid == SCB_LIST_NULL)
+        {
+         busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+         if (busy_scbid != SCB_LIST_NULL)
+          {
+           outb(busy_scbid, p->base + SCBPTR);
+           next_scbid = inb(p->base + SCB_LINKED_NEXT);
+         }
+       }
+
+       if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+        {
+         aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+       }
+
+       if (next_scbid != SCB_LIST_NULL)
+        {
+         next_scb = p->scb_data->scb_array[next_scbid];
+         if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+           continue;
+          }
+         /* Requeue for later processing */
+         scbq_insert_head(&p->waiting_scbs, next_scb);
+         next_scb->flags |= SCB_WAITINGQ;
+       }
+      }
     }
   }
 
+  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
   /*
    * Search waiting for selection list.
    */
   {
-    unsigned char next, prev;
+    unsigned char next, prev, scb_index;
 
-    next = inb(WAITING_SCBH + base);  /* Start at head of list. */
+    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
 
     while (next != SCB_LIST_NULL)
     {
-      outb(next, SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      /*
-       * Select the SCB.
-       */
-      if (aic7xxx_match_scb(scb, target, channel))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index >= p->scb_data->numscbs)
+      {
+        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+              scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
       {
-        next = aic7xxx_abort_waiting_scb(p, scb, prev);
+        unsigned char linked_next;
+
+        next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+        if (linked_next != SCB_LIST_NULL)
+        {
+          struct aic7xxx_scb *next_scb;
+          /*
+           * Requeue the waiting SCB via the waiting list.
+           */
+          next_scb = p->scb_data->scb_array[linked_next];
+          if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+            scbq_insert_head(&p->waiting_scbs, next_scb);
+            next_scb->flags |= SCB_WAITINGQ;
+          }
+        }
         found++;
       }
       else
       {
         prev = next;
-        next = inb(SCB_NEXT + base);
+        next = inb(p->base + SCB_NEXT);
+      }
+    }
+  }
+
+  /*
+   * Go through disconnected list and remove any entries we have queued
+   * for completion, zeroing their control byte too.
+   */
+  {
+    unsigned char next, prev, scb_index;
+
+    next = inb(p->base + DISCONNECTED_SCBH);
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
+    {
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index > p->scb_data->numscbs)
+      {
+        panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+              "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        next = aic7xxx_rem_scb_from_disc_list(p, next);
+      }
+      else
+      {
+        prev = next;
+        next = inb(p->base + SCB_NEXT);
+      }
+    }
+  }
+
+  /*
+   * Go through the hardware SCB array looking for commands that
+   * were active but not on any list.
+   */
+  for (i = 0; i < p->scb_data->maxhscbs; i++)
+  {
+    unsigned char scbid;
+
+    outb(i, p->base + SCBPTR);
+    scbid = inb(p->base + SCB_TAG);
+    if (scbid < p->scb_data->numscbs)
+    {
+      scbp = p->scb_data->scb_array[scbid];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        aic7xxx_add_curscb_to_free_list(p);
       }
     }
   }
 
   /*
    * Go through the entire SCB array now and look for commands for
-   * for this target that are active.  These are other (most likely
+   * for this target that are stillactive.  These are other (most likely
    * tagged) commands that were disconnected when the reset occurred.
    */
-  for (i = 0; i < p->scb_link->numscbs; i++)
+  for (i = 0; i < p->scb_data->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    scbp = p->scb_data->scb_array[i];
+    if (((scbp->flags & SCB_ACTIVE) != 0) &&
+        aic7xxx_match_scb(scbp, target, channel, lun, tag))
     {
-      /*
-       * Ensure the target is "free"
-       */
-      aic7xxx_unbusy_target(target, channel, base);
-      if (! (scb->state & SCB_PAGED_OUT))
+      scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~SCB_ACTIVE;
+      aic7xxx_error(scbp->cmd) = DID_RESET;
+
+      found++;
+
+      if ((scbp->flags & SCB_WAITINGQ) != 0)
       {
-        outb(scb->position, SCBPTR + base);
-        outb(0, SCB_CONTROL + base);
+        scbq_remove(&p->waiting_scbs, scbp);
+        scbp->flags &= ~SCB_WAITINGQ;
       }
-      scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-      scb->cmd->result = (DID_RESET << 16);
-      found++;
     }
   }
 
-  outb(active_scb, SCBPTR + base);
+  outb(active_scb, p->base + SCBPTR);
   return (found);
 }
 
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_clear_intstat
+ *
+ * Description:
+ *   Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+  /* Clear any interrupt conditions this may have caused. */
+  outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+  outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+  outb(CLRSCSIINT, p->base + CLRINT);
+}
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_reset_current_bus
@@ -1904,11 +2452,28 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
  *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  outb(SCSIRSTO, SCSISEQ + base);
+  unsigned char scsiseq;
+
+  /* Disable reset interrupts. */
+  outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+  /* Turn on the bus reset. */
+  scsiseq = inb(p->base + SCSISEQ);
+  outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+  udelay(1000);
+
+  /* Turn off the bus reset. */
+  outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+  aic7xxx_clear_intstat(p);
+
+  /* Re-enable reset interrupts. */
+  outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
   udelay(1000);
-  outb(0, SCSISEQ + base);
 }
 
 /*+F*************************************************************************
@@ -1921,25 +2486,24 @@ aic7xxx_reset_current_bus(int base)
 static int
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
 {
-  int base = p->base;
-  unsigned char sblkctl;
-  char cur_channel;
   unsigned long offset, offset_max;
   int found;
+  unsigned char sblkctl;
+  char cur_channel;
 
+  pause_sequencer(p);
   /*
-   * Clean up all the state information for the
-   * pending transactions on this bus.
+   * Clean up all the state information for the pending transactions
+   * on this bus.
    */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
   if (channel == 'B')
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    outb(0, ACTIVE_B + base);
-    offset = TARG_SCRATCH + base + 8;
-    offset_max = TARG_SCRATCH + base + 16;
+    offset = TARG_SCRATCH + 8;
+    offset_max = TARG_SCRATCH + 16;
   }
   else
   {
@@ -1949,1292 +2513,1427 @@ aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      outb(0, ACTIVE_A + base);
-      outb(0, ACTIVE_B + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 16;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 16;
     }
     else
     {
+      /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      outb(0, ACTIVE_A + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 8;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 8;
     }
   }
+
   while (offset < offset_max)
   {
     /*
-     * Revert to async/narrow transfers
-     * until we renegotiate.
+     * Revert to async/narrow transfers until we renegotiate.
      */
     u_char targ_scratch;
-    targ_scratch = inb(offset);
+
+    targ_scratch = inb(p->base + offset);
     targ_scratch &= SXFR;
-    outb(targ_scratch, offset);
+    outb(targ_scratch, p->base + offset);
     offset++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
-
-  /*
-   * Case 1: Command for another bus is active
-   */
-  sblkctl = inb(SBLKCTL + base);
+  sblkctl = inb(p->base + SBLKCTL);
   cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
   if (cur_channel != channel)
   {
+    /*
+     * Case 1: Command for another bus is active
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
-           channel);
+    printk("scsi%d: Stealthily resetting channel %c\n",
+           p->host_no, channel);
 #endif
     /*
-     * Stealthily reset the other bus without upsetting the current bus
+     * Stealthily reset the other bus without upsetting the current bus.
      */
-    outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    outb(sblkctl, SBLKCTL + base);
-
-    UNPAUSE_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    outb(sblkctl, p->base + SBLKCTL);
+    unpause_sequencer(p, /* unpause_always */ FALSE);
   }
-  /*
-   * Case 2: A command from this bus is active or we're idle
-   */
   else
   {
+    /*
+     * Case 2: A command from this bus is active or we're idle.
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
-           channel);
+    printk("scsi%d: Resetting current channel %c\n",
+           p->host_no, channel);
 #endif
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+#if 0
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    RESTART_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    restart_sequencer(p);
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
 #endif
   }
 
-  /*
-   * Cause the mid-level SCSI code to delay any further 
-   * queueing by the bus settle time for us.
-   */
-  p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
   /*
    * Now loop through all the SCBs that have been marked for abortion,
    * and call the scsi_done routines.
    */
-  aic7xxx_done_aborted_scbs(p);
-  return found;
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  return (found);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_page_scb
+ *   aic7xxx_run_waiting_queues
  *
  * Description:
- *   Swap in_scbp for out_scbp down in the cards SCB array.
- *   We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
+ *   Scan the awaiting_scbs queue downloading and starting as many
+ *   scbs as we can.
  *-F*************************************************************************/
 static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
-    struct aic7xxx_scb *in_scbp)
+aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
-  int index;
+  struct aic7xxx_scb *scb;
 
-  /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
-       out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
-  aic7xxx_getscb(p, out_scbp);
-  out_scbp->state |= SCB_PAGED_OUT;
-  if (!(out_scbp->control & TAG_ENB))
-  {
-    /* Stick in non-tagged array */
-    index = (out_scbp->target_channel_lun >> 4) | 
-            (out_scbp->target_channel_lun & SELBUSB);
-    p->pagedout_ntscbs[index] = out_scbp;
-  }
-
-  /* Page-in */
-  in_scbp->position = out_scbp->position;
-  out_scbp->position = SCB_LIST_NULL;
-  aic7xxx_putscb(p, in_scbp);
-  in_scbp->state &= ~SCB_PAGED_OUT;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_run_waiting_queues
- *
- * Description:
- *   Scan the assigned_scbs and waiting_scbs queues.  For scbs in the
- *   assigned_scbs queue, we download and start them.  For scbs in the
- *   waiting_scbs queue, we page in as many as we can being careful
- *   not to cause a deadlock for a reconnecting target.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
-{
-  struct aic7xxx_scb *scb;
-  u_char cur_scb, intstat;
-  u_long base = p->base;
-  long flags;
-
-  if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
-    return;
-
-  save_flags(flags);
-  cli();
-
-  PAUSE_SEQUENCER(p);
-  cur_scb = inb(SCBPTR + base);
-  intstat = inb(INTSTAT + base);
+  if (p->waiting_scbs.head == NULL)
+    return;
 
+  pause_sequencer(p);
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
-  scb = p->assigned_scbs.head;
-  while (scb != NULL)
-  {
-    scbq_remove_head(&(p->assigned_scbs));
-    outb(scb->position, SCBPTR + base);
-    aic7xxx_putscb(p, scb);
-    /* Mark this as an active command. */
-    scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
-    outb(scb->position, QINFIFO + base);
-    scb = p->assigned_scbs.head;
-  }
-
-  /* Now deal with SCBs that require paging. */
   scb = p->waiting_scbs.head;
-  if (scb != NULL)
+  while (scb != NULL)
   {
-    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
-    int count = 0;
-    u_char next_scb;
-
-    while (scb != NULL)
+    if (p->curqincnt >= p->qfullcount)
     {
-      /* Attempt to page this SCB in */
-      if (disc_scb == SCB_LIST_NULL)
-        break;
-
-      /*
-       * Advance disc_scb to the next one in the list.
-       */
-      outb(disc_scb, SCBPTR + base);
-      next_scb = inb(SCB_NEXT + base); 
-
-      /*
-       * We have to be careful about when we allow an SCB to be paged out. 
-       * There must always be at least one slot availible for a reconnecting
-       * target in case it references an SCB that has been paged out.  Our
-       * heuristic is that either the disconnected list has at least two
-       * entries in it or there is one entry and the sequencer is activily
-       * working on an SCB which implies that it will either complete or
-       * disconnect before another reconnection can occur.
-       */
-      if ((next_scb != SCB_LIST_NULL) || active)
+      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+      if (p->curqincnt >= p->qfullcount)
       {
-        u_char out_scbi;
-        struct aic7xxx_scb *out_scbp;
-
-        scbq_remove_head(&(p->waiting_scbs));
-
-        /*
-         * Find the in-core SCB for the one we're paging out.
-         */
-        out_scbi = inb(SCB_TAG + base); 
-        out_scbp = (p->scb_array[out_scbi]);
-
-        /* Do the page out and mark the paged in SCB as active. */
-        aic7xxx_page_scb(p, out_scbp, scb);
-
-        /* Mark this as an active command. */
-        scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
-        /* Queue the command */
-        outb(scb->position, QINFIFO + base);
-        count++;
-
-        /* Advance to the next disconnected SCB */
-        disc_scb = next_scb;
-        scb = p->waiting_scbs.head;
+        break;
       }
-      else
-        scb = NULL;
     }
 
-    if (count)
+    /*
+     * We have some space.
+     */
+    scbq_remove_head(&(p->waiting_scbs));
+    scb->flags &= ~SCB_WAITINGQ;
+
+    outb(scb->hscb->tag, p->base + QINFIFO);
+
+    if ((p->flags & PAGE_ENABLED) != 0)
     {
-      /* 
-       * Update the head of the disconnected list.
+      /*
+       * We only care about this statistic when paging
+       * since it's impossible to overflow the qinfifo
+       * in the non-paging case.
        */
-      outb(disc_scb, DISCONNECTED_SCBH + base);
-      if (disc_scb != SCB_LIST_NULL)
-      {
-        outb(disc_scb, SCBPTR + base);
-        outb(SCB_LIST_NULL, SCB_PREV + base);
-      }
+      p->curqincnt++;
     }
+    scb = p->waiting_scbs.head;
   }
-  /* Restore old position */
-  outb(cur_scb, SCBPTR + base);
 
-  /*
-   * Guard against unpausing the sequencer if there is an interrupt
-   * waiting to happen.
-   */
-  if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
+  unpause_sequencer(p, FALSE);
+}
 
-  restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_sdtr
+ *
+ * Description:
+ *   Constucts a synchronous data transfer message in the message
+ *   buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char period, unsigned char offset)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_SDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(period,           p->base + MSG_OUT + 3 + start_byte);
+  outb(offset,           p->base + MSG_OUT + 4 + start_byte);
+  outb(start_byte + 5,   p->base + MSG_LEN);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_isr
+ *   aic7xxx_construct_wdtr
  *
  * Description:
- *   SCSI controller interrupt handler.
+ *   Constucts a wide data transfer message in the message buffer
+ *   on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char bus_width)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_WDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(bus_width,        p->base + MSG_OUT + 3 + start_byte);
+  outb(start_byte + 4,   p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_calc_residual
  *
- *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- *         be disabled all through this function unless we say otherwise.
+ * Description:
+ *   Calculate the residual data not yet transferred.
  *-F*************************************************************************/
 static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb = NULL;
-  short         transfer;
-  unsigned char ha_flags, scsi_id, bus_width;
-  unsigned char offset, rate, scratch, scratch_offset;
-  unsigned char max_offset, rej_byte;
-  unsigned short target_mask;
-  char channel;
-  unsigned int addr; /* must be 32 bits */
+  struct aic7xxx_hwscb *hscb;
   Scsi_Cmnd *cmd;
+  int actual;
 
-  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+  cmd = scb->cmd;
+  hscb = scb->hscb;
 
   /*
-   * Search for the host with a pending interrupt.  If we can't find
-   * one, then we've encountered a spurious interrupt.
+   *  Don't destroy valid residual information with
+   *  residual coming from a check sense operation.
    */
-  while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+  if (((scb->hscb->control & DISCONNECTED) == 0) &&
+      (scb->flags & SCB_SENSE) == 0)
   {
-    if (p->next == NULL)
-    {
-      p = NULL;
-    }
-    else
+    /*
+     *  We had an 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 = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+    actual -= (hscb->residual_data_count[2] << 16) |
+              (hscb->residual_data_count[1] <<  8) |
+              hscb->residual_data_count[0];
+
+    if (actual < cmd->underflow)
     {
-      p = (struct aic7xxx_host *) p->next->hostdata;
+      printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+             "Wanted at least %u, got %u, residual SG count %d.\n",
+             p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+             hscb->residual_SG_segment_count);
+      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+      aic7xxx_status(cmd) = hscb->target_status;
     }
   }
 
-  if (p == NULL)
-    return;
-
   /*
-   * Keep track of interrupts for /proc/scsi
+   * Clean out the residual information in the SCB for the
+   * next consumer.
    */
-  p->isr_count++;
+  hscb->residual_data_count[2] = 0;
+  hscb->residual_data_count[1] = 0;
+  hscb->residual_data_count[0] = 0;
+  hscb->residual_SG_segment_count = 0;
+}
 
-  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_device_reset
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+  unsigned short targ_mask;
+  unsigned char  targ_scratch;
+  int scratch_offset = target;
+  int found;
+
+  if (channel == 'B')
   {
-    /*
-     * We must only have one card at this IRQ and it must have been
-     * added to the board data before the spurious interrupt occurred.
-     * It is sufficient that we check isr_count and not the spurious
-     * interrupt count.
-     */
-    printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
-    return;
+    scratch_offset += 8;
   }
-
-  base = p->base;
+  targ_mask = (0x01 << scratch_offset);
   /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
+   * Go back to async/narrow transfers and renegotiate.
    */
-  intstat = inb(INTSTAT + base);
+  p->needsdtr |= p->needsdtr_copy & targ_mask;
+  p->needwdtr |= p->needwdtr_copy & targ_mask;
+  p->sdtr_pending &= ~targ_mask;
+  p->wdtr_pending &= ~targ_mask;
+  targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+  targ_scratch &= SXFR;
+  outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+  found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+         "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
 
-  /*
-   * Indicate that we're in the interrupt handler.
-   */
-  p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_seqint
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  struct aic7xxx_scb *scb;
+  unsigned short target_mask;
+  unsigned char target, scratch_offset;
+  char channel;
 
-  if (intstat & BRKADRINT)
+  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
   {
-    int i;
-    unsigned char errno = inb(ERROR + base);
-
-    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
-    for (i = 0; i < NUMBER(hard_error); i++)
-    {
-      if (errno & hard_error[i].errno)
-      {
-        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
-      }
-    }
-    panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
-          inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+    target = (inb(p->base + SELID) >> 4) & 0x0F;
+  }
+  else
+  {
+    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
   }
+  scratch_offset = target;
+  channel = 'A';
+  if (inb(p->base + SBLKCTL) & SELBUSB)
+  {
+    channel = 'B';
+    scratch_offset += 8;
+  }
+  target_mask = (0x01 << scratch_offset);
 
-  if (intstat & SEQINT)
+  switch (intstat & SEQINT_MASK)
   {
-    /*
-     * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT condition will
-     * unpaused the sequencer before this point.
-     */
-    PAUSE_SEQUENCER(p);
+    case NO_MATCH:
+      {
+        /*
+         * This could be for a normal abort request.  Figure out
+         * which SCB we were trying to find and only give an error
+         * if we didn't ask for this to happen.
+         */
+        unsigned char scb_index;
+        unsigned char busy_scbid;
+        unsigned char arg1;
 
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    scratch_offset = scsi_id;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
-      scratch_offset += 8;
-    }
-    target_mask = (0x01 << scratch_offset);
+        busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+            /*unbusy*/ FALSE);
+        arg1 = inb(p->base + ARG_1);
 
-    switch (intstat & SEQINT_MASK)
-    {
-      case NO_MATCH:
-       if (p->flags & PAGE_ENABLED)
+        if (arg1 == SCB_LIST_NULL)
         {
-         /* SCB Page-in request */
-         struct aic7xxx_scb *outscb;
-         u_char arg_1 = inb(ARG_1 + base);
-          int use_disconnected = FALSE;
-
-          /*
-           * The sequencer expects this value upon return.  Assume
-           * we will find the paged out SCB and set the value now.
-           * If we don't, and one of the methods used to acquire an
-           * SCB calls aic7xxx_done(), we will end up in our queue
-           * routine and unpause the sequencer without giving it the
-           * correct return value, which causes a hang.
-           */
-         outb(SCB_PAGEDIN, RETURN_1 + base);
-         if (arg_1 == SCB_LIST_NULL)
-          {
-           /* Non-tagged command */
-           int index = scsi_id;
-            if (channel == 'B')
-            {
-              index |= SELBUSB;
-            }
-           scb = p->pagedout_ntscbs[index];
-         }
-         else
-           scb = (p->scb_array[arg_1]);
+          /* untagged request */
+          scb_index = busy_scbid;
+        }
+        else
+        {
+          scb_index = arg1;
+        }
 
-          if (!(scb->state & SCB_PAGED_OUT))
+        if (scb_index < p->scb_data->numscbs)
+        {
+          scb = p->scb_data->scb_array[scb_index];
+          if (scb->hscb->control & ABORT_SCB)
           {
-           printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
-                 "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-                 p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-           aic7xxx_unbusy_target(scsi_id, channel, base);
-           outb(CLRSELTIMEO, CLRSINT1 + base);
-            outb(0, RETURN_1 + base);
+            /*
+             * We expected this.  Let the busfree handler take care
+             * of this when we the abort is finially sent.  Set
+             * IDENTIFY_SEEN so that the busfree handler knows that
+             * there is an SCB to cleanup.
+             */
+            outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+            printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+                   p->host_no, TC_OF_SCB(scb));
             break;
           }
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+               "target - Issuing BUS DEVICE RESET.\n",
+               p->host_no, target, CHAN_TO_INT(channel));
+
+        printk(KERN_WARNING "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               inb(p->base + SAVED_TCL), arg1,
+               (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+        aic7xxx_handle_device_reset(p, target, channel);
+      }
+      break;
 
-         /*
-          * Now to pick the SCB to page out.  Either take a free SCB, an
-           * assigned SCB, an SCB that just completed, or the first one
-           * on the disconnected SCB list.
-          */
-         if (p->scb_link->free_scbs.head != NULL)
-          {
-           outscb = p->scb_link->free_scbs.head;
-           scbq_remove_head(&p->scb_link->free_scbs);
-           scb->position = outscb->position;
-           outscb->position = SCB_LIST_NULL;
-           scbq_insert_head(&p->page_scbs, outscb);
-           outb(scb->position, SCBPTR + base);
-           aic7xxx_putscb(p, scb);
-           scb->state &= ~SCB_PAGED_OUT;
-         }
-         else if (p->assigned_scbs.head != NULL)
-          {
-            outscb = p->assigned_scbs.head;
-            scbq_remove_head(&p->assigned_scbs);
-            scb->position = outscb->position;
-            outscb->position = SCB_LIST_NULL;
-            scbq_insert_head(&p->waiting_scbs, outscb);
-            outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
-            outb(scb->position, SCBPTR + base);
-           aic7xxx_putscb(p, scb);
-            scb->state &= ~SCB_PAGED_OUT;
-          }
-          else if (intstat & CMDCMPLT)
-          {
-            int scb_index;
+    case NO_MATCH_BUSY:
+      {
+        /*
+         * XXX - Leave this as a panic for the time being since it
+         * indicates a bug in the timeout code for this to happen.
+         */
+        unsigned char scb_index;
 
-            outb(CLRCMDINT, CLRINT + base);
-            scb_index = inb(QOUTFIFO + base);
-            if (!(inb(QOUTCNT + base) & p->qcntmask))
-            {
-              intstat &= ~CMDCMPLT;
-            }
-            outscb = (p->scb_array[scb_index]);
-            if (!(outscb->state & SCB_ACTIVE))
+        scb_index = inb(p->base + CUR_SCBID);
+        scb = p->scb_data->scb_array[scb_index];
+
+        panic("scsi%d:  Target %d, channel %c, Target busy link failure, "
+              "but busy SCB exists!\n",
+              p->host_no, target, channel);
+      }
+      break;
+
+    case SEND_REJECT:
+      {
+        unsigned char rej_byte;
+
+        rej_byte = inb(p->base + REJBYTE);
+        printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+               "received from target, SEQ_FLAGS=0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+               inb(p->base + SEQ_FLAGS));
+      }
+      break;
+
+    case NO_IDENT:
+      {
+        /*
+         * The reconnecting target either did not send an identify
+         * message, or did, but we didn't find and SCB to match and
+         * before it could respond to our ATN/abort, it hit a dataphase.
+         * The only safe thing to do is to blow it away with a bus
+         * reset.
+         */
+        int found;
+
+        printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+               "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel),
+               inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+        printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+              "%d SCBs aborted\n", p->host_no, channel, found);
+      }
+      break;
+
+    case BAD_PHASE:
+      if (inb(p->base + LASTPHASE) == P_BUSFREE)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+               p->host_no, CHAN_TO_INT(channel), target);
+        restart_sequencer(p);
+      }
+      else
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+               "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+      }
+      break;
+
+    case EXTENDED_MSG:
+      {
+       unsigned char message_length;
+       unsigned char message_code;
+        unsigned char scb_index;
+
+       message_length = inb(p->base + MSGIN_EXT_LEN);
+       message_code = inb(p->base + MSGIN_EXT_OPCODE);
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+
+       switch (message_code)
+       {
+          case MSG_EXT_SDTR:
+          {
+            unsigned char period;
+            unsigned char offset;
+            unsigned char saved_offset;
+            unsigned char targ_scratch;
+            unsigned char max_offset;
+            unsigned char rate;
+
+            if (message_length != MSG_EXT_SDTR_LEN)
             {
-             printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
-                    "during NO_MATCH interrupt\n", scb_index, p->host_no);
-              use_disconnected = TRUE;
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
             }
+
+            period = inb(p->base + MSGIN_EXT_BYTES);
+            saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+            targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if (targ_scratch & WIDEXFER)
+              max_offset = MAX_OFFSET_16BIT;
             else
+              max_offset = MAX_OFFSET_8BIT;
+            offset = MIN(saved_offset, max_offset);
+
+            aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+            /*
+             * Preserve the WideXfer flag.
+             */
+            targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+            /*
+             * Update both the target scratch area and current SCSIRATE.
+             */
+            outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(targ_scratch, p->base + SCSIRATE);
+
+            /*
+             * See if we initiated Sync Negotiation and didn't have
+             * have to fall down to async transfers.
+             */
+            if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
             {
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              outb(scb->position, SCBPTR + base);
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
-              outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
-              if ((outscb->cmd->flags & WAS_SENSE) && 
-                 !(outscb->cmd->flags & ASKED_FOR_SENSE))
+              /* We started it. */
+              if (saved_offset == offset)
               {
-                /*
-                 * Got sense information.
-                 */
-               outscb->cmd->flags &= ASKED_FOR_SENSE;
+               /*
+                * Don't send an SDTR back to the target.
+                */
+               outb(0, p->base + RETURN_1);
               }
-              p->device_status[TARGET_INDEX(outscb->cmd)].flags
-                |= DEVICE_SUCCESS;
-              aic7xxx_done(p, outscb);
-            }
-          }
-          else
-          {
-            use_disconnected = TRUE;
-          }
-          if (use_disconnected)
-          {
-           u_char tag;
-           u_char next;
-           u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-           if (disc_scb != SCB_LIST_NULL)
-            {
-             outb(disc_scb, SCBPTR + base);
-             tag = inb(SCB_TAG + base);
-             outscb = (p->scb_array[tag]);
-             next = inb(SCB_NEXT + base);
-             if (next != SCB_LIST_NULL)
-              {
-               outb(next, SCBPTR + base);
-               outb(SCB_LIST_NULL, SCB_PREV + base);
-               outb(disc_scb, SCBPTR + base);
-             }
-             outb(next, DISCONNECTED_SCBH + base);
-             aic7xxx_page_scb(p, outscb, scb);
-           }
-            else if (inb(QINCNT + base) & p->qcntmask)
-            {
-              /* Pull one of our queued commands as a last resort. */
-              disc_scb = inb(QINFIFO + base);
-              outb(disc_scb, SCBPTR + base);
-              tag = inb(SCB_TAG + base);
-              outscb = (p->scb_array[tag]);
-              if ((outscb->control & 0x23) != TAG_ENB)
+              else
               {
-                /*
-                 * This is not a simple tagged command so its position
-                 * in the queue matters.  Take the command at the end of
-                 * the queue instead.
-                 */
-                int i;
-                int saved_queue[AIC7XXX_MAXSCB];
-                int queued = inb(QINCNT + base) & p->qcntmask;
-
-                /* Count the command we removed already */
-                saved_queue[0] = disc_scb;
-                queued++;
-
-                /* Empty the input queue. */
-                for (i = 1; i < queued; i++)
-                {
-                  saved_queue[i] = inb(QINFIFO + base);
-                }
-
-                /* Put everyone back but the last entry. */
-                queued--;
-                for (i = 0; i < queued; i++)
-                {
-                  outb(saved_queue[i], QINFIFO + base);
-                }
-
-                outb(saved_queue[queued], SCBPTR + base);
-                tag = inb(SCB_TAG + base);
-                outscb = (p->scb_array[tag]);
+               /* We went too low - force async. */
+               outb(SEND_REJ, p->base + RETURN_1);
               }
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              scbq_insert_head(&p->waiting_scbs, outscb);
-              outscb->state |= SCB_WAITINGQ;
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
             }
             else
             {
-             printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
-                   "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-                   p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-              aic7xxx_unbusy_target(scsi_id, channel, base);
-              outb(CLRSELTIMEO, CLRSINT1 + base);
-              outb(0, RETURN_1 + base);
+              /*
+               * Send our own SDTR in reply.
+               *
+               * We want to see this message as we don't expect a target
+               * to send us a SDTR request first.
+               */
+              printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+              aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+              outb(SEND_MSG, p->base + RETURN_1);
             }
+            /*
+             * Clear the flags.
+             */
+            p->needsdtr &= ~target_mask;
+            break;
           }
-       }
-       else
-        {
-         printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
-               "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-               p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-         aic7xxx_unbusy_target(scsi_id, channel, base);
-         outb(0, SCB_CONTROL + base);
-         outb(CLRSELTIMEO, CLRSINT1 + base);
-          outb(0, RETURN_1 + base);
-        }
-       break;
 
-      case BAD_PHASE:
-       panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
-       break;
+          case MSG_EXT_WDTR:
+          {
+            unsigned char scratch, bus_width;
 
-      case SEND_REJECT:
-        rej_byte = inb(REJBYTE + base);
-        if ((rej_byte & 0xF0) == 0x20)
-        {
-          scb_index = inb(SCB_TAG + base);
-          scb = (p->scb_array[scb_index]);
-          printk(KERN_WARNING "scsi%d: Tagged message received without identify."
-                 "Disabling tagged commands for target %d channel %c.\n",
-                  p->host_no, scsi_id, channel);
-          scb->cmd->device->tagged_supported = 0;
-          scb->cmd->device->tagged_queue = 0;
-        }
-        else
-        {
-          printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
-                 "from target %d channel %c.\n",
-                 p->host_no, rej_byte, scsi_id, channel);
-        }
-       break;
+            if (message_length != MSG_EXT_WDTR_LEN)
+            {
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
+            }
 
-      case NO_IDENT:
-       panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
-             "message. SAVED_TCL 0x%x.\n",
-              p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-       break;
+            bus_width = inb(p->base + MSGIN_EXT_BYTES);
+            scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
 
-      case SDTR_MSG:
-       /*
-        * Help the sequencer to translate the negotiated
-        * transfer rate. Transfer is 1/4 the period
-        * in ns as is returned by the sync negotiation
-        * message. So, we must multiply by four.
-        */
-       transfer = (inb(ARG_1 + base) << 2);
-       offset = inb(ACCUM + base);
-       scratch = inb(TARG_SCRATCH + base + scratch_offset);
+            if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
+            {
+              /*
+               * Don't send an WDTR back to the target, since we asked first.
+               */
+              outb(0, p->base + RETURN_1);
+              switch (bus_width)
+              {
+               case BUS_8_BIT:
+                 scratch &= 0x7F;
+                 break;
+
+               case BUS_16_BIT:
+                  if (aic7xxx_verbose)
+                  {
+                   printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+                        "bit transfers.\n", p->host_no, target, channel);
+                  }
+                 scratch |= WIDEXFER;
+                 break;
+
+               case BUS_32_BIT:
+                 outb(SEND_REJ, p->base + RETURN_1);
+                  /* No verbose here!  We want to see this condition. */
+                 printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+                       "requesting 32 bit transfers, rejecting...\n",
+                        p->host_no, target, channel);
+                 break;
+
+               default:
+                 break;
+              }
+            }
+            else
+            {
+              /*
+               * Send our own WDTR in reply.
+               */
+              switch (bus_width)
+              {
+               case BUS_8_BIT:
+                 scratch &= 0x7F;
+                 break;
+
+               case BUS_32_BIT:
+               case BUS_16_BIT:
+                 if (p->bus_type == AIC_WIDE)
+                 {
+                    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+                          "bit transfers.\n", p->host_no, target, channel);
+                    bus_width = BUS_16_BIT;
+                    scratch |= WIDEXFER;
+                 }
+                 else
+                 {
+                    bus_width = BUS_8_BIT;
+                    scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
+                 }
+                 break;
+
+               default:
+                 break;
+              }
+              aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+              outb(SEND_MSG, p->base + RETURN_1);
+            }
+            p->needwdtr &= ~target_mask;
+            outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(scratch, p->base + SCSIRATE);
+            break;
+         }  /* case MSG_EXT_WDTR */
+
+          default:
+            /*
+             * Unknown extended message - reject it.
+             */
+            outb(SEND_REJ, p->base + RETURN_1);
+            break;
+       }  /* switch (message_code) */
+      }  /* case EXTENDED_MSG */
+      break;
+
+    case REJECT_MSG:
+      {
        /*
-        * The maximum offset for a wide device is 0x08; for a
-        * 8-bit bus device the maximum offset is 0x0F.
+        * What we care about here is if we had an outstanding SDTR
+        * or WDTR message for this target. If we did, this is a
+        * signal that the target is refusing negotiation.
         */
-       if (scratch & WIDEXFER)
+       unsigned char targ_scratch;
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+       targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+       if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
        {
-         max_offset = 0x08;
+          /*
+           * note 8bit xfers and clear flag
+           */
+          targ_scratch &= 0x7F;
+          p->needwdtr &= ~target_mask;
+          printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+                "negotiation; using 8 bit transfers.\n",
+                p->host_no, target, channel);
        }
        else
        {
-         max_offset = 0x0F;
-       }
-       aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
-                         scsi_id, channel);
-       /*
-        * Preserve the wide transfer flag.
-        */
-       scratch = rate | (scratch & WIDEXFER);
-       outb(scratch, TARG_SCRATCH + base + scratch_offset);
-       outb(scratch, SCSIRATE + base);
-       if ((scratch & 0x0F) == 0)
-       {
-          /*
-           * One of two things happened.  Either the device requested
-           * asynchronous data transfers, or it requested a synchronous
-           * data transfer rate that was so low that asynchronous
-           * transfers are faster (not to mention the controller won't
-           * support them).  In both cases the synchronous data transfer
-           * rate and the offset are set to 0 indicating asynchronous
-           * transfers.
-           *
-           * If the device requested an asynchronous transfer, then
-           * accept the request.  If the device is being forced to
-           * asynchronous data transfers and this is the first time
-           * we've seen the request, accept the request.  If we've
-           * already seen the request, then attempt to force
-           * asynchronous data transfers by rejecting the message.
-           */
-          if ((offset == 0) || (p->sdtr_pending & target_mask))
+          if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
           {
             /*
-             * Device requested asynchronous transfers or we're
-             * forcing asynchronous transfers for the first time.
+             * note asynch xfers and clear flag
              */
-            outb(0, RETURN_1 + base);
-          }
-          else
-          {
-            /*
-            * The first time in forcing asynchronous transfers
-             * failed, so we try sending a reject message.
-            */
-           outb(SEND_REJ, RETURN_1 + base);
+            targ_scratch &= 0xF0;
+            p->needsdtr &= ~target_mask;
+            printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+                  "synchronous negotiation; using asynchronous transfers.\n",
+                  p->host_no, target, channel);
           }
+          /*
+           * Otherwise, we ignore it.
+           */
        }
-       else
-       {
-         /*
-          * See if we initiated Sync Negotiation
-          */
-         if (p->sdtr_pending & target_mask)
-         {
-           /*
-            * Don't send an SDTR back to the target.
-            */
-           outb(0, RETURN_1 + base);
-         }
-         else
-         {
-           /*
-            * Send our own SDTR in reply.
-            */
-           printk("aic7xxx: Sending SDTR!!\n");
-           outb(SEND_SDTR, RETURN_1 + base);
-         }
-       }
-       /*
-        * Clear the flags.
-        */
-       p->needsdtr &= ~target_mask;
-       p->sdtr_pending &= ~target_mask;
-       break;
+        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+        outb(targ_scratch, p->base + SCSIRATE);
+      }
+      break;
 
-      case WDTR_MSG:
+    case BAD_STATUS:
       {
-       bus_width = inb(ARG_1 + base);
-       printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
-              "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
-       scratch = inb(TARG_SCRATCH + base + scratch_offset);
+       unsigned char scb_index;
+       struct aic7xxx_hwscb *hscb;
+       Scsi_Cmnd *cmd;
+
+       /* The sequencer will notify us when a command has an error that
+        * would be of interest to the kernel.  This allows us to leave
+        * the sequencer running in the common case of command completes
+        * without error.  The sequencer will have DMA'd the SCB back
+        * up to us, so we can reference the drivers SCB array.
+        */
+       scb_index = inb(p->base + SCB_TAG);
+       scb = p->scb_data->scb_array[scb_index];
+       hscb = scb->hscb;
 
-       if (p->wdtr_pending & target_mask)
+       /*
+        * Set the default return value to 0 indicating not to send
+        * sense.  The sense code will change this if needed and this
+        * reduces code duplication.
+        */
+       outb(0, p->base + RETURN_1);
+       if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
        {
-         /*
-          * Don't send an WDTR back to the target, since we asked first.
-          */
-         outb(0, RETURN_1 + base);
-         switch (bus_width)
-         {
-           case BUS_8_BIT:
-             scratch &= 0x7F;
-             break;
-
-           case BUS_16_BIT:
-             printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-             scratch |= 0x80;
-             break;
-
-           case BUS_32_BIT:
-             outb(SEND_REJ, RETURN_1 + base);
-             printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
-                     "transfers, rejecting...\n", p->host_no, scsi_id, channel);
-             break;
-         }
+          printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+                "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+                intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
        }
        else
        {
-         /*
-          * Send our own WDTR in reply.
-          */
-         printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
-         switch (bus_width)
-         {
-           case BUS_8_BIT:
-             scratch &= 0x7F;
-             break;
+          cmd = scb->cmd;
+         hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+          aic7xxx_status(cmd) = hscb->target_status;
 
-           case BUS_32_BIT:
-             /*
-               * Negotiate 16 bits.
-               */
-             bus_width = BUS_16_BIT;
-             /* Yes, we mean to fall thru here. */
-
-           case BUS_16_BIT:
-             printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-             scratch |= 0x80;
-             break;
-         }
-         outb(bus_width | SEND_WDTR, RETURN_1 + base);
-       }
-       p->needwdtr &= ~target_mask;
-       p->wdtr_pending &= ~target_mask;
-       outb(scratch, TARG_SCRATCH + base + scratch_offset);
-       outb(scratch, SCSIRATE + base);
-       break;
+          cmd->result |= hscb->target_status;
+
+          switch (status_byte(hscb->target_status))
+          {
+            case GOOD:
+             printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+                     "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+              break;
+
+            case CHECK_CONDITION:
+              if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+              {
+               unsigned int addr;    /* must be 32 bits */
+               /*
+                * XXX - How do we save the residual (if there is one).
+                */
+                aic7xxx_calculate_residual(p, scb);
+
+               /*
+                * Send a sense command to the requesting target.
+                * XXX - revisit this and get rid of the memcopys.
+                */
+               memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+                      sizeof(generic_sense));
+
+               scb->sense_cmd[1] = (cmd->lun << 5);
+               scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+               scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+               scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+               cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+                /*
+                 * XXX - We should allow disconnection, but can't as it
+                 * might allow overlapped tagged commands.
+                 */
+               /* hscb->control &= DISCENB; */
+                hscb->control = 0;
+               hscb->target_status = 0;
+               hscb->SG_segment_count = 1;
+
+               addr = VIRT_TO_BUS(&scb->sg_list[0]);
+               memcpy(&hscb->SG_list_pointer, &addr,
+                      sizeof(hscb->SG_list_pointer));
+
+               memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+                      sizeof(hscb->data_pointer));
+               /* Maintain SCB_LINKED_NEXT */
+               hscb->data_count &= 0xFF000000;
+               hscb->data_count |= scb->sg_list[0].length;
+
+               addr = VIRT_TO_BUS(scb->sense_cmd);
+               memcpy(&hscb->SCSI_cmd_pointer, &addr,
+                      sizeof(hscb->SCSI_cmd_pointer));
+               hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+
+                scb->sg_count = hscb->SG_segment_count;
+               scb->flags |= SCB_SENSE;
+                /*
+                 * Ensure the target is busy since this will be an
+                 * an untagged request.
+                 */
+                aic7xxx_busy_target(p, target, channel, hscb->tag);
+               outb(SEND_SENSE, p->base + RETURN_1);
+              }  /* first time sense, no errors */
+             else
+             {
+               if (aic7xxx_error(cmd) == 0)
+               {
+                 aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+               }
+             }
+              break;
+
+            case QUEUE_FULL:
+#ifdef NOT_YET
+              if (scb->hscb->control & TAG_ENB)
+              {
+               if (cmd->device->queue_depth > 2)
+               {
+                  cmd->device->queue_depth--;  /* Not correct */
+                  printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+                        "reduced to %d\n", p->host_no,
+                        TC_OF_SCB(scb), cmd->device->queue_depth);
+               }
+               /*
+                * XXX - Requeue this unconditionally?
+                */
+
+               /*
+                * We'd like to be able to give the SCB some more time
+                * (untimeout, then timeout).
+                */
+               break;
+              }
+#endif
+              printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+                     "queue depth %d, active %d\n", p->host_no,
+                     TC_OF_SCB(scb), cmd->device->queue_depth,
+                     p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+              /* Else treat this as if it was a BUSY condition. */
+              scb->hscb->target_status = (BUSY << 1) |
+                  (scb->hscb->target_status & 0x01);
+              /* Fall through to the BUSY case. */
+
+            case BUSY:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+                     p->host_no, TC_OF_SCB(scb));
+              if (!aic7xxx_error(cmd))
+              {
+               /*
+                * The mid-level SCSI code should be fixed to
+                * retry the command at a later time instead of
+                * trying right away.
+                */
+               aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+              }
+              udelay(1000);  /*  A small pause (1ms) to help the drive */
+              break;
+
+            default:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+                     "status 0x%x.\n", p->host_no,
+                    TC_OF_SCB(scb), scb->hscb->target_status);
+              if (!aic7xxx_error(cmd))
+              {
+               aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+              }
+              break;
+          }  /* end switch */
+       }  /* end else of */
       }
+      break;
 
-      case REJECT_MSG:
+    case AWAITING_MSG:
       {
-       /*
-        * What we care about here is if we had an
-        * outstanding SDTR or WDTR message for this
-        * target. If we did, this is a signal that
-        * the target is refusing negotiation.
-        */
+       unsigned char scb_index;
+        unsigned char message_offset;
 
-       scratch = inb(TARG_SCRATCH + base + scratch_offset);
+       scb_index = inb(p->base + SCB_TAG);
+       scb = p->scb_data->scb_array[scb_index];
 
-       if (p->wdtr_pending & target_mask)
+       /*
+        * This SCB had a MK_MESSAGE set in its control byte informing
+        * the sequencer that we wanted to send a special message to
+        * this target.
+        */
+        message_offset = inb(p->base + MSG_LEN);
+       if (scb->flags & SCB_DEVICE_RESET)
        {
-         /*
-          * note 8bit xfers and clear flag
-          */
-         scratch &= 0x7F;
-         p->needwdtr &= ~target_mask;
-         p->wdtr_pending &= ~target_mask;
-         printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
-                 "negotiation; using 8 bit transfers.\n",
-                 p->host_no, scsi_id, channel);
+          outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+          outb(1, p->base + MSG_LEN);
+          printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+                p->host_no, TC_OF_SCB(scb));
        }
-       else
+        else if (scb->flags & SCB_ABORT)
+        {
+          if ((scb->hscb->control & TAG_ENB) != 0)
+          {
+            outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+          }
+          else
+          {
+            outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+          }
+          outb(message_offset + 1, p->base + MSG_LEN);
+          printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+                 p->host_no, TC_OF_SCB(scb));
+        }
+       else if (scb->flags & SCB_MSGOUT_WDTR)
        {
-         if (p->sdtr_pending & target_mask)
-         {
-           /*
-            * note asynch xfers and clear flag
-            */
-           scratch &= 0xF0;
-           p->needsdtr &= ~target_mask;
-           p->sdtr_pending &= ~target_mask;
-           printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
-                   "synchronous negotiation; using asynchronous transfers.\n",
-                   p->host_no, scsi_id, channel);
-         }
-         /*
-          * Otherwise, we ignore it.
-          */
+          aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
+        {
+          unsigned char target_scratch;
+          unsigned short ultra_enable;
+          int i, sxfr;
+
+          /*
+           * Pull the user defined setting from scratch RAM.
+           */
+          target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+          sxfr = target_scratch & SXFR;
+          ultra_enable = inb(p->base + ULTRA_ENB) |
+              (inb(p->base + ULTRA_ENB + 1) << 8);
+          if (ultra_enable & target_mask)
+          {
+            sxfr |= 0x100;
+          }
+          for (i = 0; i < num_aic7xxx_syncrates; i++)
+          {
+            if (sxfr == aic7xxx_syncrates[i].rate)
+            break;
+          }
+          aic7xxx_construct_sdtr(p, message_offset,
+                                 aic7xxx_syncrates[i].period,
+                                 target_scratch & WIDEXFER ?
+                                 MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
+        }
+        else 
+        {
+          panic("aic7xxx: AWAITING_MSG for an SCB that does "
+                "not have a waiting message.");
        }
-       outb(scratch, TARG_SCRATCH + base + scratch_offset);
-       outb(scratch, SCSIRATE + base);
-       break;
       }
+      break;
 
-      case BAD_STATUS:
-        /* The sequencer will notify us when a command has an error that
-         * would be of interest to the kernel.  This allows us to leave
-         * the sequencerrunning in the common case of command completes
-         * without error.
-         */
+    case DATA_OVERRUN:
+      {
+       unsigned char scb_index = inb(p->base + SCB_TAG);
+        unsigned char lastphase = inb(p->base + LASTPHASE);
+       unsigned int i, overrun;
+
+       scb = (p->scb_data->scb_array[scb_index]);
+       overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+                 (inb(p->base + STCNT + 2) << 16);
+       overrun = 0x00FFFFFF - overrun;
+       printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+               "in %s phase, tag %d; forcing a retry.\n",
+               p->host_no, TC_OF_SCB(scb), overrun,
+               lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+               scb->hscb->tag);
+        printk(KERN_WARNING "%s seen Data Phase.  Length = %d, NumSGs = %d.\n",
+               inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+               aic7xxx_length(scb->cmd, 0), scb->sg_count);
+        for (i = 0; i < scb->sg_count; i++)
+        {
+          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, scb->sg_list[i].address, scb->sg_list[i].length);
+        }
+       /*
+        * XXX - What do we really want to do on an overrun?  The
+        *       mid-level SCSI code should handle this, but for now,
+        *       we'll just indicate that the command should retried.
+        */
+       aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
+      }
+      break;
 
-       scb_index = inb(SCB_TAG + base);
-       scb = (p->scb_array[scb_index]);
-       outb(0, RETURN_1 + base);   /* CHECK_CONDITION may change this */
-       if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-       {
-         printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-                "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-                intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-       }
-       else
-       {
-         cmd = scb->cmd;
-          scb->target_status = inb(SCB_TARGET_STATUS + base);
-         aic7xxx_status(cmd) = scb->target_status;
+/* #if AIC7XXX_NOT_YET */
+    /* XXX Fill these in later */
+    case MSG_BUFFER_BUSY:
+      printk("aic7xxx: Message buffer busy.\n");
+      break;
+    case MSGIN_PHASEMIS:
+      printk("aic7xxx: Message-in phasemis.\n");
+      break;
+/*#endif */
+
+    case ABORT_CMDCMPLT:
+      /* This interrupt serves to pause the sequencer until we can clean
+       * up the QOUTFIFO allowing us to handle any abort SCBs that may
+       * completed yet still have an SCB in the QINFIFO or waiting for
+       * selection queue.  By the time we get here, we should have
+       * already cleaned up the queues, so all we need to do is unpause
+       * the sequencer.
+       */
+      break;
 
-         cmd->result |= scb->target_status;
+    default:              /* unknown */
+      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, intstat, inb(p->base + SCSISIGI));
+      break;
+  }
 
-         switch (status_byte(scb->target_status))
-         {
-           case GOOD:
-              printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
-             break;
+  /*
+   * Clear the sequencer interrupt and unpause the sequencer.
+   */
+  outb(CLRSEQINT, p->base + CLRINT);
+  unpause_sequencer(p, /* unpause always */ TRUE);
+}
 
-           case CHECK_CONDITION:
-             if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
-             {
-                unsigned char tcl;
-               unsigned int  req_buf; /* must be 32 bits */
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_scsiint
+ *
+ * Description:
+ *   Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  unsigned char scb_index;
+  unsigned char status;
+  struct aic7xxx_scb *scb;
 
-                tcl = scb->target_channel_lun;
+  scb_index = inb(p->base + SCB_TAG);
+  status = inb(p->base + SSTAT1);
 
-               /*
-                 * Send a sense command to the requesting target.
-                 */
-               cmd->flags |= WAS_SENSE;
-               memcpy((void *) scb->sense_cmd, (void *) generic_sense,
-                      sizeof(generic_sense));
-
-               scb->sense_cmd[1] = (cmd->lun << 5);
-               scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
-               scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
-               scb->sg_list[0].length = sizeof(cmd->sense_buffer);
-               req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
-               cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
-                scb->control = scb->control & DISCENB;
-               scb->target_channel_lun = tcl;
-               addr = VIRT_TO_BUS(scb->sense_cmd);
-               scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-               memcpy(scb->SCSI_cmd_pointer, &addr,
-                      sizeof(scb->SCSI_cmd_pointer));
-               scb->SG_segment_count = 1;
-               memcpy(scb->SG_list_pointer, &req_buf,
-                      sizeof(scb->SG_list_pointer));
-                scb->data_count = scb->sg_list[0].length;
-               memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-                      sizeof(scb->data_pointer));
-
-                aic7xxx_putscb(p, scb);
-                /*
-                 * Ensure that the target is "BUSY" so we don't get overlapping
-                 * commands if we happen to be doing tagged I/O.
-                 */
-               aic7xxx_busy_target(scsi_id, channel, base);
+  if (scb_index < p->scb_data->numscbs)
+  {
+    scb = p->scb_data->scb_array[scb_index];
+    if ((scb->flags & SCB_ACTIVE) == 0)
+    {
+      scb = NULL;
+    }
+  }
+  else
+  {
+    scb = NULL;
+  }
 
-                aic7xxx_add_waiting_scb(base, scb);
-               outb(SEND_SENSE, RETURN_1 + base);
-             }  /* first time sense, no errors */
-              else
-              {
-                cmd->flags &= ~ASKED_FOR_SENSE;
-               if (aic7xxx_error(cmd) == 0)
-                {
-                 aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-                }
-              }
-             break;
-
-           case BUSY:
-             printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
-                     p->host_no, scb->target_channel_lun);
-             if (!aic7xxx_error(cmd))
-             {
-                /* The error code here used to be DID_BUS_BUSY,
-                 * but after extensive testing, it has been determined
-                 * that a DID_BUS_BUSY return is a waste of time.  If
-                 * the problem is something that will go away, then it
-                 * will, if it isn't, then you don't want the endless
-                 * looping that you get with a DID_BUS_BUSY.  Better
-                 * to be on the safe side and specify an error condition
-                 * that will eventually lead to a reset or abort of some
-                 * sort instead of an endless loop.
-                 */
-               aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-             }
-             break;
-
-           case QUEUE_FULL:
-             printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
-              scb->state |= SCB_ASSIGNEDQ;
-              scbq_insert_tail(&p->assigned_scbs, scb);
-             break;
-
-           default:
-             printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
-                    p->host_no, scb->target_status);
-             if (!aic7xxx_error(cmd))
-             {
-               aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-             }
-             break;
-         }  /* end switch */
-       }  /* end else of */
-       break;
+  if ((status & SCSIRSTI) != 0)
+  {
+    char channel;
 
-      case RESIDUAL:
-       scb_index = inb(SCB_TAG + base);
-       scb = (p->scb_array[scb_index]);
-       if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-       {
-         printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-                "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-                intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-       }
-       else
-       {
-         cmd = scb->cmd;
-         /*
-          *  Don't destroy valid residual information with
-          *  residual coming from a check sense operation.
-          */
-         if (!(cmd->flags & WAS_SENSE))
-         {
-           /*
-            *  We had an 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 = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
-           actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
-                     (inb(SCB_RESID_DCNT1 + base) <<  8) |
-                     inb(SCB_RESID_DCNT0 + base);
-
-           if (actual < cmd->underflow)
-           {
-             printk(KERN_WARNING "scsi%d: Target %d underflow - "
-                    "Wanted at least %u, got %u, residual SG count %d.\n",
-                    p->host_no, cmd->target, cmd->underflow, actual,
-                     inb(SCB_RESID_SGCNT + base));
-             aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-             aic7xxx_status(cmd) = scb->target_status;
-           }
-         }
-       }
-       break;
+    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
 
-      case ABORT_TAG:
-       scb_index = inb(SCB_TAG + base);
-       scb = (p->scb_array[scb_index]);
-       if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-       {
-         printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-                "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
-                intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-       }
-       else
-       {
-         cmd = scb->cmd;
-         /*
-          * We didn't receive a valid tag back from the target
-          * on a reconnect.
-          */
-         printk("scsi%d: Invalid tag received on target %d, channel %c, "
-                 "lun %d - Sending ABORT_TAG.\n", p->host_no,
-                 scsi_id, channel, cmd->lun & 0x07);
-
-         cmd->result = (DID_RETRY_COMMAND << 16);
-          aic7xxx_done(p, scb);
-       }
-       break;
+    printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+           p->host_no, channel);
+    /*
+     * Go through and abort all commands for the channel, but do not
+     * reset the channel again.
+     */
+    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+    scb = NULL;
+  }
+  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
+  {
+    /*
+     * First look at what phase we were last in.  If it's message-out,
+     * chances are pretty good that the bus free was in response to
+     * one of our abort requests.
+     */
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+    unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+    char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+    int printerror = TRUE;
 
-      case AWAITING_MSG:
-       scb_index = inb(SCB_TAG + base);
-       scb = (p->scb_array[scb_index]);
-       if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-       {
-         printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-                "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-                intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-       }
-       else
-       {
-         /*
-          * This SCB had a zero length command, informing the sequencer
-          * that we wanted to send a special message to this target.
-          * We only do this for BUS_DEVICE_RESET messages currently.
-          */
-          if (scb->state & SCB_DEVICE_RESET)
-          {
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
-          scsi_id);
-#endif
-            outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-            outb(1, MSG_LEN + base);
-          }
-          else
-          {
-            panic("scsi%d: AWAITING_SCB for an SCB that does "
-                  "not have a waiting message.\n", p->host_no);
-          }
-       }
-       break;
+    outb(0, p->base + SCSISEQ);
+    if (lastphase == P_MESGOUT)
+    {
+      unsigned char sindex;
+      unsigned char message;
 
-      case IMMEDDONE:
-        scb_index = inb(SCB_TAG + base);
-       scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
-         scsi_id, scb_index, scb->state);
-#endif
-        if (scb->state & SCB_DEVICE_RESET)
-        {
-          int found;
+      sindex = inb(p->base + SINDEX);
+      message = inb(p->base + sindex - 1);
 
-          /*
-           * Go back to async/narrow transfers and renegotiate.
-           */
-          aic7xxx_unbusy_target(scsi_id, channel, base);
-          p->needsdtr |= (p->needsdtr_copy & target_mask);
-          p->needwdtr |= (p->needwdtr_copy & target_mask);
-          p->sdtr_pending &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
-          scratch = inb(TARG_SCRATCH + base + scratch_offset);
-          scratch &= SXFR;
-          outb(scratch, TARG_SCRATCH + base + scratch_offset);
-          found = aic7xxx_reset_device(p, (int) scsi_id, channel);
-          printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
-                 "aborted.\n", p->host_no, found);
-          /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
-          run_aborted_queue = TRUE;
+      if (message == MSG_ABORT)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_ABORT_TAG)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_BUS_DEV_RESET)
+      {
+        aic7xxx_handle_device_reset(p, target, channel);
+        scb = NULL;
+        printerror = 0;
+      }
+    }
+    if (printerror != 0)
+    {
+      if (scb != NULL)
+      {
+        unsigned char tag;
+
+        if ((scb->hscb->control & TAG_ENB) != 0)
+        {
+          tag = scb->hscb->tag;
         }
         else
         {
-          panic("scsi%d: Immediate complete for unknown operation.\n",
-                p->host_no);
+          tag = SCB_LIST_NULL;
         }
-        break;
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      }
+      printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, lastphase,
+             (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    }
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    outb(CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if ((status & SELTO) != 0)
+  {
+    unsigned char scbptr;
+    unsigned char nextscb;
+    Scsi_Cmnd *cmd;
+
+    scbptr = inb(p->base + WAITING_SCBH);
+    outb(scbptr, p->base + SCBPTR);
+    scb_index = inb(p->base + SCB_TAG);
 
-      case DATA_OVERRUN:
+    scb = NULL;
+    if (scb_index < p->scb_data->numscbs)
+    {
+      scb = p->scb_data->scb_array[scb_index];
+      if ((scb->flags & SCB_ACTIVE) == 0)
       {
-        unsigned int overrun;
-
-        scb = (p->scb_array[inb(base + SCB_TAG)]);
-        overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
-                  (inb(base + STCNT2) << 16);
-        overrun =0x00FFFFFF - overrun;
-        printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
-               "a retry.\n", p->host_no, overrun);
-        aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
-        break;
+        scb = NULL;
       }
+    }
+    if (scb == NULL)
+    {
+      printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, scb_index);
+      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+             "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+             inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+             inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+    }
+    else
+    {
+      /*
+       * XXX - If we queued an abort tag, go clean up the disconnected list.
+       */
+      cmd = scb->cmd;
+      cmd->result = (DID_TIME_OUT << 16);
+
+      /*
+       * Clear an pending messages for the timed out
+       * target and mark the target as free.
+       */
+      outb(0, p->base + MSG_LEN);
+      aic7xxx_index_busy_target(p, cmd->target,
+          cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+      outb(0, p->base + SCB_CONTROL);
+
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      nextscb = inb(p->base + SCB_NEXT);
+      outb(nextscb, p->base + WAITING_SCBH);
+
+      /*
+       * Put this SCB back on the free list.
+       */
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    /*
+     * Stop the selection.
+     */
+    outb(0, p->base + SCSISEQ);
+    outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if (scb == NULL)
+  {
+    printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+           "during scsiint 0x%x scb(%d)\n"
+           "      SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+           p->host_no, status, scb_index, inb(p->base + SIMODE0),
+           inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    /*
+     * Turn off the interrupt and set status to zero, so that it
+     * falls through the rest of the SCSIINT code.
+     */
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  else if (status & SCSIPERR)
+  {
+    /*
+     * Determine the bus phase and queue an appropriate message.
+     */
+    char  *phase;
+    Scsi_Cmnd *cmd;
+    unsigned char mesg_out = MSG_NOOP;
+    unsigned char lastphase = inb(p->base + LASTPHASE);
 
-#if AIC7XXX_NOT_YET
-      /* XXX Fill these in later */
-      case MESG_BUFFER_BUSY:
+    cmd = scb->cmd;
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        phase = "Data-Out";
         break;
-      case MSGIN_PHASEMIS:
+      case P_DATAIN:
+        phase = "Data-In";
+        mesg_out = MSG_INITIATOR_DET_ERR;
         break;
-#endif
+      case P_COMMAND:
+        phase = "Command";
+        break;
+      case P_MESGOUT:
+        phase = "Message-Out";
+        break;
+      case P_STATUS:
+        phase = "Status";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_MESGIN:
+        phase = "Message-In";
+        mesg_out = MSG_PARITY_ERROR;
+        break;
+      default:
+        phase = "unknown";
+        break;
+    }
 
-      default:               /* unknown */
-       printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-              p->host_no, intstat, inb(SCSISIGI + base));
-       break;
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+           p->host_no, TC_OF_SCB(scb), phase);
+
+    /*
+     * We've set the hardware to assert ATN if we get a parity
+     * error on "in" phases, so all we need to do is stuff the
+     * message buffer with the appropriate message.  "In" phases
+     * have set mesg_out to something other than MSG_NOP.
+     */
+    if (mesg_out != MSG_NOOP)
+    {
+      outb(mesg_out, p->base + MSG_OUT);
+      outb(1, p->base + MSG_LEN);
+      scb = NULL;
     }
+    else
+    {
+      /*
+       * Should we allow the target to make this decision for us?
+       */
+      cmd->result = DID_RETRY_COMMAND << 16;
+    }
+    outb(CLRSCSIPERR, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+  }
+  else
+  {
+    /*
+     * We don't know what's going on. Turn off the
+     * interrupt source and try to continue.
+     */
+    printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  if (scb != NULL)
+  {
+    aic7xxx_done(p, scb);
+    aic7xxx_done_cmds_complete(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *
+ *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ *         be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct aic7xxx_host *p;
+  unsigned char intstat;
+  unsigned long flags;
 
-    /*
-     * Clear the sequencer interrupt and unpause the sequencer.
-     */
-    outb(CLRSEQINT, CLRINT + base);
-    UNPAUSE_SEQUENCER(p);
-  }
+  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
 
-  if (intstat & SCSIINT)
+  /*
+   * Search for the host with a pending interrupt.  If we can't find
+   * one, then we've encountered a spurious interrupt.
+   */
+  while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
   {
-    int status = inb(SSTAT1 + base);
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
-    }
-
-    scb_index = inb(SCB_TAG + base);
-    scb = (p->scb_array[scb_index]);
-    if (status & SCSIRSTI)
-    {
-      PAUSE_SEQUENCER(p);
-      printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
-             p->host_no, channel);
-      /*
-       * Go through and abort all commands for the channel, but do not
-       * reset the channel again.
-       */
-      aic7xxx_reset_channel(p, channel, FALSE);
-      run_aborted_queue = TRUE;
-    }
-    else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+    if (p->next == NULL)
     {
-      printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
-      /*
-       * Turn off the interrupt and set status to zero, so that it
-       * falls through the rest of the SCSIINT code.
-       */
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
-      scb = NULL;
+      p = NULL;
     }
-    else if (status & SCSIPERR)
+    else
     {
-      char  *phase;
-      unsigned char mesg_out = MSG_NOP;
-      unsigned char lastphase = inb(LASTPHASE + base);
-
-      cmd = scb->cmd;
-      switch (lastphase)
-      {
-        case P_DATAOUT:
-          phase = "Data-Out";
-          break;
-        case P_DATAIN:
-          phase = "Data-In";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_COMMAND:
-          phase = "Command";
-          break;
-        case P_MESGOUT:
-          phase = "Message-Out";
-          break;
-        case P_STATUS:
-          phase = "Status";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_MESGIN:
-          phase = "Message-In";
-          mesg_out = MSG_MSG_PARITY_ERROR;
-          break;
-        default:
-          phase = "unknown";
-          break;
-      }
-
-      /*
-       * A parity error has occurred during a data
-       * transfer phase. Flag it and continue.
-       */
-      printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
-             "channel %d, lun %d.\n", p->host_no, phase,
-             cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
-
-      /*
-       * We've set the hardware to assert ATN if we get a parity
-       * error on "in" phases, so all we need to do is stuff the
-       * message buffer with the appropriate message. In phases
-       * have set mesg_out to something other than MSG_NOP.
-       */
-      if (mesg_out != MSG_NOP)
-      {
-        outb(mesg_out, MSG0 + base);
-        outb(1, MSG_LEN + base);
-        cmd->result = DID_PARITY << 16;
-      }
-      else
-      {
-        /*
-         * Should we allow the target to make this decision for us?
-         */
-        cmd->result = DID_RETRY_COMMAND << 16;
-      }
-      aic7xxx_done(p, scb);
+      p = (struct aic7xxx_host *) p->next->hostdata;
     }
-    else if (status & SELTO)
-    {
-      unsigned char waiting;
+  }
 
-      cmd = scb->cmd;
+  if (p == NULL)
+    return;
 
-      cmd->result = (DID_TIME_OUT << 16);
-      /*
-       * Clear an pending messages for the timed out
-       * target and mark the target as free.
-       */
-      ha_flags = inb(FLAGS + base);
-      outb(0, MSG_LEN + base);
-      aic7xxx_unbusy_target(scsi_id, channel, base);
-      /*
-       * Stop the selection.
-       */
-      outb(0, SCSISEQ + base);
-      outb(0, SCB_CONTROL + base);
-      outb(CLRSELTIMEO, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  intstat = inb(p->base + INTSTAT);
 
-      /*
-       * Shift the waiting for selection queue forward
-       */
-      waiting = inb(WAITING_SCBH + base);
-      outb(waiting, SCBPTR + base);
-      waiting = inb(SCB_NEXT + base);
-      outb(waiting, WAITING_SCBH + base);
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
-      RESTART_SEQUENCER(p);
-      aic7xxx_done(p, scb);
-    }
-    else if (!(status & BUSFREE))
+  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+  {
+    /*
+     * We must only have one card at this IRQ and it must have been
+     * added to the board data before the spurious interrupt occurred.
+     * It is sufficient that we check isr_count and not the spurious
+     * interrupt count.
+     */
+    printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+    if (intstat)
     {
-      /*
-       * We don't know what's going on. Turn off the
-       * interrupt source and try to continue.
-       */
-      printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      /* Try clearing all interrupts. */
+      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
     }
+    return;
+  }
+
+  if (p->flags & IN_ISR)
+  {
+    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+           p->host_no);
+    return;
   }
 
-  if (run_aborted_queue)
-    aic7xxx_done_aborted_scbs(p);
+  /*
+   * Indicate that we're in the interrupt handler.
+   */
+  save_flags(flags);
+  cli();
+  p->flags |= IN_ISR;
 
   if (intstat & CMDCMPLT)
   {
-    int complete;
+    struct aic7xxx_scb *scb = NULL;
+    Scsi_Cmnd *cmd;
+    unsigned char qoutcnt;
+    unsigned char scb_index;
+    int i, interrupts_cleared = 0;
 
     /*
      * 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(QOUTFIFO + base);
+    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
-      scb = (p->scb_array[complete]);
-      if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-      {
-       printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
-              "       QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
-               "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
-               inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
-               scb->position);
-       outb(CLRCMDINT, CLRINT + base);
-       continue;
-      }
-      cmd = scb->cmd;
-      cmd->result |= (aic7xxx_error(cmd) << 16);
-      if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+#if 1
+  if (qoutcnt >= p->qfullcount - 1)
+    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+           "qoutcnt = %d.\n", qoutcnt);
+#endif
+    while (qoutcnt > 0)
+    {
+      for (i = 0; i < qoutcnt; i++)
       {
-        /*
-         * Got sense information.
-         */
-       cmd->flags &= ASKED_FOR_SENSE;
+        scb_index = inb(p->base + QOUTFIFO);
+        scb = p->scb_data->scb_array[scb_index];
+        if (scb == NULL)
+        {
+         printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+                "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+                 inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+          continue;
+        }
+        else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+         printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+                "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+                 p->host_no, scb_index, inb(p->base + QOUTCNT),
+                 inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+         continue;
+        }
+        cmd = scb->cmd;
+        if (scb->hscb->residual_SG_segment_count != 0)
+        {
+          aic7xxx_calculate_residual(p, scb);
+        }
+        if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+        {
+          /*
+           * Have to clean up any possible entries in the
+           * waiting queue and the QINFIFO.
+           */
+          int target;
+          char channel;
+          int lun;
+          unsigned char tag;
+
+          tag = SCB_LIST_NULL;
+          target = cmd->target;
+          lun = cmd->lun;
+          channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+          if (scb->hscb->control & TAG_ENB)
+          {
+            tag = scb->hscb->tag;
+          }
+          aic7xxx_reset_device(p, target, channel, lun, tag);
+          /*
+           * Run the done queue, but don't complete the commands; we
+           * do this once at the end of the loop.
+           */
+          aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+        }
+        cmd->result |= (aic7xxx_error(cmd) << 16);
+        p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+        aic7xxx_done(p, scb);
       }
-      p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
       /*
        * Clear interrupt status before checking the output queue again.
        * This eliminates a race condition whereby a command could
@@ -3242,56 +3941,152 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
        * so notification of the command being complete never made it
        * back up to the kernel.
        */
-      outb(CLRCMDINT, CLRINT + base);
-      aic7xxx_done(p, scb);
+      outb(CLRCMDINT, p->base + CLRINT);
+      interrupts_cleared++;
+      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+    }
 
-#ifdef AIC7XXX_PROC_STATS
-      /*
-       * XXX: we should actually know how much actually transferred
-       * XXX: for each command, but apparently that's too difficult.
-       */
-      actual = aic7xxx_length(cmd, 0);
-      if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+    if (interrupts_cleared == 0)
+    {
+      outb(CLRCMDINT, p->base + CLRINT);
+    }
+
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = inb(p->base + ERROR);
+
+    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+    for (i = 0; i < NUMBER(hard_error); i++)
+    {
+      if (errno & hard_error[i].errno)
       {
-        struct aic7xxx_xferstats *sp;
-        long *ptr;
-        int x;
+        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
+      }
+    }
+    printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+           inb(p->base + ERROR),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  }
+
+  if (intstat & SEQINT)
+  {
+    aic7xxx_handle_seqint(p, intstat);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    aic7xxx_handle_scsiint(p, intstat);
+  }
+
+  if (p->waiting_scbs.head != NULL)
+  {
+    aic7xxx_run_waiting_queues(p);
+  }
+
+  p->flags &= ~IN_ISR;
+  restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_device_queue_depth
+ *
+ * Description:
+ *   Determines the queue depth for a given device.  There are two ways
+ *   a queue depth can be obtained for a tagged queueing device.  One
+ *   way is the default queue depth which is determined by whether
+ *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
+ *   default queue depth (dependent on the number of hardware SCBs).
+ *   The other way we determine queue depth is through the use of the
+ *   aic7xxx_tag_info array which is enabled by defining
+ *   AIC7XXX_TAGGED_QUEUEING_BY_DEVICE.  This array can be initialized
+ *   with queue depths for individual devices.  It also allows tagged
+ *   queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+  int default_depth = 2;
+
+  device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  if (device->tagged_supported)
+  {
+    unsigned short target_mask;
+    int tag_enabled = TRUE;
+
+    target_mask = (1 << (device->id | (device->channel << 3)));
 
-        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
-        sp->xfers++;
+#ifdef AIC7XXX_CMDS_PER_LUN
+    default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+    if (p->scb_data->maxhscbs <= 4)
+    {
+      default_depth = 4;  /* Not many SCBs to work with. */
+    }
+    else
+    {
+      default_depth = 8;
+    }
+#endif
+    if (!(p->discenable & target_mask))
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+             "enable tagged queueing.\n",
+             p->host_no, device->id, device->channel);
+    }
+    else
+    {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+      device->queue_depth = default_depth;
+#else
+      if (p->instance > NUMBER(aic7xxx_tag_info))
+      {
+        device->queue_depth = default_depth;
+      }
+      else
+      {
+        unsigned char  tindex;
 
-        if (cmd->request.cmd == WRITE)
+        tindex = device->id | (device->channel << 3);
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
         {
-          sp->w_total++;
-          sp->w_total512 += (actual >> 9);
-          ptr = sp->w_bins;
+          tag_enabled = FALSE;
+          device->queue_depth = 2;  /* Tagged queueing is disabled. */
         }
-        else
+        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
-          sp->r_total++;
-          sp->r_total512 += (actual >> 9);
-          ptr = sp->r_bins;
+          device->queue_depth = default_depth;
         }
-        for (x = 9; x <= 17; x++)
+        else
         {
-          if (actual < (1 << x))
-          {
-            ptr[x - 9]++;
-            break;
-          }
+          device->queue_depth =
+            aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
-        if (x > 17)
+      }
+#endif
+      if ((device->tagged_queue == 0) && tag_enabled)
+      {
+        if (aic7xxx_verbose)
         {
-          ptr[x - 9]++;
+         printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+                "queue depth %d.\n", p->host_no,
+                device->id, device->channel, device->queue_depth);
         }
+        device->tagged_queue = 1;
+        device->current_tag = SCB_LIST_NULL;
       }
-#endif /* AIC7XXX_PROC_STATS */
-
-    } while (inb(QOUTCNT + base) & p->qcntmask);
+    }
   }
-  aic7xxx_done_cmds_complete(p);
-  p->flags &= ~IN_ISR;
-  aic7xxx_run_waiting_queues(p);
+#endif
 }
 
 /*+F*************************************************************************
@@ -3306,59 +4101,18 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
  *   algorithm for determining the queue depth based on the maximum
  *   SCBs for the controller.
  *-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
     Scsi_Device *scsi_devs)
 {
-  Scsi_Device *device = scsi_devs;
-  int tq_depth = 2;
+  Scsi_Device *device;
   struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-  tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-  {
-    if (p->maxhscbs <= 4)
-    {
-      tq_depth = 4;  /* Not many SCBs to work with. */
-    }
-    else
-    {
-      tq_depth = 8;
-    }
-  }
-#endif
-
   for (device = scsi_devs; device != NULL; device = device->next)
   {
     if (device->host == host)
     {
-      device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-      if (device->tagged_supported)
-      {
-        unsigned short target_mask = (1 << device->id) | device->channel;
-
-        if (!(p->discenable & target_mask))
-        {
-          printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
-                 "tagged queueing for target %d, channel %d, LUN %d.\n",
-                 host->host_no, device->id, device->channel, device->lun);
-        }
-        else
-        {
-          device->queue_depth = tq_depth;
-          if (device->tagged_queue == 0)
-          {
-            printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
-                  "channel %d, LUN %d, queue depth %d.\n", host->host_no,
-                   device->id, device->channel, device->lun,
-                   device->queue_depth);
-            device->tagged_queue = 1;
-            device->current_tag = SCB_LIST_NULL;
-          }
-        }
-      }
-#endif
+      aic7xxx_device_queue_depth(p, device);
     }
   }
 }
@@ -3385,7 +4139,7 @@ static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
  *   The fourth byte's lowest bit seems to be an enabled/disabled
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
-static aha_type
+static aha_chip_type
 aic7xxx_probe(int slot, int base, aha_status_type *bios)
 {
   int i;
@@ -3394,7 +4148,7 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    aha_type type;
+    aha_chip_type type;
     int bios_disabled;
   } AIC7xxx[] = {
     { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3433,7 +4187,8 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
        return (AIC7xxx[i].type);
       }
 
-      printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+             "disabled at slot %d, ignored.\n", slot);
     }
   }
 
@@ -3460,10 +4215,9 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
  *   useful in that it gives us an 800 nsec timer.  After a read from the
  *   SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
  *   later.
- *
  *-F*************************************************************************/
 static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
 {
   int i = 0, k = 0;
   unsigned char temp;
@@ -3476,11 +4230,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0)   \
+  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)        \
   {                                            \
     ;  /* Do nothing */                                \
   }                                            \
-  (void) inb(SEECTL_2840 + base);
+  (void) inb(p->base + SEECTL_2840);
 
   /*
    * Read the first 32 registers of the seeprom.  For the 2840,
@@ -3493,8 +4247,8 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
     /*
      * Send chip select for one clock cycle.
      */
-    outb(CK_2840 | CS_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3503,11 +4257,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = CS_2840 | seeprom_read.bits[i];
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * Send the 6 bit address (MSB first, LSB last).
@@ -3517,11 +4271,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
       temp = k;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = CS_2840 | temp;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3533,12 +4287,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
     for (i = 0; i <= 16; i++)
     {
       temp = CS_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * The serial EEPROM has a checksum in the last word.  Keep a
@@ -3554,12 +4308,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(CK_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(CK_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
   }
 
 #if 0
@@ -3573,17 +4327,64 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
     }
     printk(" 0x%x", seeprom[k]);
   }
-  printk("\n");
-#endif
-
-  if (checksum != sc->checksum)
+  printk("\n");
+#endif
+
+  if (checksum != sc->checksum)
+  {
+    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    return (0);
+  }
+
+  return (1);
+#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   acquire_seeprom
+ *
+ * Description:
+ *   Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+  int wait;
+
+  /*
+   * Request access of the memory port.  When access is
+   * granted, SEERDY will go high.  We use a 1 second
+   * timeout which should be near 1 second more than
+   * is needed.  Reason: after the 7870 chip reset, there
+   * should be no contention.
+   */
+  outb(SEEMS, p->base + SEECTL);
+  wait = 1000;  /* 1000 msec = 1 second */
+  while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+  {
+    wait--;
+    udelay(1000);  /* 1 msec */
+  }
+  if ((inb(p->base + SEECTL) & SEERDY) == 0)
   {
-    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    outb(0, p->base + SEECTL);
     return (0);
   }
-
   return (1);
-#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   release_seeprom
+ *
+ * Description:
+ *   Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+  outb(0, p->base + SEECTL);
 }
 
 /*+F*************************************************************************
@@ -3625,7 +4426,7 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
  *   first).  The clock cycling from low to high initiates the next data
  *   bit to be sent from the chip.
  *
- *   The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ *   The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
  *   register.  After successful arbitration for the memory port, the
  *   SEECS bit of the SEECTL register is connected to the chip select.
  *   The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3635,17 +4436,14 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
  *   to this is when we first request access to the memory port.  The
  *   SEERDY goes high to signify that access has been granted and, for
  *   this case, has no implied timing.
- *
  *-F*************************************************************************/
 static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
-    seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+    unsigned int len, seeprom_chip_type chip)
 {
   int i = 0, k;
-  unsigned long timeout;
   unsigned char temp;
   unsigned short checksum = 0;
-  unsigned short *seeprom = (unsigned short *) sc;
   struct seeprom_cmd {
     unsigned char len;
     unsigned char bits[3];
@@ -3653,43 +4451,33 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(SEECTL + base) & SEERDY) == 0)   \
+  while ((inb(p->base + SEECTL) & SEERDY) == 0)        \
   {                                            \
     ;  /* Do nothing */                                \
   }
 
   /*
-   * Request access of the memory port.  When access is
-   * granted, SEERDY will go high.  We use a 1 second
-   * timeout which should be near 1 second more than
-   * is needed.  Reason: after the 7870 chip reset, there
-   * should be no contention.
+   * Request access of the memory port.
    */
-  outb(SEEMS, SEECTL + base);
-  timeout = jiffies + 100;  /* 1 second timeout */
-  while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
-  {
-    ; /* Do nothing!  Wait for access to be granted.  */
-  }
-  if ((inb(SEECTL + base) & SEERDY) == 0)
+  if (acquire_seeprom(p) == 0)
   {
-    outb(0, SEECTL + base);
     return (0);
   }
 
   /*
-   * Read the first 32 registers of the seeprom.  For the 7870,
-   * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
-   * but only the first 32 are used by Adaptec BIOS.  The loop
-   * will range from 0 to 31.
+   * Read 'len' registers of the seeprom.  For the 7870, the 93C46
+   * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+   * the first 32 are used by Adaptec BIOS.  Some adapters use the
+   * 93C56 SEEPROM which is a 2048-bit device.  The loop will range
+   * from 0 to 'len' - 1.
    */
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     /*
      * Send chip select for one clock cycle.
      */
-    outb(SEEMS | SEECK | SEECS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3698,25 +4486,25 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
     /*
-     * Send the 6 bit address (MSB first, LSB last).
+     * Send the 6 or 8 bit address (MSB first, LSB last).
      */
     for (i = ((int) chip - 1); i >= 0; i--)
     {
       temp = k + offset;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = SEEMS | SEECS | (temp << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3728,56 +4516,57 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
     for (i = 0; i <= 16; i++)
     {
       temp = SEEMS | SEECS;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
-     * The serial EEPROM has a checksum in the last word.  Keep a
-     * running checksum for all words read except for the last
-     * word.  We'll verify the checksum after all words have been
-     * read.
+     * The serial EEPROM should have a checksum in the last word.
+     * Keep a running checksum for all words read except for the
+     * last word.  We'll verify the checksum after all words have
+     * been read.
      */
-    if (k < (sizeof(*sc) / 2) - 1)
+    if (k < (len - 1))
     {
-      checksum = checksum + seeprom[k];
+      checksum = checksum + scarray[k];
     }
 
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS | SEECK, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS | SEECK, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
   }
 
   /*
    * Release access to the memory port and the serial EEPROM.
    */
-  outb(0, SEECTL + base);
+  release_seeprom(p);
 
 #if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk("Computed checksum 0x%x, checksum read 0x%x\n",
+         checksum, scarray[len - 1]);
   printk("Serial EEPROM:");
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     if (((k % 8) == 0) && (k != 0))
     {
       printk("\n              ");
     }
-    printk(" 0x%x", seeprom[k]);
+    printk(" 0x%x", scarray[k]);
   }
   printk("\n");
 #endif
 
-  if (checksum != sc->checksum)
+  if (checksum != scarray[len - 1])
   {
     return (0);
   }
@@ -3788,563 +4577,452 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
 
 /*+F*************************************************************************
  * Function:
- *   detect_maxscb
+ *   write_brdctl
  *
  * Description:
- *   Detects the maximum number of SCBs for the controller and returns
- *   the count and a mask in config (config->maxscbs, config->qcntmask).
+ *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
-  unsigned char sblkctl_reg;
-  int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
-  config->flags |= PAGE_ENABLED;
-#endif
-  base = config->base;
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-    case AIC_284x:
-      /*
-       * Check for Rev C or E boards. Rev E boards can supposedly have
-       * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
-       * It's still not clear extactly what is different about the Rev E
-       * boards, but we think it allows 8 bit entries in the QOUTFIFO to
-       * support "paging" SCBs (more than 4 commands can be active at once).
-       *
-       * The Rev E boards have a read/write autoflush bit in the
-       * SBLKCTL register, while in the Rev C boards it is read only.
-       */
-      sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
-      outb(sblkctl_reg, SBLKCTL + base);
-      if (inb(SBLKCTL + base) == sblkctl_reg)
-      {
-        /*
-         * We detected a Rev E board, we allow paging on this board.
-         */
-        printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
-               board_names[config->type]);
-       outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
-      }
-      else
-      {
-        /* Do not allow paging. */
-        config->flags &= ~PAGE_ENABLED;
-        printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
-               board_names[config->type]);
-      }
-      break;
-
-    default:
-      break;
-  }
-
-  /*
-   * Walk the SCBs to determine how many there are.
-   */
-  i = 1;
-  outb(0, SCBPTR + base);
-  outb(0, SCBARRAY + base);
-
-  while (i < AIC7XXX_MAXSCB)
-  {
-    outb(i, SCBPTR + base);
-    outb(i, SCBARRAY + base);
-    if (inb(SCBARRAY + base) != i)
-      break;
-    outb(0, SCBPTR + base);
-    if (inb(SCBARRAY + base) != 0)
-      break;
-
-    outb(i, SCBPTR + base);      /* Clear the control byte. */
-    outb(0, SCBARRAY + base);
-
-    config->qcntmask |= i;       /* Update the count mask. */
-    i++;
-  }
-  outb(i, SCBPTR + base);   /* Ensure we clear the control bytes. */
-  outb(0, SCBARRAY + base);
-  outb(0, SCBPTR + base); 
-  outb(0, SCBARRAY + base);
-
-  config->maxhscbs = i;
-  config->qcntmask |= i;
-  if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
-  {
-    config->maxscbs = AIC7XXX_MAXSCB;
-  }
-  else
-  {
-    config->flags &= ~PAGE_ENABLED;  /* Disable paging if we have 255 SCBs!. */
-    config->maxscbs = config->maxhscbs;
-  }
-
-  printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
-  if (config->flags & PAGE_ENABLED)
-    printk(", %d page-enabled SCBs.\n", config->maxscbs);
-  else
-    printk(", paging not enabled.\n");
-
+  unsigned char brdctl;
+
+  brdctl = BRDCS | BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl |= value;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDCS;
+  outb(brdctl, p->base + BRDCTL);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_register
+ *   read_brdctl
  *
  * Description:
- *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
-    struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
 {
-  int i;
-  unsigned char sblkctl, flags = 0;
-  int max_targets;
-  int found = 1;
-  unsigned int sram, base;
-  unsigned char target_settings;
-  unsigned char scsi_conf, host_conf;
-  unsigned short ultraenable = 0;
-  int have_seeprom = FALSE;
-  struct Scsi_Host *host;
-  struct aic7xxx_host *p;
-  struct seeprom_config sc;
-
-  base = config->base;
-
-  /*
-   * Lock out other contenders for our i/o space.
-   */
-  request_region(base, MAXREG - MINREG, "aic7xxx");
+  outb(BRDRW | BRDCS, p->base + BRDCTL);
+  return (inb(p->base + BRDCTL));
+}
 
-  switch (config->type)
+/*+F*************************************************************************
+ * Function:
+ *   configure_termination
+ *
+ * Description:
+ *   Configures the termination settings on PCI adapters that have
+ *   SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+    unsigned short adapter_control, unsigned char max_targ)
+{
+  unsigned char brdctl_int, brdctl_ext;
+  int internal50_present;
+  int internal68_present = 0;
+  int external_present = 0;
+  int eprom_present;
+  int high_on;
+  int low_on;
+  int old_verbose;
+
+  if (acquire_seeprom(p))
   {
-    case AIC_7770:
-    case AIC_7771:
-      /*
-       * Use the boot-time option for the interrupt trigger type.  If not
-       * supplied (-1), then we use BIOS settings to determine the interrupt
-       * trigger type (level or edge) and use this value for pausing and
-       * unpausing the sequencer.
-       */
-      switch (aic7xxx_irq_trigger)
-      {
-        case  0: config->unpause = INTEN;          /* Edge */
-                 break;
-        case  1: config->unpause = IRQMS | INTEN;  /* Level */
-                 break;
-        case -1:
-        default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; 
-                 break;
-      }
-      config->pause = config->unpause | PAUSE;
+    if (adapter_control & CFAUTOTERM)
+    {
+      old_verbose = aic7xxx_verbose;
+      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
+                       "verify driver");
+      printk(KERN_INFO "         detected settings and use manual termination "
+                       "if necessary."); 
+
+      /* Configure auto termination. */
+      outb(SEECS | SEEMS, p->base + SEECTL);
 
       /*
-       * For some 274x boards, we must clear the CHIPRST bit and pause
-       * the sequencer. For some reason, this makes the driver work.
-       * For 284x boards, we give it a CHIPRST just like the 294x boards.
+       * First read the status of our cables.  Set the rom bank to
+       * 0 since the bank setting serves as a multiplexor for the
+       * cable detection logic.  BRDDAT5 controls the bank switch.
        */
-      outb(config->pause | CHIPRST, HCNTRL + base);
-      aic7xxx_delay(1);
-      if (inb(HCNTRL + base) & CHIPRST)
-      {
-       printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
-      }
-      outb(config->pause, HCNTRL + base);
+      write_brdctl(p, 0);
 
       /*
-       * Just to be on the safe side with the 274x, we will re-read the irq
-       * since there was some issue about resetting the board.
+       * Now read the state of the internal connectors.  The
+       * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+       * set when cables are not present (BRDDAT6 is INT50 and
+       * BRDDAT7 is INT68).
        */
-      config->irq = inb(INTDEF + base) & 0x0F;
-      if ((config->type == AIC_7771) &&
-          (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
-      {
-        config->bios = AIC_DISABLED;
-        config->flags |= USE_DEFAULTS;
-      }
-      else
+      brdctl_int = read_brdctl(p);
+      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+      if (max_targ > 8)
       {
-        host_conf = inb(HOSTCONF + base);
-        config->bus_speed = host_conf & DFTHRSH;
-        config->busrtime = (host_conf << 2) & BOFF;
+        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
       }
 
       /*
-       * Setup the FIFO threshold and the bus off time
+       * Set the rom bank to 1 and determine
+       * the other signals.
        */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
+      write_brdctl(p, BRDDAT5);
 
       /*
-       * A reminder until this can be detected automatically.
+       * Now read the state of the external connectors.  BRDDAT6 is
+       * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+       * set when the eprom is present.
        */
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-            (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_284x:
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_284X;
-      config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
-      config->irq = inb(INTDEF + base) & 0x0F;
-      host_conf = inb(HOSTCONF + base);
-
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      have_seeprom = read_2840_seeprom(base, &sc);
-      if (!have_seeprom)
+      brdctl_ext = read_brdctl(p);
+      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+      eprom_present = brdctl_ext & BRDDAT7;
+      if (aic7xxx_verbose)
       {
-       printk("aic7xxx: Unable to read SEEPROM.\n");
-      }
-      else
-      {
-       printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (sc.bios_control & CF284XEXTEND)
-          config->flags |= EXTENDED_TRANSLATION;
-        if (!(sc.bios_control & CFBIOSEN))
+        if (max_targ > 8)
         {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
         else
         {
-         config->parity = (sc.adapter_control & CFSPARITY) ?
-                          AIC_ENABLED : AIC_DISABLED;
-         config->low_term = (sc.adapter_control & CF284XSTERM) ?
-                               AIC_ENABLED : AIC_DISABLED;
-         /*
-          * XXX - Adaptec *does* make 284x wide controllers, but the
-          *       documents do not say where the high byte termination
-          *       enable bit is located.
-           */
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
+        printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+               "brdctl_ext=0x%x\n",
+               eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
       }
 
-      host_conf = inb(HOSTCONF + base);
-      config->bus_speed = host_conf & DFTHRSH;
-      config->busrtime = (host_conf << 2) & BOFF;
-
-      /*
-       * Setup the FIFO threshold and the bus off time
-       */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
-
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-            (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      /*
-       * Remember if Ultra was enabled in case there is no SEEPROM.
-       * Fall through to the rest of the AIC_78xx code.
-       */
-      if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
-        config->flags |= ULTRA_ENABLED;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
       /*
-       * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+       * Now set the termination based on what we found.  BRDDAT6
+       * controls wide termination enable.
        */
-      config->scsi_id = inb(SCSIID + base) & OID;
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_294X;
-      config->pause = config->unpause | PAUSE;
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
+      high_on = FALSE;
+      low_on = FALSE;
+      if ((max_targ > 8) &&
+          ((external_present == 0) || (internal68_present == 0)))
+      {
+        high_on = TRUE;
+      }
 
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      if ((config->type == AIC_7873) || (config->type == AIC_7883))
+      if ((internal50_present + internal68_present + external_present) <= 1)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c56_66);
+        low_on = TRUE;
       }
-      else
+          
+      if (internal50_present && internal68_present && external_present)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c46);
+        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+               "         Only two connectors on the adapter may be "
+               "used at a time!\n");
       }
-      if (!have_seeprom)
+
+      if (high_on == TRUE)
+        write_brdctl(p, BRDDAT6);
+      else
+        write_brdctl(p, 0);
+
+      if (low_on == TRUE)
+        *sxfrctl1 |= STPWEN;
+
+      if (aic7xxx_verbose)
       {
-        for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
+        if (max_targ > 8)
         {
-          if (inb(sram) != 0x00)
-            break;
-        }
-        if (sram == base + TARG_SCRATCH)
-        {
-          for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-          {
-            if (inb(sram) != 0xFF)
-              break;
-          }
-        }
-        if ((sram != base + 0x60) && (config->scsi_id != 0))
-        {
-          config->flags &= ~USE_DEFAULTS;
-         printk("\naic7xxx: Unable to read SEEPROM; "
-                 "using leftover BIOS values.\n");
+          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+                 low_on ? "ON" : "OFF",
+                 high_on ? "ON" : "OFF");
         }
         else
         {
-          printk("\n");
-          printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
-                 "settings.\n");
-          config->flags |= USE_DEFAULTS;
-          config->flags &= ~ULTRA_ENABLED;
-          config->scsi_id = 7;
+          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
         }
-        scsi_conf = ENSPCHK | RESET_SCSI;
+      }
+      aic7xxx_verbose = old_verbose;
+    }
+    else
+    {
+      if (adapter_control & CFSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+      }
+      outb(SEEMS | SEECS, p->base + SEECTL);
+      /*
+       * Configure high byte termination.
+       */
+      if (adapter_control & CFWSTERM)
+      {
+        write_brdctl(p, BRDDAT6);
       }
       else
       {
-       printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (!(sc.bios_control & CFBIOSEN))
-        {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
-          scsi_conf = ENSPCHK | RESET_SCSI;
-        }
-        else
-        {
-          scsi_conf = 0;
-          if (sc.adapter_control & CFRESETB)
-            scsi_conf |= RESET_SCSI;
-          if (sc.adapter_control & CFSPARITY)
-            scsi_conf |= ENSPCHK;
-         if (sc.bios_control & CFEXTEND)
-            config->flags |= EXTENDED_TRANSLATION;
-         config->scsi_id = (sc.brtime_id & CFSCSIID);
-         config->parity = (sc.adapter_control & CFSPARITY) ?
-                            AIC_ENABLED : AIC_DISABLED;
-         config->low_term = (sc.adapter_control & CFSTERM) ?
-                              AIC_ENABLED : AIC_DISABLED;
-         config->high_term = (sc.adapter_control & CFWSTERM) ?
-                               AIC_ENABLED : AIC_DISABLED;
-         config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
-          if (((config->type == AIC_7880) || (config->type == AIC_7881) ||
-               (config->type == AIC_7882) || (config->type == AIC_7883) ||
-               (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
-          {
-            printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
-                   "speed.\n");
-            config->flags |= ULTRA_ENABLED;
-          }
-        }
+        write_brdctl(p, 0);
+      }
+      if (aic7xxx_verbose)
+      {
+        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+               (adapter_control & CFSTERM) ? "ON" : "OFF",
+               (adapter_control & CFWSTERM) ? "ON" : "OFF");
       }
+    }
+    release_seeprom(p);
+  }
+}
 
-      outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
-      config->bus_speed = DFTHRSH_100;
-      outb(config->bus_speed, DSPCISTATUS + base);
+/*+F*************************************************************************
+ * Function:
+ *   detect_maxscb
+ *
+ * Description:
+ *   Detects the maximum number of SCBs for the controller and returns
+ *   the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char max_scbid = 255;
 
-      /*
-       * In case we are a wide card...
-       */
-      outb(config->scsi_id, SCSICONF + base + 1);
+  /*
+   * It's possible that we've already done this for multichannel
+   * adapters.
+   */
+  if (p->scb_data->maxhscbs == 0)
+  {
+    /*
+     * We haven't initialized the SCB settings yet.  Walk the SCBs to
+     * determince how many there are.
+     */
+    outb(0, p->base + FREE_SCBH);
 
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-            (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
+    for (i = 0; i < AIC7XXX_MAXSCB; i++)
+    {
+      outb(i, p->base + SCBPTR);
+      outb(i, p->base + SCB_CONTROL);
+      if (inb(p->base + SCB_CONTROL) != i)
+        break;
+      outb(0, p->base + SCBPTR);
+      if (inb(p->base + SCB_CONTROL) != 0)
+        break;
 
-    default:
-      panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+      outb(i, p->base + SCBPTR);
+      outb(0, p->base + SCB_CONTROL);   /* Clear the control byte. */
+      outb(i + 1, p->base + SCB_NEXT);  /* Set the next pointer. */
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);  /* Make the tag invalid. */
+
+      /* Make the non-tagged targets not busy. */
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+    }
+
+    /* Make sure the last SCB terminates the free list. */
+    outb(i - 1, p->base + SCBPTR);
+    outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+    /* Ensure we clear the first (0) SCBs control byte. */
+    outb(0, p->base + SCBPTR);
+    outb(0, p->base + SCB_CONTROL);
+
+    p->scb_data->maxhscbs = i;
+  }
+
+  if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+  {
+    /* Determine the number of valid bits in the FIFOs. */
+    outb(max_scbid, p->base + QINFIFO);
+    max_scbid = inb(p->base + QINFIFO);
+    p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+  }
+  else
+  {
+    p->scb_data->maxscbs = p->scb_data->maxhscbs;
+  }
+  if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+  {
+    /*
+     * Disable paging if the QINFIFO doesn't allow more SCBs than
+     * we have in hardware.
+     */
+    p->flags &= ~PAGE_ENABLED;
+  }
+
+  /*
+   * Set the Queue Full Count.  Some cards have more queue space than
+   * SCBs.
+   */
+  switch (p->chip_class)
+  {
+    case AIC_777x:
+      p->qfullcount = 4;
+      p->qcntmask = 0x07;
+      break;
+    case AIC_785x:
+    case AIC_786x:
+      p->qfullcount = 8;
+      p->qcntmask = 0x0f;
+      break;
+    case AIC_787x:
+    case AIC_788x:
+      if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+      {
+        p->qfullcount = AIC7XXX_MAXSCB;
+        p->qcntmask = 0xFF;
+      }
+      else
+      {
+        p->qfullcount = 16;
+        p->qcntmask = 0x1F;
+      }
+      break;
   }
+}
 
-  detect_maxscb(config);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_register
+ *
+ * Description:
+ *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char sblkctl, flags = 0;
+  int max_targets;
+  int found = 1;
+  char channel_ids[] = {'A', 'B', 'C'};
+  unsigned char target_settings;
+  unsigned char scsi_conf, sxfrctl1;
+  unsigned short ultraenable = 0;
+  struct Scsi_Host *host;
 
-  if (config->chip_type == AIC_777x)
-  {
-    if (config->pause & IRQMS)
-    {
-      printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
-    }
-    else
-    {
-      printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
-    }
-  }
+  /*
+   * Lock out other contenders for our i/o space.
+   */
+  request_region(p->base, MAXREG - MINREG, "aic7xxx");
 
   /*
    * Read the bus type from the SBLKCTL register. Set the FLAGS
    * register in the sequencer for twin and wide bus cards.
    */
-  sblkctl = inb(SBLKCTL + base);
-  if (config->flags & PAGE_ENABLED)
+  sblkctl = inb(p->base + SBLKCTL);
+  if (p->flags & PAGE_ENABLED)
     flags = PAGESCBS;
 
   switch (sblkctl & SELBUS_MASK)
   {
     case SELNARROW:     /* narrow/normal bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-      config->bus_type = AIC_SINGLE;
-      outb(flags | SINGLE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+      p->bus_type = AIC_SINGLE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELWIDE:     /* Wide bus */
-      config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
-      config->bus_type = AIC_WIDE;
-      printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
-            board_names[config->type]);
-      outb(flags | WIDE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+      p->bus_type = AIC_WIDE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELBUSB:     /* Twin bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
-      config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
-      config->bus_type = AIC_TWIN;
-      printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
-            board_names[config->type]);
-      outb(flags | TWIN_BUS, FLAGS + base);
-#else
-      config->bus_type = AIC_SINGLE;
-      printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
-            board_names[config->type]);
-      outb(flags, FLAGS + base);
-#endif
+      p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+      p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+      p->bus_type = AIC_TWIN;
+      printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+             p->scsi_id, p->scsi_id_b);
+      outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
       break;
 
     default:
       printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
-            "mail deang@teleport.com\n", inb(SBLKCTL + base));
-      outb(0, FLAGS + base);
+            "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+      outb(0, p->base + SEQ_FLAGS);
       return (0);
   }
 
   /*
-   * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
-   * take the card out of diagnostic mode and make the host adapter
-   * LED follow bus activity (will not always be on).
-   */
-  outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
-
-  /*
-   * 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?
-   *
-   * The PCI cards get their interrupt from PCI BIOS.
+   * Detect SCB parameters and initialize the SCB array.
    */
-  if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
-  {
-    printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
-          "ignoring.\n");
-    return (0);
-  }
+  detect_maxscb(p);
+  printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+         p->scb_data->maxhscbs, p->scb_data->maxscbs,
+         p->qfullcount, p->qcntmask);
 
-  /*
-   * 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(config);
+  host = p->host;
 
-  /*
-   * Register each "host" and fill in the returned Scsi_Host
-   * structure as best we can. Some of the parameters aren't
-   * really relevant for bus types beyond ISA, 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 aic7xxx_host));
-  host->can_queue = config->maxscbs;
+  host->can_queue = p->scb_data->maxscbs;
   host->cmd_per_lun = 2;
+  host->sg_tablesize = AIC7XXX_MAX_SG;
   host->select_queue_depths = aic7xxx_select_queue_depth;
-  host->this_id = config->scsi_id;
-  host->io_port = config->base;
+  host->this_id = p->scsi_id;
+  host->io_port = p->base;
   host->n_io_port = 0xFF;
-  host->base = (unsigned char *)config->mbase;
-  host->irq = config->irq;
-  if (config->bus_type == AIC_WIDE)
+  host->base = (unsigned char *) p->mbase;
+  host->irq = p->irq;
+  if (p->bus_type == AIC_WIDE)
   {
     host->max_id = 16;
   }
-  if (config->bus_type == AIC_TWIN)
+  if (p->bus_type == AIC_TWIN)
   {
     host->max_channel = 1;
   }
 
-  p = (struct aic7xxx_host *) host->hostdata;
-
   p->host = host;
-  p->host_no = (int)host->host_no;
+  p->host_no = host->host_no;
   p->isr_count = 0;
-  p->base = base;
-  p->maxscbs = config->maxscbs;
-  p->maxhscbs = config->maxhscbs;
-  p->qcntmask = config->qcntmask;
-  p->mbase = (char *)config->mbase;
-  p->type = config->type;
-  p->chip_type = config->chip_type;
-  p->flags = config->flags;
-  p->chan_num = config->chan_num;
-  p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
-  if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
-  {
-    shared_3985_scbs = &(p->scb_usage);
-    p->scb_link = &(p->scb_usage);
-  }
-#endif
-  p->scb_link->numscbs = 0;
-  p->bus_type = config->bus_type;
-  p->seeprom = sc;
   p->next = NULL;
   p->completeq.head = NULL;
   p->completeq.tail = NULL;
-  scbq_init(&p->scb_link->free_scbs);
-  scbq_init(&p->page_scbs);
+  scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
-  scbq_init(&p->assigned_scbs);
-
-  p->unpause = config->unpause;
-  p->pause = config->pause;
 
-  for (i = 0; i <= 15; i++)
+  for (i = 0; i <= NUMBER(p->device_status); i++)
   {
     p->device_status[i].commands_sent = 0;
     p->device_status[i].flags = 0;
+    p->device_status[i].active_cmds = 0;
     p->device_status[i].last_reset = 0;
   }
-  if (aic7xxx_boards[config->irq] == NULL)
+  if (aic7xxx_boards[p->irq] == NULL)
   {
+    int result;
+    int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+    irg_flags = SA_INTERRUPT;
+#endif
     /*
      * Warning! This must be done before requesting the irq.  It is
      * possible for some boards to raise an interrupt as soon as
@@ -4352,17 +5030,26 @@ aic7xxx_register(Scsi_Host_Template *template,
      * kernel, an interrupt is triggered immediately.  Therefore, we
      * must ensure the board data is correctly set before the request.
      */
-    aic7xxx_boards[config->irq] = host;
+    aic7xxx_boards[p->irq] = host;
 
     /*
-     * Register IRQ with the kernel.
+     * Register IRQ with the kernel.  Only allow sharing IRQs with
+     * PCI devices.
      */
-    if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-       "aic7xxx", NULL))
+    if (p->chip_class == AIC_777x)
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+    }
+    else
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+                "aic7xxx", NULL));
+    }
+    if (result < 0)
     {
       printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
-             config->irq);
-      aic7xxx_boards[config->irq] = NULL;
+             p->irq);
+      aic7xxx_boards[p->irq] = NULL;
       return (0);
     }
   }
@@ -4373,79 +5060,74 @@ aic7xxx_register(Scsi_Host_Template *template,
      * registered host adapter. Add this host adapter's Scsi_Host
      * to the beginning of the linked list of hosts at the same IRQ.
      */
-    p->next = aic7xxx_boards[config->irq];
-    aic7xxx_boards[config->irq] = host;
-  }
-
-  /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
-   */
-  printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
-  aic7xxx_loadseq(base);
-
-  /*
-   * Set Fast Mode and Enable the board
-   */
-  outb(FASTMODE, SEQCTL + base);
-
-  if (p->chip_type == AIC_777x)
-  {
-    outb(ENABLE, BCTL + base);
+    p->next = aic7xxx_boards[p->irq];
+    aic7xxx_boards[p->irq] = host;
   }
 
-  printk("done.\n");
-
   /*
    * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
    */
   if (p->bus_type == AIC_TWIN)
   {
     /*
-     * Select Channel B.
+     * The controller is gated to channel B after a chip reset; set
+     * bus B values first.
      */
-    outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
-    outb(config->scsi_id_b, SCSIID + base);
-    scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
-    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-    outb(ENSELTIMO, SIMODE1 + base);
-#endif
+    outb(p->scsi_id_b, p->base + SCSIID);
+    scsi_conf = inb(p->base + SCSICONF + 1);
+    sxfrctl1 = inb(p->base + SXFRCTL1);
+    outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+         ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
     if (p->flags & ULTRA_ENABLED)
     {
-      outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
     }
     else
     {
-      outb(DFON | SPIOEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN, p->base + SXFRCTL0);
     }
 
-    /*
-     * Select Channel A
-     */
-    outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+    if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+    {
+      /* Reset SCSI bus B. */
+      if (aic7xxx_verbose)
+        printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+      aic7xxx_reset_current_bus(p);
+    }
+
+    /* Select channel A */
+    outb(SELNARROW, p->base + SBLKCTL);
   }
-  outb(config->scsi_id, SCSIID + base);
-  scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
-  outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-  outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+  outb(p->scsi_id, p->base + SCSIID);
+  scsi_conf = inb(p->base + SCSICONF);
+  sxfrctl1 = inb(p->base + SXFRCTL1);
+  outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+       ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
   if (p->flags & ULTRA_ENABLED)
   {
-    outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
   }
   else
   {
-    outb(DFON | SPIOEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN, p->base + SXFRCTL0);
+  }
+
+  if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+  {
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose)
+      printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay.
+     */
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
   }
 
   /*
@@ -4472,67 +5154,47 @@ aic7xxx_register(Scsi_Host_Template *template,
   /*
    * Grab the disconnection disable table and invert it for our needs
    */
-  if (have_seeprom)
+  if (p->flags & USE_DEFAULTS)
   {
-    p->discenable = 0x0;
+    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+           "device parameters.\n");
+    p->discenable = 0xFFFF;
   }
   else
   {
-    if (config->bios == AIC_DISABLED)
-    {
-      printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
-             "device parameters.\n");
-      p->discenable = 0xFFFF;
-    }
-    else
-    {
-      p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
-          inb(DISC_DSB + base));
-    }
+    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+        inb(p->base + DISC_DSB));
   }
 
   for (i = 0; i < max_targets; i++)
   {
-    if (config->flags & USE_DEFAULTS)
+    if (p->flags & USE_DEFAULTS)
     {
-      target_settings = 0;  /* 10 MHz */
+      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
       p->needsdtr_copy |= (0x01 << i);
       p->needwdtr_copy |= (0x01 << i);
+      if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+        ultraenable |= (0x01 << i);
     }
     else
     {
-      if (have_seeprom)
+      target_settings = inb(p->base + TARG_SCRATCH + i);
+      if (target_settings & 0x0F)
       {
-       target_settings = ((sc.device_flags[i] & CFXFER) << 4);
-       if (sc.device_flags[i] & CFSYNCH)
-       {
-         p->needsdtr_copy |= (0x01 << i);
-       }
-       if (sc.device_flags[i] & CFWIDEB)
-       {
-         p->needwdtr_copy |= (0x01 << i);
-       }
-       if (sc.device_flags[i] & CFDISC)
-        {
-          p->discenable |= (0x01 << i);
-        }
+        p->needsdtr_copy |= (0x01 << i);
+        /*
+         * Default to asynchronous transfers (0 offset)
+         */
+        target_settings &= 0xF0;
       }
-      else
+      if (target_settings & 0x80)
       {
-        target_settings = inb(TARG_SCRATCH + base + i);
-        if (target_settings & 0x0F)
-        {
-          p->needsdtr_copy |= (0x01 << i);
-          /*
-           * Default to asynchronous transfers (0 offset)
-           */
-          target_settings &= 0xF0;
-        }
-        if (target_settings & 0x80)
-        {
-          p->needwdtr_copy |= (0x01 << i);
-          target_settings &= 0x7F;
-        }
+        p->needwdtr_copy |= (0x01 << i);
+        /*
+         * Clear the wide flag. When wide negotiation is successful,
+         * we'll enable it.
+         */
+        target_settings &= 0x7F;
       }
       if (p->flags & ULTRA_ENABLED)
       {
@@ -4543,7 +5205,7 @@ aic7xxx_register(Scsi_Host_Template *template,
           case 0x20:
             ultraenable |= (0x01 << i);
             break;
-          case 0x40:
+          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
             target_settings &= ~(0x70);
             break;
           default:
@@ -4551,7 +5213,7 @@ aic7xxx_register(Scsi_Host_Template *template,
         }
       }
     }
-    outb(target_settings, (TARG_SCRATCH + base + i));
+    outb(target_settings, p->base + TARG_SCRATCH + i);
   }
 
   /*
@@ -4566,103 +5228,448 @@ aic7xxx_register(Scsi_Host_Template *template,
   p->needsdtr = p->needsdtr_copy;
   p->needwdtr = p->needwdtr_copy;
   p->orderedtag = 0;
-#if 0
-  printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
-  printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif
-  outb(ultraenable & 0xFF, ULTRA_ENB + base);
-  outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+  outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+  outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
+
+  /*
+   * Set the number of available hardware SCBs.
+   */
+  outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
+
+  /*
+   * 2s compliment of maximum tag value.
+   */
+  i = p->scb_data->maxscbs;
+  outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
+
+  /*
+   * Allocate enough hardware scbs to handle the maximum number of
+   * concurrent transactions we can have.  We have to make sure that
+   * the allocated memory is contiguous memory.  The Linux kmalloc
+   * routine should only allocate contiguous memory, but note that
+   * this could be a problem if kmalloc() is changed.
+   */
+  if (p->scb_data->hscbs == NULL)
+  {
+    size_t array_size;
+    unsigned int hscb_physaddr;
+
+    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+    p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+    if (p->scb_data->hscbs == NULL)
+    {
+      printk("aic7xxx: Unable to allocate hardware SCB array; "
+             "failing detection.\n");
+      release_region(p->base, MAXREG - MINREG);
+      /*
+       * Ensure that we only free the IRQ when there is _not_ another
+       * aic7xxx adapter sharing this IRQ.  The adapters are always
+       * added to the beginning of the list, so we can grab the next
+       * pointer and place it back in the board array.
+       */
+      if (p->next == NULL)
+      {
+        free_irq(p->irq, aic7xxx_isr);
+      }
+      aic7xxx_boards[p->irq] = p->next;
+      return(0);
+    }
+    /* At least the control byte of each SCB needs to be 0. */
+    memset(p->scb_data->hscbs, 0, array_size);
+
+    /* Tell the sequencer where it can find the hardware SCB array. */
+    hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+    outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+    outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+    outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+    outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+  }
+
+  /*
+   * QCount mask to deal with broken aic7850s that sporadically get
+   * garbage in the upper bits of their QCNT registers.
+    */
+  outb(p->qcntmask, p->base + QCNTMASK);
+
+  /*
+   * We don't have any waiting selections or disconnected SCBs.
+   */
+  outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+  outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+
+  /*
+   * Message out buffer starts empty
+   */
+  outb(0, p->base + MSG_LEN);
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home. On the PCI bus you *may* be home,
+   * but then your mailing address is dynamically assigned
+   * so no one can find you anyway :-)
+   */
+  aic7xxx_loadseq(p);
+
+  if (p->chip_class == AIC_777x)
+  {
+    outb(ENABLE, p->base + BCTL);  /* Enable the boards BUS drivers. */
+  }
+
+  /*
+   * 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, /* unpause_always */ TRUE);
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_chip_reset
+ *
+ * Description:
+ *   Perform a chip reset on the aic7xxx SCSI controller.  The controller
+ *   is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+  unsigned char hcntrl;
+  int wait;
+
+  /* Retain the IRQ type across the chip reset. */
+  hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+  /*
+   * For some 274x boards, we must clear the CHIPRST bit and pause
+   * the sequencer. For some reason, this makes the driver work.
+   */
+  outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+  /*
+   * In the future, we may call this function as a last resort for
+   * error handling.  Let's be nice and not do any unecessary delays.
+   */
+  wait = 1000;  /* 1 second (1000 * 1000 usec) */
+  while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+  {
+    udelay(1000);  /* 1 msec = 1000 usec */
+    wait = wait - 1;
+  }
+
+  if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+  {
+    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+  }
+
+  outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_alloc
+ *
+ * Description:
+ *   Allocate and initialize a host structure.  Returns NULL upon error
+ *   and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+    aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+  struct aic7xxx_host *p = NULL;
+  struct Scsi_Host *host;
+
+  /*
+   * Allocate a storage area by registering us with the mid-level
+   * SCSI layer.
+   */
+  host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+  if (host != NULL)
+  {
+    p = (struct aic7xxx_host *) host->hostdata;
+    memset(p, 0, sizeof(struct aic7xxx_host));
+    p->host = host;
+
+    if (scb_data != NULL)
+    {
+      /*
+       * We are sharing SCB data areas; use the SCB data pointer
+       * provided.
+       */
+      p->scb_data = scb_data;
+      p->flags |= SHARED_SCBDATA;
+    }
+    else
+    {
+      /*
+       * We are not sharing SCB data; allocate one.
+       */
+      p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+      if (p->scb_data != NULL)
+      {
+        memset(p->scb_data, 0, sizeof(scb_data_type));
+        scbq_init (&p->scb_data->free_scbs);
+      }
+      else
+      {
+        /*
+         * For some reason we don't have enough memory.  Free the
+         * allocated memory for the aic7xxx_host struct, and return NULL.
+         */
+        scsi_unregister(host);
+        p = NULL;
+      }
+    }
+    if (p != NULL)
+    {
+      p->host_no = host->host_no;
+      p->base = base;
+      p->mbase = mbase;
+      p->maddr = NULL;
+      p->flags = flags;
+      p->chip_type = chip_type;
+      p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+      p->pause = p->unpause | PAUSE;
+    }
+  }
+  return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_free
+ *
+ * Description:
+ *   Frees and releases all resources associated with an instance of
+ *   the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+  int i;
+
+  /*
+   * We should be careful in freeing the scb_data area.  For those
+   * adapters sharing external SCB RAM(398x), there will be only one
+   * scb_data area allocated.  The flag SHARED_SCBDATA indicates if
+   * one adapter is sharing anothers SCB RAM.
+   */
+  if (!(p->flags & SHARED_SCBDATA))
+  {
+    /*
+     * Free the allocated hardware SCB space.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      kfree(p->scb_data->scb_array[i]);
+    }
+    /*
+     * Free the hardware SCBs.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
 
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
+  }
   /*
-   * Set the number of available SCBs.
+   * Free the instance of the device structure.
    */
-  outb(config->maxhscbs, SCBCOUNT + base);
+  scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_load_seeprom
+ *
+ * Description:
+ *   Load the seeprom and configure adapter and target settings.
+ *   Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+  int have_seeprom = 0;
+  int i, max_targets;
+  unsigned char target_settings, scsi_conf;
+  unsigned short scarray[128];
+  struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+  }
+  switch (p->chip_type)
+  {
+    case AIC_7770:  /* None of these adapters have seeproms. */
+    case AIC_7771:
+    case AIC_7850:
+    case AIC_7855:
+      break;
 
-  /*
-   * 2s compliment of maximum tag value.
-   */
-  i = p->maxscbs;
-  outb(-i & 0xFF, COMP_SCBCOUNT + base);
+    case AIC_284x:
+      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+      break;
 
-  /*
-   * Set the QCNT (queue count) mask to deal with broken aic7850s that
-   * sporatically get garbage in the upper bits of their QCNT registers.
-   */
-  outb(config->qcntmask, QCNTMASK + base);
+    case AIC_7861:
+    case AIC_7870:
+    case AIC_7871:
+    case AIC_7872:
+    case AIC_7874:
+    case AIC_7881:
+    case AIC_7882:
+    case AIC_7884:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(*sc)/2, C46);
+      break;
 
-  /*
-   * Clear the active flags - no targets are busy.
-   */
-  outb(0, ACTIVE_A + base);
-  outb(0, ACTIVE_B + base);
+    case AIC_7860:  /* Motherboard Ultra controllers might have RAID port. */
+    case AIC_7880:
+      have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+      if (!have_seeprom)
+      {
+        have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+      }
+      break;
 
-  /*
-   * We don't have any waiting selections or disconnected SCBs.
-   */
-  outb(SCB_LIST_NULL, WAITING_SCBH + base);
-  outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+    case AIC_7873:  /* The 3985 adapters use the 93c56 serial EEPROM. */
+    case AIC_7883:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(scarray)/2, C56_66);
+      break;
 
-  /*
-   * Message out buffer starts empty
-   */
-  outb(0, MSG_LEN + base);
+    default:
+      break;
+  }
 
-  /*
-   * Reset the SCSI bus. Is this necessary?
-   *   There may be problems for a warm boot without resetting
-   *   the SCSI bus. Either BIOS settings in scratch RAM
-   *   will not get reinitialized, or devices may stay at
-   *   previous negotiated settings (SDTR and WDTR) while
-   *   the driver will think that no negotiations have been
-   *   performed.
-   *
-   * Some devices need a long time to "settle" after a SCSI
-   * bus reset.
-   */
-  if (!aic7xxx_no_reset)
+  if (!have_seeprom)
+  {
+    if (aic7xxx_verbose)
+    {
+      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+    }
+    p->flags |= USE_DEFAULTS;
+  }
+  else
   {
-    printk("aic7xxx: Resetting the SCSI bus...");
-    if (p->bus_type == AIC_TWIN)
+    if (aic7xxx_verbose)
+    {
+      printk("done\n");
+    }
+    p->flags |= HAVE_SEEPROM;
+
+    /*
+     * Update the settings in sxfrctl1 to match the termination settings.
+     */
+    *sxfrctl1 = 0;
+
+    /*
+     * First process the settings that are different between the VLB
+     * and PCI adapter seeproms.
+     */
+    if (p->chip_class == AIC_777x)
     {
+      /* VLB adapter seeproms */
+      if (sc->bios_control & CF284XEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
+
+      if (sc->adapter_control & CF284XSTERM)
+        *sxfrctl1 |= STPWEN;
       /*
-       * Select Channel B.
+       * The 284x SEEPROM doesn't have a max targets field.  We
+       * set it to 16 to make sure we take care of the 284x-wide
+       * adapters.  For narrow adapters, going through the extra
+       * 8 target entries will not cause any harm since they will
+       * will not be used.
+       *
+       * XXX - We should probably break out the bus detection
+       *       from the register function so we can use it here
+       *       to tell us how many targets there really are.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+      max_targets = 16;
+    }
+    else
+    {
+      /* PCI adapter seeproms */
+      if (sc->bios_control & CFEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-      outb(SCSIRSTO, SCSISEQ + base);
-      udelay(1000);
-      outb(0, SCSISEQ + base);
+      if (sc->adapter_control & CFSTERM)
+        *sxfrctl1 |= STPWEN;
 
-      /* Ensure we don't get a RSTI interrupt from this. */
-      outb(CLRSCSIRSTI, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+      /* Limit to 16 targets just in case. */
+      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+    }
 
-     /*
-       * Select Channel A.
-       */
-      outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+    for (i = 0; i < max_targets; i++)
+    {
+      target_settings = (sc->device_flags[i] & CFXFER) << 4;
+      if (sc->device_flags[i] & CFSYNCH)
+        target_settings |= SOFS;
+      if (sc->device_flags[i] & CFWIDEB)
+        target_settings |= WIDEXFER;
+      if (sc->device_flags[i] & CFDISC)
+        p->discenable |= (0x01 << i);
+      outb(target_settings, p->base + TARG_SCRATCH + i);
     }
+    outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+    outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
 
-    outb(SCSIRSTO, SCSISEQ + base);
-    udelay(1000);
-    outb(0, SCSISEQ + base);
+    p->scsi_id = sc->brtime_id & CFSCSIID;
 
-    /* Ensure we don't get a RSTI interrupt from this. */
-    outb(CLRSCSIRSTI, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
+    scsi_conf = (p->scsi_id & 0x7);
+    if (sc->adapter_control & CFSPARITY)
+      scsi_conf |= ENSPCHK;
+    if (sc->adapter_control & CFRESETB)
+      scsi_conf |= RESET_SCSI;
 
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
+    if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+    {
+      /*
+       * We allow the operator to override ultra enable through
+       * the boot prompt.
+       */
+      if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+      {
+        /* Treat us as a non-ultra card */
+        p->flags &= ~ULTRA_ENABLED;
+      }
+    }
 
-    printk("done.\n");
-  }
+    /* Set the host ID */
+    outb(scsi_conf, p->base + SCSICONF);
+    /* In case we are a wide card */
+    outb(p->scsi_id, p->base + SCSICONF + 1);
 
-  /*
-   * 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 (found);
+    if (p->chip_class != AIC_777x)
+    {
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
+       */
+      *sxfrctl1 = 0;
+      configure_termination(p, sxfrctl1, sc->adapter_control,
+        (unsigned char) sc->max_targets & CFMAXTARG);
+    }
+  }
+  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -4671,17 +5678,24 @@ aic7xxx_register(Scsi_Host_Template *template,
  *
  * Description:
  *   Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe().  A sequence of
+ *       probe(), attach()/detach(), and init() makes more sense than
+ *       one do-it-all function.  This may be useful when (and if) the
+ *       mid-level SCSI code is overhauled.
  *-F*************************************************************************/
 int
 aic7xxx_detect(Scsi_Host_Template *template)
 {
-  int found = 0, slot, base;
-  unsigned char irq = 0;
+  int found = 0;
+  aha_status_type adapter_bios;
+  aha_chip_class_type chip_class;
+  aha_chip_type chip_type;
+  int slot, base;
+  int chan_num = 0;
+  unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
   int i;
-  struct aic7xxx_host_config config;
-
-  template->proc_dir = &proc_scsi_aic7xxx;
-  config.chan_num = 0;
+  struct aic7xxx_host *p;
 
   /*
    * Since we may allow sharing of IRQs, it is imperative
@@ -4695,6 +5709,10 @@ aic7xxx_detect(Scsi_Host_Template *template)
     aic7xxx_boards[i] = NULL;
   }
 
+  template->proc_dir = &proc_scsi_aic7xxx;
+  template->name = aic7xxx_info(NULL);
+  template->sg_tablesize = AIC7XXX_MAX_SG;
+
   /*
    * Initialize the spurious count to 0.
    */
@@ -4716,33 +5734,174 @@ aic7xxx_detect(Scsi_Host_Template *template)
       continue;
     }
 
-    config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
-    if (config.type != AIC_NONE)
+    chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+    if (chip_type != AIC_NONE)
     {
+
+      switch (chip_type)
+      {
+        case AIC_7770:
+        case AIC_7771:
+          printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[chip_type], slot);
+          break;
+        case AIC_284x:
+          printk("aic7xxx: <%s> at VLB %d\n",
+                 board_names[chip_type], slot);
+          break;
+        default:
+          break;
+      }
+
       /*
        * We found a card, allow 1 spurious interrupt.
        */
       aic7xxx_spurious_count = 1;
 
       /*
-       * We "find" a AIC-7770 if we locate the card
-       * signature and we can set it up and register
-       * it with the kernel without incident.
+       * Pause the card preserving the IRQ type.  Allow the operator
+       * to override the IRQ trigger.
        */
-      config.chip_type = AIC_777x;
-      config.base = base;
-      config.mbase = 0;
-      config.irq = irq;
-      config.parity = AIC_ENABLED;
-      config.low_term = AIC_UNKNOWN;
-      config.high_term = AIC_UNKNOWN;
-      config.flags = 0;
-      if (aic7xxx_extended)
-        config.flags |= EXTENDED_TRANSLATION;
-      config.bus_speed = DFTHRSH_100;
-      config.busrtime = BOFF;
-      found += aic7xxx_register(template, &config);
+      if (aic7xxx_irq_trigger == 1)
+        hcntrl = IRQMS;  /* Level */
+      else if (aic7xxx_irq_trigger == 0)
+        hcntrl = 0;  /* Edge */
+      else
+        hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+      outb(hcntrl | PAUSE, base + HCNTRL);
+
+      irq = inb(INTDEF + base) & 0x0F;
+      switch (irq)
+      {
+        case 9:
+        case 10:
+        case 11:
+        case 12:
+        case 14:
+        case 15:
+          break;
+
+        default:
+          printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+          "level, ignoring.\n");
+          irq = 0;
+          break;
+      }
+
+      if (irq != 0)
+      {
+        p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+        if (p == NULL)
+        {
+          printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+          continue;
+        }
+        p->irq = irq & 0x0F;
+        p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+        p->flags |= PAGE_ENABLED;
+#endif
+        p->instance = found;
+        if (aic7xxx_extended)
+        {
+          p->flags |= EXTENDED_TRANSLATION;
+        }
+        aic7xxx_chip_reset(p);
+
+        switch (p->chip_type)
+        {
+          case AIC_7770:
+          case AIC_7771:
+          {
+            unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+            /*
+             * Get the primary channel information.  Right now we don't
+             * do anything with this, but someday we will be able to inform
+             * the mid-level SCSI code which channel is primary.
+             */
+            if (biosctrl & CHANNEL_B_PRIMARY)
+            {
+              p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+            }
+
+            if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+            {
+              p->flags |= USE_DEFAULTS;
+            }
+            break;
+          }
+
+          case AIC_284x:
+            if (!load_seeprom(p, &sxfrctl1))
+            {
+              if (aic7xxx_verbose)
+                printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+            }
+            break;
+
+          default:  /* Won't get here. */
+            break;
+        }
+        printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+               (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+               (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+        /*
+         * Check for Rev C or E boards. Rev E boards can supposedly have
+         * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+         * It's still not clear extactly what is different about the Rev E
+         * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+         * support "paging" SCBs (more than 4 commands can be active at once).
+         *
+         * The Rev E boards have a read/write autoflush bit in the
+         * SBLKCTL register, while in the Rev C boards it is read only.
+         */
+        sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+        outb(sblkctl, p->base + SBLKCTL);
+        if (inb(p->base + SBLKCTL) == sblkctl)
+        {
+          /*
+           * We detected a Rev E board, we allow paging on this board.
+           */
+          printk("Revision >= E\n");
+          outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+        }
+        else
+        {
+          /* Do not allow paging. */
+          p->flags &= ~PAGE_ENABLED;
+          printk("Revision <= C\n");
+        }
+
+        if (aic7xxx_verbose)
+          printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                 (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+        /*
+         * Set the FIFO threshold and the bus off time.
+         */
+        hostconf = inb(p->base + HOSTCONF);
+        outb(hostconf & DFTHRSH, p->base + BUSSPD);
+        outb((hostconf << 2) & BOFF, p->base + BUSTIME);
 
+        /*
+         * Try to initialize the card and register it with the kernel.
+         */
+        if (aic7xxx_register(template, p))
+        {
+          /*
+           * We successfully found a board and registered it.
+           */
+          found = found + 1;
+        }
+        else
+        {
+          /*
+           * Something went wrong; release and free all resources.
+           */
+          aic7xxx_free(p);
+        }
+      }
       /*
        * Disallow spurious interrupts.
        */
@@ -4758,15 +5917,15 @@ aic7xxx_detect(Scsi_Host_Template *template)
   {
     struct
     {
-      unsigned short vendor_id;
-      unsigned short device_id;
-      aha_type       card_type;
-      aha_chip_type  chip_type;
+      unsigned short      vendor_id;
+      unsigned short      device_id;
+      aha_chip_type       chip_type;
+      aha_chip_class_type chip_class;
     } const aic7xxx_pci_devices[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -4779,14 +5938,14 @@ aic7xxx_detect(Scsi_Host_Template *template)
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
     };
 
-    int error;
+    int error, flags;
     int done = 0;
     unsigned int iobase, mbase;
     unsigned short index = 0;
     unsigned char pci_bus, pci_device_fn;
-    unsigned int  csize_lattime;
-    unsigned int  class_revid;
-    unsigned int  devconfig;
+    unsigned char ultra_enb = 0;
+    unsigned int  devconfig, class_revid;
+    scb_data_type *shared_scb_data = NULL;
     char rev_id[] = {'B', 'C', 'D'};
 
     for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4803,36 +5962,33 @@ aic7xxx_detect(Scsi_Host_Template *template)
         }
         else  /* Found an Adaptec PCI device. */
         {
-          config.type = aic7xxx_pci_devices[i].card_type;
-          config.chip_type = aic7xxx_pci_devices[i].chip_type;
-          config.chan_num = 0;
-          config.bios = AIC_ENABLED;  /* Assume bios is enabled. */
-          config.flags = 0;
-          config.busrtime = 40;
-          switch (config.type)
+          chip_class = aic7xxx_pci_devices[i].chip_class;
+          chip_type = aic7xxx_pci_devices[i].chip_type;
+          chan_num = 0;
+          flags = 0;
+          switch (aic7xxx_pci_devices[i].chip_type)
           {
             case AIC_7850:
             case AIC_7855:
-            case AIC_7860:
-            case AIC_7861:
-              config.bios = AIC_DISABLED;
-              config.flags |= USE_DEFAULTS;
-              config.bus_speed = DFTHRSH_100;
+              flags |= USE_DEFAULTS;
               break;
 
             case AIC_7872:  /* 3940 */
             case AIC_7882:  /* 3940-Ultra */
-              config.chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
+              flags |= MULTI_CHANNEL;
+              chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
               number_of_3940s++;
               break;
 
             case AIC_7873:  /* 3985 */
             case AIC_7883:  /* 3985-Ultra */
-              config.chan_num = number_of_3985s;  /* Has 3 controllers */
+              chan_num = number_of_3985s;  /* Has 3 controllers */
+              flags |= MULTI_CHANNEL;
               number_of_3985s++;
               if (number_of_3985s == 3)
               {
                 number_of_3985s = 0;
+                shared_scb_data = NULL;
               }
               break;
 
@@ -4849,39 +6005,165 @@ aic7xxx_detect(Scsi_Host_Template *template)
                                             PCI_INTERRUPT_LINE, &irq);
           error += pcibios_read_config_dword(pci_bus, pci_device_fn,
                                             PCI_BASE_ADDRESS_1, &mbase);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             DEVCONFIG, &devconfig);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             CLASS_PROGIF_REVID, &class_revid);
+
+          printk("aic7xxx: <%s> at PCI %d\n",
+                 board_names[chip_type], PCI_SLOT(pci_device_fn));
 
           /*
-           * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+           * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
            * we mask it off.
            */
           iobase &= PCI_BASE_ADDRESS_IO_MASK;
 
+          p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+                            shared_scb_data);
+
+          if (p == NULL)
+          {
+            printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+            continue;
+          }
+
+          /* Remember to set the channel number, irq, and chip class. */
+          p->chan_num = chan_num;
+          p->irq = irq;
+          p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+          p->flags |= PAGE_ENABLED;
+#endif
+          p->instance = found;
+
           /*
-           * Read the PCI burst size and latency timer.
+           * Remember how the card was setup in case there is no seeprom.
            */
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CSIZE_LATTIME, &csize_lattime);
-          printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
-                 "PCLKS\n", (int) (csize_lattime & CACHESIZE),
-                 (csize_lattime >> 8) & 0x000000ff);
+          p->scsi_id = inb(p->base + SCSIID) & OID;
+          if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+          {
+            p->flags |= ULTRA_ENABLED;
+            ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+          }
+         sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CLASS_PROGIF_REVID, &class_revid);
-          if ((class_revid & DEVREVID) < 3)
+          aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+          if (devconfig & RAMPSM)
           {
-            printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
-                   rev_id[class_revid & DEVREVID]);
+            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+                   "access.\n");
+            /*
+             * XXX - Assume 9 bit SRAM and enable parity checking.
+             */
+            devconfig |= EXTSCBPEN;
+
+            /*
+             * XXX - Assume fast SRAM and only enable 2 cycle access if we
+             *       are sharing the SRAM across multiple adapters (398x).
+             */
+            if ((devconfig & MPORTMODE) == 0)
+            {
+              devconfig |= EXTSCBTIME;
+            }
+            devconfig &= ~SCBRAMSEL;
+            pcibios_write_config_dword(pci_bus, pci_device_fn,
+                                       DEVCONFIG, devconfig);
           }
+#endif
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             DEVCONFIG, &devconfig);
-          if (error)
+          if ((p->flags & USE_DEFAULTS) == 0)
+          {
+            load_seeprom(p, &sxfrctl1);
+          }
+
+          /*
+           * Take the LED out of diagnostic mode
+           */
+          sblkctl = inb(p->base + SBLKCTL);
+          outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+          /*
+           * We don't know where this is set in the SEEPROM or by the
+           * BIOS, so we default to 100%.
+           */
+          outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+          if (p->flags & USE_DEFAULTS)
           {
-            panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
-                  error);
+            int j;
+            /*
+             * Default setup; should only be used if the adapter does
+             * not have a SEEPROM.
+             */
+            /*
+             * Check the target scratch area to see if someone set us
+             * up already.  We are previously set up if the scratch
+             * area contains something other than all zeroes and ones.
+             */
+            for (j = TARG_SCRATCH; j < 0x60; j++)
+            {
+              if (inb(p->base + j) != 0x00)      /* Check for all zeroes. */
+                break;
+            }
+            if (j == TARG_SCRATCH)
+            {
+              for (j = TARG_SCRATCH; j < 0x60; j++)
+              {
+                if (inb(p->base + 1) != 0xFF)    /* Check for all ones. */
+                  break;
+              }
+            }
+            if ((j != 0x60) && (p->scsi_id != 0))
+            {
+              p->flags &= ~USE_DEFAULTS;
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+              }
+            }
+            else
+            {
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+                       "settings.\n");
+              }
+              /*
+               * Assume only one connector and always turn on
+               * termination.
+               */
+              sxfrctl1 = STPWEN;
+              p->scsi_id = 7;
+            }
+            outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+                 p->base + SCSICONF);
+            /* In case we are a wide card. */
+            outb(p->scsi_id, p->base + SCSICONF + 1);
+            if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+            {
+              /*
+               * If there wasn't a BIOS or the board wasn't in this mode
+               * to begin with, turn off Ultra.
+               */
+              p->flags &= ~ULTRA_ENABLED;
+            }
           }
 
-          printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+          /*
+           * Print some additional information about the adapter.
+           */
+          printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+                 "IO Mem 0x%x, IRQ %d",
+                 (p->flags & USE_DEFAULTS) ? "dis" : "en",
+                 p->base, p->mbase, p->irq);
+          if ((class_revid & DEVREVID) < 3)
+          {
+            printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+          }
+          printk("\n");
 
           /*
            * I don't think we need to bother with allowing
@@ -4890,58 +6172,57 @@ aic7xxx_detect(Scsi_Host_Template *template)
            */
           aic7xxx_spurious_count = 1;
 
-          config.base = iobase;
-          config.mbase = mbase;
-          config.irq = irq;
-          config.parity = AIC_ENABLED;
-          config.low_term = AIC_UNKNOWN;
-          config.high_term = AIC_UNKNOWN;
           if (aic7xxx_extended)
-            config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
-          if (devconfig & RAMPSM)
-#else
-          if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
-              (config.type != AIC_7883))
-#endif
+            p->flags |= EXTENDED_TRANSLATION;
+
+          if (aic7xxx_verbose)
+            printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                   (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+          /*
+           * Put our termination setting into sxfrctl1 now that the
+           * generic initialization is complete.
+           */
+          sxfrctl1 |= inb(p->base + SXFRCTL1);
+          outb(sxfrctl1, p->base + SXFRCTL1);
+
+          if (aic7xxx_register(template, p) == 0)
+          {
+            aic7xxx_free(p);
+          }
+          else
           {
+            found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
             /*
-             * External SRAM present.  The probe will walk the SCBs to see
-             * how much SRAM we have and set the number of SCBs accordingly.
-             * We have to turn off SCBRAMSEL to access the external SCB
-             * SRAM.
+             * Set the shared SCB data once we've successfully probed a
+             * 398x adapter.
              *
-             * It seems that early versions of the aic7870 didn't use these
-             * bits, hence the hack for the 3940 above.  I would guess that
-             * recent 3940s using later aic7870 or aic7880 chips do actually
-             * set RAMPSM.
-             *
-             * The documentation isn't clear, but it sounds like the value
-             * written to devconfig must not have RAMPSM set.  The second
-             * sixteen bits of the register are R/O anyway, so it shouldn't
-             * affect RAMPSM either way.
+             * Note that we can only do this if the use of external
+             * SCB RAM is enabled.
              */
-            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
-                   "access.\n");
-            devconfig &= ~(RAMPSM | SCBRAMSEL);
-            pcibios_write_config_dword(pci_bus, pci_device_fn,
-                                       DEVCONFIG, devconfig);
+            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+            {
+              if (shared_scb_data == NULL)
+              {
+                shared_scb_data = p->scb_data;
+              }
+            }
+#endif
           }
-          found += aic7xxx_register(template, &config);
 
+          index++;
           /*
            * Disable spurious interrupts.
            */
-          aic7xxx_spurious_count = 0;
-
-          index++;
+          aic7xxx_spurious_count = 0;
         }  /* Found an Adaptec PCI device. */
       }
     }
   }
 #endif CONFIG_PCI
 
-  template->name = aic7xxx_info(NULL);
   return (found);
 }
 
@@ -4957,45 +6238,45 @@ static void
 aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     struct aic7xxx_scb *scb)
 {
-  unsigned int addr; /* must be 32 bits */
   unsigned short mask;
+  struct aic7xxx_hwscb *hscb;
 
   mask = (0x01 << TARGET_INDEX(cmd));
+  hscb = scb->hscb;
+
   /*
    * Setup the control byte if we need negotiation and have not
    * already requested it.
    */
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  if (cmd->device->tagged_queue)
+  if (p->discenable & mask)
   {
-    cmd->tag = scb->tag;
-    cmd->device->current_tag = scb->tag;
-    scb->control |= TAG_ENB;
-    p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-    if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
-    {
-      scb->control |= 0x02;
-      p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
-    }
-#if 0
-    if (p->orderedtag & mask)
+    hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+    if (cmd->device->tagged_queue)
     {
-      scb->control |= 0x02;
-      p->orderedtag = p->orderedtag & ~mask;
+      cmd->tag = hscb->tag;
+      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      {
+        hscb->control |= MSG_SIMPLE_Q_TAG;
+      }
+      else
+      {
+        hscb->control |= MSG_ORDERED_Q_TAG;
+        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+      }
     }
-#endif
-  }
-#endif
-  if (p->discenable & mask)
-  {
-    scb->control |= DISCENB;
+#endif  /* Tagged queueing */
   }
+
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
     p->wdtr_pending |= mask;
-    scb->control |= NEEDWDTR;
+    hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_MSGOUT_WDTR;
 #if 0
-    printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+    printk("scsi%d: Sending WDTR request to target %d.\n",
+           p->host_no, cmd->target);
 #endif
   }
   else
@@ -5003,19 +6284,20 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
       p->sdtr_pending |= mask;
-      scb->control |= NEEDSDTR;
+      hscb->control |= MK_MESSAGE;
+      scb->flags |= SCB_MSGOUT_SDTR;
 #if 0
-      printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+      printk("scsi%d: Sending SDTR request to target %d.\n",
+             p->host_no, cmd->target);
 #endif
     }
   }
-
 #if 0
   printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
          "mask(0x%x).\n",
         cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
 #endif
-  scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
        ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
   /*
@@ -5029,9 +6311,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
    * XXX - this relies on the host data being stored in a
    *       little-endian format.
    */
-  addr = VIRT_TO_BUS(cmd->cmnd);
-  scb->SCSI_cmd_length = cmd->cmd_len;
-  memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+  hscb->SCSI_cmd_length = cmd->cmd_len;
+  hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
 
   if (cmd->use_sg)
   {
@@ -5051,15 +6332,16 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
       scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
       scb->sg_list[i].length = (unsigned int) sg[i].length;
     }
-    scb->SG_segment_count = cmd->use_sg;
-    addr = VIRT_TO_BUS(scb->sg_list);
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-    memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-           sizeof(scb->data_pointer));
-    scb->data_count = scb->sg_list[0].length;
+    hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+    hscb->SG_segment_count = cmd->use_sg;
+    scb->sg_count = hscb->SG_segment_count;
+
+    /* Copy the first SG into the data pointer area. */
+    hscb->data_pointer = scb->sg_list[0].address;
+    hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
 #if 0
     printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
-           cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+           cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
 #endif
   }
   else
@@ -5068,28 +6350,23 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
   printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
        (unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
-    if (cmd->request_bufflen == 0)
+    if (cmd->request_bufflen)
     {
-      /*
-       * In case the higher level SCSI code ever tries to send a zero
-       * length command, ensure the SCB indicates no data.  The driver
-       * will interpret a zero length command as a Bus Device Reset.
-       */
-      scb->SG_segment_count = 0;
-      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-      scb->data_count = 0;
+      hscb->SG_segment_count = 1;
+      scb->sg_count = 1;
+      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+      scb->sg_list[0].length = cmd->request_bufflen;
+      hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+      hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+      hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
     }
     else
     {
-      scb->SG_segment_count = 1;
-      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
-      scb->sg_list[0].length = cmd->request_bufflen;
-      addr = VIRT_TO_BUS(&scb->sg_list[0]);
-      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-      scb->data_count = scb->sg_list[0].length;
-      addr = VIRT_TO_BUS(cmd->request_buffer);
-      memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+      hscb->SG_segment_count = 0;
+      scb->sg_count = 0;
+      hscb->SG_list_pointer = 0;
+      hscb->data_pointer = 0;
+      hscb->data_count = SCB_LIST_NULL << 24;
     }
   }
 }
@@ -5107,7 +6384,6 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
   long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  u_char curscb, intstat;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   if (p->host != cmd->host)
@@ -5139,34 +6415,21 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
        cmd->lun & 0x07);
 #endif
 
-  /*
-   * This is a critical section, since we don't want the interrupt
-   * routine mucking with the host data or the card.  For this reason
-   * it is nice to know that this function can only be called in one
-   * of two ways from scsi.c  First, as part of a routine queue command,
-   * in which case, the irq for our card is disabled before this
-   * function is called.  This doesn't help us if there is more than
-   * one card using more than one IRQ in our system, therefore, we
-   * should disable all interrupts on these grounds alone.  Second,
-   * this can be called as part of the scsi_done routine, in which case
-   * we are in the aic7xxx_isr routine already and interrupts are
-   * disabled, therefore we should saveflags first, then disable the
-   * interrupts, do our work, then restore the CPU flags. If it weren't
-   * for the possibility of more than one card using more than one IRQ
-   * in our system, we wouldn't have to touch the interrupt flags at all.
-   */
-  save_flags(processor_flags);
-  cli();
-
+  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+      > cmd->device->queue_depth)
+  {
+    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+           p->host_no, cmd->target, cmd->channel);
+  }
   scb = aic7xxx_allocate_scb(p);
   if (scb == NULL)
   {
-    panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
   }
   else
   {
     scb->cmd = cmd;
-    aic7xxx_position(cmd) = scb->tag;
+    aic7xxx_position(cmd) = scb->hscb->tag;
 #if 0
     debug_scb(scb);
 #endif;
@@ -5178,14 +6441,14 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
     aic7xxx_buildscb(p, cmd, scb);
 
 #if 0
-    if (scb != (p->scb_array[scb->position]))
+    if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
     {
       printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
              "address.\n");
     }
     printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
-          scb->position, (unsigned int) scb->cmd,
-          scb->state, (unsigned int) p->free_scb);
+          scb->hscb->tag, (unsigned int) scb->cmd,
+          scb->flags, (unsigned int) p->free_scb);
 #endif
 
     /*
@@ -5200,70 +6463,28 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
     cmd->host_scribble = NULL;
     memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
 
-    if (scb->position != SCB_LIST_NULL)
-    {
-      /* We've got a valid slot, yeah! */
-      if (p->flags & IN_ISR)
-      {
-        scbq_insert_tail(&p->assigned_scbs, scb);
-        scb->state |= SCB_ASSIGNEDQ;
-      }
-      else
-      {
-        /*
-         * 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);
-        intstat = inb(INTSTAT + p->base);
-
-        /*
-         * 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.
-         */
-        curscb = inb(SCBPTR + p->base);
-        outb(scb->position, SCBPTR + p->base);
-        aic7xxx_putscb(p, scb);
-        outb(curscb, SCBPTR + p->base);
-        outb(scb->position, QINFIFO + p->base);
-        scb->state |= SCB_ACTIVE;
+    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
-        /*
-         * Guard against unpausing the sequencer if there is an interrupt
-         * waiting to happen.
-         */
-        if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-        {
-          UNPAUSE_SEQUENCER(p);
-        }
-      }
-    }
-    else
+    save_flags(processor_flags);
+    cli();
+    scbq_insert_tail(&p->waiting_scbs, scb);
+    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
     {
-      scb->state |= SCB_WAITINGQ;
-      scbq_insert_tail(&p->waiting_scbs, scb);
-      if (!(p->flags & IN_ISR))
-      {
-        aic7xxx_run_waiting_queues(p);
-      }
+      aic7xxx_run_waiting_queues(p);
     }
 
+    restore_flags(processor_flags);
 #if 0
     printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
-           (long) cmd, (long) scb->cmd, scb->position);
+           (long) cmd, (long) scb->cmd, scb->hscb->tag);
 #endif;
-    restore_flags(processor_flags);
   }
   return (0);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_abort_reset
+ *   aic7xxx_bus_device_reset
  *
  * Description:
  *   Abort or reset the current SCSI command(s).  If the scb has not
@@ -5275,204 +6496,257 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
 static int
 aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  struct aic7xxx_scb  *scb;
+  struct aic7xxx_scb   *scb;
+  struct aic7xxx_hwscb *hscb;
   unsigned char bus_state;
-  int base, result = -1;
+  int result = -1;
   char channel;
 
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  hscb = scb->hscb;
+
+  /*
+   * Ensure that the card doesn't do anything behind our back.
+   * Also make sure that we didn't just miss an interrupt that
+   * could affect this abort/reset.
+   */
+  pause_sequencer(p);
+  while (inb(p->base + INTSTAT) & INT_PEND);
+  {
+    aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+    pause_sequencer(p);
+  } 
+  if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+  {
+    result = SCSI_RESET_NOT_RUNNING;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    return(result);
+  }
 
-  channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
-  if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+
+  printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+         p->host_no, TC_OF_SCB(scb), scb->flags);
+  bus_state = inb(p->base + LASTPHASE);
+
+  switch (bus_state)
   {
+    case P_DATAOUT:
+      printk("Data-Out phase, ");
+      break;
+    case P_DATAIN:
+      printk("Data-In phase, ");
+      break;
+    case P_COMMAND:
+      printk("Command phase, ");
+      break;
+    case P_MESGOUT:
+      printk("Message-Out phase, ");
+      break;
+    case P_STATUS:
+      printk("Status phase, ");
+      break;
+    case P_MESGIN:
+      printk("Message-In phase, ");
+      break;
+    default:
+      /*
+       * We're not in a valid phase, so assume we're idle.
+       */
+      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+      break;
+  }
+  printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+         inb(p->base + SCSISIGI),
+         inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+         inb(p->base + SSTAT0), inb(p->base + SSTAT1));
 
-    if (scb->state & SCB_IN_PROGRESS)
+  channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+  /*
+   * Determine our course of action.
+   */
+  if (scb->flags & SCB_ABORT)
+  {
+    /*
+     * Been down this road before; do a full bus reset.
+     */
+    scb->flags |= SCB_RECOVERY_SCB;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    result = -1;
+  }
+#if 0
+  else if (hscb->control & TAG_ENB)
     {
       /*
-       * Ensure that the card doesn't do anything
-       * behind our back.
+       * We could be starving this command; try sending and ordered tag
+       * command to the target we come from.
        */
-      PAUSE_SEQUENCER(p);
+      scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+      p->orderedtag = p->orderedtag | 0xFF;
+      result = SCSI_RESET_PENDING;
+      unpause_sequencer(p, /* unpause_always */ TRUE);
+      printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+             p->host_no);
+    }
+#endif
+  else
+  {
+    unsigned char active_scb_index, saved_scbptr;
+    struct aic7xxx_scb *active_scb;
 
-      printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
-      bus_state = inb(LASTPHASE + p->base);
+    /*
+     * Send an Abort Message:
+     * The target that is holding up the bus may not be the same as
+     * the one that triggered this timeout (different commands have
+     * different timeout lengths).  Our strategy here is to queue an
+     * abort message to the timed out target if it is disconnected.
+     * Otherwise, if we have an active target we stuff the message buffer
+     * with an abort message and assert ATN in the hopes that the target
+     * will let go of the bus and go to the mesgout phase.  If this
+     * fails, we'll get another timeout a few seconds later which will
+     * attempt a bus reset.
+     */
+    saved_scbptr = inb(p->base + SCBPTR);
+    active_scb_index = inb(p->base + SCB_TAG);
+    active_scb = p->scb_data->scb_array[active_scb_index];
 
-      switch (bus_state)
+    if (bus_state != P_BUSFREE)
+    {
+      if (active_scb_index >= p->scb_data->numscbs)
       {
-       case P_DATAOUT:
-          printk("Data-Out phase, ");
-          break;
-       case P_DATAIN:
-          printk("Data-In phase, ");
-          break;
-       case P_COMMAND:
-          printk("Command phase, ");
-          break;
-       case P_MESGOUT:
-          printk("Message-Out phase, ");
-          break;
-       case P_STATUS:
-          printk("Status phase, ");
-          break;
-       case P_MESGIN:
-          printk("Message-In phase, ");
-          break;
-       default:
-          printk("while idle, LASTPHASE = 0x%x, ", bus_state);
-          /*
-           * We're not in a valid phase, so assume we're idle.
-           */
-          bus_state = 0;
-          break;
+        /*
+         * Perform a bus reset.
+         *
+         * XXX - We want to queue an abort for the timedout SCB
+         *       instead.
+         */
+        result = -1;
+        printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+               "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
       }
-      printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
-      /*
-       * First, determine if we want to do a bus reset or simply a bus device
-       * reset.  If this is the first time that a transaction has timed out
-       * and the SCB is not paged out, just schedule a bus device reset.
-       * Otherwise, we reset the bus and abort all pending I/Os on that bus.
-       */
-      if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT)))
+      else
       {
-#if 0
-       if (scb->control & TAG_ENB)
-       {
+        /* Send the abort message to the active SCB. */
+        outb(1, p->base + MSG_LEN);
+        if (active_scb->hscb->control & TAG_ENB)
+        {
+          outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+        }
+        else
+        {
+          outb(MSG_ABORT, p->base + MSG_OUT);
+        }
+        outb(bus_state | ATNO, p->base + SCSISIGO);
+        printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+               p->host_no);
+        active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+        if (active_scb != scb)
+        {
           /*
-           * We could be starving this command; try sending and ordered tag
-           * command to the target we come from.
+           * XXX - We would like to increment the timeout on scb, but
+           *       access to that routine is denied because it is hidden
+           *       in scsi.c.  If we were able to do this, it would give
+           *       scb a new lease on life.
            */
-          scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
-          p->orderedtag = p->orderedtag | 0xFF;
           result = SCSI_RESET_PENDING;
-          UNPAUSE_SEQUENCER(p);
-          printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
-       }
-#endif
-       unsigned char active_scb, control;
-       struct aic7xxx_scb *active_scbp;
+          aic7xxx_error(active_scb->cmd) = DID_RESET;
+        }
+        else
+        {
+          aic7xxx_error(scb->cmd) = DID_RESET;
+          result = SCSI_RESET_PENDING;
+        }
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+      }
+    }
+    else
+    {
+      unsigned char hscb_index, linked_next;
+      int disconnected;
 
-       /*
-        * Send a Bus Device Reset Message:
-        * The target we select to send the message to may be entirely
-        * different than the target pointed to by the scb that timed
-        * out.  If the command is in the QINFIFO or the waiting for
-        * selection list, its not tying up the bus and isn't responsible
-        * for the delay so we pick off the active command which should
-        * be the SCB selected by SCBPTR.  If its disconnected or active,
-        * we device reset the target scbp points to.  Although it may
-        * be that this target is not responsible for the delay, it may
-        * may also be that we're timing out on a command that just takes
-        * too much time, so we try the bus device reset there first.
-        */
-       active_scb = inb(SCBPTR + base);
-       active_scbp = (p->scb_array[inb(SCB_TAG + base)]);
-       control = inb(SCB_CONTROL + base);
+      disconnected = FALSE;
+      hscb_index = aic7xxx_find_scb(p, scb);
+      if (hscb_index == SCB_LIST_NULL)
+      {
+        disconnected = TRUE;
+        linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+      }
+      else
+      {
+        outb(hscb_index, p->base + SCBPTR);
+        if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+        {
+          disconnected = TRUE;
+        }
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+      }
+      if (disconnected)
+      {
+        /*
+         * Simply set the ABORT_SCB control bit and preserve the
+         * linked next pointer.
+         */
+        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+        scb->hscb->data_count &= ~0xFF000000;
+        scb->hscb->data_count |= linked_next << 24;
+        if ((p->flags & PAGE_ENABLED) == 0)
+        {
+          scb->hscb->control &= ~DISCONNECTED;
+        }
+        scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+        if (hscb_index != SCB_LIST_NULL)
+        {
+          unsigned char scb_control;
 
-       /*
-        * Test to see if scbp is disconnected
-        */
-       outb(scb->position, SCBPTR + base);
-       if (inb(SCB_CONTROL + base) & DISCONNECTED)
-       {
-#ifdef AIC7XXX_DEBUG_ABORT
-          printk("aic7xxx: (abort_scb) scb %d is disconnected; "
-                 "bus device reset message queued.\n", scb->position);
-#endif
-          if (p->flags & PAGE_ENABLED)
-          {
-            /* Pull this SCB out of the disconnected list. */
-            u_char prev = inb(SCB_PREV + base);
-            u_char next = inb(SCB_NEXT + base);
-            if (prev == SCB_LIST_NULL)
-            {
-              /* Head of list */
-              outb(next, DISCONNECTED_SCBH + base);
-            }
-            else
-            {
-              outb(prev, SCBPTR + base);
-              outb(next, SCB_NEXT + base);
-              if (next != SCB_LIST_NULL)
-              {
-               outb(next, SCBPTR + base);
-               outb(prev, SCB_PREV + base);
-              }
-              outb(scb->position, SCBPTR + base);
-            }
-          }
-         scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-          scb->control = scb->control & DISCENB;
-          scb->SCSI_cmd_length = 0;
-         scb->SG_segment_count = 0;
-         memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-         memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-         scb->data_count = 0;
-         aic7xxx_putscb(p, scb);
-         aic7xxx_add_waiting_scb(base, scb);
-         outb(active_scb, SCBPTR + base);
-          result = SCSI_RESET_PENDING;
-         UNPAUSE_SEQUENCER(p);
-       }
-       else
-       {
-         /*
-          * Is the active SCB really active?
-          */
-         if ((active_scbp->state & SCB_ACTIVE) && bus_state)
-         {
-            /*
-             * Load the message buffer and assert attention.
-             */
-            active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-            outb(1, MSG_LEN + base);
-            outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-            outb(bus_state | ATNO, SCSISIGO + base);
-#ifdef AIC7XXX_DEBUG_ABORT
-            printk("aic7xxx: (abort_scb) asserted ATN - "
-                   "bus device reset in message buffer.\n");
-#endif
-            if (active_scbp != scb)
-            {
-              /*
-               * XXX - We would like to increment the timeout on scb, but
-               *       access to that routine is denied because it is hidden
-               *       in scsi.c.  If we were able to do this, it would give
-               *       scb a new lease on life.
-               */
-              ;
-            }
-            aic7xxx_error(scb->cmd) = DID_RESET;
-            /*
-             * Restore the active SCB and unpause the sequencer.
-             */
-            outb(active_scb, SCBPTR + base);
-            if (active_scbp != scb)
-            {
-              /*
-               * The mid-level SCSI code requested us to reset a command
-               * different from the one that we actually reset.  Return
-               * a "not running" indication and hope that the SCSI code
-               * will Do the Right Thing (tm).
-               */
-              result = SCSI_RESET_NOT_RUNNING;
-            }
-            else
-            {
-              result = SCSI_RESET_PENDING;
-            }
-            UNPAUSE_SEQUENCER(p);
-         }
-       }
+          scb_control = inb(p->base + SCB_CONTROL);
+          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+        }
+        /*
+         * Actually requeue this SCB in case we can select the
+         * device before it reconnects.  If the transaction we
+         * want to abort is not tagged, unbusy it first so that
+         * we don't get held back from sending the command.
+         */
+        if ((scb->hscb->control & TAG_ENB) == 0)
+        {
+          unsigned char target;
+          int lun;
+
+          target = scb->cmd->target;
+          lun = scb->cmd->lun;
+          aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+              0, /* requeue */ TRUE);
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+               p->host_no, TC_OF_SCB(scb));
+        scbq_insert_head(&p->waiting_scbs, scb);
+        scb->flags |= SCB_WAITINGQ;
+        outb(saved_scbptr, p->base + SCBPTR);
+        if ((p->flags & IN_ISR) == 0)
+        {
+          /*
+           * Processing the waiting queue may unpause us.
+           */
+          aic7xxx_run_waiting_queues(p);
+          /*
+           * If we are using AAP, aic7xxx_run_waiting_queues() will not
+           * unpause us, so ensure we are unpaused.
+           */
+          unpause_sequencer(p, /*unpause_always*/ FALSE);
+        }
+        else
+        {
+          unpause_sequencer(p, /*unpause_always*/ TRUE);
+        }
+        result = SCSI_RESET_PENDING;
+      }
+      else
+      {
+        scb->flags |= SCB_RECOVERY_SCB;
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+        result = -1;
       }
     }
   }
-  /* Make sure the sequencer is unpaused upon return. */
-  if (result == -1)
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
   return (result);
 }
 
@@ -5490,16 +6764,48 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
   int    base, result;
+  unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
 
+  save_flags(processor_flags);
+  cli();
+
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
-         scb->position, TCL_OF_SCB(scb));
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+  }
 #endif
 
+  if (p->flags & IN_TIMEOUT)
+  {
+    /*
+     * We've already started a recovery operation.
+     */
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    {
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_PENDING);
+    }
+    else
+    {
+      /*
+       * This is the second time we've tried to abort the recovery
+       * SCB.  We want the mid-level SCSI code to call the reset
+       * function to reset the SCSI bus.
+       */
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_NOT_RUNNING);
+    }
+  }
   if (cmd->serial_number != cmd->serial_number_at_timeout)
   {
     result = SCSI_ABORT_NOT_RUNNING;
@@ -5508,14 +6814,34 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
-  else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
   else
   {
-    result = SCSI_ABORT_SNOOZE;
+    /*
+     * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+     *       Right Thing with it.
+     */
+    p->flags |= IN_TIMEOUT;
+    result = aic7xxx_bus_device_reset(p, scb->cmd);
+    switch (result)
+    {
+      case SCSI_RESET_NOT_RUNNING:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_NOT_RUNNING;
+        break;
+      case SCSI_RESET_PENDING:
+        result = SCSI_ABORT_PENDING;
+        break;
+      default:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_SNOOZE;
+        break;
+     }
   }
+  restore_flags(processor_flags);
   return (result);
 }
 
@@ -5535,18 +6861,27 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, found, tindex, min_target, max_target, result = -1;
+  int    base, found, tindex, min_target, max_target;
+  int    result = -1;
   char   channel = 'A';
   unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
   channel = cmd->channel ? 'B': 'A';
   tindex = (cmd->channel << 4) | cmd->target;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Reset called with no SCB for cmd.\n");
+  }
 #endif
 
   /* 
@@ -5561,34 +6896,45 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
   if (scb->cmd != cmd)
     scb = NULL;
 
-  if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) 
-      && (scb != NULL))
+  if (p->flags & IN_TIMEOUT)
   {
     /*
-     * Attempt a bus device reset if commands have completed successfully
-     * since the last bus device reset, or it has been less than 100ms
-     * since the last reset.
+     * We've already started a recovery operation.
      */
-    if ((p->flags & DEVICE_SUCCESS) ||
-        ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
     {
-      if (cmd->serial_number != cmd->serial_number_at_timeout)
-      {
-        result = SCSI_RESET_NOT_RUNNING;
-      }
-      else
+      restore_flags(processor_flags);
+      return (SCSI_RESET_PENDING);
+    }
+  }
+  else
+  {
+    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+        && (scb != NULL))
+    {
+      /*
+       * Attempt a bus device reset if commands have completed successfully
+       * since the last bus device reset, or it has been less than 100ms
+       * since the last reset.
+       */
+      if ((p->flags & DEVICE_SUCCESS) ||
+          ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
       {
-        if (scb == NULL)
+       if (cmd->serial_number != cmd->serial_number_at_timeout)
+       {
+          result = SCSI_RESET_NOT_RUNNING;
+       }
+       else if (scb == NULL)
         {
           result = SCSI_RESET_NOT_RUNNING;
         }
         else if (flags & SCSI_RESET_ASYNCHRONOUS)
         {
-          if (scb->state & SCB_ABORTED)
+          if (scb->flags & SCB_ABORTED)
           {
             result = SCSI_RESET_PENDING;
           }
-          else if (!(scb->state & SCB_IN_PROGRESS))
+          else if (!(scb->flags & SCB_ACTIVE))
           {
             result = SCSI_RESET_NOT_RUNNING;
           }
@@ -5599,20 +6945,23 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
           if ((flags & SCSI_RESET_SYNCHRONOUS) &&
               (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
           {
-            scb->state |= SCB_ABORTED;
+            scb->flags |= SCB_ABORTED;
             result = SCSI_RESET_PENDING;
           }
           else
           {
+            p->flags |= IN_TIMEOUT;
             result = aic7xxx_bus_device_reset(p, cmd);
             if (result == 0)
+            {
+              p->flags &= ~IN_TIMEOUT;
               result = SCSI_RESET_PENDING;
+            }
           }
-        }
+       }
       }
     }
   }
-
   if (result == -1)
   {
     /*
@@ -5625,11 +6974,11 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
       {
        result = SCSI_RESET_NOT_RUNNING;
       }
-      else if (!(scb->state & SCB_IN_PROGRESS))
+      else if (!(scb->flags & SCB_ACTIVE))
       {
        result = SCSI_RESET_NOT_RUNNING;
       }
-      else if ((scb->state & SCB_ABORTED) &&
+      else if ((scb->flags & SCB_ABORTED) &&
                (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
       {
        result = SCSI_RESET_PENDING;
@@ -5641,8 +6990,9 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
       /*
        * The reset channel function assumes that the sequencer is paused.
        */
-      PAUSE_SEQUENCER(p);
+      pause_sequencer(p);
       found = aic7xxx_reset_channel(p, channel, TRUE);
+      p->flags = p->flags & ~IN_TIMEOUT;
 
       /*
        * If this is a synchronous reset and there is no SCB for this
@@ -5688,8 +7038,10 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
       }
 
       result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+      p->flags &= ~IN_TIMEOUT;
     }
   }
+  aic7xxx_run_waiting_queues(p);
   restore_flags(processor_flags);
   return (result);
 }
index d4de8fd83487769931ace77b9a354325c28b3ec9..5ba54acfa79983459ee9577128b56ad1889b741a 100644 (file)
        aic7xxx_info,                                           \
        NULL,                                                   \
        aic7xxx_queue,                                          \
-       aic7xxx_abort,                                          \
+       NULL,                                                   \
        aic7xxx_reset,                                          \
        NULL,                                                   \
        aic7xxx_biosparam,                                      \
        -1,                     /* max simultaneous cmds      */\
        -1,                     /* scsi id of host adapter    */\
-       SG_ALL,                 /* max scatter-gather cmds    */\
+       0,                      /* max scatter-gather cmds    */\
        2,                      /* cmds per lun (linked cmds) */\
        0,                      /* number of 7xxx's present   */\
        0,                      /* no memory DMA restrictions */\
@@ -57,7 +57,6 @@ extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
 extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
 extern int aic7xxx_detect(Scsi_Host_Template *);
 extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_abort(Scsi_Cmnd *);
 extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
diff --git a/drivers/scsi/aic7xxx.seq b/drivers/scsi/aic7xxx.seq
deleted file mode 100644 (file)
index 98d4b95..0000000
+++ /dev/null
@@ -1,1127 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux and FreeBSD.
- *
- * Copyright (c) 1994 John Aycock
- *   The University of Calgary Department of Computer Science.
- *
- *Modifications/enhancements:
- *  Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other 
- * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
- *
- * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq.
- *
- *-M*************************************************************************/
-
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $"
-
-#ifdef linux
-#include "aic7xxx_reg.h"
-#else
-#if defined(__NetBSD__)
-#include "../../../../dev/ic/aic7xxxreg.h"
-#elif defined(__FreeBSD__)
-#include "../../dev/aic7xxx/aic7xxx_reg.h"
-#endif
-#endif
-
-/*
- * We can't just use ACCUM in the sequencer code because it
- * must be treated specially by the assembler, and it currently
- * looks for the symbol 'A'.  This is the only register defined in
- * the assembler's symbol space.
- */
-A = ACCUM
-
-/* After starting the selection hardware, we check for reconnecting targets
- * as well as for our selection to complete just in case the reselection wins
- * bus arbitration.  The problem with this is that we must keep track of the
- * SCB that we've already pulled from the QINFIFO and started the selection
- * on just in case the reselection wins so that we can retry the selection at
- * a later time.  This problem cannot be resolved by holding a single entry
- * in scratch ram since a reconnecting target can request sense and this will
- * create yet another SCB waiting for selection.  The solution used here is to 
- * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
- * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
- * SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
- * add an entry to this list every time a request sense occurs.  The sequencer
- * will automatically consume the entries.
- */
-
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
-reset:
-       clr     DFCNTRL
-       clr     SCSISIGO                /* De-assert BSY */
-/*
- * We jump to start after every bus free.
- */
-start:
-       and     FLAGS,0x0f              /* clear target specific flags */
-       mvi     SCSISEQ,ENRSELI         /* Always allow reselection */
-       clr     SCSIRATE                /*
-                                        * We don't know the target we will
-                                        * connect to, so default to narrow
-                                        * transfers to avoid parity problems.
-                                        */
-poll_for_work:
-       /*
-        * Are we a twin channel device?
-        * For fairness, we check the other bus first,
-        * since we just finished a transaction on the
-        * current channel.
-        */
-       test    FLAGS,TWIN_BUS  jz start2
-       xor     SBLKCTL,SELBUSB                 /* Toggle to the other bus */
-       test    SSTAT0,SELDI    jnz reselect
-       xor     SBLKCTL,SELBUSB                 /* Toggle to the original bus */
-start2:
-       test    SSTAT0,SELDI    jnz reselect
-       cmp     WAITING_SCBH,SCB_LIST_NULL jne start_waiting
-       mov     A, QCNTMASK
-       test    QINCNT,A        jz poll_for_work
-
-/*
- * We have at least one queued SCB now and we don't have any 
- * SCBs in the list of SCBs awaiting selection.  Set the SCB
- * pointer from the FIFO so we see the right bank of SCB 
- * registers.
- */
-       mov     SCBPTR,QINFIFO
-
-/*
- * See if there is not already an active SCB for this target.  This code
- * locks out on a per target basis instead of target/lun.  Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands.  It may be beneficial to make findscb a more general procedure
- * to see if the added cost of the search is negligible.  This code also 
- * assumes that the kernel driver will clear the active flags on board 
- * initialization, board reset, and a target SELTO.  Tagged commands
- * don't set the active bits since you can queue more than one command
- * at a time.  We do, however, look to see if there are any non-tagged
- * I/Os in progress, and requeue the command if there are.  Tagged and
- * non-tagged commands cannot be mixed to a single target.
- */
-
-test_busy:
-       mov     FUNCTION1,SCB_TCL
-       mov     A,FUNCTION1
-       test    SCB_TCL,0x88    jz test_a       /* Id < 8 && A channel */
-
-       test    ACTIVE_B,A      jnz requeue
-       test    SCB_CONTROL,TAG_ENB     jnz start_scb
-       /* Mark the current target as busy */
-       or      ACTIVE_B,A
-       jmp     start_scb
-
-/* Place the currently active SCB back on the queue for later processing */
-requeue:
-       mov     QINFIFO, SCBPTR
-       jmp     poll_for_work
-
-/*
- * Pull the first entry off of the waiting for selection list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the waiting_scb list.
- */
-start_waiting:
-       mov     SCBPTR,WAITING_SCBH
-       jmp     start_scb2
-
-test_a:
-       test    ACTIVE_A,A jnz requeue
-       test    SCB_CONTROL,TAG_ENB jnz start_scb
-       /* Mark the current target as busy */
-       or      ACTIVE_A,A
-
-start_scb:
-       mov     SCB_NEXT,WAITING_SCBH
-       mov     WAITING_SCBH, SCBPTR
-start_scb2:
-       and     SINDEX,0xf7,SBLKCTL     /* Clear the channel select bit */
-       and     A,0x08,SCB_TCL          /* Get new channel bit */
-       or      SINDEX,A
-       mov     SBLKCTL,SINDEX          /* select channel */
-       mov     SCB_TCL call initialize_scsiid
-
-/*
- * Enable selection phase as an initiator, and do automatic ATN
- * after the selection.  We do this now so that we can overlap the
- * rest of our work to set up this target with the arbitration and
- * selection bus phases.
- */
-start_selection:
-       mvi     SCSISEQ,0x58            /* ENSELO|ENAUTOATNO|ENRSELI */
-
-/*
- * 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.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
-       test    SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */
-
-/*
- * The kernel has sent us an SCB with no command attached.  This implies
- * that the kernel wants to send a message of some sort to this target,
- * so we interrupt the driver, allow it to fill the message buffer, and
- * then go back into the arbitration loop
- */
-       mvi     INTSTAT,AWAITING_MSG
-       jmp     wait_for_selection
-
-mk_identify:
-       and     A,DISCENB,SCB_CONTROL   /* mask off disconnect privledge */
-
-       and     MSG0,0x7,SCB_TCL        /* lun */
-       or      MSG0,A                  /* or in disconnect privledge */
-       or      MSG0,MSG_IDENTIFY
-       mvi     MSG_LEN, 1
-
-       test    SCB_CONTROL,0xb0 jz  !message   /* WDTR, SDTR or TAG?? */
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-
-mk_tag:
-       mvi     DINDEX, MSG1
-       test    SCB_CONTROL,TAG_ENB jz mk_tag_done
-       and     DINDIR,0x23,SCB_CONTROL
-       mov     DINDIR,SCB_TAG
-
-       add     MSG_LEN,COMP_MSG0,DINDEX        /* update message length */
-
-mk_tag_done:
-
-       test    SCB_CONTROL,0x90 jz !message    /* NEEDWDTR|NEEDSDTR */
-       mov     DINDEX  call mk_dtr     /* build DTR message if needed */
-
-!message:
-wait_for_selection:
-       test    SSTAT0,SELDO    jnz select 
-       test    SSTAT0,SELDI    jz wait_for_selection
-
-/*
- * Reselection has been initiated by a target. Make a note that we've been
- * reselected, but haven't seen an IDENTIFY message from the target
- * yet.
- */
-reselect:
-       clr     MSG_LEN         /* Don't have anything in the mesg buffer */
-       mov     SELID           call initialize_scsiid
-       or      FLAGS,RESELECTED
-       jmp     select2
-
-/*
- * After the selection, remove this SCB from the "waiting for selection"
- * list.  This is achieved by simply moving our "next" pointer into
- * WAITING_SCBH.  Our next pointer will be set to null the next time this
- * SCB is used, so don't bother with it now.
- */
-select:
-       mov     WAITING_SCBH,SCB_NEXT
-       or      FLAGS,SELECTED
-select2:
-/*
- * 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.
- */
-       or      SXFRCTL0,CLRCHN
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- */
-       call    ndx_dtr
-       mov     SCSIRATE,SINDIR
-
-/*
- * Initialize Ultra mode setting.
- */
-       mov     FUNCTION1,SCSIID
-       mov     A,FUNCTION1
-       and     SINDEX,0xdf,SXFRCTL0            /* default to Ultra disabled */
-       test    SCSIID, 0x80     jnz ultra_b    /* Target ID > 7 */
-       test    SBLKCTL, SELBUSB jnz ultra_b    /* Second channel device */
-       test    ULTRA_ENB,A      jz  set_sxfrctl0
-       or      SINDEX, ULTRAEN  jmp set_sxfrctl0
-ultra_b:
-       test    ULTRA_ENB_B,A    jz  set_sxfrctl0
-       or      SINDEX, ULTRAEN
-
-set_sxfrctl0:
-       mov     SXFRCTL0,SINDEX
-
-       mvi     SCSISEQ,ENAUTOATNP              /*
-                                                * ATN on parity errors
-                                                * for "in" phases
-                                                */
-       mvi     CLRSINT1,CLRBUSFREE
-       mvi     CLRSINT0,0x60                   /* CLRSELDI|CLRSELDO */
-/*
- * 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.
- *
- */
-ITloop:
-       test    SSTAT1,BUSFREE  jnz p_busfree
-       test    SSTAT1,REQINIT  jz ITloop
-
-       and     A,PHASE_MASK,SCSISIGI
-       mov     LASTPHASE,A
-       mov     SCSISIGO,A
-
-       cmp     ALLZEROS,A      je p_dataout
-       cmp     A,P_DATAIN      je p_datain
-       cmp     A,P_COMMAND     je p_command
-       cmp     A,P_MESGOUT     je p_mesgout
-       cmp     A,P_STATUS      je p_status
-       cmp     A,P_MESGIN      je p_mesgin
-
-       mvi     INTSTAT,BAD_PHASE       /* unknown phase - signal driver */
-       jmp     ITloop                  /* Try reading the bus again. */
-
-p_dataout:
-       mvi     DMAPARAMS,0x7d                  /*
-                                                * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-                                                * DIRECTION|FIFORESET
-                                                */
-       jmp     data_phase_init
-
-/*
- * If we re-enter the data phase after going through another phase, the
- * STCNT may have been cleared, so restore it from the residual field.
- */
-data_phase_reinit:
-       mov     STCNT0,SCB_RESID_DCNT0
-       mov     STCNT1,SCB_RESID_DCNT1
-       mov     STCNT2,SCB_RESID_DCNT2
-       jmp     data_phase_loop
-
-p_datain:
-       mvi     DMAPARAMS,0x79          /*
-                                        * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-                                        * !DIRECTION|FIFORESET
-                                        */
-data_phase_init:
-       call    assert
-
-       test    FLAGS, DPHASE   jnz data_phase_reinit
-       call    sg_scb2ram
-       or      FLAGS, DPHASE           /* We have seen a data phase */
-
-data_phase_loop:
-/* Guard against overruns */
-       test    SG_COUNT, 0xff jnz data_phase_inbounds
-/*
- * Turn on 'Bit Bucket' mode, set the transfer count to
- * 16meg and let the target run until it changes phase.
- * When the transfer completes, notify the host that we
- * had an overrun.
- */
-       or      SXFRCTL1,BITBUCKET
-       mvi     STCNT0,0xff
-       mvi     STCNT1,0xff
-       mvi     STCNT2,0xff
-
-data_phase_inbounds:
-/* If we are the last SG block, don't set wideodd. */
-       cmp     SG_COUNT,0x01 jne data_phase_wideodd
-       and     DMAPARAMS, 0xbf         /* Turn off WIDEODD */
-data_phase_wideodd:
-       mov     DMAPARAMS  call dma
-
-/* Go tell the host about any overruns */
-       test    SXFRCTL1,BITBUCKET jnz data_phase_overrun
-
-/* Exit if we had an underrun */
-       test    SSTAT0,SDONE    jz data_phase_finish /* underrun STCNT != 0 */
-
-/*
- * Advance the scatter-gather pointers if needed 
- */
-sg_advance:
-       dec     SG_COUNT        /* one less segment to go */
-
-       test    SG_COUNT, 0xff  jz data_phase_finish /* Are we done? */
-
-       clr     A                       /* add sizeof(struct scatter) */
-       add     SG_NEXT0,SG_SIZEOF,SG_NEXT0
-       adc     SG_NEXT1,A,SG_NEXT1
-
-/*
- * Load a struct scatter 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 all DMA's, assumes a little-endian host data storage.
- */
-sg_load:
-       clr     HCNT2
-       clr     HCNT1
-       mvi     HCNT0,SG_SIZEOF
-
-       mov     HADDR0,SG_NEXT0
-       mov     HADDR1,SG_NEXT1
-       mov     HADDR2,SG_NEXT2
-       mov     HADDR3,SG_NEXT3
-
-       or      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.
- */
-dma_finish:
-       test    DFSTATUS,HDONE  jz dma_finish
-       /* Turn off DMA preserving WIDEODD */
-       and     DFCNTRL,WIDEODD
-dma_finish2:
-       test    DFCNTRL,HDMAENACK jnz dma_finish2
-
-/*
- * Copy data from FIFO into SCB data pointer and data count.  In
- * both FreeBSD and Linux, the scatter list entry is 8 bytes.
- * 
- * struct ahc_dma_seg {
- *       physaddr addr;                  four bytes, little-endian order
- *       long    len;                    four bytes, little endian order
- * };
- */
-
-       mov     HADDR0,DFDAT
-       mov     HADDR1,DFDAT
-       mov     HADDR2,DFDAT
-       mov     HADDR3,DFDAT
-       mov     HCNT0,DFDAT
-       mov     HCNT1,DFDAT
-       mov     HCNT2,DFDAT
-
-/* Load STCNT as well.  It is a mirror of HCNT */
-       mov     STCNT0,HCNT0
-       mov     STCNT1,HCNT1
-       mov     STCNT2,HCNT2
-        test    SSTAT1,PHASEMIS  jz data_phase_loop
-
-data_phase_finish:
-/*
- * After a DMA finishes, save the SG and STCNT residuals back into the SCB
- * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
- * were transferred on the SCSI (as opposed to the host) bus.
- */
-       mov     SCB_RESID_DCNT0,STCNT0
-       mov     SCB_RESID_DCNT1,STCNT1
-       mov     SCB_RESID_DCNT2,STCNT2
-       mov     SCB_RESID_SGCNT, SG_COUNT
-       jmp     ITloop
-
-data_phase_overrun:
-/*
- * Turn off BITBUCKET mode and notify the host
- */
-       and     SXFRCTL1,0x7f           /* ~BITBUCKET */
-       mvi     INTSTAT,DATA_OVERRUN
-       jmp     ITloop
-
-/*
- * Command phase.  Set up the DMA registers and let 'er rip.
- */
-p_command:
-       call    assert
-
-/*
- * Load HADDR and HCNT.
- */
-       mov     HADDR0, SCB_CMDPTR0
-       mov     HADDR1, SCB_CMDPTR1
-       mov     HADDR2, SCB_CMDPTR2
-       mov     HADDR3, SCB_CMDPTR3
-       mov     HCNT0, SCB_CMDLEN
-       clr     HCNT1
-       clr     HCNT2
-
-       mov     STCNT0, HCNT0
-       mov     STCNT1, HCNT1
-       mov     STCNT2, HCNT2
-
-       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     SCB_TARGET_STATUS       call inb_first
-       jmp     mesgin_done
-
-/*
- * Message out phase.  If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
- */
-p_mesgout:
-       test    MSG_LEN, 0xff   jnz  p_mesgout_start
-       mvi     MSG_NOP         call mk_mesg    /* build NOP message */
-
-p_mesgout_start:
-/*
- * Set up automatic PIO transfer from MSG0.  Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
- */
-       mvi     SINDEX,MSG0
-       mov     DINDEX,MSG_LEN
-
-/*
- * 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.
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
- */
-p_mesgout_loop:
-       test    SSTAT1,PHASEMIS jnz p_mesgout_phasemis
-       test    SSTAT0,SPIORDY  jz p_mesgout_loop
-       test    SSTAT1,PHASEMIS jnz p_mesgout_phasemis
-       cmp     DINDEX,1        jne p_mesgout_outb      /* last byte? */
-       mvi     CLRSINT1,CLRATNO                        /* drop ATN */
-p_mesgout_outb:
-       dec     DINDEX
-       or      CLRSINT0, CLRSPIORDY
-       mov     SCSIDATL,SINDIR
-       
-p_mesgout4:
-       test    DINDEX,0xff     jnz p_mesgout_loop
-
-/*
- * 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_mesgout_snoop:
-       test    SSTAT1,BUSFREE  jnz p_mesgout_done
-       test    SSTAT1,REQINIT  jz p_mesgout_snoop
-
-       test    SSTAT1,PHASEMIS jnz p_mesgout_done
-
-       or      SCSISIGO,ATNO                   /* turn on ATNO */
-
-       jmp     ITloop
-
-p_mesgout_phasemis:
-       mvi     CLRSINT1,CLRATNO        /* Be sure to turn ATNO off */
-p_mesgout_done:
-       clr     MSG_LEN                 /* no active msg */
-       jmp     ITloop
-
-/*
- * Message in phase.  Bytes are read using Automatic PIO mode.
- */
-p_mesgin:
-       mvi     A               call inb_first  /* read the 1st message byte */
-       mov     REJBYTE,A                       /* save it for the driver */
-
-       test    A,MSG_IDENTIFY          jnz mesgin_identify
-       cmp     A,MSG_DISCONNECT        je mesgin_disconnect
-       cmp     A,MSG_SDPTRS            je mesgin_sdptrs
-       cmp     ALLZEROS,A              je mesgin_complete
-       cmp     A,MSG_RDPTRS            je mesgin_rdptrs
-       cmp     A,MSG_EXTENDED          je mesgin_extended
-       cmp     A,MSG_REJECT            je mesgin_reject
-
-rej_mesgin:
-/*
- * 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.
- */
-       or      SCSISIGO,ATNO                   /* turn on ATNO */
-       mvi     INTSTAT,SEND_REJECT             /* let driver know */
-
-       mvi     MSG_REJECT      call mk_mesg
-
-mesgin_done:
-       call    inb_last                        /*ack & turn auto PIO back on*/
-       jmp     ITloop
-
-
-mesgin_complete:
-/*
- * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT,
- * and trigger a completion interrupt.  Check status for non zero return
- * and interrupt driver if needed.  This allows the driver to interpret
- * errors only when they occur instead of always uploading the scb.  If
- * the status is SCSI_CHECK, the driver will download a new scb requesting
- * sense to replace the old one, modify the "waiting for selection" SCB list
- * and set RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE the
- * sequencer imediately jumps to main loop where it will run down the waiting
- * SCB list and process the sense request.  If the kernel driver does not
- * wish to request sense, it need only clear RETURN_1, and the command is
- * allowed to complete.  We don't bother to post to the QOUTFIFO in the
- * error case since it would require extra work in the kernel driver to
- * ensure that the entry was removed before the command complete code tried
- * processing it.
- *
- * First check for residuals
- */
-       test    SCB_RESID_SGCNT,0xff    jz check_status
-/*
- * If we have a residual count, interrupt and tell the host.  Other
- * alternatives are to pause the sequencer on all command completes (yuck),
- * dma the resid directly to the host (slick, we may have space to do it now)
- * or have the sequencer pause itself when it encounters a non-zero resid 
- * (unnecessary pause just to flag the command -yuck-, but takes one instruction
- * and since it shouldn't happen that often is good enough for our purposes).  
- */
-resid:
-       mvi     INTSTAT,RESIDUAL
-
-check_status:
-       test    SCB_TARGET_STATUS,0xff  jz status_ok    /* Good Status? */
-       mvi     INTSTAT,BAD_STATUS                      /* let driver know */
-       cmp     RETURN_1, SEND_SENSE    jne status_ok
-       jmp     mesgin_done
-
-status_ok:
-/* First, mark this target as free. */
-       test    SCB_CONTROL,TAG_ENB jnz test_immediate  /*
-                                                        * Tagged commands
-                                                        * don't busy the
-                                                        * target.
-                                                        */
-       mov     FUNCTION1,SCB_TCL
-       mov     A,FUNCTION1
-       test    SCB_TCL,0x88 jz clear_a
-       xor     ACTIVE_B,A
-       jmp     test_immediate
-
-clear_a:
-       xor     ACTIVE_A,A
-
-test_immediate:
-       test    SCB_CMDLEN,0xff jnz complete  /* Immediate message complete */
-/*
- * Pause the sequencer until the driver gets around to handling the command
- * complete.  This is so that any action that might require careful timing
- * with the completion of this command can occur.
- */
-       mvi     INTSTAT,IMMEDDONE
-       jmp     start
-complete:
-       mov     QOUTFIFO,SCB_TAG
-       mvi     INTSTAT,CMDCMPLT
-       jmp     mesgin_done
-
-
-/*
- * Is it an extended message?  We only support the synchronous and wide data
- * transfer request messages, which will probably be in response to
- * WDTR or SDTR message outs from us.  If it's not SDTR or WDTR, reject it -
- * apparently this can be done after any message in byte, according
- * to the SCSI-2 spec.
- */
-mesgin_extended:
-       mvi     ARG_1           call inb_next   /* extended message length */
-       mvi     REJBYTE_EXT     call inb_next   /* extended message code */
-
-       cmp     REJBYTE_EXT,MSG_SDTR    je p_mesginSDTR
-       cmp     REJBYTE_EXT,MSG_WDTR    je p_mesginWDTR
-       jmp     rej_mesgin
-
-p_mesginWDTR:
-       cmp     ARG_1,2         jne rej_mesgin  /* extended mesg length=2 */
-       mvi     ARG_1           call inb_next   /* Width of bus */
-       mvi     INTSTAT,WDTR_MSG                /* let driver know */
-       test    RETURN_1,0xff jz mesgin_done    /* Do we need to send WDTR? */
-       cmp     RETURN_1,SEND_REJ je rej_mesgin /*
-                                                * Bus width was too large 
-                                                * Reject it.
-                                                */
-
-/* We didn't initiate the wide negotiation, so we must respond to the request */
-       and     RETURN_1,0x7f                   /* Clear the SEND_WDTR Flag */
-       mvi     DINDEX,MSG0
-       mvi     MSG0    call mk_wdtr            /* build WDTR message */
-       or      SCSISIGO,ATNO                   /* turn on ATNO */
-       jmp     mesgin_done
-
-p_mesginSDTR:
-       cmp     ARG_1,3         jne rej_mesgin  /* extended mesg length=3 */
-       mvi     ARG_1           call inb_next   /* xfer period */
-       mvi     A               call inb_next   /* REQ/ACK offset */
-       mvi     INTSTAT,SDTR_MSG                /* call driver to convert */
-
-       test    RETURN_1,0xff   jz mesgin_done  /* Do we need to mk_sdtr/rej */
-       cmp     RETURN_1,SEND_REJ je rej_mesgin /*
-                                                * Requested SDTR too small
-                                                * Reject it.
-                                                */
-       clr     ARG_1                           /* Use the scratch ram rate */
-       mvi     DINDEX, MSG0
-       mvi     MSG0     call mk_sdtr
-       or      SCSISIGO,ATNO                   /* turn on ATNO */
-       jmp     mesgin_done
-
-/*
- * Is it a disconnect message?  Set a flag in the SCB to remind us
- * and await the bus going free.
- */
-mesgin_disconnect:
-       or      SCB_CONTROL,DISCONNECTED
-       test    FLAGS, PAGESCBS jz mesgin_done
-/*
- * Link this SCB into the DISCONNECTED list.  This list holds the
- * candidates for paging out an SCB if one is needed for a new command.
- * Modifying the disconnected list is a critical(pause dissabled) section.
- */
-       mvi     SCB_PREV, SCB_LIST_NULL
-       mvi     SEQCTL,0x50                     /* PAUSEDIS|FASTMODE */
-       mov     SCB_NEXT, DISCONNECTED_SCBH
-       mov     DISCONNECTED_SCBH, SCBPTR
-       cmp     SCB_NEXT,SCB_LIST_NULL je linkdone
-       mov     SCBPTR,SCB_NEXT
-       mov     SCB_PREV,DISCONNECTED_SCBH
-       mov     SCBPTR,DISCONNECTED_SCBH
-linkdone:
-       mvi     SEQCTL,0x10                     /* !PAUSEDIS|FASTMODE */
-       jmp     mesgin_done
-
-/*
- * Save data pointers message?  Copy working values into the SCB,
- * usually in preparation for a disconnect.
- */
-mesgin_sdptrs:
-       call    sg_ram2scb
-       jmp     mesgin_done
-
-/*
- * Restore pointers message?  Data pointers are recopied from the
- * SCB anytime we enter a data phase for the first time, so all
- * we need to do is clear the DPHASE flag and let the data phase
- * code do the rest.
- */
-mesgin_rdptrs:
-       and     FLAGS,0xef                      /*
-                                                * !DPHASE we'll reload them
-                                                * the next time through
-                                                */
-       jmp     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.
- */
-mesgin_identify:
-       test    A,0x78  jnz rej_mesgin  /*!DiscPriv|!LUNTAR|!Reserved*/
-
-       and     A,0x07                  /* lun in lower three bits */
-       or      SAVED_TCL,A,SELID          
-       and     SAVED_TCL,0xf7
-       and     A,SELBUSB,SBLKCTL       /* B Channel?? */
-       or      SAVED_TCL,A
-       call    inb_last                /* ACK */
-
-/*
- * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
- * If we get one, we use the tag returned to switch to find the proper
- * SCB.  With SCB paging, this requires using findSCB for both tagged
- * and non-tagged transactions since the SCB may exist in any slot.
- * If we're not using SCB paging, we can use the tag as the direct
- * index to the SCB.
- */
-       mvi     ARG_1,SCB_LIST_NULL     /* Default to no-tag */
-snoop_tag_loop:
-       test    SSTAT1,BUSFREE  jnz use_findSCB
-       test    SSTAT1,REQINIT  jz snoop_tag_loop
-       test    SSTAT1,PHASEMIS jnz use_findSCB
-       mvi     A               call inb_first
-       cmp     A,MSG_SIMPLE_TAG jne use_findSCB
-get_tag:
-       mvi     ARG_1   call inb_next   /* tag value */
-/*
- * See if the tag is in range.  The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incoming tag and there is
- * no carry.
- */
-       mov     A,COMP_SCBCOUNT 
-       add     SINDEX,A,ARG_1
-       jc      abort_tag
-
-/*
- * Ensure that the SCB the tag points to is for a SCB transaction
- * to the reconnecting target.
- */
-       test    FLAGS, PAGESCBS jz index_by_tag
-       call    inb_last                        /* Ack Tag */
-use_findSCB:
-       mov     ALLZEROS        call findSCB      /* Have to search */
-setup_SCB:
-       and     SCB_CONTROL,0xfb          /* clear disconnect bit in SCB */
-       or      FLAGS,IDENTIFY_SEEN       /* make note of IDENTIFY */
-       jmp     ITloop
-index_by_tag:
-       mov     SCBPTR,ARG_1
-       mov     A,SAVED_TCL
-       cmp     SCB_TCL,A               jne abort_tag
-       test    SCB_CONTROL,TAG_ENB     jz  abort_tag
-       call    inb_last                        /* Ack Successful tag */
-       jmp     setup_SCB
-
-abort_tag:
-       or      SCSISIGO,ATNO                   /* turn on ATNO */
-       mvi     INTSTAT,ABORT_TAG               /* let driver know */
-       mvi     MSG_ABORT_TAG   call mk_mesg    /* ABORT TAG message */
-       jmp     mesgin_done
-
-/*
- * Message reject?  Let the kernel driver handle this.  If we have an 
- * outstanding WDTR or SDTR negotiation, assume that it's a response from 
- * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
- * it since we have no clue what it pertains to.
- */
-mesgin_reject:
-       mvi     INTSTAT, REJECT_MSG
-       jmp     mesgin_done
-
-/*
- * [ ADD MORE MESSAGE HANDLING HERE ]
- */
-
-/*
- * 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,CLRATNO
-       clr     LASTPHASE
-
-/*
- * if this is an immediate command, perform a pseudo command complete to
- * notify the driver.
- */
-       test    SCB_CMDLEN,0xff jz status_ok
-       jmp     start
-
-/*
- * 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,0x50                     /* PAUSEDIS|FASTMODE */
-       test    MSG_LEN,0xff    jz mk_mesg1     /* Should always succeed */
-       
-       /*
-        * Hmmm.  For some reason the mesg buffer is in use.
-        * Tell the driver.  It should look at SINDEX to find
-        * out what we wanted to use the buffer for and resolve
-        * the conflict.
-        */
-       mvi     SEQCTL,0x10                     /* !PAUSEDIS|FASTMODE */
-       mvi     INTSTAT,MSG_BUFFER_BUSY
-
-mk_mesg1:
-       mvi     MSG_LEN,1               /* length = 1 */
-       mov     MSG0,SINDEX             /* 1-byte message */
-       mvi     SEQCTL,0x10     ret     /* !PAUSEDIS|FASTMODE */
-
-/*
- * Functions to read data in Automatic PIO mode.
- *
- * 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_next:
-       or      CLRSINT0, CLRSPIORDY
-       mov     NONE,SCSIDATL                   /*dummy read from latch to ACK*/
-inb_next_wait:
-       test    SSTAT1,PHASEMIS jnz mesgin_phasemis
-       test    SSTAT0,SPIORDY  jz inb_next_wait /* wait for next byte */
-inb_first:
-       mov     DINDEX,SINDEX
-       test    SSTAT1,PHASEMIS jnz mesgin_phasemis
-       mov     DINDIR,SCSIBUSL ret             /*read byte directly from bus*/
-inb_last:
-       mov     NONE,SCSIDATL ret               /*dummy read from latch to ACK*/
-
-mesgin_phasemis:
-/*
- * We expected to receive another byte, but the target changed phase
- */
-       mvi     INTSTAT, MSGIN_PHASEMIS
-       jmp     ITloop
-
-/*
- * 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:
-       test    SSTAT0,DMADONE  jnz dma3
-       test    SSTAT1,PHASEMIS jz dma1         /* 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, the data FIFO should be flushed auto-
- * magically on STCNT=0 or a phase change, so just wait for FIFO empty
- * status.
- */
-dma3:
-       test    SINDEX,DIRECTION        jnz dma5
-dma4:
-       test    DFSTATUS,FIFOEMP        jz dma4
-
-/*
- * Now shut the DMA enables off and make sure that the DMA enables are 
- * actually off first lest we get an ILLSADDR.
- */
-dma5:
-       /* disable DMA, but maintain WIDEODD */
-       and     DFCNTRL,WIDEODD
-dma6:
-       test    DFCNTRL,0x38    jnz dma6  /* SCSIENACK|SDMAENACK|HDMAENACK */
-
-       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_scsiid:
-       and     SINDEX,0xf0             /* Get target ID */
-       and     A,0x0f,SCSIID
-       or      SINDEX,A
-       mov     SCSIID,SINDEX ret
-
-/*
- * Assert that if we've been reselected, then we've seen an IDENTIFY
- * message.
- */
-assert:
-       test    FLAGS,RESELECTED        jz return       /* reselected? */
-       test    FLAGS,IDENTIFY_SEEN     jnz return      /* seen IDENTIFY? */
-
-       mvi     INTSTAT,NO_IDENT        ret     /* no - cause a kernel panic */
-
-/*
- * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag
- * value in ARG_1.  If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged
- * SCB.  Have the kernel print a warning message if it can't be found, and
- * generate an ABORT/ABORT_TAG message to the target.  SINDEX should be
- * cleared on call.
- */
-findSCB:
-       mov     A,SAVED_TCL
-       mov     SCBPTR,SINDEX                   /* switch to next SCB */
-       mvi     SEQCTL,0x50                     /* PAUSEDIS|FASTMODE */
-       cmp     SCB_TCL,A       jne findSCB1 /* target ID/channel/lun match? */
-       test    SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
-       test    SCB_CONTROL,TAG_ENB jnz findTaggedSCB
-       cmp     ARG_1,SCB_LIST_NULL je foundSCB
-       jmp     findSCB1
-findTaggedSCB:
-       mov     A, ARG_1                        /* Tag passed in ARG_1 */
-       cmp     SCB_TAG,A       jne findSCB1    /* Found it? */
-foundSCB:
-       test    FLAGS,PAGESCBS  jz foundSCB_ret
-/* Remove this SCB from the disconnection list */
-       cmp     SCB_NEXT,SCB_LIST_NULL je unlink_prev
-       mov     SAVED_LINKPTR, SCB_PREV
-       mov     SCBPTR, SCB_NEXT
-       mov     SCB_PREV, SAVED_LINKPTR
-       mov     SCBPTR, SINDEX
-unlink_prev:
-       cmp     SCB_PREV,SCB_LIST_NULL  je rHead/* At the head of the list */
-       mov     SAVED_LINKPTR, SCB_NEXT
-       mov     SCBPTR, SCB_PREV
-       mov     SCB_NEXT, SAVED_LINKPTR
-       mov     SCBPTR, SINDEX
-       mvi     SEQCTL,0x10     ret             /* !PAUSEDIS|FASTMODE */
-rHead:
-       mov     DISCONNECTED_SCBH,SCB_NEXT
-foundSCB_ret:
-       mvi     SEQCTL,0x10     ret             /* !PAUSEDIS|FASTMODE */
-
-findSCB1:
-       mvi     SEQCTL,0x10                     /* !PAUSEDIS|FASTMODE */
-       inc     SINDEX
-       mov     A,SCBCOUNT
-       cmp     SINDEX,A        jne findSCB
-
-       mvi     INTSTAT,NO_MATCH                /* not found - signal kernel */
-       cmp     RETURN_1,SCB_PAGEDIN je return
-       or      SCSISIGO,ATNO                   /* assert ATNO */
-       cmp     ARG_1,SCB_LIST_NULL jne find_abort_tag
-       mvi     MSG_ABORT       call mk_mesg
-       jmp     ITloop
-find_abort_tag:
-       mvi     MSG_ABORT_TAG   call mk_mesg
-       jmp     ITloop
-
-/*
- * Make a working copy of the scatter-gather parameters from the SCB.
- */
-sg_scb2ram:
-       mov     HADDR0, SCB_DATAPTR0
-       mov     HADDR1, SCB_DATAPTR1
-       mov     HADDR2, SCB_DATAPTR2
-       mov     HADDR3, SCB_DATAPTR3
-       mov     HCNT0, SCB_DATACNT0
-       mov     HCNT1, SCB_DATACNT1
-       mov     HCNT2, SCB_DATACNT2
-
-       mov     STCNT0, HCNT0
-       mov     STCNT1, HCNT1
-       mov     STCNT2, HCNT2
-
-       mov     SG_COUNT,SCB_SGCOUNT
-
-       mov     SG_NEXT0, SCB_SGPTR0
-       mov     SG_NEXT1, SCB_SGPTR1
-       mov     SG_NEXT2, SCB_SGPTR2
-       mov     SG_NEXT3, SCB_SGPTR3 ret
-
-/*
- * Copying RAM values back to SCB, for Save Data Pointers message, but
- * only if we've actually been into a data phase to change them.  This
- * protects against bogus data in scratch ram and the residual counts
- * since they are only initialized when we go into data_in or data_out.
- */
-sg_ram2scb:
-       test    FLAGS, DPHASE   jz return
-       mov     SCB_SGCOUNT,SG_COUNT
-
-       mov     SCB_SGPTR0,SG_NEXT0
-       mov     SCB_SGPTR1,SG_NEXT1
-       mov     SCB_SGPTR2,SG_NEXT2
-       mov     SCB_SGPTR3,SG_NEXT3
-       
-       mov     SCB_DATAPTR0,SHADDR0
-       mov     SCB_DATAPTR1,SHADDR1
-       mov     SCB_DATAPTR2,SHADDR2
-       mov     SCB_DATAPTR3,SHADDR3
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer
- */
-       mov     SCB_DATACNT0,SCB_RESID_DCNT0
-       mov     SCB_DATACNT1,SCB_RESID_DCNT1
-       mov     SCB_DATACNT2,SCB_RESID_DCNT2 ret
-
-/*
- * Add the array base TARG_SCRATCH 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_dtr:
-       shr     A,SCSIID,4
-       test    SBLKCTL,SELBUSB jz ndx_dtr_2
-       or      A,0x08          /* Channel B entries add 8 */
-ndx_dtr_2:
-       add     SINDEX,TARG_SCRATCH,A ret
-
-/*
- * If we need to negotiate transfer parameters, build the WDTR or SDTR message
- * starting at the address passed in SINDEX.  DINDEX is modified on return.
- * The SCSI-II spec requires that Wide negotiation occur first and you can
- * only negotiate one or the other at a time otherwise in the event of a message
- * reject, you wouldn't be able to tell which message was the culprit.
- */
-mk_dtr:
-       test    SCB_CONTROL,NEEDWDTR jnz  mk_wdtr_16bit
-       mvi     ARG_1, MAXOFFSET        /* Force an offset of 15 or 8 if WIDE */
-
-mk_sdtr:
-       mvi     DINDIR,1                /* extended message */
-       mvi     DINDIR,3                /* extended message length = 3 */
-       mvi     DINDIR,1                /* SDTR code */
-       call    sdtr_to_rate
-       mov     DINDIR,RETURN_1         /* REQ/ACK transfer period */
-       cmp     ARG_1, MAXOFFSET je mk_sdtr_max_offset
-       and     DINDIR,0x0f,SINDIR      /* Sync Offset */
-
-mk_sdtr_done:
-       add     MSG_LEN,COMP_MSG0,DINDEX ret    /* update message length */
-
-mk_sdtr_max_offset:
-/*
- * We're initiating sync negotiation, so request the max offset we can (15 or 8)
- */
-       /* Talking to a WIDE device? */
-       test    SCSIRATE, WIDEXFER      jnz wmax_offset 
-       mvi     DINDIR, MAX_OFFSET_8BIT
-       jmp     mk_sdtr_done
-
-wmax_offset:
-       mvi     DINDIR, MAX_OFFSET_16BIT
-       jmp     mk_sdtr_done
-
-mk_wdtr_16bit:
-       mvi     ARG_1,BUS_16_BIT
-mk_wdtr:
-       mvi     DINDIR,1                /* extended message */
-       mvi     DINDIR,2                /* extended message length = 2 */
-       mvi     DINDIR,3                /* WDTR code */
-       mov     DINDIR,ARG_1            /* bus width */
-
-       add     MSG_LEN,COMP_MSG0,DINDEX ret    /* update message length */
-       
-sdtr_to_rate:
-       call    ndx_dtr                 /* index scratch space for target */
-       shr     A,SINDIR,0x4
-       dec     SINDEX                  /* Preserve SINDEX */
-       and     A,0x7
-       clr     RETURN_1
-sdtr_to_rate_loop:
-       test    A,0x0f  jz sdtr_to_rate_done
-       add     RETURN_1,0x19
-       dec     A       
-       jmp     sdtr_to_rate_loop
-sdtr_to_rate_done:
-       shr     RETURN_1,0x2
-       add     RETURN_1,0x19
-       test    SXFRCTL0,ULTRAEN jz return
-       shr     RETURN_1,0x1
-return:
-       ret
diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg
new file mode 100644 (file)
index 0000000..fcde1ca
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
+ */
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+       address                 0x000
+       access_mode RW
+       bit     TEMODE          0x80
+       bit     ENSELO          0x40
+       bit     ENSELI          0x20
+       bit     ENRSELI         0x10
+       bit     ENAUTOATNO      0x08
+       bit     ENAUTOATNI      0x04
+       bit     ENAUTOATNP      0x02
+       bit     SCSIRSTO        0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+       address                 0x001
+       access_mode RW
+       bit     DFON            0x80
+       bit     DFPEXP          0x40
+       bit     FAST20          0x20
+       bit     CLRSTCNT        0x10
+       bit     SPIOEN          0x08
+       bit     SCAMEN          0x04
+       bit     CLRCHN          0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+       address                 0x002
+       access_mode RW
+       bit     BITBUCKET       0x80
+       bit     SWRAPEN         0x40
+       bit     ENSPCHK         0x20
+       mask    STIMESEL        0x18
+       bit     ENSTIMER        0x04
+       bit     ACTNEGEN        0x02
+       bit     STPWEN          0x01    /* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+       address                 0x003
+       access_mode RO
+       bit     CDI             0x80
+       bit     IOI             0x40
+       bit     MSGI            0x20
+       bit     ATNI            0x10
+       bit     SELI            0x08
+       bit     BSYI            0x04
+       bit     REQI            0x02
+       bit     ACKI            0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+       mask    PHASE_MASK      CDI|IOI|MSGI
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOI
+       mask    P_COMMAND       CDI
+       mask    P_MESGOUT       CDI|MSGI
+       mask    P_STATUS        CDI|IOI
+       mask    P_MESGIN        CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus.  Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+       address                 0x003
+       access_mode WO
+       bit     CDO             0x80
+       bit     IOO             0x40
+       bit     MSGO            0x20
+       bit     ATNO            0x10
+       bit     SELO            0x08
+       bit     BSYO            0x04
+       bit     REQO            0x02
+       bit     ACKO            0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+       mask    PHASE_MASK      CDI|IOI|MSGI
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOI
+       mask    P_COMMAND       CDI
+       mask    P_MESGOUT       CDI|MSGI
+       mask    P_STATUS        CDI|IOI
+       mask    P_MESGIN        CDI|IOI|MSGI
+}
+
+/* 
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+       address                 0x004
+       access_mode RW
+       bit     WIDEXFER        0x80            /* Wide transfer control */
+       mask    SXFR            0x70            /* Sync transfer rate */
+       mask    SOFS            0x0f            /* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID        {
+       address                 0x005
+       access_mode RW
+       mask    TID             0xf0            /* Target ID mask */
+       mask    OID             0x0f            /* Our ID mask */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode.  SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+       address                 0x006
+       access_mode RW
+}
+
+register SCSIDATH {
+       address                 0x007
+       access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transferred.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */ 
+register STCNT {
+       address                 0x008
+       size    3
+       access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+       address                 0x00b
+       access_mode WO
+       bit     CLRSELDO        0x40
+       bit     CLRSELDI        0x20
+       bit     CLRSELINGO      0x10
+       bit     CLRSWRAP        0x08
+       bit     CLRSPIORDY      0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0        {
+       address                 0x00b
+       access_mode RO
+       bit     TARGET          0x80            /* Board acting as target */
+       bit     SELDO           0x40            /* Selection Done */
+       bit     SELDI           0x20            /* Board has been selected */
+       bit     SELINGO         0x10            /* Selection In Progress */
+       bit     SWRAP           0x08            /* 24bit counter wrap */
+       bit     SDONE           0x04            /* STCNT = 0x000000 */
+       bit     SPIORDY         0x02            /* SCSI PIO Ready */
+       bit     DMADONE         0x01            /* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+       address                 0x00c
+       access_mode WO
+       bit     CLRSELTIMEO     0x80
+       bit     CLRATNO         0x40
+       bit     CLRSCSIRSTI     0x20
+       bit     CLRBUSFREE      0x08
+       bit     CLRSCSIPERR     0x04
+       bit     CLRPHASECHG     0x02
+       bit     CLRREQINIT      0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1        {
+       address                 0x00c
+       access_mode RO
+       bit     SELTO           0x80
+       bit     ATNTARG         0x40
+       bit     SCSIRSTI        0x20
+       bit     PHASEMIS        0x10
+       bit     BUSFREE         0x08
+       bit     SCSIPERR        0x04
+       bit     PHASECHG        0x02
+       bit     REQINIT         0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+       address                 0x00d
+       access_mode RO
+       bit     OVERRUN         0x80
+       mask    SFCNT           0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+       address                 0x00e
+       access_mode RO
+       mask    SCSICNT         0xf0
+       mask    OFFCNT          0x0f
+}
+
+/*
+ * SCSI Test Control (p. 3-27)
+ */
+register SCSITEST {
+       address                 0x00f
+       access_mode RW
+       bit     RQAKCNT         0x04
+       bit     CNTRTEST        0x02
+       bit     CMODE           0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+       address                 0x010
+       access_mode RW
+       bit     ENSELDO         0x40
+       bit     ENSELDI         0x20
+       bit     ENSELINGO       0x10
+       bit     ENSWRAP         0x08
+       bit     ENSDONE         0x04
+       bit     ENSPIORDY       0x02
+       bit     ENDMADONE       0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+       address                 0x011
+       access_mode RW
+       bit     ENSELTIMO       0x80
+       bit     ENATNTARG       0x40
+       bit     ENSCSIRST       0x20
+       bit     ENPHASEMIS      0x10
+       bit     ENBUSFREE       0x08
+       bit     ENSCSIPERR      0x04
+       bit     ENPHASECHG      0x02
+       bit     ENREQINIT       0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+       address                 0x012
+       access_mode RO
+}
+
+register SCSIBUSH {
+       address                 0x013
+       access_mode RO
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+       address                 0x014
+       size    4
+       access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+       address                 0x018
+       access_mode RW
+       bit     STAGE6          0x20
+       bit     STAGE5          0x10
+       bit     STAGE4          0x08
+       bit     STAGE3          0x04
+       bit     STAGE2          0x02
+       bit     STAGE1          0x01
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+       address                 0x019
+       access_mode RW
+       mask    SELID_MASK      0xf0
+       bit     ONEBIT          0x08
+}
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection.  In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+       address                 0x01f
+       access_mode RW
+       bit     DIAGLEDEN       0x80    /* Aic78X0 only */
+       bit     DIAGLEDON       0x40    /* Aic78X0 only */
+       bit     AUTOFLUSHDIS    0x20
+       bit     SELBUSB         0x08
+       bit     SELWIDE         0x02
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+       address                 0x060
+       access_mode RW
+       bit     PERRORDIS       0x80
+       bit     PAUSEDIS        0x40
+       bit     FAILDIS         0x20
+       bit     FASTMODE        0x10
+       bit     BRKADRINTEN     0x08
+       bit     STEP            0x04
+       bit     SEQRESET        0x02
+       bit     LOADRAM         0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
+ * four bytes in sucessesion.  The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+       address                 0x061
+       access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+       address                 0x062
+       access_mode RW
+}
+
+register SEQADDR1 {
+       address                 0x063
+       access_mode RW
+       mask    SEQADDR1_MASK   0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+       address                 0x064
+       access_mode RW
+       accumulator
+}
+
+register SINDEX        {
+       address                 0x065
+       access_mode RW
+       sindex
+}
+
+register DINDEX {
+       address                 0x066
+       access_mode RW
+}
+
+register ALLONES {
+       address                 0x069
+       access_mode RO
+       allones
+}
+
+register ALLZEROS {
+       address                 0x06a
+       access_mode RO
+       allzeros
+}
+
+register NONE {
+       address                 0x06a
+       access_mode WO
+       none
+}
+
+register FLAGS {
+       address                 0x06b
+       access_mode RO
+       bit     ZERO            0x02
+       bit     CARRY           0x01
+}
+
+register SINDIR        {
+       address                 0x06c
+       access_mode RO
+}
+
+register DINDIR         {
+       address                 0x06d
+       access_mode WO
+}
+
+register FUNCTION1 {
+       address                 0x06e
+       access_mode RW
+}
+
+register STACK {
+       address                 0x06f
+       access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+       address                 0x084
+       access_mode RW
+       bit     ACE             0x08
+       bit     ENABLE          0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND {
+       address                 0x084
+       access_mode RW
+       bit     CACHETHEN       0x80    /* Cache Threshold enable */
+       bit     DPARCKEN        0x40    /* Data Parity Check Enable */
+       bit     MPARCKEN        0x20    /* Memory Parity Check Enable */
+       bit     EXTREQLCK       0x10    /* External Request Lock */
+}
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+register BUSTIME {
+       address                 0x085
+       access_mode RW
+       mask    BOFF            0xf0
+       mask    BON             0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+register BUSSPD {
+       address                 0x086
+       access_mode RW
+       mask    DFTHRSH         0xc0
+       mask    STBOFF          0x38
+       mask    STBON           0x07
+       mask    DFTHRSH_100     0xc0
+}
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+       address                 0x087
+       access_mode RW
+       bit     POWRDN          0x40
+       bit     SWINT           0x10
+       bit     IRQMS           0x08
+       bit     PAUSE           0x04
+       bit     INTEN           0x02
+       bit     CHIPRST         0x01
+       bit     CHIPRSTACK      0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+       address                 0x088
+       size    4
+       access_mode RW
+}
+
+register HCNT {
+       address                 0x08c
+       size    3
+       access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+       address                 0x090
+       access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+       address                 0x091
+       access_mode RW
+       bit     BRKADRINT 0x08
+       bit     SCSIINT   0x04
+       bit     CMDCMPLT  0x02
+       bit     SEQINT    0x01
+       mask    BAD_PHASE       SEQINT          /* unknown scsi bus phase */
+       mask    SEND_REJECT     0x10|SEQINT     /* sending a message reject */
+       mask    NO_IDENT        0x20|SEQINT     /* no IDENTIFY after reconnect*/
+       mask    NO_MATCH        0x30|SEQINT     /* no cmd match for reconnect */
+       mask    EXTENDED_MSG    0x40|SEQINT     /* Extended message received */
+       mask    NO_MATCH_BUSY   0x50|SEQINT     /* Couldn't find BUSY SCB */
+       mask    REJECT_MSG      0x60|SEQINT     /* Reject message received */
+       mask    BAD_STATUS      0x70|SEQINT     /* Bad status from target */
+       mask    RESIDUAL        0x80|SEQINT     /* Residual byte count != 0 */
+       mask    ABORT_CMDCMPLT  0x91            /*
+                                                * Command tagged for abort
+                                                * completed successfully.
+                                                */
+       mask    AWAITING_MSG    0xa0|SEQINT     /*
+                                                * Kernel requested to specify
+                                                 * a message to this target
+                                                 * (command was null), so tell
+                                                 * it that it can fill the
+                                                 * message buffer.
+                                                 */
+       mask    MSG_BUFFER_BUSY 0xc0|SEQINT     /*
+                                                * Sequencer wants to use the
+                                                * message buffer, but it
+                                                * already contains a message
+                                                */
+       mask    MSGIN_PHASEMIS  0xd0|SEQINT     /*
+                                                * Target changed phase on us
+                                                * when we were expecting
+                                                * another msgin byte.
+                                                */
+       mask    DATA_OVERRUN    0xe0|SEQINT     /*
+                                                * Target attempted to write
+                                                * beyond the bounds of its
+                                                * command.
+                                                */
+
+       mask    SEQINT_MASK     0xf0|SEQINT     /* SEQINT Status Codes */
+       mask    INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors.  You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+       address                 0x092
+       access_mode RO
+       bit     PARERR          0x08
+       bit     ILLOPCODE       0x04
+       bit     ILLSADDR        0x02
+       bit     ILLHADDR        0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+       address                 0x092
+       access_mode WO
+       bit     CLRBRKADRINT    0x08
+       bit     CLRSCSIINT      0x04
+       bit     CLRCMDINT       0x02
+       bit     CLRSEQINT       0x01
+}
+
+register DFCNTRL {
+       address                 0x093
+       access_mode RW
+       bit     WIDEODD         0x40
+       bit     SCSIEN          0x20
+       bit     SDMAEN          0x10
+       bit     SDMAENACK       0x10
+       bit     HDMAEN          0x08
+       bit     HDMAENACK       0x08
+       bit     DIRECTION       0x04
+       bit     FIFOFLUSH       0x02
+       bit     FIFORESET       0x01
+}
+
+register DFSTATUS {
+       address                 0x094
+       access_mode RO
+       bit     DWORDEMP        0x20
+       bit     MREQPEND        0x10
+       bit     HDONE           0x08
+       bit     DFTHRESH        0x04
+       bit     FIFOFULL        0x02
+       bit     FIFOEMP         0x01
+}
+
+register DFDAT {
+       address                 0x099
+       access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+       address                 0x09a
+       access_mode RW
+       bit     SCBAUTO         0x80
+       mask    SCBCNT_MASK     0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+       address                 0x09b
+       access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT        {
+       address                 0x09c
+       access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+       address                 0x09d
+       access_mode WO
+}
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+       address                 0x09e
+       access_mode RO
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+       address                 0x0a0
+       SCB_CONTROL {
+               size    1
+               bit     MK_MESSAGE      0x80
+               bit     DISCENB         0x40
+               bit     TAG_ENB         0x20
+               bit     MUST_DMAUP_SCB  0x10
+               bit     ABORT_SCB       0x08
+               bit     DISCONNECTED    0x04
+               mask    SCB_TAG_TYPE    0x03
+       }
+       SCB_TCL {
+               size    1
+               bit     SELBUSB         0x08
+               mask    TID             0xf0
+               mask    LID             0x07
+       }
+       SCB_TARGET_STATUS {
+               size    1
+       }
+       SCB_SGCOUNT {
+               size    1
+       }
+       SCB_SGPTR {
+               size    4
+       }
+       SCB_RESID_SGCNT {
+               size    1
+       }
+       SCB_RESID_DCNT  {
+               size    3
+       }
+       SCB_DATAPTR {
+               size    4
+       }
+       SCB_DATACNT {
+               size    3
+       }
+       SCB_LINKED_NEXT {
+               size    1
+       }
+       SCB_CMDPTR {
+               size    4
+       }
+       SCB_CMDLEN {
+               size    1
+       }
+       SCB_TAG {
+               size    1
+       }
+       SCB_NEXT {
+               size    1
+       }
+       SCB_PREV {
+               size    1
+       }
+       SCB_BUSYTARGETS {
+               size    4
+       }
+}
+
+const  SG_SIZEOF       0x08            /* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+       address                 0x0c0
+       access_mode RW
+       bit     CS_2840         0x04
+       bit     CK_2840         0x02
+       bit     DO_2840         0x01
+}
+
+register STATUS_2840 {
+       address                 0x0c1
+       access_mode RW
+       bit     EEPROM_TF       0x80
+       mask    BIOS_SEL        0x60
+       mask    ADSEL           0x1e
+       bit     DI_2840         0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register DSPCISTATUS {
+       address                 0x086
+}
+
+register BRDCTL        {
+       address                 0x01d
+       bit     BRDDAT7         0x80
+       bit     BRDDAT6         0x40
+       bit     BRDDAT5         0x20
+       bit     BRDSTB          0x10
+       bit     BRDCS           0x08
+       bit     BRDRW           0x04
+       bit     BRDCTL1         0x02
+       bit     BRDCTL0         0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device.  In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device.  When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM.  When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.  
+ *
+ * After successful arbitration for the memory port, the SEECS bit of 
+ * the SEECTL register is connected to the chip select.  The SEECK, 
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in 
+ * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
+ * gives us an 800 nsec timer.  After a write to the SEECTL register, 
+ * the SEERDY goes high 800 nsec later.  The one exception to this is 
+ * when we first request access to the memory port.  The SEERDY goes 
+ * high to signify that access has been granted and, for this case, has 
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to 
+ * read the serial EEPROM.
+ */
+register SEECTL {
+       address                 0x01e
+       bit     EXTARBACK       0x80
+       bit     EXTARBREQ       0x40
+       bit     SEEMS           0x20
+       bit     SEERDY          0x10
+       bit     SEECS           0x08
+       bit     SEECK           0x04
+       bit     SEEDO           0x02
+       bit     SEEDI           0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, 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).  The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space.  This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+       address                 0x020
+
+       /*
+        * 1 byte per target starting at this address for configuration values
+        */
+       TARG_SCRATCH {
+               size            16
+       }
+       ULTRA_ENB {
+               size            2
+       }
+       /*
+        * Bit vector of targets that have disconnection disabled.
+        */
+       DISC_DSB {
+               size            2
+       }
+       /*
+        * Length of pending message
+        */
+       MSG_LEN {
+               size            1
+       }
+       /* We reserve 8bytes to store outgoing messages */
+       MSG_OUT {
+               size            8
+       }
+       /* Parameters for DMA Logic */
+       DMAPARAMS {
+               size            1
+               bit     WIDEODD         0x40
+               bit     SCSIEN          0x20
+               bit     SDMAEN          0x10
+               bit     SDMAENACK       0x10
+               bit     HDMAEN          0x08
+               bit     HDMAENACK       0x08
+               bit     DIRECTION       0x04
+               bit     FIFOFLUSH       0x02
+               bit     FIFORESET       0x01
+       }
+       /*
+        * Number of SCBs supported by
+        * this card.
+        */
+       SCBCOUNT {
+               size            1
+       }
+       /*
+        * Two's complement of SCBCOUNT
+        */
+       COMP_SCBCOUNT {
+               size            1
+       }
+       /*
+        * Mask of bits to test against
+        * when looking at the Queue Count
+        * registers.  Works around a bug
+        * on aic7850 chips. 
+        */
+       QCNTMASK {
+               size            1
+       }
+       SEQ_FLAGS {
+               size            1
+               bit     RESELECTED      0x80
+               bit     IDENTIFY_SEEN   0x40
+               bit     TAGGED_SCB      0x20
+               bit     DPHASE          0x10
+               bit     PAGESCBS        0x04
+               bit     WIDE_BUS        0x02
+               bit     TWIN_BUS        0x01
+       }
+       /*
+        * Temporary storage for the
+        * target/channel/lun of a
+        * reconnecting target
+        */
+       SAVED_TCL {
+               size            1
+       }
+       SG_COUNT {
+               size            1
+       }
+       /* working value of SG pointer */
+       SG_NEXT {
+               size            4
+       }
+       /*
+        * head of list of SCBs awaiting
+        * selection
+        */
+       WAITING_SCBH {
+               size            1
+       }
+       SAVED_LINKPTR {
+               size            1
+       }
+       SAVED_SCBPTR {
+               size            1
+       }
+       /*
+        * The sequencer will stick the frist byte of any rejected message here
+        * so we can see what is getting thrown away.
+        */
+       REJBYTE {
+               size            1
+       }
+       /*
+        * The last bus phase as seen by the sequencer. 
+        */
+       LASTPHASE {
+               size            1
+               bit     CDI             0x80
+               bit     IOI             0x40
+               bit     MSGI            0x20
+               mask    PHASE_MASK      CDI|IOI|MSGI
+               mask    P_DATAOUT       0x00
+               mask    P_DATAIN        IOI
+               mask    P_COMMAND       CDI
+               mask    P_MESGOUT       CDI|MSGI
+               mask    P_STATUS        CDI|IOI
+               mask    P_MESGIN        CDI|IOI|MSGI
+               mask    P_BUSFREE       0x01
+       }
+       MSGIN_EXT_LEN {
+               size            1
+       }
+       MSGIN_EXT_OPCODE {
+               size            1
+       }
+       /*
+        * location 3, stores the last
+        * byte of an extended message if
+        * it passes the two bytes of space
+        * we allow now.  This byte isn't
+        * used for anything, it just makes
+        * the code shorter for tossing
+        * extra bytes.
+        */
+       MSGIN_EXT_BYTES {
+               size            3
+       }
+       /*
+        * head of list of SCBs that are
+        * disconnected.  Used for SCB
+        * paging.
+        */
+       DISCONNECTED_SCBH {
+               size            1
+       }
+       /*
+        * head of list of SCBs that are
+        * not in use.  Used for SCB paging.
+        */
+       FREE_SCBH {
+               size            1
+       }
+       HSCB_ADDR {
+               size            4
+       }
+       CUR_SCBID {
+               size            1
+       }
+       ARG_1 {
+               size            1
+               mask    SEND_MSG        0x80
+               mask    SEND_SENSE      0x40
+               mask    SEND_REJ        0x20
+               alias   RETURN_1
+       }
+       /*
+        * These are reserved registers in the card's scratch ram.  Some of
+        * the values are specified in the AHA2742 technical reference manual
+        * and are initialized by the BIOS at boot time.
+        */
+       SCSICONF {
+               address         0x05a
+               size            1
+               bit     RESET_SCSI      0x40
+       }
+       HOSTCONF {
+               address         0x05d
+               size            1
+       }
+       HA_274_BIOSCTRL {
+               address         0x05f
+               size            1
+               mask    BIOSMODE                0x30
+               mask    BIOSDISABLED            0x30    
+               bit     CHANNEL_B_PRIMARY       0x08
+       }
+}
+
+const SCB_LIST_NULL    0xff
+
+
+/* WDTR Message values */
+const BUS_8_BIT                0x00
+const BUS_16_BIT               0x01
+const BUS_32_BIT               0x02
+const MAX_OFFSET_8BIT          0x0f
+const MAX_OFFSET_16BIT 0x08
diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq
new file mode 100644 (file)
index 0000000..a5722f1
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
+ */
+
+#include <aic7xxx.reg>
+#include <scsi_message.h>
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration.  The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time.  This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection.  The solution used here is to 
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
+ * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued.  The sequencer will
+ * automatically consume the entries.
+ */
+
+/*
+ * We assume that the kernel driver may reset us at any time, even in the
+ * middle of a DMA, so clear DFCNTRL too.
+ */
+reset:
+       clr     SCSISIGO;               /* De-assert BSY */
+       /* Always allow reselection */
+       mvi     SCSISEQ, ENRSELI|ENAUTOATNP;
+       call    clear_target_state;
+poll_for_work:
+       test    SSTAT0,SELDO    jnz select;
+       test    SSTAT0,SELDI    jnz reselect;
+       test    SCSISEQ, ENSELO jnz poll_for_work;
+.if ( TWIN_CHANNEL )
+       /*
+        * Twin channel devices cannot handle things like SELTO
+        * interrupts on the "background" channel.  So, if we
+        * are selecting, keep polling the current channel util
+        * either a selection or reselection occurs.
+        */
+       xor     SBLKCTL,SELBUSB;        /* Toggle to the other bus */
+       test    SSTAT0,SELDO    jnz select;
+       test    SSTAT0,SELDI    jnz reselect;
+       test    SCSISEQ, ENSELO jnz poll_for_work;
+       xor     SBLKCTL,SELBUSB;        /* Toggle back */
+.endif
+       cmp     WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+test_queue:
+       /* Has the driver posted any work for us? */
+       mov     A, QCNTMASK;
+       test    QINCNT,A        jz poll_for_work;
+
+/*
+ * We have at least one queued SCB now and we don't have any 
+ * SCBs in the list of SCBs awaiting selection.  If we have
+ * any SCBs available for use, pull the tag from the QINFIFO
+ * and get to work on it.
+ */
+.if ( SCB_PAGING )
+       mov     ALLZEROS        call    get_free_or_disc_scb;
+       cmp     SINDEX, SCB_LIST_NULL   je poll_for_work;
+.endif
+dequeue_scb:
+       mov     CUR_SCBID,QINFIFO;
+.if !( SCB_PAGING )
+       /* In the non-paging case, the SCBID == hardware SCB index */
+       mov     SCBPTR, CUR_SCBID;
+.endif
+dma_queued_scb:
+/*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+       mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+       mov     CUR_SCBID       call dma_scb;
+
+/*
+ * See if there is not already an active SCB for this target.  This code
+ * locks out on a per target basis instead of target/lun.  Although this
+ * is not ideal for devices that have multiple luns active at the same
+ * time, it is faster than looping through all SCB's looking for active
+ * commands.  We also don't have enough spare SCB space for us to store the
+ * SCBID of the currently busy transaction for each target/lun making it
+ * impossible to link up the SCBs.
+ */
+test_busy:
+       test    SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb;
+       mvi     SEQCTL, PAUSEDIS|FASTMODE;
+       mov     SAVED_SCBPTR, SCBPTR;
+       mov     SCB_TCL         call    index_untagged_scb;
+       mov     ARG_1, SINDIR;                  /*
+                                                * ARG_1 should
+                                                * now have the SCB ID of
+                                                * any active, non-tagged,
+                                                * command for this target.
+                                                */
+       cmp     ARG_1, SCB_LIST_NULL je make_busy;
+.if ( SCB_PAGING )
+       /*
+        * Put this SCB back onto the free list.  It
+        * may be necessary to satisfy the search for
+        * the active SCB.
+        */
+       mov     SCBPTR, SAVED_SCBPTR;
+       call    add_scb_to_free_list;
+       /* Find the active SCB */
+       mov     ALLZEROS        call findSCB;
+       /*
+        * If we couldn't find it, tell the kernel.  This should
+        * never happen.
+        */
+       cmp     SINDEX, SCB_LIST_NULL   jne paged_busy_link;
+       mvi     INTSTAT, NO_MATCH_BUSY;
+paged_busy_link:
+       /* Link us in */
+       mov     SCB_LINKED_NEXT, CUR_SCBID;
+       /* Put it back on the disconnected list */
+       call    add_scb_to_disc_list;
+       mvi     SEQCTL, FASTMODE;
+       jmp     poll_for_work;
+.else
+simple_busy_link:
+       mov     SCBPTR, ARG_1;
+       mov     SCB_LINKED_NEXT, CUR_SCBID;
+       mvi     SEQCTL, FASTMODE;
+       jmp     poll_for_work;
+.endif
+make_busy:
+       mov     DINDIR, CUR_SCBID;
+       mov     SCBPTR, SAVED_SCBPTR;
+       mvi     SEQCTL, FASTMODE;
+
+start_scb:
+       /*
+        * Place us on the waiting list in case our selection
+        * doesn't win during bus arbitration.
+        */
+       mov     SCB_NEXT,WAITING_SCBH;
+       mov     WAITING_SCBH, SCBPTR;
+start_waiting:
+       /*
+        * Pull the first entry off of the waiting SCB list
+        * We don't have to "test_busy" because only transactions that
+        * have passed that test can be in the WAITING_SCB list.
+        */
+       mov     SCBPTR, WAITING_SCBH;
+       call    start_selection;
+       jmp     poll_for_work;
+
+start_selection:
+.if ( TWIN_CHANNEL )
+       and     SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
+       and     A,SELBUSB,SCB_TCL;      /* Get new channel bit */
+       or      SINDEX,A;
+       mov     SBLKCTL,SINDEX;         /* select channel */
+.endif
+initialize_scsiid:
+       and     A, TID, SCB_TCL;        /* Get target ID */
+       and     SCSIID, OID;            /* Clear old target */
+       or      SCSIID, A;
+       mvi     SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+reselect:
+       clr     MSG_LEN;        /* Don't have anything in the mesg buffer */
+       mvi     CLRSINT0, CLRSELDI;
+       /* XXX test for and handle ONE BIT condition */
+       and     SAVED_TCL, SELID_MASK, SELID;
+       or      SEQ_FLAGS,RESELECTED;
+       jmp     select2;
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list.  This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH.  Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select:
+       /* Turn off the selection hardware */
+       mvi     SCSISEQ, ENRSELI|ENAUTOATNP;    /*
+                                                * ATN on parity errors
+                                                * for "in" phases
+                                                */
+       mvi     CLRSINT0, CLRSELDO;
+       mov     SCBPTR, WAITING_SCBH;
+       mov     WAITING_SCBH,SCB_NEXT;
+       mov     SAVED_TCL, SCB_TCL;
+/*
+ * 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.
+ *
+ * Messages are stored in scratch RAM starting with a length byte
+ * followed by the message itself.
+ */
+
+mk_identify:
+       and     MSG_OUT,0x7,SCB_TCL;    /* lun */
+       and     A,DISCENB,SCB_CONTROL;  /* mask off disconnect privledge */
+       or      MSG_OUT,A;              /* or in disconnect privledge */
+       or      MSG_OUT,MSG_IDENTIFYFLAG;
+       mvi     MSG_LEN, 1;
+
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+mk_tag:
+       test    SCB_CONTROL,TAG_ENB jz  mk_message;
+       and     MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+       mov     MSG_OUT[2],SCB_TAG;
+       add     MSG_LEN,2;      /* update message length */
+
+/*
+ * Interrupt the driver, and allow it to tweak the message buffer
+ * if it asks.
+ */
+mk_message:
+       test    SCB_CONTROL,MK_MESSAGE  jz select2;
+       mvi     INTSTAT,AWAITING_MSG;
+
+select2:
+       mvi     CLRSINT1,CLRBUSFREE;
+       or      SIMODE1, ENBUSFREE;             /*
+                                                * We aren't expecting a
+                                                * bus free, so interrupt
+                                                * the kernel driver if it
+                                                * happens.
+                                                */
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ */
+       or      SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN;
+.if ( ULTRA )
+ultra:
+       mvi     SINDEX, ULTRA_ENB+1;
+       test    SAVED_TCL, 0x80         jnz ultra_2;    /* Target ID > 7 */
+       dec     SINDEX;
+ultra_2:
+       mov     FUNCTION1,SAVED_TCL;
+       mov     A,FUNCTION1;
+       test    SINDIR, A       jz ndx_dtr;
+       or      SXFRCTL0, FAST20;
+.endif
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCRATCH.
+ */
+ndx_dtr:
+       shr     A,4,SAVED_TCL;
+       test    SBLKCTL,SELBUSB jz ndx_dtr_2;
+       or      SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
+       or      A,0x08;                 /* Channel B entries add 8 */
+ndx_dtr_2:
+       add     SINDEX,TARG_SCRATCH,A;
+       mov     SCSIRATE,SINDIR;
+
+
+/*
+ * 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.
+ *
+ */
+ITloop:
+       test    SSTAT1,REQINIT          jz ITloop;
+       test    SSTAT1, SCSIPERR        jnz ITloop;
+
+       and     A,PHASE_MASK,SCSISIGI;
+       mov     LASTPHASE,A;
+       mov     SCSISIGO,A;
+
+       cmp     ALLZEROS,A      je p_dataout;
+       cmp     A,P_DATAIN      je p_datain;
+       cmp     A,P_COMMAND     je p_command;
+       cmp     A,P_MESGOUT     je p_mesgout;
+       cmp     A,P_STATUS      je p_status;
+       cmp     A,P_MESGIN      je p_mesgin;
+
+       mvi     INTSTAT,BAD_PHASE;      /* unknown phase - signal driver */
+       jmp     ITloop;                 /* Try reading the bus again. */
+
+await_busfree:
+       and     SIMODE1, ~ENBUSFREE;
+       call    clear_target_state;
+       mov     NONE, SCSIDATL;         /* Ack the last byte */
+       test    SSTAT1,REQINIT|BUSFREE  jz .;
+       test    SSTAT1, BUSFREE jnz poll_for_work;
+       mvi     INTSTAT, BAD_PHASE;
+       
+clear_target_state:
+       clr     DFCNTRL;
+       clr     SCSIRATE;               /*
+                                        * We don't know the target we will
+                                        * connect to, so default to narrow
+                                        * transfers to avoid parity problems.
+                                        */
+       and     SXFRCTL0, ~FAST20;      
+       mvi     LASTPHASE, P_BUSFREE;
+       /* clear target specific flags */
+       and     SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret;
+
+p_dataout:
+       mvi     DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET;
+       jmp     data_phase_init;
+
+/*
+ * If we re-enter the data phase after going through another phase, the
+ * STCNT may have been cleared, so restore it from the residual field.
+ */
+data_phase_reinit:
+       mvi     DINDEX, STCNT;
+       mvi     SCB_RESID_DCNT  call bcopy_3;
+       jmp     data_phase_loop;
+
+p_datain:
+       mvi     DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+data_phase_init:
+       call    assert;                 /*
+                                        * Ensure entering a data
+                                        * phase is okay - seen identify, etc.
+                                        */
+
+       test    SEQ_FLAGS, DPHASE       jnz data_phase_reinit;
+
+       /*
+        * Initialize the DMA address and counter from the SCB.
+        * Also set SG_COUNT and SG_NEXT in memory since we cannot
+        * modify the values in the SCB itself until we see a
+        * save data pointers message.
+        */
+       mvi     DINDEX, HADDR;
+       mvi     SCB_DATAPTR     call bcopy_7;
+
+       call    set_stcnt_from_hcnt;
+
+       mov     SG_COUNT,SCB_SGCOUNT;
+
+       mvi     DINDEX, SG_NEXT;
+       mvi     SCB_SGPTR       call bcopy_4;
+
+data_phase_loop:
+/* Guard against overruns */
+       test    SG_COUNT, 0xff jnz data_phase_inbounds;
+/*
+ * Turn on 'Bit Bucket' mode, set the transfer count to
+ * 16meg and let the target run until it changes phase.
+ * When the transfer completes, notify the host that we
+ * had an overrun.
+ */
+       or      SXFRCTL1,BITBUCKET;
+       mvi     HCNT[0], 0xff;
+       mvi     HCNT[1], 0xff;
+       mvi     HCNT[2], 0xff;
+       call    set_stcnt_from_hcnt;
+
+data_phase_inbounds:
+/* If we are the last SG block, ensure wideodd is off. */
+       cmp     SG_COUNT,0x01 jne data_phase_wideodd;
+       and     DMAPARAMS, ~WIDEODD;
+data_phase_wideodd:
+       mov     DMAPARAMS  call dma;
+
+/* Go tell the host about any overruns */
+       test    SXFRCTL1,BITBUCKET jnz data_phase_overrun;
+
+/* Exit if we had an underrun.  dma clears SINDEX in this case. */
+       test    SINDEX,0xff     jz data_phase_finish;
+
+/*
+ * Advance the scatter-gather pointers if needed 
+ */
+sg_advance:
+       dec     SG_COUNT;       /* one less segment to go */
+
+       test    SG_COUNT, 0xff  jz data_phase_finish; /* Are we done? */
+
+       clr     A;                      /* add sizeof(struct scatter) */
+       add     SG_NEXT[0],SG_SIZEOF;
+       adc     SG_NEXT[1],A;
+
+/*
+ * Load a struct scatter 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 all DMA's, assumes little-endian host data storage.
+ */
+sg_load:
+       mvi     DINDEX, HADDR;
+       mvi     SG_NEXT call bcopy_4;
+
+       mvi     HCNT[0],SG_SIZEOF;
+       clr     HCNT[1];
+       clr     HCNT[2];
+
+       or      DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+       call    dma_finish;
+
+/*
+ * Copy data from FIFO into SCB data pointer and data count.  This assumes
+ * that the SG segments are of the form:
+ *
+ * struct ahc_dma_seg {
+ *     u_int32_t       addr;           four bytes, little-endian order
+ *     u_int32_t       len;            four bytes, little endian order
+ * };
+ */
+       mvi     HADDR   call dfdat_in_7;
+
+/* Load STCNT as well.  It is a mirror of HCNT */
+       call    set_stcnt_from_hcnt;
+       test    SSTAT1,PHASEMIS jz data_phase_loop;
+
+data_phase_finish:
+/*
+ * After a DMA finishes, save the SG and STCNT residuals back into the SCB
+ * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
+ * were transferred on the SCSI (as opposed to the host) bus.
+ */
+       mov     SCB_RESID_DCNT[0],STCNT[0];
+       mov     SCB_RESID_DCNT[1],STCNT[1];
+       mov     SCB_RESID_DCNT[2],STCNT[2];
+       mov     SCB_RESID_SGCNT, SG_COUNT;
+
+       /* We have seen a data phase */
+       or      SEQ_FLAGS, DPHASE;
+
+       jmp     ITloop;
+
+data_phase_overrun:
+/*
+ * Turn off BITBUCKET mode and notify the host
+ */
+       and     SXFRCTL1, ~BITBUCKET;
+       mvi     INTSTAT,DATA_OVERRUN;
+       jmp     ITloop;
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+       call    assert;
+
+/*
+ * Load HADDR and HCNT.
+ */
+       mvi     DINDEX, HADDR;
+       mvi     SCB_CMDPTR      call bcopy_5;
+       clr     HCNT[1];
+       clr     HCNT[2];
+
+       call    set_stcnt_from_hcnt;
+
+       mvi     (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+       jmp     ITloop;
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+       call    assert;
+
+       mov     SCB_TARGET_STATUS, SCSIDATL;
+       jmp     ITloop;
+
+/*
+ * Message out phase.  If there is not an active message, but the target
+ * took us into this phase anyway, build a no-op message and send it.
+ */
+p_mesgout:
+       test    MSG_LEN, 0xff   jnz  p_mesgout_start;
+       mvi     MSG_NOOP        call mk_mesg;   /* build NOP message */
+p_mesgout_start:
+/*
+ * Set up automatic PIO transfer from MSG_OUT.  Bit 3 in
+ * SXFRCTL0 (SPIOEN) is already on.
+ */
+       mvi     SINDEX,MSG_OUT;
+       mov     DINDEX,MSG_LEN;
+
+/*
+ * 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.
+ * ATN must be dropped *at least* 90ns before we ack the last byte, so
+ * the code is aranged to execute two instructions before the byte is
+ * transferred to give a good margin of safety
+ *
+ * Keep an eye out for a phase change, in case the target issues
+ * a MESSAGE REJECT.
+ */
+p_mesgout_loop:
+       test    SSTAT1, REQINIT         jz p_mesgout_loop;
+       test    SSTAT1, SCSIPERR        jnz p_mesgout_loop;
+       and     LASTPHASE, PHASE_MASK, SCSISIGI;
+       cmp     LASTPHASE, P_MESGOUT jne p_mesgout_done;
+p_mesgout_testretry:
+       test    DINDEX,0xff     jnz p_mesgout_dropatn;
+       or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+       jmp     p_mesgout_start;
+/*
+ * 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_mesgout_dropatn:
+       cmp     DINDEX,1        jne p_mesgout_outb;     /* last byte? */
+       mvi     CLRSINT1,CLRATNO;                       /* drop ATN */
+p_mesgout_outb:
+       dec     DINDEX;
+       mov     SCSIDATL,SINDIR;
+       jmp     p_mesgout_loop;
+
+p_mesgout_done:
+       mvi     CLRSINT1,CLRATNO;       /* Be sure to turn ATNO off */
+       clr     MSG_LEN;                /* no active msg */
+       jmp     ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+       mvi     ACCUM           call inb_first; /* read the 1st message byte */
+       mov     REJBYTE,A;                      /* save it for the driver */
+
+       test    A,MSG_IDENTIFYFLAG      jnz mesgin_identify;
+       cmp     A,MSG_DISCONNECT        je mesgin_disconnect;
+       cmp     A,MSG_SAVEDATAPOINTER   je mesgin_sdptrs;
+       cmp     ALLZEROS,A              je mesgin_complete;
+       cmp     A,MSG_RESTOREPOINTERS   je mesgin_rdptrs;
+       cmp     A,MSG_EXTENDED          je mesgin_extended;
+       cmp     A,MSG_MESSAGE_REJECT    je mesgin_reject;
+       cmp     A,MSG_NOOP              je mesgin_done;
+
+rej_mesgin:
+/*
+ * We have no idea what this message in is, so we issue a message reject
+ * and hope for the best.  In any case, rejection should be a rare
+ * occurrence - signal the driver when it happens.
+ */
+       mvi     INTSTAT,SEND_REJECT;            /* let driver know */
+
+       mvi     MSG_MESSAGE_REJECT      call mk_mesg;
+
+mesgin_done:
+       mov     NONE,SCSIDATL;          /*dummy read from latch to ACK*/
+       jmp     ITloop;
+
+
+mesgin_complete:
+/*
+ * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt.  Before doing so, check to see if there
+ * is a residual or the status byte is something other than NO_ERROR (0).  In
+ * either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command and set
+ * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
+ * the SCB, and process it as the next command by adding it to the waiting list.
+ * If the kernel driver does not wish to request sense, it need only clear
+ * RETURN_1, and the command is allowed to complete normally.  We don't bother
+ * to post to the QOUTFIFO in the error cases since it would require extra
+ * work in the kernel driver to ensure that the entry was removed before the
+ * command complete code tried processing it.
+ */
+
+/*
+ * First check for residuals
+ */
+       test    SCB_RESID_SGCNT,0xff    jnz upload_scb;
+       test    SCB_TARGET_STATUS,0xff  jz status_ok;   /* Good Status? */
+upload_scb:
+       mvi     DMAPARAMS, FIFORESET;
+       mov     SCB_TAG         call dma_scb;
+check_status:
+       test    SCB_TARGET_STATUS,0xff  jz status_ok;   /* Just a residual? */
+       mvi     INTSTAT,BAD_STATUS;                     /* let driver know */
+       cmp     RETURN_1, SEND_SENSE    jne status_ok;
+       /* This SCB becomes the next to execute as it will retrieve sense */
+       mov     SCB_LINKED_NEXT, SCB_TAG;
+       jmp     dma_next_scb;
+
+status_ok:
+/* First, mark this target as free. */
+       test    SCB_CONTROL,TAG_ENB jnz complete;       /*
+                                                        * Tagged commands
+                                                        * don't busy the
+                                                        * target.
+                                                        */
+       mov     SAVED_SCBPTR, SCBPTR;
+       mov     SAVED_LINKPTR, SCB_LINKED_NEXT;
+       mov     SCB_TCL call index_untagged_scb;
+       mov     DINDIR, SAVED_LINKPTR;
+       mov     SCBPTR, SAVED_SCBPTR;
+
+complete:
+       /* Post the SCB and issue an interrupt */
+       mov     QOUTFIFO,SCB_TAG;
+       mvi     INTSTAT,CMDCMPLT;
+       test    SCB_CONTROL, ABORT_SCB jz dma_next_scb;
+       mvi     INTSTAT, ABORT_CMDCMPLT;
+
+dma_next_scb:
+       cmp     SCB_LINKED_NEXT, SCB_LIST_NULL  je add_to_free_list;
+.if !( SCB_PAGING )
+       /* Only DMA on top of ourselves if we are the SCB to download */
+       mov     A, SCB_LINKED_NEXT;
+       cmp     SCB_TAG, A      je dma_next_scb2;
+       call    add_scb_to_free_list;
+       mov     SCBPTR, A;
+       jmp     add_to_waiting_list;
+.endif
+dma_next_scb2:
+       mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+       mov     SCB_LINKED_NEXT         call dma_scb;
+add_to_waiting_list:
+       mov     SCB_NEXT,WAITING_SCBH;
+       mov     WAITING_SCBH, SCBPTR;
+       /*
+        * Prepare our selection hardware before the busfree so we have a
+        * high probability of winning arbitration.
+        */
+       call    start_selection;
+       jmp     await_busfree;
+add_to_free_list:
+       call    add_scb_to_free_list;
+       jmp     await_busfree;
+
+/*
+ * Is it an extended message?  Copy the message to our message buffer and
+ * notify the host.  The host will tell us whether to reject this message,
+ * respond to it with the message that the host placed in our message buffer,
+ * or simply to do nothing.
+ */
+mesgin_extended:
+       mvi     MSGIN_EXT_LEN    call inb_next;
+       mov     A, MSGIN_EXT_LEN;
+mesgin_extended_loop:
+       mov     DINDEX  call    inb_next;
+       dec     A;
+       cmp     DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test;
+       dec     DINDEX;         /* dump by repeatedly filling the last byte */
+mesgin_extended_loop_test:
+       test    A, 0xFF         jnz mesgin_extended_loop;
+mesgin_extended_intr:
+       mvi     INTSTAT,EXTENDED_MSG;           /* let driver know */
+       cmp     RETURN_1,SEND_REJ je rej_mesgin;
+       cmp     RETURN_1,SEND_MSG jne mesgin_done;
+/* The kernel has setup a message to be sent */
+       or      SCSISIGO,ATNO,LASTPHASE;        /* turn on ATNO */
+       jmp     mesgin_done;
+
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.
+ */
+mesgin_disconnect:
+       or      SCB_CONTROL,DISCONNECTED;
+.if ( SCB_PAGING )
+       call    add_scb_to_disc_list;
+.endif
+       jmp     await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ */
+mesgin_sdptrs:
+       test    SEQ_FLAGS, DPHASE       jz mesgin_done;
+       mov     SCB_SGCOUNT,SG_COUNT;
+
+       /* The SCB SGPTR becomes the next one we'll download */
+       mvi     DINDEX, SCB_SGPTR;
+       mvi     SG_NEXT call bcopy_4;
+       
+       /* The SCB DATAPTR0 becomes the current SHADDR */
+       mvi     DINDEX, SCB_DATAPTR;
+       mvi     SHADDR          call bcopy_4;
+
+/*
+ * Use the residual number since STCNT is corrupted by any message transfer.
+ */
+       mvi     SCB_RESID_DCNT  call    bcopy_3;
+
+       jmp     mesgin_done;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+       and     SEQ_FLAGS, ~DPHASE;             /*
+                                                * We'll reload them
+                                                * the next time through
+                                                * the dataphase.
+                                                */
+       jmp     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.
+ */
+mesgin_identify:
+       test    A,0x78  jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/
+       and     A,0x07;                 /* lun in lower three bits */
+       or      SAVED_TCL,A;            /* SAVED_TCL should be complete now */
+       mov     SAVED_TCL call index_untagged_scb;
+       mov     ARG_1, SINDIR;
+.if ( SCB_PAGING )
+       cmp     ARG_1,SCB_LIST_NULL     jne use_findSCB;
+.else
+       cmp     ARG_1,SCB_LIST_NULL     je snoop_tag;
+       /* Directly index the SCB */
+       mov     SCBPTR,ARG_1;
+       test    SCB_CONTROL,DISCONNECTED jz not_found;
+       jmp     setup_SCB;
+.endif
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  With SCB paging, this requires using findSCB for both tagged
+ * and non-tagged transactions since the SCB may exist in any slot.
+ * If we're not using SCB paging, we can use the tag as the direct
+ * index to the SCB.
+ */
+snoop_tag:
+       mov     NONE,SCSIDATL;          /* ACK Identify MSG */
+snoop_tag_loop:
+       test    SSTAT1,REQINIT          jz snoop_tag_loop;
+       test    SSTAT1, SCSIPERR        jnz snoop_tag_loop;
+       and     LASTPHASE, PHASE_MASK, SCSISIGI;
+       cmp     LASTPHASE, P_MESGIN     jne not_found;
+       cmp     SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+       or      SEQ_FLAGS, TAGGED_SCB;
+       mvi     ARG_1   call inb_next;  /* tag value */
+/*
+ * See if the tag is in range.  The tag is < SCBCOUNT if we add
+ * the complement of SCBCOUNT to the incomming tag and there is
+ * no carry.
+ */
+       mov     A,COMP_SCBCOUNT;
+       add     SINDEX,A,ARG_1;
+       jc      not_found;
+
+.if ! ( SCB_PAGING )
+index_by_tag:
+       mov     SCBPTR,ARG_1;
+       mov     A, SAVED_TCL;
+       cmp     SCB_TCL,A               jne not_found;
+       test    SCB_CONTROL,TAG_ENB     jz  not_found;
+       test    SCB_CONTROL,DISCONNECTED jz not_found;
+.else
+/*
+ * Ensure that the SCB the tag points to is for an SCB transaction
+ * to the reconnecting target.
+ */
+use_findSCB:
+       mov     ALLZEROS        call findSCB;     /* Have to search */
+       cmp     SINDEX, SCB_LIST_NULL   je not_found;
+.endif
+setup_SCB:
+       and     SCB_CONTROL,~DISCONNECTED;
+       or      SEQ_FLAGS,IDENTIFY_SEEN;          /* make note of IDENTIFY */
+       jmp     mesgin_done;
+
+not_found:
+       mvi     INTSTAT, NO_MATCH;
+       mvi     MSG_BUS_DEV_RESET       call mk_mesg;
+       jmp     mesgin_done;
+
+/*
+ * Message reject?  Let the kernel driver handle this.  If we have an 
+ * outstanding WDTR or SDTR negotiation, assume that it's a response from 
+ * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
+ * it since we have no clue what it pertains to.
+ */
+mesgin_reject:
+       mvi     INTSTAT, REJECT_MSG;
+       jmp     mesgin_done;
+
+/*
+ * [ ADD MORE MESSAGE HANDLING HERE ]
+ */
+
+/*
+ * 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, PAUSEDIS|FASTMODE;
+       test    MSG_LEN,0xff    jz mk_mesg1;    /* Should always succeed */
+       
+       /*
+        * Hmmm.  For some reason the mesg buffer is in use.
+        * Tell the driver.  It should look at SINDEX to find
+        * out what we wanted to use the buffer for and resolve
+        * the conflict.
+        */
+       mvi     SEQCTL,FASTMODE;
+       mvi     INTSTAT,MSG_BUFFER_BUSY;
+
+mk_mesg1:
+       or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
+       mvi     MSG_LEN,1;              /* length = 1 */
+       mov     MSG_OUT,SINDEX;         /* 1-byte message */
+       mvi     SEQCTL,FASTMODE ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * 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_next:
+       mov     NONE,SCSIDATL;          /*dummy read from latch to ACK*/
+inb_next_wait:
+       /*
+        * If there is a parity error, wait for the kernel to
+        * see the interrupt and prepare our message response
+        * before continuing.
+        */
+       test    SSTAT1, REQINIT jz inb_next_wait;
+       test    SSTAT1, SCSIPERR jnz inb_next_wait;
+       and     LASTPHASE, PHASE_MASK, SCSISIGI;
+       cmp     LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+       mov     DINDEX,SINDEX;
+       mov     DINDIR,SCSIBUSL ret;            /*read byte directly from bus*/
+inb_last:
+       mov     NONE,SCSIDATL ret;              /*dummy read from latch to ACK*/
+
+mesgin_phasemis:
+/*
+ * We expected to receive another byte, but the target changed phase
+ */
+       mvi     INTSTAT, MSGIN_PHASEMIS;
+       jmp     ITloop;
+
+/*
+ * 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;
+dma_loop:
+       test    SSTAT0,DMADONE  jnz dma_dmadone;
+       test    SSTAT1,PHASEMIS jz dma_loop;    /* ie. underrun */
+dma_phasemis:
+       test    SSTAT0,SDONE    jnz dma_checkfifo;
+       mov     SINDEX,ALLZEROS;                /* Notify caller of phasemiss */
+
+/*
+ * 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, the data FIFO should be flushed auto-
+ * magically on STCNT=0 or a phase change, so just wait for FIFO empty
+ * status.
+ */
+dma_checkfifo:
+       test    DFCNTRL,DIRECTION       jnz dma_fifoempty;
+dma_fifoflush:
+       test    DFSTATUS,FIFOEMP        jz dma_fifoflush;
+
+dma_fifoempty:
+       /* Don't clobber an inprogress host data transfer */
+       test    DFSTATUS, MREQPEND      jnz dma_fifoempty;
+/*
+ * Now shut the DMA enables off and make sure that the DMA enables are 
+ * actually off first lest we get an ILLSADDR.
+ */
+dma_dmadone:
+       and     DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+       test    DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; 
+return:
+       ret;
+
+/*
+ * Assert that if we've been reselected, then we've seen an IDENTIFY
+ * message.
+ */
+assert:
+       test    SEQ_FLAGS,RESELECTED    jz return;      /* reselected? */
+       test    SEQ_FLAGS,IDENTIFY_SEEN jnz return;     /* seen IDENTIFY? */
+
+       mvi     INTSTAT,NO_IDENT        ret;    /* no - tell the kernel */
+
+.if ( SCB_PAGING )
+/*
+ * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
+ * or by the SCBIDn ARG_1.  The search begins at the SCB index passed in
+ * via SINDEX.  If the SCB cannot be found, SINDEX will be SCB_LIST_NULL,
+ * otherwise, SCBPTR is set to the proper SCB.
+ */
+findSCB:
+       mov     SCBPTR,SINDEX;                  /* switch to next SCB */
+       mov     A, ARG_1;                       /* Tag passed in ARG_1 */
+       cmp     SCB_TAG,A       jne findSCB_loop;
+       test    SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/
+findSCB_loop:
+       inc     SINDEX;
+       mov     A,SCBCOUNT;
+       cmp     SINDEX,A        jne findSCB;
+/*
+ * We didn't find it.  If we're paging, pull an SCB and DMA down the
+ * one we want.  If we aren't paging or the SCB we dma down has the
+ * abort flag set, return not found.
+ */
+       mov     ALLZEROS        call    get_free_or_disc_scb;
+       mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+       mov     ARG_1   call dma_scb;
+       test    SCB_RESID_SGCNT, 0xff jz . + 2;
+       or      SCB_CONTROL, MUST_DMAUP_SCB;
+       test    SCB_CONTROL, ABORT_SCB jz return;
+find_error:
+       mvi     SINDEX, SCB_LIST_NULL ret;
+foundSCB:
+       test    SCB_CONTROL, ABORT_SCB jnz find_error;
+rem_scb_from_disc_list:
+/* Remove this SCB from the disconnection list */
+       cmp     SCB_NEXT,SCB_LIST_NULL je unlink_prev;
+       mov     SAVED_LINKPTR, SCB_PREV;
+       mov     SCBPTR, SCB_NEXT;
+       mov     SCB_PREV, SAVED_LINKPTR;
+       mov     SCBPTR, SINDEX;
+unlink_prev:
+       cmp     SCB_PREV,SCB_LIST_NULL  je rHead;/* At the head of the list */
+       mov     SAVED_LINKPTR, SCB_NEXT;
+       mov     SCBPTR, SCB_PREV;
+       mov     SCB_NEXT, SAVED_LINKPTR;
+       mov     SCBPTR, SINDEX ret;
+rHead:
+       mov     DISCONNECTED_SCBH,SCB_NEXT ret;
+.else
+       ret;
+.endif
+
+set_stcnt_from_hcnt:
+       mov     STCNT[0], HCNT[0];
+       mov     STCNT[1], HCNT[1];
+       mov     STCNT[2], HCNT[2] ret;
+
+bcopy_7:
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR;
+bcopy_5:
+       mov     DINDIR, SINDIR;
+bcopy_4:
+       mov     DINDIR, SINDIR;
+bcopy_3:
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR ret;
+
+dma_scb:
+       /*
+        * SCB index is in SINDEX.  Determine the physical address in
+        * the host where this SCB is located and load HADDR with it.
+        */
+       shr     DINDEX, 3, SINDEX;
+       shl     A, 5, SINDEX;
+       add     HADDR[0], A, HSCB_ADDR[0];
+       mov     A, DINDEX;
+       adc     HADDR[1], A, HSCB_ADDR[1];
+       clr     A;
+       adc     HADDR[2], A, HSCB_ADDR[2];
+       adc     HADDR[3], A, HSCB_ADDR[3];
+       /* Setup Count */
+       mvi     HCNT[0], 28;
+       clr     HCNT[1];
+       clr     HCNT[2];
+       mov     DFCNTRL, DMAPARAMS;
+       test    DMAPARAMS, DIRECTION    jnz dma_scb_fromhost;
+       /* Fill it with the SCB data */
+copy_scb_tofifo:
+       mvi     SINDEX, SCB_CONTROL;
+       add     A, 28, SINDEX;
+copy_scb_tofifo_loop:
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       cmp     SINDEX, A jne copy_scb_tofifo_loop;
+       or      DFCNTRL, HDMAEN|FIFOFLUSH;
+dma_scb_fromhost:
+       call    dma_finish;
+       /* If we were putting the SCB, we are done */
+       test    DMAPARAMS, DIRECTION    jz      return;
+       mvi     SCB_CONTROL  call dfdat_in_7;
+       call    dfdat_in_7_continued;
+       call    dfdat_in_7_continued;
+       jmp     dfdat_in_7_continued;
+dfdat_in_7:
+       mov     DINDEX,SINDEX;
+dfdat_in_7_continued:
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT;
+       mov     DINDIR,DFDAT ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+       test    DFSTATUS,HDONE  jz dma_finish;
+       /* Turn off DMA */
+       and     DFCNTRL, ~HDMAEN;
+       test    DFCNTRL, HDMAEN jnz .;
+       ret;
+
+index_untagged_scb:
+       mov     DINDEX, SINDEX;
+       shr     DINDEX, 4;
+       and     DINDEX, 0x03;                   /* Bottom two bits of tid */
+       add     DINDEX, SCB_BUSYTARGETS;
+       shr     A, 6, SINDEX;                   /* Target ID divided by 4 */
+       test    SINDEX, SELBUSB jz index_untagged_scb2;
+       add     A, 2;                           /* Add 2 positions */
+index_untagged_scb2:
+       mov     SCBPTR, A;                      /*
+                                                * Select the SCB with this 
+                                                * target's information.
+                                                */
+       mov     SINDEX, DINDEX  ret;
+
+add_scb_to_free_list:
+       mov     SCB_NEXT, FREE_SCBH;
+       mvi     SCB_TAG, SCB_LIST_NULL;
+       mov     FREE_SCBH, SCBPTR ret;
+
+.if ( SCB_PAGING )
+get_free_or_disc_scb:
+       cmp     FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+       cmp     DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+       mvi     SINDEX, SCB_LIST_NULL   ret;
+dequeue_disc_scb:
+       mov     SCBPTR, DISCONNECTED_SCBH;
+/*
+ * If we have a residual, then we are in the middle of some I/O
+ * and we have to send this SCB back up to the kernel so that the
+ * saved data pointers and residual information isn't lost.
+ */
+       test    SCB_CONTROL, MUST_DMAUP_SCB jz . + 3;
+       and     SCB_CONTROL, ~MUST_DMAUP_SCB;
+       jmp     dma_up_scb;
+       test    SCB_RESID_SGCNT,0xff    jnz dma_up_scb;
+       cmp     SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb;
+dma_up_scb:
+       mvi     DMAPARAMS, FIFORESET;
+       mov     SCB_TAG         call dma_scb;
+unlink_disc_scb:
+       /* jmp instead of call since we want to return anyway */
+       mov     SCBPTR  jmp rem_scb_from_disc_list;
+dequeue_free_scb:
+       mov     SCBPTR, FREE_SCBH;
+       mov     FREE_SCBH, SCB_NEXT ret;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list.  This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+       mvi     SCB_PREV, SCB_LIST_NULL;
+       mov     SCB_NEXT, DISCONNECTED_SCBH;
+       mov     DISCONNECTED_SCBH, SCBPTR;
+       cmp     SCB_NEXT,SCB_LIST_NULL je return;
+       mov     SCBPTR,SCB_NEXT;
+       mov     SCB_PREV,DISCONNECTED_SCBH;
+       mov     SCBPTR,DISCONNECTED_SCBH ret;
+.endif
diff --git a/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644 (file)
index 0000000..267a015
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SCSI messages definitions.
+ */
+
+/* Messages (1 byte) */                     /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE                0x00 /* M/M */
+#define MSG_EXTENDED           0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER    0x02 /* O/O */
+#define MSG_RESTOREPOINTERS    0x03 /* O/O */
+#define MSG_DISCONNECT         0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR  0x05 /* M/M */
+#define MSG_ABORT              0x06 /* O/M */
+#define MSG_MESSAGE_REJECT     0x07 /* M/M */
+#define MSG_NOOP               0x08 /* M/M */
+#define MSG_PARITY_ERROR       0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE  0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET      0x0c /* O/M */
+#define MSG_ABORT_TAG          0x0d /* O/O */
+#define MSG_CLEAR_QUEUE                0x0e /* O/O */
+#define MSG_INIT_RECOVERY      0x0f /* O/O */
+#define MSG_REL_RECOVERY       0x10 /* O/O */
+#define MSG_TERM_IO_PROC       0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG       0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG      0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG      0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE   0x23 /* O/O */
+
+/* Identify message */              /* M/M */  
+#define MSG_IDENTIFYFLAG       0x80 
+#define MSG_IDENTIFY(lun, disc)        (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m)      ((m) & MSG_IDENTIFYFLAG)
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR           0x01
+#define MSG_EXT_SDTR_LEN       0x03
+
+#define MSG_EXT_WDTR           0x03
+#define MSG_EXT_WDTR_LEN       0x02
diff --git a/drivers/scsi/aic7xxx/sequencer.h b/drivers/scsi/aic7xxx/sequencer.h
new file mode 100644 (file)
index 0000000..7112c2e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $
+ */
+
+#if defined(__KERNEL__)
+typedef unsigned char u_int8_t;
+#endif
+
+struct ins_format1 {
+       u_int8_t immediate;
+       u_int8_t source;
+       u_int8_t destination;
+       u_int8_t opcode_ret;
+};
+
+struct ins_format2 {
+       u_int8_t shift_control;
+       u_int8_t source;
+       u_int8_t destination;
+       u_int8_t opcode_ret;
+#define RETURN_BIT 0x01
+};
+
+struct ins_format3 {
+       u_int8_t immediate;
+       u_int8_t source;
+       u_int8_t address;
+       u_int8_t opcode_addr;
+#define ADDR_HIGH_BIT 0x01
+};
+
+struct instruction {
+       union {
+               struct ins_format1 format1;
+               struct ins_format2 format2;
+               struct ins_format3 format3;
+               u_int8_t           bytes[4];
+       } format;
+       u_int   srcline;
+       struct symbol *patch_label;
+       struct {
+               struct instruction *stqe_next; /* next element */
+       } links;
+};
+
+#define        AIC_OP_OR       0x0
+#define        AIC_OP_AND      0x1
+#define AIC_OP_XOR     0x2
+#define        AIC_OP_ADD      0x3
+#define        AIC_OP_ADC      0x4
+#define        AIC_OP_ROL      0x5
+
+#define        AIC_OP_JMP      0x8
+#define AIC_OP_JC      0x9
+#define AIC_OP_JNC     0xa
+#define AIC_OP_CALL    0xb
+#define        AIC_OP_JNE      0xc
+#define        AIC_OP_JNZ      0xd
+#define        AIC_OP_JE       0xe
+#define        AIC_OP_JZ       0xf
+
+/* Pseudo Ops */
+#define        AIC_OP_SHL      0x10
+#define        AIC_OP_SHR      0x20
+#define        AIC_OP_ROR      0x30
diff --git a/drivers/scsi/aic7xxx_asm.c b/drivers/scsi/aic7xxx_asm.c
deleted file mode 100644 (file)
index 544edf0..0000000
+++ /dev/null
@@ -1,734 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx sequencer code assembler.
- *
- * Copyright (c) 1994 John Aycock
- *   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, 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; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Comments are started by `#' and continue to the end of the line; lines
- * may be of the form:
- *      <label>*
- *      <label>*  <undef-sym> = <value>
- *      <label>*  <opcode> <operand>*
- *
- * A <label> is an <undef-sym> ending in a colon.  Spaces, tabs, and commas
- * are token separators.
- *-M*************************************************************************/
-static const char id[] = "$Id: aic7xxx_asm.c,v 3.0 1996/04/16 08:52:23 deang Exp $";
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MEMORY         448
-#define MAXLINE                1024
-#define MAXTOKEN       32
-#define ADOTOUT                "a.out"
-#define NOVALUE                -1
-
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-#define MAX_ARGS       16
-static const char *cpp[] = {
-  "/lib/cpp -P - -",
-  "/usr/lib/cpp -P - -",
-  "/usr/bin/cpp -P - -",
-  "/usr/bin/gcc -E -P -",
-  "/usr/bin/cc -E -P -"
-};
-
-#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-
-/*
- * AIC-7770/AIC-7870 register definitions
- */
-#define R_SINDEX       0x65
-#define R_ALLONES      0x69
-#define R_ALLZEROS     0x6a
-#define R_NONE         0x6a
-
-int debug;
-int lineno, LC;
-char *filename;
-unsigned char M[MEMORY][4];
-
-void 
-error(const char *s)
-{
-       fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
-       exit(EXIT_FAILURE);
-}
-
-void *
-Malloc(size_t size)
-{
-       void *p = malloc(size);
-       if (!p)
-               error("out of memory");
-       return(p);
-}
-
-void *
-Realloc(void *ptr, size_t size)
-{
-       void *p = realloc(ptr, size);
-       if (!p)
-               error("out of memory");
-       return(p);
-}
-
-char *
-Strdup(char *s)
-{
-       char *p = (char *)Malloc(strlen(s) + 1);
-       strcpy(p, s);
-       return(p);
-}
-
-typedef struct sym_t {
-       struct sym_t    *next;          /* MUST BE FIRST */
-       char            *name;
-       int             value;
-       int             npatch; 
-       int             *patch;
-} sym_t;
-
-sym_t *head;
-
-void
-define(char *name, int value)
-{
-       sym_t *p, *q;
-
-       for (p = head, q = (sym_t *)&head; p; p = p->next) {
-               if (!strcmp(p->name, name))
-                       error("redefined symbol");
-               q = p;
-       }
-
-       p = q->next = (sym_t *)Malloc(sizeof(sym_t));
-       p->next = NULL;
-       p->name = Strdup(name);
-       p->value = value;
-       p->npatch = 0;
-       p->patch = NULL;
-
-       if (debug) {
-               fprintf(stderr, "\"%s\" ", p->name);
-               if (p->value != NOVALUE)
-                       fprintf(stderr, "defined as 0x%x\n", p->value);
-               else
-                       fprintf(stderr, "undefined\n");
-       }
-}
-
-sym_t *
-lookup(char *name)
-{
-       sym_t *p;
-
-       for (p = head; p; p = p->next)
-               if (!strcmp(p->name, name))
-                       return(p);
-       return(NULL);
-}
-
-void 
-patch(sym_t *p, int location)
-{
-       p->npatch += 1;
-       p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
-
-       p->patch[p->npatch - 1] = location;
-}
-
-void backpatch(void)
-{
-       int i;
-       sym_t *p;
-
-       for (p = head; p; p = p->next) {
-
-               if (p->value == NOVALUE) {
-                       fprintf(stderr,
-                               "%s: undefined symbol \"%s\"\n",
-                               filename, p->name);
-                       exit(EXIT_FAILURE);
-               }
-
-               if (p->npatch) {
-                       if (debug)
-                               fprintf(stderr,
-                                       "\"%s\" (0x%x) patched at",
-                                       p->name, p->value);
-
-                       for (i = 0; i < p->npatch; i++) {
-                               M[p->patch[i]][0] &= ~1;
-                               M[p->patch[i]][0] |= ((p->value >> 8) & 1);
-                               M[p->patch[i]][1] = p->value & 0xff;
-
-                               if (debug)
-                                       fprintf(stderr, " 0x%x", p->patch[i]);
-                       }
-
-                       if (debug)
-                               fputc('\n', stderr);
-               }
-       }
-}
-
-/*
- *  Output words in byte-reversed order (least significant first)
- *  since the sequencer RAM is loaded that way.
- */
-void
-output(FILE *fp)
-{
-       int i;
-
-       for (i = 0; i < LC; i++)
-               fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
-                       M[i][3],
-                       M[i][2],
-                       M[i][1],
-                       M[i][0]);
-       printf("%d out of %d instructions used.\n", LC, MEMORY);
-}
-
-char **
-getl(int *n)
-{
-       int i;
-       char *p, *quote;
-       static char buf[MAXLINE];
-       static char *a[MAXTOKEN];
-
-       i = 0;
-
-       while (fgets(buf, sizeof(buf), stdin)) {
-
-               lineno += 1;
-
-               if (buf[strlen(buf)-1] != '\n')
-                       error("line too long");
-
-               p = strchr(buf, '#');
-               if (p)
-                       *p = '\0';
-               p = buf;
-rescan:
-               quote = strchr(p, '\"');
-               if (quote)
-                       *quote = '\0';
-               for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
-                       if (i < MAXTOKEN-1)
-                               a[i++] = p;
-                       else
-                               error("too many tokens");
-               if (quote) {
-                       quote++; 
-                       p = strchr(quote, '\"');
-                       if (!p)
-                               error("unterminated string constant");
-                       else if (i < MAXTOKEN-1) {
-                               a[i++] = quote;
-                               *p = '\0';
-                               p++;
-                       }
-                       else
-                               error("too many tokens");
-                       goto rescan;
-               }               
-               if (i) {
-                       *n = i;
-                       return(a);
-               }
-       }
-       return(NULL);
-}
-
-#define A      0x8000          /* `A'ccumulator ok */
-#define I      0x4000          /* use as immediate value */
-#define SL     0x2000          /* shift left */
-#define SR     0x1000          /* shift right */
-#define RL     0x0800          /* rotate left */
-#define RR     0x0400          /* rotate right */
-#define LO     0x8000          /* lookup: ori-{jmp,jc,jnc,call} */
-#define LA     0x4000          /* lookup: and-{jz,jnz} */
-#define LX     0x2000          /* lookup: xor-{je,jne} */
-#define NA     -1              /* not applicable */
-
-struct {
-       const char *name;
-       int n;                  /* number of operands, including opcode */
-       unsigned int op;        /* immediate or L?|pos_from_0 */
-       unsigned int dest;      /* NA, pos_from_0, or I|immediate */
-       unsigned int src;       /* NA, pos_from_0, or I|immediate */
-       unsigned int imm;       /* pos_from_0, A|pos_from_0, or I|immediate */
-       unsigned int addr;      /* NA or pos_from_0 */
-       int fmt;                /* instruction format - 1, 2, or 3 */
-} instr[] = {
-/*
- *               N  OP    DEST         SRC             IMM     ADDR    FMT
- */
-       { "mov",  3, 1,    1,           2,              I|0xff, NA,     1 },
-       { "mov",  4, LO|2, NA,          1,              I|0,    3,      3 },
-       { "mvi",  3, 0,    1,           I|R_ALLZEROS,   A|2,    NA,     1 },
-       { "mvi",  4, LO|2, NA,          I|R_ALLZEROS,   1,      3,      3 },
-       { "not",  2, 2,    1,           1,              I|0xff, NA,     1 },
-       { "and",  3, 1,    1,           1,              A|2,    NA,     1 },
-       { "and",  4, 1,    1,           3,              A|2,    NA,     1 },
-       { "or",   3, 0,    1,           1,              A|2,    NA,     1 },
-       { "or",   4, 0,    1,           3,              A|2,    NA,     1 },
-       { "or",   5, LO|3, NA,          1,              2,      4,      3 },
-       { "xor",  3, 2,    1,           1,              A|2,    NA,     1 },
-       { "xor",  4, 2,    1,           3,              A|2,    NA,     1 },
-       { "nop",  1, 1,    I|R_NONE,    I|R_ALLZEROS,   I|0xff, NA,     1 },
-       { "inc",  2, 3,    1,           1,              I|1,    NA,     1 },
-       { "inc",  3, 3,    1,           2,              I|1,    NA,     1 },
-       { "dec",  2, 3,    1,           1,              I|0xff, NA,     1 },
-       { "dec",  3, 3,    1,           2,              I|0xff, NA,     1 },
-       { "jmp",  2, LO|0,   NA,        I|R_SINDEX,     I|0,    1,      3 },
-       { "jc",   2, LO|0,   NA,        I|R_SINDEX,     I|0,    1,      3 },
-       { "jnc",  2, LO|0,   NA,        I|R_SINDEX,     I|0,    1,      3 },
-       { "call", 2, LO|0,   NA,        I|R_SINDEX,     I|0,    1,      3 },
-       { "test", 5, LA|3,   NA,        1,              A|2,    4,      3 },
-       { "cmp",  5, LX|3,   NA,        1,              A|2,    4,      3 },
-       { "ret",  1, 1,  I|R_NONE,      I|R_ALLZEROS,   I|0xff, NA,     1 },
-       { "ret",  1, 1,  I|R_NONE,      I|R_ALLZEROS,   I|0xff, NA,     1 },
-       { "clc",  1, 3,  I|R_NONE,      I|R_ALLZEROS,   I|1,    NA,     1 },
-       { "clc",  4, 3,  2,             I|R_ALLZEROS,   A|3,    NA,     1 },
-       { "stc",  2, 3,  1,             I|R_ALLONES,    I|1,    NA,     1 },
-       { "add",  3, 3,  1,             1,              A|2,    NA,     1 },
-       { "add",  4, 3,  1,             3,              A|2,    NA,     1 },
-       { "adc",  3, 4,  1,             1,              A|2,    NA,     1 },
-       { "adc",  4, 4,  1,             3,              A|2,    NA,     1 },
-       { "shl",  3, 5,  1,             1,              SL|2,   NA,     2 },
-       { "shl",  4, 5,  1,             2,              SL|3,   NA,     2 },
-       { "shr",  3, 5,  1,             1,              SR|2,   NA,     2 },
-       { "shr",  4, 5,  1,             2,              SR|3,   NA,     2 },
-       { "rol",  3, 5,  1,             1,              RL|2,   NA,     2 },
-       { "rol",  4, 5,  1,             2,              RL|3,   NA,     2 },
-       { "ror",  3, 5,  1,             1,              RR|2,   NA,     2 },
-       { "ror",  4, 5,  1,             2,              RR|3,   NA,     2 },
-       /*
-        *  Extensions (note also that mvi allows A)
-        */
-       { "clr",  2, 1,  1,     I|R_ALLZEROS,           I|0xff, NA,     1 },
-       { 0,      0, 0,  0,     0,                      0,      0,      0 }
-};
-
-int 
-eval_operand(char **a, int spec)
-{
-       int i;
-       unsigned int want = spec & (LO|LA|LX);
-
-       static struct {
-               unsigned int what;
-               const char *name;
-               int value;
-       } jmptab[] = {
-               { LO,   "jmp",          8  },
-               { LO,   "jc",           9  },
-               { LO,   "jnc",          10 },
-               { LO,   "call",         11 },
-               { LA,   "jz",           15 },
-               { LA,   "jnz",          13 },
-               { LX,   "je",           14 },
-               { LX,   "jne",          12 },
-       };
-
-       spec &= ~(LO|LA|LX);
-
-       for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
-               if (jmptab[i].what == want &&
-                   !strcmp(jmptab[i].name, a[spec]))
-               {
-                       return(jmptab[i].value);
-               }
-
-       if (want)
-               error("invalid jump");
-
-       return(spec);           /* "case 0" - no flags set */
-}
-
-int
-eval_sdi(char **a, int spec)
-{
-       sym_t *p;
-       unsigned val;
-
-       if (spec == NA)
-               return(NA);
-
-       switch (spec & (A|I|SL|SR|RL|RR)) {
-           case SL:
-           case SR:
-           case RL:
-           case RR:
-               if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
-                       val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
-               else {
-                       p = lookup(a[spec &~ (SL|SR|RL|RR)]);
-                       if (!p)
-                               error("undefined symbol used");
-                       val = p->value;
-               }
-
-               switch (spec & (SL|SR|RL|RR)) {         /* blech */
-                   case SL:
-                       if (val > 7)
-                               return(0xf0);
-                       return(((val % 8) << 4) |
-                              (val % 8));
-                   case SR:
-                       if (val > 7)
-                               return(0xf0);
-                       return(((val % 8) << 4) |
-                              (1 << 3) |
-                              ((8 - (val % 8)) % 8));
-                   case RL:
-                       return(val % 8);
-                   case RR:
-                       return((8 - (val % 8)) % 8);
-               }
-           case I:
-               return(spec &~ I);
-           case A:
-               /*
-                *  An immediate field of zero selects
-                *  the accumulator.  Vigorously object
-                *  if zero is given otherwise - it's
-                *  most likely an error.
-                */
-               spec &= ~A;
-               if (!strcmp("A", a[spec]))
-                       return(0);
-               if (isdigit(*a[spec]) &&
-                   strtol(a[spec], NULL, 0) == 0)
-               {
-                       error("immediate value of zero selects accumulator");
-               }
-               /* falls through */
-           case 0:
-               if (isdigit(*a[spec]))
-                       return(strtol(a[spec], NULL, 0));
-               p = lookup(a[spec]);
-               if (p)
-                       return(p->value);
-               error("undefined symbol used");
-       }
-
-       return(NA);             /* shut the compiler up */
-}
-
-int
-eval_addr(char **a, int spec)
-{
-       sym_t *p;
-
-       if (spec == NA)
-               return(NA);
-       if (isdigit(*a[spec]))
-               return(strtol(a[spec], NULL, 0));
-
-       p = lookup(a[spec]);
-
-       if (p) {
-               if (p->value != NOVALUE)
-                       return(p->value);
-               patch(p, LC);
-       } else {
-               define(a[spec], NOVALUE);
-               p = lookup(a[spec]);
-               patch(p, LC);
-       }
-
-       return(NA);             /* will be patched in later */
-}
-
-int
-crack(char **a, int n)
-{
-       int i;
-       int I_imm, I_addr;
-       int I_op, I_dest, I_src, I_ret;
-
-       /*
-        *  Check for "ret" at the end of the line; remove
-        *  it unless it's "ret" alone - we still want to
-        *  look it up in the table.
-        */
-       I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
-       if (I_ret && n > 1)
-               n -= 1;
-
-       for (i = 0; instr[i].name; i++) {
-               /*
-                *  Look for match in table given constraints,
-                *  currently just the name and the number of
-                *  operands.
-                */
-               if (!strcmp(instr[i].name, *a) && instr[i].n == n)
-                       break;
-       }
-       if (!instr[i].name)
-               error("unknown opcode or wrong number of operands");
-
-       I_op    = eval_operand(a, instr[i].op);
-       I_src   = eval_sdi(a, instr[i].src);
-       I_imm   = eval_sdi(a, instr[i].imm);
-       I_dest  = eval_sdi(a, instr[i].dest);
-       I_addr  = eval_addr(a, instr[i].addr);
-
-       if( LC >= MEMORY )
-               error("Memory exhausted!\n");
-
-       switch (instr[i].fmt) {
-           case 1:
-           case 2:
-               M[LC][0] = (I_op << 1) | I_ret;
-               M[LC][1] = I_dest;
-               M[LC][2] = I_src;
-               M[LC][3] = I_imm;
-               break;
-           case 3:
-               if (I_ret)
-                       error("illegal use of \"ret\"");
-               M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
-               M[LC][1] = I_addr & 0xff;
-               M[LC][2] = I_src;
-               M[LC][3] = I_imm;
-               break;
-       }
-
-       return (1);             /* no two-byte instructions yet */
-}
-
-#undef SL
-#undef SR
-#undef RL
-#undef RR
-#undef LX
-#undef LA
-#undef LO
-#undef I
-#undef A
-
-void
-assemble(FILE *ofile)
-{
-       int n;
-       char **a;
-       sym_t *p;
-
-       while ((a = getl(&n))) {
-
-               while (a[0][strlen(*a)-1] == ':') {
-                       a[0][strlen(*a)-1] = '\0';
-                       p = lookup(*a);
-                       if (p)
-                               p->value = LC;
-                       else
-                               define(*a, LC);
-                       a += 1;
-                       n -= 1;
-               }
-
-               if (!n)                 /* line was all labels */
-                       continue;
-
-               if (n == 3 && !strcmp("VERSION", *a))
-                       fprintf(ofile, "#define %s \"%s\"\n", a[1], a[2]);
-               else {
-                       if (n == 3 && !strcmp("=", a[1]))
-                               define(*a, strtol(a[2], NULL, 0));
-                       else
-                               LC += crack(a, n);
-               }
-       }
-
-       backpatch();
-       output(ofile);
-
-       if (debug)
-               output(stderr);
-}
-
-int
-main(int argc, char **argv)
-{
-       int c;
-       int pid;
-       int ifile;
-       int status;
-       FILE *ofile;
-       char *ofilename;
-       int fd[2];
-
-       ofile = NULL;
-       ofilename = NULL;
-       while ((c = getopt(argc, argv, "dho:vD:")) != EOF) {
-               switch (c) {
-                   case 'd':
-                       debug = !0;
-                       break;
-                   case 'D':
-                   {
-                       char *p;
-                       if ((p = strchr(optarg, '=')) != NULL) {
-                               *p = '\0';
-                               define(optarg, strtol(p + 1, NULL, 0));
-                       }
-                       else
-                               define(optarg, 1);
-                       break;
-                   }
-                   case 'o':
-                       ofilename = optarg;
-                       if ((ofile = fopen(ofilename, "w")) == NULL) {
-                               perror(optarg);
-                               exit(EXIT_FAILURE);
-                       }
-                       break;
-                   case 'h':
-                       printf("usage: %s [-d] [-Dname] [-ooutput] input\n", 
-                               *argv);
-                       exit(EXIT_SUCCESS);
-                       break;
-                   case 'v':
-                       printf("%s\n", id);
-                       exit(EXIT_SUCCESS);
-                       break;
-                   default:
-                       exit(EXIT_FAILURE);
-                       break;
-               }
-       }
-
-       if (argc - optind != 1) {
-               fprintf(stderr, "%s: must have one input file\n", *argv);
-               exit(EXIT_FAILURE);
-       }
-       filename = argv[optind];
-
-       
-       if ((ifile = open(filename, O_RDONLY)) < 0) {
-               perror(filename);
-               exit(EXIT_FAILURE);
-       }
-
-       if (!ofilename) {
-               ofilename = ADOTOUT;
-               if ((ofile = fopen(ofilename, "w")) < 0) {
-                       perror(ofilename);
-                       exit(EXIT_FAILURE);
-               }
-       }
-
-       if (pipe(fd) < 0) {
-               perror("pipe failed");
-               exit(1);
-       }
-
-       if ((pid = fork()) < 0 ) {
-               perror("fork failed");
-               exit(1);
-       }
-       else if (pid > 0) {             /* Parent */
-               close(fd[1]);           /* Close write end */
-               if (fd[0] != STDIN_FILENO) {
-                       if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
-                               perror("dup2 error on stdin");
-                               exit(EXIT_FAILURE);
-                       }
-                       close(fd[0]);
-               }
-               assemble(ofile);
-               if (wait(&status) < 0) {
-                       perror("wait error");
-               }
-
-               if (status != 0) {
-                       unlink(ofilename);
-               }
-               exit(status);
-       } else {                                /* Child */
-                int i, arg_cnt, found;
-               char *args[MAX_ARGS];
-               char *buf;
-
-                arg_cnt = 0;
-                found = FALSE;
-                for (i = 0; (!found && (i < NUMBER(cpp))); i++) {
-                       char *bp;
-
-                       buf = strdup(cpp[i]);
-
-                       for (bp = strtok(buf, " \t\n"), arg_cnt = 0;
-                             bp != NULL;
-                             bp = strtok(NULL, " \t\n"), arg_cnt++) {
-                               if (arg_cnt == 0) {
-                                       if (access(bp, X_OK) == 0) {
-                                               found = TRUE;
-                                       }
-                               }
-
-                               args[arg_cnt] = bp;
-                       }
-
-                        if (!found) {
-                               free(buf);
-                        }
-                }
-               args[arg_cnt] = NULL;
-
-                if (found) {
-                       close(fd[0]);           /* Close Read end */
-                       if (fd[1] != STDOUT_FILENO) {
-                               if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
-                                       perror("dup2 error on stdout");
-                                       exit(EXIT_FAILURE);
-                               }
-                               close(fd[1]);
-                       }
-                       if (ifile != STDIN_FILENO) {
-                               if (dup2(ifile, STDIN_FILENO) != STDIN_FILENO) {
-                                       perror("dup2 error on stdin");
-                                       exit(EXIT_FAILURE);
-                               }
-                               close(ifile);
-                       }
-
-                       if (execvp(args[0], args) < 0) {
-                               perror("execvp() error");
-                               exit(EXIT_FAILURE);
-                       }
-               } else {
-                       fprintf(stderr, "%s: Cannot find CPP command.\n", argv[0]);
-                       exit(EXIT_FAILURE);
-               }
-       }
-       return(EXIT_SUCCESS);
-}
index a968c324f929cb264b23cc8e76a1cc880eb29ec1..1092d4862c6bc73e3301b9fc6dbb868375830915 100644 (file)
@@ -24,7 +24,7 @@
  *
  *  Dean W. Gehnert, deang@teleport.com, 05/01/96
  *
- *  $Id: aic7xxx_proc.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #define BLS buffer + len + size
@@ -77,16 +77,18 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
   static u8 buff[512];
-  int   i; 
+  int   i;
+  int   found = FALSE;
   int   size = 0;
   int   len = 0;
   off_t begin = 0;
   off_t pos = 0;
   static char *bus_names[] = { "Single", "Twin", "Wide" };
-  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-787x", "AIC-788x" };
+  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
+      "AIC-787x", "AIC-788x" };
 
   HBAptr = NULL;
-  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+  for (i=0; i < NUMBER(aic7xxx_boards); i++)
   {
     if ((HBAptr = aic7xxx_boards[i]) != NULL)
     {
@@ -95,16 +97,23 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
         break;
       }
 
-      while ((HBAptr->hostdata != NULL) &&
+      while ((HBAptr->hostdata != NULL) && !found &&
           ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
       {
         if (HBAptr->host_no == hostno)
         {
-          break; break;
+          found = TRUE;
         }
       }
 
-      HBAptr = NULL;
+      if (!found)
+      {
+        HBAptr = NULL;
+      }
+      else
+      {
+        break;
+      }
     }
   }
 
@@ -129,8 +138,10 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
 
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
   size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
-  size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+  size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
+#if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+#endif
   len += size; pos = begin + len; size = 0;
 
   size += sprintf(BLS, "\n");
@@ -141,11 +152,6 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
 #ifdef AIC7XXX_CMDS_PER_LUN
   size += sprintf(BLS, "  AIC7XXX_CMDS_PER_LUN   : %d\n", AIC7XXX_CMDS_PER_LUN);
 #endif
-#ifdef AIC7XXX_TWIN_SUPPORT
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Enabled\n");
-#else
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Disabled\n");
-#endif
 #ifdef AIC7XXX_TAGGED_QUEUEING
   size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Enabled\n");
 #else
@@ -165,16 +171,18 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
-  size += sprintf(BLS, "          SCSI Adapter: %s\n", board_names[p->type]);
+  size += sprintf(BLS, "          SCSI Adapter: %s\n",
+      board_names[p->chip_type]);
   size += sprintf(BLS, "                        (%s chipset)\n",
-      chip_names[p->chip_type]);
+      chip_names[p->chip_class]);
   size += sprintf(BLS, "              Host Bus: %s\n", bus_names[p->bus_type]);
   size += sprintf(BLS, "               Base IO: %#.4x\n", p->base);
+  size += sprintf(BLS, "        Base IO Memory: 0x%x\n", p->mbase);
   size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
   size += sprintf(BLS, "                  SCBs: Used %d, HW %d, Page %d\n",
-      p->scb_link->numscbs, p->maxhscbs, p->maxscbs);
+      p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
   size += sprintf(BLS, "            Interrupts: %d", p->isr_count);
-  if (p->chip_type == AIC_777x)
+  if (p->chip_class == AIC_777x)
   {
     size += sprintf(BLS, " %s\n",
         (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
index d26a0c20e6749ee694a76dc1703639b8c437e03c..f3b8c349ced4e6264b4eb411923a8bbb45dcf6e1 100644 (file)
-/*+M*************************************************************************
- * Adaptec AIC7xxx register and scratch ram definitions.
- *
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This version corresponds to version 1.12 of FreeBSDs aic7xxx_reg.h
- *
- * $Id: aic7xxx_reg.h,v 4.0 1996/10/13 08:23:42 deang Exp $
- *-M*************************************************************************/
-
 /*
- * This header is shared by the sequencer code and the kernel level driver.
- *
- * All page numbers refer to the Adaptec AIC-7770 Data Book available from
- * Adaptec's Technical Documents Department 1-800-934-2766
- */
+  * DO NOT EDIT - This file is automatically generated.
+  */
+
+#define        SCSISEQ                         0x00
+#define                TEMODE                  0x80
+#define                ENSELO                  0x40
+#define                ENSELI                  0x20
+#define                ENRSELI                 0x10
+#define                ENAUTOATNO              0x08
+#define                ENAUTOATNI              0x04
+#define                ENAUTOATNP              0x02
+#define                SCSIRSTO                0x01
+
+#define        SXFRCTL0                        0x01
+#define                DFON                    0x80
+#define                DFPEXP                  0x40
+#define                FAST20                  0x20
+#define                CLRSTCNT                0x10
+#define                SPIOEN                  0x08
+#define                SCAMEN                  0x04
+#define                CLRCHN                  0x02
+
+#define        SXFRCTL1                        0x02
+#define                BITBUCKET               0x80
+#define                SWRAPEN                 0x40
+#define                ENSPCHK                 0x20
+#define                STIMESEL                0x18
+#define                ENSTIMER                0x04
+#define                ACTNEGEN                0x02
+#define                STPWEN                  0x01
+
+#define        SCSISIGO                        0x03
+#define                CDO                     0x80
+#define                IOO                     0x40
+#define                MSGO                    0x20
+#define                ATNO                    0x10
+#define                SELO                    0x08
+#define                BSYO                    0x04
+#define                REQO                    0x02
+#define                ACKO                    0x01
+
+#define        SCSISIGI                        0x03
+#define                ATNI                    0x10
+#define                SELI                    0x08
+#define                BSYI                    0x04
+#define                REQI                    0x02
+#define                ACKI                    0x01
+
+#define        SCSIRATE                        0x04
+#define                WIDEXFER                0x80
+#define                SXFR                    0x70
+#define                SOFS                    0x0f
+
+#define        SCSIID                          0x05
+#define                OID                     0x0f
+
+#define        SCSIDATL                        0x06
+
+#define        SCSIDATH                        0x07
+
+#define        STCNT                           0x08
+
+#define        CLRSINT0                        0x0b
+#define                CLRSELDO                0x40
+#define                CLRSELDI                0x20
+#define                CLRSELINGO              0x10
+#define                CLRSWRAP                0x08
+#define                CLRSPIORDY              0x02
+
+#define        SSTAT0                          0x0b
+#define                TARGET                  0x80
+#define                SELDO                   0x40
+#define                SELDI                   0x20
+#define                SELINGO                 0x10
+#define                SWRAP                   0x08
+#define                SDONE                   0x04
+#define                SPIORDY                 0x02
+#define                DMADONE                 0x01
+
+#define        CLRSINT1                        0x0c
+#define                CLRSELTIMEO             0x80
+#define                CLRATNO                 0x40
+#define                CLRSCSIRSTI             0x20
+#define                CLRBUSFREE              0x08
+#define                CLRSCSIPERR             0x04
+#define                CLRPHASECHG             0x02
+#define                CLRREQINIT              0x01
+
+#define        SSTAT1                          0x0c
+#define                SELTO                   0x80
+#define                ATNTARG                 0x40
+#define                SCSIRSTI                0x20
+#define                PHASEMIS                0x10
+#define                BUSFREE                 0x08
+#define                SCSIPERR                0x04
+#define                PHASECHG                0x02
+#define                REQINIT                 0x01
+
+#define        SSTAT2                          0x0d
+#define                OVERRUN                 0x80
+#define                SFCNT                   0x1f
+
+#define        SSTAT3                          0x0e
+#define                SCSICNT                 0xf0
+#define                OFFCNT                  0x0f
+
+#define        SCSITEST                        0x0f
+#define                RQAKCNT                 0x04
+#define                CNTRTEST                0x02
+#define                CMODE                   0x01
+
+#define        SIMODE0                         0x10
+#define                ENSELDO                 0x40
+#define                ENSELDI                 0x20
+#define                ENSELINGO               0x10
+#define                ENSWRAP                 0x08
+#define                ENSDONE                 0x04
+#define                ENSPIORDY               0x02
+#define                ENDMADONE               0x01
+
+#define        SIMODE1                         0x11
+#define                ENSELTIMO               0x80
+#define                ENATNTARG               0x40
+#define                ENSCSIRST               0x20
+#define                ENPHASEMIS              0x10
+#define                ENBUSFREE               0x08
+#define                ENSCSIPERR              0x04
+#define                ENPHASECHG              0x02
+#define                ENREQINIT               0x01
+
+#define        SCSIBUSL                        0x12
+
+#define        SCSIBUSH                        0x13
+
+#define        SHADDR                          0x14
+
+#define        SELTIMER                        0x18
+#define                STAGE6                  0x20
+#define                STAGE5                  0x10
+#define                STAGE4                  0x08
+#define                STAGE3                  0x04
+#define                STAGE2                  0x02
+#define                STAGE1                  0x01
+
+#define        SELID                           0x19
+#define                SELID_MASK              0xf0
+#define                ONEBIT                  0x08
+
+#define        BRDCTL                          0x1d
+#define                BRDDAT7                 0x80
+#define                BRDDAT6                 0x40
+#define                BRDDAT5                 0x20
+#define                BRDSTB                  0x10
+#define                BRDCS                   0x08
+#define                BRDRW                   0x04
+#define                BRDCTL1                 0x02
+#define                BRDCTL0                 0x01
+
+#define        SEECTL                          0x1e
+#define                EXTARBACK               0x80
+#define                EXTARBREQ               0x40
+#define                SEEMS                   0x20
+#define                SEERDY                  0x10
+#define                SEECS                   0x08
+#define                SEECK                   0x04
+#define                SEEDO                   0x02
+#define                SEEDI                   0x01
+
+#define        SBLKCTL                         0x1f
+#define                DIAGLEDEN               0x80
+#define                DIAGLEDON               0x40
+#define                AUTOFLUSHDIS            0x20
+#define                SELWIDE                 0x02
+
+#define        SRAM_BASE                       0x20
+
+#define        TARG_SCRATCH                    0x20
+
+#define        ULTRA_ENB                       0x30
+
+#define        DISC_DSB                        0x32
+
+#define        MSG_LEN                         0x34
+
+#define        MSG_OUT                         0x35
+
+#define        DMAPARAMS                       0x3d
+#define                WIDEODD                 0x40
+#define                SCSIEN                  0x20
+#define                SDMAENACK               0x10
+#define                SDMAEN                  0x10
+#define                HDMAEN                  0x08
+#define                HDMAENACK               0x08
+#define                DIRECTION               0x04
+#define                FIFOFLUSH               0x02
+#define                FIFORESET               0x01
 
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-#define SCSISEQ                        0x000
-#define                TEMODEO         0x80
-#define                ENSELO          0x40
-#define                ENSELI          0x20
-#define                ENRSELI         0x10
-#define                ENAUTOATNO      0x08
-#define                ENAUTOATNI      0x04
-#define                ENAUTOATNP      0x02
-#define                SCSIRSTO        0x01
+#define        SCBCOUNT                        0x3e
 
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-#define        SXFRCTL0                0x001
-#define                DFON            0x80
-#define                DFPEXP          0x40
-#define                ULTRAEN         0x20
-#define                CLRSTCNT        0x10
-#define                SPIOEN          0x08
-#define                SCAMEN          0x04
-#define                CLRCHN          0x02
-/*  UNUSED                     0x01 */
+#define        COMP_SCBCOUNT                   0x3f
 
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-#define        SXFRCTL1                0x002
-#define                BITBUCKET       0x80
-#define                SWRAPEN         0x40
-#define                ENSPCHK         0x20
-#define                STIMESEL        0x18
-#define                ENSTIMER        0x04
-#define                ACTNEGEN        0x02
-#define                STPWEN          0x01    /* Powered Termination */
+#define        QCNTMASK                        0x40
 
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-#define SCSISIGI               0x003
-#define                CDI             0x80
-#define                IOI             0x40
-#define                MSGI            0x20
-#define                ATNI            0x10
-#define                SELI            0x08
-#define                BSYI            0x04
-#define                REQI            0x02
-#define                ACKI            0x01
+#define        SEQ_FLAGS                       0x41
+#define                RESELECTED              0x80
+#define                IDENTIFY_SEEN           0x40
+#define                TAGGED_SCB              0x20
+#define                DPHASE                  0x10
+#define                PAGESCBS                0x04
+#define                WIDE_BUS                0x02
+#define                TWIN_BUS                0x01
 
-/*
- * Possible phases in SCSISIGI
- */
-#define                PHASE_MASK      0xe0
-#define                P_DATAOUT       0x00
-#define                P_DATAIN        0x40
-#define                P_COMMAND       0x80
-#define                P_MESGOUT       0xa0
-#define                P_STATUS        0xc0
-#define                P_MESGIN        0xe0
-/*
- * SCSI Control Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus.  Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-#define SCSISIGO               0x003
-#define                CDO             0x80
-#define                IOO             0x40
-#define                MSGO            0x20
-#define                ATNO            0x10
-#define                SELO            0x08
-#define                BSYO            0x04
-#define                REQO            0x02
-#define                ACKO            0x01
-
-/* 
- * SCSI Rate Control (p. 3-17).
- * Contents of this register determine the Synchronous SCSI data transfer
- * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
- * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
- * greater than 0 enables synchronous transfers.
- */
-#define SCSIRATE               0x004
-#define                WIDEXFER        0x80            /* Wide transfer control */
-#define                SXFR            0x70            /* Sync transfer rate */
-#define                SOFS            0x0f            /* Sync offset */
+#define        SAVED_TCL                       0x42
 
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel.
- */
-#define SCSIID                 0x005
-#define                TID             0xf0            /* Target ID mask */
-#define                OID             0x0f            /* Our ID mask */
+#define        SG_COUNT                        0x43
 
-/*
- * SCSI Latched Data (p. 3-19).
- * Read/Write latches used to transfer data on the SCSI bus during
- * Automatic or Manual PIO mode.  SCSIDATH can be used for the
- * upper byte of a 16bit wide asynchronous data phase transfer.
- */
-#define SCSIDATL               0x006
-#define SCSIDATH               0x007
+#define        SG_NEXT                         0x44
 
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transfered
- * across the SCSI bus.  The counter is decremented only once
- * the data has been safely transfered.  SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */ 
-#define STCNT                  0x008
-#define STCNT0                 0x008
-#define STCNT1                 0x009
-#define STCNT2                 0x00a
+#define        WAITING_SCBH                    0x48
 
-/*
- * Clear SCSI Interrupt 0 (p. 3-20)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
- */
-#define        CLRSINT0                0x00b
-#define                CLRSELDO        0x40
-#define                CLRSELDI        0x20
-#define                CLRSELINGO      0x10
-#define                CLRSWRAP        0x08
-/*  UNUSED                     0x04 */
-#define                CLRSPIORDY      0x02
-/*  UNUSED                     0x01 */
+#define        SAVED_LINKPTR                   0x49
 
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-#define SSTAT0                 0x00b
-#define                TARGET          0x80            /* Board acting as target */
-#define                SELDO           0x40            /* Selection Done */
-#define                SELDI           0x20            /* Board has been selected */
-#define                SELINGO         0x10            /* Selection In Progress */
-#define                SWRAP           0x08            /* 24bit counter wrap */
-#define                SDONE           0x04            /* STCNT = 0x000000 */
-#define                SPIORDY         0x02            /* SCSI PIO Ready */
-#define                DMADONE         0x01            /* DMA transfer completed */
+#define        SAVED_SCBPTR                    0x4a
 
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-#define CLRSINT1               0x00c
-#define                CLRSELTIMEO     0x80
-#define                CLRATNO         0x40
-#define                CLRSCSIRSTI     0x20
-/*  UNUSED                     0x10 */
-#define                CLRBUSFREE      0x08
-#define                CLRSCSIPERR     0x04
-#define                CLRPHASECHG     0x02
-#define                CLRREQINIT      0x01
+#define        REJBYTE                         0x4b
 
-/*
- * SCSI Status 1 (p. 3-24)
- */
-#define SSTAT1                 0x00c
-#define                SELTO           0x80
-#define                ATNTARG         0x40
-#define                SCSIRSTI        0x20
-#define                PHASEMIS        0x10
-#define                BUSFREE         0x08
-#define                SCSIPERR        0x04
-#define                PHASECHG        0x02
-#define                REQINIT         0x01
+#define        LASTPHASE                       0x4c
+#define                P_MESGIN                0xe0
+#define                PHASE_MASK              0xe0
+#define                P_STATUS                0xc0
+#define                P_MESGOUT               0xa0
+#define                P_COMMAND               0x80
+#define                CDI                     0x80
+#define                IOI                     0x40
+#define                P_DATAIN                0x40
+#define                MSGI                    0x20
+#define                P_BUSFREE               0x01
+#define                P_DATAOUT               0x00
 
-/*
- * SCSI Interrupt Mode 1 (pp. 3-28,29)
- * Setting any bit will enable the corresponding function
- * in SIMODE1 to interrupt via the IRQ pin.
- */
-#define        SIMODE1                 0x011
-#define                ENSELTIMO       0x80
-#define                ENATNTARG       0x40
-#define                ENSCSIRST       0x20
-#define                ENPHASEMIS      0x10
-#define                ENBUSFREE       0x08
-#define                ENSCSIPERR      0x04
-#define                ENPHASECHG      0x02
-#define                ENREQINIT       0x01
+#define        MSGIN_EXT_LEN                   0x4d
 
-/*
- * SCSI Data Bus (High) (p. 3-29)
- * This register reads data on the SCSI Data bus directly.
- */
-#define        SCSIBUSL                0x012
-#define        SCSIBUSH                0x013
+#define        MSGIN_EXT_OPCODE                0x4e
 
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transfered on the SCSI bus.  They are counted up in the same
- * manner as STCNT is counted down.  SHADDR should always be used
- * to determine the address of the last byte transfered since HADDR
- * can be skewed by write ahead.
- */
-#define        SHADDR                  0x014
-#define        SHADDR0                 0x014
-#define        SHADDR1                 0x015
-#define        SHADDR2                 0x016
-#define        SHADDR3                 0x017
+#define        MSGIN_EXT_BYTES                 0x4f
 
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-#define SELID                  0x019
-#define                SELID_MASK      0xf0
-#define                ONEBIT          0x08
-/*  UNUSED                     0x07 */
+#define        DISCONNECTED_SCBH               0x52
 
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection.  In a twin channel configuration
- * addresses 0x00-0x1e are gated to the appropriate channel based on this
- * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-#define SBLKCTL                        0x01f
-#define                DIAGLEDEN       0x80    /* Aic78X0 only */
-#define                DIAGLEDON       0x40    /* Aic78X0 only */
-#define                AUTOFLUSHDIS    0x20
-/*  UNUSED                     0x10 */
-#define                SELBUS_MASK     0x0a
-#define                SELBUSB         0x08
-/*  UNUSED                     0x04 */
-#define                SELWIDE         0x02
-/*  UNUSED                     0x01 */
-#define                SELNARROW       0x00
+#define        FREE_SCBH                       0x53
 
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-#define SEQCTL                 0x060
-#define                PERRORDIS       0x80
-#define                PAUSEDIS        0x40
-#define                FAILDIS         0x20
-#define        FASTMODE        0x10
-#define                BRKADRINTEN     0x08
-#define                STEP            0x04
-#define                SEQRESET        0x02
-#define                LOADRAM         0x01
+#define        HSCB_ADDR                       0x54
 
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
- * four bytes in succession.  The SEQADDRs will increment after the most
- * significant byte is written
- */
-#define SEQRAM                 0x061
+#define        CUR_SCBID                       0x58
 
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-#define SEQADDR0               0x062
-#define SEQADDR1               0x063
-#define        SEQADDR1_MASK   0x01
+#define        ARG_1                           0x59
+#define        RETURN_1                        0x59
+#define                SEND_MSG                0x80
+#define                SEND_SENSE              0x40
+#define                SEND_REJ                0x20
 
-/*
- * Accumulator
- * We cheat by passing arguments in the Accumulator up to the kernel driver
- */
-#define ACCUM                  0x064
-
-#define SINDEX                 0x065
-#define DINDEX                 0x066
-#define ALLZEROS               0x06a
-#define NONE                   0x06a
-#define SINDIR                 0x06c
-#define DINDIR                 0x06d
-#define FUNCTION1              0x06e
+#define        SCSICONF                        0x5a
+#define                RESET_SCSI              0x40
 
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transfered across the host bus.
- */
-#define HADDR                  0x088
-#define HADDR0                 0x088
-#define HADDR1                 0x089
-#define HADDR2                 0x08a
-#define HADDR3                 0x08b
-
-#define HCNT                   0x08c
-#define HCNT0                  0x08c
-#define HCNT1                  0x08d
-#define HCNT2                  0x08e
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-#define SCBPTR                 0x090
+#define        HOSTCONF                        0x5d
 
-/*
- * Board Control (p. 3-43)
- */
-#define BCTL                   0x084
-/*   RSVD                      0xf0 */
-#define                ACE             0x08    /* Support for external processors */
-/*   RSVD                      0x06 */
-#define                ENABLE          0x01
+#define        HA_274_BIOSCTRL                 0x5f
+#define                BIOSMODE                0x30
+#define                BIOSDISABLED            0x30
+#define                CHANNEL_B_PRIMARY       0x08
 
-/*
- * On the aic78X0 chips, Board Control is replaced by the DSCommand
- * register (p. 4-64)
- */
-#define        DSCOMMAND               0x084
-#define                CACHETHEN       0x80    /* Cache Threshold enable */
-#define                DPARCKEN        0x40    /* Data Parity Check Enable */
-#define                MPARCKEN        0x20    /* Memory Parity Check Enable */
-#define                EXTREQLCK       0x10    /* External Request Lock */
+#define        SEQCTL                          0x60
+#define                PERRORDIS               0x80
+#define                PAUSEDIS                0x40
+#define                FAILDIS                 0x20
+#define                FASTMODE                0x10
+#define                BRKADRINTEN             0x08
+#define                STEP                    0x04
+#define                SEQRESET                0x02
+#define                LOADRAM                 0x01
 
-/*
- * Bus On/Off Time (p. 3-44)
- */
-#define BUSTIME                        0x085
-#define                BOFF            0xf0
-#define                BON             0x0f
+#define        SEQRAM                          0x61
 
-/*
- * Bus Speed (p. 3-45)
- */
-#define        BUSSPD                  0x086
-#define                DFTHRSH         0xc0
-#define                STBOFF          0x38
-#define                STBON           0x07
-#define                DFTHRSH_100     0xc0
+#define        SEQADDR0                        0x62
 
-/*
- * Host Control (p. 3-47) R/W
- * Overall host control of the device.
- */
-#define HCNTRL                 0x087
-/*    UNUSED                   0x80 */
-#define                POWRDN          0x40
-/*    UNUSED                   0x20 */
-#define                SWINT           0x10
-#define                IRQMS           0x08
-#define                PAUSE           0x04
-#define                INTEN           0x02
-#define                CHIPRST         0x01
-#define                CHIPRSTACK      0x01
+#define        SEQADDR1                        0x63
+#define                SEQADDR1_MASK           0x01
 
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-#define INTSTAT                        0x091
-#define                SEQINT_MASK     0xf1            /* SEQINT Status Codes */
-#define                        BAD_PHASE       0x01    /* unknown scsi bus phase */
-#define                        SEND_REJECT     0x11    /* sending a message reject */
-#define                        NO_IDENT        0x21    /* no IDENTIFY after reconnect*/
-#define                        NO_MATCH        0x31    /* no cmd match for reconnect */
-#define                        SDTR_MSG        0x41    /* SDTR message received */
-#define                        WDTR_MSG        0x51    /* WDTR message received */
-#define                        REJECT_MSG      0x61    /* Reject message received */
-#define                        BAD_STATUS      0x71    /* Bad status from target */
-#define                        RESIDUAL        0x81    /* Residual byte count != 0 */
-#define                        ABORT_TAG       0x91    /* Sent an ABORT_TAG message */
-#define                        AWAITING_MSG    0xa1    /*
-                                                * Kernel requested to specify
-                                                 * a message to this target
-                                                 * (command was null), so tell
-                                                 * it that it can fill the
-                                                 * message buffer.
-                                                 */
-#define                        IMMEDDONE       0xb1    /*
-                                                * An immediate command has
-                                                * completed
-                                                */
-#define                        MSG_BUFFER_BUSY 0xc1    /*
-                                                * Sequencer wants to use the
-                                                * message buffer, but it
-                                                * already contains a message
-                                                */
-#define                        MSGIN_PHASEMIS  0xd1    /*
-                                                * Target changed phase on us
-                                                * when we were expecting
-                                                * another msgin byte.
-                                                */
-#define                        DATA_OVERRUN    0xe1    /*
-                                                * Target attempted to write
-                                                * beyond the bounds of its
-                                                * command.
-                                                */
-#define        BRKADRINT 0x08
-#define                SCSIINT   0x04
-#define                CMDCMPLT  0x02
-#define                SEQINT    0x01
-#define                INT_PEND  (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
+#define        ACCUM                           0x64
 
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors.  You usually cannot recover from
- * these without a full board reset.
- */
-#define ERROR                  0x092
-/*    UNUSED                   0xf0 */
-#define                PARERR          0x08
-#define                ILLOPCODE       0x04
-#define                ILLSADDR        0x02
-#define                ILLHADDR        0x01
+#define        SINDEX                          0x65
 
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-#define CLRINT                 0x092
-#define                CLRBRKADRINT    0x08
-#define                CLRSCSIINT      0x04
-#define                CLRCMDINT       0x02
-#define                CLRSEQINT       0x01
-
-#define        DFCNTRL                 0x093
-#define                WIDEODD         0x40
-#define                SCSIEN          0x20
-#define                SDMAEN          0x10
-#define                SDMAENACK       0x10
-#define                HDMAEN          0x08
-#define                HDMAENACK       0x08
-#define                DIRECTION       0x04
-#define                FIFOFLUSH       0x02
-#define                FIFORESET       0x01
-
-#define        DFSTATUS                0x094
-#define                HDONE           0x08
-#define                FIFOEMP         0x01
-
-#define        DFDAT                   0x099
+#define        DINDEX                          0x66
 
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-#define SCBCNT                 0x09a
-#define                SCBAUTO         0x80
-#define                SCBCNT_MASK     0x1f
+#define        ALLONES                         0x69
 
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the sequencer has yet to start)
- */
-#define QINFIFO                        0x09b
+#define        ALLZEROS                        0x6a
 
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-#define QINCNT                 0x09c
+#define        NONE                            0x6a
 
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-#define QOUTFIFO               0x09d
+#define        FLAGS                           0x6b
+#define                ZERO                    0x02
+#define                CARRY                   0x01
 
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-#define QOUTCNT                        0x09e
+#define        SINDIR                          0x6c
 
-/*
- * SCB Definition (p. 5-4)
- * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
- * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
- * whether or not to DMA an SCB from host ram. This flag prevents the
- * "re-fetching" of transactions that are requeued because the target is
- * busy with another command. We also use bits 6 & 7 to indicate whether
- * or not to initiate SDTR or WDTR respectively when starting this command.
- */
-#define SCBARRAY               0x0a0
-#define        SCB_CONTROL             0x0a0
-#define                NEEDWDTR        0x80
-#define                DISCENB         0x40
-#define                TAG_ENB         0x20
-#define                NEEDSDTR        0x10
-#define                DISCONNECTED    0x04
-#define                SCB_TAG_TYPE    0x03
-#define        SCB_TCL                 0x0a1
-#define        SCB_TARGET_STATUS       0x0a2
-#define        SCB_SGCOUNT             0x0a3
-#define        SCB_SGPTR               0x0a4
-#define                SCB_SGPTR0      0x0a4
-#define                SCB_SGPTR1      0x0a5
-#define                SCB_SGPTR2      0x0a6
-#define                SCB_SGPTR3      0x0a7
-#define        SCB_RESID_SGCNT         0x0a8
-#define SCB_RESID_DCNT         0x0a9
-#define                SCB_RESID_DCNT0 0x0a9
-#define                SCB_RESID_DCNT1 0x0aa
-#define                SCB_RESID_DCNT2 0x0ab
-#define SCB_DATAPTR            0x0ac
-#define                SCB_DATAPTR0    0x0ac
-#define                SCB_DATAPTR1    0x0ad
-#define                SCB_DATAPTR2    0x0ae
-#define                SCB_DATAPTR3    0x0af
-#define        SCB_DATACNT             0x0b0
-#define                SCB_DATACNT0    0x0b0
-#define                SCB_DATACNT1    0x0b1
-#define                SCB_DATACNT2    0x0b2
-/* UNUSED - QUAD PADDING       0x0b3 */
-#define SCB_CMDPTR             0x0b4
-#define                SCB_CMDPTR0     0x0b4
-#define                SCB_CMDPTR1     0x0b5
-#define                SCB_CMDPTR2     0x0b6
-#define                SCB_CMDPTR3     0x0b7
-#define        SCB_CMDLEN              0x0b8
-#define SCB_TAG                        0x0b9
-#define        SCB_NEXT                0x0ba
-#define        SCB_PREV                0x0bb
-
-#define        SG_SIZEOF               0x08            /* sizeof(struct ahc_dma) */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-#define        SEECTL_2840             0x0c0
-/*     UNUSED                  0xf8 */
-#define                CS_2840         0x04
-#define                CK_2840         0x02
-#define                DO_2840         0x01
-
-#define        STATUS_2840             0x0c1
-#define                EEPROM_TF       0x80
-#define                BIOS_SEL        0x60
-#define                ADSEL           0x1e
-#define                DI_2840         0x01
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-#define DSPCISTATUS            0x086
+#define        DINDIR                          0x6d
 
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device.  In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device.  When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM.  When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted.  
- *
- * After successful arbitration for the memory port, the SEECS bit of 
- * the SEECTL register is connected to the chip select.  The SEECK, 
- * SEEDO, and SEEDI are connected to the clock, data out, and data in 
- * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
- * gives us an 800 nsec timer.  After a write to the SEECTL register, 
- * the SEERDY goes high 800 nsec later.  The one exception to this is 
- * when we first request access to the memory port.  The SEERDY goes 
- * high to signify that access has been granted and, for this case, has 
- * no implied timing.
- *
- * See 93cx6.c for detailed information on the protocol necessary to 
- * read the serial EEPROM.
- */
-#define SEECTL                 0x01e
-#define                EXTARBACK       0x80
-#define                EXTARBREQ       0x40
-#define                SEEMS           0x20
-#define                SEERDY          0x10
-#define                SEECS           0x08
-#define                SEECK           0x04
-#define                SEEDO           0x02
-#define                SEEDI           0x01
-
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the sequencer code.
- *
- * The host adapter card (at least the BIOS) uses 20-2f for SCSI
- * device information, 32-33 and 5a-5f as well. As it turns out, the
- * BIOS trashes 20-2f, 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).  The kernel driver will enable synchronous negotiation
- * for all targets that have a value other than 0 in the lower four
- * bits of the target scratch space.  This should work regardless of
- * whether the bios has been installed.
- */
+#define        FUNCTION1                       0x6e
 
-/*
- * 1 byte per target starting at this address for configuration values
- */
-#define TARG_SCRATCH           0x020
+#define        STACK                           0x6f
 
-/*
- * The sequencer will stick the frist byte of any rejected message here so
- * we can see what is getting thrown away.  Extended messages put the
- * extended message type in REJBYTE_EXT.
- */
-#define REJBYTE                        0x030
-#define REJBYTE_EXT            0x031
+#define        BCTL                            0x84
+#define                ACE                     0x08
+#define                ENABLE                  0x01
 
-/*
- * Bit vector of targets that have disconnection disabled.
- */
-#define        DISC_DSB                0x032
-#define                DISC_DSB_A      0x032
-#define                DISC_DSB_B      0x033
+#define        DSCOMMAND                       0x84
+#define                CACHETHEN               0x80
+#define                DPARCKEN                0x40
+#define                MPARCKEN                0x20
+#define                EXTREQLCK               0x10
 
-/*
- * Length of pending message
- */
-#define MSG_LEN                        0x034
-
-/* We reserve 8bytes to store outgoing messages */
-#define MSG0                   0x035
-#define                COMP_MSG0       0xcb      /* 2's complement of MSG0 */
-#define MSG1                   0x036
-#define MSG2                   0x037
-#define MSG3                   0x038
-#define MSG4                   0x039
-#define MSG5                   0x03a
-#define MSG6                   0x03b
-#define MSG7                   0x03c
+#define        BUSTIME                         0x85
+#define                BOFF                    0xf0
+#define                BON                     0x0f
 
-/*
- * These are offsets into the card's scratch ram.  Some of the values are
- * specified in the AHA2742 technical reference manual and are initialized
- * by the BIOS at boot time.
- */
-#define LASTPHASE              0x03d
-#define ARG_1                  0x03e
-#define                MAXOFFSET       0x01
-#define RETURN_1               0x03f
-#define                SEND_WDTR       0x80
-#define                SEND_SDTR       0x60
-#define                SEND_SENSE      0x40
-#define                SEND_REJ        0x20
-#define                SCB_PAGEDIN     0x10
-
-#define SIGSTATE               0x040
-
-#define DMAPARAMS              0x041   /* Parameters for DMA Logic */
-
-#define        SG_COUNT                0x042
-#define        SG_NEXT                 0x043   /* working value of SG pointer */
-#define                SG_NEXT0        0x043
-#define                SG_NEXT1        0x044
-#define                SG_NEXT2        0x045
-#define                SG_NEXT3        0x046
-
-#define        SCBCOUNT                0x047   /*
-                                        * Number of SCBs supported by
-                                        * this card.
-                                        */
-#define        COMP_SCBCOUNT           0x048   /*
-                                        * Two's compliment of SCBCOUNT
-                                        */
-#define QCNTMASK               0x049   /*
-                                        * Mask of bits to test against
-                                        * when looking at the Queue Count
-                                        * registers.  Works around a bug
-                                        * on aic7850 chips. 
-                                        */
-#define FLAGS                  0x04a
-#define                SINGLE_BUS      0x00
-#define                TWIN_BUS        0x01
-#define                WIDE_BUS        0x02
-#define                PAGESCBS        0x04
-#define                DPHASE          0x10
-#define                SELECTED        0x20
-#define                IDENTIFY_SEEN   0x40
-#define                RESELECTED      0x80
-
-#define        SAVED_TCL               0x04b   /*
-                                        * Temporary storage for the
-                                        * target/channel/lun of a
-                                        * reconnecting target
-                                        */
-#define        ACTIVE_A                0x04c
-#define        ACTIVE_B                0x04d
-#define WAITING_SCBH           0x04e   /*
-                                        * head of list of SCBs awaiting
-                                        * selection
-                                        */
-#define DISCONNECTED_SCBH      0x04f   /*
-                                        * head of list of SCBs that are
-                                        * disconnected.  Used for SCB
-                                        * paging.
-                                        */
-#define                SCB_LIST_NULL   0xff
-
-#define SAVED_LINKPTR          0x050
-#define SAVED_SCBPTR           0x051
-#define ULTRA_ENB              0x052
-#define ULTRA_ENB_B            0x053
-
-#define SCSICONF               0x05a
-#define                RESET_SCSI      0x40
-
-#define HOSTCONF               0x05d
-
-#define HA_274_BIOSCTRL                0x05f
-#define BIOSMODE               0x30
-#define BIOSDISABLED           0x30
-#define CHANNEL_B_PRIMARY      0x08
-
-/* Message codes */
-#define MSG_EXTENDED           0x01
-#define                MSG_SDTR        0x01
-#define                MSG_WDTR        0x03
-#define MSG_SDPTRS             0x02
-#define MSG_RDPTRS             0x03
-#define MSG_DISCONNECT         0x04
-#define MSG_INITIATOR_DET_ERROR        0x05
-#define MSG_ABORT              0x06
-#define        MSG_REJECT              0x07
-#define MSG_NOP                        0x08
-#define MSG_MSG_PARITY_ERROR   0x09
-#define MSG_BUS_DEVICE_RESET   0x0c
-#define MSG_ABORT_TAG          0x0d
-#define MSG_SIMPLE_TAG         0x20
-#define MSG_IDENTIFY           0x80
-
-/* WDTR Message values */
-#define        BUS_8_BIT               0x00
-#define BUS_16_BIT             0x01
-#define BUS_32_BIT             0x02
-
-#define MAX_OFFSET_8BIT                0x0f
-#define MAX_OFFSET_16BIT       0x08
+#define        BUSSPD                          0x86
+#define                DFTHRSH_100             0xc0
+#define                DFTHRSH                 0xc0
+#define                STBOFF                  0x38
+#define                STBON                   0x07
+
+#define        DSPCISTATUS                     0x86
+
+#define        HCNTRL                          0x87
+#define                POWRDN                  0x40
+#define                SWINT                   0x10
+#define                IRQMS                   0x08
+#define                PAUSE                   0x04
+#define                INTEN                   0x02
+#define                CHIPRST                 0x01
+#define                CHIPRSTACK              0x01
+
+#define        HADDR                           0x88
+
+#define        HCNT                            0x8c
+
+#define        SCBPTR                          0x90
+
+#define        INTSTAT                         0x91
+#define                SEQINT_MASK             0xf1
+#define                DATA_OVERRUN            0xe1
+#define                MSGIN_PHASEMIS          0xd1
+#define                MSG_BUFFER_BUSY         0xc1
+#define                AWAITING_MSG            0xa1
+#define                ABORT_CMDCMPLT          0x91
+#define                RESIDUAL                0x81
+#define                BAD_STATUS              0x71
+#define                REJECT_MSG              0x61
+#define                NO_MATCH_BUSY           0x51
+#define                EXTENDED_MSG            0x41
+#define                NO_MATCH                0x31
+#define                NO_IDENT                0x21
+#define                SEND_REJECT             0x11
+#define                INT_PEND                0x0f
+#define                BRKADRINT               0x08
+#define                SCSIINT                 0x04
+#define                CMDCMPLT                0x02
+#define                BAD_PHASE               0x01
+#define                SEQINT                  0x01
+
+#define        CLRINT                          0x92
+#define                CLRBRKADRINT            0x08
+#define                CLRSCSIINT              0x04
+#define                CLRCMDINT               0x02
+#define                CLRSEQINT               0x01
+
+#define        ERROR                           0x92
+#define                PARERR                  0x08
+#define                ILLOPCODE               0x04
+#define                ILLSADDR                0x02
+#define                ILLHADDR                0x01
+
+#define        DFCNTRL                         0x93
+
+#define        DFSTATUS                        0x94
+#define                DWORDEMP                0x20
+#define                MREQPEND                0x10
+#define                HDONE                   0x08
+#define                DFTHRESH                0x04
+#define                FIFOFULL                0x02
+#define                FIFOEMP                 0x01
+
+#define        DFDAT                           0x99
+
+#define        SCBCNT                          0x9a
+#define                SCBAUTO                 0x80
+#define                SCBCNT_MASK             0x1f
+
+#define        QINFIFO                         0x9b
+
+#define        QINCNT                          0x9c
+
+#define        QOUTFIFO                        0x9d
+
+#define        QOUTCNT                         0x9e
+
+#define        SCB_CONTROL                     0xa0
+#define                MK_MESSAGE              0x80
+#define                DISCENB                 0x40
+#define                TAG_ENB                 0x20
+#define                MUST_DMAUP_SCB          0x10
+#define                ABORT_SCB               0x08
+#define                DISCONNECTED            0x04
+#define                SCB_TAG_TYPE            0x03
+
+#define        SCB_BASE                        0xa0
+
+#define        SCB_TCL                         0xa1
+#define                TID                     0xf0
+#define                SELBUSB                 0x08
+#define                LID                     0x07
+
+#define        SCB_TARGET_STATUS               0xa2
+
+#define        SCB_SGCOUNT                     0xa3
+
+#define        SCB_SGPTR                       0xa4
+
+#define        SCB_RESID_SGCNT                 0xa8
+
+#define        SCB_RESID_DCNT                  0xa9
+
+#define        SCB_DATAPTR                     0xac
+
+#define        SCB_DATACNT                     0xb0
+
+#define        SCB_LINKED_NEXT                 0xb3
+
+#define        SCB_CMDPTR                      0xb4
+
+#define        SCB_CMDLEN                      0xb8
+
+#define        SCB_TAG                         0xb9
+
+#define        SCB_NEXT                        0xba
+
+#define        SCB_PREV                        0xbb
+
+#define        SCB_BUSYTARGETS                 0xbc
+
+#define        SEECTL_2840                     0xc0
+#define                CS_2840                 0x04
+#define                CK_2840                 0x02
+#define                DO_2840                 0x01
+
+#define        STATUS_2840                     0xc1
+#define                EEPROM_TF               0x80
+#define                BIOS_SEL                0x60
+#define                ADSEL                   0x1e
+#define                DI_2840                 0x01
+
+
+#define        BUS_8_BIT       0x00
+#define        MAX_OFFSET_8BIT 0x0f
+#define        BUS_16_BIT      0x01
+#define        MAX_OFFSET_16BIT        0x08
+#define        SCB_LIST_NULL   0xff
+#define        SG_SIZEOF       0x08
+#define        BUS_32_BIT      0x02
diff --git a/drivers/scsi/aic7xxx_seq.h b/drivers/scsi/aic7xxx_seq.h
new file mode 100644 (file)
index 0000000..dda2f7a
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static u_int8_t seqprog[] = {
+       0xff, 0x6a, 0x03, 0x02,
+       0x12, 0x6a, 0x00, 0x00,
+       0x00, 0x65, 0x6f, 0x16,
+       0x40, 0x0b, 0x3c, 0x1a,
+       0x20, 0x0b, 0x37, 0x1a,
+       0x40, 0x00, 0x03, 0x1a,
+       0x08, 0x1f, 0x1f, 0x04,
+       0x40, 0x0b, 0x3c, 0x1a,
+       0x20, 0x0b, 0x37, 0x1a,
+       0x40, 0x00, 0x03, 0x1a,
+       0x08, 0x1f, 0x1f, 0x04,
+       0xff, 0x48, 0x2c, 0x18,
+       0xff, 0x40, 0x64, 0x02,
+       0x00, 0x9c, 0x03, 0x1e,
+       0x00, 0x6a, 0xaa, 0x17,
+       0xff, 0x65, 0x03, 0x1c,
+       0xff, 0x9b, 0x58, 0x02,
+       0xff, 0x58, 0x90, 0x02,
+       0x0d, 0x6a, 0x3d, 0x00,
+       0x00, 0x58, 0x74, 0x17,
+       0x28, 0xa0, 0x2a, 0x1a,
+       0x50, 0x6a, 0x60, 0x00,
+       0xff, 0x90, 0x4a, 0x02,
+       0x00, 0xa1, 0x9e, 0x17,
+       0xff, 0x6c, 0x59, 0x02,
+       0xff, 0x59, 0x27, 0x1c,
+       0xff, 0x4a, 0x90, 0x02,
+       0x00, 0x65, 0xa7, 0x17,
+       0x00, 0x6a, 0x4f, 0x17,
+       0xff, 0x65, 0x1f, 0x18,
+       0x51, 0x6a, 0x91, 0x00,
+       0xff, 0x58, 0xb3, 0x02,
+       0x00, 0x65, 0xb8, 0x17,
+       0x10, 0x6a, 0x60, 0x00,
+       0x00, 0x65, 0x03, 0x10,
+       0xff, 0x59, 0x90, 0x02,
+       0xff, 0x58, 0xb3, 0x02,
+       0x10, 0x6a, 0x60, 0x00,
+       0x00, 0x65, 0x03, 0x10,
+       0xff, 0x58, 0x6d, 0x02,
+       0xff, 0x4a, 0x90, 0x02,
+       0x10, 0x6a, 0x60, 0x00,
+       0xff, 0x48, 0xba, 0x02,
+       0xff, 0x90, 0x48, 0x02,
+       0xff, 0x48, 0x90, 0x02,
+       0x00, 0x65, 0x2f, 0x16,
+       0x00, 0x65, 0x03, 0x10,
+       0xf7, 0x1f, 0x65, 0x02,
+       0x08, 0xa1, 0x64, 0x02,
+       0x00, 0x65, 0x65, 0x00,
+       0xff, 0x65, 0x1f, 0x02,
+       0xf0, 0xa1, 0x64, 0x02,
+       0x0f, 0x05, 0x05, 0x02,
+       0x00, 0x05, 0x05, 0x00,
+       0x5a, 0x6a, 0x00, 0x01,
+       0xff, 0x6a, 0x34, 0x02,
+       0x20, 0x6a, 0x0b, 0x00,
+       0xf0, 0x19, 0x42, 0x02,
+       0x80, 0x41, 0x41, 0x00,
+       0x00, 0x65, 0x4c, 0x10,
+       0x12, 0x6a, 0x00, 0x00,
+       0x40, 0x6a, 0x0b, 0x00,
+       0xff, 0x48, 0x90, 0x02,
+       0xff, 0xba, 0x48, 0x02,
+       0xff, 0xa1, 0x42, 0x02,
+       0x07, 0xa1, 0x35, 0x02,
+       0x40, 0xa0, 0x64, 0x02,
+       0x00, 0x35, 0x35, 0x00,
+       0x80, 0x35, 0x35, 0x00,
+       0x01, 0x6a, 0x34, 0x00,
+       0x20, 0xa0, 0x4a, 0x1e,
+       0x23, 0xa0, 0x36, 0x02,
+       0xff, 0xb9, 0x37, 0x02,
+       0x02, 0x34, 0x34, 0x06,
+       0x80, 0xa0, 0x4c, 0x1e,
+       0xa1, 0x6a, 0x91, 0x00,
+       0x08, 0x6a, 0x0c, 0x00,
+       0x08, 0x11, 0x11, 0x00,
+       0x1a, 0x01, 0x01, 0x00,
+       0x31, 0x6a, 0x65, 0x00,
+       0x80, 0x42, 0x52, 0x1a,
+       0xff, 0x65, 0x65, 0x06,
+       0xff, 0x42, 0x6e, 0x02,
+       0xff, 0x6e, 0x64, 0x02,
+       0x00, 0x6c, 0x56, 0x1e,
+       0x20, 0x01, 0x01, 0x00,
+       0x4c, 0x42, 0x64, 0x0a,
+       0x08, 0x1f, 0x5a, 0x1e,
+       0x08, 0x42, 0x42, 0x00,
+       0x08, 0x64, 0x64, 0x00,
+       0x20, 0x64, 0x65, 0x06,
+       0xff, 0x6c, 0x04, 0x02,
+       0x01, 0x0c, 0x5c, 0x1e,
+       0x04, 0x0c, 0x5c, 0x1a,
+       0xe0, 0x03, 0x64, 0x02,
+       0xff, 0x64, 0x4c, 0x02,
+       0xff, 0x64, 0x03, 0x02,
+       0x00, 0x6a, 0x74, 0x1c,
+       0x40, 0x64, 0x79, 0x1c,
+       0x80, 0x64, 0xa5, 0x1c,
+       0xa0, 0x64, 0xb0, 0x1c,
+       0xc0, 0x64, 0xad, 0x1c,
+       0xe0, 0x64, 0xc3, 0x1c,
+       0x01, 0x6a, 0x91, 0x00,
+       0x00, 0x65, 0x5c, 0x10,
+       0xf7, 0x11, 0x11, 0x02,
+       0x00, 0x65, 0x6f, 0x16,
+       0xff, 0x06, 0x6a, 0x02,
+       0x09, 0x0c, 0x6c, 0x1e,
+       0x08, 0x0c, 0x03, 0x1a,
+       0x01, 0x6a, 0x91, 0x00,
+       0xff, 0x6a, 0x93, 0x02,
+       0xff, 0x6a, 0x04, 0x02,
+       0xdf, 0x01, 0x01, 0x02,
+       0x01, 0x6a, 0x4c, 0x00,
+       0x0f, 0x41, 0x41, 0x03,
+       0x7d, 0x6a, 0x3d, 0x00,
+       0x00, 0x65, 0x7a, 0x10,
+       0x08, 0x6a, 0x66, 0x00,
+       0xa9, 0x6a, 0x71, 0x17,
+       0x00, 0x65, 0x82, 0x10,
+       0x79, 0x6a, 0x3d, 0x00,
+       0x00, 0x65, 0x4c, 0x17,
+       0x10, 0x41, 0x76, 0x1a,
+       0x88, 0x6a, 0x66, 0x00,
+       0xac, 0x6a, 0x6d, 0x17,
+       0x00, 0x65, 0x6a, 0x17,
+       0xff, 0xa3, 0x43, 0x02,
+       0x44, 0x6a, 0x66, 0x00,
+       0xa4, 0x6a, 0x70, 0x17,
+       0xff, 0x43, 0x88, 0x1a,
+       0x80, 0x02, 0x02, 0x00,
+       0xff, 0x6a, 0x8c, 0x00,
+       0xff, 0x6a, 0x8d, 0x00,
+       0xff, 0x6a, 0x8e, 0x00,
+       0x00, 0x65, 0x6a, 0x17,
+       0x01, 0x43, 0x8a, 0x18,
+       0xbf, 0x3d, 0x3d, 0x02,
+       0x00, 0x3d, 0x41, 0x17,
+       0x80, 0x02, 0xa2, 0x1a,
+       0xff, 0x65, 0x9c, 0x1e,
+       0xff, 0x43, 0x43, 0x06,
+       0xff, 0x43, 0x9c, 0x1e,
+       0xff, 0x6a, 0x64, 0x02,
+       0x08, 0x44, 0x44, 0x06,
+       0x00, 0x45, 0x45, 0x08,
+       0x88, 0x6a, 0x66, 0x00,
+       0x44, 0x6a, 0x70, 0x17,
+       0x08, 0x6a, 0x8c, 0x00,
+       0xff, 0x6a, 0x8d, 0x02,
+       0xff, 0x6a, 0x8e, 0x02,
+       0x0d, 0x93, 0x93, 0x00,
+       0x00, 0x65, 0x9a, 0x17,
+       0x88, 0x6a, 0x92, 0x17,
+       0x00, 0x65, 0x6a, 0x17,
+       0x10, 0x0c, 0x82, 0x1e,
+       0xff, 0x08, 0xa9, 0x02,
+       0xff, 0x09, 0xaa, 0x02,
+       0xff, 0x0a, 0xab, 0x02,
+       0xff, 0x43, 0xa8, 0x02,
+       0x10, 0x41, 0x41, 0x00,
+       0x00, 0x65, 0x5c, 0x10,
+       0x7f, 0x02, 0x02, 0x02,
+       0xe1, 0x6a, 0x91, 0x00,
+       0x00, 0x65, 0x5c, 0x10,
+       0x00, 0x65, 0x4c, 0x17,
+       0x88, 0x6a, 0x66, 0x00,
+       0xb4, 0x6a, 0x6f, 0x17,
+       0xff, 0x6a, 0x8d, 0x02,
+       0xff, 0x6a, 0x8e, 0x02,
+       0x00, 0x65, 0x6a, 0x17,
+       0x3d, 0x6a, 0x41, 0x17,
+       0x00, 0x65, 0x5c, 0x10,
+       0x00, 0x65, 0x4c, 0x17,
+       0xff, 0x06, 0xa2, 0x02,
+       0x00, 0x65, 0x5c, 0x10,
+       0xff, 0x34, 0xb2, 0x1a,
+       0x08, 0x6a, 0x2f, 0x17,
+       0x35, 0x6a, 0x65, 0x00,
+       0xff, 0x34, 0x66, 0x02,
+       0x01, 0x0c, 0xb4, 0x1e,
+       0x04, 0x0c, 0xb4, 0x1a,
+       0xe0, 0x03, 0x4c, 0x02,
+       0xa0, 0x4c, 0xc0, 0x18,
+       0xff, 0x66, 0xbb, 0x1a,
+       0x10, 0x4c, 0x03, 0x00,
+       0x00, 0x65, 0xb2, 0x10,
+       0x01, 0x66, 0xbd, 0x18,
+       0x40, 0x6a, 0x0c, 0x00,
+       0xff, 0x66, 0x66, 0x06,
+       0xff, 0x6c, 0x06, 0x02,
+       0x00, 0x65, 0xb4, 0x10,
+       0x40, 0x6a, 0x0c, 0x00,
+       0xff, 0x6a, 0x34, 0x02,
+       0x00, 0x65, 0x5c, 0x10,
+       0x64, 0x6a, 0x3c, 0x17,
+       0xff, 0x64, 0x4b, 0x02,
+       0x80, 0x64, 0x0b, 0x1b,
+       0x04, 0x64, 0xfe, 0x1c,
+       0x02, 0x64, 0x01, 0x1d,
+       0x00, 0x6a, 0xd1, 0x1c,
+       0x03, 0x64, 0x09, 0x1d,
+       0x01, 0x64, 0xf2, 0x1c,
+       0x07, 0x64, 0x2d, 0x1d,
+       0x08, 0x64, 0xcf, 0x1c,
+       0x11, 0x6a, 0x91, 0x00,
+       0x07, 0x6a, 0x2f, 0x17,
+       0xff, 0x06, 0x6a, 0x02,
+       0x00, 0x65, 0x5c, 0x10,
+       0xff, 0xa8, 0xd3, 0x1a,
+       0xff, 0xa2, 0xda, 0x1e,
+       0x01, 0x6a, 0x3d, 0x00,
+       0x00, 0xb9, 0x74, 0x17,
+       0xff, 0xa2, 0xda, 0x1e,
+       0x71, 0x6a, 0x91, 0x00,
+       0x40, 0x59, 0xda, 0x18,
+       0xff, 0xb9, 0xb3, 0x02,
+       0x00, 0x65, 0xe4, 0x10,
+       0x20, 0xa0, 0xe0, 0x1a,
+       0xff, 0x90, 0x4a, 0x02,
+       0xff, 0xb3, 0x49, 0x02,
+       0x00, 0xa1, 0x9e, 0x17,
+       0xff, 0x49, 0x6d, 0x02,
+       0xff, 0x4a, 0x90, 0x02,
+       0xff, 0xb9, 0x9d, 0x02,
+       0x02, 0x6a, 0x91, 0x00,
+       0x08, 0xa0, 0xe4, 0x1e,
+       0x91, 0x6a, 0x91, 0x00,
+       0xff, 0xb3, 0xf0, 0x1c,
+       0xff, 0xb3, 0x64, 0x02,
+       0x00, 0xb9, 0xea, 0x1c,
+       0x00, 0x65, 0xa7, 0x17,
+       0xff, 0x64, 0x90, 0x02,
+       0x00, 0x65, 0xec, 0x10,
+       0x0d, 0x6a, 0x3d, 0x00,
+       0x00, 0xb3, 0x74, 0x17,
+       0xff, 0x48, 0xba, 0x02,
+       0xff, 0x90, 0x48, 0x02,
+       0x00, 0x65, 0x2f, 0x16,
+       0x00, 0x65, 0x69, 0x10,
+       0x00, 0x65, 0xa7, 0x17,
+       0x00, 0x65, 0x69, 0x10,
+       0x4d, 0x6a, 0x37, 0x17,
+       0xff, 0x4d, 0x64, 0x02,
+       0x00, 0x66, 0x37, 0x17,
+       0xff, 0x64, 0x64, 0x06,
+       0x52, 0x66, 0xf8, 0x18,
+       0xff, 0x66, 0x66, 0x06,
+       0xff, 0x64, 0xf4, 0x1a,
+       0x41, 0x6a, 0x91, 0x00,
+       0x20, 0x59, 0xcd, 0x1c,
+       0x80, 0x59, 0xcf, 0x18,
+       0x10, 0x4c, 0x03, 0x00,
+       0x00, 0x65, 0xcf, 0x10,
+       0x04, 0xa0, 0xa0, 0x00,
+       0x00, 0x65, 0xb8, 0x17,
+       0x00, 0x65, 0x69, 0x10,
+       0x10, 0x41, 0xcf, 0x1e,
+       0xff, 0x43, 0xa3, 0x02,
+       0xa4, 0x6a, 0x66, 0x00,
+       0x44, 0x6a, 0x70, 0x17,
+       0xac, 0x6a, 0x66, 0x00,
+       0x14, 0x6a, 0x70, 0x17,
+       0xa9, 0x6a, 0x71, 0x17,
+       0x00, 0x65, 0xcf, 0x10,
+       0xef, 0x41, 0x41, 0x02,
+       0x00, 0x65, 0xcf, 0x10,
+       0x78, 0x64, 0xcd, 0x1a,
+       0x07, 0x64, 0x64, 0x02,
+       0x00, 0x42, 0x42, 0x00,
+       0x00, 0x42, 0x9e, 0x17,
+       0xff, 0x6c, 0x59, 0x02,
+       0xff, 0x59, 0x25, 0x19,
+       0xff, 0x59, 0x15, 0x1d,
+       0xff, 0x59, 0x90, 0x02,
+       0x04, 0xa0, 0x2a, 0x1f,
+       0x00, 0x65, 0x27, 0x11,
+       0xff, 0x06, 0x6a, 0x02,
+       0x01, 0x0c, 0x16, 0x1f,
+       0x04, 0x0c, 0x16, 0x1b,
+       0xe0, 0x03, 0x4c, 0x02,
+       0xe0, 0x4c, 0x2a, 0x19,
+       0x20, 0x12, 0x2a, 0x19,
+       0x20, 0x41, 0x41, 0x00,
+       0x59, 0x6a, 0x37, 0x17,
+       0xff, 0x3f, 0x64, 0x02,
+       0x00, 0x59, 0x65, 0x06,
+       0x00, 0x65, 0x2a, 0x13,
+       0xff, 0x59, 0x90, 0x02,
+       0xff, 0x42, 0x64, 0x02,
+       0x00, 0xa1, 0x2a, 0x19,
+       0x20, 0xa0, 0x2a, 0x1f,
+       0x04, 0xa0, 0x2a, 0x1f,
+       0x00, 0x6a, 0x4f, 0x17,
+       0xff, 0x65, 0x2a, 0x1d,
+       0xfb, 0xa0, 0xa0, 0x02,
+       0x40, 0x41, 0x41, 0x00,
+       0x00, 0x65, 0xcf, 0x10,
+       0x31, 0x6a, 0x91, 0x00,
+       0x0c, 0x6a, 0x2f, 0x17,
+       0x00, 0x65, 0xcf, 0x10,
+       0x61, 0x6a, 0x91, 0x00,
+       0x00, 0x65, 0xcf, 0x10,
+       0x50, 0x6a, 0x60, 0x00,
+       0xff, 0x34, 0x33, 0x1f,
+       0x10, 0x6a, 0x60, 0x00,
+       0xc1, 0x6a, 0x91, 0x00,
+       0x10, 0x4c, 0x03, 0x00,
+       0x01, 0x6a, 0x34, 0x00,
+       0xff, 0x65, 0x35, 0x02,
+       0x10, 0x6a, 0x60, 0x01,
+       0xff, 0x06, 0x6a, 0x02,
+       0x01, 0x0c, 0x38, 0x1f,
+       0x04, 0x0c, 0x38, 0x1b,
+       0xe0, 0x03, 0x4c, 0x02,
+       0xe0, 0x4c, 0x3f, 0x19,
+       0xff, 0x65, 0x66, 0x02,
+       0xff, 0x12, 0x6d, 0x03,
+       0xff, 0x06, 0x6a, 0x03,
+       0xd1, 0x6a, 0x91, 0x00,
+       0x00, 0x65, 0x5c, 0x10,
+       0xff, 0x65, 0x93, 0x02,
+       0x01, 0x0b, 0x49, 0x1b,
+       0x10, 0x0c, 0x42, 0x1f,
+       0x04, 0x0b, 0x46, 0x1b,
+       0xff, 0x6a, 0x65, 0x02,
+       0x04, 0x93, 0x48, 0x1b,
+       0x01, 0x94, 0x47, 0x1f,
+       0x10, 0x94, 0x48, 0x1b,
+       0xc7, 0x93, 0x93, 0x02,
+       0x38, 0x93, 0x4a, 0x1b,
+       0xff, 0x6a, 0x6a, 0x03,
+       0x80, 0x41, 0x4b, 0x1f,
+       0x40, 0x41, 0x4b, 0x1b,
+       0x21, 0x6a, 0x91, 0x01,
+       0xff, 0x65, 0x90, 0x02,
+       0xff, 0x59, 0x64, 0x02,
+       0x00, 0xb9, 0x53, 0x19,
+       0x04, 0xa0, 0x5d, 0x1b,
+       0x01, 0x65, 0x65, 0x06,
+       0xff, 0x3e, 0x64, 0x02,
+       0x00, 0x65, 0x4f, 0x19,
+       0x00, 0x6a, 0xaa, 0x17,
+       0x0d, 0x6a, 0x3d, 0x00,
+       0x00, 0x59, 0x74, 0x17,
+       0xff, 0xa8, 0x5b, 0x1f,
+       0x10, 0xa0, 0xa0, 0x00,
+       0x08, 0xa0, 0x4b, 0x1f,
+       0xff, 0x6a, 0x65, 0x01,
+       0x08, 0xa0, 0x5c, 0x1b,
+       0xff, 0xba, 0x63, 0x1d,
+       0xff, 0xbb, 0x49, 0x02,
+       0xff, 0xba, 0x90, 0x02,
+       0xff, 0x49, 0xbb, 0x02,
+       0xff, 0x65, 0x90, 0x02,
+       0xff, 0xbb, 0x68, 0x1d,
+       0xff, 0xba, 0x49, 0x02,
+       0xff, 0xbb, 0x90, 0x02,
+       0xff, 0x49, 0xba, 0x02,
+       0xff, 0x65, 0x90, 0x03,
+       0xff, 0xba, 0x52, 0x03,
+       0xff, 0x6a, 0x6a, 0x03,
+       0xff, 0x8c, 0x08, 0x02,
+       0xff, 0x8d, 0x09, 0x02,
+       0xff, 0x8e, 0x0a, 0x03,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x02,
+       0xff, 0x6c, 0x6d, 0x03,
+       0x3d, 0x65, 0x66, 0x0a,
+       0x55, 0x65, 0x64, 0x0a,
+       0x00, 0x54, 0x88, 0x06,
+       0xff, 0x66, 0x64, 0x02,
+       0x00, 0x55, 0x89, 0x08,
+       0xff, 0x6a, 0x64, 0x02,
+       0x00, 0x56, 0x8a, 0x08,
+       0x00, 0x57, 0x8b, 0x08,
+       0x1c, 0x6a, 0x8c, 0x00,
+       0xff, 0x6a, 0x8d, 0x02,
+       0xff, 0x6a, 0x8e, 0x02,
+       0xff, 0x3d, 0x93, 0x02,
+       0x04, 0x3d, 0x8c, 0x1b,
+       0xa0, 0x6a, 0x65, 0x00,
+       0x1c, 0x65, 0x64, 0x06,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0xff, 0x6c, 0x99, 0x02,
+       0x00, 0x65, 0x83, 0x19,
+       0x0a, 0x93, 0x93, 0x00,
+       0x00, 0x65, 0x9a, 0x17,
+       0x04, 0x3d, 0x4b, 0x1f,
+       0xa0, 0x6a, 0x92, 0x17,
+       0x00, 0x65, 0x93, 0x17,
+       0x00, 0x65, 0x93, 0x17,
+       0x00, 0x65, 0x93, 0x11,
+       0xff, 0x65, 0x66, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x02,
+       0xff, 0x99, 0x6d, 0x03,
+       0x08, 0x94, 0x9a, 0x1f,
+       0xf7, 0x93, 0x93, 0x02,
+       0x08, 0x93, 0x9c, 0x1b,
+       0xff, 0x6a, 0x6a, 0x03,
+       0xff, 0x65, 0x66, 0x02,
+       0x4c, 0x66, 0x66, 0x0a,
+       0x03, 0x66, 0x66, 0x02,
+       0xbc, 0x66, 0x66, 0x06,
+       0x6a, 0x65, 0x64, 0x0a,
+       0x08, 0x65, 0xa5, 0x1f,
+       0x02, 0x64, 0x64, 0x06,
+       0xff, 0x64, 0x90, 0x02,
+       0xff, 0x66, 0x65, 0x03,
+       0xff, 0x53, 0xba, 0x02,
+       0xff, 0x6a, 0xb9, 0x00,
+       0xff, 0x90, 0x53, 0x03,
+       0xff, 0x53, 0xb6, 0x19,
+       0xff, 0x52, 0xad, 0x19,
+       0xff, 0x6a, 0x65, 0x01,
+       0xff, 0x52, 0x90, 0x02,
+       0x10, 0xa0, 0xb1, 0x1f,
+       0xef, 0xa0, 0xa0, 0x02,
+       0x00, 0x65, 0xb3, 0x11,
+       0xff, 0xa8, 0xb3, 0x1b,
+       0xff, 0xb3, 0xb5, 0x1d,
+       0x01, 0x6a, 0x3d, 0x00,
+       0x00, 0xb9, 0x74, 0x17,
+       0x00, 0x90, 0x5e, 0x11,
+       0xff, 0x53, 0x90, 0x02,
+       0xff, 0xba, 0x53, 0x03,
+       0xff, 0x6a, 0xbb, 0x00,
+       0xff, 0x52, 0xba, 0x02,
+       0xff, 0x90, 0x52, 0x02,
+       0xff, 0xba, 0x4b, 0x1d,
+       0xff, 0xba, 0x90, 0x02,
+       0xff, 0x52, 0xbb, 0x02,
+       0xff, 0x52, 0x90, 0x03,
+};
+#define        ULTRA                   0x8
+#define        SCB_PAGING              0x4
+#define        TWIN_CHANNEL            0x2
+struct patch {
+       int     options;
+       int     negative;
+       int     begin;
+       int     end;
+} patches[] = {
+       { 0x00000002, 0, 0x006, 0x00b },
+       { 0x00000004, 0, 0x00e, 0x010 },
+       { 0x00000004, 1, 0x011, 0x012 },
+       { 0x00000004, 0, 0x01a, 0x023 },
+       { 0x00000004, 1, 0x023, 0x027 },
+       { 0x00000002, 0, 0x02f, 0x033 },
+       { 0x00000008, 0, 0x04f, 0x056 },
+       { 0x00000004, 1, 0x0e5, 0x0ea },
+       { 0x00000004, 0, 0x0ff, 0x100 },
+       { 0x00000004, 0, 0x110, 0x111 },
+       { 0x00000004, 1, 0x111, 0x115 },
+       { 0x00000004, 1, 0x120, 0x125 },
+       { 0x00000004, 0, 0x125, 0x127 },
+       { 0x00000004, 0, 0x14f, 0x169 },
+       { 0x00000004, 1, 0x169, 0x16a },
+       { 0x00000004, 0, 0x1aa, 0x1bf },
+       { 0x00000000, 0, 0x000, 0x000 }
+};
index a77fe820f8c71f47de260297cf8aafe92e62319f..de8305b2345705aeddf7c39a96ce26b680ed02cb 100644 (file)
 #include "esp.h"
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -303,6 +307,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
 #endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+    IDESCSI,
+#endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
 #endif
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
new file mode 100644 (file)
index 0000000..8053e0c
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * linux/drivers/scsi/ide-scsi.c       Version 0.3 - ALPHA     Jul  24, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * Emulation of a SCSI host adapter for IDE ATAPI devices.
+ *
+ * With this driver, one can use the Linux SCSI drivers instead of the
+ * native IDE ATAPI drivers.
+ *
+ * Ver 0.1   Dec  3 96   Initial version.
+ * Ver 0.2   Jan 26 97   Fixed bug in cleanup_module() and added emulation
+ *                        of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
+ *                        to Janos Farkas for pointing this out.
+ *                       Avoid using bitfields in structures for m68k.
+ *                       Added Scather/Gather and DMA support.
+ * Ver 0.3   Jul 24 97   Add support for ATAPI PD/CD drives.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "../block/ide.h"
+
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "ide-scsi.h"
+
+#define IDESCSI_DEBUG_LOG              0
+
+typedef struct idescsi_pc_s {
+       u8 c[12];                               /* Actual packet bytes */
+       int request_transfer;                   /* Bytes to transfer */
+       int actually_transferred;               /* Bytes actually transferred */
+       int buffer_size;                        /* Size of our data buffer */
+       struct request *rq;                     /* The corresponding request */
+       byte *buffer;                           /* Data buffer */
+       byte *current_position;                 /* Pointer into the above buffer */
+       struct scatterlist *sg;                 /* Scather gather table */
+       int b_count;                            /* Bytes transferred from current entry */
+       Scsi_Cmnd *scsi_cmd;                    /* SCSI command */
+       void (*done)(Scsi_Cmnd *);              /* Scsi completion routine */
+       unsigned int flags;                     /* Status/Action flags */
+} idescsi_pc_t;
+
+/*
+ *     Packet command status bits.
+ */
+#define PC_DMA_IN_PROGRESS             0       /* 1 while DMA in progress */
+#define PC_WRITING                     1       /* Data direction */
+
+typedef struct {
+       ide_drive_t *drive;
+       idescsi_pc_t *pc;                       /* Current packet command */
+       unsigned int flags;                     /* Status/Action flags */
+       int media;
+} idescsi_scsi_t;
+
+/*
+ *     Per ATAPI device status bits.
+ */
+#define IDESCSI_DRQ_INTERRUPT          0       /* DRQ interrupt device */
+
+/*
+ *     ide-scsi requests.
+ */
+#define IDESCSI_PC_RQ                  90
+
+/*
+ *     Bits of the interrupt reason register.
+ */
+#define IDESCSI_IREASON_COD    0x1             /* Information transferred is command */
+#define IDESCSI_IREASON_IO     0x2             /* The device requests us to read */
+
+#define IDE_MIN(a,b)      ((a)<(b) ? (a):(b))
+#define IDE_MAX(a,b)      ((a)>(b) ? (a):(b))
+
+static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+       while (bcount--)
+               IN_BYTE (IDE_DATA_REG);
+}
+
+static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+       while (bcount--)
+               OUT_BYTE (0, IDE_DATA_REG);
+}
+
+/*
+ *     PIO data transfer routines using the scather gather table.
+ */
+static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+       int count;
+
+       while (bcount) {
+               if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+                       printk (KERN_ERR "ide-scsi: scather gather table too small, discarding data\n");
+                       idescsi_discard_data (drive, bcount);
+                       return;
+               }
+               count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+               atapi_input_bytes (drive, pc->sg->address + pc->b_count, count);
+               bcount -= count; pc->b_count += count;
+               if (pc->b_count == pc->sg->length) {
+                       pc->sg++;
+                       pc->b_count = 0;
+               }
+       }
+}
+
+static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+       int count;
+
+       while (bcount) {
+               if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+                       printk (KERN_ERR "ide-scsi: scather gather table too small, padding with zeros\n");
+                       idescsi_output_zeros (drive, bcount);
+                       return;
+               }
+               count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+               atapi_output_bytes (drive, pc->sg->address + pc->b_count, count);
+               bcount -= count; pc->b_count += count;
+               if (pc->b_count == pc->sg->length) {
+                       pc->sg++;
+                       pc->b_count = 0;
+               }
+       }
+}
+
+/*
+ *     Most of the SCSI commands are supported directly by ATAPI devices.
+ *     idescsi_transform_pc handles the few exceptions.
+ */
+static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+       idescsi_scsi_t *scsi = drive->scsi;
+       u8 *c = pc->c, *buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;
+       int i;
+
+       if (scsi->media == TYPE_ROM) {
+               if (c[0] == READ_6 || c[0] == WRITE_6) {
+                       c[8] = c[4];            c[5] = c[3];            c[4] = c[2];
+                       c[3] = c[1] & 0x1f;     c[2] = 0;               c[1] &= 0xe0;
+                       c[0] += (READ_10 - READ_6);
+               }
+               if (c[0] == MODE_SENSE || (c[0] == MODE_SELECT && buf[3] == 8)) {
+                       pc->request_transfer -= 4;
+                       memset (c, 0, 12);
+                       c[0] = sc[0] | 0x40;    c[2] = sc[2];           c[8] = sc[4] - 4;
+                       if (c[0] == MODE_SENSE_10) return;
+                       for (i = 0; i <= 7; i++) buf[i] = 0;
+                       for (i = 8; i < pc->buffer_size - 4; i++) buf[i] = buf[i + 4];
+               }
+       }
+}
+
+static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+       idescsi_scsi_t *scsi = drive->scsi;
+       u8 *buf = pc->buffer;
+       int i;
+
+       if (scsi->media == TYPE_ROM) {
+               if (pc->c[0] == MODE_SENSE_10 && pc->scsi_cmd->cmnd[0] == MODE_SENSE) {
+                       buf[0] = buf[1];        buf[1] = buf[2];
+                       buf[2] = 0;             buf[3] = 8;
+                       for (i = pc->buffer_size - 1; i >= 12; i--) buf[i] = buf[i - 4];
+                       for (i = 11; i >= 4; i--) buf[i] = 0;
+               }
+               if (pc->c[0] == INQUIRY)
+                       buf[2] |= 2;
+       }
+}
+
+static inline void idescsi_free_bh (struct buffer_head *bh)
+{
+       struct buffer_head *bhp;
+
+       while (bh) {
+               bhp = bh;
+               bh = bh->b_reqnext;
+               kfree (bhp);
+       }
+}
+
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+       ide_drive_t *drive = hwgroup->drive;
+       idescsi_scsi_t *scsi = drive->scsi;
+       struct request *rq = hwgroup->rq;
+       idescsi_pc_t *pc = (idescsi_pc_t *) rq->buffer;
+
+       if (rq->cmd != IDESCSI_PC_RQ) {
+               ide_end_request (uptodate, hwgroup);
+               return;
+       }
+       ide_end_drive_cmd (drive, 0, 0);
+       if (rq->errors >= ERROR_MAX) {
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+               pc->scsi_cmd->result = DID_ERROR << 16;
+       } else if (rq->errors) {
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+               pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+       } else {
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: success for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+               pc->scsi_cmd->result = DID_OK << 16;
+               idescsi_transform_pc2 (drive, pc);
+       }
+       pc->done(pc->scsi_cmd);
+       idescsi_free_bh (rq->bh);
+       kfree(pc); kfree(rq);
+       scsi->pc = NULL;
+}
+
+/*
+ *     Our interrupt handler.
+ */
+static void idescsi_pc_intr (ide_drive_t *drive)
+{
+       idescsi_scsi_t *scsi = drive->scsi;
+       byte status, ireason;
+       int bcount;
+       idescsi_pc_t *pc=scsi->pc;
+       struct request *rq = pc->rq;
+       unsigned int temp;
+
+#if IDESCSI_DEBUG_LOG
+       printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
+#endif /* IDESCSI_DEBUG_LOG */
+
+       if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: DMA complete\n", drive->name);
+#endif /* IDESCSI_DEBUG_LOG */
+               pc->actually_transferred=pc->request_transfer;
+               (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+       }
+
+       status = GET_STAT();                                            /* Clear the interrupt */
+
+       if ((status & DRQ_STAT) == 0) {                                 /* No more interrupts */
+#if IDESCSI_DEBUG_LOG
+               printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDESCSI_DEBUG_LOG */
+               sti();
+               if (status & ERR_STAT)
+                       rq->errors++;
+               idescsi_end_request (1, HWGROUP(drive));
+               return;
+       }
+       bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG);
+       ireason = IN_BYTE (IDE_IREASON_REG);
+
+       if (ireason & IDESCSI_IREASON_COD) {
+               printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
+               ide_do_reset (drive);
+               return;
+       }
+       if (ireason & IDESCSI_IREASON_IO) {
+               temp = pc->actually_transferred + bcount;
+               if ( temp > pc->request_transfer) {
+                       if (temp > pc->buffer_size) {
+                               printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
+                               idescsi_discard_data (drive,bcount);
+                               ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);
+                               return;
+                       }
+#if IDESCSI_DEBUG_LOG
+                       printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
+#endif /* IDESCSI_DEBUG_LOG */
+               }
+       }
+       if (ireason & IDESCSI_IREASON_IO) {
+               if (pc->sg)
+                       idescsi_input_buffers (drive, pc, bcount);
+               else
+                       atapi_input_bytes (drive,pc->current_position,bcount);
+       } else {
+               if (pc->sg)
+                       idescsi_output_buffers (drive, pc, bcount);
+               else
+                       atapi_output_bytes (drive,pc->current_position,bcount);
+       }
+       pc->actually_transferred+=bcount;                               /* Update the current position */
+       pc->current_position+=bcount;
+
+       ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);              /* And set the interrupt handler again */
+}
+
+static void idescsi_transfer_pc (ide_drive_t *drive)
+{
+       idescsi_scsi_t *scsi = drive->scsi;
+       byte ireason;
+
+       if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+               printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
+               return;
+       }
+       ireason = IN_BYTE (IDE_IREASON_REG);
+       if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) {
+               printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n");
+               ide_do_reset (drive);
+               return;
+       }
+       ide_set_handler (drive, &idescsi_pc_intr, WAIT_CMD);    /* Set the interrupt routine */
+       atapi_output_bytes (drive, scsi->pc->c, 12);            /* Send the actual packet */
+}
+
+/*
+ *     Issue a packet command
+ */
+static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+       idescsi_scsi_t *scsi = drive->scsi;
+       int bcount;
+       struct request *rq = pc->rq;
+       int dma_ok = 0;
+
+       scsi->pc=pc;                                                    /* Set the current packet command */
+       pc->actually_transferred=0;                                     /* We haven't transferred any data yet */
+       pc->current_position=pc->buffer;
+       bcount = IDE_MIN (pc->request_transfer, 63 * 1024);             /* Request to transfer the entire buffer at once */
+
+       if (drive->using_dma && rq->bh)
+               dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+
+       OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+       OUT_BYTE (dma_ok,IDE_FEATURE_REG);
+       OUT_BYTE (bcount >> 8,IDE_BCOUNTH_REG);
+       OUT_BYTE (bcount & 0xff,IDE_BCOUNTL_REG);
+       OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+       if (dma_ok) {
+               set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+               (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+       }
+       if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
+               ide_set_handler (drive, &idescsi_transfer_pc, WAIT_CMD);
+               OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);              /* Issue the packet command */
+       } else {
+               OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+               idescsi_transfer_pc (drive);
+       }
+}
+
+/*
+ *     idescsi_do_request is our request handling function.
+ */
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+#if IDESCSI_DEBUG_LOG
+       printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+       printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDESCSI_DEBUG_LOG */
+
+       if (rq->cmd == IDESCSI_PC_RQ) {
+               idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
+               return;
+       }
+       printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd);
+       idescsi_end_request (0,HWGROUP (drive));
+}
+
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+       MOD_DEC_USE_COUNT;
+}
+
+static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
+static int idescsi_initialized = 0;
+
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return -EINVAL;
+}
+
+static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+/*
+ *     idescsi_setup will register the driver for each scsi.
+ */
+void idescsi_setup (ide_drive_t *drive)
+{
+       idescsi_scsi_t *scsi;
+       int i, id;
+
+       if (!idescsi_initialized) {
+               idescsi_initialized = 1;
+               for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++)
+                       idescsi_drives[i] = NULL;
+       }
+       if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
+               printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
+               return;
+       }
+       for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+       idescsi_drives[id] = drive;
+       drive->scsi = scsi;
+       drive->ready_stat = 0;
+       memset (scsi, 0, sizeof (idescsi_scsi_t));
+       scsi->drive = drive;
+       scsi->media = (drive->id->config >> 8) & 0x1f;
+       if (drive->id && (drive->id->config & 0x0060) == 0x20)
+               set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+}
+
+int idescsi_detect (Scsi_Host_Template *host_template)
+{
+       struct Scsi_Host *host;
+       int id;
+
+       host_template->proc_dir = &idescsi_proc_dir;
+       host = scsi_register(host_template, 0);
+       for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+       host->max_id = id;
+       host->can_queue = host->cmd_per_lun * id;
+       return 1;
+}
+
+int idescsi_release (struct Scsi_Host *host)
+{
+       return 0;
+}
+
+const char *idescsi_info (struct Scsi_Host *host)
+{
+       return "SCSI host adapter emulation for IDE ATAPI devices";
+}
+
+static inline struct buffer_head *idescsi_kmalloc_bh (int count)
+{
+       struct buffer_head *bh, *bhp, *first_bh;
+
+       if ((first_bh = bhp = bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+               goto abort;
+       memset (bh, 0, sizeof (struct buffer_head));
+       bh->b_reqnext = NULL;
+       while (--count) {
+               if ((bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+                       goto abort;
+               memset (bh, 0, sizeof (struct buffer_head));
+               bhp->b_reqnext = bh;
+               bhp = bh;
+               bh->b_reqnext = NULL;
+       }
+       return first_bh;
+abort:
+       idescsi_free_bh (first_bh);
+       return NULL;
+}
+
+static inline int idescsi_set_direction (idescsi_pc_t *pc)
+{
+       switch (pc->c[0]) {
+               case READ_6: case READ_10: case READ_12:
+                       clear_bit (PC_WRITING, &pc->flags);
+                       return 0;
+               case WRITE_6: case WRITE_10: case WRITE_12:
+                       set_bit (PC_WRITING, &pc->flags);
+                       return 0;
+               default:
+                       return 1;
+       }
+}
+
+static inline struct buffer_head *idescsi_dma_bh (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+       struct buffer_head *bh = NULL, *first_bh = NULL;
+       int segments = pc->scsi_cmd->use_sg;
+       struct scatterlist *sg = pc->scsi_cmd->request_buffer;
+
+       if (!drive->using_dma || pc->request_transfer % 1024)
+               return NULL;
+       if (idescsi_set_direction(pc))
+               return NULL;
+       if (segments) {
+               if ((first_bh = bh = idescsi_kmalloc_bh (segments)) == NULL)
+                       return NULL;
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+               while (segments--) {
+                       bh->b_data = sg->address;
+                       bh->b_size = sg->length;
+                       bh = bh->b_reqnext;
+                       sg++;
+               }
+       } else {
+               if ((first_bh = bh = idescsi_kmalloc_bh (1)) == NULL)
+                       return NULL;
+#if IDESCSI_DEBUG_LOG
+               printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+               bh->b_data = pc->scsi_cmd->request_buffer;
+               bh->b_size = pc->request_transfer;
+       }
+       return first_bh;
+}
+
+int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+       ide_drive_t *drive = idescsi_drives[cmd->target];
+       struct request *rq = NULL;
+       idescsi_pc_t *pc = NULL;
+
+#if IDESCSI_DEBUG_LOG
+       printk ("idescsi_queue called, serial = %lu, cmd[0] = %x, id = %d\n", cmd->serial_number, cmd->cmnd[0], cmd->target);
+#endif /* IDESCSI_DEBUG_LOG */
+
+       if (!drive) {
+               printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
+               goto abort;
+       }
+       pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+       rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+       if (rq == NULL || pc == NULL) {
+               printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
+               goto abort;
+       }
+
+       memset (pc->c, 0, 12);
+       pc->flags = 0;
+       pc->rq = rq;
+       memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+       if (cmd->use_sg) {
+               pc->buffer = NULL;
+               pc->sg = cmd->request_buffer;
+       } else {
+               pc->buffer = cmd->request_buffer;
+               pc->sg = NULL;
+       }
+       pc->b_count = 0;
+       pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
+       pc->scsi_cmd = cmd;
+       pc->done = done;
+       idescsi_transform_pc1 (drive, pc);
+
+       ide_init_drive_cmd (rq);
+       rq->buffer = (char *) pc;
+       rq->bh = idescsi_dma_bh (drive, pc);
+       rq->cmd = IDESCSI_PC_RQ;
+       (void) ide_do_drive_cmd (drive, rq, ide_end);
+       return 0;
+abort:
+       if (pc) kfree (pc);
+       if (rq) kfree (rq);
+       cmd->result = DID_ERROR << 16;
+       done(cmd);
+       return 0;
+}
+
+int idescsi_abort (Scsi_Cmnd *cmd)
+{
+       return SCSI_ABORT_SNOOZE;
+}
+
+int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+{
+       return SCSI_RESET_PUNT;
+}
+
+int idescsi_bios (Disk *disk, kdev_t dev, int *parm)
+{
+       ide_drive_t *drive = idescsi_drives[disk->device->id];
+
+       if (drive->cyl && drive->head && drive->sect) {
+               parm[0] = drive->head;
+               parm[1] = drive->sect;
+               parm[2] = drive->cyl;
+       }
+       return 0;
+}
diff --git a/drivers/scsi/ide-scsi.h b/drivers/scsi/ide-scsi.h
new file mode 100644 (file)
index 0000000..2ccbc3c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * linux/drivers/scsi/ide-scsi.h
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+#ifndef IDESCSI_H
+#define IDESCSI_H
+
+extern int idescsi_detect (Scsi_Host_Template *host_template);
+extern int idescsi_release (struct Scsi_Host *host);
+extern const char *idescsi_info (struct Scsi_Host *host);
+extern int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int idescsi_abort (Scsi_Cmnd *cmd);
+extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
+extern int idescsi_bios (Disk *disk, kdev_t dev, int *parm);
+
+#define IDESCSI                                                                \
+{      NULL,                   /* next         */                      \
+       NULL,                   /* module       */                      \
+       NULL,                   /* proc_dir     */                      \
+       NULL,                   /* proc_info    */                      \
+       "idescsi",              /* name         */                      \
+       idescsi_detect,         /* detect       */                      \
+       idescsi_release,        /* release      */                      \
+       idescsi_info,           /* info         */                      \
+       NULL,                   /* command      */                      \
+       idescsi_queue,          /* queuecommand */                      \
+       idescsi_abort,          /* abort        */                      \
+       idescsi_reset,          /* reset        */                      \
+       NULL,                   /* slave_attach */                      \
+       idescsi_bios,           /* bios_param   */                      \
+       10,                     /* can_queue    */                      \
+       -1,                     /* this_id      */                      \
+       256,                    /* sg_tablesize */                      \
+       5,                      /* cmd_per_lun  */                      \
+       0,                      /* present      */                      \
+       0,                      /* isa_dma      */                      \
+       DISABLE_CLUSTERING      /* clustering   */                      \
+}
+
+#endif /* IDESCSI_H */
index 9dfa0a22301e610f2badbe18daae0e42f712afa7..1b3196d62ab9ec57c8444bc1f38cae631c90ebb2 100644 (file)
@@ -524,7 +524,9 @@ void set_blocksize(kdev_t dev, int size)
                        if (bh->b_size == size)
                                 continue;
                        
+                       bhnext->b_count++;
                        wait_on_buffer(bh);
+                       bhnext->b_count--;
                        if (bh->b_dev == dev && bh->b_size != size) {
                                clear_bit(BH_Dirty, &bh->b_state);
                                clear_bit(BH_Uptodate, &bh->b_state);
@@ -546,7 +548,7 @@ static inline int can_reclaim(struct buffer_head *bh, int size)
                         
        if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
            buffer_dirty(bh)) {
-               refile_buffer(bh);
+               /* WSH: don't attempt to refile here! */
                return 0;
        }
 
@@ -569,6 +571,8 @@ static struct buffer_head *find_candidate(struct buffer_head *list,int *list_len
                           of other sizes, this is necessary now that we
                           no longer have the lav code. */
                        try_to_free_buffer(bh,&bh,1);
+                       if (!bh)
+                               break;
                        continue;
                }
 
@@ -612,14 +616,15 @@ static void refill_freelist(int size)
        }
 
 repeat:
+       if(needed <= 0)
+               return;
+
        /* OK, we cannot grow the buffer cache, now try to get some
           from the lru list */
 
        /* First set the candidate pointers to usable buffers.  This
           should be quick nearly all of the time. */
 
-       if(needed <= 0) return;
-
        for(i=0; i<BUF_DIRTY; i++){
                buffers[i] = nr_buffers_type[i];
                candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
@@ -652,12 +657,9 @@ repeat:
                        if (candidate[i] && !can_reclaim(candidate[i],size))
                                candidate[i] = find_candidate(candidate[i],&buffers[i], size);
                }
-               if (needed >= 0)
-                       goto repeat;
+               goto repeat;
        }
        
-       if(needed <= 0) return;
-       
        /* Too bad, that was not enough. Try a little harder to grow some. */
        
        if (nr_free_pages > min_free_pages + 5) {
@@ -670,6 +672,8 @@ repeat:
        /* and repeat until we find something good */
        if (!grow_buffers(GFP_ATOMIC, size))
                wakeup_bdflush(1);
+
+       /* decrease needed even if there is no success */
        needed -= PAGE_SIZE;
        goto repeat;
 }
@@ -919,6 +923,34 @@ static void put_unused_buffer_head(struct buffer_head * bh)
        wake_up(&buffer_wait);
 }
 
+/* 
+ * We can't put completed temporary IO buffer_heads directly onto the
+ * unused_list when they become unlocked, since the device driver
+ * end_request routines still expect access to the buffer_head's
+ * fields after the final unlock.  So, the device driver puts them on
+ * the reuse_list instead once IO completes, and we recover these to
+ * the unused_list here.
+ *
+ * The reuse_list receives buffers from interrupt routines, so we need
+ * to be IRQ-safe here (but note that interrupts only _add_ to the
+ * reuse_list, never take away. So we don't need to worry about the
+ * reuse_list magically emptying).
+ */
+static inline void recover_reusable_buffer_heads(void)
+{
+       if (reuse_list) {
+               struct buffer_head *head;
+
+               head = xchg(&reuse_list, NULL);
+       
+               do {
+                       struct buffer_head *bh = head;
+                       head = head->b_next_free;
+                       put_unused_buffer_head(bh);
+               } while (head);
+       }
+}
+
 static void get_more_buffer_heads(void)
 {
        struct buffer_head * bh;
@@ -946,38 +978,14 @@ static void get_more_buffer_heads(void)
                 */
                run_task_queue(&tq_disk);
                sleep_on(&buffer_wait);
+               /*
+                * After we wake up, check for released async buffer heads.
+                */
+               recover_reusable_buffer_heads();
        }
 
 }
 
-/* 
- * We can't put completed temporary IO buffer_heads directly onto the
- * unused_list when they become unlocked, since the device driver
- * end_request routines still expect access to the buffer_head's
- * fields after the final unlock.  So, the device driver puts them on
- * the reuse_list instead once IO completes, and we recover these to
- * the unused_list here.
- *
- * The reuse_list receives buffers from interrupt routines, so we need
- * to be IRQ-safe here (but note that interrupts only _add_ to the
- * reuse_list, never take away. So we don't need to worry about the
- * reuse_list magically emptying).
- */
-static inline void recover_reusable_buffer_heads(void)
-{
-       if (reuse_list) {
-               struct buffer_head *head;
-
-               head = xchg(&reuse_list, NULL);
-       
-               do {
-                       struct buffer_head *bh = head;
-                       head = head->b_next_free;
-                       put_unused_buffer_head(bh);
-               } while (head);
-       }
-}
-
 static struct buffer_head * get_unused_buffer_head(void)
 {
        struct buffer_head * bh;
@@ -1158,6 +1166,8 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
                free_async_buffers(bh);
                restore_flags(flags);
                after_unlock_page(page);
+               if (waitqueue_active(&buffer_wait))
+                       wake_up(&buffer_wait);
        }
        ++current->maj_flt;
        return 0;
@@ -1520,6 +1530,7 @@ asmlinkage int sync_old_buffers(void)
                                 ndirty++;
                                 if(bh->b_flushtime > jiffies) continue;
                                 nwritten++;
+                                next->b_count++;
                                 bh->b_count++;
                                 bh->b_flushtime = 0;
 #ifdef DEBUG
@@ -1527,8 +1538,10 @@ asmlinkage int sync_old_buffers(void)
 #endif
                                 ll_rw_block(WRITE, 1, &bh);
                                 bh->b_count--;
+                                next->b_count--;
                         }
        }
+       run_task_queue(&tq_disk);
 #ifdef DEBUG
        if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
        printk("Wrote %d/%d buffers\n", nwritten, ndirty);
@@ -1660,6 +1673,7 @@ int bdflush(void * unused)
                                             currently dirty buffers are not shared, so it does not matter */
                                          if (refilled && major == LOOP_MAJOR)
                                                   continue;
+                                         next->b_count++;
                                          bh->b_count++;
                                          ndirty++;
                                          bh->b_flushtime = 0;
@@ -1675,6 +1689,7 @@ int bdflush(void * unused)
                                          if(nlist != BUF_DIRTY) ncount++;
 #endif
                                          bh->b_count--;
+                                         next->b_count--;
                                  }
                 }
 #ifdef DEBUG
index 048ea2f5c9991b6181d87b5f89ac907ea0971a64..44b4663bbcafd749c109aa043d855f4a680feb41 100644 (file)
@@ -1015,10 +1015,15 @@ start_up:
                        goto end_rename;
        }
        retval = -EPERM;
-       if (new_inode && (new_dir->i_mode & S_ISVTX) &&
-           current->fsuid != new_inode->i_uid &&
-           current->fsuid != new_dir->i_uid && !fsuser())
-               goto end_rename;
+       if (new_inode) {
+               if ((new_dir->i_mode & S_ISVTX) &&
+                   current->fsuid != new_inode->i_uid &&
+                   current->fsuid != new_dir->i_uid && !fsuser())
+                       goto end_rename;
+               if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
+                       goto end_rename;
+       }
+       
        if (S_ISDIR(old_inode->i_mode)) {
                retval = -ENOTDIR;
                if (new_inode && !S_ISDIR(new_inode->i_mode))
index f66e1d2b21a6761332ce231de7a0ee3cca627f28..1c7587b4264b72e3427b9a13566cbbd7e62008e6 100644 (file)
@@ -373,7 +373,6 @@ smb_receive_trans2(struct smb_server *server,
        int total_data = 0;
        int total_param = 0;
        int result;
-       unsigned char *inbuf = server->packet;
        unsigned char *rcv_buf;
        int buf_len;
        int data_len = 0;
@@ -389,8 +388,8 @@ smb_receive_trans2(struct smb_server *server,
                *ldata = *lparam = 0;
                return 0;
        }
-       total_data = WVAL(inbuf, smb_tdrcnt);
-       total_param = WVAL(inbuf, smb_tprcnt);
+       total_data = WVAL(server->packet, smb_tdrcnt);
+       total_param = WVAL(server->packet, smb_tprcnt);
 
        DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
 
@@ -415,6 +414,8 @@ smb_receive_trans2(struct smb_server *server,
 
        while (1)
        {
+               unsigned char *inbuf = server->packet;
+
                if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
                    > total_param)
                {
index 4d33990c1ed160f4e043400419266e5494f4fef6..8e6b047e08e200c07ed4d3caff703d827cf36907 100644 (file)
@@ -53,6 +53,7 @@
 
 #define WIN_DOORLOCK           0xde    /* lock door on removable drives */
 #define WIN_DOORUNLOCK         0xdf    /* unlock door on removable drives */
+#define WIN_ACKMC              0xdb    /* acknowledge media change */
 
 #define WIN_MULTREAD           0xC4    /* read sectors using multiple mode */
 #define WIN_MULTWRITE          0xC5    /* write sectors using multiple mode */
 #define WIN_SRST               0x08    /* ATAPI soft reset command */
 #define WIN_PACKETCMD          0xa0    /* Send a packet command. */
 
+/* Non-standard commands */
+#define EXABYTE_ENABLE_NEST    0xf0
+
 /* Bits for HD_ERROR */
 #define MARK_ERR       0x01    /* Bad address mark */
 #define TRK0_ERR       0x02    /* couldn't find track 0 */
 #define ABRT_ERR       0x04    /* Command aborted */
 #define ID_ERR         0x10    /* ID field not found */
+#define MC_ERR         0x20    /* media changed */
 #define ECC_ERR                0x40    /* Uncorrectable ECC error */
 #define        BBD_ERR         0x80    /* block marked bad */
 
index e3cc49a9a6bfe056aace382d6a900dd9a11f29cd..6fb26f757d427ee3faf655928ef2af9890aa3dfd 100644 (file)
@@ -75,5 +75,6 @@ struct ipx_route_def
 #define SIOCAIPXITFCRT         (SIOCPROTOPRIVATE)
 #define SIOCAIPXPRISLT         (SIOCPROTOPRIVATE+1)
 #define SIOCIPXCFGDATA         (SIOCPROTOPRIVATE+2)
+#define SIOCIPXNCPCONN         (SIOCPROTOPRIVATE+3)
 #endif
 
index 8688741923077d01a0843960a6c453a582b8cabf..ac85c786c92b13afcbeccb18860f068e93bfb776 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/swapctl.h>
 
 static inline unsigned long page_address(struct page * page)
 {
@@ -20,7 +21,7 @@ static inline unsigned long page_address(struct page * page)
 #define PAGE_HASH_BITS 11
 #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
 
-#define PAGE_AGE_VALUE 16
+#define PAGE_AGE_VALUE ((PAGE_INITIAL_AGE)+(PAGE_ADVANCE))
 
 extern unsigned long page_cache_size; /* # of pages currently in the hash table */
 extern struct page * page_hash_table[PAGE_HASH_SIZE];
index 6c4e19dfb2ec3b1868d9549db9bf54eab47d4117..d6cfad56df2a673068c4e02ca64ccb67f99c2611 100644 (file)
 #define PCI_DEVICE_ID_INTEL_82371SB_1  0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2  0x7020
 #define PCI_DEVICE_ID_INTEL_82437VX    0x7030
+#define PCI_DEVICE_ID_INTEL_82371AB    0x7111
 #define PCI_DEVICE_ID_INTEL_P6         0x84c4
 
 #define PCI_VENDOR_ID_ADAPTEC          0x9004
index fb017b7d1e80b76c9df7d66648479ea6bdc390ca..08cc269e38e4d33908272b2a1471d8dc74bbed77 100644 (file)
@@ -141,6 +141,7 @@ enum scsi_directory_inos {
        PROC_SCSI_A2091,
        PROC_SCSI_GVP11,
        PROC_SCSI_ATARI,
+       PROC_SCSI_IDESCSI,
        PROC_SCSI_SCSI_DEBUG,   
        PROC_SCSI_NOT_PRESENT,
        PROC_SCSI_FILE,                        /* I'm assuming here that we */
index 135ddfe6079bf3851acf6f00272da41f4384968a..523d4c483c5041b13cd878f498c2dbd4b73d949e 100644 (file)
@@ -342,7 +342,7 @@ extern void wake_up(struct wait_queue ** p);
 extern void wake_up_interruptible(struct wait_queue ** p);
 extern void wake_up_process(struct task_struct * tsk);
 
-extern void notify_parent(struct task_struct * tsk);
+extern void notify_parent(struct task_struct * tsk, int signal);
 extern void force_sig(unsigned long sig,struct task_struct * p);
 extern int send_sig(unsigned long sig,struct task_struct * p,int priv);
 extern int in_group_p(gid_t grp);
index a85fcf346d446d8663adb2407d25ac4e5a8a3fd6..4d0f5ea5044df223c210bd0ed3048f8c15c3ee22 100644 (file)
@@ -109,7 +109,11 @@ struct ipx_opt
  * know the connection this socket belongs to. 
  */
        struct ncp_server       *ncp_server;
-       
+/* 
+ * To handle special ncp connection-handling sockets for mars_nwe,
+ * the connection number must be stored in the socket.
+ */
+       unsigned short          ipx_ncp_conn;
 };
 #endif
 
index aefb59a44582f5d348044177a75f642ac65dfb0c..1943074a2f0a7f7f5c55f63710b4ae5cf7e349b8 100644 (file)
@@ -59,6 +59,7 @@ extern int console_loglevel;
 static int init(void *);
 extern int bdflush(void *);
 extern int kswapd(void *);
+extern void kswapd_setup(void);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -920,6 +921,7 @@ static int init(void * unused)
        /* Launch bdflush from here, instead of the old syscall way. */
        kernel_thread(bdflush, NULL, 0);
        /* Start the background pageout daemon. */
+       kswapd_setup();
        kernel_thread(kswapd, NULL, 0);
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff --git a/init/patches/acer-ess1688-support b/init/patches/acer-ess1688-support
deleted file mode 100644 (file)
index cdfc330..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ESS1688 support for Acer-notebooks (Kurt Huwig <kurt@huwig.de>)
index dcdbaadedf8241782873714bcff2f1012a2ce2f6..c92914a0cc1c2dfb0bd5f6f0bbf1545183b4ed1f 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -13,6 +13,7 @@
 #include <linux/stat.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
+#include <linux/swapctl.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -672,6 +673,11 @@ static pte_t shm_swap_in(struct vm_area_struct * shmd, unsigned long offset, uns
                        shm_swp--;
                }
                shm_rss++;
+
+               /* Give the physical reallocated page a bigger start */
+               if (shm_rss < (MAP_NR(high_memory) >> 3))
+                       mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
                pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
                shp->shm_pages[idx] = pte_val(pte);
        } else
index 7e7f01251cb727b304489083b39fe49a263562c7..a45a48b482c1e204605259d35059c6f2b49ad492 100644 (file)
@@ -100,11 +100,9 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv)
        return 0;
 }
 
-void notify_parent(struct task_struct * tsk)
+void notify_parent(struct task_struct * tsk, int signal)
 {
-       if (tsk->p_pptr == task[smp_num_cpus])          /* Init */
-               tsk->exit_signal = SIGCHLD;
-       send_sig(tsk->exit_signal, tsk->p_pptr, 1);
+       send_sig(signal, tsk->p_pptr, 1);
        wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
 }
 
@@ -385,11 +383,10 @@ static inline void forget_original_parent(struct task_struct * father)
        struct task_struct * p;
 
        for_each_task(p) {
-               if (p->p_opptr == father)
-                       if (task[smp_num_cpus]) /* init */
-                               p->p_opptr = task[smp_num_cpus];
-                       else
-                               p->p_opptr = task[0];
+               if (p->p_opptr == father) {
+                       p->exit_signal = SIGCHLD;
+                       p->p_opptr = task[smp_num_cpus] ? : task[0];    /* init */
+               }
        }
 }
 
@@ -519,7 +516,7 @@ static void exit_notify(void)
                kill_pg(current->pgrp,SIGCONT,1);
        }
        /* Let father know we died */
-       notify_parent(current);
+       notify_parent(current, current->exit_signal);
        
        /*
         * This loop does two things:
@@ -533,15 +530,14 @@ static void exit_notify(void)
                current->p_cptr = p->p_osptr;
                p->p_ysptr = NULL;
                p->flags &= ~(PF_PTRACED|PF_TRACESYS);
-               if (task[smp_num_cpus] && task[smp_num_cpus] != current) /* init */
-                       p->p_pptr = task[smp_num_cpus];
-               else
-                       p->p_pptr = task[0];
+
+               p->p_pptr = p->p_opptr;
                p->p_osptr = p->p_pptr->p_cptr;
                p->p_osptr->p_ysptr = p;
                p->p_pptr->p_cptr = p;
                if (p->state == TASK_ZOMBIE)
-                       notify_parent(p);
+                       notify_parent(p, p->exit_signal);
+
                /*
                 * process group orphan check
                 * Case ii: Our child is in a different pgrp 
@@ -672,7 +668,7 @@ repeat:
                                        REMOVE_LINKS(p);
                                        p->p_pptr = p->p_opptr;
                                        SET_LINKS(p);
-                                       notify_parent(p);
+                                       notify_parent(p, p->exit_signal);
                                } else
                                        release(p);
 #ifdef DEBUG_PROC_TREE
index d55265012420649f5f5201101d9da8d546b1675f..8ee973b5bc931a9af059db39173d7c4a133ca6bf 100644 (file)
@@ -201,7 +201,7 @@ int do_sysctl (int *name, int nlen,
        do {
                context = 0;
                error = parse_table(name, nlen, oldval, oldlenp, 
-                                   newval, newlen, tmp->clt_table, &context);
+                                   newval, newlen, tmp->ctl_table, &context);
                if (context)
                        kfree(context);
                if (error != -ENOTDIR)
index ead0b329a0cb7a96376b18799b963cf22256c7c7..d5d8bbcd9e733d59a3ced9bf26c6a392de7aeb46 100644 (file)
@@ -41,18 +41,7 @@ struct page * page_hash_table[PAGE_HASH_SIZE];
  * Simple routines for both non-shared and shared mappings.
  */
 
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
-       atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -100,7 +89,7 @@ repeat:
                /* page wholly truncated - free it */
                if (offset >= start) {
                        if (PageLocked(page)) {
-                               wait_on_page(page);
+                               __wait_on_page(page);
                                goto repeat;
                        }
                        inode->i_nrpages--;
@@ -169,8 +158,12 @@ int shrink_mmap(int priority, int dma)
                switch (page->count) {
                        case 1:
                                /* If it has been referenced recently, don't free it */
-                               if (clear_bit(PG_referenced, &page->flags))
+                               if (clear_bit(PG_referenced, &page->flags)) {
+                                       /* age this page potential used */
+                                       if (priority < 4)
+                                               age_page(page);
                                        break;
+                               }
 
                                /* is it a page cache page? */
                                if (page->inode) {
@@ -448,7 +441,7 @@ static void profile_readahead(int async, struct file *filp)
 
 #define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK)
 
-#if 0  /* small readahead */
+#ifdef CONFIG_READA_SMALL  /* small readahead */
 #define MAX_READAHEAD PageAlignSize(4096*7)
 #define MIN_READAHEAD PageAlignSize(4096*2)
 #else /* large readahead */
@@ -784,8 +777,15 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long
 found_page:
        /*
         * Ok, found a page in the page cache, now we need to check
-        * that it's up-to-date
+        * that it's up-to-date.  First check whether we'll need an
+        * extra page -- better to overlap the allocation with the I/O.
         */
+       if (no_share && !new_page) {
+               new_page = __get_free_page(GFP_KERNEL);
+               if (!new_page)
+                       goto failure;
+       }
+
        if (PageLocked(page))
                goto page_locked_wait;
        if (!PageUptodate(page))
@@ -810,13 +810,8 @@ success:
        }
 
        /*
-        * Check that we have another page to copy it over to..
+        * No sharing ... copy to the new page.
         */
-       if (!new_page) {
-               new_page = __get_free_page(GFP_KERNEL);
-               if (!new_page)
-                       goto failure;
-       }
        memcpy((void *) new_page, (void *) old_page, PAGE_SIZE);
        flush_page_to_ram(new_page);
        release_page(page);
@@ -880,6 +875,8 @@ page_read_error:
         */
 failure:
        release_page(page);
+       if (new_page)
+               free_page(new_page);
 no_page:
        return 0;
 }
@@ -1003,6 +1000,8 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
        unsigned long page;
        int error;
 
+       if (pte_none(pte))
+               return 0;
        if (!(flags & MS_INVALIDATE)) {
                if (!pte_present(pte))
                        return 0;
@@ -1015,8 +1014,6 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
                page = pte_page(pte);
                mem_map[MAP_NR(page)].count++;
        } else {
-               if (pte_none(pte))
-                       return 0;
                flush_cache_page(vma, address);
                pte_clear(ptep);
                flush_tlb_page(vma, address);
index 0f712014daf1e0d69fe07d8d1c828a6cc3475614..30d78db8aca6571eb875546b850bc145387c7bb5 100644 (file)
@@ -202,7 +202,7 @@ asmlinkage int sys_mlock(unsigned long start, size_t len)
 
        /* we may lock at most half of physical memory... */
        /* (this check is pretty bogus, but doesn't hurt) */
-       if (locked > MAP_NR(high_memory)/2)
+       if (locked > (MAP_NR(high_memory) >> 1))
                return -ENOMEM;
 
        return do_mlock(start, len, 1);
@@ -259,7 +259,7 @@ asmlinkage int sys_mlockall(int flags)
 
        /* we may lock at most half of physical memory... */
        /* (this check is pretty bogus, but doesn't hurt) */
-       if (current->mm->total_vm > MAP_NR(high_memory)/2)
+       if (current->mm->total_vm > (MAP_NR(high_memory) >> 1))
                return -ENOMEM;
 
        return do_mlockall(flags);
index 781e5bcf6b118420a96cf9478bcaf3b6f1dad2ac..6c3e08e09aab3b9c32e7f886c38edf08d5c55589 100644 (file)
@@ -264,11 +264,11 @@ unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
 
        /*
         * select nr of pages we try to keep free for important stuff
-        * with a minimum of 16 pages. This is totally arbitrary
+        * with a minimum of 24 pages. This is totally arbitrary
         */
        i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
-       if (i < 16)
-               i = 16;
+       if (i < 24)
+               i = 24;
        min_free_pages = i;
        free_pages_low = i + (i>>1);
        free_pages_high = i + i;
@@ -311,7 +311,8 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
        unsigned long page = __get_free_page(GFP_KERNEL);
 
        if (pte_val(*page_table) != entry) {
-               free_page(page);
+               if (page)
+                       free_page(page);
                return;
        }
        if (!page) {
@@ -327,6 +328,11 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
        }
        vma->vm_mm->rss++;
        tsk->maj_flt++;
+
+       /* Give the physical reallocated page a bigger start */
+       if (vma->vm_mm->rss < (MAP_NR(high_memory) >> 2))
+               mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
        if (!write_access && add_to_swap_cache(MAP_NR(page), entry)) {
                /* keep swap page allocated for the moment (swap cache) */
                set_pte(page_table, mk_pte(page, vma->vm_page_prot));
index 06552a3b557ea2b0f9f68f0e112fb40aaf669f00..5eaa8ec58368049569c8cf83b279286743dff69e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/swap.h>
 #include <linux/fs.h>
 #include <linux/swapctl.h>
+#include <linux/pagemap.h>
 #include <linux/smp_lock.h>
 
 #include <asm/dma.h>
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
+/*
+ * To check memory consuming code elsewhere set this to 1
+ */
+#define MM_DEBUG 0
+
 /* 
  * When are we next due for a page scan? 
  */
 static int next_swap_jiffies = 0;
 
+/*
+ * Was the last kswapd wakeup caused by
+ *     nr_free_pages < free_pages_low
+ */
+static int last_wakeup_low = 0;
+
 /* 
  * How often do we do a pageout scan during normal conditions?
  * Default is four times a second.
@@ -275,10 +287,42 @@ static int swap_out_process(struct task_struct * p, int dma, int wait)
 static int swap_out(unsigned int priority, int dma, int wait)
 {
        static int swap_task;
-       int loop, counter;
+       int loop, counter, shfrv;
        struct task_struct *p;
 
-       counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
+#ifdef MM_DEBUG
+       shfrv = 10;
+#else
+       /*
+        * Trouble due ageing pages: In some situations it is possible that we cross only tasks
+        * which are swapped out or which have only physical pages with age >= 3.
+        * High values of swap_cnt for memory consuming tasks do aggravate such situations.
+        *
+        * If PAGEOUT_WEIGHT has a value of 8192 a right shift value of 10 leads to
+        *     (8 * nr_tasks) >> priority
+        * Together with a high number of tasks, say 100, we have counters (due priority)
+        *     12(6) + 25(5) + 50(4) + 100(3) + 200(2) + 400(1) + 800(0)
+        * and as total result 1587 scans of swap_out() to swap out a task page.
+        *
+        * Just assume 80 tasks are swapped out and the remaining tasks have a swap_cnt value >= 40
+        * together with pages with age >= 3.  Then we need approx 20*40*2 = 1600 scans to get a
+        * free page.
+        * And now assume that the amount of cached pages, buffers, and ipc pages are really low.
+        */
+       switch (priority) {
+               case 6: case 5: case 4:  /* be friendly */
+                       shfrv = 10;
+                       break;
+               case 3: case 2: case 1:  /* more intensive */
+                       shfrv =  9;
+                       break;
+               case 0: default:         /* sorry we need a page */
+                       shfrv =  8;
+                       break;
+       }
+#endif
+
+       counter = ((PAGEOUT_WEIGHT * nr_tasks) >> shfrv) >> priority;
        for(; counter >= 0; counter--) {
                /*
                 * Check that swap_task is suitable for swapping.  If not, look for
@@ -305,14 +349,19 @@ static int swap_out(unsigned int priority, int dma, int wait)
                 * Determine the number of pages to swap from this process.
                 */
                if (!p->swap_cnt) {
-                       /* Normalise the number of pages swapped by
-                          multiplying by (RSS / 1MB) */
+                       /*
+                        * Normalise the number of pages swapped by
+                        * multiplying by (RSS / 1MB)
+                        */
                        p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
                }
                if (!--p->swap_cnt)
                        swap_task++;
                switch (swap_out_process(p, dma, wait)) {
                        case 0:
+                               if (p->state == TASK_STOPPED)
+                                       /* Stopped task occupy nonused ram */
+                                       break;
                                if (p->swap_cnt)
                                        swap_task++;
                                break;
@@ -322,6 +371,14 @@ static int swap_out(unsigned int priority, int dma, int wait)
                                break;
                }
        }
+#ifdef MM_DEBUG
+       if (!priority) {
+               printk("swap_out: physical ram %6dkB, min pages   %6dkB\n",
+                       (int)(high_memory>>10), min_free_pages<<(PAGE_SHIFT-10));
+               printk("swap_out:   free pages %6dkB, async pages %6dkB\n",
+                       nr_free_pages<<(PAGE_SHIFT-10), nr_async_pages<<(PAGE_SHIFT-10));
+       }
+#endif
        return 0;
 }
 
@@ -360,6 +417,24 @@ int try_to_free_page(int priority, int dma, int wait)
        return 0;
 }
 
+/*
+ * Before we start the kernel thread, print out the 
+ * kswapd initialization message (otherwise the init message 
+ * may be printed in the middle of another driver's init 
+ * message).  It looks very bad when that happens.
+ */
+void kswapd_setup(void)
+{
+       int i;
+       char *revision="$Revision: 1.4.2.2 $", *s, *e;
+
+       if ((s = strchr(revision, ':')) &&
+           (e = strchr(s, '$')))
+               s++, i = e - s;
+       else
+               s = revision, i = -1;
+       printk ("Starting kswapd v%.*s\n", i, s);
+}
 
 /*
  * The background pageout daemon.
@@ -368,7 +443,6 @@ int try_to_free_page(int priority, int dma, int wait)
 int kswapd(void *unused)
 {
        int i;
-       char *revision="$Revision: 1.4.2.2 $", *s, *e;
        
        current->session = 1;
        current->pgrp = 1;
@@ -394,14 +468,10 @@ int kswapd(void *unused)
 
        init_swap_timer();
        
-       if ((s = strchr(revision, ':')) &&
-           (e = strchr(s, '$')))
-               s++, i = e - s;
-       else
-               s = revision, i = -1;
-       printk ("Started kswapd v%.*s\n", i, s);
-
        while (1) {
+               /* low on memory, we need to start swapping soon */
+               next_swap_jiffies = jiffies +
+                       (last_wakeup_low ? swapout_interval >> 1 : swapout_interval);
                kswapd_awake = 0;
                current->signal = 0;
                run_task_queue(&tq_disk);
@@ -410,7 +480,8 @@ int kswapd(void *unused)
                swapstats.wakeups++;
                /* Do the background pageout: */
                for (i=0; i < kswapd_ctl.maxpages; i++)
-                       try_to_free_page(GFP_KERNEL, 0, 0);
+                       try_to_free_page(GFP_KERNEL, 0,
+                                        (nr_free_pages < min_free_pages));
        }
 }
 
@@ -421,16 +492,15 @@ int kswapd(void *unused)
 void swap_tick(void)
 {
        int     want_wakeup = 0;
-       static int      last_wakeup_low = 0;
 
        if ((nr_free_pages + nr_async_pages) < free_pages_low) {
                if (last_wakeup_low)
-                       want_wakeup = jiffies >= next_swap_jiffies;
+                       want_wakeup = (jiffies >= next_swap_jiffies);
                else
                        last_wakeup_low = want_wakeup = 1;
        }
        else if (((nr_free_pages + nr_async_pages) < free_pages_high) && 
-                jiffies >= next_swap_jiffies) {
+                (jiffies >= next_swap_jiffies)) {
                last_wakeup_low = 0;
                want_wakeup = 1;
        }
@@ -440,7 +510,6 @@ void swap_tick(void)
                        wake_up(&kswapd_wait);
                        need_resched = 1;
                }
-               next_swap_jiffies = jiffies + swapout_interval;
        }
        timer_active |= (1<<SWAP_TIMER);
 }
index f9981fb0db1a2b9bec1ad69fcc9a72fd5669d5e6..ac3fbe6098ce588e9bde92a53c6cf9906314f23b 100644 (file)
@@ -1427,7 +1427,6 @@ int dev_ioctl(unsigned int cmd, void *arg)
                case SIOCGIFMTU:
                case SIOCGIFMEM:
                case SIOCGIFHWADDR:
-               case SIOCSIFHWADDR:
                case SIOCGIFSLAVE:
                case SIOCGIFMAP:
                        return dev_ifsioc(arg, cmd);
@@ -1444,6 +1443,7 @@ int dev_ioctl(unsigned int cmd, void *arg)
                case SIOCSIFMETRIC:
                case SIOCSIFMTU:
                case SIOCSIFMEM:
+               case SIOCSIFHWADDR:
                case SIOCSIFMAP:
                case SIOCSIFSLAVE:
                case SIOCADDMULTI:
index 0c94d79d6064a88b358b68e2e535458173d5fbdb..938fe744341eba3de30fb2972387aeeaa85ffe9e 100644 (file)
@@ -1704,7 +1704,7 @@ void arp_send(int type, int ptype, u32 dest_ip,
        skb->arp = 1;
        skb->dev = dev;
        skb->free = 1;
-       skb->protocol = htons (ETH_P_IP);
+       skb->protocol = htons (ETH_P_ARP);
 
        /*
         *      Fill the device header for the ARP frame
index e23180563f263e521322d7fb9b39078e57caf927..fcf9676f6ebc7689b82a4549c80c662a8a3fa385 100644 (file)
@@ -266,6 +266,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
                if(sk->daddr            == saddr                && /* remote address */
                   sk->dummy_th.dest    == sport                && /* remote port    */
                   sk->num              == hnum                 && /* local port     */
+                  sk->state            != TCP_CLOSE            && /* comment above is false */
                   sk->rcv_saddr        == daddr)                  /* local address  */
                        goto hit; /* You sunk my battleship! */
        sk = tcp_v4_lookup_longway(daddr, hnum);
index a9b9861503aab573874630731c28bd5e54b2fb8c..c050a0d56dbe19ecd290b85f408cecd45e54c3fe 100644 (file)
@@ -468,7 +468,60 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
        ipx_socket      *sock1 = NULL, *sock2 = NULL;
        struct sk_buff  *skb1 = NULL, *skb2 = NULL;
 
-       sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+       if (intrfc == ipx_primary_net
+         && ntohs(ipx->ipx_dest.sock) == 0x451) 
+       {
+         /* 
+          * The packet's target is a NCP connection handler. We want to
+          * hand it to the correct socket directly within the kernel,
+          * so that the mars_nwe packet distribution process
+          * does not have to do it. Here we only care about NCP and
+          * BURST packets.
+          * You might call this a hack, but believe me, you do not
+          * want a complete NCP layer in the kernel, and this is
+          * VERY fast as well.
+          */
+         int connection = 0;
+
+         if (    *((char*)(ipx+1))   == 0x22
+             &&  *((char*)(ipx+1)+1) == 0x22) 
+         {
+               /*
+                * The packet is a NCP request
+                */
+                connection = ( ((int) *((char*)(ipx+1)+5)) << 8 )
+                              | (int) *((char*)(ipx+1)+3);
+         } 
+         else if (    *((char*)(ipx+1))   == 0x77
+                  &&  *((char*)(ipx+1)+1) == 0x77) 
+         {
+               /*
+                * The packet is a BURST packet
+                */
+                connection = ( ((int) *((char*)(ipx+1)+9)) << 8 )
+                              | (int) *((char*)(ipx+1)+8);
+         }
+
+          if (connection) 
+         {
+           /*
+            * Now we have to look for a special NCP connection handling
+            * socket. Only these sockets have ipx_ncp_conn != 0, set
+            * by SIOCIPXNCPCONN.
+            */
+            for (sock1=intrfc->if_sklist;
+               (sock1 != NULL) &&
+               (sock1->protinfo.af_ipx.ipx_ncp_conn != connection);
+               sock1=sock1->next);;
+          }
+        }
+        if (sock1 == NULL) 
+       {
+               /* No special socket found, forward the packet the
+                * normal way.
+                */
+               sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+       }
 
        /*
         *      We need to check if there is a primary net and if
@@ -1723,6 +1776,7 @@ static int ipx_create(struct socket *sock, int protocol)
        }
        sk->rcvbuf=SK_RMEM_MAX;
        sk->sndbuf=SK_WMEM_MAX;
+       sk->allocation=GFP_KERNEL;
        sk->prot=NULL;  /* So we use default free mechanisms */
        skb_queue_head_init(&sk->receive_queue);
        skb_queue_head_init(&sk->write_queue);
@@ -2242,6 +2296,21 @@ static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
                        if(err) return err;
                        return(ipxcfg_get_config_data((void *)arg));
                }
+
+               case SIOCIPXNCPCONN:
+                {
+                 /*
+                  * This socket wants to take care of the NCP connection
+                  * handed to us in arg.
+                  */
+                  if (!suser()) return(-EPERM);
+                  err = verify_area(VERIFY_READ, (void *)arg,
+                                sizeof(unsigned short));
+                  if (err) return err;
+                  sk->protinfo.af_ipx.ipx_ncp_conn = get_fs_word(arg);
+                  return 0;
+                }
+
                case SIOCGSTAMP:
                        if (sk)
                        {