Metropoli BBS
VIEWER: aic7xxx.c MODE: TEXT (ASCII)
/*+M*************************************************************************
 * Adaptec AIC7xxx device driver for Linux.
 *
 * 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.
 *
 * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
 * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
 * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
 * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
 * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
 * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
 * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
 * ANSI SCSI-2 specification (draft 10c), ...
 *
 * --------------------------------------------------------------------------
 *
 *  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 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
 *           aic7xxx=no_reset
 *           aic7xxx=ultra
 *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
 *           aic7xxx=verbose
 *
 *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
 *
 *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
 *-M*************************************************************************/

#ifdef MODULE
#include <linux/module.h>
#endif

#include <stdarg.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/blk.h>
#include "sd.h"
#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() */

#include <linux/config.h>	/* for CONFIG_PCI */

/*
 * To generate the correct addresses for the controller to issue
 * on the bus.  Originally added for DEC Alpha support.
 */
#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))

struct proc_dir_entry proc_scsi_aic7xxx = {
    PROC_SCSI_AIC7XXX, 7, "aic7xxx",
    S_IFDIR | S_IRUGO | S_IXUGO, 2,
    0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

#define AIC7XXX_C_VERSION  "$Revision: 4.1.1 $"

#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
#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
#define MAX_TARGETS  16
#define MAX_LUNS     8
#ifndef TRUE
#  define TRUE 1
#endif
#ifndef FALSE
#  define FALSE 0
#endif

/*
 * Defines for PCI bus support, testing twin bus support, DMAing of
 * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
 * delay time.
 *
 *   o PCI bus support - this has been implemented and working since
 *     the December 1, 1994 release of this driver. If you don't have
 *     a PCI bus, then you can configure your kernel without PCI
 *     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.  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
 *     tagged queueing. Therefore, the maximum commands per lun is
 *     set to 2. If you want to implement tagged queueing, ensure
 *     this define is not commented out.
 *
 *   o Commands per lun - If tagged queueing is enabled, then you
 *     may want to try increasing AIC7XXX_CMDS_PER_LUN to more
 *     than 2.  By default, we limit the SCBs per LUN to 2 with
 *     or without tagged queueing enabled.  If tagged queueing is
 *     disabled, the sequencer will keep the 2nd SCB in the input
 *     queue until the first one completes - so it is OK to to have
 *     more than 1 SCB queued.  If tagged queueing is enabled, then
 *     the sequencer will attempt to send the 2nd SCB to the device
 *     while the first SCB is executing and the device is disconnected.
 *     For adapters limited to 4 SCBs, you may want to actually
 *     decrease the commands per LUN to 1, if you often have more
 *     than 2 devices active at the same time.  This will allocate
 *     1 SCB for each device and ensure that there will always be
 *     a free SCB for up to 4 devices active at the same time.
 *     When SCB paging is enabled, set the commands per LUN to 8
 *     or higher (see SCB paging support below).  Note that if
 *     AIC7XXX_CMDS_PER_LUN is not defined and tagged queueing is
 *     enabled, the driver will attempt to set the commands per
 *     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
 *     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
 *     FreeBSD driver (by Justin Gibbs) and allows for up to 255
 *     active SCBs.  This will increase performance when tagged
 *     queueing is enabled.  Note that you should increase the
 *     AIC7XXX_CMDS_PER_LUN to 8 as most tagged queueing devices
 *     allow at least this many.
 *
 *  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, 01/26/96
 */

/* Uncomment this for 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.
 */
#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

/*
 * 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.
 */
#ifdef CONFIG_AIC7XXX_PROC_STATS
#define AIC7XXX_PROC_STATS
#endif

/*
 * Enable SCB paging.
 */
#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
#define AIC7XXX_PAGE_ENABLE
#endif

/*
 * 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.
 */
#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
#define AIC7XXX_USE_EXT_SCBRAM
#endif

/*
 * For debugging the abort/reset code.
 */
#define AIC7XXX_DEBUG_ABORT

/*
 * For general debug messages
 */
#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
{
  unsigned 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
 */
typedef enum {
  AIC_NONE,
  AIC_7770,	/* EISA aic7770 on motherboard */
  AIC_7771,	/* EISA aic7771 on 274x */
  AIC_284x,	/* VLB  aic7770 on 284x, BIOS disabled */
  AIC_7850,	/* PCI  aic7850 */
  AIC_7855,	/* PCI  aic7855 */
  AIC_7860,	/* PCI  aic7860 (7850 Ultra) */
  AIC_7861,     /* PCI  aic7861 on 2940AU */
  AIC_7870,	/* PCI  aic7870 on motherboard */
  AIC_7871,	/* PCI  aic7871 on 294x */
  AIC_7872,	/* PCI  aic7872 on 3940 */
  AIC_7873,	/* PCI  aic7873 on 3985 */
  AIC_7874,	/* PCI  aic7874 on 294x Differential */
  AIC_7880,	/* PCI  aic7880 on motherboard */
  AIC_7881,	/* PCI  aic7881 on 294x Ultra */
  AIC_7882,	/* PCI  aic7882 on 3940 Ultra */
  AIC_7883,	/* PCI  aic7883 on 3985 Ultra */
  AIC_7884	/* PCI  aic7884 on 294x Ultra Differential */
} aha_chip_type;

typedef enum {
  AIC_777x,	/* AIC-7770 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 (ultra) */
} aha_chip_class_type;

typedef enum {
  AIC_SINGLE,  /* Single Channel */
  AIC_TWIN,    /* Twin Channel */
  AIC_WIDE     /* Wide Channel */
} aha_bus_type;

typedef enum {
  AIC_UNKNOWN,
  AIC_ENABLED,
  AIC_DISABLED
} aha_status_type;

typedef enum {
  LIST_HEAD,
  LIST_SECOND
} insert_type;

typedef enum {
  ABORT_RESET_INACTIVE,
  ABORT_RESET_PENDING,
  ABORT_RESET_SUCCESS
} aha_abort_reset_type;

/*
 * Define an array of board names that can be indexed by aha_type.
 * Don't forget to change this when changing the types!
 */
static const char *board_names[] = {
  "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 */
};

/*
 * There should be a specific return value for this in scsi.h, but
 * it seems that most drivers ignore it.
 */
#define DID_UNDERFLOW   DID_ERROR

/*
 *  What we want to do is have the higher level scsi driver requeue
 *  the command to us. There is no specific driver status for this
 *  condition, but the higher level scsi driver will requeue the
 *  command on a DID_BUS_BUSY error.
 *
 *  Upon further inspection and testing, it seems that DID_BUS_BUSY
 *  will *always* retry the command.  We can get into an infinite loop
 *  if this happens when we really want some sort of counter that
 *  will automatically abort/reset the command after so many retries.
 *  Using DID_ERROR will do just that.  (Made by a suggestion by
 *  Doug Ledford 8/1/96)
 */
#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)
 */
#define HID0		0x80   /* 0,1: msb of ID2, 2-7: ID1      */
#define HID1		0x81   /* 0-4: ID3, 5-7: LSB ID2         */
#define HID2		0x82   /* product                        */
#define HID3		0x83   /* firmware revision              */

/*
 * AIC-7770 I/O range to reserve for a card
 */
#define MINREG		0xC00
#define MAXREG		0xCBF

#define INTDEF		0x5C		/* Interrupt Definition Register */

/*
 * AIC-78X0 PCI registers
 */
#define	CLASS_PROGIF_REVID	0x08
#define		DEVREVID	0x000000FFul
#define		PROGINFC	0x0000FF00ul
#define		SUBCLASS	0x00FF0000ul
#define		BASECLASS	0xFF000000ul

#define	CSIZE_LATTIME		0x0C
#define		CACHESIZE	0x0000003Ful	/* only 5 bits */
#define		LATTIME		0x0000FF00ul

#define	DEVCONFIG		0x40
#define		MPORTMODE	0x00000400ul	/* aic7870 only */
#define		RAMPSM		0x00000200ul	/* aic7870 only */
#define		VOLSENSE	0x00000100ul
#define		SCBRAMSEL	0x00000080ul
#define		MRDCEN		0x00000040ul
#define		EXTSCBTIME	0x00000020ul	/* aic7870 only */
#define		EXTSCBPEN	0x00000010ul	/* aic7870 only */
#define		BERREN		0x00000008ul
#define		DACEN		0x00000004ul
#define		STPWLEVEL	0x00000002ul
#define		DIFACTNEGEN	0x00000001ul	/* aic7870 only */


/*
 * Define the different types of SEEPROMs on aic7xxx adapters
 * and make it also represent the address size used in accessing
 * its registers.  The 93C46 chips have 1024 bits organized into
 * 64 16-bit words, while the 93C56 chips have 2048 bits organized
 * into 128 16-bit words.  The C46 chips use 6 bits to address
 * 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;

/*
 *
 * Define the format of the SEEPROM registers (16 bits).
 *
 */
struct seeprom_config {

/*
 * SCSI ID Configuration Flags
 */
#define CFXFER		0x0007		/* synchronous transfer rate */
#define CFSYNCH		0x0008		/* enable synchronous transfer */
#define CFDISC		0x0010		/* enable disconnection */
#define CFWIDEB		0x0020		/* wide bus device (wide card) */
/* UNUSED		0x00C0 */
#define CFSTART		0x0100		/* send start unit SCSI command */
#define CFINCBIOS	0x0200		/* include in BIOS scan */
#define CFRNFOUND	0x0400		/* report even if not found */
/* UNUSED		0xF800 */
  unsigned short device_flags[16];	/* words 0-15 */

/*
 * BIOS Control Bits
 */
#define CFSUPREM	0x0001		/* support all removable drives */
#define CFSUPREMB	0x0002		/* support removable drives for boot only */
#define CFBIOSEN	0x0004		/* BIOS enabled */
/* UNUSED		0x0008 */
#define CFSM2DRV	0x0010		/* support more than two drives */
#define CF284XEXTEND	0x0020		/* extended translation (284x cards) */
/* UNUSED		0x0040 */
#define CFEXTEND	0x0080		/* extended translation enabled */
/* UNUSED		0xFF00 */
  unsigned short bios_control;		/* word 16 */

/*
 * Host Adapter Control Bits
 */
#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 */
#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 boot */
/* UNUSED		0xFF80 */
  unsigned short adapter_control;	/* word 17 */

/*
 * Bus Release, Host Adapter ID
 */
#define CFSCSIID	0x000F		/* host adapter SCSI ID */
/* UNUSED		0x00F0 */
#define CFBRTIME	0xFF00		/* bus release time */
  unsigned short brtime_id;		/* word 18 */

/*
 * Maximum targets
 */
#define CFMAXTARG	0x00FF	/* maximum targets */
/* UNUSED		0xFF00 */
  unsigned short max_targets;		/* word 19 */

  unsigned short res_1[11];		/* words 20-30 */
  unsigned short checksum;		/* word 31 */
};

#define SELBUS_MASK		0x0a
#define 	SELNARROW	0x00
#define 	SELBUSB		0x08
#define SINGLE_BUS		0x00

#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
 * to completion - it's easier that way - making a note of the error
 * condition in this location. This then will modify a DID_OK status
 * into an appropriate error for the higher-level SCSI code.
 */
#define aic7xxx_error(cmd)	((cmd)->SCp.Status)

/*
 * Keep track of the targets returned status.
 */
#define aic7xxx_status(cmd)	((cmd)->SCp.sent_command)

/*
 * The position of the SCSI commands scb within the scb array.
 */
#define aic7xxx_position(cmd)	((cmd)->SCp.have_data_in)

/*
 * "Static" structures. Note that these are NOT initialized
 * to zero inside the kernel - we have to initialize them all
 * explicitly.
 *
 * We support multiple adapter cards per interrupt, but keep a
 * linked list of Scsi_Host structures for each IRQ.  On an interrupt,
 * use the IRQ as an index into aic7xxx_boards[] to locate the card
 * information.
 */
static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1];

/*
 * When we detect and register the card, it is possible to
 * have the card raise a spurious interrupt.  Because we need
 * to support multiple cards, we cannot tell which card caused
 * the spurious interrupt.  And, we might not even have added
 * the card info to the linked list at the time the spurious
 * interrupt gets raised.  This variable is suppose to keep track
 * of when we are registering a card and how many spurious
 * interrupts we have encountered.
 *
 *   0 - do not allow spurious interrupts.
 *   1 - allow 1 spurious interrupt
 *   2 - have 1 spurious interrupt, do not allow any more.
 *
 * I've made it an integer instead of a boolean in case we
 * want to allow more than one spurious interrupt for debugging
 * purposes.  Otherwise, it could just go from true to false to
 * true (or something like that).
 *
 * When the driver detects the cards, we'll set the count to 1
 * for each card detection and registration.  After the registration
 * of a card completes, we'll set the count back to 0.  So far, it
 * seems to be enough to allow a spurious interrupt only during
 * card registration; if a spurious interrupt is going to occur,
 * this is where it happens.
 *
 * We should be able to find a way to avoid getting the spurious
 * interrupt.  But until we do, we have to keep this ugly code.
 */
static int aic7xxx_spurious_count;

/*
 * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
 * in the scatter-gather lists.  We need to convert the virtual
 * addresses to physical addresses.
 */
struct hw_scatterlist {
  unsigned int address;
  unsigned int length;
};

/*
 * Maximum number of SG segments these cards can support.
 */
#define	AIC7XXX_MAX_SG 122

/*
 * 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 int  SG_list_pointer;
/* 8*/  unsigned char residual_SG_segment_count;
/* 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
					 */
#define SCB_PIO_TRANSFER_SIZE	26 	/* amount we need to upload/download
					 * via PIO to initialize a transaction.
					 */
/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
                                         * or disconnected down in the sequencer.
                                         */
/*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_RESET		= 0x4000,
	SCB_WAS_BUSY		= 0x8000
} 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.
                                               */
};

/*
 * Define a linked list of SCBs.
 */
typedef struct {
  struct aic7xxx_scb *head;
  struct aic7xxx_scb *tail;
} scb_queue_type;

static struct {
  unsigned char errno;
  const char *errmesg;
} hard_error[] = {
  { ILLHADDR,  "Illegal Host Access" },
  { ILLSADDR,  "Illegal Sequencer Address referenced" },
  { ILLOPCODE, "Illegal Opcode in sequencer program" },
  { PARERR,    "Sequencer Ram Parity Error" }
};

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)
                                    */
  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.
 */
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 */
  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 ABORT_PENDING           0x0400
#define SHARED_SCBDATA          0x0800
#define HAVE_SEEPROM            0x1000
#define RESET_PENDING           0x2000
#define IN_ABORT		0x4000
  unsigned int             flags;
  unsigned long		   last_reset;
  unsigned long		   reset_start;
  unsigned int             isr_count;        /* Interrupt count */
  unsigned short           needsdtr_copy;    /* default config */
  unsigned short           needsdtr;
  unsigned short           sdtr_pending;
  unsigned short           needwdtr_copy;    /* default config */
  unsigned short           needwdtr;
  unsigned short           wdtr_pending;
  unsigned short           orderedtag;
  unsigned short           discenable;	     /* Targets allowed to disconnect */
  aha_chip_type            chip_type;        /* card type */
  aha_chip_class_type      chip_class;
  aha_bus_type             bus_type;         /* normal/twin/wide bus */
  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;
  unsigned char            qfullcount;
  unsigned char            cmdoutcnt;
  unsigned char            curqincnt;
  struct Scsi_Host        *next;             /* allow for multiple IRQs */
  unsigned char            activescbs;       /* active scbs */
  scb_queue_type           waiting_scbs;     /*
                                              * SCBs waiting for space in
                                              * the QINFIFO.
                                              */
  scb_data_type           *scb_data;

  struct aic7xxx_cmd_queue {
    Scsi_Cmnd *head;
    Scsi_Cmnd *tail;
  } completeq;
  struct aic7xxx_device_status {
    unsigned char active_cmds;
    unsigned char max_queue_depth;
    unsigned char temp_queue_depth;
    unsigned char last_queue_full;
    unsigned char last_queue_full_count;
    struct timer_list timer;
    long last_reset;
#define  DEVICE_SUCCESS                 0x01
#define  BUS_DEVICE_RESET_PENDING       0x02
#define  DEVICE_TIMEOUT                 0x04
    int  flags;
    int  commands_sent;
    scb_queue_type delayed_scbs;
    Scsi_Cmnd *scsi_cmnd0;
    Scsi_Cmnd *scsi_cmnd1;
  } device_status[16];
