bk://linux-scsi.bkbits.net/scsi-misc-2.6
andmike@us.ibm.com|ChangeSet|20040426030557|42926 andmike

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/04/25 22:05:57-05:00 andmike@us.ibm.com 
#   [PATCH] fix module unload problem in sd
#   
#   Move scsi_device_get out of sd probe path to allow module to be unloaded
#   when devices are not open.
# 
# drivers/scsi/sd.c
#   2004/04/22 00:20:13-05:00 andmike@us.ibm.com +15 -10
#   fix module unload problem in sd
# 
# ChangeSet
#   2004/04/25 09:23:51-05:00 aradford@amcc.com 
#   [PATCH] 3ware driver update
#   
#   This patch includes the following driver changes:
#   
#      1.26.00.038 - Roll driver minor version to 26 to denote kernel 2.6.
#                    Add support for cmds_per_lun module parameter.
#      1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code.
#                    Fix data_buffer_length usage in tw_chrdev_ioctl().
#                    Update contact information.
# 
# drivers/scsi/3w-xxxx.h
#   2004/04/21 17:27:30-05:00 aradford@amcc.com +4 -4
#   3ware driver update
# 
# drivers/scsi/3w-xxxx.c
#   2004/04/21 17:27:29-05:00 aradford@amcc.com +26 -22
#   3ware driver update
# 
# ChangeSet
#   2004/04/25 09:20:56-05:00 garloff@suse.de 
#   [PATCH] scsi: don't attach device if PQ indicates not connected
# 
# include/scsi/scsi_device.h
#   2004/04/21 11:55:21-05:00 garloff@suse.de +1 -0
#   don't attach device if PQ indicates not connected
# 
# include/scsi/scsi.h
#   2004/04/21 11:54:43-05:00 garloff@suse.de +7 -0
#   don't attach device if PQ indicates not connected
# 
# drivers/scsi/scsi_sysfs.c
#   2004/04/21 11:56:33-05:00 garloff@suse.de +2 -1
#   don't attach device if PQ indicates not connected
# 
# drivers/scsi/scsi_scan.c
#   2004/04/21 11:56:02-05:00 garloff@suse.de +5 -10
#   don't attach device if PQ indicates not connected
# 
# ChangeSet
#   2004/04/25 09:13:40-05:00 chrisw@osdl.org 
#   [PATCH] Update aacraid MAINTAINERS entry
# 
# MAINTAINERS
#   2004/04/19 07:59:12-05:00 chrisw@osdl.org +2 -4
#   Update aacraid MAINTAINERS entry
# 
# ChangeSet
#   2004/04/25 09:12:06-05:00 jejb@mulgrave.(none) 
#   aic7xxx: compile fix for EISA only case
#   
#   We can't refer to PCI functions for a pure
#   EISA machine.
# 
# drivers/scsi/aic7xxx/aic7xxx_osm.c
#   2004/04/25 09:11:53-05:00 jejb@mulgrave.(none) +2 -0
#   aic7xxx: compile fix for EISA only case
# 
# ChangeSet
#   2004/04/25 09:10:30-05:00 akpm@osdl.org 
#   [PATCH] aic7xxx: fix oops whe hardware is not present
#   
#   From: Herbert Xu <herbert@gondor.apana.org.au>
#   
#   This is because aic7xxx does not unregister itself properly if no devices
#   are found.  This patch fixes the problem.
# 
# drivers/scsi/aic7xxx/aic7xxx_osm.h
#   2004/04/06 23:09:31-05:00 akpm@osdl.org +1 -1
#   aic7xxx: fix oops whe hardware is not present
# 
# drivers/scsi/aic7xxx/aic7xxx_osm.c
#   2004/04/06 23:09:31-05:00 akpm@osdl.org +19 -4
#   aic7xxx: fix oops whe hardware is not present
# 
# drivers/scsi/aic7xxx/aic7770_osm.c
#   2004/04/06 23:09:31-05:00 akpm@osdl.org +12 -13
#   aic7xxx: fix oops whe hardware is not present
# 
# ChangeSet
#   2004/04/25 09:08:55-05:00 Kai.Makisara@kolumbus.fi 
#   [PATCH] SCSI tape log message fixes
#   
#   This patch changes the st console/log messages:
#   
#   - __GFP_NOWARN added to buffer allocation to suppress useless messages
#     when having to use smaller than default segments
#   - move log message from enlarge_buffer() to caller so that the tape name
#     can be printed and remove some debugging messages; now the st messages
#     should include drive name where applicable (a problem reported by
#     Hironobu Ishii)
#   - setting options is logged only when debugging; the most important
#     options are now seen in sysfs
# 
# drivers/scsi/st.c
#   2004/04/04 07:39:34-05:00 Kai.Makisara@kolumbus.fi +54 -66
#   SCSI tape log message fixes
# 
# ChangeSet
#   2004/04/25 09:05:51-05:00 praka@users.sourceforge.net 
#   [PATCH] qla2xxx set current state fixes
#   
#   - always set_current_state(TASK_UNINTERRUBTIBLE) unless we explicitly
#     check for signals.
#   
#   - make all timeouts take HZ based values.
# 
# drivers/scsi/qla2xxx/qla_os.c
#   2004/03/31 15:56:30-06:00 praka@users.sourceforge.net +14 -14
#    qla2xxx set current state fixes
# 
# drivers/scsi/qla2xxx/qla_init.c
#   2004/03/31 15:48:53-06:00 praka@users.sourceforge.net +1 -1
#    qla2xxx set current state fixes
# 
# ChangeSet
#   2004/04/25 09:04:26-05:00 aris@cathedrallabs.org 
#   [PATCH] qlogic_cs: use qlogicfas408 module
#   
#   this patch kills qlogic_core.c and I guess the same idea can be
#   applied to other pcmcia scsi drivers. comments?
# 
# drivers/scsi/pcmcia/qlogic_stub.c
#   2004/03/30 13:46:44-06:00 aris@cathedrallabs.org +35 -30
#   qlogic_cs: use qlogicfas408 module
# 
# drivers/scsi/pcmcia/Makefile
#   2004/03/30 13:04:19-06:00 aris@cathedrallabs.org +1 -1
#   qlogic_cs: use qlogicfas408 module
# 
# BitKeeper/deleted/.del-qlogic_core.c~5bf1eee84f4e0415
#   2004/04/25 09:04:14-05:00 aris@cathedrallabs.org +0 -0
#   Delete: drivers/scsi/pcmcia/qlogic_core.c
# 
# ChangeSet
#   2004/04/25 09:02:48-05:00 aris@cathedrallabs.org 
#   [PATCH] qlogicfas: split and create a new module
# 
# drivers/scsi/qlogicfas408.h
#   2004/03/30 10:49:32-06:00 aris@cathedrallabs.org +27 -31
#   qlogicfas: split and create a new module with only
# 
# drivers/scsi/qlogicfas408.c
#   2004/04/25 09:02:28-05:00 aris@cathedrallabs.org +637 -0
# 
# drivers/scsi/qlogicfas.c
#   2004/03/30 10:51:19-06:00 aris@cathedrallabs.org +50 -571
#   qlogicfas: split and create a new module with only
# 
# drivers/scsi/Makefile
#   2004/03/30 06:54:40-06:00 aris@cathedrallabs.org +2 -1
#   qlogicfas: split and create a new module with only
# 
# drivers/scsi/qlogicfas408.c
#   2004/04/25 09:02:28-05:00 aris@cathedrallabs.org +0 -0
#   BitKeeper file /home/jejb/BK/scsi-misc-2.6/drivers/scsi/qlogicfas408.c
# 
# ChangeSet
#   2004/04/25 09:00:34-05:00 aris@cathedrallabs.org 
#   [PATCH] qlogicfas: kill horrible irq probing
#   
#   	this patch kills irq probe and also I/O because isn't useful to
#   	probe I/O if we can't probe irq later.
# 
# drivers/scsi/qlogicfas.c
#   2004/03/18 05:31:52-06:00 aris@cathedrallabs.org +26 -45
#   qlogicfas: kill horrible irq probing
# 
# ChangeSet
#   2004/04/25 08:44:44-05:00 jejb@mulgrave.(none) 
#   MPT Fusion add back FC909 support
#   
#   From: "Moore, Eric Dean" <Emoore@lsil.com>
# 
# drivers/message/fusion/mptscsih.c
#   2004/04/25 08:44:31-05:00 jejb@mulgrave.(none) +23 -10
#   MPT Fusion add back FC909 support
# 
# drivers/message/fusion/mptctl.h
#   2004/04/25 08:44:31-05:00 jejb@mulgrave.(none) +31 -0
#   MPT Fusion add back FC909 support
# 
# drivers/message/fusion/mptctl.c
#   2004/04/25 08:44:31-05:00 jejb@mulgrave.(none) +52 -26
#   MPT Fusion add back FC909 support
# 
# drivers/message/fusion/mptbase.h
#   2004/04/25 08:44:31-05:00 jejb@mulgrave.(none) +2 -2
#   MPT Fusion add back FC909 support
# 
# drivers/message/fusion/mptbase.c
#   2004/04/25 08:44:31-05:00 jejb@mulgrave.(none) +157 -0
#   MPT Fusion add back FC909 support
# 
# ChangeSet
#   2004/04/12 21:02:39-07:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-scsi
# 
# drivers/scsi/sr.c
#   2004/04/12 21:02:36-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/scsi/scsi_transport_spi.c
#   2004/04/12 21:02:36-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/scsi/dpt_i2o.c
#   2004/04/12 21:02:36-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/scsi/Kconfig
#   2004/04/12 21:02:36-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/04/08 15:01:18-07:00 akpm@bix.(none) 
#   Merge bk://linux-scsi.bkbits.net/scsi-misc-2.6
#   into bix.(none):/usr/src/bk-scsi
# 
# drivers/scsi/scsi_transport_spi.c
#   2004/04/08 15:01:15-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/04/07 00:02:57-07:00 akpm@bix.(none) 
#   Merge bk://linux-scsi.bkbits.net/scsi-misc-2.6
#   into bix.(none):/usr/src/bk-scsi
# 
# drivers/scsi/sr.c
#   2004/04/07 00:02:54-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/scsi/scsi_transport_spi.c
#   2004/04/07 00:02:54-07:00 akpm@bix.(none) +0 -1
#   Auto merged
# 
# drivers/scsi/dpt_i2o.c
#   2004/04/07 00:02:54-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# drivers/scsi/Kconfig
#   2004/04/07 00:02:53-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS	Sun Apr 25 23:04:08 2004
+++ b/MAINTAINERS	Sun Apr 25 23:04:08 2004
@@ -168,10 +168,8 @@
 
 AACRAID SCSI RAID DRIVER
 P:	Adaptec OEM Raid Solutions
-M:	linux-aacraid-devel@dell.com
-L:	linux-aacraid-devel@dell.com
-L:	linux-aacraid-announce@dell.com
-W:	http://domsch.com/linux
+L:	linux-scsi@vger.kernel.org
+W:	http://linux.dell.com/storage.shtml
 S:	Supported
 
 ACPI
diff -Nru a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
--- a/drivers/message/fusion/mptbase.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/message/fusion/mptbase.c	Sun Apr 25 23:04:08 2004
@@ -223,6 +223,7 @@
 
 //int		mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
 static int	ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers);
+static void	mpt_sp_ioc_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf);
 static void	mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);
 static void	mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info);
 
@@ -263,6 +264,8 @@
  */
 
 static struct pci_device_id mptbase_pci_table[] = {
+	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909,
+		PCI_ANY_ID, PCI_ANY_ID },
 	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929,
 		PCI_ANY_ID, PCI_ANY_ID },
 	{ PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919,
@@ -389,6 +392,12 @@
 				else
 					mpt_sp_log_info(ioc, log_info);
 			}