#ifdef AIC7XXX_PROC_STATS
  /*
   * Statistics Kept:
   *
   * Total Xfers (count for each command that has a data xfer),
   * broken down further by reads && writes.
   *
   * Binned sizes, writes && reads:
   *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
   *
   * Total amounts read/written above 512 bytes (amts under ignored)
   */
  struct aic7xxx_xferstats {
    long xfers;                              /* total xfer count */
    long w_total;                            /* total writes */
    long w_total512;                         /* 512 byte blocks written */
    long w_bins[10];                         /* binned write */
    long r_total;                            /* total reads */
    long r_total512;                         /* 512 byte blocks read */
    long r_bins[10];                         /* binned reads */
  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
#endif /* AIC7XXX_PROC_STATS */
};

/*
 * Valid SCSIRATE values. (p. 3-17)
 * 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;
  /* Rates in Ultra mode have bit 8 of sxfr set */
#define		ULTRA_SXFR 0x100
  short rate;
  const char *english;
} aic7xxx_syncrates[] = {
  { 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 =
    sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);

#ifdef CONFIG_PCI
static int number_of_3940s = 0;
static int number_of_3985s = 0;
#endif /* CONFIG_PCI */

#ifdef AIC7XXX_DEBUG

#if 0
static void
debug_scb(struct aic7xxx_scb *scb)
{
  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_scb(x)
#endif AIC7XXX_DEBUG

#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
                        (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
                        ((scb->hscb)->target_channel_lun & 0x07)

#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01),  \
                        ((cmd->target) & 0x0f), \
                        ((cmd->lun) & 0x07)

static inline int
CHAN_TO_INT(char chan)
{
  switch(chan)
  {
    case ALL_CHANNELS:
	return(-1);
    case 'B':
    case 'b':
        return(1);
    case 'A':
    case 'a':
        return(0);
    default:
        printk(KERN_WARNING "aic7xxx: Bad usage of char channel.\n");
        return(0);
  }
}

static inline char
INT_TO_CHAN(int chan)
{
  switch(chan)
  {
    case -1:
	return(ALL_CHANNELS);
    case 1:
        return('B');
    case 0:
        return('A');
    default:
        printk(KERN_WARNING "aic7xxx: Bad usage of int channel.\n");
        return('A');
  }
}


#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.
 */
static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
static int aic7xxx_irq_trigger = -1;         /*
                                              * -1 use board setting
                                              *  0 use edge triggered
                                              *  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)
  {
#ifdef __alpha__
    int i;

    for (i=0; i < size; i++)
    {
      p->maddr[port] = valp[i];
    }
#else
    __asm __volatile("
      cld;
    1:  lodsb;
      movb %%al,(%0);
      loop 1b"      :
              :
      "r" (p->maddr + port),
      "S" (valp), "c" (size)  :
      "%esi", "%ecx", "%eax");
#endif
  }
  else
  {
    outsb(p->base + port, valp, size);
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_setup
 *
 * Description:
 *   Handle Linux boot parameters. This routine allows for assigning a value
 *   to a parameter with a ':' between the parameter and the value.
 *   ie. aic7xxx=unpause:0x0A,extended
 *-F*************************************************************************/
void
aic7xxx_setup(char *s, int *dummy)
{
  int   i, n;
  char *p;

  static struct {
    const char *name;
    unsigned int *flag;
  } options[] = {
    { "extended",    &aic7xxx_extended },
    { "no_reset",    &aic7xxx_no_reset },
    { "irq_trigger", &aic7xxx_irq_trigger },
    { "ultra",       &aic7xxx_enable_ultra },
    { "verbose",     &aic7xxx_verbose },
    { NULL,          NULL }
  };

  for (p = strtok(s, ","); p; p = strtok(NULL, ","))
  {
    for (i = 0; options[i].name; i++)
    {
      n = strlen(options[i].name);
      if (!strncmp(options[i].name, p, n))
      {
        if (p[n] == ':')
        {
          *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
        }
        else
        {
          *(options[i].flag) += 1;
        }
      }
    }
  }
}

/*+F*************************************************************************
 * Function:
 *   pause_sequencer
 *
 * Description:
 *   Pause the sequencer and wait for it to actually stop - this
 *   is important since the sequencer can disable pausing for critical
 *   sections.
 *-F*************************************************************************/
static inline void
pause_sequencer(struct aic7xxx_host *p)
{
  outb(p->pause, p->base + HCNTRL);
  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
  {
    ;
  }
}

/*+F*************************************************************************
 * Function:
 *   unpause_sequencer
 *
 * Description:
 *   Unpause the sequencer. Unremarkable, yet done often enough to
 *   warrant an easy way to do it.
 *-F*************************************************************************/
static inline void
unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
{
  if (unpause_always ||
      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
  {
    outb(p->unpause, p->base + HCNTRL);
  }
}

/*+F*************************************************************************
 * Function:
 *   restart_sequencer
 *
 * Description:
 *   Restart the sequencer program from address zero.  This assumes
 *   that the sequencer is already paused.
 *-F*************************************************************************/
static inline void
restart_sequencer(struct aic7xxx_host *p)
{
  /* Set the sequencer address to 0. */
  outb(0, p->base + SEQADDR0);
  outb(0, p->base + SEQADDR1);

  /*
   * 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 ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
      || (instrptr >= cur_patch->end))
    {
      /*
       * 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)
      {
        /* Out of patches. */
        cur_patch = NULL;
      }
    }
    else
    {
      /* Found an OK patch. */
      break;
    }
  }
  return (cur_patch);
}


/*+F*************************************************************************
 * Function:
 *   aic7xxx_download_instr
 *
 * Description:
 *   Find the next patch to download.
 *-F*************************************************************************/
static void
aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
{
  unsigned char opcode;
  struct ins_format3 *instr;

  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;
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_length
 *
 * Description:
 *   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)
{
  int i, segments;
  unsigned length;
  struct scatterlist *sg;

  segments = cmd->use_sg - sg_last;
  sg = (struct scatterlist *) cmd->request_buffer;

  if (cmd->use_sg)
  {
    for (i = length = 0; i < segments; i++)
    {
      length += sg[i].length;
    }
  }
  else
  {
    length = cmd->request_bufflen;
  }

  return (length);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_scsirate
 *
 * Description:
 *   Look up the valid period to SCSIRATE conversion in our table
 *-F*************************************************************************/
static void
aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
    unsigned char *period, unsigned char *offset, int target, char channel)
{
  int i = num_aic7xxx_syncrates;
  unsigned long ultra_enb_addr;
  unsigned char ultra_enb, sxfrctl0;

  /*
   * If the offset is 0, then the device is requesting asynchronous
   * transfers.
   */
  if ((*period != 0) && (*offset != 0))
  {
    for (i = 0; i < num_aic7xxx_syncrates; i++)
    {
      if (*period <= aic7xxx_syncrates[i].period)
      {
        /*
         * Watch out for Ultra speeds when ultra is not enabled and
         * vice-versa.
         */
        if (!(p->flags & ULTRA_ENABLED) &&
            (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
        {
          /*
           * This should only happen if the drive is the first to negotiate
           * and chooses a high rate.   We'll just move down the table until
           * we hit a non ultra speed.
           */
          continue;
        }
        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
        *period = aic7xxx_syncrates[i].period;

        if (aic7xxx_verbose)
        {
          printk("(scsi%d:%d:%d:%d) Synchronous at %sMHz, "
                 "offset %d.\n", p->host_no, CHAN_TO_INT(channel), target, 0,
                 aic7xxx_syncrates[i].english, *offset);
        }
        break;
      }
    }
  }

  if (i >= num_aic7xxx_syncrates)
  {
    /*
     * Use asynchronous transfers.
     */
    *scsirate = 0;
    *period = 0;
    *offset = 0;
    if (aic7xxx_verbose)
    {
      printk("(scsi%d:%d:%d:%d) Using asynchronous transfers.\n",
             p->host_no, CHAN_TO_INT(channel), target, 0);
    }
  }

  /*
   * 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 ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
  {
    ultra_enb |= 0x01 << (target & 0x07);
    sxfrctl0 |= FAST20;
  }
  else
  {
    ultra_enb &= ~(0x01 << (target & 0x07));
    sxfrctl0 &= ~FAST20;
  }
  outb(ultra_enb, p->base + ultra_enb_addr);
  outb(sxfrctl0, p->base + SXFRCTL0);
}

/*+F*************************************************************************
 * Function:
 *   scbq_init
 *
 * Description:
 *   SCB queue initialization.
 *
 *-F*************************************************************************/
static inline void
scbq_init(scb_queue_type *queue)
{
  queue->head = NULL;
  queue->tail = NULL;
}

/*+F*************************************************************************
 * Function:
 *   scbq_insert_head
 *
 * Description:
 *   Add an SCB to the head of the list.
 *
 *-F*************************************************************************/
static inline void
scbq_insert_head(scb_queue_type *queue, struct aic7xxx_scb *scb)
{
  unsigned long processor_flags;
  save_flags(processor_flags);
  cli();
  scb->q_next = queue->head;
  queue->head = scb;
  if (queue->tail == NULL)       /* If list was empty, update tail. */
    queue->tail = queue->head;
  restore_flags(processor_flags);
}

/*+F*************************************************************************
 * Function:
 *   scbq_remove_head
 *
 * Description:
 *   Remove an SCB from the head of the list.
 *
 *-F*************************************************************************/
static inline void
scbq_remove_head(scb_queue_type *queue)
{
  unsigned long processor_flags;
  save_flags(processor_flags);
  cli();
  if (queue->head != NULL)
    queue->head = queue->head->q_next;
  if (queue->head == NULL)       /* If list is now empty, update tail. */
    queue->tail = NULL;
  restore_flags(processor_flags);
}

/*+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)
{
  unsigned long processor_flags;
  save_flags(processor_flags);
  cli();
  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;
      }
    }
  }
  restore_flags(processor_flags);
}

/*+F*************************************************************************
 * Function:
 *   scbq_insert_tail
 *
 * Description:
 *   Add an SCB at the tail of the list.
 *
 *-F*************************************************************************/
static inline void
scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
{
  unsigned long processor_flags;
  save_flags(processor_flags);
  cli();
  scb->q_next = NULL;
  if (queue->tail != NULL)       /* Add the scb at the end of the list. */
    queue->tail->q_next = scb;

  queue->tail = scb;             /* Update the tail. */
  if (queue->head == NULL)       /* If list was empty, update head. */
    queue->head = queue->tail;
  restore_flags(processor_flags);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_match_scb
 *
 * Description:
 *   Checks to see if an scb matches the target/channel as specified.
 *   If target is ALL_TARGETS (-1), then we're looking for any device
 *   on the specified channel; this happens when a channel is going
 *   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,
    int lun, unsigned char tag)
{
  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;

  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));

  if (aic7xxx_verbose > 4)
  {
    if (match)
    {
      printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) matches search criteria"
        " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
        CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
        CHAN_TO_INT(channel), target, lun, tag);
    }
    else
    {
      printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) doesn't match search criteria"
        " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
        CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
        CHAN_TO_INT(channel), target, lun, tag);
    }
  }

  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);
  outb(SCB_LIST_NULL, p->base + SCB_TAG);

  aic7xxx_add_curscb_to_free_list(p);

  if (prev != SCB_LIST_NULL)
  {
    outb(prev, p->base + SCBPTR);
    outb(next, p->base + SCB_NEXT);
  }
  else
  {
    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*************************************************************************
 * Function:
 *   aic7xxx_busy_target
 *
 * Description:
 *   Set the specified target busy.
 *-F*************************************************************************/
static void
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_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;

  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)
  {
    outb(SCB_LIST_NULL, p->base + scb_offset);
  }
  outb(active_scb, p->base + SCBPTR);
  return (busy_scbid);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_find_scb
 *
 * Description:
 *   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 unsigned char
aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
  unsigned char saved_scbptr;
  unsigned char curindex;

  saved_scbptr = inb(p->base + SCBPTR);
  curindex = 0;
  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
  {
    outb(curindex, p->base + SCBPTR);
    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
    {
      break;
    }
  }
  outb(saved_scbptr, p->base + SCBPTR);
  if (curindex >= p->scb_data->maxhscbs)
  {
    curindex = SCB_LIST_NULL;
  }

  return (curindex);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_allocate_scb
 *
 * Description:
 *   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;
  struct aic7xxx_hwscb *hscbp = NULL;

  scbp = p->scb_data->free_scbs.head;
  if (scbp != NULL)
  {
    scbq_remove_head(&p->scb_data->free_scbs);
  }
  else
  {
    if (p->scb_data->numscbs < p->scb_data->maxscbs)
    {
      int scb_index = p->scb_data->numscbs;
      int scb_size = sizeof(struct aic7xxx_scb) +
                     sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;

      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;
      }
    }
  }

  return (scbp);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_queue_cmd_complete
 *
 * Description:
 *   Due to race conditions present in the SCSI subsystem, it is easier
 *   to queue completed commands, then call scsi_done() on them when
 *   we're finished.  This function queues the completed commands.
 *-F*************************************************************************/
static inline void
aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
  unsigned int flags;
  save_flags(flags);
  cli();
  cmd->host_scribble = (char *)p->completeq.head;
  p->completeq.head = cmd;
  restore_flags(flags);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_done_cmds_complete
 *
 * Description:
 *   Process the completed command queue.
 *-F*************************************************************************/