+			if (ioc_stat & MPI_IOCSTATUS_MASK) {
+				if ((int)ioc->chip_type <= (int)FC929)
+						;
+				else
+					mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf);
+			}
 		} else {
 			/*
 			 *  Process turbo (context) reply...
@@ -1350,6 +1359,10 @@
 	}
 
 	ioc->chip_type = FCUNK;
+	if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) {
+		ioc->chip_type = FC909;
+		ioc->prod_name = "LSIFC909";
+	}
 	if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) {
 		ioc->chip_type = FC929;
 		ioc->prod_name = "LSIFC929";
@@ -6086,9 +6099,153 @@
 	case 0x00080000:
 		desc = "Outbound DMA Overrun";
 		break;
+	
+	case 0x00090000:
+		desc = "Task Management";
+		break;
+
+	case 0x000A0000:
+		desc = "Device Problem";
+		break;
+
+	case 0x000B0000:
+		desc = "Invalid Phase Change";
+		break;
+
+	case 0x000C0000:
+		desc = "Untagged Table Size";
+		break;
+	
 	}
 
 	printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): F/W: %s\n", ioc->name, log_info, desc);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	mpt_sp_ioc_info - IOC information returned from SCSI Parallel IOC.
+ *	@ioc: Pointer to MPT_ADAPTER structure
+ *	@ioc_status: U32 IOCStatus word from IOC
+ *	@mf: Pointer to MPT request frame
+ *
+ *	Refer to lsi/mpi.h.
+ */
+static void
+mpt_sp_ioc_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf)
+{
+	u32 status = ioc_status & MPI_IOCSTATUS_MASK;
+	char *desc = "";
+
+	switch (status) {
+	case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
+		desc = "Invalid Function";
+		break;
+
+	case MPI_IOCSTATUS_BUSY: /* 0x0002 */
+		desc = "Busy";
+		break;
+
+	case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
+		desc = "Invalid SGL";
+		break;
+
+	case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
+		desc = "Internal Error";
+		break;
+
+	case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
+		desc = "Reserved";
+		break;
+
+	case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
+		desc = "Insufficient Resources";
+		break;
+
+	case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
+		desc = "Invalid Field";
+		break;
+
+	case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
+		desc = "Invalid State";
+		break;
+
+	case MPI_IOCSTATUS_CONFIG_INVALID_ACTION: /* 0x0020 */
+	case MPI_IOCSTATUS_CONFIG_INVALID_TYPE:   /* 0x0021 */
+	case MPI_IOCSTATUS_CONFIG_INVALID_PAGE:   /* 0x0022 */
+	case MPI_IOCSTATUS_CONFIG_INVALID_DATA:   /* 0x0023 */
+	case MPI_IOCSTATUS_CONFIG_NO_DEFAULTS:    /* 0x0024 */
+	case MPI_IOCSTATUS_CONFIG_CANT_COMMIT:    /* 0x0025 */
+		/* No message for Config IOCStatus values */
+		break;
+
+	case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
+		/* No message for recovered error
+		desc = "SCSI Recovered Error";
+		*/
+		break;
+
+	case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
+		desc = "SCSI Invalid Bus";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
+		desc = "SCSI Invalid TargetID";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
+	  {
+		SCSIIORequest_t *pScsiReq = (SCSIIORequest_t *) mf;
+		U8 cdb = pScsiReq->CDB[0];
+		if (cdb != 0x12) { /* Inquiry is issued for device scanning */
+			desc = "SCSI Device Not There";
+		}
+		break;
+	  }
+
+	case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
+		desc = "SCSI Data Overrun";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
+		/* This error is checked in scsi_io_done(). Skip. 
+		desc = "SCSI Data Underrun";
+		*/
+		break;
+
+	case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
+		desc = "SCSI I/O Data Error";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
+		desc = "SCSI Protocol Error";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
+		desc = "SCSI Task Terminated";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
+		desc = "SCSI Residual Mismatch";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
+		desc = "SCSI Task Management Failed";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
+		desc = "SCSI IOC Terminated";
+		break;
+
+	case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+		desc = "SCSI Ext Terminated";
+		break;
+
+	default:
+		desc = "Others";
+		break;
+	}
+	if (desc != "")
+		printk(MYIOC_s_INFO_FMT "IOCStatus(0x%04x): %s\n", ioc->name, status, desc);
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff -Nru a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
--- a/drivers/message/fusion/mptbase.h	Sun Apr 25 23:04:08 2004
+++ b/drivers/message/fusion/mptbase.h	Sun Apr 25 23:04:08 2004
@@ -81,8 +81,8 @@
 #define COPYRIGHT	"Copyright (c) 1999-2004 " MODULEAUTHOR
 #endif
 
-#define MPT_LINUX_VERSION_COMMON	"3.01.03"
-#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-3.01.03"
+#define MPT_LINUX_VERSION_COMMON	"3.01.05"
+#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-3.01.05"
 #define WHAT_MAGIC_STRING		"@" "(" "#" ")"
 
 #define show_mptmod_ver(s,ver)  \
diff -Nru a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
--- a/drivers/message/fusion/mptctl.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/message/fusion/mptctl.c	Sun Apr 25 23:04:08 2004
@@ -1199,7 +1199,7 @@
 mptctl_getiocinfo (unsigned long arg, unsigned int data_size)
 {
 	struct mpt_ioctl_iocinfo *uarg = (struct mpt_ioctl_iocinfo *) arg;
-	struct mpt_ioctl_iocinfo karg;
+	struct mpt_ioctl_iocinfo *karg;
 	MPT_ADAPTER		*ioc;
 	struct pci_dev		*pdev;
 	struct Scsi_Host	*sh;
@@ -1219,34 +1219,46 @@
 	 */
 	if (data_size == sizeof(struct mpt_ioctl_iocinfo_rev0))
 		cim_rev = 0;
-	else if (data_size == sizeof(struct mpt_ioctl_iocinfo))
+	else if (data_size == sizeof(struct mpt_ioctl_iocinfo_rev1))
 		cim_rev = 1;
+	else if (data_size == sizeof(struct mpt_ioctl_iocinfo))
+		cim_rev = 2;
 	else if (data_size == (sizeof(struct mpt_ioctl_iocinfo_rev0)+12))
 		cim_rev = 0;	/* obsolete */
 	else
 		return -EFAULT;
-
-	if (copy_from_user(&karg, uarg, data_size)) {
+	
+	karg = kmalloc(data_size, GFP_KERNEL);
+	if (karg == NULL) {
+		printk(KERN_ERR "%s::mpt_ioctl_iocinfo() @%d - no memory available!\n",
+				__FILE__, __LINE__);
+		return -ENOMEM;
+	}
+		
+	if (copy_from_user(karg, uarg, data_size)) {
 		printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
 			"Unable to read in mpt_ioctl_iocinfo struct @ %p\n",
 				__FILE__, __LINE__, (void*)uarg);
+		kfree(karg);
 		return -EFAULT;
 	}
 
-	if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+	if (((iocnum = mpt_verify_adapter(karg->hdr.iocnum, &ioc)) < 0) ||
 	    (ioc == NULL)) {
 		dctlprintk((KERN_ERR "%s::mptctl_getiocinfo() @%d - ioc%d not found!\n",
 				__FILE__, __LINE__, iocnum));
+		kfree(karg);
 		return -ENODEV;
 	}
 
 	/* Verify the data transfer size is correct.
 	 * Ignore the port setting.
 	 */
-	if (karg.hdr.maxDataSize != data_size) {
+	if (karg->hdr.maxDataSize != data_size) {
 		printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
 			"Structure size mismatch. Command not completed.\n",
 				__FILE__, __LINE__);
+		kfree(karg);
 		return -EFAULT;
 	}
 
@@ -1254,29 +1266,37 @@
 	 * program
 	 */
 	if ((int)ioc->chip_type <= (int) FC929)
-		karg.adapterType = MPT_IOCTL_INTERFACE_FC;
+		karg->adapterType = MPT_IOCTL_INTERFACE_FC;
 	else
-		karg.adapterType = MPT_IOCTL_INTERFACE_SCSI;
+		karg->adapterType = MPT_IOCTL_INTERFACE_SCSI;
 
-	port = karg.hdr.port;
+	port = karg->hdr.port;
 
-	karg.port = port;
+	karg->port = port;
 	pdev = (struct pci_dev *) ioc->pcidev;
 
-	karg.pciId = pdev->device;
+	karg->pciId = pdev->device;
 	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-	karg.hwRev = revision;
+	karg->hwRev = revision;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
-	karg.subSystemDevice = pdev->subsystem_device;
-	karg.subSystemVendor = pdev->subsystem_vendor;
+	karg->subSystemDevice = pdev->subsystem_device;
+	karg->subSystemVendor = pdev->subsystem_vendor;
 #endif
 
 	if (cim_rev == 1) {
 		/* Get the PCI bus, device, and function numbers for the IOC
 		 */
-		karg.pciInfo.u.bits.busNumber = pdev->bus->number;
-		karg.pciInfo.u.bits.deviceNumber = PCI_SLOT( pdev->devfn );
-		karg.pciInfo.u.bits.functionNumber = PCI_FUNC( pdev->devfn );
+		karg->pciInfo.u.bits.busNumber = pdev->bus->number;
+		karg->pciInfo.u.bits.deviceNumber = PCI_SLOT( pdev->devfn );
+		karg->pciInfo.u.bits.functionNumber = PCI_FUNC( pdev->devfn );
+	} else if (cim_rev == 2) {
+		/* Get the PCI bus, device, function and segment ID numbers 
+		   for the IOC */
+		karg->pciInfo.u.bits.busNumber = pdev->bus->number;
+		karg->pciInfo.u.bits.deviceNumber = PCI_SLOT( pdev->devfn );
+		karg->pciInfo.u.bits.functionNumber = PCI_FUNC( pdev->devfn );
+		karg->pciInfo.u.bits.functionNumber = PCI_FUNC( pdev->devfn );
+		karg->pciInfo.segmentID = pci_domain_nr(pdev->bus);
 	}
 
 	/* Get number of devices
@@ -1297,31 +1317,33 @@
 			}
 		}
 	}
-	karg.numDevices = numDevices;
+	karg->numDevices = numDevices;
 
 	/* Set the BIOS and FW Version
 	 */
-	karg.FWVersion = ioc->facts.FWVersion.Word;
-	karg.BIOSVersion = ioc->biosVersion;
+	karg->FWVersion = ioc->facts.FWVersion.Word;
+	karg->BIOSVersion = ioc->biosVersion;
 
 	/* Set the Version Strings.
 	 */
-	strncpy (karg.driverVersion, MPT_LINUX_PACKAGE_NAME, MPT_IOCTL_VERSION_LENGTH);
-	karg.driverVersion[MPT_IOCTL_VERSION_LENGTH-1]='\0';
+	strncpy (karg->driverVersion, MPT_LINUX_PACKAGE_NAME, MPT_IOCTL_VERSION_LENGTH);
+	karg->driverVersion[MPT_IOCTL_VERSION_LENGTH-1]='\0';
 
-	karg.busChangeEvent = 0;
-	karg.hostId = ioc->pfacts[port].PortSCSIID;
-	karg.rsvd[0] = karg.rsvd[1] = 0;
+	karg->busChangeEvent = 0;
+	karg->hostId = ioc->pfacts[port].PortSCSIID;
+	karg->rsvd[0] = karg->rsvd[1] = 0;
 
 	/* Copy the data from kernel memory to user memory
 	 */
-	if (copy_to_user((char *)arg, &karg, data_size)) {
+	if (copy_to_user((char *)arg, karg, data_size)) {
 		printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
 			"Unable to write out mpt_ioctl_iocinfo struct @ %p\n",
 				__FILE__, __LINE__, (void*)uarg);
+		kfree(karg);
 		return -EFAULT;
 	}
 
+	kfree(karg);
 	return 0;
 }
 
@@ -2909,6 +2931,8 @@
 	if (++where && err) goto out_fail;
 	err = register_ioctl32_conversion(MPTIOCINFO1, compat_mptctl_ioctl);
 	if (++where && err) goto out_fail;
+	err = register_ioctl32_conversion(MPTIOCINFO2, compat_mptctl_ioctl);
+	if (++where && err) goto out_fail;
 	err = register_ioctl32_conversion(MPTTARGETINFO, compat_mptctl_ioctl);
 	if (++where && err) goto out_fail;
 	err = register_ioctl32_conversion(MPTTEST, compat_mptctl_ioctl);
@@ -2968,6 +2992,7 @@
 			" (%d:err=%d)\n", where, err);
 	unregister_ioctl32_conversion(MPTIOCINFO);
 	unregister_ioctl32_conversion(MPTIOCINFO1);
+	unregister_ioctl32_conversion(MPTIOCINFO2);
 	unregister_ioctl32_conversion(MPTTARGETINFO);
 	unregister_ioctl32_conversion(MPTTEST);
 	unregister_ioctl32_conversion(MPTEVENTQUERY);
@@ -3018,6 +3043,7 @@
 #ifdef CONFIG_COMPAT
 	unregister_ioctl32_conversion(MPTIOCINFO);
 	unregister_ioctl32_conversion(MPTIOCINFO1);
+	unregister_ioctl32_conversion(MPTIOCINFO2);
 	unregister_ioctl32_conversion(MPTTARGETINFO);
 	unregister_ioctl32_conversion(MPTTEST);
 	unregister_ioctl32_conversion(MPTEVENTQUERY);
diff -Nru a/drivers/message/fusion/mptctl.h b/drivers/message/fusion/mptctl.h
--- a/drivers/message/fusion/mptctl.h	Sun Apr 25 23:04:08 2004
+++ b/drivers/message/fusion/mptctl.h	Sun Apr 25 23:04:08 2004
@@ -91,6 +91,7 @@
 
 #define MPTIOCINFO		_IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo)
 #define MPTIOCINFO1		_IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo_rev0)
+#define MPTIOCINFO2		_IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo_rev1)
 #define MPTTARGETINFO		_IOWR(MPT_MAGIC_NUMBER,18,struct mpt_ioctl_targetinfo)
 #define MPTTEST			_IOWR(MPT_MAGIC_NUMBER,19,struct mpt_ioctl_test)
 #define MPTEVENTQUERY		_IOWR(MPT_MAGIC_NUMBER,21,struct mpt_ioctl_eventquery)
@@ -165,6 +166,18 @@
 	} u;
 };
 