static inline void
aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
{
  Scsi_Cmnd *cmd;
  unsigned int processor_flags;
  
  save_flags(processor_flags);
  cli();
  while (p->completeq.head != NULL)
  {
    cmd = p->completeq.head;
    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
    cmd->host_scribble = NULL;
    restore_flags(processor_flags);
    cmd->scsi_done(cmd);
    cli();
  }
  restore_flags(processor_flags);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_free_scb
 *
 * Description:
 *   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_hwscb *hscb;

  hscb = scb->hscb;

  scb->flags = SCB_FREE;
  scb->cmd = NULL;
  hscb->control = 0;
  hscb->target_status = 0;

  scbq_insert_head(&p->scb_data->free_scbs, scb);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_done
 *
 * Description:
 *   Calls the higher level scsi done function and frees the scb.
 *-F*************************************************************************/
static void
aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
  Scsi_Cmnd *cmd = scb->cmd;
  int tindex = TARGET_INDEX(cmd);
  struct aic7xxx_scb *scbp;
  unsigned char queue_depth;

  if (scb->flags & SCB_RECOVERY_SCB)
  {
    p->flags &= ~ABORT_PENDING;
  }
  if (scb->flags & SCB_RESET)
  {
      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
	(cmd->result & 0xffff);
  }
  else if (scb->flags & SCB_ABORT)
  {
      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
	(cmd->result & 0xffff);
  }
  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
  {
    unsigned short mask;
    int message_error = FALSE;

    mask = 0x01 << TARGET_INDEX(scb->cmd);
 
    /*
     * Check to see if we get an invalid message or a message error
     * after failing to negotiate a wide or sync transfer message.
     */
    if ((scb->flags & SCB_SENSE) && 
          ((scb->cmd->sense_buffer[12] == 0x43) ||  /* INVALID_MESSAGE */
          (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR  */
    {
      message_error = TRUE;
    }

    if (scb->flags & SCB_MSGOUT_WDTR)
    {
      p->wdtr_pending &= ~mask;
      if (message_error)
      {
        p->needwdtr &= ~mask;
        p->needwdtr_copy &= ~mask;
      }
    }
    if (scb->flags & SCB_MSGOUT_SDTR)
    {
      p->sdtr_pending &= ~mask;
      if (message_error)
      {
        p->needsdtr &= ~mask;
        p->needsdtr_copy &= ~mask;
      }
    }
  }
  queue_depth = (p->device_status[tindex].timer.expires) ?
		 p->device_status[tindex].temp_queue_depth :
		 p->device_status[tindex].max_queue_depth;
  scbp = p->device_status[tindex].delayed_scbs.head;
  if ( (scbp != NULL) && 
       (queue_depth > p->device_status[tindex].active_cmds) )
  {
    scbq_remove_head(&p->device_status[tindex].delayed_scbs);
    scbq_insert_tail(&p->waiting_scbs, scbp);
    scbp = p->device_status[tindex].delayed_scbs.head;
    if ( (scbp != NULL) && 
         (queue_depth > (p->device_status[tindex].active_cmds + 1)) )
    {
      scbq_remove_head(&p->device_status[tindex].delayed_scbs);
      scbq_insert_tail(&p->waiting_scbs, scbp);
    }
  }
  if ( (p->device_status[tindex].timer.expires) &&
      ((p->device_status[tindex].active_cmds == 1) ||
       (p->device_status[tindex].max_queue_depth ==
	p->device_status[tindex].temp_queue_depth)) )
  {
    del_timer(&p->device_status[tindex].timer);
    p->device_status[tindex].timer.expires = 0;
    p->device_status[tindex].temp_queue_depth = 
          p->device_status[tindex].max_queue_depth;
  }
  p->device_status[tindex].active_cmds--;
  aic7xxx_free_scb(p, scb);
  aic7xxx_queue_cmd_complete(p, cmd);

#ifdef AIC7XXX_PROC_STATS
  if ( (cmd->cmnd[0] != TEST_UNIT_READY) &&
       (cmd->cmnd[0] != INQUIRY) )
  {
    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 ((actual > 0) && (((cmd->result >> 16) & 0xf) == DID_OK))
    {
      struct aic7xxx_xferstats *sp;
      long *ptr;
      int x;

      sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
      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_run_done_queue
 *
 * Description:
 *   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_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
{
  struct aic7xxx_scb *scb;
  int i, found = 0;

  for (i = 0; i < p->scb_data->numscbs; i++)
  {
    scb = p->scb_data->scb_array[i];
    if (scb->flags & SCB_QUEUED_FOR_DONE)
    {
      if (aic7xxx_verbose > 3)
        printk("(scsi%d:%d:%d:%d) Aborting scb %d\n",
             p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
      found++;
      aic7xxx_done(p, scb);
    }
  }
  if (aic7xxx_verbose > 1)
  {
    printk(KERN_WARNING "(scsi%d:-1:-1:-1) %d commands found and queued for "
	"completion.\n", p->host_no, found);
  }
  if (complete)
  {
    aic7xxx_done_cmds_complete(p);
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_abort_waiting_scb
 *
 * Description:
 *   Manipulate the waiting for selection list and return the
 *   scb that follows the one that we remove.
 *-F*************************************************************************/
static unsigned char
aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
    unsigned char scbpos, unsigned char prev)
{
  unsigned char curscb, next;

  /*
   * Select the SCB we want to abort and pull the next pointer out of it.
   */
  curscb = inb(p->base + SCBPTR);
  outb(scbpos, p->base + SCBPTR);
  next = inb(p->base + SCB_NEXT);

  /*
   * Clear the necessary fields
   */
  outb(0, p->base + SCB_CONTROL);
  outb(SCB_LIST_NULL, p->base + SCB_TAG);

  aic7xxx_add_curscb_to_free_list(p);

  /*
   * Update the waiting list
   */
  if (prev == SCB_LIST_NULL)
  {
    /*
     * First in the list
     */
    outb(next, p->base + WAITING_SCBH);
  }
  else
  {
    /*
     * Select the scb that pointed to us and update its next pointer.
     */
    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, p->base + SCBPTR);
  scb->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
  scb->flags &= ~SCB_ACTIVE;

  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, scb_queue_type *queue)
{
  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_RECOVERY_SCB);
       }
       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?
       */
      if ( !(scbp->flags & SCB_WAITINGQ) )
      {    /*  OK...we aren't already on a queue, so put us on there  */
        p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds--;
        scbq_insert_head(queue, scbp);
        scbp->flags |= SCB_WAITINGQ;
      }
      scbp = removed_scbs.head;
    }
  }

  return (found);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_reset_device
 *
 * Description:
 *   The device at the given target/channel has been reset.  Abort
 *   all active and queued scbs for that target/channel.  This function
 *   need not worry about linked next pointers because if was a MSG_ABORT_TAG
 *   then we had a tagged command (no linked next), if it was MSG_ABORT or
 *   MSG_BUS_DEV_RESET then the device won't know about any commands any more
 *   and no busy commands will exist, and if it was a bus reset, then nothing
 *   knows about any linked next commands any more.  In all cases, we don't
 *   need to worry about the linked next or busy scb, we just need to clear
 *   them.
 *-F*************************************************************************/
static void
aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
                     int lun, unsigned char tag)
{
  struct aic7xxx_scb *scbp;
  unsigned char active_scb;
  int i = 0, j, init_lists = FALSE;

  /*
   * Restore this when we're done
   */
  active_scb = inb(p->base + SCBPTR);

  if (aic7xxx_verbose > 2)
    printk("(scsi%d:%d:%d:%d) Reset device, active_scb %d\n",
         p->host_no, CHAN_TO_INT(channel), target, lun, active_scb);
  /*
   * Deal with the busy target and linked next issues.
   */
  {
    int min_target, max_target;
    unsigned char busy_scbid;
    struct aic7xxx_scb *scbp, *prev_scbp;

    /* Make all targets 'relative' to bus A. */
    if (target == ALL_TARGETS)
    {
      switch (channel)
      {
        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;
      }
    }
    else
    { 
      min_target = target + ((channel == 'B') ? 8 : 0);
      max_target = min_target;
    }


    for (i = min_target; i <= max_target; i++)
    {
      if (aic7xxx_verbose > 2)
	printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning up status information "
	  "and delayed_scbs.\n", p->host_no, CHAN_TO_INT(channel), i, lun);
      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/ TRUE);
      p->device_status[i].flags &= ~BUS_DEVICE_RESET_PENDING;
      p->device_status[i].last_reset = jiffies;
      p->device_status[i].last_queue_full_count = 0;
      p->device_status[i].last_queue_full = 0;
      p->device_status[i].temp_queue_depth =
        p->device_status[i].max_queue_depth;
      j = 0; 
      prev_scbp = NULL; 
      scbp = p->device_status[i].delayed_scbs.head;
      while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
      {
        prev_scbp = scbp;
        scbp = scbp->q_next;
        if ( prev_scbp == scbp )
        {
	  if (aic7xxx_verbose)
	    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! scb->q_next == scb "
	      "in the delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
              i, lun);
	  scbp = NULL;
	  prev_scbp->q_next = NULL;
	  p->device_status[i].delayed_scbs.tail = prev_scbp;
        }
        if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
        {
	  scbq_remove(&p->device_status[i].delayed_scbs, prev_scbp);
	  if ( prev_scbp->flags & SCB_WAITINGQ )
	    p->device_status[i].active_cmds++;
	  prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
	  prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
        }
      }
      if ( j > p->scb_data->numscbs )
      {
        if (aic7xxx_verbose)
	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! There's a loop in the "
	    "delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
            i, lun);
        scbq_init(&p->device_status[i].delayed_scbs);
      }
      if ( (p->device_status[i].delayed_scbs.head == NULL) &&
           (p->device_status[i].timer.expires) )
      {
        del_timer(&p->device_status[i].timer);
        p->device_status[i].timer.expires = 0;
      }
    }
  }

  if (aic7xxx_verbose > 2)
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning QINFIFO.\n", p->host_no,
	CHAN_TO_INT(channel), target, lun );
  aic7xxx_search_qinfifo(p, target, channel, lun, tag,
      SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);

/*
 *  Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
 *  ABORT/RESET commands.
 */
  if (aic7xxx_verbose > 2)
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting_scbs.\n",
	p->host_no, CHAN_TO_INT(channel), target, lun );
  {
    struct aic7xxx_scb *scbp, *prev_scbp;

    j = 0; 
    prev_scbp = NULL; 
    scbp = p->waiting_scbs.head;
    while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
    {
      prev_scbp = scbp;
      scbp = scbp->q_next;
      if ( prev_scbp == scbp )
      {
	if (aic7xxx_verbose)
	  printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! scb->q_next == scb "
	    "in the waiting_scbs queue!\n", p->host_no);
	scbp = NULL;
	prev_scbp->q_next = NULL;
	p->waiting_scbs.tail = prev_scbp;
      }
      if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
      {
	scbq_remove(&p->waiting_scbs, prev_scbp);
	if ( prev_scbp->flags & SCB_WAITINGQ )
	  p->device_status[TARGET_INDEX(prev_scbp->cmd)].active_cmds++;
	prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
	prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
      }
    }
    if ( j > p->scb_data->numscbs )
    {
      if (aic7xxx_verbose)
	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There's a loop in the "
	  "waiting_scbs queue!\n", p->host_no);
      scbq_init(&p->waiting_scbs);
    }
  }


  /*
   * Search waiting for selection list.
   */
  if (aic7xxx_verbose > 2)
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting for selection "
	"list.\n", p->host_no, CHAN_TO_INT(channel), target, lun);
  {
    unsigned char next, prev, scb_index;

    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
    prev = SCB_LIST_NULL;
    j = 0;
    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
    {
      outb(next, p->base + SCBPTR);
      scb_index = inb(p->base + SCB_TAG);
      if (scb_index >= p->scb_data->numscbs)
      {
	if (aic7xxx_verbose)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
		"SCB index=%d, numscbs=%d\n", p->host_no,
                CHAN_TO_INT(channel), target, lun, scb_index,
                p->scb_data->numscbs);
	next = inb(p->base + SCB_NEXT);
	aic7xxx_add_curscb_to_free_list(p);
      }
      else
      {
        scbp = p->scb_data->scb_array[scb_index];
        if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
        {
          next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
	  scbp->flags &= ~SCB_ACTIVE;
	  scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
        }
        else
        {
          prev = next;
          next = inb(p->base + SCB_NEXT);
        }
      }
    }
    if ( j > p->scb_data->maxhscbs )
    {
      if (aic7xxx_verbose)
	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
	  "waiting for selection list!\n", p->host_no);
      init_lists = TRUE;
    }
  }

  /*
   * 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;
    j = 0;
    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
    {
      outb(next, p->base + SCBPTR);
      scb_index = inb(p->base + SCB_TAG);
      if (scb_index > p->scb_data->numscbs)
      {
	if (aic7xxx_verbose)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
		"SCB index=%d, numscbs=%d\n", p->host_no,
                CHAN_TO_INT(channel), target, lun, scb_index,
                p->scb_data->numscbs);
	next = aic7xxx_rem_scb_from_disc_list(p, next);
      }
      else
      {
        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);
	  scbp->flags = SCB_RESET | SCB_QUEUED_FOR_DONE;
	  scbp->hscb->control = 0;
        }
        else
        {
          prev = next;
          next = inb(p->base + SCB_NEXT);
        }
      }
    }
    if ( j > p->scb_data->maxhscbs )
    {
      if (aic7xxx_verbose)
	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
	  "disconnected list!\n", p->host_no);
      init_lists = TRUE;
    }
  }

  /*
   * Walk the free list making sure no entries on the free list have
   * a valid SCB_TAG value or SCB_CONTROL byte.
   */
  {
    unsigned char next;

    j = 0;
    next = inb(p->base + FREE_SCBH);
    while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
    {
      outb(next, p->base + SCBPTR);
      outb(SCB_LIST_NULL, p->base + SCB_TAG);
      outb(0, p->base + SCB_CONTROL);
      next = inb(p->base + SCB_NEXT);
    }
    if ( j > p->scb_data->maxhscbs )
    {
      if (aic7xxx_verbose)
	printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!!  There is a loop in the "
	  "free list!\n", p->host_no);
      init_lists = TRUE;
    }
  }

  /*
   * Go through the hardware SCB array looking for commands that
   * were active but not on any list.
   */
  if (init_lists)
  {
    outb(SCB_LIST_NULL, p->base + FREE_SCBH);
    outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
    outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
  }
  for (i = p->scb_data->maxhscbs; i >= 0; --i)
  {
    unsigned char scbid;

    if (init_lists)
    {
      outb(SCB_LIST_NULL, p->base + SCB_TAG);
      outb(SCB_LIST_NULL, p->base + SCB_NEXT);
      outb(SCB_LIST_NULL, p->base + SCB_PREV);
      outb(SCB_LIST_NULL, p->base + SCB_LINKED_NEXT);
      outb(0, p->base + SCB_CONTROL);
      aic7xxx_add_curscb_to_free_list(p);
    }
    else
    {
      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))
        {
	  outb(0, p->base + SCB_CONTROL);
	  outb(SCB_LIST_NULL, p->base + SCB_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 stillactive.  These are other (most likely
   * tagged) commands that were disconnected when the reset occurred.
   */
  for (i = 0; i < p->scb_data->numscbs; i++)
  {
    scbp = p->scb_data->scb_array[i];
    if (((scbp->flags & SCB_ACTIVE) != 0) &&
        aic7xxx_match_scb(scbp, target, channel, lun, tag))
    {
      scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
      scbp->flags &= ~SCB_ACTIVE;

      if ((scbp->flags & SCB_WAITINGQ) != 0)
      {
        scbq_remove(&p->waiting_scbs, scbp);
        scbq_remove(&p->device_status[TARGET_INDEX(scbp->cmd)].delayed_scbs,
	  scbp);
        scbp->flags &= ~SCB_WAITINGQ;
	p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds++;
      }
    }
  }

  outb(active_scb, p->base + SCBPTR);
}


/*+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
 *
 * Description:
 *   Reset the current SCSI bus.
 *-F*************************************************************************/
static void
aic7xxx_reset_current_bus(struct aic7xxx_host *p)
{
  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);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_reset_channel
 *
 * Description:
 *   Reset the channel.
 *-F*************************************************************************/
static void
aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
{
  unsigned long offset, offset_max;
  unsigned char sblkctl;
  char cur_channel;

  if (aic7xxx_verbose > 1)
    printk(KERN_WARNING "(scsi%d:%d:-1:-1) Reset channel called, %s initiate "
	"reset.\n", p->host_no, CHAN_TO_INT(channel),
	(initiate_reset == TRUE) ? "will" : "won't" );


  if (channel == 'B')
  {
    p->needsdtr |= (p->needsdtr_copy & 0xFF00);
    p->sdtr_pending &= 0x00FF;
    offset = TARG_SCRATCH + 8;
    offset_max = TARG_SCRATCH + 16;
  }
  else
  {
    if (p->bus_type == AIC_WIDE)
    {
      p->needsdtr = p->needsdtr_copy;
      p->needwdtr = p->needwdtr_copy;
      p->sdtr_pending = 0x0;
      p->wdtr_pending = 0x0;
      offset = TARG_SCRATCH;
      offset_max = TARG_SCRATCH + 16;
    }
    else
    {
      /* Channel A */
      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
      p->sdtr_pending &= 0xFF00;
      offset = TARG_SCRATCH;
      offset_max = TARG_SCRATCH + 8;
    }
  }

  while (offset < offset_max)
  {
    /*
     * Revert to async/narrow transfers until we renegotiate.
     */
    u_char targ_scratch;

    targ_scratch = inb(p->base + offset);
    targ_scratch &= SXFR;
    outb(targ_scratch, p->base + offset);
    offset++;
  }

  /*
   * Reset the bus and unpause/restart the controller
   */
  sblkctl = inb(p->base + SBLKCTL);
  cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
  if (cur_channel != channel)
  {
    /*
     * Case 1: Command for another bus is active
     */
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:-1:-1) Stealthily resetting idle "
           "channel.\n", p->host_no, CHAN_TO_INT(channel));
    /*
     * Stealthily reset the other bus without upsetting the current bus.
     */
    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
    if (initiate_reset)
    {
      aic7xxx_reset_current_bus(p);
    }
    outb(0, p->base + SCSISEQ);
    aic7xxx_clear_intstat(p);
    outb(sblkctl, p->base + SBLKCTL);
  }
  else
  {
    /*
     * Case 2: A command from this bus is active or we're idle.
     */
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:-1:-1) Resetting currently active "
	"channel.\n", p->host_no, CHAN_TO_INT(channel));
    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
    if (initiate_reset)
    {
      aic7xxx_reset_current_bus(p);
    }
    outb(0, p->base + SCSISEQ);
    aic7xxx_clear_intstat(p);
  }
  if (aic7xxx_verbose)
    printk(KERN_WARNING "(scsi%d:%d:-1:-1) Channel reset\n",
	p->host_no, CHAN_TO_INT(channel));
  /*
   * Clean up all the state information for the pending transactions
   * on this bus.
   */
  aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);

  if ( p->bus_type != AIC_TWIN )
  {
    restart_sequencer(p);
    pause_sequencer(p);
  }

  p->host->last_reset = jiffies + (HZ * AIC7XXX_RESET_DELAY);

  /*
   * Now loop through all the SCBs that have been marked for abortion,
   * and call the scsi_done routines.
   */
  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
  return;
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_run_waiting_queues
 *
 * Description:
 *   Scan the awaiting_scbs queue downloading and starting as many
 *   scbs as we can.
 *-F*************************************************************************/
static inline void
aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
{
  struct aic7xxx_scb *scb;
  int tindex;


  if (p->waiting_scbs.head == NULL)
    return;

  /*
   * First handle SCBs that are waiting but have been assigned a slot.
   */
  pause_sequencer(p);
  scb = p->waiting_scbs.head;
  while (scb != NULL)
  {
    if (p->curqincnt >= p->qfullcount)
    {
      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
      if (p->curqincnt >= p->qfullcount)
      {
        break;
      }
    }

    /*
     * We have some space.
     */
    scbq_remove_head(&p->waiting_scbs);
    tindex = TARGET_INDEX(scb->cmd);
    if ( p->device_status[tindex].active_cmds >=
         p->device_status[tindex].temp_queue_depth )
    {
        scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
    }
    else
    {
        scb->flags &= ~SCB_WAITINGQ;
        p->device_status[tindex].active_cmds++;
        outb(scb->hscb->tag, p->base + QINFIFO);
        p->curqincnt++;
    }
    scb = p->waiting_scbs.head;
  }
  unpause_sequencer(p, FALSE);
}


/*+F*************************************************************************
 * Function:
 *   aic7xxx_timer
 *
 * Description:
 *   Take expired extries off of delayed queues and place on waiting queue
 *   then run waiting queue to start commands.
 ***************************************************************************/
static void
aic7xxx_timer(struct aic7xxx_host *p)
{
   int i;
   unsigned long processor_flags;
   struct aic7xxx_scb *scb;
   
   save_flags(processor_flags);
   cli();
   if (aic7xxx_verbose > 3)
     printk(KERN_WARNING "(scsi%d:-1:-1:-1) Timer running.\n", p->host_no);
   for(i=0; i<MAX_TARGETS; i++)
   {
     if ( (p->device_status[i].timer.expires) && 
          (p->device_status[i].timer.expires <= jiffies) )
     {
       p->device_status[i].timer.expires = 0;
       if ( (p->device_status[i].timer.prev != NULL) ||
            (p->device_status[i].timer.next != NULL) )
       {
         del_timer(&p->device_status[i].timer);
       }
       p->device_status[i].temp_queue_depth = 
	 p->device_status[i].max_queue_depth;
       scb = p->device_status[i].delayed_scbs.head;
       while ( scb != NULL )
       {
         scbq_remove_head(&p->device_status[i].delayed_scbs);
         scbq_insert_tail(&p->waiting_scbs, scb);
         scb = p->device_status[i].delayed_scbs.head;
       }
     }
   }
   aic7xxx_run_waiting_queues(p);
   unpause_sequencer(p, FALSE);
   restore_flags(processor_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_construct_wdtr
 *
 * Description:
 *   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
 *
 * Description:
 *   Calculate the residual data not yet transferred.
 *-F*************************************************************************/
static void
aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
  struct aic7xxx_hwscb *hscb;
  Scsi_Cmnd *cmd;
  int actual;

  cmd = scb->cmd;
  hscb = scb->hscb;

  /*
   *  Don't destroy valid residual information with
   *  residual coming from a check sense operation.
   */
  if (((scb->hscb->control & DISCONNECTED) == 0) &&
      (scb->flags & SCB_SENSE) == 0)
  {
    /*
     *  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) && (aic7xxx_verbose) )
    {
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Underflow - "
             "Wanted at least %u, got %u, residual SG count %d.\n",
             p->host_no, CTL_OF_SCB(scb), cmd->underflow, actual,
             hscb->residual_SG_segment_count);
      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
      aic7xxx_status(cmd) = hscb->target_status;
    }
  }

  /*
   * Clean out the residual information in the SCB for the
   * next consumer.
   */
  hscb->residual_data_count[2] = 0;
  hscb->residual_data_count[1] = 0;
  hscb->residual_data_count[0] = 0;
  hscb->residual_SG_segment_count = 0;
}

/*+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;

  if (channel == 'B')
  {
    scratch_offset += 8;
  }
  targ_mask = (0x01 << scratch_offset);
  /*
   * Go back to async/narrow transfers and renegotiate.
   */
  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);
  aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
  if (aic7xxx_verbose)
    printk(KERN_WARNING "(scsi%d:%d:%d:-1) Bus Device Reset delivered.\n",
         p->host_no, CHAN_TO_INT(channel), target);
  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
}

/*+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, lun;
  unsigned char queue_flag = FALSE;
  char channel;

  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
  {
    target = (inb(p->base + SELID) >> 4) & 0x0F;
  }
  else
  {
    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
  }
  scratch_offset = target;
  channel = 'A';
  lun = inb(p->base + SAVED_TCL) & 0x07;
  if (inb(p->base + SBLKCTL) & SELBUSB)
  {
    channel = 'B';
    scratch_offset += 8;
  }
  target_mask = (0x01 << scratch_offset);

  switch (intstat & SEQINT_MASK)
  {
    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;

        busy_scbid = aic7xxx_index_busy_target(p, target, channel,
            /*unbusy*/ FALSE);
        arg1 = inb(p->base + ARG_1);

        if (arg1 == SCB_LIST_NULL)
        {
          /* untagged request */
          scb_index = busy_scbid;
        }
        else
        {
          scb_index = arg1;
        }

        if (scb_index < p->scb_data->numscbs)
        {
          scb = p->scb_data->scb_array[scb_index];
          if (scb->hscb->control & ABORT_SCB)
          {
            /*
             * We expected this.  Let the busfree handler take care
             * of this when we the abort is finally 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);
	    if (aic7xxx_verbose > 1)
              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reconnect SCB abort "
		"successful\n", p->host_no, CTL_OF_SCB(scb));
            break;
          }
        }
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) No active SCB for reconnecting "
               "target - Issuing BUS DEVICE RESET.\n",
               p->host_no, CHAN_TO_INT(channel), target, lun);

        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;

    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;

        scb_index = inb(p->base + CUR_SCBID);
        scb = p->scb_data->scb_array[scb_index];

        panic("(scsi%d:%d:%d:%d) Target busy link failure, "
              "but busy SCB exists!\n",
              p->host_no, CHAN_TO_INT(channel), target, lun );
      }
      break;

    case SEND_REJECT:
      {
        unsigned char rej_byte;

        rej_byte = inb(p->base + REJBYTE);
	if (aic7xxx_verbose > 1)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting unknown message "
               "(0x%x) received from target, SEQ_FLAGS=0x%x\n",
               p->host_no, CHAN_TO_INT(channel), target, lun,
               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.
         */
        if (aic7xxx_verbose)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target did not send an "
               "IDENTIFY message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
               p->host_no, CHAN_TO_INT(channel), target, lun,
               inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));

        aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);

      }
      break;

    case BAD_PHASE:
      if (inb(p->base + LASTPHASE) == P_BUSFREE)
      {
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Missed busfree.\n",
               p->host_no, CHAN_TO_INT(channel), target, lun);
        restart_sequencer(p);
      }
      else
      {
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unknown scsi bus phase, "
               "continuing\n", p->host_no, CHAN_TO_INT(channel), target, lun);
      }
      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)
            {
              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)
            {
              /* We started it. */
              if (saved_offset == offset)
              {
        	/*
        	 * Don't send an SDTR back to the target.
        	 */
        	outb(0, p->base + RETURN_1);
              }
              else
              {
        	/*
                 * We went too low - force async and disable future
                 * sync negotiation
                 */
                p->needsdtr_copy &= ~target_mask;
        	outb(SEND_REJ, p->base + RETURN_1);
              }
            }
            else if ( offset != 0 )
            {
              /*
               * 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:%d:%d:%d) Sending reply SDTR.\n",
                p->host_no, CTL_OF_SCB(scb));
              aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
              outb(SEND_MSG, p->base + RETURN_1);
            }
            else
            {
              /*
               * The incoming SDTR was too low, reject it.
               */
              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting SDTR request.\n",
                p->host_no, CTL_OF_SCB(scb));
              outb(SEND_REJ, p->base + RETURN_1);
              p->needsdtr_copy &= ~target_mask;
            }
            /*
             * Clear the flags.
             */
            p->needsdtr &= ~target_mask;
            p->sdtr_pending &= ~target_mask;
            break;
          }

          case MSG_EXT_WDTR:
          {
            unsigned char scratch, bus_width;

            if (message_length != MSG_EXT_WDTR_LEN)
            {
              outb(SEND_REJ, p->base + RETURN_1);
              break;
            }

            bus_width = inb(p->base + MSGIN_EXT_BYTES);
            scratch = inb(p->base + TARG_SCRATCH + 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:
                  p->needwdtr_copy &= ~target_mask;
        	  scratch &= 0x7F;
        	  break;

        	case BUS_16_BIT:
                  if (aic7xxx_verbose)
                  {
        	    printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
  	  	         "transfers.\n", p->host_no, CTL_OF_SCB(scb));
                  }
        	  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:%d:%d:%d) "
  			"requesting 32 bit transfers, rejecting...\n",
                	 p->host_no, CTL_OF_SCB(scb));
        	  break;

        	default:
        	  break;
              }
            }
            else
            {
              int send_reject = FALSE;

              /*
               * Send our own WDTR in reply.
               */
              switch (bus_width)
              {
        	case BUS_8_BIT:
                  p->needwdtr_copy &= ~target_mask;
        	  scratch &= 0x7F;
        	  break;

        	case BUS_32_BIT:
        	case BUS_16_BIT:
        	  if (p->bus_type == AIC_WIDE)
        	  {
                    printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
  			   "transfers.\n", p->host_no, CTL_OF_SCB(scb));
                    bus_width = BUS_16_BIT;
                    scratch |= WIDEXFER;
        	  }
        	  else
        	  {
                    bus_width = BUS_8_BIT;
                    scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
                    send_reject = TRUE;
        	  }
        	  break;

        	default:
        	  break;
              }
              if (send_reject)
              {
                outb(SEND_REJ, p->base + RETURN_1);
                printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target initiated "
                       "wide negotiation on a narrow bus - rejecting!",
                       p->host_no, CTL_OF_SCB(scb));
              }
              else
              {
                aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
                outb(SEND_MSG, p->base + RETURN_1);
              }
            }
            p->needwdtr &= ~target_mask;
            p->wdtr_pending &= ~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:
      {
	/*
	 * 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 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)
	{
          /*
           * note 8bit xfers and clear flag
           */
          targ_scratch &= 0x7F;
          p->needwdtr &= ~target_mask;
          p->needwdtr_copy &= ~target_mask;
          p->wdtr_pending &= ~target_mask;
          if (aic7xxx_verbose)
            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing WIDE "
  		 "negotiation; using 8 bit transfers.\n",
  		 p->host_no, CTL_OF_SCB(scb));
	}
	else
	{
          if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
          {
            /*
             * note asynch xfers and clear flag
             */
            targ_scratch &= 0xF0;
            p->needsdtr &= ~target_mask;
            p->needsdtr_copy &= ~target_mask;
            p->sdtr_pending &= ~target_mask;
 	    if (aic7xxx_verbose)
              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing "
  		   "synchronous negotiation; using asynchronous transfers.\n",
  		   p->host_no, CTL_OF_SCB(scb));
          }
          /*
           * Otherwise, we ignore it.
           */
	}
        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
        outb(targ_scratch, p->base + SCSIRATE);
      }
      break;

    case BAD_STATUS:
      {
	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;

	/*
	 * 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))
	{
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Invalid SCB during "
        	 "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
        	 CHAN_TO_INT(channel), target, lun, intstat, scb_index,
                 scb->flags, (unsigned int) scb->cmd);
	}
	else
	{
          cmd = scb->cmd;
  	  hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
          aic7xxx_status(cmd) = hscb->target_status;

          cmd->result |= hscb->target_status;

          switch (status_byte(hscb->target_status))
          {
            case GOOD:
  	      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Interrupted for status of "
                     "GOOD???\n", p->host_no, CTL_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:
              queue_flag = TRUE;    /* Mark that this is a QUEUE_FULL and */
            case BUSY:              /* drop through to here */
            {
              struct aic7xxx_scb *next_scbp, *prev_scbp;
              int ti = TARGET_INDEX(scb->cmd);
              
              aic7xxx_search_qinfifo(p, target, channel, lun,
                SCB_LIST_NULL, 0, TRUE,
                &p->device_status[ti].delayed_scbs);
              next_scbp = p->waiting_scbs.head;
              while ( next_scbp != NULL )
              {
                prev_scbp = next_scbp;
                next_scbp = next_scbp->q_next;
                if ( aic7xxx_match_scb(prev_scbp, target, channel, lun,
                     SCB_LIST_NULL) )
                {
                  scbq_remove(&p->waiting_scbs, prev_scbp);
                  scbq_insert_tail(&p->device_status[ti].delayed_scbs,
                    prev_scbp);
                }
              }
	      scbq_insert_head(&p->device_status[ti].delayed_scbs, scb);
	      p->device_status[ti].active_cmds--;
	      scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
                  
              if (p->device_status[ti].timer.expires == 0) 
              {
		if ( p->device_status[ti].active_cmds )
		{
                  p->device_status[ti].timer.expires = jiffies + (HZ * 2);
                  add_timer(&p->device_status[ti].timer);
		}
		else
		{
                  p->device_status[ti].timer.expires = jiffies + (HZ / 2);
                  add_timer(&p->device_status[ti].timer);
		}
              }
   	      if (aic7xxx_verbose > 1)
              {
                if (queue_flag)
                  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue full received; "
                     "queue depth %d, active %d\n", p->host_no,
                     CTL_OF_SCB(scb), p->device_status[ti].max_queue_depth,
                     p->device_status[ti].active_cmds);
                else
                  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target busy\n",
                     p->host_no, CTL_OF_SCB(scb));

              }
              if (queue_flag)
              {
	        p->device_status[ti].temp_queue_depth = 
		  p->device_status[ti].active_cmds;
                if ( (p->device_status[ti].last_queue_full <
                     (p->device_status[ti].active_cmds - 1)) ||
                     (p->device_status[ti].last_queue_full >
                     (p->device_status[ti].active_cmds + 1)) )
                {
                  p->device_status[ti].last_queue_full = 
                      p->device_status[ti].active_cmds;
                  p->device_status[ti].last_queue_full_count = 0;
                }
                else
                {
                  p->device_status[ti].last_queue_full_count++;
                }
                if ( (p->device_status[ti].last_queue_full_count > 14) &&
                     (p->device_status[ti].active_cmds > 4) )
                {
		  if (aic7xxx_verbose)
                    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue depth reduced "
                      "to %d\n", p->host_no, CTL_OF_SCB(scb), 
                      p->device_status[ti].active_cmds);
                  p->device_status[ti].max_queue_depth = 
                      p->device_status[ti].active_cmds;
                  p->device_status[ti].last_queue_full = 0;
                  p->device_status[ti].last_queue_full_count = 0;
                }
              }
              break;
            }
            
            default:
              printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unexpected target "
                     "status 0x%x.\n", p->host_no,
        	     CTL_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 AWAITING_MSG:
      {
	unsigned char scb_index;
        unsigned char message_offset;

	scb_index = inb(p->base + SCB_TAG);
	scb = p->scb_data->scb_array[scb_index];

	/*
	 * 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)
	{
          outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
          outb(1, p->base + MSG_LEN);
          if (aic7xxx_verbose > 1)
            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset mailed.\n",
        	 p->host_no, CTL_OF_SCB(scb));
	}
        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);
          if (aic7xxx_verbose > 1)
            printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort message mailed.\n",
                 p->host_no, CTL_OF_SCB(scb));
        }
	else if (scb->flags & SCB_MSGOUT_WDTR)
	{
          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.");
	}
      }
      break;

    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;
        if (aic7xxx_verbose)
        {
	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Data overrun of %d bytes "
               "detected in %s phase, tag %d; forcing a retry.\n",
               p->host_no, CTL_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_WARNING "     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;

/* #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;

    default:		   /* unknown */
      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
             p->host_no, intstat, inb(p->base + SCSISIGI));
      break;
  }

  /*
   * Clear the sequencer interrupt and unpause the sequencer.
   */
  outb(CLRSEQINT, p->base + CLRINT);
  unpause_sequencer(p, /* unpause always */ TRUE);
}