+struct mpt_ioctl_pci_info2 {
+	union {
+		struct {
+			unsigned int  deviceNumber   :  5;
+			unsigned int  functionNumber :  3;
+			unsigned int  busNumber      : 24;
+		} bits;
+		unsigned int  asUlong;
+	} u;
+  int segmentID;
+};
+
 /*
  *  Adapter Information Page
  *  Read only.
@@ -175,6 +188,24 @@
 #define MPT_IOCTL_VERSION_LENGTH	(32)
 
 struct mpt_ioctl_iocinfo {
+	mpt_ioctl_header hdr;
+	int		 adapterType;	/* SCSI or FCP */
+	int		 port;		/* port number */
+	int		 pciId;		/* PCI Id. */
+	int		 hwRev;		/* hardware revision */
+	int		 subSystemDevice;	/* PCI subsystem Device ID */
+	int		 subSystemVendor;	/* PCI subsystem Vendor ID */
+	int		 numDevices;		/* number of devices */
+	int		 FWVersion;		/* FW Version (integer) */
+	int		 BIOSVersion;		/* BIOS Version (integer) */
+	char		 driverVersion[MPT_IOCTL_VERSION_LENGTH];	/* Driver Version (string) */
+	char		 busChangeEvent;
+	char		 hostId;
+	char		 rsvd[2];
+	struct mpt_ioctl_pci_info2  pciInfo; /* Added Rev 2 */
+};
+
+struct mpt_ioctl_iocinfo_rev1 {
 	mpt_ioctl_header hdr;
 	int		 adapterType;	/* SCSI or FCP */
 	int		 port;		/* port number */
diff -Nru a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
--- a/drivers/message/fusion/mptscsih.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/message/fusion/mptscsih.c	Sun Apr 25 23:04:08 2004
@@ -805,6 +805,14 @@
 
 		if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
 			copy_sense_data(sc, hd, mf, pScsiReply);
+                
+		/*
+		 *  Look for + dump FCP ResponseInfo[]!
+		 */
+		if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
+			printk(KERN_NOTICE "  FCP_ResponseInfo=%08xh\n",
+			le32_to_cpu(pScsiReply->ResponseInfo));
+		}
 
 		switch(status) {
 		case MPI_IOCSTATUS_BUSY:			/* 0x0002 */
@@ -1106,16 +1114,21 @@
 			 * Do OS callback
 			 * Free driver resources (chain, msg buffers)
 			 */
-			if (SCpnt->use_sg) {
-				pci_unmap_sg(hd->ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer,
-					    SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
-			} else if (SCpnt->request_bufflen) {
-				scPrivate	*my_priv;
-
-				my_priv = (scPrivate *) &SCpnt->SCp;
-				pci_unmap_single(hd->ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
-					   SCpnt->request_bufflen,
-					   scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+			if (scsi_device_online(SCpnt->device)) {
+				if (SCpnt->use_sg) {
+					pci_unmap_sg(hd->ioc->pcidev,
+						(struct scatterlist *) SCpnt->request_buffer,
+						SCpnt->use_sg,
+						scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+				} else if (SCpnt->request_bufflen) {
+					scPrivate	*my_priv;
+
+					my_priv = (scPrivate *) &SCpnt->SCp;
+					pci_unmap_single(hd->ioc->pcidev,
+						(dma_addr_t)(ulong)my_priv->p1,
+						SCpnt->request_bufflen,
+						scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+				}
 			}
 			SCpnt->result = DID_RESET << 16;
 			SCpnt->host_scribble = NULL;
diff -Nru a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
--- a/drivers/scsi/3w-xxxx.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/3w-xxxx.c	Sun Apr 25 23:04:08 2004
@@ -1,12 +1,12 @@
 /* 
    3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
 
-   Written By: Adam Radford <linux@3ware.com>
+   Written By: Adam Radford <linuxraid@amcc.com>
    Modifications By: Joel Jacobson <linux@3ware.com>
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2003 3ware Inc.
+   Copyright (C) 1999-2004 3ware Inc.
 
    Kernel compatiblity By: 	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -47,10 +47,10 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
    Bugs/Comments/Suggestions should be mailed to:                            
-   linux@3ware.com
+   linuxraid@amcc.com
 
    For more information, goto:
-   http://www.3ware.com
+   http://www.amcc.com
 
    History
    -------
@@ -179,6 +179,11 @@
    1.02.00.036 - Increase character ioctl timeout to 60 seconds.
    1.02.00.037 - Fix tw_ioctl() to handle all non-data ATA passthru cmds
                  for 'smartmontools' support.
+   1.26.00.038 - Roll driver minor version to 26 to denote kernel 2.6.
+                 Add support for cmds_per_lun module parameter.
+   1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code.
+                 Fix data_buffer_length usage in tw_chrdev_ioctl().
+                 Update contact information.
 */
 
 #include <linux/module.h>
@@ -205,6 +210,7 @@
 #include <linux/reboot.h>
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
+#include <linux/moduleparam.h>
 
 #include <asm/errno.h>
 #include <asm/io.h>
@@ -242,10 +248,15 @@
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.037";
+char *tw_driver_version="1.26.00.039";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 static int twe_major = -1;
+static int cmds_per_lun;
+
+/* Module parameters */
+module_param(cmds_per_lun, int, 0);
+MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
 
 /* Functions */
 
@@ -683,7 +694,7 @@
 			break;
 		case TW_OP_AEN_LISTEN:
 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
-			memset(tw_ioctl->data_buffer, 0, tw_ioctl->data_buffer_length);
+			memset(tw_ioctl->data_buffer, 0, data_buffer_length);
 
 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
 			if (tw_dev->aen_head == tw_dev->aen_tail) {
@@ -738,7 +749,7 @@
 			timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
 
 			/* Now wait for the command to complete */
-			wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+			timeout = wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
 
 			/* Check if we timed out, got a signal, or didn't get
 			   an interrupt */
@@ -777,7 +788,7 @@
 	}
 
 	/* Now copy the response to userspace */
-	error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1);
+	error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length - 1);
 	if (error == 0)
 		retval = 0;
 out2:
@@ -1141,14 +1152,6 @@
 			/* Set card status as online */
 			tw_dev->online = 1;
 
-#ifdef CONFIG_3W_XXXX_CMD_PER_LUN
-			tw_host->cmd_per_lun = CONFIG_3W_XXXX_CMD_PER_LUN;
-			if (tw_host->cmd_per_lun > TW_MAX_CMDS_PER_LUN)
-				tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
-#else
-			/* Use SHT cmd_per_lun here */
-			tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
-#endif
 			tw_dev->free_head = TW_Q_START;
 			tw_dev->free_tail = TW_Q_START;
 			tw_dev->free_wrap = TW_Q_LENGTH - 1;
@@ -3386,13 +3389,13 @@
 
 	dprintk(KERN_WARNING "3w-xxxx: tw_slave_configure()\n");
 
-#ifdef CONFIG_3W_XXXX_CMD_PER_LUN
-	max_cmds = CONFIG_3W_XXXX_CMD_PER_LUN;
-	if (max_cmds > TW_MAX_CMDS_PER_LUN)
+	if (cmds_per_lun) {
+		max_cmds = cmds_per_lun;
+		if (max_cmds > TW_MAX_CMDS_PER_LUN)
+			max_cmds = TW_MAX_CMDS_PER_LUN;
+	} else {
 		max_cmds = TW_MAX_CMDS_PER_LUN;
-#else
-	max_cmds = TW_MAX_CMDS_PER_LUN;
-#endif
+	}
 	scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, max_cmds);
 
 	return 0;
@@ -3488,6 +3491,7 @@
 	.eh_abort_handler	= tw_scsi_eh_abort,
 	.eh_host_reset_handler	= tw_scsi_eh_reset,
 	.bios_param		= tw_scsi_biosparam,
+	.slave_configure	= tw_slave_configure,
 	.can_queue		= TW_Q_LENGTH-2,
 	.this_id		= -1,
 	.sg_tablesize		= TW_MAX_SGL_LENGTH,
diff -Nru a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
--- a/drivers/scsi/3w-xxxx.h	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/3w-xxxx.h	Sun Apr 25 23:04:08 2004
@@ -1,12 +1,12 @@
 /* 
    3w-xxxx.h -- 3ware Storage Controller device driver for Linux.
    
-   Written By: Adam Radford <linux@3ware.com>
+   Written By: Adam Radford <linuxraid@amcc.com>
    Modifications By: Joel Jacobson <linux@3ware.com>
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2003 3ware Inc.
+   Copyright (C) 1999-2004 3ware Inc.
 
    Kernel compatiblity By:	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -45,10 +45,10 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
    Bugs/Comments/Suggestions should be mailed to:                            
-   linux@3ware.com
+   linuxraid@amcc.com
    
    For more information, goto:
-   http://www.3ware.com
+   http://www.amcc.com
 */
 
 #ifndef _3W_XXXX_H
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/Makefile	Sun Apr 25 23:04:08 2004
@@ -75,7 +75,8 @@
 obj-$(CONFIG_SCSI_NCR_D700)	+= 53c700.o NCR_D700.o
 obj-$(CONFIG_SCSI_NCR_Q720)	+= NCR_Q720_mod.o
 obj-$(CONFIG_SCSI_SYM53C416)	+= sym53c416.o
-obj-$(CONFIG_SCSI_QLOGIC_FAS)	+= qlogicfas.o
+obj-$(CONFIG_SCSI_QLOGIC_FAS)	+= qlogicfas408.o	qlogicfas.o
+obj-$(CONFIG_PCMCIA_QLOGIC)	+= qlogicfas408.o
 obj-$(CONFIG_SCSI_QLOGIC_ISP)	+= qlogicisp.o 
 obj-$(CONFIG_SCSI_QLOGIC_FC)	+= qlogicfc.o 
 obj-$(CONFIG_SCSI_QLOGIC_1280)	+= qla1280.o 
diff -Nru a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
--- a/drivers/scsi/aic7xxx/aic7770_osm.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c	Sun Apr 25 23:04:08 2004
@@ -73,7 +73,7 @@
 static int aic7770_linux_config(struct aic7770_identity *entry,
 				aic7770_dev_t dev, u_int eisaBase);
 
-void
+int
 ahc_linux_eisa_init(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
@@ -82,7 +82,7 @@
 	int i;
 
 	if (aic7xxx_probe_eisa_vl == 0)
-		return;
+		return -ENODEV;
 
 	/*
 	 * Linux requires the EISA IDs to be specified in
@@ -93,7 +93,7 @@
 					 (ahc_num_aic7770_devs + 1),
 					 M_DEVBUF, M_NOWAIT);
 	if (aic7770_driver.id_table == NULL)
-		return;
+		return -ENOMEM;
 
 	for (eid = (struct eisa_device_id *)aic7770_driver.id_table,
 	     id = aic7770_ident_table, i = 0;
@@ -109,15 +109,16 @@
 	}
 	eid->sig[0] = 0;
 
-	eisa_driver_register(&aic7770_driver);
+	return eisa_driver_register(&aic7770_driver);
 #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) */
 	struct aic7770_identity *entry;
 	u_int  slot;
 	u_int  eisaBase;
 	u_int  i;
+	int ret = -ENODEV;
 
 	if (aic7xxx_probe_eisa_vl == 0)
-		return;
+		return ret;
 
 	eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
 	for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
@@ -146,9 +147,12 @@
 			continue;  /* no EISA card in slot */
 
 		entry = aic7770_find_device(eisa_id);
-		if (entry != NULL)
+		if (entry != NULL) {
 			aic7770_linux_config(entry, NULL, eisaBase);
+			ret = 0;
+		}
 	}
+	return ret;
 #endif
 }
 
@@ -156,13 +160,8 @@
 ahc_linux_eisa_exit(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-	if (aic7xxx_probe_eisa_vl == 0)
-		return;
-
-	if (aic7770_driver.id_table != NULL) {
-		eisa_driver_unregister(&aic7770_driver);
-		free(aic7770_driver.id_table, M_DEVBUF);
-	}
+	eisa_driver_unregister(&aic7770_driver);
+	free(aic7770_driver.id_table, M_DEVBUF);
 #endif
 }
 
diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c	Sun Apr 25 23:04:08 2004
@@ -892,18 +892,25 @@
 	ahc_list_lockinit();
 
 #ifdef CONFIG_PCI
-	ahc_linux_pci_init();
+	found = ahc_linux_pci_init();
+	if (found)
+		goto out;
 #endif
 
 #ifdef CONFIG_EISA
-	ahc_linux_eisa_init();
+	found = ahc_linux_eisa_init();
+	if (found) {
+#ifdef CONFIG_PCI
+		ahc_linux_pci_exit();
+#endif
+		goto out;
+	}
 #endif
 
 	/*
 	 * Register with the SCSI layer all
 	 * controllers we've found.
 	 */
-	found = 0;
 	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
 
 		if (ahc_linux_register_host(ahc, template) == 0)
@@ -913,6 +920,8 @@
 	spin_lock_irq(&io_request_lock);
 #endif
 	aic7xxx_detect_complete++;
+
+out:
 	return (found);
 }
 
@@ -1534,6 +1543,7 @@
 
 	/* Still equal.  Sort by BIOS address, ioport, or bus/slot/func. */
 	switch (rvalue) {
+#ifdef CONFIG_PCI
 	case AHC_PCI:
 	{
 		char primary_channel;
@@ -1566,6 +1576,7 @@
 			value = 1;
 		break;
 	}
+#endif
 	case AHC_EISA:
 		if ((rahc->flags & AHC_BIOS_ENABLED) != 0) {
 			value = rahc->platform_data->bios_address
@@ -5067,11 +5078,17 @@
 	}
 }
 
+static void __exit ahc_linux_exit(void);
+
 static int __init
 ahc_linux_init(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-	return (ahc_linux_detect(&aic7xxx_driver_template) ? 0 : -ENODEV);
+	int rc = ahc_linux_detect(&aic7xxx_driver_template);
+	if (rc)
+		return rc;
+	ahc_linux_exit();
+	return -ENODEV;
 #else
 	scsi_register_module(MODULE_SCSI_HA, &aic7xxx_driver_template);
 	if (aic7xxx_driver_template.present == 0) {
diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.h	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h	Sun Apr 25 23:04:08 2004
@@ -840,7 +840,7 @@
 
 #ifdef CONFIG_EISA
 extern uint32_t aic7xxx_probe_eisa_vl;
-void			 ahc_linux_eisa_init(void);
+int			 ahc_linux_eisa_init(void);
 void			 ahc_linux_eisa_exit(void);
 int			 aic7770_map_registers(struct ahc_softc *ahc,
 					       u_int port);
diff -Nru a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
--- a/drivers/scsi/pcmcia/Makefile	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/pcmcia/Makefile	Sun Apr 25 23:04:08 2004
@@ -9,4 +9,4 @@
 
 aha152x_cs-objs	:= aha152x_stub.o aha152x_core.o
 fdomain_cs-objs	:= fdomain_stub.o fdomain_core.o
-qlogic_cs-objs	:= qlogic_stub.o qlogic_core.o
+qlogic_cs-objs	:= qlogic_stub.o
diff -Nru a/drivers/scsi/pcmcia/qlogic_core.c b/drivers/scsi/pcmcia/qlogic_core.c
--- a/drivers/scsi/pcmcia/qlogic_core.c	Sun Apr 25 23:04:08 2004
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,2 +0,0 @@
-#define PCMCIA 1
-#include "qlogicfas.c"
diff -Nru a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
--- a/drivers/scsi/pcmcia/qlogic_stub.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/pcmcia/qlogic_stub.c	Sun Apr 25 23:04:08 2004
@@ -47,7 +47,7 @@
 
 #include "scsi.h"
 #include "hosts.h"
-#include "../qlogicfas.h"
+#include "../qlogicfas408.h"
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -56,13 +56,13 @@
 #include <pcmcia/ds.h>
 #include <pcmcia/ciscode.h>
 
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ * drain
+ */
+#define INT_TYPE	0
 
-extern Scsi_Host_Template qlogicfas_driver_template;
-extern void qlogicfas_preset(int port, int irq);
-extern int qlogicfas_bus_reset(Scsi_Cmnd *);
-extern irqreturn_t do_ql_ihandl(int irq, void *dev_id, struct pt_regs *regs);
-
-static char *qlogic_name = "qlogic_cs";
+static char qlogic_name[] = "qlogic_cs";
 
 #ifdef PCMCIA_DEBUG
 static int pc_debug = PCMCIA_DEBUG;
@@ -73,6 +73,24 @@
 #define DEBUG(n, args...)
 #endif
 
+static Scsi_Host_Template qlogicfas_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= qlogic_name,
+	.proc_name		= qlogic_name,
+	.info			= qlogicfas408_info,
+	.queuecommand		= qlogicfas408_queuecommand,
+	.eh_abort_handler	= qlogicfas408_abort,
+	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
+	.eh_device_reset_handler= qlogicfas408_device_reset,
+	.eh_host_reset_handler	= qlogicfas408_host_reset,
+	.bios_param		= qlogicfas408_biosparam,
+	.can_queue		= 1,
+	.this_id		= -1,
+	.sg_tablesize		= SG_ALL,
+	.cmd_per_lun		= 1,
+	.use_clustering		= DISABLE_CLUSTERING,
+};
+
 /*====================================================================*/
 
 /* Parameters that can be set with 'insmod' */
@@ -110,29 +128,17 @@
 	int qltyp;		/* type of chip */
 	int qinitid;
 	struct Scsi_Host *shost;	/* registered host structure */
-	qlogicfas_priv_t priv;
+	struct qlogicfas408_priv *priv;
 
-	qltyp = inb(qbase + 0xe) & 0xf8;
+	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
 	qinitid = host->this_id;
 	if (qinitid < 0)
 		qinitid = 7;	/* if no ID, use 7 */
-	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
-	REG0;
-	outb(0x40 | qlcfg8 | qinitid, qbase + 8);	/* (ini) bus id, disable scsi rst */
-	outb(qlcfg5, qbase + 5);	/* select timer */
-	outb(qlcfg9, qbase + 9);	/* prescaler */
-
-#if QL_RESET_AT_START
-	outb(3, qbase + 3);
-	REG1;
-	/* FIXME: timeout */
-	while (inb(qbase + 0xf) & 4)
-		cpu_relax();
-	REG0;
-#endif
+
+	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
 
 	host->name = qlogic_name;
-	shost = scsi_host_alloc(host, sizeof(struct qlogicfas_priv));
+	shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
 	if (!shost)
 		goto err;
 	shost->io_port = qbase;
@@ -141,12 +147,14 @@
 	if (qlirq != -1)
 		shost->irq = qlirq;
 
-	priv = (qlogicfas_priv_t)&(shost->hostdata[0]);
+	priv = get_priv_by_host(shost);
 	priv->qlirq = qlirq;
 	priv->qbase = qbase;
 	priv->qinitid = qinitid;
+	priv->shost = shost;
+	priv->int_type = INT_TYPE;					
 
-	if (request_irq(qlirq, do_ql_ihandl, 0, qlogic_name, shost))
+	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
 		goto free_scsi_host;
 
 	sprintf(priv->qinfo,
@@ -307,9 +315,6 @@
 		outb(0x04, link->io.BasePort1 + 0xd);
 	}
 
-	qlogicfas_driver_template.name = qlogic_name;
-	qlogicfas_driver_template.proc_name = qlogic_name;
-
 	/* The KXL-810AN has a bigger IO port window */
 	if (link->io.NumPorts1 == 32)
 		host = qlogic_detect(&qlogicfas_driver_template, link,
@@ -402,7 +407,7 @@
 				outb(0x04, link->io.BasePort1 + 0xd);
 			}
 			/* Ugggglllyyyy!!! */
-			qlogicfas_bus_reset(NULL);
+			qlogicfas408_bus_reset(NULL);
 		}
 		break;
 	}
diff -Nru a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
--- a/drivers/scsi/qla2xxx/qla_init.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/qla2xxx/qla_init.c	Sun Apr 25 23:04:08 2004
@@ -946,7 +946,7 @@
 			break;
 
 		/* Delay for a while */
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(HZ / 2);
 
 		DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
diff -Nru a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
--- a/drivers/scsi/qla2xxx/qla_os.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/qla2xxx/qla_os.c	Sun Apr 25 23:04:08 2004
@@ -914,7 +914,7 @@
 
 		spin_unlock_irq(ha->host->host_lock);
 
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(2*HZ);
 
 		spin_lock_irq(ha->host->host_lock);
@@ -962,7 +962,7 @@
 	    test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) &&
 		time_before(jiffies, wait_online)) {
 
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(HZ);
 	}
 	if (ha->flags.online == TRUE) 
@@ -1005,7 +1005,7 @@
 	    atomic_read(&ha->loop_state) == LOOP_DOWN) ||
 	    test_bit(CFG_ACTIVE, &ha->cfg_flags) ||
 	    atomic_read(&ha->loop_state) != LOOP_READY) {
-		set_current_state(TASK_INTERRUPTIBLE);
+		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(HZ);
 		if (time_after_eq(jiffies, loop_timeout)) {
 			return_status = QLA_FUNCTION_FAILED;
@@ -2125,8 +2125,8 @@
 
 		qla2x00_check_fabric_devices(ha);
 
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(5);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/100);
 	}
 
 	pci_set_drvdata(pdev, ha);
@@ -2868,7 +2868,7 @@
 			    "Memory Allocation failed - request_ring\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
@@ -2882,7 +2882,7 @@
 			    "Memory Allocation failed - response_ring\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
@@ -2896,7 +2896,7 @@
 			    "Memory Allocation failed - init_cb\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
@@ -2909,7 +2909,7 @@
 			    "Memory Allocation failed - ioctl_mem\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
@@ -2921,7 +2921,7 @@
 			    "qla2x00_allocate_sp_pool()\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
@@ -2938,7 +2938,7 @@
 				    "Memory Allocation failed - sns_cmd\n");
 
 				qla2x00_mem_free(ha);
-				set_current_state(TASK_INTERRUPTIBLE);
+				set_current_state(TASK_UNINTERRUPTIBLE);
 				schedule_timeout(HZ/10);
 
 				continue;
@@ -2954,7 +2954,7 @@
 				    "Memory Allocation failed - ms_iocb\n");
 
 				qla2x00_mem_free(ha);
-				set_current_state(TASK_INTERRUPTIBLE);
+				set_current_state(TASK_UNINTERRUPTIBLE);
 				schedule_timeout(HZ/10);
 
 				continue;
@@ -2973,7 +2973,7 @@
 				    "Memory Allocation failed - ct_sns\n");
 
 				qla2x00_mem_free(ha);
-				set_current_state(TASK_INTERRUPTIBLE);
+				set_current_state(TASK_UNINTERRUPTIBLE);
 				schedule_timeout(HZ/10);
 
 				continue;
@@ -2990,7 +2990,7 @@
 			    "Memory Allocation failed - iodesc_pd\n");
 
 			qla2x00_mem_free(ha);
-			set_current_state(TASK_INTERRUPTIBLE);
+			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/10);
 
 			continue;
diff -Nru a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c
--- a/drivers/scsi/qlogicfas.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/qlogicfas.c	Sun Apr 25 23:04:08 2004
@@ -1,43 +1,19 @@
-/*----------------------------------------------------------------*/
 /*
-   Qlogic linux driver - work in progress. No Warranty express or implied.
-   Use at your own risk.  Support Tort Reform so you won't have to read all
-   these silly disclaimers.
-
-   Copyright 1994, Tom Zerucha.   
-   tz@execpc.com
-   
-   Additional Code, and much appreciated help by
-   Michael A. Griffith
-   grif@cs.ucr.edu
-
-   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
-   help respectively, and for suffering through my foolishness during the
-   debugging process.
-
-   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
-   (you can reference it, but it is incomplete and inaccurate in places)
-
-   Version 0.46 1/30/97 - kernel 1.2.0+
-
-   Functions as standalone, loadable, and PCMCIA driver, the latter from
-   Dave Hinds' PCMCIA package.
-   
-   Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
-   SCSI driver cleanup and audit. This driver still needs work on the
-   following
-   	-	Non terminating hardware waits
-   	-	Some layering violations with its pcmcia stub
-
-   Redistributable under terms of the GNU General Public License
-
-   For the avoidance of doubt the "preferred form" of this code is one which
-   is in an open non patent encumbered format. Where cryptographic key signing
-   forms part of the process of creating an executable the information
-   including keys needed to generate an equivalently functional executable
-   are deemed to be part of the source code.
-
-*/
+ * Qlogic FAS408 ISA card driver
+ *
+ * Copyright 1994, Tom Zerucha.   
+ * tz@execpc.com
+ * 
+ * Redistributable under terms of the GNU General Public License
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Check qlogicfas408.c for more credits and info.
+ */
 
 #include <linux/module.h>
 #include <linux/blkdev.h>		/* to get disk capacity */
@@ -57,429 +33,28 @@
 
 #include "scsi.h"
 #include "hosts.h"
-#include "qlogicfas.h"
-
-/*----------------------------------------------------------------*/
-int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */
-int qlcfg6 = SYNCXFRPD;
-int qlcfg7 = SYNCOFFST;
-int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
-int qlcfg9 = ((XTALFREQ + 4) / 5);
-int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
-
-static char qlogicfas_name[] = "qlogicfas";
-
-int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *));
-
-/*----------------------------------------------------------------*/
-
-/*----------------------------------------------------------------*/
-/* local functions */
-/*----------------------------------------------------------------*/
-
-/* error recovery - reset everything */
-
-static void ql_zap(qlogicfas_priv_t priv)
-{
-	int x;
-	int qbase = priv->qbase;
-
-	x = inb(qbase + 0xd);
-	REG0;
-	outb(3, qbase + 3);	/* reset SCSI */
-	outb(2, qbase + 3);	/* reset chip */
-	if (x & 0x80)
-		REG1;
-}
-
-/*
- *	Do a pseudo-dma tranfer
- */
- 
-static int ql_pdma(qlogicfas_priv_t priv, int phase, char *request, int reqlen)
-{
-	int j;
-	int qbase = priv->qbase;
-	j = 0;
-	if (phase & 1) {	/* in */
-#if QL_TURBO_PDMA
-		rtrc(4)
-		/* empty fifo in large chunks */
-		if (reqlen >= 128 && (inb(qbase + 8) & 2)) {	/* full */
-			insl(qbase + 4, request, 32);
-			reqlen -= 128;
-			request += 128;
-		}
-		while (reqlen >= 84 && !(j & 0xc0))	/* 2/3 */
-			if ((j = inb(qbase + 8)) & 4) 
-			{
-				insl(qbase + 4, request, 21);
-				reqlen -= 84;
-				request += 84;
-			}
-		if (reqlen >= 44 && (inb(qbase + 8) & 8)) {	/* 1/3 */
-			insl(qbase + 4, request, 11);
-			reqlen -= 44;
-			request += 44;
-		}
-#endif
-		/* until both empty and int (or until reclen is 0) */
-		rtrc(7)
-		j = 0;
-		while (reqlen && !((j & 0x10) && (j & 0xc0))) 
-		{
-			/* while bytes to receive and not empty */
-			j &= 0xc0;
-			while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 
-			{
-				*request++ = inb(qbase + 4);
-				reqlen--;
-			}
-			if (j & 0x10)
-				j = inb(qbase + 8);
-
-		}
-	} else {		/* out */
-#if QL_TURBO_PDMA
-		rtrc(4)
-		    if (reqlen >= 128 && inb(qbase + 8) & 0x10) {	/* empty */
-			outsl(qbase + 4, request, 32);
-			reqlen -= 128;
-			request += 128;
-		}
-		while (reqlen >= 84 && !(j & 0xc0))	/* 1/3 */
-			if (!((j = inb(qbase + 8)) & 8)) {
-				outsl(qbase + 4, request, 21);
-				reqlen -= 84;
-				request += 84;
-			}
-		if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {	/* 2/3 */
-			outsl(qbase + 4, request, 10);
-			reqlen -= 40;
-			request += 40;
-		}
-#endif
-		/* until full and int (or until reclen is 0) */
-		rtrc(7)
-		    j = 0;
-		while (reqlen && !((j & 2) && (j & 0xc0))) {
-			/* while bytes to send and not full */
-			while (reqlen && !((j = inb(qbase + 8)) & 2)) 
-			{
-				outb(*request++, qbase + 4);
-				reqlen--;
-			}
-			if (j & 2)
-				j = inb(qbase + 8);
-		}
-	}
-	/* maybe return reqlen */
-	return inb(qbase + 8) & 0xc0;
-}
-
-/*
- *	Wait for interrupt flag (polled - not real hardware interrupt) 
- */
-
-static int ql_wai(qlogicfas_priv_t priv)
-{
-	int k;
-	int qbase = priv->qbase;
-	unsigned long i;
-
-	k = 0;
-	i = jiffies + WATCHDOG;
-	while (time_before(jiffies, i) && !priv->qabort &&
-					!((k = inb(qbase + 4)) & 0xe0)) {
-		barrier();
-		cpu_relax();
-	}
-	if (time_after_eq(jiffies, i))
-		return (DID_TIME_OUT);
-	if (priv->qabort)
-		return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
-	if (k & 0x60)
-		ql_zap(priv);
-	if (k & 0x20)
-		return (DID_PARITY);
-	if (k & 0x40)
-		return (DID_ERROR);
-	return 0;
-}
-
-/*
- *	Initiate scsi command - queueing handler 
- *	caller must hold host lock
- */
-
-static void ql_icmd(Scsi_Cmnd * cmd)
-{
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
-	int 	qbase = priv->qbase;
-	unsigned int i;
-
-	priv->qabort = 0;
-
-	REG0;
-	/* clearing of interrupts and the fifo is needed */
-
-	inb(qbase + 5);		/* clear interrupts */
-	if (inb(qbase + 5))	/* if still interrupting */
-		outb(2, qbase + 3);	/* reset chip */
-	else if (inb(qbase + 7) & 0x1f)
-		outb(1, qbase + 3);	/* clear fifo */
-	while (inb(qbase + 5));	/* clear ints */
-	REG1;
-	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
-	outb(0, qbase + 0xb);	/* disable ints */
-	inb(qbase + 8);		/* clear int bits */
-	REG0;
-	outb(0x40, qbase + 0xb);	/* enable features */
-
-	/* configurables */
-	outb(qlcfgc, qbase + 0xc);
-	/* config: no reset interrupt, (initiator) bus id */
-	outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
-	outb(qlcfg7, qbase + 7);
-	outb(qlcfg6, qbase + 6);
-	 /**/ outb(qlcfg5, qbase + 5);	/* select timer */
-	outb(qlcfg9 & 7, qbase + 9);	/* prescaler */
-/*	outb(0x99, qbase + 5);	*/
-	outb(cmd->device->id, qbase + 4);
-
-	for (i = 0; i < cmd->cmd_len; i++)
-		outb(cmd->cmnd[i], qbase + 2);
-
-	priv->qlcmd = cmd;
-	outb(0x41, qbase + 3);	/* select and send command */
-}
-
-/*
- *	Process scsi command - usually after interrupt 
- */
-
-static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
-{
-	unsigned int i, j;
-	unsigned long k;
-	unsigned int result;	/* ultimate return result */
-	unsigned int status;	/* scsi returned status */
-	unsigned int message;	/* scsi returned message */
-	unsigned int phase;	/* recorded scsi phase */
-	unsigned int reqlen;	/* total length of transfer */
-	struct scatterlist *sglist;	/* scatter-gather list pointer */
-	unsigned int sgcount;	/* sg counter */
-	char *buf;
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
-	int qbase = priv->qbase;
-
-	rtrc(1)
-	j = inb(qbase + 6);
-	i = inb(qbase + 5);
-	if (i == 0x20) {
-		return (DID_NO_CONNECT << 16);
-	}
-	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */
-	if (i != 0x18) {
-		printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
-		ql_zap(priv);
-		return (DID_BAD_INTR << 16);
-	}
-	j &= 7;			/* j = inb( qbase + 7 ) >> 5; */
-
-	/* correct status is supposed to be step 4 */
-	/* it sometimes returns step 3 but with 0 bytes left to send */
-	/* We can try stuffing the FIFO with the max each time, but we will get a
-	   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
-
-	if (j != 3 && j != 4) {
-		printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
-		     j, i, inb(qbase + 7) & 0x1f);
-		ql_zap(priv);
-		return (DID_ERROR << 16);
-	}
-	result = DID_OK;
-	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */
-		outb(1, qbase + 3);	/* clear fifo */
-	/* note that request_bufflen is the total xfer size when sg is used */
-	reqlen = cmd->request_bufflen;
-	/* note that it won't work if transfers > 16M are requested */
-	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */
-		rtrc(2)
-		outb(reqlen, qbase);	/* low-mid xfer cnt */
-		outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */
-		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */
-		outb(0x90, qbase + 3);	/* command do xfer */
-		/* PIO pseudo DMA to buffer or sglist */
-		REG1;
-		if (!cmd->use_sg)
-			ql_pdma(priv, phase, cmd->request_buffer,
-				cmd->request_bufflen);
-		else {
-			sgcount = cmd->use_sg;
-			sglist = cmd->request_buffer;
-			while (sgcount--) {
-				if (priv->qabort) {
-					REG0;
-					return ((priv->qabort == 1 ?
-						DID_ABORT : DID_RESET) << 16);
-				}
-				buf = page_address(sglist->page) + sglist->offset;
-				if (ql_pdma(priv, phase, buf, sglist->length))
-					break;
-				sglist++;
-			}
-		}
-		REG0;
-		rtrc(2)
-		/*
-		 *	Wait for irq (split into second state of irq handler
-		 *	if this can take time) 
-		 */
-		if ((k = ql_wai(priv)))
-			return (k << 16);
-		k = inb(qbase + 5);	/* should be 0x10, bus service */
-	}
-
-	/*
-	 *	Enter Status (and Message In) Phase 
-	 */
-	 
-	k = jiffies + WATCHDOG;
-
-	while (time_before(jiffies, k) && !priv->qabort &&
-						!(inb(qbase + 4) & 6))
-		cpu_relax();	/* wait for status phase */
-
-	if (time_after_eq(jiffies, k)) {
-		ql_zap(priv);
-		return (DID_TIME_OUT << 16);
-	}
-
-	/* FIXME: timeout ?? */
-	while (inb(qbase + 5))
-		cpu_relax();	/* clear pending ints */
-
-	if (priv->qabort)
-		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
-
-	outb(0x11, qbase + 3);	/* get status and message */
-	if ((k = ql_wai(priv)))
-		return (k << 16);
-	i = inb(qbase + 5);	/* get chip irq stat */
-	j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */
-	status = inb(qbase + 2);
-	message = inb(qbase + 2);
-
-	/*
-	 *	Should get function complete int if Status and message, else 
-	 *	bus serv if only status 
-	 */
-	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
-		printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
-		result = DID_ERROR;
-	}
-	outb(0x12, qbase + 3);	/* done, disconnect */
-	rtrc(1)
-	if ((k = ql_wai(priv)))
-		return (k << 16);
-
-	/*
-	 *	Should get bus service interrupt and disconnect interrupt 
-	 */
-	 
-	i = inb(qbase + 5);	/* should be bus service */
-	while (!priv->qabort && ((i & 0x20) != 0x20)) {
-		barrier();
-		cpu_relax();
-		i |= inb(qbase + 5);
-	}
-	rtrc(0)
-
-	if (priv->qabort)
-		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
-		
-	return (result << 16) | (message << 8) | (status & STATUS_MASK);
-}
+#include "qlogicfas408.h"
 