/*+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;

  scb_index = inb(p->base + SCB_TAG);
  status = inb(p->base + SSTAT1);

  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;
  }

  if ((status & SCSIRSTI) != 0)
  {
    char channel;

    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';

    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;

    outb(0, p->base + SCSISEQ);
    if (lastphase == P_MESGOUT)
    {
      unsigned char sindex;
      unsigned char message;

      sindex = inb(p->base + SINDEX);
      message = inb(p->base + sindex - 1);

      if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
      {
	if (aic7xxx_verbose > 1)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB %d abort delivered.\n",
                   p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
        aic7xxx_reset_device(p, target, channel, ALL_LUNS,
		(message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
	aic7xxx_run_done_queue(p, FALSE);
        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
        {
          tag = SCB_LIST_NULL;
        }
        aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
	aic7xxx_run_done_queue(p, FALSE);
      }
      else
      {  /* Since we don't really know what happened here, we'll wait */
	 /* for the commands to timeout and get aborted if need be    */
        aic7xxx_add_curscb_to_free_list(p);
      }
      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));
      scb = NULL;
    }
    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);

    scb = NULL;
    if (scb_index < p->scb_data->numscbs)
    {
      scb = p->scb_data->scb_array[scb_index];
      if ((scb->flags & SCB_ACTIVE) == 0)
      {
        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);

    cmd = scb->cmd;
    switch (lastphase)
    {
      case P_DATAOUT:
        phase = "Data-Out";
        break;
      case P_DATAIN:
        phase = "Data-In";
        mesg_out = MSG_INITIATOR_DET_ERR;
        break;
      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;
    }

    /*
     * A parity error has occurred during a data
     * transfer phase. Flag it and continue.
     */
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Parity error during phase %s.\n",
           p->host_no, CTL_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);
  }
}

/*+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;
  unsigned int interrupts_cleared = 0;

  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;

  /*
   * 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))
  {
    if (p->next == NULL)
    {
      p = NULL;
    }
    else
    {
      p = (struct aic7xxx_host *) p->next->hostdata;
    }
  }

  if (p == NULL)
    return;

  /*
   * Handle all the interrupt sources - especially for SCSI
   * interrupts, we won't get a second chance at them.
   */
  intstat = inb(p->base + INTSTAT);

  /*
   * Keep track of interrupts for /proc/scsi
   */
  p->isr_count++;

  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)
    {
      /* Try clearing all interrupts. */
      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
    }
    return;
  }

  if (p->flags & IN_ISR)
  {
    panic(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
           p->host_no);
    return;
  }

  /*
   * Indicate that we're in the interrupt handler.
   */
  save_flags(flags);
  p->flags |= IN_ISR;

  if (intstat & CMDCMPLT)
  {
    struct aic7xxx_scb *scb = NULL;
    Scsi_Cmnd *cmd;
    unsigned char qoutcnt;
    unsigned char scb_index;
    int i;

    /*
     * The sequencer will continue running when it
     * issues this interrupt. There may be >1 commands
     * finished, so loop until we've processed them all.
     */
    cli();
    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;

#if 1
  if (qoutcnt >= p->qfullcount - 1)
    printk(KERN_WARNING "(scsi%d:-1:-1:-1) Command complete near Qfull count, "
           "qoutcnt = %d.\n", p->host_no, qoutcnt);
#endif
    while (qoutcnt > 0)
    {
      if (p->flags & PAGE_ENABLED)
      {
        p->cmdoutcnt += qoutcnt;
        if ( p->cmdoutcnt >= p->qfullcount )
        {
          outb(0, p->base + CMDOUTCNT);
          p->cmdoutcnt = 0;
        }
      }
      for (i = 0; i < qoutcnt; i++)
      {
        scb_index = inb(p->base + QOUTFIFO);
	if ( scb_index >= p->scb_data->numscbs )
	    scb = NULL;
	else
            scb = p->scb_data->scb_array[scb_index];
        if (scb == NULL)
        {
	  printk(KERN_WARNING "(scsi%d:-1:-1:-1) 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:-1:-1:-1) 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;
        }
	switch (status_byte(scb->hscb->target_status))
	{
	  case QUEUE_FULL:
	  case BUSY:
	    scb->hscb->target_status = 0;
            scb->cmd->result = 0;
            aic7xxx_error(scb->cmd) = DID_OK;
	    break;
	  default:
            cmd = scb->cmd;
            if (scb->hscb->residual_SG_segment_count != 0)
            {
              aic7xxx_calculate_residual(p, scb);
            }
            cmd->result |= (aic7xxx_error(cmd) << 16);
            p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
            aic7xxx_done(p, scb);
	}
      }
      /*
       * Clear interrupt status before checking the output queue again.
       * This eliminates a race condition whereby a command could
       * complete between the queue poll and the interrupt clearing,
       * so notification of the command being complete never made it
       * back up to the kernel.
       */
      outb(CLRCMDINT, p->base + CLRINT);
      interrupts_cleared++;
      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
    }

    if (interrupts_cleared == 0)
    {
      outb(CLRCMDINT, p->base + CLRINT);
    }
    restore_flags(flags);
  }

  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)
      {
        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
      }
    }
    aic7xxx_reset_channel(p, 'A', TRUE);
    if ( p->bus_type == AIC_TWIN )
    {
      aic7xxx_reset_channel(p, 'B', TRUE);
      restart_sequencer(p);
      pause_sequencer(p);
    }
    outb(CLRBRKADRINT, p->base + CLRINT);
  }

  if (intstat & SEQINT)
  {
    cli();
    aic7xxx_handle_seqint(p, intstat);
    restore_flags(flags);
  }

  if (intstat & SCSIINT)
  {
    cli();
    aic7xxx_handle_scsiint(p, intstat);
    restore_flags(flags);
  }

  aic7xxx_done_cmds_complete(p);
  cli();
  aic7xxx_run_waiting_queues(p);
  unpause_sequencer(p, TRUE);
  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;
  unsigned char tindex;

  tindex = device->id | (device->channel << 3);

  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)));

#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:%d) Disconnection disabled, unable to "
             "enable tagged queueing.\n",
             p->host_no, device->channel, device->id, device->lun);
    }
    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
      {

        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
        {
          tag_enabled = FALSE;
          device->queue_depth = 2;  /* Tagged queueing is disabled. */
        }
        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
        {
          device->queue_depth = default_depth;
        }
        else
        {
          device->queue_depth =
            aic7xxx_tag_info[p->instance].tag_commands[tindex];
        }
      }
#endif
      if ((device->tagged_queue == 0) && tag_enabled)
      {
        if (aic7xxx_verbose)
        {
    	  printk(KERN_INFO "(scsi%d:%d:%d:%d) Enabled tagged queuing, "
    	         "queue depth %d.\n", p->host_no,
    	         device->channel, device->id, device->lun, device->queue_depth);
        }
        p->device_status[tindex].max_queue_depth = device->queue_depth;
        p->device_status[tindex].temp_queue_depth = device->queue_depth;
        device->tagged_queue = 1;
        device->current_tag = SCB_LIST_NULL;
      }
    }
  }
#endif
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_select_queue_depth
 *
 * Description:
 *   Sets the queue depth for each SCSI device hanging off the input
 *   host adapter.  We use a queue depth of 2 for devices that do not
 *   support tagged queueing.  If AIC7XXX_CMDS_PER_LUN is defined, we
 *   use that for tagged queueing devices; otherwise we use our own
 *   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,
    Scsi_Device *scsi_devs)
{
  Scsi_Device *device;
  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;

  for (device = scsi_devs; device != NULL; device = device->next)
  {
    if (device->host == host)
    {
      aic7xxx_device_queue_depth(p, device);
    }
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_probe
 *
 * Description:
 *   Probing for EISA boards: it looks like the first two bytes
 *   are a manufacturer code - three characters, five bits each:
 *
 *               BYTE 0   BYTE 1   BYTE 2   BYTE 3
 *              ?1111122 22233333 PPPPPPPP RRRRRRRR
 *
 *   The characters are baselined off ASCII '@', so add that value
 *   to each to get the real ASCII code for it. The next two bytes
 *   appear to be a product and revision number, probably vendor-
 *   specific. This is what is being searched for at each port,
 *   and what should probably correspond to the ID= field in the
 *   ECU's .cfg file for the card - if your card is not detected,
 *   make sure your signature is listed in the array.
 *
 *   The fourth byte's lowest bit seems to be an enabled/disabled
 *   flag (rest of the bits are reserved?).
 *-F*************************************************************************/
static aha_chip_type
aic7xxx_probe(int slot, int base, aha_status_type *bios)
{
  int i;
  unsigned char buf[4];

  static struct {
    int n;
    unsigned char signature[sizeof(buf)];
    aha_chip_type type;
    int bios_disabled;
  } AIC7xxx[] = {
    { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
    { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770, FALSE }, /* motherboard 7770  */
    { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x, FALSE }, /* 284x BIOS enabled */
    { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x, TRUE }   /* 284x BIOS disabled */
  };

  /*
   * The VL-bus cards need to be primed by
   * writing before a signature check.
   */
  for (i = 0; i < sizeof(buf); i++)
  {
    outb(0x80 + i, base);
    buf[i] = inb(base + i);
  }

  for (i = 0; i < NUMBER(AIC7xxx); i++)
  {
    /*
     * Signature match on enabled card?
     */
    if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
    {
      if (inb(base + 4) & 1)
      {
        if (AIC7xxx[i].bios_disabled)
        {
          *bios = AIC_DISABLED;
        }
        else
        {
          *bios = AIC_ENABLED;
        }
	return (AIC7xxx[i].type);
      }

      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
             "disabled at slot %d, ignored.\n", slot);
    }
  }

  return (AIC_NONE);
}

/*+F*************************************************************************
 * Function:
 *   read_2840_seeprom
 *
 * Description:
 *   Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
 *   not successful.
 *
 *   See read_seeprom (for the 2940) for the instruction set of the 93C46
 *   chip.
 *
 *   The 2840 interface to the 93C46 serial EEPROM is through the
 *   STATUS_2840 and SEECTL_2840 registers.  The CS_2840, CK_2840, and
 *   DO_2840 bits of the SEECTL_2840 register are connected to the chip
 *   select, clock, and data out lines respectively of the serial EEPROM.
 *   The DI_2840 bit of the STATUS_2840 is connected to the data in line
 *   of the serial EEPROM.  The EEPROM_TF bit of STATUS_2840 register is
 *   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_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
{
  int i = 0, k = 0;
  unsigned char temp;
  unsigned short checksum = 0;
  unsigned short *seeprom = (unsigned short *) sc;
  struct seeprom_cmd {
    unsigned char len;
    unsigned char bits[3];
  };
  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};

#define CLOCK_PULSE(p) \
  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)	\
  {						\
    ;  /* Do nothing */				\
  }						\
  (void) inb(p->base + SEECTL_2840);

  /*
   * Read the first 32 registers of the seeprom.  For the 2840,
   * 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.
   */
  for (k = 0; k < (sizeof(*sc) / 2); k++)
  {
    /*
     * Send chip select for one clock cycle.
     */
    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
    CLOCK_PULSE(p);

    /*
     * Now we're ready to send the read command followed by the
     * address of the 16-bit register we want to read.
     */
    for (i = 0; i < seeprom_read.len; i++)
    {
      temp = CS_2840 | seeprom_read.bits[i];
      outb(temp, p->base + SEECTL_2840);
      CLOCK_PULSE(p);
      temp = temp ^ CK_2840;
      outb(temp, p->base + SEECTL_2840);
      CLOCK_PULSE(p);
    }
    /*
     * Send the 6 bit address (MSB first, LSB last).
     */
    for (i = 5; i >= 0; i--)
    {
      temp = k;
      temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
      temp = CS_2840 | temp;
      outb(temp, p->base + SEECTL_2840);
      CLOCK_PULSE(p);
      temp = temp ^ CK_2840;
      outb(temp, p->base + SEECTL_2840);
      CLOCK_PULSE(p);
    }

    /*
     * Now read the 16 bit register.  An initial 0 precedes the
     * register contents which begins with bit 15 (MSB) and ends
     * with bit 0 (LSB).  The initial 0 will be shifted off the
     * top of our word as we let the loop run from 0 to 16.
     */
    for (i = 0; i <= 16; i++)
    {
      temp = CS_2840;
      outb(temp, p->base + SEECTL_2840);
      CLOCK_PULSE(p);
      temp = temp ^ CK_2840;
      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
     * 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)
    {
      checksum = checksum + seeprom[k];
    }

    /*
     * Reset the chip select for the next command cycle.
     */
    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
  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
  printk("Serial EEPROM:");
  for (k = 0; k < (sizeof(*sc) / 2); k++)
  {
    if (((k % 8) == 0) && (k != 0))
    {
      printk("\n              ");
    }
    printk(" 0x%x", seeprom[k]);
  }
  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)
  {
    outb(0, p->base + SEECTL);
    return (0);
  }
  return (1);
}