-/*
- *	Interrupt handler 
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ * drain
  */
+#define INT_TYPE	2
 
-static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
-{
-	Scsi_Cmnd *icmd;
-	struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(host->hostdata[0]);
-	int qbase = priv->qbase;
-	REG0;
-
-	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */
-		return;
-
-	if (priv->qlcmd == NULL) {	/* no command to process? */
-		int i;
-		i = 16;
-		while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */
-		return;
-	}
-	icmd = priv->qlcmd;
-	icmd->result = ql_pcmd(icmd);
-	priv->qlcmd = NULL;
-	/*
-	 *	If result is CHECK CONDITION done calls qcommand to request 
-	 *	sense 
-	 */
-	(icmd->scsi_done) (icmd);
-}
-
-irqreturn_t do_ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
-{
-	unsigned long flags;
-	struct Scsi_Host *host = dev_id;
-
-	spin_lock_irqsave(host->host_lock, flags);
-	ql_ihandl(irq, dev_id, regs);
-	spin_unlock_irqrestore(host->host_lock, flags);
-	return IRQ_HANDLED;
-}
-
-/*
- *	Queued command
- */
-
-int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
-	if (cmd->device->id == priv->qinitid) {
-		cmd->result = DID_BAD_TARGET << 16;
-		done(cmd);
-		return 0;
-	}
-
-	cmd->scsi_done = done;
-	/* wait for the last command's interrupt to finish */
-	while (priv->qlcmd != NULL) {
-		barrier();
-		cpu_relax();
-	}
-	ql_icmd(cmd);
-	return 0;
-}
+static char qlogicfas_name[] = "qlogicfas";
 
-#ifndef PCMCIA
 /*
  *	Look for qlogic card and init if found 
  */
  
-struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host, int qbase,
+static struct Scsi_Host *__qlogicfas_detect(Scsi_Host_Template *host,
+								int qbase,
 								int qlirq)
 {
-	int i, j;		/* these are only used by IRQ detect */
 	int qltyp;		/* type of chip */
 	int qinitid;
 	struct Scsi_Host *hreg;	/* registered host structure */
-	qlogicfas_priv_t priv;
+	struct qlogicfas408_priv *priv;
 
 	/*	Qlogic Cards only exist at 0x230 or 0x330 (the chip itself
 	 *	decodes the address - I check 230 first since MIDI cards are
@@ -490,70 +65,36 @@
 	 *	the second card, but I haven't tested this.
 	 */
 
-	if (!qbase) {
-		for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
-			if (!request_region(qbase, 0x10, qlogicfas_name))
-				continue;
-			REG1;
-			if (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7)
-			    && ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7))
-				break;
-			release_region(qbase, 0x10);
-		}
-		if (qbase == 0x430)
-			return NULL;
-	} else
-		printk(KERN_INFO "Ql: Using preset base address of %03x\n", qbase);
+	if (!qbase || qlirq == -1)
+		goto err;
+
+	if (!request_region(qbase, 0x10, qlogicfas_name)) {
+		printk(KERN_INFO "%s: address %#x is busy\n", qlogicfas_name,
+							      qbase);
+		goto err;
+	}
+
+	if (!qlogicfas408_detect(qbase, INT_TYPE)) {
+		printk(KERN_WARNING "%s: probe failed for %#x\n",
+								qlogicfas_name,
+								qbase);
+		goto err_release_mem;
+	}
 
-	qltyp = inb(qbase + 0xe) & 0xf8;
+	printk(KERN_INFO "%s: Using preset base address of %03x,"
+			 " IRQ %d\n", qlogicfas_name, qbase, qlirq);
+
+	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
 	qinitid = host->this_id;
 	if (qinitid < 0)
 		qinitid = 7;	/* if no ID, use 7 */
-	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
-	REG0;
-	outb(0x40 | qlcfg8 | qinitid, qbase + 8);	/* (ini) bus id, disable scsi rst */
-	outb(qlcfg5, qbase + 5);	/* select timer */
-	outb(qlcfg9, qbase + 9);	/* prescaler */
-
-#if QL_RESET_AT_START
-	outb(3, qbase + 3);
-	REG1;
-	/* FIXME: timeout */
-	while (inb(qbase + 0xf) & 4)
-		cpu_relax();
-	REG0;
-#endif
-
-	/*
-	 *	IRQ probe - toggle pin and check request pending 
-	 */
 
-	if (qlirq == -1) {
-		i = 0xffff;
-		j = 3;
-		outb(0x90, qbase + 3);	/* illegal command - cause interrupt */
-		REG1;
-		outb(10, 0x20);	/* access pending interrupt map */
-		outb(10, 0xa0);
-		while (j--) {
-			outb(0xb0 | QL_INT_ACTIVE_HIGH, qbase + 0xd);	/* int pin off */
-			i &= ~(inb(0x20) | (inb(0xa0) << 8));		/* find IRQ off */
-			outb(0xb4 | QL_INT_ACTIVE_HIGH, qbase + 0xd);	/* int pin on */
-			i &= inb(0x20) | (inb(0xa0) << 8);		/* find IRQ on */
-		}
-		REG0;
-		while (inb(qbase + 5));	/* purge int */
-		j = -1;
-		while (i)	/* find on bit */
-			i >>= 1, j++;	/* should check for exactly 1 on */
-		qlirq = j;
-	} else
-		printk(KERN_INFO "Ql: Using preset IRQ %d\n", qlirq);
+	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
 
-	hreg = scsi_host_alloc(host, sizeof(struct qlogicfas_priv));
+	hreg = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
 	if (!hreg)
 		goto err_release_mem;
-	priv = (qlogicfas_priv_t)&(hreg->hostdata[0]);
+	priv = get_priv_by_host(hreg);
 	hreg->io_port = qbase;
 	hreg->n_io_port = 16;
 	hreg->dma_channel = -1;
@@ -563,13 +104,14 @@
 	priv->qlirq = qlirq;
 	priv->qinitid = qinitid;
 	priv->shost = hreg;
+	priv->int_type = INT_TYPE;
 
 	sprintf(priv->qinfo,
 		"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
 		qltyp, qbase, qlirq, QL_TURBO_PDMA);
 	host->name = qlogicfas_name;
 
-	if (request_irq(qlirq, do_ql_ihandl, 0, qlogicfas_name, hreg))
+	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogicfas_name, hreg))
 		goto free_scsi_host;
 
 	if (scsi_add_host(hreg, NULL))
@@ -587,11 +129,12 @@
 
 err_release_mem:
 	release_region(qbase, 0x10);
+err:
 	return NULL;
 }
 
 #define MAX_QLOGICFAS	8
-static qlogicfas_priv_t cards;
+static struct qlogicfas408_priv *cards;
 static int iobase[MAX_QLOGICFAS];
 static int irq[MAX_QLOGICFAS] = { [0 ... MAX_QLOGICFAS-1] = -1 };
 MODULE_PARM(iobase, "1-" __MODULE_STRING(MAX_QLOGICFAS) "i");
@@ -599,23 +142,21 @@
 MODULE_PARM_DESC(iobase, "I/O address");
 MODULE_PARM_DESC(irq, "IRQ");
 
-int __devinit qlogicfas_detect(Scsi_Host_Template *sht)
+static int __devinit qlogicfas_detect(Scsi_Host_Template *sht)
 {
-	struct Scsi_Host	*shost;
-	qlogicfas_priv_t	priv;
-	int	i,
-		num = 0;
+	struct Scsi_Host *shost;
+	struct qlogicfas408_priv *priv;
+	int num;
 
-	for (i = 0; i < MAX_QLOGICFAS; i++) {
+	for (num = 0; num < MAX_QLOGICFAS; num++) {
 		shost = __qlogicfas_detect(sht, iobase[num], irq[num]);
 		if (shost == NULL) {
 			/* no more devices */
 			break;
 		}
-		priv = (qlogicfas_priv_t)&(shost->hostdata[0]);
+		priv = get_priv_by_host(shost);
 		priv->next = cards;
 		cards = priv;
-		num++;
 	}
 
 	return num;
@@ -623,13 +164,10 @@
 
 static int qlogicfas_release(struct Scsi_Host *shost)
 {
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(shost->hostdata[0]);
-	int qbase = priv->qbase;
+	struct qlogicfas408_priv *priv = get_priv_by_host(shost);
 
 	if (shost->irq) {
-		REG1;
-		outb(0, qbase + 0xb);	/* disable ints */
-	
+		qlogicfas408_disable_ints(priv);	
 		free_irq(shost->irq, shost);
 	}
 	if (shost->dma_channel != 0xff)
@@ -641,100 +179,21 @@
 
 	return 0;
 }
-#endif	/* ifndef PCMCIA */
-
-/* 
- *	Return bios parameters 
- */
-
-int qlogicfas_biosparam(struct scsi_device * disk,
-		        struct block_device *dev,
-			sector_t capacity, int ip[])
-{
-/* This should mimic the DOS Qlogic driver's behavior exactly */
-	ip[0] = 0x40;
-	ip[1] = 0x20;
-	ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
-	if (ip[2] > 1024) {
-		ip[0] = 0xff;
-		ip[1] = 0x3f;
-		ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
-#if 0
-		if (ip[2] > 1023)
-			ip[2] = 1023;
-#endif
-	}
-	return 0;
-}
-
-/*
- *	Abort a command in progress
- */
- 
-static int qlogicfas_abort(Scsi_Cmnd * cmd)
-{
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
-	priv->qabort = 1;
-	ql_zap(priv);
-	return SUCCESS;
-}
-
-/* 
- *	Reset SCSI bus
- *	FIXME: This function is invoked with cmd = NULL directly by
- *	the PCMCIA qlogic_stub code. This wants fixing
- */
-
-int qlogicfas_bus_reset(Scsi_Cmnd * cmd)
-{
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(cmd->device->host->hostdata[0]);
-	priv->qabort = 2;
-	ql_zap(priv);
-	return SUCCESS;
-}
-
-/* 
- *	Reset SCSI host controller
- */
-
-static int qlogicfas_host_reset(Scsi_Cmnd * cmd)
-{
-	return FAILED;
-}
-
-/* 
- *	Reset SCSI device
- */
-
-static int qlogicfas_device_reset(Scsi_Cmnd * cmd)
-{
-	return FAILED;
-}
-
-/*
- *	Return info string
- */
-
-static const char *qlogicfas_info(struct Scsi_Host *host)
-{
-	qlogicfas_priv_t priv = (qlogicfas_priv_t)&(host->hostdata[0]);
-	return priv->qinfo;
-}
 
 /*
  *	The driver template is also needed for PCMCIA
  */
-Scsi_Host_Template qlogicfas_driver_template = {
+static Scsi_Host_Template qlogicfas_driver_template = {
 	.module			= THIS_MODULE,
 	.name			= qlogicfas_name,
 	.proc_name		= qlogicfas_name,
-	.info			= qlogicfas_info,
-	.queuecommand		= qlogicfas_queuecommand,
-	.eh_abort_handler	= qlogicfas_abort,
-	.eh_bus_reset_handler	= qlogicfas_bus_reset,
-	.eh_device_reset_handler= qlogicfas_device_reset,
-	.eh_host_reset_handler	= qlogicfas_host_reset,
-	.bios_param		= qlogicfas_biosparam,
+	.info			= qlogicfas408_info,
+	.queuecommand		= qlogicfas408_queuecommand,
+	.eh_abort_handler	= qlogicfas408_abort,
+	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
+	.eh_device_reset_handler= qlogicfas408_device_reset,
+	.eh_host_reset_handler	= qlogicfas408_host_reset,
+	.bios_param		= qlogicfas408_biosparam,
 	.can_queue		= 1,
 	.this_id		= -1,
 	.sg_tablesize		= SG_ALL,
@@ -742,11 +201,13 @@
 	.use_clustering		= DISABLE_CLUSTERING,
 };
 
-#ifndef PCMCIA
 static __init int qlogicfas_init(void)
 {
 	if (!qlogicfas_detect(&qlogicfas_driver_template)) {
 		/* no cards found */
+		printk(KERN_INFO "%s: no cards were found, please specify "
+				 "I/O address and IRQ using iobase= and irq= "
+				 "options", qlogicfas_name);
 		return -ENODEV;
 	}
 
@@ -755,16 +216,15 @@
 
 static __exit void qlogicfas_exit(void)
 {
-	qlogicfas_priv_t	priv;
+	struct qlogicfas408_priv *priv;
 
 	for (priv = cards; priv != NULL; priv = priv->next)
 		qlogicfas_release(priv->shost);
 }
 
 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
-MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
+MODULE_DESCRIPTION("Driver for the Qlogic FAS408 based ISA card");
 MODULE_LICENSE("GPL");
 module_init(qlogicfas_init);
 module_exit(qlogicfas_exit);
-#endif	/* ifndef PCMCIA */
 
diff -Nru a/drivers/scsi/qlogicfas.h b/drivers/scsi/qlogicfas.h
--- a/drivers/scsi/qlogicfas.h	Sun Apr 25 23:04:08 2004
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,124 +0,0 @@
-/* to be used by qlogicfas and qlogic_cs */
-#ifndef __QLOGICFAS_H
-#define __QLOGICFAS_H
-
-/*----------------------------------------------------------------*/
-/* Configuration */
-
-/* Set the following to 2 to use normal interrupt (active high/totempole-
-   tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
-   drain */
-
-#define QL_INT_ACTIVE_HIGH 2
-
-/* Set the following to max out the speed of the PIO PseudoDMA transfers,
-   again, 0 tends to be slower, but more stable.  */
-
-#define QL_TURBO_PDMA 1
-
-/* This should be 1 to enable parity detection */
-
-#define QL_ENABLE_PARITY 1
-
-/* This will reset all devices when the driver is initialized (during bootup).
-   The other linux drivers don't do this, but the DOS drivers do, and after
-   using DOS or some kind of crash or lockup this will bring things back
-   without requiring a cold boot.  It does take some time to recover from a
-   reset, so it is slower, and I have seen timeouts so that devices weren't
-   recognized when this was set. */
-
-#define QL_RESET_AT_START 0
-
-/* crystal frequency in megahertz (for offset 5 and 9)
-   Please set this for your card.  Most Qlogic cards are 40 Mhz.  The
-   Control Concepts ISA (not VLB) is 24 Mhz */
-
-#define XTALFREQ	40
-
-/**********/
-/* DANGER! modify these at your own risk */
-/* SLOWCABLE can usually be reset to zero if you have a clean setup and
-   proper termination.  The rest are for synchronous transfers and other
-   advanced features if your device can transfer faster than 5Mb/sec.
-   If you are really curious, email me for a quick howto until I have
-   something official */
-/**********/
-
-/*****/
-/* config register 1 (offset 8) options */
-/* This needs to be set to 1 if your cabling is long or noisy */
-#define SLOWCABLE 1
-
-/*****/
-/* offset 0xc */
-/* This will set fast (10Mhz) synchronous timing when set to 1
-   For this to have an effect, FASTCLK must also be 1 */
-#define FASTSCSI 0
-
-/* This when set to 1 will set a faster sync transfer rate */
-#define FASTCLK 0	/*(XTALFREQ>25?1:0)*/
-
-/*****/
-/* offset 6 */
-/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
-   achievable data rate (assuming the rest of the system is capable
-   and set properly) */
-#define SYNCXFRPD 5	/*(XTALFREQ/5)*/
-
-/*****/
-/* offset 7 */
-/* This is the count of how many synchronous transfers can take place
-	i.e. how many reqs can occur before an ack is given.
-	The maximum value for this is 15, the upper bits can modify
-	REQ/ACK assertion and deassertion during synchronous transfers
-	If this is 0, the bus will only transfer asynchronously */
-#define SYNCOFFST 0
-/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
-	of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
-	cause the deassertion to be early by 1/2 clock.  Bits 5&4 control
-	the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
-
-/*----------------------------------------------------------------*/
-#ifdef PCMCIA
-#undef QL_INT_ACTIVE_HIGH
-#define QL_INT_ACTIVE_HIGH 0
-#endif
-
-struct qlogicfas_priv;
-typedef struct qlogicfas_priv *qlogicfas_priv_t;
-struct qlogicfas_priv {
-	 int		qbase;		/* Port */
-	 int		qinitid;	/* initiator ID */
-	 int		qabort;		/* Flag to cause an abort */
-	 int		qlirq;		/* IRQ being used */
-	 char		qinfo[80];	/* description */
-	 Scsi_Cmnd 	*qlcmd;		/* current command being processed */
-	 struct Scsi_Host	*shost;	/* pointer back to host */
-	 qlogicfas_priv_t	next;	/* next private struct */
-};
-
-extern int qlcfg5;
-extern int qlcfg6;
-extern int qlcfg7;
-extern int qlcfg8;
-extern int qlcfg9;
-extern int qlcfgc;
-
-/* The qlogic card uses two register maps - These macros select which one */
-#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
-#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
-
-/* following is watchdog timeout in microseconds */
-#define WATCHDOG 5000000
-
-/*----------------------------------------------------------------*/
-/* the following will set the monitor border color (useful to find
-   where something crashed or gets stuck at and as a simple profiler) */
-
-#if 0
-#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
-#else
-#define rtrc(i) {}
-#endif
-#endif	/* __QLOGICFAS_H */
-
diff -Nru a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/qlogicfas408.c	Sun Apr 25 23:04:08 2004
@@ -0,0 +1,637 @@
+/*----------------------------------------------------------------*/
+/*
+   Qlogic linux driver - work in progress. No Warranty express or implied.
+   Use at your own risk.  Support Tort Reform so you won't have to read all
+   these silly disclaimers.
+
+   Copyright 1994, Tom Zerucha.   
+   tz@execpc.com
+   
+   Additional Code, and much appreciated help by
+   Michael A. Griffith
+   grif@cs.ucr.edu
+
+   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+   help respectively, and for suffering through my foolishness during the
+   debugging process.
+
+   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+   (you can reference it, but it is incomplete and inaccurate in places)
+
+   Version 0.46 1/30/97 - kernel 1.2.0+
+
+   Functions as standalone, loadable, and PCMCIA driver, the latter from
+   Dave Hinds' PCMCIA package.
+   
+   Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
+   SCSI driver cleanup and audit. This driver still needs work on the
+   following
+   	-	Non terminating hardware waits
+   	-	Some layering violations with its pcmcia stub
+
+   Redistributable under terms of the GNU General Public License
+
+   For the avoidance of doubt the "preferred form" of this code is one which
+   is in an open non patent encumbered format. Where cryptographic key signing
+   forms part of the process of creating an executable the information
+   including keys needed to generate an equivalently functional executable
+   are deemed to be part of the source code.
+
+*/
+
+#include <linux/module.h>
+#include <linux/blkdev.h>		/* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "qlogicfas408.h"
+
+/*----------------------------------------------------------------*/
+static int qlcfg5 = (XTALFREQ << 5);	/* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
+static int qlcfg9 = ((XTALFREQ + 4) / 5);
+static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
+
+/*----------------------------------------------------------------*/
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+
+/* error recovery - reset everything */
+
+static void ql_zap(struct qlogicfas408_priv *priv)
+{
+	int x;
+	int qbase = priv->qbase;
+	int int_type = priv->int_type;
+
+	x = inb(qbase + 0xd);
+	REG0;
+	outb(3, qbase + 3);	/* reset SCSI */
+	outb(2, qbase + 3);	/* reset chip */
+	if (x & 0x80)
+		REG1;
+}
+
+/*
+ *	Do a pseudo-dma tranfer
+ */
+ 
+static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
+{
+	int j;
+	int qbase = priv->qbase;
+	j = 0;
+	if (phase & 1) {	/* in */
+#if QL_TURBO_PDMA
+		rtrc(4)
+		/* empty fifo in large chunks */
+		if (reqlen >= 128 && (inb(qbase + 8) & 2)) {	/* full */
+			insl(qbase + 4, request, 32);
+			reqlen -= 128;
+			request += 128;
+		}
+		while (reqlen >= 84 && !(j & 0xc0))	/* 2/3 */
+			if ((j = inb(qbase + 8)) & 4) 
+			{
+				insl(qbase + 4, request, 21);
+				reqlen -= 84;
+				request += 84;
+			}
+		if (reqlen >= 44 && (inb(qbase + 8) & 8)) {	/* 1/3 */
+			insl(qbase + 4, request, 11);
+			reqlen -= 44;
+			request += 44;
+		}
+#endif
+		/* until both empty and int (or until reclen is 0) */
+		rtrc(7)
+		j = 0;
+		while (reqlen && !((j & 0x10) && (j & 0xc0))) 
+		{
+			/* while bytes to receive and not empty */
+			j &= 0xc0;
+			while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 
+			{
+				*request++ = inb(qbase + 4);
+				reqlen--;
+			}
+			if (j & 0x10)
+				j = inb(qbase + 8);
+
+		}
+	} else {		/* out */
+#if QL_TURBO_PDMA
+		rtrc(4)
+		    if (reqlen >= 128 && inb(qbase + 8) & 0x10) {	/* empty */
+			outsl(qbase + 4, request, 32);
+			reqlen -= 128;
+			request += 128;
+		}
+		while (reqlen >= 84 && !(j & 0xc0))	/* 1/3 */
+			if (!((j = inb(qbase + 8)) & 8)) {
+				outsl(qbase + 4, request, 21);
+				reqlen -= 84;
+				request += 84;
+			}
+		if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {	/* 2/3 */
+			outsl(qbase + 4, request, 10);
+			reqlen -= 40;
+			request += 40;
+		}
+#endif
+		/* until full and int (or until reclen is 0) */
+		rtrc(7)
+		    j = 0;
+		while (reqlen && !((j & 2) && (j & 0xc0))) {
+			/* while bytes to send and not full */
+			while (reqlen && !((j = inb(qbase + 8)) & 2)) 
+			{
+				outb(*request++, qbase + 4);
+				reqlen--;
+			}
+			if (j & 2)
+				j = inb(qbase + 8);
+		}
+	}
+	/* maybe return reqlen */
+	return inb(qbase + 8) & 0xc0;
+}
+
+/*
+ *	Wait for interrupt flag (polled - not real hardware interrupt) 
+ */
+
+static int ql_wai(struct qlogicfas408_priv *priv)
+{
+	int k;
+	int qbase = priv->qbase;
+	unsigned long i;
+
+	k = 0;
+	i = jiffies + WATCHDOG;
+	while (time_before(jiffies, i) && !priv->qabort &&
+					!((k = inb(qbase + 4)) & 0xe0)) {
+		barrier();
+		cpu_relax();
+	}
+	if (time_after_eq(jiffies, i))
+		return (DID_TIME_OUT);
+	if (priv->qabort)
+		return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
+	if (k & 0x60)
+		ql_zap(priv);
+	if (k & 0x20)
+		return (DID_PARITY);
+	if (k & 0x40)
+		return (DID_ERROR);
+	return 0;
+}
+
+/*
+ *	Initiate scsi command - queueing handler 
+ *	caller must hold host lock
+ */
+
+static void ql_icmd(Scsi_Cmnd * cmd)
+{
+	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+	int 	qbase = priv->qbase;
+	int	int_type = priv->int_type;
+	unsigned int i;
+
+	priv->qabort = 0;
+
+	REG0;
+	/* clearing of interrupts and the fifo is needed */
+
+	inb(qbase + 5);		/* clear interrupts */
+	if (inb(qbase + 5))	/* if still interrupting */
+		outb(2, qbase + 3);	/* reset chip */
+	else if (inb(qbase + 7) & 0x1f)
+		outb(1, qbase + 3);	/* clear fifo */
+	while (inb(qbase + 5));	/* clear ints */
+	REG1;
+	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
+	outb(0, qbase + 0xb);	/* disable ints */
+	inb(qbase + 8);		/* clear int bits */
+	REG0;
+	outb(0x40, qbase + 0xb);	/* enable features */
+
+	/* configurables */
+	outb(qlcfgc, qbase + 0xc);
+	/* config: no reset interrupt, (initiator) bus id */
+	outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
+	outb(qlcfg7, qbase + 7);
+	outb(qlcfg6, qbase + 6);
+	 /**/ outb(qlcfg5, qbase + 5);	/* select timer */
+	outb(qlcfg9 & 7, qbase + 9);	/* prescaler */
+/*	outb(0x99, qbase + 5);	*/
+	outb(cmd->device->id, qbase + 4);
+
+	for (i = 0; i < cmd->cmd_len; i++)
+		outb(cmd->cmnd[i], qbase + 2);
+
+	priv->qlcmd = cmd;
+	outb(0x41, qbase + 3);	/* select and send command */
+}
+
+/*
+ *	Process scsi command - usually after interrupt 
+ */
+
+static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
+{
+	unsigned int i, j;
+	unsigned long k;
+	unsigned int result;	/* ultimate return result */
+	unsigned int status;	/* scsi returned status */
+	unsigned int message;	/* scsi returned message */
+	unsigned int phase;	/* recorded scsi phase */
+	unsigned int reqlen;	/* total length of transfer */
+	struct scatterlist *sglist;	/* scatter-gather list pointer */
+	unsigned int sgcount;	/* sg counter */
+	char *buf;
+	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+	int qbase = priv->qbase;
+	int int_type = priv->int_type;
+
+	rtrc(1)
+	j = inb(qbase + 6);
+	i = inb(qbase + 5);
+	if (i == 0x20) {
+		return (DID_NO_CONNECT << 16);
+	}
+	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */
+	if (i != 0x18) {
+		printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
+		ql_zap(priv);
+		return (DID_BAD_INTR << 16);
+	}
+	j &= 7;			/* j = inb( qbase + 7 ) >> 5; */
+
+	/* correct status is supposed to be step 4 */
+	/* it sometimes returns step 3 but with 0 bytes left to send */
+	/* We can try stuffing the FIFO with the max each time, but we will get a
+	   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+
+	if (j != 3 && j != 4) {
+		printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
+		     j, i, inb(qbase + 7) & 0x1f);
+		ql_zap(priv);
+		return (DID_ERROR << 16);
+	}
+	result = DID_OK;
+	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */
+		outb(1, qbase + 3);	/* clear fifo */
+	/* note that request_bufflen is the total xfer size when sg is used */
+	reqlen = cmd->request_bufflen;
+	/* note that it won't work if transfers > 16M are requested */
+	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */
+		rtrc(2)
+		outb(reqlen, qbase);	/* low-mid xfer cnt */
+		outb(reqlen >> 8, qbase + 1);	/* low-mid xfer cnt */
+		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */
+		outb(0x90, qbase + 3);	/* command do xfer */
+		/* PIO pseudo DMA to buffer or sglist */
+		REG1;
+		if (!cmd->use_sg)
+			ql_pdma(priv, phase, cmd->request_buffer,
+				cmd->request_bufflen);
+		else {
+			sgcount = cmd->use_sg;
+			sglist = cmd->request_buffer;
+			while (sgcount--) {
+				if (priv->qabort) {
+					REG0;
+					return ((priv->qabort == 1 ?
+						DID_ABORT : DID_RESET) << 16);
+				}
+				buf = page_address(sglist->page) + sglist->offset;
+				if (ql_pdma(priv, phase, buf, sglist->length))
+					break;
+				sglist++;
+			}
+		}
+		REG0;
+		rtrc(2)
+		/*
+		 *	Wait for irq (split into second state of irq handler
+		 *	if this can take time) 
+		 */
+		if ((k = ql_wai(priv)))
+			return (k << 16);
+		k = inb(qbase + 5);	/* should be 0x10, bus service */
+	}
+
+	/*
+	 *	Enter Status (and Message In) Phase 
+	 */
+	 
+	k = jiffies + WATCHDOG;
+
+	while (time_before(jiffies, k) && !priv->qabort &&
+						!(inb(qbase + 4) & 6))
+		cpu_relax();	/* wait for status phase */
+
+	if (time_after_eq(jiffies, k)) {
+		ql_zap(priv);
+		return (DID_TIME_OUT << 16);
+	}
+
+	/* FIXME: timeout ?? */
+	while (inb(qbase + 5))
+		cpu_relax();	/* clear pending ints */
+
+	if (priv->qabort)
+		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+
+	outb(0x11, qbase + 3);	/* get status and message */
+	if ((k = ql_wai(priv)))
+		return (k << 16);
+	i = inb(qbase + 5);	/* get chip irq stat */
+	j = inb(qbase + 7) & 0x1f;	/* and bytes rec'd */
+	status = inb(qbase + 2);
+	message = inb(qbase + 2);
+
+	/*
+	 *	Should get function complete int if Status and message, else 
+	 *	bus serv if only status 
+	 */
+	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+		printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+		result = DID_ERROR;
+	}
+	outb(0x12, qbase + 3);	/* done, disconnect */
+	rtrc(1)
+	if ((k = ql_wai(priv)))
+		return (k << 16);
+
+	/*
+	 *	Should get bus service interrupt and disconnect interrupt 
+	 */
+	 
+	i = inb(qbase + 5);	/* should be bus service */
+	while (!priv->qabort && ((i & 0x20) != 0x20)) {
+		barrier();
+		cpu_relax();
+		i |= inb(qbase + 5);
+	}
+	rtrc(0)
+
+	if (priv->qabort)
+		return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+		
+	return (result << 16) | (message << 8) | (status & STATUS_MASK);
+}
+
+/*
+ *	Interrupt handler 
+ */
+
+static void ql_ihandl(int irq, void *dev_id, struct pt_regs *regs)
+{
+	Scsi_Cmnd *icmd;
+	struct Scsi_Host *host = (struct Scsi_Host *)dev_id;
+	struct qlogicfas408_priv *priv = get_priv_by_host(host);
+	int qbase = priv->qbase;
+	REG0;
+
+	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */
+		return;
+
+	if (priv->qlcmd == NULL) {	/* no command to process? */
+		int i;
+		i = 16;
+		while (i-- && inb(qbase + 5));	/* maybe also ql_zap() */
+		return;
+	}
+	icmd = priv->qlcmd;
+	icmd->result = ql_pcmd(icmd);
+	priv->qlcmd = NULL;
+	/*
+	 *	If result is CHECK CONDITION done calls qcommand to request 
+	 *	sense 
+	 */
+	(icmd->scsi_done) (icmd);
+}
+
+irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct Scsi_Host *host = dev_id;
+
+	spin_lock_irqsave(host->host_lock, flags);
+	ql_ihandl(irq, dev_id, regs);
+	spin_unlock_irqrestore(host->host_lock, flags);
+	return IRQ_HANDLED;
+}
+
+/*
+ *	Queued command
+ */
+
+int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+	if (cmd->device->id == priv->qinitid) {
+		cmd->result = DID_BAD_TARGET << 16;
+		done(cmd);
+		return 0;
+	}
+
+	cmd->scsi_done = done;
+	/* wait for the last command's interrupt to finish */
+	while (priv->qlcmd != NULL) {
+		barrier();
+		cpu_relax();
+	}
+	ql_icmd(cmd);
+	return 0;
+}
+
+/* 
+ *	Return bios parameters 
+ */
+
+int qlogicfas408_biosparam(struct scsi_device * disk,
+		        struct block_device *dev,
+			sector_t capacity, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+	ip[0] = 0x40;
+	ip[1] = 0x20;
+	ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+	if (ip[2] > 1024) {
+		ip[0] = 0xff;
+		ip[1] = 0x3f;
+		ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+#if 0
+		if (ip[2] > 1023)
+			ip[2] = 1023;
+#endif
+	}
+	return 0;
+}
+
+/*
+ *	Abort a command in progress
+ */
+ 
+int qlogicfas408_abort(Scsi_Cmnd * cmd)
+{
+	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+	priv->qabort = 1;
+	ql_zap(priv);
+	return SUCCESS;
+}
+
+/* 
+ *	Reset SCSI bus
+ *	FIXME: This function is invoked with cmd = NULL directly by
+ *	the PCMCIA qlogic_stub code. This wants fixing
+ */
+
+int qlogicfas408_bus_reset(Scsi_Cmnd * cmd)
+{
+	struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+	priv->qabort = 2;
+	ql_zap(priv);
+	return SUCCESS;
+}
+
+/* 
+ *	Reset SCSI host controller
+ */
+
+int qlogicfas408_host_reset(Scsi_Cmnd * cmd)
+{
+	return FAILED;
+}
+
+/* 
+ *	Reset SCSI device
+ */
+
+int qlogicfas408_device_reset(Scsi_Cmnd * cmd)
+{
+	return FAILED;
+}
+
+/*
+ *	Return info string
+ */
+
+char *qlogicfas408_info(struct Scsi_Host *host)
+{
+	struct qlogicfas408_priv *priv = get_priv_by_host(host);
+	return priv->qinfo;
+}
+
+/*
+ *	Get type of chip
+ */
+
+int qlogicfas408_get_chip_type(int qbase, int int_type)
+{
+	REG1;
+	return inb(qbase + 0xe) & 0xf8;
+}
+
+/*
+ *	Perform initialization tasks
+ */
+
+void qlogicfas408_setup(int qbase, int id, int int_type)
+{
+	outb(1, qbase + 8);	/* set for PIO pseudo DMA */
+	REG0;
+	outb(0x40 | qlcfg8 | id, qbase + 8);	/* (ini) bus id, disable scsi rst */
+	outb(qlcfg5, qbase + 5);	/* select timer */
+	outb(qlcfg9, qbase + 9);	/* prescaler */
+
+#if QL_RESET_AT_START
+	outb(3, qbase + 3);
+
+	REG1;
+	/* FIXME: timeout */
+	while (inb(qbase + 0xf) & 4)
+		cpu_relax();
+
+	REG0;
+#endif
+}
+
+/*
+ *	Checks if this is a QLogic FAS 408
+ */
+
+int qlogicfas408_detect(int qbase, int int_type)
+{
+        REG1;
+	return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
+	       ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));		
+}
+
+/*
+ *	Disable interrupts
+ */
+
+void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
+{
+	int qbase = priv->qbase;
+	int int_type = priv->int_type;
+
+	REG1;
+	outb(0, qbase + 0xb);	/* disable ints */
+}
+
+/*
+ *	Init and exit functions
+ */
+
+static int __init qlogicfas408_init(void)
+{
+	return 0;
+}
+
+static void __exit qlogicfas408_exit(void)
+{
+
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
+MODULE_LICENSE("GPL");
+module_init(qlogicfas408_init);
+module_exit(qlogicfas408_exit);
+
+EXPORT_SYMBOL(qlogicfas408_info);
+EXPORT_SYMBOL(qlogicfas408_queuecommand);
+EXPORT_SYMBOL(qlogicfas408_abort);
+EXPORT_SYMBOL(qlogicfas408_bus_reset);
+EXPORT_SYMBOL(qlogicfas408_device_reset);
+EXPORT_SYMBOL(qlogicfas408_host_reset);
+EXPORT_SYMBOL(qlogicfas408_biosparam);
+EXPORT_SYMBOL(qlogicfas408_ihandl);
+EXPORT_SYMBOL(qlogicfas408_get_chip_type);
+EXPORT_SYMBOL(qlogicfas408_setup);
+EXPORT_SYMBOL(qlogicfas408_detect);
+EXPORT_SYMBOL(qlogicfas408_disable_ints);
+
diff -Nru a/drivers/scsi/qlogicfas408.h b/drivers/scsi/qlogicfas408.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/qlogicfas408.h	Sun Apr 25 23:04:08 2004
@@ -0,0 +1,120 @@
+/* to be used by qlogicfas and qlogic_cs */
+#ifndef __QLOGICFAS408_H
+#define __QLOGICFAS408_H
+
+/*----------------------------------------------------------------*/
+/* Configuration */
+
+/* Set the following to max out the speed of the PIO PseudoDMA transfers,
+   again, 0 tends to be slower, but more stable.  */
+
+#define QL_TURBO_PDMA 1
+
+/* This should be 1 to enable parity detection */
+
+#define QL_ENABLE_PARITY 1
+
+/* This will reset all devices when the driver is initialized (during bootup).
+   The other linux drivers don't do this, but the DOS drivers do, and after
+   using DOS or some kind of crash or lockup this will bring things back
+   without requiring a cold boot.  It does take some time to recover from a
+   reset, so it is slower, and I have seen timeouts so that devices weren't
+   recognized when this was set. */
+
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+   Please set this for your card.  Most Qlogic cards are 40 Mhz.  The
+   Control Concepts ISA (not VLB) is 24 Mhz */
+
+#define XTALFREQ	40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+   proper termination.  The rest are for synchronous transfers and other
+   advanced features if your device can transfer faster than 5Mb/sec.
+   If you are really curious, email me for a quick howto until I have
+   something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+   For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0	/*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+   achievable data rate (assuming the rest of the system is capable
+   and set properly) */
+#define SYNCXFRPD 5	/*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
+/* This is the count of how many synchronous transfers can take place
+	i.e. how many reqs can occur before an ack is given.
+	The maximum value for this is 15, the upper bits can modify
+	REQ/ACK assertion and deassertion during synchronous transfers
+	If this is 0, the bus will only transfer asynchronously */
+#define SYNCOFFST 0
+/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
+	of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
+	cause the deassertion to be early by 1/2 clock.  Bits 5&4 control
+	the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
+
+/*----------------------------------------------------------------*/
+
+struct qlogicfas408_priv {
+	 int		qbase;		/* Port */
+	 int		qinitid;	/* initiator ID */
+	 int		qabort;		/* Flag to cause an abort */
+	 int		qlirq;		/* IRQ being used */
+	 int		int_type;	/* type of irq, 2 for ISA board, 0 for PCMCIA */
+	 char		qinfo[80];	/* description */
+	 Scsi_Cmnd 	*qlcmd;		/* current command being processed */
+	 struct Scsi_Host *shost;	/* pointer back to host */
+	 struct qlogicfas408_priv *next; /* next private struct */
+};
+
+/* The qlogic card uses two register maps - These macros select which one */
+#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | int_type, qbase + 0xd ))
+
+/* following is watchdog timeout in microseconds */
+#define WATCHDOG 5000000
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+   where something crashed or gets stuck at and as a simple profiler) */
+
+#define rtrc(i) {}
+
+#define get_priv_by_cmd(x) (struct qlogicfas408_priv *)&((x)->device->host->hostdata[0])
+#define get_priv_by_host(x) (struct qlogicfas408_priv *)&((x)->hostdata[0])
+
+irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id, struct pt_regs *regs);
+int qlogicfas408_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *));
+int qlogicfas408_biosparam(struct scsi_device * disk,
+		        struct block_device *dev,
+			sector_t capacity, int ip[]);
+int qlogicfas408_abort(Scsi_Cmnd * cmd);
+int qlogicfas408_bus_reset(Scsi_Cmnd * cmd);
+int qlogicfas408_host_reset(Scsi_Cmnd * cmd);
+int qlogicfas408_device_reset(Scsi_Cmnd * cmd);
+char *qlogicfas408_info(struct Scsi_Host *host);
+int qlogicfas408_get_chip_type(int qbase, int int_type);
+void qlogicfas408_setup(int qbase, int id, int int_type);
+int qlogicfas408_detect(int qbase, int int_type);
+void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv);
+#endif	/* __QLOGICFAS408_H */
+
diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/scsi_scan.c	Sun Apr 25 23:04:08 2004
@@ -543,17 +543,12 @@
 	 * 011 the same. Stay compatible with previous code, and create a
 	 * Scsi_Device for a PQ of 1
 	 *