/*+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*************************************************************************
 * Function:
 *   read_seeprom
 *
 * Description:
 *   Reads the serial EEPROM and returns 1 if successful and 0 if
 *   not successful.
 *
 *   The instruction set of the 93C46/56/66 chips is as follows:
 *
 *               Start  OP
 *     Function   Bit  Code  Address    Data     Description
 *     -------------------------------------------------------------------
 *     READ        1    10   A5 - A0             Reads data stored in memory,
 *                                               starting at specified address
 *     EWEN        1    00   11XXXX              Write enable must precede
 *                                               all programming modes
 *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
 *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
 *     ERAL        1    00   10XXXX              Erase all registers
 *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
 *     EWDS        1    00   00XXXX              Disables all programming
 *                                               instructions
 *     *Note: A value of X for address is a don't care condition.
 *     *Note: The 93C56 and 93C66 have 8 address bits.
 * 
 *
 *   The 93C46 has a four wire interface: clock, chip select, data in, and
 *   data out.  In order to perform one of the above functions, you need
 *   to enable the chip select for a clock period (typically a minimum of
 *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
 *   respectively.  While the chip select remains high, you can clock in
 *   the instructions (above) starting with the start bit, followed by the
 *   OP code, Address, and Data (if needed).  For the READ instruction, the
 *   requested 16-bit register contents is read from the data out line but
 *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
 *   first).  The clock cycling from low to high initiates the next data
 *   bit to be sent from the chip.
 *
 *   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,
 *   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.
 *-F*************************************************************************/
static int
read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
    unsigned int len, seeprom_chip_type chip)
{
  int i = 0, k;
  unsigned char temp;
  unsigned short checksum = 0;
  struct seeprom_cmd {
    unsigned char len;
    unsigned char bits[3];
  };
  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};

#define CLOCK_PULSE(p) \
  while ((inb(p->base + SEECTL) & SEERDY) == 0)	\
  {						\
    ;  /* Do nothing */				\
  }

  /*
   * Request access of the memory port.
   */
  if (acquire_seeprom(p) == 0)
  {
    return (0);
  }

  /*
   * 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 < len; k++)
  {
    /*
     * Send chip select for one clock cycle.
     */
    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
    CLOCK_PULSE(p);

    /*
     * Now we're ready to send the read command followed by the
     * address of the 16-bit register we want to read.
     */
    for (i = 0; i < seeprom_read.len; i++)
    {
      temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
      outb(temp, p->base + SEECTL);
      CLOCK_PULSE(p);
      temp = temp ^ SEECK;
      outb(temp, p->base + SEECTL);
      CLOCK_PULSE(p);
    }
    /*
     * 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, p->base + SEECTL);
      CLOCK_PULSE(p);
      temp = temp ^ SEECK;
      outb(temp, p->base + SEECTL);
      CLOCK_PULSE(p);
    }

    /*
     * Now read the 16 bit register.  An initial 0 precedes the
     * register contents which begins with bit 15 (MSB) and ends
     * with bit 0 (LSB).  The initial 0 will be shifted off the
     * top of our word as we let the loop run from 0 to 16.
     */
    for (i = 0; i <= 16; i++)
    {
      temp = SEEMS | SEECS;
      outb(temp, p->base + SEECTL);
      CLOCK_PULSE(p);
      temp = temp ^ SEECK;
      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
      outb(temp, p->base + SEECTL);
      CLOCK_PULSE(p);
    }

    /*
     * 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 < (len - 1))
    {
      checksum = checksum + scarray[k];
    }

    /*
     * Reset the chip select for the next command cycle.
     */
    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.
   */
  release_seeprom(p);

#if 0
  printk("Computed checksum 0x%x, checksum read 0x%x\n",
         checksum, scarray[len - 1]);
  printk("Serial EEPROM:");
  for (k = 0; k < len; k++)
  {
    if (((k % 8) == 0) && (k != 0))
    {
      printk("\n              ");
    }
    printk(" 0x%x", scarray[k]);
  }
  printk("\n");
#endif

  if (checksum != scarray[len - 1])
  {
    return (0);
  }

  return (1);
#undef CLOCK_PULSE
}

/*+F*************************************************************************
 * Function:
 *   write_brdctl
 *
 * Description:
 *   Writes a value to the BRDCTL register.
 *-F*************************************************************************/
static inline void
write_brdctl(struct aic7xxx_host *p, unsigned char value)
{
  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:
 *   read_brdctl
 *
 * Description:
 *   Reads the BRDCTL register.
 *-F*************************************************************************/
static inline unsigned char
read_brdctl(struct aic7xxx_host *p)
{
  outb(BRDRW | BRDCS, p->base + BRDCTL);
  return (inb(p->base + BRDCTL));
}

/*+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))
  {
    if (adapter_control & CFAUTOTERM)
    {
      old_verbose = aic7xxx_verbose;
      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
                       "verify driver\n");
      printk(KERN_INFO "         detected settings and use manual termination "
                       "if necessary.\n"); 

      /* Configure auto termination. */
      outb(SEECS | SEEMS, p->base + SEECTL);

      /*
       * 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.
       */
      write_brdctl(p, 0);

      /*
       * 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).
       */
      brdctl_int = read_brdctl(p);
      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
      if (max_targ > 8)
      {
        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
      }

      /*
       * Set the rom bank to 1 and determine
       * the other signals.
       */
      write_brdctl(p, BRDDAT5);

      /*
       * 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.
       */
      brdctl_ext = read_brdctl(p);
      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
      eprom_present = brdctl_ext & BRDDAT7;
      if (aic7xxx_verbose)
      {
        if (max_targ > 8)
        {
          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
        {
          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);
      }

      /*
       * Now set the termination based on what we found.  BRDDAT6
       * controls wide termination enable.
       */
      high_on = FALSE;
      low_on = FALSE;
      if ((max_targ > 8) &&
          ((external_present == 0) || (internal68_present == 0)))
      {
        high_on = TRUE;
      }

      if ((internal50_present + internal68_present + external_present) <= 1)
      {
        low_on = TRUE;
      }
          
      if (internal50_present && internal68_present && external_present)
      {
        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
               "         Only two connectors on the adapter may be "
               "used at a time!\n");
      }

      if (high_on == TRUE)
        write_brdctl(p, BRDDAT6);
      else
        write_brdctl(p, 0);

      if (low_on == TRUE)
        *sxfrctl1 |= STPWEN;

      if (aic7xxx_verbose)
      {
        if (max_targ > 8)
        {
          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
                 low_on ? "ON" : "OFF",
                 high_on ? "ON" : "OFF");
        }
        else
        {
          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
        }
      }
      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
      {
        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);
  }
}

/*+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;

  /*
   * 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);

    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;

      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;
  }
}

/*+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;

  /*
   * 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(p->base + SBLKCTL);
  if (p->flags & PAGE_ENABLED)
    flags = PAGESCBS;

  switch (sblkctl & SELBUS_MASK)
  {
    case SELNARROW:     /* narrow/normal bus */
      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 */
      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 */
      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(p->base + SBLKCTL));
      outb(0, p->base + SEQ_FLAGS);
      return (0);
  }

  /*
   * Detect SCB parameters and initialize the SCB array.
   */
  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);

  host = p->host;

  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 = p->scsi_id;
  host->io_port = p->base;
  host->n_io_port = 0xFF;
  host->base = (unsigned char *) p->mbase;
  host->irq = p->irq;
  if (p->bus_type == AIC_WIDE)
  {
    host->max_id = 16;
  }
  if (p->bus_type == AIC_TWIN)
  {
    host->max_channel = 1;
  }

  p->host = host;
  p->last_reset = 0;
  p->host_no = host->host_no;
  p->isr_count = 0;
  p->next = NULL;
  p->completeq.head = NULL;
  p->completeq.tail = NULL;
  scbq_init(&p->scb_data->free_scbs);
  scbq_init(&p->waiting_scbs);

  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;
    p->device_status[i].last_queue_full = 0;
    p->device_status[i].last_queue_full_count = 0;
    p->device_status[i].max_queue_depth = 2;
    p->device_status[i].temp_queue_depth = 2;
    scbq_init(&p->device_status[i].delayed_scbs);
    init_timer(&p->device_status[i].timer);
    p->device_status[i].timer.expires = 0;
    p->device_status[i].timer.data = (unsigned long)p;
    p->device_status[i].timer.function = (void *)aic7xxx_timer;
  }
  if (aic7xxx_boards[p->irq] == NULL)
  {
    int result;
    int irq_flags = 0;

#ifdef AIC7XXX_OLD_ISR_TYPE
    irq_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
     * they are enabled.  So when we request the irq from the Linux
     * kernel, an interrupt is triggered immediately.  Therefore, we
     * must ensure the board data is correctly set before the request.
     */
    aic7xxx_boards[p->irq] = host;

    /*
     * Register IRQ with the kernel.  Only allow sharing IRQs with
     * PCI devices.
     */
    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)
      {
        irq_flags = (irq_flags & SA_INTERRUPT) ? 0 : SA_INTERRUPT;
        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",
             p->irq);
      aic7xxx_boards[p->irq] = NULL;
      return (0);
    }
  }
  else
  {
    /*
     * We have found a host adapter sharing an IRQ of a previously
     * 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[p->irq];
    aic7xxx_boards[p->irq] = host;
  }

  /*
   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
   */
  if (p->bus_type == AIC_TWIN)
  {
    /*
     * The controller is gated to channel B after a chip reset; set
     * bus B values first.
     */
    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 | FAST20, p->base + SXFRCTL0);
    }
    else
    {
      outb(DFON | SPIOEN, p->base + SXFRCTL0);
    }

    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(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 | FAST20, p->base + SXFRCTL0);
  }
  else
  {
    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);
  }

  /*
   * Look at the information that board initialization or the board
   * BIOS has left us. In the lower four bits of each target's
   * scratch space any value other than 0 indicates that we should
   * initiate synchronous transfers. If it's zero, the user or the
   * BIOS has decided to disable synchronous negotiation to that
   * target so we don't activate the needsdtr flag.
   */
  p->needsdtr_copy = 0x0;
  p->sdtr_pending = 0x0;
  p->needwdtr_copy = 0x0;
  p->wdtr_pending = 0x0;
  if (p->bus_type == AIC_SINGLE)
  {
    max_targets = 8;
  }
  else
  {
    max_targets = 16;
  }

  /*
   * Grab the disconnection disable table and invert it for our needs
   */
  if (p->flags & USE_DEFAULTS)
  {
    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
           "device parameters.\n");
    p->discenable = 0xFFFF;
  }
  else
  {
    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
        inb(p->base + DISC_DSB));
  }

  for (i = 0; i < max_targets; i++)
  {
    if (p->flags & USE_DEFAULTS)
    {
      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
    {
      target_settings = inb(p->base + TARG_SCRATCH + 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);
        /*
         * Clear the wide flag. When wide negotiation is successful,
         * we'll enable it.
         */
        target_settings &= 0x7F;
      }
      if (p->flags & ULTRA_ENABLED)
      {
        switch (target_settings & 0x70)
        {
          case 0x00:
          case 0x10:
          case 0x20:
            ultraenable |= (0x01 << i);
            break;
          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
            target_settings &= ~(0x70);
            break;
          default:
            break;
        }
      }
    }
    outb(target_settings, p->base + TARG_SCRATCH + i);
  }

  /*
   * If we are not wide, forget WDTR. This makes the driver
   * work on some cards that don't leave these fields cleared
   * when BIOS is not installed.
   */
  if (p->bus_type != AIC_WIDE)
  {
    p->needwdtr_copy = 0;
  }
  p->needsdtr = p->needsdtr_copy;
  p->needwdtr = p->needwdtr_copy;
  p->orderedtag = 0;
  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);

  /*
   * Set FIFO depth and command out count.  These are only used when
   * paging is enabled and should not be touched for AIC-7770 based
   * adapters; FIFODEPTH and CMDOUTCNT overlay SCSICONF and SCSICONF+1
   * which are used to control termination.
   */
  if (p->flags & PAGE_ENABLED)
  {
    outb(p->qfullcount, p->base + FIFODEPTH);
    outb(0, p->base + CMDOUTCNT);
    p->cmdoutcnt = 0;
  }

  /*
   * 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);
  }
  /*
   * Free the instance of the device structure.
   */
  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_7855:
      break;

    case AIC_284x:
      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
      break;

    case AIC_7850:  /* The 2910B is a 7850 with a seeprom. */
    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;

    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;

    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;

    default:
      break;
  }

  if (!have_seeprom)
  {
    if (aic7xxx_verbose)
    {
      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
    }
    p->flags |= USE_DEFAULTS;
  }
  else
  {
    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;
      /*
       * 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.
       */
      max_targets = 16;
    }
    else
    {
      /* PCI adapter seeproms */
      if (sc->bios_control & CFEXTEND)
        p->flags |= EXTENDED_TRANSLATION;

      if (sc->adapter_control & CFSTERM)
        *sxfrctl1 |= STPWEN;

      /* Limit to 16 targets just in case. */
      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
    }

    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);

    p->scsi_id = sc->brtime_id & CFSCSIID;

    scsi_conf = (p->scsi_id & 0x7);
    if (sc->adapter_control & CFSPARITY)
      scsi_conf |= ENSPCHK;
    /*
     * The 7850 controllers with a seeprom, do not honor the CFRESETB
     * flag in the seeprom.  Assume that we want to reset the SCSI bus.
     */
    if ((sc->adapter_control & CFRESETB) || (p->chip_class == AIC_7850))
      scsi_conf |= RESET_SCSI;

    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;
      }
    }

    /* 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);

    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*************************************************************************
 * Function:
 *   aic7xxx_detect
 *
 * 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;
  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 *p;

  /*
   * Since we may allow sharing of IRQs, it is imperative
   * that we "null-out" the aic7xxx_boards array. It is
   * not guaranteed to be initialized to 0 (NULL). We use
   * a NULL entry to indicate that no prior hosts have
   * been found/registered for that IRQ.
   */
  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
  {
    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.
   */
  aic7xxx_spurious_count = 0;

  /*
   * EISA/VL-bus card signature probe.
   */
  for (slot = MINSLOT; slot <= MAXSLOT; slot++)
  {
    base = SLOTBASE(slot) + MINREG;

    if (check_region(base, MAXREG - MINREG))
    {
      /*
       * Some other driver has staked a
       * claim to this i/o region already.
       */
      continue;
    }

    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;

      /*
       * Pause the card preserving the IRQ type.  Allow the operator
       * to override the IRQ trigger.
       */
      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);
      p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
      if (p == NULL)
      {
        printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
        continue;
      }
      aic7xxx_chip_reset(p);

      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 %d, ignoring.\n", irq);
          irq = 0;
          aic7xxx_free(p);
          break;
      }

      if (irq != 0)
      {
        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;
        }

        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.
       */
      aic7xxx_spurious_count = 0;
    }
  }

#ifdef CONFIG_PCI
  /*
   * PCI-bus probe.
   */
  if (pcibios_present())
  {
    struct
    {
      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_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},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AIC_7873, AIC_787x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AIC_7874, AIC_787x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AIC_7880, AIC_788x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AIC_7881, AIC_788x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AIC_7882, AIC_788x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AIC_7883, AIC_788x},
      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
    };

    int error, flags;
    int done = 0;
    unsigned int iobase, mbase;
    unsigned short index = 0;
    unsigned char pci_bus, pci_device_fn;
    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++)
    {
      done = FALSE;
      while (!done)
      {
        if (pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
                                aic7xxx_pci_devices[i].device_id,
                                index, &pci_bus, &pci_device_fn))
        {
          index = 0;
          done = TRUE;
        }
        else  /* Found an Adaptec PCI device. */
        {
          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_7855:
              flags |= USE_DEFAULTS;
              break;

            case AIC_7872:  /* 3940 */
            case AIC_7882:  /* 3940-Ultra */
              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 */
              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;

            default:
              break;
          }

          /*
           * Read sundry information from PCI BIOS.
           */
          error = pcibios_read_config_dword(pci_bus, pci_device_fn,
                                            PCI_BASE_ADDRESS_0, &iobase);
          error += pcibios_read_config_byte(pci_bus, pci_device_fn,
                                            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 (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;

          /*
           * Remember how the card was setup in case there is no seeprom.
           */
          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;

          aic7xxx_chip_reset(p);

#ifdef AIC7XXX_USE_EXT_SCBRAM
          if (devconfig & RAMPSM)
          {
            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

          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)
          {
            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;
            }
          }

          /*
           * 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
           * spurious interrupts for the 787x/785x, but what
           * the hey.
           */
          aic7xxx_spurious_count = 1;

          if (aic7xxx_extended)
            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
            /*
             * Set the shared SCB data once we've successfully probed a
             * 398x adapter.
             *
             * Note that we can only do this if the use of external
             * SCB RAM is enabled.
             */
            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
            {
              if (shared_scb_data == NULL)
              {
                shared_scb_data = p->scb_data;
              }
            }
#endif
          }

          index++;
          /*
           * Disable spurious interrupts.
           */
          aic7xxx_spurious_count = 0;
        }  /* Found an Adaptec PCI device. */
      }
    }
  }
#endif CONFIG_PCI
  return (found);
}

static void
aic7xxx_fake_scsi_done(Scsi_Cmnd *cmd)
{
  memset(&cmd->sense_buffer[0], '\0', sizeof(cmd->sense_buffer));
}

static void
aic7xxx_make_fake_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *cmd, int which)
{
  Scsi_Cmnd *cmd2;

  if (which == 0)
    p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 = cmd2 =
	kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
  else
    p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 = cmd2 =
	kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
  if (cmd2 != NULL)
  {
    memcpy(cmd2, cmd, sizeof(Scsi_Cmnd));
    memset(&cmd2->cmnd[0], '\0', sizeof(cmd2->cmnd));
    cmd2->cmnd[0] = TEST_UNIT_READY;
    cmd2->cmd_len = 6;
    cmd2->bufflen = 0;
    cmd2->request_bufflen = 0;
    cmd2->buffer = NULL;
    cmd2->request_buffer = NULL;
    cmd2->use_sg = 0;
    cmd2->underflow = 0;
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_buildscb
 *
 * Description:
 *   Build a SCB.
 *-F*************************************************************************/
static void
aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
    struct aic7xxx_scb *scb)
{
  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.
   */
  if (p->discenable & mask)
  {
    hscb->control |= DISCENB;
#ifdef AIC7XXX_TAGGED_QUEUEING
    if (cmd->device->tagged_queue)
    {
      cmd->tag = hscb->tag;
      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 200)
      {
        hscb->control |= MSG_SIMPLE_Q_TAG;
      }
      else
      {
        hscb->control |= MSG_ORDERED_Q_TAG;
        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
      }
    }
#endif  /* Tagged queueing */
  }
  if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
  {
    if ( cmd->cmnd[0] == TEST_UNIT_READY )
    {
      p->wdtr_pending |= mask;
      hscb->control |= MK_MESSAGE;
      scb->flags |= SCB_MSGOUT_WDTR;
    }
    else
    {
      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
        aic7xxx_make_fake_cmnd(p, cmd, 0);
      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
        aic7xxx_make_fake_cmnd(p, cmd, 1);
      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 != NULL )
        aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0,
          aic7xxx_fake_scsi_done);
      if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
        aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
          aic7xxx_fake_scsi_done);
    }
#if 0
    printk("scsi%d: Sending WDTR request to target %d.\n",
           p->host_no, cmd->target);
#endif
  }
  else
  {
    if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
    {
      if ( cmd->cmnd[0] == TEST_UNIT_READY )
      {
        p->sdtr_pending |= mask;
        hscb->control |= MK_MESSAGE;
        scb->flags |= SCB_MSGOUT_SDTR;
      }
      else
      {
        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
          aic7xxx_make_fake_cmnd(p, cmd, 0);
        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
          aic7xxx_make_fake_cmnd(p, cmd, 1);
        if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
          aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
            aic7xxx_fake_scsi_done);
      }
#if 0
      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
  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);

  /*
   * The interpretation of request_buffer and request_bufflen
   * changes depending on whether or not use_sg is zero; a
   * non-zero use_sg indicates the number of elements in the
   * scatter-gather array.
   */

  /*
   * XXX - this relies on the host data being stored in a
   *       little-endian format.
   */
  hscb->SCSI_cmd_length = cmd->cmd_len;
  hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);

  if (cmd->use_sg)
  {
    struct scatterlist *sg;  /* Must be mid-level SCSI code scatterlist */

    /*
     * We must build an SG list in adapter format, as the kernel's SG list
     * cannot be used directly because of data field size (__alpha__)
     * differences and the kernel SG list uses virtual addresses where
     * we need physical addresses.
     */
    int i;

    sg = (struct scatterlist *)cmd->request_buffer;
    for (i = 0; i < cmd->use_sg; i++)
    {
      scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
      scb->sg_list[i].length = (unsigned int) sg[i].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), hscb->data_count);
#endif
  }
  else
  {
#if 0
  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)
    {
      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
    {
      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;
    }
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_queue
 *
 * Description:
 *   Queue a SCB to the controller.
 *-F*************************************************************************/
int
aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
{
  long processor_flags;
  struct aic7xxx_host *p;
  struct aic7xxx_scb *scb;
  int tindex = TARGET_INDEX(cmd);

  p = (struct aic7xxx_host *) cmd->host->hostdata;

  /*
   * Check to see if channel was scanned.
   */
  if (!(p->flags & A_SCANNED) && (cmd->channel == 0))
  {
    printk(KERN_INFO "scsi%d: Scanning channel A for devices.\n", p->host_no);
    p->flags |= A_SCANNED;
  }
  else
  {
    if (!(p->flags & B_SCANNED) && (cmd->channel == 1))
    {
      printk(KERN_INFO "scsi%d: Scanning channel B for devices.\n", p->host_no);
      p->flags |= B_SCANNED;
    }
  }

#if 0
  printk("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
	cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
	cmd->lun & 0x07);
#endif

  if ( (p->device_status[tindex].active_cmds > cmd->device->queue_depth) &&
      !(p->wdtr_pending & (0x1 << tindex)) && 
      !(p->sdtr_pending & (0x1 << tindex)) )
  {
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Commands queued exceeds queue "
	   "depth, active=%d\n",
           p->host_no, CTL_OF_CMD(cmd), 
	   p->device_status[tindex].active_cmds);
  }
  scb = aic7xxx_allocate_scb(p);
  if (scb == NULL)
  {
    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
  }
  else
  {
    scb->cmd = cmd;
    aic7xxx_position(cmd) = scb->hscb->tag;
#if 0
    debug_scb(scb);
#endif;

    /*
     * Construct the SCB beforehand, so the sequencer is
     * paused a minimal amount of time.
     */
    aic7xxx_buildscb(p, cmd, scb);

#if 0
    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->hscb->tag, (unsigned int) scb->cmd,
	   scb->flags, (unsigned int) p->free_scb);
#endif

    /*
     * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
     * is set up properly, and the parity error flag is reset, then send
     * the SCB to the sequencer and watch the fun begin.
     */
    cmd->scsi_done = fn;
    aic7xxx_error(cmd) = DID_OK;
    aic7xxx_status(cmd) = 0;
    cmd->result = 0;
    cmd->host_scribble = NULL;
    memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));

    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;

    save_flags(processor_flags);
    cli();
    if (p->device_status[tindex].delayed_scbs.head != NULL) 
    {
        scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
    }
    else
    {
        scbq_insert_tail(&p->waiting_scbs, scb);
    }
    if ((p->flags & (IN_ISR | IN_ABORT | RESET_PENDING)) == 0)
    {
      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->hscb->tag);
#endif;
  }
  return (0);
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_bus_device_reset
 *
 * Description:
 *   Abort or reset the current SCSI command(s).  If the scb has not
 *   previously been aborted, then we attempt to send a BUS_DEVICE_RESET
 *   message to the target.  If the scb has previously been unsuccessfully
 *   aborted, then we will reset the channel and have all devices renegotiate.
 *   Returns an enumerated type that indicates the status of the operation.
 *-F*************************************************************************/
static int
aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
  struct aic7xxx_scb   *scb;
  struct aic7xxx_hwscb *hscb;
  int result = -1;
  char channel;
  unsigned char saved_scbptr, lastphase;
  unsigned char hscb_index, linked_next;
  int disconnected;

  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
  hscb = scb->hscb;

  lastphase = inb(p->base + LASTPHASE);
  if (aic7xxx_verbose > 1)
  {
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus Device reset, scb flags 0x%x, ",
         p->host_no, CTL_OF_SCB(scb), scb->flags);
    switch (lastphase)
    {
      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, ", lastphase);
        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));
  }

  channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';

    /*
     * Send a Device Reset 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);
  disconnected = FALSE;

  if (lastphase != P_BUSFREE)
  {
    if (inb(p->base + SCB_TAG) >= p->scb_data->numscbs)
    {
        /*
         * Perform a bus reset.
         *
         * XXX - We want to queue an abort for the timedout SCB
         *       instead.
         */
      printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
             "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
      return(SCSI_RESET_ERROR);
    }
    if (scb->hscb->tag == inb(p->base + SCB_TAG))
    { 
      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
      {
        /* Send the abort message to the active SCB. */
        outb(1, p->base + MSG_LEN);
        outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
        outb(lastphase | ATNO, p->base + SCSISIGO);
	if (aic7xxx_verbose > 1)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset message in "
		"message buffer\n", p->host_no, CTL_OF_SCB(scb));
        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
        aic7xxx_error(scb->cmd) = DID_RESET;
	p->device_status[TARGET_INDEX(scb->cmd)].flags &= 
		~DEVICE_SUCCESS;
	p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
		BUS_DEVICE_RESET_PENDING;
        return(SCSI_RESET_PENDING);
      }
      else
      {
	/* We want to send out the message, but it could screw an already */
	/* in place and being used message.  Instead, we return an error  */
	/* to try and start the bus reset phase since this command is     */
        /* probably hung (aborts failed, and now reset is failing).  We   */
        /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
        /* any more on this device, but instead will escalate to a bus or */
        /* host reset (additionally, we won't try to abort any more).     */
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset, Message buffer "
		"in use\n", p->host_no, CTL_OF_SCB(scb));
	scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
        aic7xxx_error(scb->cmd) = DID_RESET;
	p->device_status[TARGET_INDEX(scb->cmd)].flags &= 
		~DEVICE_SUCCESS;
	p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
		BUS_DEVICE_RESET_PENDING;
        return(SCSI_RESET_ERROR);
      }
    }
  }
  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_RESET | SCB_DEVICE_RESET | SCB_QUEUED_ABORT;
    p->device_status[TARGET_INDEX(scb->cmd)].flags &= ~DEVICE_SUCCESS;
    p->device_status[TARGET_INDEX(scb->cmd)].flags |= 
	BUS_DEVICE_RESET_PENDING;
    if (hscb_index != SCB_LIST_NULL)
    {
      unsigned char scb_control;

      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)
    {
      aic7xxx_search_qinfifo(p, cmd->target, channel, cmd->lun, 
	     SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
    }
    if (aic7xxx_verbose > 1)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queueing device reset command.\n",
           p->host_no, CTL_OF_SCB(scb));
    if ( !(scb->flags & SCB_WAITINGQ) ) /* Make sure we don't already have   */
    {					/* an abort scb queued, or else we   */
					/* corrupt the waiting queue and     */
					/* active_cmds counter by queueing   */
					/* again.			     */
      scbq_insert_head(&p->waiting_scbs, scb);
      scb->flags |= SCB_WAITINGQ;
      p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
    }
    else
    {
      scb->flags &= ~SCB_ABORT;
    }
    result = SCSI_RESET_PENDING;
  }
  else if (result == -1)
  {
    result = SCSI_RESET_ERROR;
  }
  outb(saved_scbptr, p->base + SCBPTR);
  return (result);
}


/*+F*************************************************************************
 * Function:
 *   aic7xxx_abort
 *
 * Description:
 *   Abort the current SCSI command(s).
 *-F*************************************************************************/
int
aic7xxx_abort(Scsi_Cmnd *cmd)
{
  struct aic7xxx_scb  *scb = NULL;
  struct aic7xxx_host *p;
  int    result, found=0;
  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
  unsigned long processor_flags;
  Scsi_Cmnd *cmd_next, *cmd_prev;

  p = (struct aic7xxx_host *) cmd->host->hostdata;
  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);

  save_flags(processor_flags);
  pause_sequencer(p);
  cli();

/*
 *  Run the isr to grab any command in the QOUTFIFO and any other misc.
 *  assundry tasks.  This should also set up the bh handler if there is
 *  anything to be done, but it won't run until we are done here since
 *  we are following a straight code path without entering the scheduler
 *  code.
 */

  while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
  {
    aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL);
    pause_sequencer(p);
  }

  if (scb == NULL)    /*  Totally bogus cmd since it points beyond our  */
  {                   /*  valid SCB range.  The suspect scb hasn't been */
                      /*  allocated yet.                                */
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called with bogus Scsi_Cmnd->"
	"SCB mapping.\n", p->host_no, CTL_OF_CMD(cmd));
    unpause_sequencer(p, TRUE);
    restore_flags(processor_flags);
    return(SCSI_ABORT_NOT_RUNNING);
  }
  if (scb->cmd != cmd)  /*  Hmmm...either this SCB is currently free with a */
  {                     /*  NULL cmd pointer (NULLed out when freed) or it  */
                        /*  has already been recycled for another command   */
                        /*  Either way, this SCB has nothing to do with this*/
                        /*  command and we need to deal with cmd without    */
                        /*  touching the SCB.                               */
                        /*  The theory here is to return a value that will  */
                        /*  make the queued for complete command actually   */
                        /*  finish successfully, or to indicate that we     */
                        /*  don't have this cmd any more and the mid level  */
                        /*  code needs to find it.                          */
    cmd_next = p->completeq.head;
    cmd_prev = NULL;
    while (cmd_next != NULL) 
    {
      if (cmd_next == cmd) 
      {
	if (aic7xxx_verbose > 1)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for command "
	  "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
	if ( cmd_prev == NULL )
	  p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
	else
	  cmd_prev->host_scribble = cmd_next->host_scribble;
	cmd_next->done(cmd_next);
        unpause_sequencer(p, TRUE);
	restore_flags(processor_flags);
	return(SCSI_ABORT_SUCCESS);
      }				  
      cmd_prev = cmd_next;
      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
    }
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for already completed"
	" command.\n", p->host_no, CTL_OF_CMD(cmd));
    unpause_sequencer(p, TRUE);
    restore_flags(processor_flags);
    return(SCSI_ABORT_NOT_RUNNING);
  }
    