-	 * XXX Save the PQ field let the upper layers figure out if they
-	 * want to attach or not to this device, do not set online FALSE;
-	 * otherwise, offline devices still get an sd allocated, and they
-	 * use up an sd slot.
-	 */
-	if (((inq_result[0] >> 5) & 7) == 1) {
-		SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: peripheral"
-				" qualifier of 1, device offlined\n"));
-		scsi_device_set_state(sdev, SDEV_OFFLINE);
-	}
+	 * Don't set the device offline here; rather let the upper
+	 * level drivers eval the PQ to decide whether they should
+	 * attach. So remove ((inq_result[0] >> 5) & 7) == 1 check.
+	 */ 
 
+	sdev->inq_periph_qual = (inq_result[0] >> 5) & 7;
 	sdev->removable = (0x80 & inq_result[1]) >> 7;
 	sdev->lockable = sdev->removable;
 	sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2);
diff -Nru a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
--- a/drivers/scsi/scsi_sysfs.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/scsi_sysfs.c	Sun Apr 25 23:04:08 2004
@@ -181,7 +181,8 @@
 /* all probing is done in the individual ->probe routines */
 static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
 {
-	return 1;
+	struct scsi_device *sdp = to_scsi_device(dev);
+	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
 }
 
 struct bus_type scsi_bus_type = {
diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/sd.c	Sun Apr 25 23:04:08 2004
@@ -179,7 +179,16 @@
 		goto out;
 	sdkp = scsi_disk(disk);
 	if (!kref_get(&sdkp->kref))
-		sdkp = NULL;
+		goto out_sdkp;
+	if (scsi_device_get(sdkp->device))
+		goto out_put;
+	up(&sd_ref_sem);
+	return sdkp;
+
+ out_put:
+	kref_put(&sdkp->kref);
+ out_sdkp:
+	sdkp = NULL;
  out:
 	up(&sd_ref_sem);
 	return sdkp;
@@ -188,6 +197,7 @@
 static void scsi_disk_put(struct scsi_disk *sdkp)
 {
 	down(&sd_ref_sem);
+	scsi_device_put(sdkp->device);
 	kref_put(&sdkp->kref);
 	up(&sd_ref_sem);
 }
@@ -1342,16 +1352,13 @@
 	if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD))
 		goto out;
 
-	if ((error = scsi_device_get(sdp)) != 0)
-		goto out;
-
 	SCSI_LOG_HLQUEUE(3, printk("sd_attach: scsi device: <%d,%d,%d,%d>\n", 
 			 sdp->host->host_no, sdp->channel, sdp->id, sdp->lun));
 
 	error = -ENOMEM;
 	sdkp = kmalloc(sizeof(*sdkp), GFP_KERNEL);
 	if (!sdkp)
-		goto out_put_sdev;
+		goto out;
 
 	memset (sdkp, 0, sizeof(*sdkp));
 	kref_init(&sdkp->kref, scsi_disk_release);
@@ -1427,8 +1434,6 @@
 	put_disk(gd);
 out_free:
 	kfree(sdkp);
-out_put_sdev:
-	scsi_device_put(sdp);
 out:
 	return error;
 }
@@ -1450,7 +1455,9 @@
 
 	del_gendisk(sdkp->disk);
 	sd_shutdown(dev);
-	scsi_disk_put(sdkp);
+	down(&sd_ref_sem);
+	kref_put(&sdkp->kref);
+	up(&sd_ref_sem);
 
 	return 0;
 }
@@ -1479,8 +1486,6 @@
 	put_disk(disk);
 
 	kfree(sdkp);
-
-	scsi_device_put(sdev);
 }
 
 /*
diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c
--- a/drivers/scsi/st.c	Sun Apr 25 23:04:08 2004
+++ b/drivers/scsi/st.c	Sun Apr 25 23:04:08 2004
@@ -17,7 +17,7 @@
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
  */
 
-static char *verstr = "20040318";
+static char *verstr = "20040403";
 
 #include <linux/module.h>
 
@@ -1031,7 +1031,8 @@
 
 	/* See that we have at least a one page buffer available */
 	if (!enlarge_buffer(STp->buffer, PAGE_SIZE, STp->restr_dma)) {
-		printk(KERN_WARNING "%s: Can't allocate tape buffer.\n", name);
+		printk(KERN_WARNING "%s: Can't allocate one page tape buffer.\n",
+		       name);
 		retval = (-EOVERFLOW);
 		goto err_out;
 	}
@@ -1318,6 +1319,8 @@
 			bufsize = count;
 		if (bufsize > STbp->buffer_size &&
 		    !enlarge_buffer(STbp, bufsize, STp->restr_dma)) {
+			printk(KERN_WARNING "%s: Can't allocate %d byte tape buffer.\n",
+			       tape_name(STp), bufsize);
 			retval = (-EOVERFLOW);
 			goto out;
 		}
@@ -1960,26 +1963,29 @@
 
 
 
+DEB(
 /* Set the driver options */
 static void st_log_options(Scsi_Tape * STp, ST_mode * STm, char *name)
 {
-	printk(KERN_INFO
-	       "%s: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
-	       name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
-	       STm->do_read_ahead);
-	printk(KERN_INFO
-	       "%s:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
-	       name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
-	printk(KERN_INFO
-	       "%s:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
-	       name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
-	       STp->scsi2_logical);
-	printk(KERN_INFO
-	       "%s:    sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
-        DEB(printk(KERN_INFO
-                   "%s:    debugging: %d\n",
-                   name, debugging);)
+	if (debugging) {
+		printk(KERN_INFO
+		       "%s: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+		       name, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+		       STm->do_read_ahead);
+		printk(KERN_INFO
+		       "%s:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+		       name, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+		printk(KERN_INFO
+		       "%s:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+		       name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+		       STp->scsi2_logical);
+		printk(KERN_INFO
+		       "%s:    sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
+		printk(KERN_INFO "%s:    debugging: %d\n",
+		       name, debugging);
+	}
 }
+	)
 
 
 static int st_set_options(Scsi_Tape *STp, long options)
@@ -2017,8 +2023,8 @@
 		STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
 		STp->immediate = (options & MT_ST_NOWAIT) != 0;
 		STm->sysv = (options & MT_ST_SYSV) != 0;
-		DEB( debugging = (options & MT_ST_DEBUGGING) != 0; )
-		st_log_options(STp, STm, name);
+		DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
+		     st_log_options(STp, STm, name); )
 	} else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
 		value = (code == MT_ST_SETBOOLEANS);
 		if ((options & MT_ST_BUFFER_WRITES) != 0)
@@ -2050,19 +2056,19 @@
 			STm->sysv = value;
                 DEB(
 		if ((options & MT_ST_DEBUGGING) != 0)
-			debugging = value; )
-		st_log_options(STp, STm, name);
+			debugging = value;
+			st_log_options(STp, STm, name); )
 	} else if (code == MT_ST_WRITE_THRESHOLD) {
 		/* Retained for compatibility */
 	} else if (code == MT_ST_DEF_BLKSIZE) {
 		value = (options & ~MT_ST_OPTIONS);
 		if (value == ~MT_ST_OPTIONS) {
 			STm->default_blksize = (-1);
-			printk(KERN_INFO "%s: Default block size disabled.\n", name);
+			DEBC( printk(KERN_INFO "%s: Default block size disabled.\n", name));
 		} else {
 			STm->default_blksize = value;
-			printk(KERN_INFO "%s: Default block size set to %d bytes.\n",
-			       name, STm->default_blksize);
+			DEBC( printk(KERN_INFO "%s: Default block size set to %d bytes.\n",
+			       name, STm->default_blksize));
 			if (STp->ready == ST_READY) {
 				STp->blksize_changed = FALSE;
 				set_mode_densblk(STp, STm);
@@ -2072,12 +2078,12 @@
 		value = (options & ~MT_ST_OPTIONS);
 		if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
 			STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
-			printk(KERN_INFO "%s: Long timeout set to %d seconds.\n", name,
-			       (value & ~MT_ST_SET_LONG_TIMEOUT));
+			DEBC( printk(KERN_INFO "%s: Long timeout set to %d seconds.\n", name,
+			       (value & ~MT_ST_SET_LONG_TIMEOUT)));
 		} else {
 			STp->timeout = value * HZ;
-			printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
-                               name, value);
+			DEBC( printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
+				name, value) );
 		}
 	} else if (code == MT_ST_SET_CLN) {
 		value = (options & ~MT_ST_OPTIONS) & 0xff;
@@ -2096,12 +2102,12 @@
 		if (code == MT_ST_DEF_DENSITY) {
 			if (value == MT_ST_CLEAR_DEFAULT) {
 				STm->default_density = (-1);
-				printk(KERN_INFO "%s: Density default disabled.\n",
-                                       name);
+				DEBC( printk(KERN_INFO "%s: Density default disabled.\n",
+                                       name));
 			} else {
 				STm->default_density = value & 0xff;
-				printk(KERN_INFO "%s: Density default set to %x\n",
-				       name, STm->default_density);
+				DEBC( printk(KERN_INFO "%s: Density default set to %x\n",
+				       name, STm->default_density));
 				if (STp->ready == ST_READY) {
 					STp->density_changed = FALSE;
 					set_mode_densblk(STp, STm);
@@ -2110,31 +2116,31 @@
 		} else if (code == MT_ST_DEF_DRVBUFFER) {
 			if (value == MT_ST_CLEAR_DEFAULT) {
 				STp->default_drvbuffer = 0xff;
-				printk(KERN_INFO
-                                       "%s: Drive buffer default disabled.\n", name);
+				DEBC( printk(KERN_INFO
+                                       "%s: Drive buffer default disabled.\n", name));
 			} else {
 				STp->default_drvbuffer = value & 7;
-				printk(KERN_INFO
+				DEBC( printk(KERN_INFO
                                        "%s: Drive buffer default set to %x\n",
-				       name, STp->default_drvbuffer);
+				       name, STp->default_drvbuffer));
 				if (STp->ready == ST_READY)
 					st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer);
 			}
 		} else if (code == MT_ST_DEF_COMPRESSION) {
 			if (value == MT_ST_CLEAR_DEFAULT) {
 				STm->default_compression = ST_DONT_TOUCH;
-				printk(KERN_INFO
-                                       "%s: Compression default disabled.\n", name);
+				DEBC( printk(KERN_INFO
+                                       "%s: Compression default disabled.\n", name));
 			} else {
 				if ((value & 0xff00) != 0) {
 					STp->c_algo = (value & 0xff00) >> 8;
-					printk(KERN_INFO "%s: Compression algorithm set to 0x%x.\n",
-					       name, STp->c_algo);
+					DEBC( printk(KERN_INFO "%s: Compression algorithm set to 0x%x.\n",
+					       name, STp->c_algo));
 				}
 				if ((value & 0xff) != 0xff) {
 					STm->default_compression = (value & 1 ? ST_YES : ST_NO);
-					printk(KERN_INFO "%s: Compression default set to %x\n",
-					       name, (value & 1));
+					DEBC( printk(KERN_INFO "%s: Compression default set to %x\n",
+					       name, (value & 1)));
 					if (STp->ready == ST_READY) {
 						STp->compression_changed = FALSE;
 						st_compression(STp, (STm->default_compression == ST_YES));
@@ -3465,7 +3471,7 @@
 	if (nbr <= 0)
 		return FALSE;
 
-	priority = GFP_KERNEL;
+	priority = GFP_KERNEL | __GFP_NOWARN;
 	if (need_dma)
 		priority |= GFP_DMA;
 	for (b_size = PAGE_SIZE, order=0;
@@ -3482,8 +3488,6 @@
 				order--;
 				continue;
 			}
-			printk(KERN_NOTICE "st: failed to enlarge buffer to %d bytes.\n",
-			       new_size);
 			DEB(STbuffer->buffer_size = got);
 			normalize_buffer(STbuffer);
 			return FALSE;
@@ -3495,9 +3499,6 @@
 		segs++;
 	}
 	STbuffer->b_data = page_address(STbuffer->frp[0].page);
-        DEBC(printk(ST_DEB_MSG
-                    "st: Succeeded to enlarge buffer at %p to %d bytes (segs %d->%d, %d).\n",
-                    STbuffer, got, STbuffer->orig_frp_segs, STbuffer->frp_segs, b_size));
 
 	return TRUE;
 }
@@ -3513,11 +3514,6 @@
 		__free_pages(STbuffer->frp[i].page, order);
 		STbuffer->buffer_size -= STbuffer->frp[i].length;
 	}
-        DEB(
-	if (debugging && STbuffer->orig_frp_segs < STbuffer->frp_segs)
-		printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes (segs %d->%d).\n",
-		       STbuffer, STbuffer->buffer_size, STbuffer->frp_segs, STbuffer->orig_frp_segs);
-        ) /* end DEB */
 	STbuffer->frp_segs = STbuffer->orig_frp_segs;
 	STbuffer->frp_sg_current = 0;
 }
@@ -3547,11 +3543,9 @@
 		ubp += cnt;
 		offset = 0;
 	}
-	if (do_count) {		/* Should never happen */
-		printk(KERN_WARNING "st: append_to_buffer overflow (left %d).\n",
-		       do_count);
+	if (do_count) /* Should never happen */
 		return (-EIO);
-	}
+
 	return 0;
 }
 
@@ -3581,11 +3575,9 @@
 		ubp += cnt;
 		offset = 0;
 	}
-	if (do_count) {		/* Should never happen */
-		printk(KERN_WARNING "st: from_buffer overflow (left %d).\n",
-		       do_count);
+	if (do_count) /* Should never happen */
 		return (-EIO);
-	}
+
 	return 0;
 }
 
@@ -3605,10 +3597,6 @@
 		if (src_offset < st_bp->frp[src_seg].length)
 			break;
 		offset -= st_bp->frp[src_seg].length;
-	}
-	if (src_seg == st_bp->frp_segs) {	/* Should never happen */
-		printk(KERN_WARNING "st: move_buffer offset overflow.\n");
-		return;
 	}
 
 	st_bp->buffer_bytes = st_bp->read_pointer = total;
diff -Nru a/include/scsi/scsi.h b/include/scsi/scsi.h
--- a/include/scsi/scsi.h	Sun Apr 25 23:04:08 2004
+++ b/include/scsi/scsi.h	Sun Apr 25 23:04:08 2004
@@ -362,6 +362,13 @@
 #define SCSI_2          3
 #define SCSI_3          4
 
+/*
+ * INQ PERIPHERAL QUALIFIERS
+ */
+#define SCSI_INQ_PQ_CON         0x00
+#define SCSI_INQ_PQ_NOT_CON     0x01
+#define SCSI_INQ_PQ_NOT_CAP     0x03
+
 
 /*
  * Here are some scsi specific ioctl commands which are sometimes useful.
diff -Nru a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
--- a/include/scsi/scsi_device.h	Sun Apr 25 23:04:08 2004
+++ b/include/scsi/scsi_device.h	Sun Apr 25 23:04:08 2004
@@ -63,6 +63,7 @@
 	char devfs_name[256];	/* devfs junk */
 	char type;
 	char scsi_level;
+	char inq_periph_qual;	/* PQ from INQUIRY data */	
 	unsigned char inquiry_len;	/* valid bytes in 'inquiry' */
 	unsigned char * inquiry;	/* INQUIRY response data */
 	char * vendor;		/* [back_compat] point into 'inquiry' ... */