/*   At this point we know the following:
 *     the SCB pointer is valid
 *     the command pointer passed in to us and the scb->cmd pointer match
 *     this then means that the command we need to abort is the same as the
 *     command held by the scb pointer and is a valid abort request.
 *   Now, we just have to figure out what to do from here.  Current plan is:
 *     if we have already been here on this command, escalate to a reset
 *     if scb is on waiting list or QINFIFO, send it back as aborted
 *     if scb is on WAITING_SCB list in sequencer, free scb and send back
 *     if scb is disconnected and not completed, abort with abort message
 *     if scb is currently running, then it may be causing the bus to hang
 *       so we want a return value that indicates a reset would be appropriate
 *       if the command does not finish shortly
 *     if scb is already complete but not on completeq, we're screwed because
 *       this can't happen (except if the command is in the QOUTFIFO, in which
 *       case we would like it to complete successfully instead of having to
 *       to be re-done)
 *   All other scenarios already dealt with by previous code.
 */

  if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
  {
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB aborted once already, "
	"escalating.\n", p->host_no, CTL_OF_SCB(scb));
    unpause_sequencer(p, TRUE);
    restore_flags(processor_flags);
    return(SCSI_ABORT_SNOOZE);
  }
  if ( (p->flags & (RESET_PENDING | ABORT_PENDING)) || 
          (p->device_status[TARGET_INDEX(scb->cmd)].flags & 
           BUS_DEVICE_RESET_PENDING) )
  {
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset/Abort pending for this "
	"device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
    unpause_sequencer(p, TRUE);
    restore_flags(processor_flags);
    return(SCSI_ABORT_PENDING);
  }

  found = 0;
  p->flags |= IN_ABORT;
  if (aic7xxx_verbose)
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Aborting scb %d, flags 0x%x\n",
         p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);

/*
 *   First, let's check to see if the currently running command is our target
 *    since if it is, the return is fairly easy and quick since we don't want
 *    to touch the command in case it might complete, but we do want a timeout
 *    in case it's actually hung, so we really do nothing, but tell the mid
 *    level code to reset the timeout.
 */

  if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
  {
   /*
    *  Check to see if the sequencer is just sitting on this command, or
    *   if it's actively being run.
    */
    result = inb(p->base + LASTPHASE);
    switch (result)
    {
      case P_DATAOUT:    /*    For any of these cases, we can assume we are */
      case P_DATAIN:     /*    an active command and act according.  For    */
      case P_COMMAND:    /*    anything else we are going to fall on through*/
      case P_STATUS:     /*    The SCSI_ABORT_SNOOZE will give us two abort */
      case P_MESGOUT:    /*    chances to finish and then escalate to a     */
      case P_MESGIN:     /*    reset call                                   */
	if (aic7xxx_verbose > 1)
         printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB is currently active.  "
		"Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
        unpause_sequencer(p, TRUE);
	p->flags &= ~IN_ABORT;
	scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
	p->flags |= ABORT_PENDING;      /*  here so we will know not to    */
        restore_flags(processor_flags); /*  muck with other SCBs if this   */
        return(SCSI_ABORT_PENDING);      /*  one doesn't complete and clear */
        break;                          /*  out.                           */
      default:
        break;
    }
  }

  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
  {
      int tindex = TARGET_INDEX(cmd);
     
      if (aic7xxx_verbose > 1) 
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on waiting list and "
	    "aborted.\n", p->host_no, CTL_OF_SCB(scb));
      scbq_remove(&p->waiting_scbs, scb);
      scbq_remove(&p->device_status[tindex].delayed_scbs, scb);
      p->device_status[tindex].active_cmds++;
      scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
      scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
      found = 1;
  }

/*
 *  We just checked the waiting_q, now for the QINFIFO
 */
  if ( found == 0 )
  {
    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
                     INT_TO_CHAN(cmd->channel),
	             cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
	             FALSE, NULL)) != 0) && (aic7xxx_verbose > 1))
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found in QINFIFO and "
        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
  }

/*
 *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
 */

  if ( found == 0 )
  {
    unsigned char scb_next_ptr;
    prev_hscbptr = SCB_LIST_NULL;
    saved_hscbptr = inb(p->base + SCBPTR);
    next_hscbptr = inb(p->base + WAITING_SCBH);
    while ( next_hscbptr != SCB_LIST_NULL )
    {
      outb( next_hscbptr, p->base + SCBPTR );
      if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
      {
        found = 1;
	if (aic7xxx_verbose > 1)
          printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on hardware waiting"
	    " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
	if ( prev_hscbptr == SCB_LIST_NULL )
	    outb(inb(p->base + SCB_NEXT), p->base + WAITING_SCBH);
	else
	{
	    scb_next_ptr = inb(p->base + SCB_NEXT);
	    outb(prev_hscbptr, p->base + SCBPTR);
	    outb(scb_next_ptr, p->base + SCB_NEXT);
	    outb(next_hscbptr, p->base + SCBPTR);
	}
	outb(SCB_LIST_NULL, p->base + SCB_TAG);
	aic7xxx_add_curscb_to_free_list(p);
	scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
        break;
      }
      prev_hscbptr = next_hscbptr;
      next_hscbptr = inb(p->base + SCB_NEXT);
    }
    outb( saved_hscbptr, p->base + SCBPTR );
  }
        
/*
 *  Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
 *  OK...the sequencer's paused, interrupts are off, and we haven't found the
 *  command anyplace where it could be easily aborted.  Time for the hard
 *  work.  We also know the command is valid.  This essentially means the
 *  command is disconnected, or connected but not into any phases yet, which
 *  we know due to the tests we ran earlier on the current active scb phase.
 *  At this point we can queue the abort tag and go on with life.
 */

  if ( found == 0 )
  {
    p->flags |= ABORT_PENDING;
    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
    scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
    result=aic7xxx_find_scb(p, scb);
    if ( result != SCB_LIST_NULL ) 
    {
      saved_hscbptr = inb(p->base + SCBPTR);
      outb(result, p->base + SCBPTR);
      tmp_char = inb(p->base + SCB_CONTROL);
      outb( tmp_char | MK_MESSAGE | ABORT_SCB, p->base + SCB_CONTROL);
      outb(saved_hscbptr, p->base + SCBPTR);
    }
    if (aic7xxx_verbose > 1)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB disconnected.  Queueing Abort"
        " SCB.\n", p->host_no, CTL_OF_SCB(scb));
    if ( (scb->hscb->control & TAG_ENB) == 0 )
    {
      aic7xxx_search_qinfifo(p, cmd->target, INT_TO_CHAN(cmd->channel),
	cmd->lun, SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
    }
    if ( !(scb->flags & SCB_WAITINGQ) )
    {
      scbq_insert_head(&p->waiting_scbs, scb);
      scb->flags |= SCB_WAITINGQ;
      p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
    }
  }
  else
  { 
    scb->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
  }
  aic7xxx_run_done_queue(p, TRUE);
  aic7xxx_run_waiting_queues(p);
  p->flags &= ~IN_ABORT;
  restore_flags(processor_flags);

/*
 *  On the return value.  If we found the command and aborted it, then we know
 *  it's already sent back and there is no reason for a further timeout, so
 *  we use SCSI_ABORT_SUCCESS.  On the queued abort side, we aren't so certain
 *  there hasn't been a bus hang or something that might keep the abort from
 *  from completing.  Therefore, we use SCSI_ABORT_PENDING.  The first time this
 *  is passed back, the timeout on the command gets extended, the second time
 *  we pass this back, the mid level SCSI code calls our reset function, which
 *  would shake loose a hung bus.
 */
  if ( found != 0 )
    return(SCSI_ABORT_SUCCESS);
  else
    return(SCSI_ABORT_PENDING); 
}


/*+F*************************************************************************
 * Function:
 *   aic7xxx_reset
 *
 * Description:
 *   Resetting the bus always succeeds - is has to, otherwise the
 *   kernel will panic! Try a surgical technique - sending a BUS
 *   DEVICE RESET message - on the offending target before pulling
 *   the SCSI bus reset line.
 *-F*************************************************************************/
int
aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
{
  struct aic7xxx_scb *scb = NULL;
  struct aic7xxx_host *p;
  int    tindex;
  int    result = -1;
  char   channel = 'A';
  unsigned long processor_flags;
#define	DEVICE_RESET 0x01
#define BUS_RESET    0x02
#define HOST_RESET   0x04
#define FAIL         0x08
#define	RESET_DELAY  0x10
  int	action;
  Scsi_Cmnd *cmd_prev, *cmd_next;


  if ( cmd == NULL )
  {
    if (aic7xxx_verbose > 1)
      printk(KERN_WARNING "(aic7xxx) Reset called with NULL Scsi_Cmnd "
	"pointer, failing.\n");
    return(SCSI_RESET_SNOOZE);
  }

  p = (struct aic7xxx_host *) cmd->host->hostdata;
  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
  channel = INT_TO_CHAN(cmd->channel);
  tindex = TARGET_INDEX(cmd);

  save_flags(processor_flags);
  pause_sequencer(p);
  cli();
  while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
  {
    aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL );
    pause_sequencer(p);
  }

  if (scb == NULL)
  {
    if (aic7xxx_verbose)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with bogus Scsi_Cmnd"
	   "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
    {
      action = HOST_RESET;
    }
    else
    {
      action = BUS_RESET;
    }
  }
  else if (scb->cmd != cmd) 
  {
    if (aic7xxx_verbose > 1)
    printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with recycled SCB "
	"for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
    cmd_prev = NULL;
    cmd_next = p->completeq.head;
    while ( cmd_next != NULL )
    {
      if (cmd_next == cmd)
      {
	if (aic7xxx_verbose > 1)
	  printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, found cmd on completeq"
	  ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
	if ( cmd_prev == NULL )
	  p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
	else
	  cmd_prev->host_scribble = cmd_next->host_scribble;
	cmd_next->done(cmd_next);
	unpause_sequencer(p, TRUE);
	restore_flags(processor_flags);
	return(SCSI_RESET_SUCCESS);
      }
      cmd_prev = cmd_next;
      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
    }
    if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
    {
      if (aic7xxx_verbose)
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, cmd not found,"
	  " failing.\n", p->host_no, CTL_OF_CMD(cmd));
      unpause_sequencer(p, TRUE);
      restore_flags(processor_flags);
      return(SCSI_RESET_NOT_RUNNING);
    }
    else
    {
      if (aic7xxx_verbose)
        printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, no scb, "
          "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
      scb = NULL;
      action = HOST_RESET;
    }
  }
  else
  {
    if (aic7xxx_verbose)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, scb %d, flags "
        "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
    {
      action = HOST_RESET;
    }
    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
    {
      action = BUS_RESET;
    }
    else 
    {
      action = DEVICE_RESET;
    }
  }
  if ( ((jiffies - p->last_reset) < (HZ * AIC7XXX_RESET_DELAY)) &&
    (action & (HOST_RESET | BUS_RESET | DEVICE_RESET)) )
  {
    if (aic7xxx_verbose > 1)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after "
	"last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
    action = RESET_DELAY;
  }
  if ( ((jiffies - p->device_status[tindex].last_reset) < 
	(HZ * AIC7XXX_RESET_DELAY)) && !(action & (HOST_RESET | BUS_RESET)))
  {
    if (aic7xxx_verbose > 1)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after last "
	"reset without requesting\n"
	"(scsi%d:%d:%d:%d) bus or host reset, escalating.\n", p->host_no,
	CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
    action = BUS_RESET;
  }
  if ( (action & DEVICE_RESET) && 
	(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING) )
  {
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset already sent to "
	"device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
    action = BUS_RESET;
  }
  if ( (action & DEVICE_RESET) &&
       (scb->flags & SCB_QUEUED_ABORT) )
  {
    if (aic7xxx_verbose > 2)
      printk(KERN_WARNING "(scsi%d:%d:%d:%d) Have already attempted to reach "
	"device with queued\n(scsi%d:%d:%d:%d) message, will escalate to bus "
	"reset.\n", p->host_no, CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
    action = BUS_RESET;
  }
  if ( (action & DEVICE_RESET) && (p->flags & (RESET_PENDING | ABORT_PENDING)) )
  {
    if (aic7xxx_verbose > 2)
     printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset stupid when "
	"other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
    action = BUS_RESET;
  }
  if ( (action & BUS_RESET) && (p->bus_type != AIC_TWIN) )
  {
    action = HOST_RESET;
  }
  if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & RESET_PENDING)
	&& ((jiffies - p->reset_start) > (2 * HZ * AIC7XXX_RESET_DELAY)) )
  {
    printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!!  Card must have left to go "
	"back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
    restore_flags(processor_flags);
    return(SCSI_RESET_SNOOZE);
  }
/*
 *  By this point, we want to already know what we are going to do and
 *  only have the following code implement our course of action.
 */
  switch (action)
  {
    case RESET_DELAY:
      return(SCSI_RESET_PENDING);
      break;
    case FAIL:
      return(SCSI_RESET_ERROR);
      break;
    case DEVICE_RESET:
      p->flags |= RESET_PENDING;
      result = aic7xxx_bus_device_reset(p, cmd);
      aic7xxx_run_done_queue(p, TRUE);
      aic7xxx_run_waiting_queues(p);
      p->flags &= ~RESET_PENDING;
      restore_flags(processor_flags);
      return(result);
      break;
    case BUS_RESET:
    case HOST_RESET:
    default:
      p->reset_start = jiffies;
      p->flags |= RESET_PENDING;
      aic7xxx_reset_channel(p, channel, TRUE);
      if ( (p->bus_type == AIC_TWIN) && (action & HOST_RESET) )
      {
        aic7xxx_reset_channel(p, (channel == 'A') ? 'B' : 'A', TRUE);
        restart_sequencer(p);
	pause_sequencer(p);
      }
      if (scb == NULL)
      {
	cmd->result = DID_RESET << 16;
        cmd->done(cmd);
      }
      p->last_reset = jiffies;
      if (action != HOST_RESET)
        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
      else
      {
        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
        while (inb(p->base + QOUTCNT)) inb(p->base + QOUTFIFO);
        if (p->flags & PAGE_ENABLED) outb(0, p->base + CMDOUTCNT);
        aic7xxx_clear_intstat(p);
      }
      p->flags &= ~RESET_PENDING;
      aic7xxx_run_waiting_queues(p);
      restore_flags(processor_flags);
      return(result);
      break;
  }
}

/*+F*************************************************************************
 * Function:
 *   aic7xxx_biosparam
 *
 * Description:
 *   Return the disk geometry for the given SCSI device.
 *-F*************************************************************************/
int
aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
{
  int heads, sectors, cylinders;
  struct aic7xxx_host *p;

  p = (struct aic7xxx_host *) disk->device->host->hostdata;

  /*
   * XXX - if I could portably find the card's configuration
   *       information, then this could be autodetected instead
   *       of left to a boot-time switch.
   */
  heads = 64;
  sectors = 32;
  cylinders = disk->capacity / (heads * sectors);

  if ((p->flags & EXTENDED_TRANSLATION) && (cylinders > 1024))
  {
    heads = 255;
    sectors = 63;
    cylinders = disk->capacity / (heads * sectors);
  }

  geom[0] = heads;
  geom[1] = sectors;
  geom[2] = cylinders;

  return (0);
}

#include "aic7xxx_proc.c"

#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
Scsi_Host_Template driver_template = AIC7XXX;

#include "scsi_module.c"
#endif

/*
 * Overrides for Emacs so that we almost follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-indent-level: 2
 * c-brace-imaginary-offset: 0
 * c-brace-offset: -2
 * c-argdecl-indent: 2
 * c-label-offset: -2
 * c-continued-statement-offset: 2
 * c-continued-brace-offset: 0
 * indent-tabs-mode: nil
 * tab-width: 8
 * End:
 */
[ RETURN TO DIRECTORY ]