patch-2.4.7 linux/drivers/message/fusion/mptscsih.c

Next file: linux/drivers/message/fusion/mptscsih.h
Previous file: linux/drivers/message/fusion/mptlan.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.6/linux/drivers/message/fusion/mptscsih.c linux/drivers/message/fusion/mptscsih.c
@@ -0,0 +1,2508 @@
+/*
+ *  linux/drivers/message/fusion/mptscsih.c
+ *      High performance SCSI / Fibre Channel SCSI Host device driver.
+ *      For use with PCI chip/adapter(s):
+ *          LSIFC9xx/LSI409xx Fibre Channel
+ *      running LSI Logic Fusion MPT (Message Passing Technology) firmware.
+ *
+ *  Credits:
+ *      This driver would not exist if not for Alan Cox's development
+ *      of the linux i2o driver.
+ *
+ *      A huge debt of gratitude is owed to David S. Miller (DaveM)
+ *      for fixing much of the stupid and broken stuff in the early
+ *      driver while porting to sparc64 platform.  THANK YOU!
+ *
+ *      (see mptbase.c)
+ *
+ *  Copyright (c) 1999-2001 LSI Logic Corporation
+ *  Original author: Steven J. Ralston
+ *  (mailto:Steve.Ralston@lsil.com)
+ *
+ *  $Id: mptscsih.c,v 1.24 2001/03/22 08:45:08 sralston Exp $
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    NO WARRANTY
+    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+    solely responsible for determining the appropriateness of using and
+    distributing the Program and assumes all risks associated with its
+    exercise of rights under this Agreement, including but not limited to
+    the risks and costs of program errors, damage to or loss of data,
+    programs or equipment, and unavailability or interruption of operations.
+
+    DISCLAIMER OF LIABILITY
+    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/blk.h>		/* for io_request_lock (spinlock) decl */
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "../../scsi/sd.h"
+
+#include "mptbase.h"
+#include "mptscsih.h"
+#include "isense.h"
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#define my_NAME		"Fusion MPT SCSI Host driver"
+#define my_VERSION	MPT_LINUX_VERSION_COMMON
+#define MYNAM		"mptscsih"
+
+MODULE_AUTHOR(MODULEAUTHOR);
+MODULE_DESCRIPTION(my_NAME);
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+typedef struct _BIG_SENSE_BUF {
+	u8		data[256];
+} BIG_SENSE_BUF;
+
+typedef struct _MPT_SCSI_HOST {
+	MPT_ADAPTER		 *ioc;
+	int			  port;
+	struct scsi_cmnd	**ScsiLookup;
+	u8			 *SgHunks;
+	dma_addr_t		  SgHunksDMA;
+	u32			  qtag_tick;
+	FCDEV_TRACKER		  TargetsQ;
+} MPT_SCSI_HOST;
+
+typedef struct _MPT_SCSI_DEV {
+	struct _MPT_SCSI_DEV	 *forw;
+	struct _MPT_SCSI_DEV	 *back;
+	MPT_ADAPTER		 *ioc;
+	int			  sense_sz;
+	BIG_SENSE_BUF		  CachedSense;
+	unsigned long		  io_cnt;
+	unsigned long		  read_cnt;
+} MPT_SCSI_DEV;
+
+/*
+ *  Other private/forward protos...
+ */
+
+static int	mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
+static void	mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
+static int	mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
+static int	mptscsih_io_direction(Scsi_Cmnd *cmd);
+static void	copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
+static u32	SCPNT_TO_MSGCTX(Scsi_Cmnd *sc);
+
+static int	mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
+
+
+static int	mpt_scsi_hosts = 0;
+static atomic_t	queue_depth;
+
+static int	ScsiDoneCtx = -1;
+static int	ScsiTaskCtx = -1;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28)
+static struct proc_dir_entry proc_mpt_scsihost =
+{
+	low_ino:	PROC_SCSI_MPT,
+	namelen:	8,
+	name:		"mptscsih",
+	mode:		S_IFDIR | S_IRUGO | S_IXUGO,
+	nlink:		2,
+};
+#endif
+
+#define SNS_LEN(scp)  sizeof((scp)->sense_buffer)
+
+#ifndef MPT_SCSI_USE_NEW_EH
+/*
+ *  Stuff to handle single-threading SCSI TaskMgmt
+ *  (abort/reset) requests...
+ */
+static spinlock_t mpt_scsih_taskQ_lock = SPIN_LOCK_UNLOCKED;
+static MPT_Q_TRACKER mpt_scsih_taskQ = {
+	(MPT_FRAME_HDR*) &mpt_scsih_taskQ,
+	(MPT_FRAME_HDR*) &mpt_scsih_taskQ
+};
+static int mpt_scsih_taskQ_cnt = 0;
+static int mpt_scsih_taskQ_bh_active = 0;
+static MPT_FRAME_HDR *mpt_scsih_active_taskmgmt_mf = NULL;
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	mptscsih_io_done - Main SCSI IO callback routine registered to
+ *	Fusion MPT (base) driver
+ *	@ioc: Pointer to MPT_ADAPTER structure
+ *	@mf: Pointer to original MPT request frame
+ *	@r: Pointer to MPT reply frame (NULL if TurboReply)
+ *
+ *	This routine is called from mpt.c::mpt_interrupt() at the completion
+ *	of any SCSI IO request.
+ *	This routine is registered with the Fusion MPT (base) driver at driver
+ *	load/init time via the mpt_register() API call.
+ *
+ *	Returns 1 indicating alloc'd request frame ptr should be freed.
+ */
+static int
+mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r)
+{
+	Scsi_Cmnd	*sc;
+	MPT_SCSI_HOST	*hd;
+	MPT_SCSI_DEV	*mpt_sdev = NULL;
+	u16		 req_idx;
+
+	if ((mf == NULL) ||
+	    (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
+		printk(KERN_ERR MYNAM ": ERROR! NULL or BAD req frame ptr (=%p)!\n", mf);
+		return 1;
+	}
+
+	hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+	req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+	sc = hd->ScsiLookup[req_idx];
+	hd->ScsiLookup[req_idx] = NULL;
+
+	dmfprintk((KERN_INFO MYNAM ": ScsiDone (req:sc:reply=%p:%p:%p)\n", mf, sc, r));
+
+	atomic_dec(&queue_depth);
+
+	/*
+	 *  Check for {1st} {IO} completion to "new" device.
+	 *  How do we know it's a new device?
+	 *  If we haven't set SDpnt->hostdata I guess...
+	 */
+	if (sc && sc->device) {
+		mpt_sdev = (MPT_SCSI_DEV*)sc->device->hostdata;
+		if (!mpt_sdev) {
+			dprintk((KERN_INFO MYNAM ": *NEW* SCSI device (%d:%d:%d)!\n",
+					   sc->device->id, sc->device->lun, sc->device->channel));
+			if ((sc->device->hostdata = kmalloc(sizeof(MPT_SCSI_DEV), GFP_ATOMIC)) == NULL) {
+				printk(KERN_ERR MYNAM ": ERROR: kmalloc(%d) FAILED!\n", (int)sizeof(MPT_SCSI_DEV));
+			} else {
+				memset(sc->device->hostdata, 0, sizeof(MPT_SCSI_DEV));
+				mpt_sdev = (MPT_SCSI_DEV *) sc->device->hostdata;
+				mpt_sdev->ioc = ioc;
+			}
+		} else {
+			if (++mpt_sdev->io_cnt && mptscsih_io_direction(sc) < 0) {
+				if (++mpt_sdev->read_cnt == 3) {
+					dprintk((KERN_INFO MYNAM ": 3rd DATA_IN, CDB[0]=%02x\n",
+							sc->cmnd[0]));
+				}
+			}
+#if 0
+			if (mpt_sdev->sense_sz) {
+				/*
+				 *  Completion of first IO down this path
+				 *  *should* invalidate device SenseData...
+				 */
+				mpt_sdev->sense_sz = 0;
+			}
+#endif
+		}
+	}
+
+#if 0
+{
+	MPT_FRAME_HDR	*mf_chk;
+
+	/* This, I imagine, is a costly check, but...
+	 *  If abort/reset active, check to see if this is a IO
+	 *  that completed while ABORT/RESET for it is waiting
+	 *  on our taskQ!
+	 */
+	if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) {
+		/* If ABORT for this IO is queued, zap it! */
+		mf_chk = search_taskQ(1,sc,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
+		if (mf_chk != NULL) {
+			sc->result = DID_ABORT << 16;
+			spin_lock_irqsave(&io_request_lock, flags);
+			sc->scsi_done(sc);
+			spin_unlock_irqrestore(&io_request_lock, flags);
+			return 1;
+		}
+	}
+}
+#endif
+
+	if (r != NULL && sc != NULL) {
+		SCSIIOReply_t	*pScsiReply;
+		SCSIIORequest_t *pScsiReq;
+		u16		 status;
+
+		pScsiReply = (SCSIIOReply_t *) r;
+		pScsiReq = (SCSIIORequest_t *) mf;
+
+		status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+
+		dprintk((KERN_NOTICE MYNAM ": Uh-Oh!  (req:sc:reply=%p:%p:%p)\n", mf, sc, r));
+		dprintk((KERN_NOTICE "  IOCStatus=%04xh, SCSIState=%02xh"
+				     ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
+				     status, pScsiReply->SCSIState, pScsiReply->SCSIStatus,
+				     le32_to_cpu(pScsiReply->IOCLogInfo)));
+
+		/*
+		 *  Look for + dump FCP ResponseInfo[]!
+		 */
+		if (pScsiReply->SCSIState & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
+			dprintk((KERN_NOTICE "  FCP_ResponseInfo=%08xh\n",
+					     le32_to_cpu(pScsiReply->ResponseInfo)));
+		}
+
+		switch(status) {
+		case MPI_IOCSTATUS_BUSY:			/* 0x0002 */
+			/*sc->result = DID_BUS_BUSY << 16;*/		/* YIKES! - Seems to
+									 * kill linux interrupt
+									 * handler
+									 */
+			sc->result = STS_BUSY;				/* Try SCSI BUSY! */
+			break;
+
+		case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:	/* 0x0040 */
+			/*  Not real sure here...  */
+			sc->result = DID_OK << 16;
+			break;
+
+		case MPI_IOCSTATUS_SCSI_INVALID_BUS:		/* 0x0041 */
+		case MPI_IOCSTATUS_SCSI_INVALID_TARGETID:	/* 0x0042 */
+			sc->result = DID_BAD_TARGET << 16;
+			break;
+
+		case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:	/* 0x0043 */
+			/*  Spoof to SCSI Selection Timeout!  */
+			sc->result = DID_NO_CONNECT << 16;
+			break;
+
+		case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:		/* 0x0045 */
+			/*
+			 *  YIKES!  I just discovered that SCSI IO which
+			 *  returns check condition, SenseKey=05 (ILLEGAL REQUEST)
+			 *  and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific),
+			 *  comes down this path!
+			 *  Do upfront check for valid SenseData and give it
+			 *  precedence!
+			 */
+			if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
+				copy_sense_data(sc, hd, mf, pScsiReply);
+				sc->result = pScsiReply->SCSIStatus;
+				break;
+			}
+
+			dprintk((KERN_NOTICE MYNAM ": sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow));
+			dprintk((KERN_NOTICE MYNAM ": ActBytesXferd=%02xh\n", le32_to_cpu(pScsiReply->TransferCount)));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+			sc->resid = sc->request_bufflen - le32_to_cpu(pScsiReply->TransferCount);
+			dprintk((KERN_NOTICE MYNAM ": SET sc->resid=%02xh\n", sc->resid));
+#endif
+
+#if 0
+			if (sc->underflow && (le32_to_cpu(pScsiReply->TransferCount) < sc->underflow)) {
+				sc->result = DID_ERROR << 16;
+				sc->resid = sc->request_bufflen - le32_to_cpu(pScsiReply->TransferCount);
+			} else {
+				sc->result = 0;
+			}
+#endif
+
+			/* workaround attempts... */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+			if (sc->resid >= 0x200) {
+				/* GRRRRR...
+				 *   //sc->result = DID_SOFT_ERROR << 16;
+				 * Try spoofing to BUSY
+				 */
+				sc->result = STS_BUSY;
+			} else {
+				sc->result = 0;
+			}
+#else
+			sc->result = 0;
+#endif
+			break;
+
+		case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:	/* 0x0048 */
+			sc->result = DID_ABORT << 16;
+			break;
+
+		case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:		/* 0x004B */
+		case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:		/* 0x004C */
+			sc->result = DID_RESET << 16;
+			break;
+
+		case MPI_IOCSTATUS_SUCCESS:			/* 0x0000 */
+			sc->result = pScsiReply->SCSIStatus;
+
+			if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
+				copy_sense_data(sc, hd, mf, pScsiReply);
+
+				/*  If running agains circa 200003dd 909 MPT f/w,
+				 *  may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
+				 *  (QUEUE_FULL) returned from device!	--> get 0x0000?128
+				 *  and with SenseBytes set to 0.
+				 */
+				if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
+					mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
+			}
+			else if (pScsiReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
+				/*
+				 *  What to do?
+				 */
+				sc->result = DID_SOFT_ERROR << 16;
+			}
+			else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
+				/*  Not real sure here either...  */
+				sc->result = DID_ABORT << 16;
+			}
+
+			if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
+				mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
+
+			break;
+
+		case MPI_IOCSTATUS_INVALID_FUNCTION:		/* 0x0001 */
+		case MPI_IOCSTATUS_INVALID_SGL:			/* 0x0003 */
+		case MPI_IOCSTATUS_INTERNAL_ERROR:		/* 0x0004 */
+		case MPI_IOCSTATUS_RESERVED:			/* 0x0005 */
+		case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES:	/* 0x0006 */
+		case MPI_IOCSTATUS_INVALID_FIELD:		/* 0x0007 */
+		case MPI_IOCSTATUS_INVALID_STATE:		/* 0x0008 */
+		case MPI_IOCSTATUS_SCSI_DATA_OVERRUN:		/* 0x0044 */
+		case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:		/* 0x0046 */
+		case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:		/* 0x0047 */
+		case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:	/* 0x0049 */
+		case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED:	/* 0x004A */
+		default:
+			/*
+			 *  What to do?
+			 */
+			sc->result = DID_SOFT_ERROR << 16;
+			break;
+
+		}	/* switch(status) */
+
+		dprintk((KERN_NOTICE MYNAM ": sc->result set to %08xh\n", sc->result));
+	}
+
+	if (sc != NULL) {
+		unsigned long flags;
+
+		/* Unmap the DMA buffers, if any. */
+		if (sc->use_sg) {
+			pci_unmap_sg(ioc->pcidev,
+				     (struct scatterlist *) sc->request_buffer,
+				     sc->use_sg,
+				     scsi_to_pci_dma_dir(sc->sc_data_direction));
+		} else if (sc->request_bufflen) {
+			pci_unmap_single(ioc->pcidev,
+					 (dma_addr_t)((long)sc->SCp.ptr),
+					 sc->request_bufflen,
+					 scsi_to_pci_dma_dir(sc->sc_data_direction));
+		}
+
+		spin_lock_irqsave(&io_request_lock, flags);
+		sc->scsi_done(sc);
+		spin_unlock_irqrestore(&io_request_lock, flags);
+	}
+
+	return 1;
+}
+
+#ifndef MPT_SCSI_USE_NEW_EH
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	search_taskQ - Search SCSI task mgmt request queue for specific
+ *			request type
+ *	@remove: (Boolean) Should request be removed if found?
+ *	@sc: Pointer to Scsi_Cmnd structure
+ *	@task_type: Task type to search for
+ *
+ *	Returns pointer to MPT request frame if found, or %NULL if request
+ *	was not found.
+ */
+static MPT_FRAME_HDR *
+search_taskQ(int remove, Scsi_Cmnd *sc, u8 task_type)
+{
+	MPT_FRAME_HDR *mf = NULL;
+	unsigned long flags;
+	int count = 0;
+	int list_sz;
+
+	dslprintk((KERN_INFO MYNAM ": spinlock#1\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	list_sz = mpt_scsih_taskQ_cnt;
+	if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) {
+		mf = mpt_scsih_taskQ.head;
+		do {
+			count++;
+			if (mf->u.frame.linkage.argp1 == sc &&
+			    mf->u.frame.linkage.arg1 == task_type) {
+				if (remove) {
+					Q_DEL_ITEM(&mf->u.frame.linkage);
+					mpt_scsih_taskQ_cnt--;
+				}
+				break;
+			}
+		} while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&mpt_scsih_taskQ);
+		if (mf == (MPT_FRAME_HDR*)&mpt_scsih_taskQ) {
+			mf = NULL;
+		}
+	}
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+	if (list_sz) {
+		dprintk((KERN_INFO MYNAM ": search_taskQ(%d,%p,%d) results=%p (%sFOUND%s)!\n",
+				   remove, sc, task_type,
+				   mf,
+				   mf ? "" : "NOT_",
+				   (mf && remove) ? "+REMOVED" : "" ));
+		dprintk((KERN_INFO MYNAM ": (searched thru %d of %d items on taskQ)\n",
+				   count,
+				   list_sz ));
+	}
+
+	return mf;
+}
+
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/*
+ *  Hack!  I'd like to report if a device is returning QUEUE_FULL
+ *  but maybe not each and every time...
+ */
+static long last_queue_full = 0;
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	mptscsih_report_queue_full - Report QUEUE_FULL status returned
+ *	from a SCSI target device.
+ *	@sc: Pointer to Scsi_Cmnd structure
+ *	@pScsiReply: Pointer to SCSIIOReply_t
+ *	@pScsiReq: Pointer to original SCSI request
+ *
+ *	This routine periodically reports QUEUE_FULL status returned from a
+ *	SCSI target device.  It reports this to the console via kernel
+ *	printk() API call, not more than once every 10 seconds.
+ */
+static void
+mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
+{
+	long time = jiffies;
+
+	if (time - last_queue_full > 10 * HZ) {
+		printk(KERN_WARNING MYNAM ": Device reported QUEUE_FULL!  SCSI bus:target:lun = %d:%d:%d\n",
+				0, sc->target, sc->lun);
+		last_queue_full = time;
+	}
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int BeenHereDoneThat = 0;
+
+/*  SCSI fops start here...  */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with
+ *	linux scsi mid-layer.
+ *	@tpnt: Pointer to Scsi_Host_Template structure
+ *
+ *	(linux Scsi_Host_Template.detect routine)
+ *
+ *	Returns number of SCSI host adapters that were successfully
+ *	registered with the linux scsi mid-layer via the scsi_register()
+ *	API call.
+ */
+int
+mptscsih_detect(Scsi_Host_Template *tpnt)
+{
+	struct Scsi_Host	*sh = NULL;
+	MPT_SCSI_HOST		*hd = NULL;
+	MPT_ADAPTER		*this;
+	unsigned long		 flags;
+	int			 sz;
+	u8			*mem;
+
+	if (! BeenHereDoneThat++) {
+		show_mptmod_ver(my_NAME, my_VERSION);
+
+		ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER);
+		ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER);
+
+#ifndef MPT_SCSI_USE_NEW_EH
+		Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR);
+		spin_lock_init(&mpt_scsih_taskQ_lock);
+#endif
+
+		if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) {
+			dprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n"));
+		} else {
+			/* FIXME! */
+		}
+	}
+
+	dprintk((KERN_INFO MYNAM ": mpt_scsih_detect()\n"));
+
+	this = mpt_adapter_find_first();
+	while (this != NULL) {
+		/* FIXME!  Multi-port (aka FC929) support...
+		 * for (i = 0; i < this->facts.NumberOfPorts; i++)
+		 */
+
+		/* 20010215 -sralston
+		 *  Added sanity check on SCSI Initiator-mode enabled
+		 *  for this MPT adapter.
+		 */
+		if (!(this->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) {
+			printk(KERN_ERR MYNAM ": Skipping %s because SCSI Initiator mode is NOT enabled!\n",
+					this->name);
+			this = mpt_adapter_find_next(this);
+			continue;
+		}
+
+		/* 20010202 -sralston
+		 *  Added sanity check on readiness of the MPT adapter.
+		 */
+		if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
+			printk(KERN_ERR MYNAM ": ERROR - Skipping %s because it's not operational!\n",
+					this->name);
+			this = mpt_adapter_find_next(this);
+			continue;
+		}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+		tpnt->proc_dir = &proc_mpt_scsihost;
+#endif
+		sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
+		if (sh != NULL) {
+			save_flags(flags);
+			cli();
+			sh->io_port = 0;
+			sh->n_io_port = 0;
+			sh->irq = 0;
+
+			/* Yikes!  This is important!
+			 * Otherwise, by default, linux only scans target IDs 0-7!
+			 */
+			sh->max_id = this->pfacts0.MaxDevices - 1;
+
+			sh->this_id = this->pfacts0.PortSCSIID;
+
+			restore_flags(flags);
+
+			hd = (MPT_SCSI_HOST *) sh->hostdata;
+			hd->ioc = this;
+			hd->port = 0;		/* FIXME! */
+
+			/* SCSI needs Scsi_Cmnd lookup table!
+			 * (with size equal to req_depth*PtrSz!)
+			 */
+			sz = hd->ioc->req_depth * sizeof(void *);
+			mem = kmalloc(sz, GFP_KERNEL);
+			if (mem == NULL)
+				return mpt_scsi_hosts;
+
+			memset(mem, 0, sz);
+			hd->ScsiLookup = (struct scsi_cmnd **) mem;
+
+			dprintk((KERN_INFO MYNAM ": ScsiLookup @ %p, sz=%d\n",
+				 hd->ScsiLookup, sz));
+
+			/* SCSI also needs SG buckets/hunk management!
+			 * (with size equal to N * req_sz * req_depth!)
+			 * (where N is number of SG buckets per hunk)
+			 */
+			sz = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth;
+			mem = pci_alloc_consistent(hd->ioc->pcidev, sz,
+						   &hd->SgHunksDMA);
+			if (mem == NULL)
+				return mpt_scsi_hosts;
+
+			memset(mem, 0, sz);
+			hd->SgHunks = (u8*)mem;
+
+			dprintk((KERN_INFO MYNAM ": SgHunks    @ %p(%08x), sz=%d\n",
+				 hd->SgHunks, hd->SgHunksDMA, sz));
+
+			hd->qtag_tick = jiffies;
+
+			this->sh = sh;
+			mpt_scsi_hosts++;
+		}
+		this = mpt_adapter_find_next(this);
+	}
+
+	return mpt_scsi_hosts;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+    static char *info_kbuf = NULL;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_release - Unregister SCSI host from linux scsi mid-layer
+ *	@host: Pointer to Scsi_Host structure
+ *
+ *	(linux Scsi_Host_Template.release routine)
+ *	This routine releases all resources associated with the SCSI host
+ *	adapter.
+ *
+ *	Returns 0 for success.
+ */
+int
+mptscsih_release(struct Scsi_Host *host)
+{
+	MPT_SCSI_HOST	*hd;
+#ifndef MPT_SCSI_USE_NEW_EH
+	unsigned long	 flags;
+
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	if (mpt_scsih_taskQ_bh_active) {
+		int count = 10 * HZ;
+
+		dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!\n"));
+
+		/* Zap the taskQ! */
+		Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR);
+		spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+		while(mpt_scsih_taskQ_bh_active && --count) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+		if (!count)
+			printk(KERN_ERR MYNAM ": ERROR! TaskMgmt thread still active!\n");
+	}
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+#endif
+
+	hd = (MPT_SCSI_HOST *) host->hostdata;
+	if (hd != NULL) {
+		int sz1, sz2;
+
+		sz1 = sz2 = 0;
+		if (hd->ScsiLookup != NULL) {
+			sz1 = hd->ioc->req_depth * sizeof(void *);
+			kfree(hd->ScsiLookup);
+			hd->ScsiLookup = NULL;
+		}
+
+		if (hd->SgHunks != NULL) {
+
+			sz2 = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth;
+			pci_free_consistent(hd->ioc->pcidev, sz2,
+					    hd->SgHunks, hd->SgHunksDMA);
+			hd->SgHunks = NULL;
+		}
+		dprintk((KERN_INFO MYNAM ": Free'd ScsiLookup (%d) and SgHunks (%d) memory\n", sz1, sz2));
+	}
+
+	if (mpt_scsi_hosts) {
+		if (--mpt_scsi_hosts == 0) {
+#if 0
+			mptscsih_flush_pending();
+#endif
+			mpt_event_deregister(ScsiDoneCtx);
+			dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n"));
+
+			mpt_deregister(ScsiDoneCtx);
+			mpt_deregister(ScsiTaskCtx);
+
+			if (info_kbuf != NULL)
+				kfree(info_kbuf);
+		}
+	}
+
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_info - Return information about MPT adapter
+ *	@SChost: Pointer to Scsi_Host structure
+ *
+ *	(linux Scsi_Host_Template.info routine)
+ *
+ *	Returns pointer to buffer where information was written.
+ */
+const char *
+mptscsih_info(struct Scsi_Host *SChost)
+{
+	MPT_SCSI_HOST *h;
+	int size = 0;
+
+	if (info_kbuf == NULL)
+		if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
+			return info_kbuf;
+
+	h = (MPT_SCSI_HOST *)SChost->hostdata;
+	info_kbuf[0] = '\0';
+	mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0);
+	info_kbuf[size-1] = '\0';
+
+	return info_kbuf;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+	static int max_qd = 1;
+#ifdef MPT_DEBUG
+	static int max_sges = 0;
+	static int max_xfer = 0;
+#endif
+#if 0
+	static int max_num_sges = 0;
+	static int max_sgent_len = 0;
+#endif
+#if 0
+static int index_log[128];
+static int index_ent = 0;
+static __inline__ void ADD_INDEX_LOG(int req_ent)
+{
+	int i = index_ent++;
+
+	index_log[i & (128 - 1)] = req_ent;
+}
+#else
+#define ADD_INDEX_LOG(req_ent)	do { } while(0)
+#endif
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
+ *	@SCpnt: Pointer to Scsi_Cmnd structure
+ *	@done: Pointer SCSI mid-layer IO completion function
+ *
+ *	(linux Scsi_Host_Template.queuecommand routine)
+ *	This is the primary SCSI IO start routine.  Create a MPI SCSIIORequest
+ *	from a linux Scsi_Cmnd request and send it to the IOC.
+ *
+ *	Returns 0. (rtn value discarded by linux scsi mid-layer)
+ */
+int
+mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+	struct Scsi_Host	*host;
+	MPT_SCSI_HOST		*hd;
+	MPT_FRAME_HDR		*mf;
+	SCSIIORequest_t		*pScsiReq;
+	int	 datadir;
+	u32	 len;
+	u32	 sgdir;
+	u32	 scsictl;
+	u32	 scsidir;
+	u32	 qtag;
+	u32	*mptr;
+	int	 sge_spill1;
+	int	 frm_sz;
+	int	 sges_left;
+	u32	 chain_offset;
+	int	 my_idx;
+	int	 i;
+
+	dmfprintk((KERN_INFO MYNAM "_qcmd: SCpnt=%p, done()=%p\n",
+		    SCpnt, done));
+
+	host = SCpnt->host;
+	hd = (MPT_SCSI_HOST *) host->hostdata;
+	
+#if 0
+	if (host->host_busy >= 60) {
+		MPT_ADAPTER *ioc = hd->ioc;
+		u16 pci_command, pci_status;
+
+		/* The IOC is probably hung, investigate status. */
+		printk("MPI: IOC probably hung IOCSTAT[%08x] INTSTAT[%08x] REPLYFIFO[%08x]\n",
+		       readl(&ioc->chip.fc9xx->DoorbellValue),
+		       readl(&ioc->chip.fc9xx->IntStatus),
+		       readl(&ioc->chip.fc9xx->ReplyFifo));
+		pci_read_config_word(ioc->pcidev, PCI_COMMAND, &pci_command);
+		pci_read_config_word(ioc->pcidev, PCI_STATUS, &pci_status);
+		printk("MPI: PCI command[%04x] status[%04x]\n", pci_command, pci_status);
+		{
+			/* DUMP req index logger. */
+			int begin, end;
+
+			begin = (index_ent - 65) & (128 - 1);
+			end = index_ent & (128 - 1);
+			printk("MPI: REQ_INDEX_HIST[");
+			while (begin != end) {
+				printk("(%04x)", index_log[begin]);
+				begin = (begin + 1) & (128 - 1);
+			}
+			printk("\n");
+		}
+		sti();
+		while(1)
+			barrier();
+	}
+#endif
+
+	SCpnt->scsi_done = done;
+
+	/* 20000617 -sralston
+	 *  GRRRRR...  Shouldn't have to do this but...
+	 *  Do explicit check for REQUEST_SENSE and cached SenseData.
+	 *  If yes, return cached SenseData.
+	 */
+#ifdef MPT_SCSI_CACHE_AUTOSENSE
+	{
+		MPT_SCSI_DEV	*mpt_sdev;
+
+		mpt_sdev = (MPT_SCSI_DEV *) SCpnt->device->hostdata;
+		if (mpt_sdev && SCpnt->cmnd[0] == REQUEST_SENSE) {
+			u8 *dest = NULL;
+
+			if (!SCpnt->use_sg)
+				dest = SCpnt->request_buffer;
+			else {
+				struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+				if (sg)
+					dest = (u8 *) (unsigned long)sg_dma_address(sg);
+			}
+
+			if (dest && mpt_sdev->sense_sz) {
+				memcpy(dest, mpt_sdev->CachedSense.data, mpt_sdev->sense_sz);
+#ifdef MPT_DEBUG
+				{
+					int  i;
+					u8  *sb;
+
+					sb = mpt_sdev->CachedSense.data;
+					if (sb && ((sb[0] & 0x70) == 0x70)) {
+						printk(KERN_WARNING MYNAM ": Returning last cached SCSI (hex) SenseData:\n");
+						printk(KERN_WARNING " ");
+						for (i = 0; i < (8 + sb[7]); i++)
+							printk("%s%02x", i == 13 ? "-" : " ", sb[i]);
+						printk("\n");
+					}
+				}
+#endif
+			}
+			SCpnt->resid = SCpnt->request_bufflen - mpt_sdev->sense_sz;
+			SCpnt->result = 0;
+/*			spin_lock(&io_request_lock);	*/
+			SCpnt->scsi_done(SCpnt);
+/*			spin_unlock(&io_request_lock);	*/
+			return 0;
+		}
+	}
+#endif
+
+	if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+/*		return 1;				*/
+		return 0;
+	}
+	pScsiReq = (SCSIIORequest_t *) mf;
+
+	my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+
+	ADD_INDEX_LOG(my_idx);
+
+	/* Map the data portion, if any. */
+	sges_left = SCpnt->use_sg;
+	if (sges_left) {
+		sges_left = pci_map_sg(hd->ioc->pcidev,
+				       (struct scatterlist *) SCpnt->request_buffer,
+				       sges_left,
+				       scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+	} else if (SCpnt->request_bufflen) {
+		dma_addr_t buf_dma_addr;
+
+		buf_dma_addr = pci_map_single(hd->ioc->pcidev,
+					      SCpnt->request_buffer,
+					      SCpnt->request_bufflen,
+					      scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+
+		/* We hide it here for later unmap. */
+		SCpnt->SCp.ptr = (char *)(unsigned long) buf_dma_addr;
+	}
+
+	/*
+	 *  Put together a MPT SCSI request...
+	 */
+
+	/* Assume SimpleQ, NO DATA XFER for now */
+
+	len = SCpnt->request_bufflen;
+	sgdir = 0x00000000;		/* SGL IN  (host<--ioc) */
+	scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
+
+	/*
+	 *  The scsi layer should be handling this stuff
+	 *  (In 2.3.x it does -DaveM)
+	 */
+
+	/*  BUG FIX!  19991030 -sralston
+	 *    TUR's being issued with scsictl=0x02000000 (DATA_IN)!
+	 *    Seems we may receive a buffer (len>0) even when there
+	 *    will be no data transfer!  GRRRRR...
+	 */
+	datadir = mptscsih_io_direction(SCpnt);
+	if (datadir < 0) {
+		scsidir = MPI_SCSIIO_CONTROL_READ;	/* DATA IN  (host<--ioc<--dev) */
+	} else if (datadir > 0) {
+		sgdir	= 0x04000000;			/* SGL OUT  (host-->ioc) */
+		scsidir = MPI_SCSIIO_CONTROL_WRITE;	/* DATA OUT (host-->ioc-->dev) */
+	} else {
+		len = 0;
+	}
+
+	qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
+
+	/*
+	 *  Attach tags to the devices
+	 */
+	if (SCpnt->device->tagged_supported) {
+		/*
+		 *  Some drives are too stupid to handle fairness issues
+		 *  with tagged queueing. We throw in the odd ordered
+		 *  tag to stop them starving themselves.
+		 */
+		if ((jiffies - hd->qtag_tick) > (5*HZ)) {
+			qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
+			hd->qtag_tick = jiffies;
+
+#if 0
+			/* These are ALWAYS zero!
+			 * (Because this is a place for the device driver to dynamically
+			 *  assign tag numbers any way it sees fit.  That's why -DaveM)
+			 */
+			dprintk((KERN_DEBUG MYNAM ": sc->device->current_tag = %08x\n",
+					SCpnt->device->current_tag));
+			dprintk((KERN_DEBUG MYNAM ": sc->tag                 = %08x\n",
+					SCpnt->tag));
+#endif
+		}
+#if 0
+		else {
+			/* Hmmm...  I always see value of 0 here,
+			 *  of which {HEAD_OF, ORDERED, SIMPLE} are NOT!  -sralston
+			 * (Because this is a place for the device driver to dynamically
+			 *  assign tag numbers any way it sees fit.  That's why -DaveM)
+			 *
+			 * if (SCpnt->tag == HEAD_OF_QUEUE_TAG)
+			 */
+			if (SCpnt->device->current_tag == HEAD_OF_QUEUE_TAG)
+				qtag = MPI_SCSIIO_CONTROL_HEADOFQ;
+			else if (SCpnt->tag == ORDERED_QUEUE_TAG)
+				qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
+		}
+#endif
+	}
+
+	scsictl = scsidir | qtag;
+
+	frm_sz = hd->ioc->req_sz;
+
+	/* Ack!
+	 * sge_spill1 = 9;
+	 */
+	sge_spill1 = (frm_sz - (sizeof(SCSIIORequest_t) - sizeof(SGEIOUnion_t) + sizeof(SGEChain32_t))) / 8;
+	/*  spill1: for req_sz == 128 (128-48==80, 80/8==10 SGEs max, first time!), --> use 9
+	 *  spill1: for req_sz ==  96 ( 96-48==48, 48/8== 6 SGEs max, first time!), --> use 5
+	 */
+	dsgprintk((KERN_INFO MYNAM ": SG: %x     spill1 = %d\n",
+		   my_idx, sge_spill1));
+
+#ifdef MPT_DEBUG
+	if (sges_left > max_sges) {
+		max_sges = sges_left;
+		dprintk((KERN_INFO MYNAM ": MPT_MaxSges = %d\n", max_sges));
+	}
+#endif
+#if 0
+	if (sges_left > max_num_sges) {
+		max_num_sges = sges_left;
+		printk(KERN_INFO MYNAM ": MPT_MaxNumSges = %d\n", max_num_sges);
+	}
+#endif
+
+	dsgprintk((KERN_INFO MYNAM ": SG: %x     sges_left = %d (initially)\n",
+		   my_idx, sges_left));
+
+	chain_offset = 0;
+	if (sges_left > (sge_spill1+1)) {
+#if 0
+		chain_offset = 0x1E;
+#endif
+		chain_offset = (frm_sz - 8) / 4;
+	}
+
+	pScsiReq->TargetID = SCpnt->target;
+	pScsiReq->Bus = hd->port;
+	pScsiReq->ChainOffset = chain_offset;
+	pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
+	pScsiReq->CDBLength = SCpnt->cmd_len;
+
+/* We have 256 bytes alloc'd per IO; let's use it. */
+/*	pScsiReq->SenseBufferLength = SNS_LEN(SCpnt);	*/
+	pScsiReq->SenseBufferLength = 255;
+
+	pScsiReq->Reserved = 0;
+	pScsiReq->MsgFlags = 0;
+	pScsiReq->LUN[0] = 0;
+	pScsiReq->LUN[1] = SCpnt->lun;
+	pScsiReq->LUN[2] = 0;
+	pScsiReq->LUN[3] = 0;
+	pScsiReq->LUN[4] = 0;
+	pScsiReq->LUN[5] = 0;
+	pScsiReq->LUN[6] = 0;
+	pScsiReq->LUN[7] = 0;
+	pScsiReq->Control = cpu_to_le32(scsictl);
+
+	/*
+	 *  Write SCSI CDB into the message
+	 */
+	for (i = 0; i < 12; i++)
+		pScsiReq->CDB[i] = SCpnt->cmnd[i];
+	for (i = 12; i < 16; i++)
+		pScsiReq->CDB[i] = 0;
+
+	/* DataLength */
+	pScsiReq->DataLength = cpu_to_le32(len);
+
+	/* SenseBuffer low address */
+	pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_pool_dma + (my_idx * 256));
+
+	mptr = (u32 *) &pScsiReq->SGL;
+
+	/*
+	 *  Now fill in the SGList...
+	 *  NOTES: For 128 byte req_sz, we can hold up to 10 simple SGE's
+	 *  in the remaining request frame.  We -could- do unlimited chains
+	 *  but each chain buffer can only be req_sz bytes in size, and
+	 *  we lose one SGE whenever we chain.
+	 *  For 128 req_sz, we can hold up to 16 SGE's per chain buffer.
+	 *  For practical reasons, limit ourselves to 1 overflow chain buffer;
+	 *  giving us 9 + 16 == 25 SGE's max.
+	 *  At 4 Kb per SGE, that yields 100 Kb max transfer.
+	 *
+	 *  (This code needs to be completely changed when/if 64-bit DMA
+	 *   addressing is used, since we will be able to fit much less than
+	 *   10 embedded SG entries. -DaveM)
+	 */
+	if (sges_left) {
+		struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+		u32  v1, v2;
+		int  sge_spill2;
+		int  sge_cur_spill;
+		int  sgCnt;
+		u8  *pSgBucket;
+		int  chain_sz;
+
+		len = 0;
+
+		/*	sge_spill2 = 15;
+		 *  spill2: for req_sz == 128 (128/8==16 SGEs max, first time!), --> use 15
+		 *  spill2: for req_sz ==  96 ( 96/8==12 SGEs max, first time!), --> use 11
+		 */
+		sge_spill2 = frm_sz / 8 - 1;
+		dsgprintk((KERN_INFO MYNAM ": SG: %x     spill2 = %d\n",
+			   my_idx, sge_spill2));
+
+		pSgBucket = NULL;
+		sgCnt = 0;
+		sge_cur_spill = sge_spill1;
+		while (sges_left) {
+#if 0
+			if (sg_dma_len(sg) > max_sgent_len) {
+				max_sgent_len = sg_dma_len(sg);
+				printk(KERN_INFO MYNAM ": MPT_MaxSgentLen = %d\n", max_sgent_len);
+			}
+#endif
+			/* Write one simple SGE */
+			v1 = sgdir | 0x10000000 | sg_dma_len(sg);
+			len += sg_dma_len(sg);
+			v2 = sg_dma_address(sg);
+			dsgprintk((KERN_INFO MYNAM ": SG: %x     Writing SGE @%p: %08x %08x, sges_left=%d\n",
+				   my_idx, mptr, v1, v2, sges_left));
+			*mptr++ = cpu_to_le32(v1);
+			*mptr++ = cpu_to_le32(v2);
+			sg++;
+			sgCnt++;
+
+			if (--sges_left == 0) {
+				/* re-write 1st word of previous SGE with SIMPLE,
+				 * LE, EOB, and EOL bits!
+				 */
+				v1 = 0xD1000000 | sgdir | sg_dma_len(sg-1);
+				dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (VERY LAST SGE!)\n",
+					   my_idx, mptr-2, v1));
+				*(mptr - 2) = cpu_to_le32(v1);
+			} else {
+				if ((sges_left > 1) && ((sgCnt % sge_cur_spill) == 0)) {
+					dsgprintk((KERN_INFO MYNAM ": SG: %x     SG spill at modulo 0!\n",
+						   my_idx));
+
+					/* Fixup previous SGE with LE bit! */
+					v1 = sgdir | 0x90000000 | sg_dma_len(sg-1);
+					dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (LAST BUCKET SGE!)\n",
+						   my_idx, mptr-2, v1));
+					*(mptr - 2) = cpu_to_le32(v1);
+
+					chain_offset = 0;
+					/* Going to need another chain? */
+					if (sges_left > (sge_spill2+1)) {
+#if 0
+						chain_offset = 0x1E;
+#endif
+						chain_offset = (frm_sz - 8) / 4;
+						chain_sz = frm_sz;
+					} else {
+						chain_sz = sges_left * 8;
+					}
+
+					/* write chain SGE at mptr. */
+					v1 = 0x30000000 | chain_offset<<16 | chain_sz;
+					if (pSgBucket == NULL) {
+						pSgBucket = hd->SgHunks
+							+ (my_idx * frm_sz * MPT_SG_BUCKETS_PER_HUNK);
+					} else {
+						pSgBucket += frm_sz;
+					}
+					v2 = (hd->SgHunksDMA +
+					      ((u8 *)pSgBucket - (u8 *)hd->SgHunks));
+					dsgprintk((KERN_INFO MYNAM ": SG: %x     Writing SGE @%p: %08x %08x (CHAIN!)\n",
+						   my_idx, mptr, v1, v2));
+					*(mptr++) = cpu_to_le32(v1);
+					*(mptr) = cpu_to_le32(v2);
+
+					mptr = (u32 *) pSgBucket;
+					sgCnt = 0;
+					sge_cur_spill = sge_spill2;
+				}
+			}
+		}
+	} else {
+		dsgprintk((KERN_INFO MYNAM ": SG: non-SG for %p, len=%d\n",
+			   SCpnt, SCpnt->request_bufflen));
+
+		if (len > 0) {
+			dma_addr_t buf_dma_addr;
+
+			buf_dma_addr = (dma_addr_t) (unsigned long)SCpnt->SCp.ptr;
+			*(mptr++) = cpu_to_le32(0xD1000000|sgdir|SCpnt->request_bufflen);
+			*(mptr++) = cpu_to_le32(buf_dma_addr);
+		}
+	}
+
+#ifdef MPT_DEBUG
+	/* if (SCpnt->request_bufflen > max_xfer) */
+	if (len > max_xfer) {
+		max_xfer = len;
+		dprintk((KERN_INFO MYNAM ": MPT_MaxXfer = %d\n", max_xfer));
+	}
+#endif
+
+	hd->ScsiLookup[my_idx] = SCpnt;
+
+	/* Main banana... */
+	mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
+
+	atomic_inc(&queue_depth);
+	if (atomic_read(&queue_depth) > max_qd) {
+		max_qd = atomic_read(&queue_depth);
+		dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd));
+	}
+
+	mb();
+	dmfprintk((KERN_INFO MYNAM ": Issued SCSI cmd (%p)\n", SCpnt));
+
+	return 0;
+}
+
+#ifdef MPT_SCSI_USE_NEW_EH		/* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+    mptscsih_abort
+    Returns: 0=SUCCESS, else FAILED
+*/
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ *
+ *	(linux Scsi_Host_Template.eh_abort_handler routine)
+ *
+ *	Returns SUCCESS or FAILED.  
+ */
+int
+mptscsih_abort(Scsi_Cmnd * SCpnt)
+{
+	MPT_FRAME_HDR	*mf;
+	SCSITaskMgmt_t	*pScsiTm;
+	MPT_SCSI_HOST	*hd;
+	u32		*msg;
+	u32		 ctx2abort;
+	int		 i;
+
+	printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO (=%p)\n", SCpnt);
+	printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+	hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+
+	if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+		return FAILED;
+	}
+
+	pScsiTm = (SCSITaskMgmt_t *) mf;
+	msg = (u32 *) mf;
+
+	pScsiTm->TargetID = SCpnt->target;
+	pScsiTm->Bus = hd->port;
+	pScsiTm->ChainOffset = 0;
+	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+
+	pScsiTm->Reserved = 0;
+	pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+	pScsiTm->Reserved1 = 0;
+	pScsiTm->MsgFlags = 0;
+
+	for (i = 0; i < 8; i++) {
+		u8 val = 0;
+		if (i == 1)
+			val = SCpnt->lun;
+		pScsiTm->LUN[i] = val;
+	}
+
+	for (i = 0; i < 7; i++)
+		pScsiTm->Reserved2[i] = 0;
+
+	/* Most important!  Set TaskMsgContext to SCpnt's MsgContext!
+	 * (the IO to be ABORT'd)
+	 *
+	 * NOTE: Since we do not byteswap MsgContext, we do not
+	 *	 swap it here either.  It is an opaque cookie to
+	 *	 the controller, so it does not matter. -DaveM
+	 */
+	ctx2abort = SCPNT_TO_MSGCTX(SCpnt);
+	dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort));
+	pScsiTm->TaskMsgContext = ctx2abort;
+
+	wmb();
+
+/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
+	mpt_put_msg_frame(hd->ioc->id, mf);
+*/
+/* FIXME!  Check return status! */
+	(void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg);
+
+	wmb();
+
+	return SUCCESS;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_bus_reset - Perform a SCSI BUS_RESET!	new_eh variant
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *
+ *	(linux Scsi_Host_Template.eh_bus_reset_handler routine)
+ *
+ *	Returns SUCCESS or FAILED.
+ */
+int
+mptscsih_bus_reset(Scsi_Cmnd * SCpnt)
+{
+	MPT_FRAME_HDR	*mf;
+	SCSITaskMgmt_t	*pScsiTm;
+	MPT_SCSI_HOST	*hd;
+	u32		*msg;
+	int		 i;
+
+	printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET (%p)\n", SCpnt);
+	printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+	hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+
+	if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+		return FAILED;
+	}
+
+	pScsiTm = (SCSITaskMgmt_t *) mf;
+	msg = (u32 *) mf;
+
+	pScsiTm->TargetID = SCpnt->target;
+	pScsiTm->Bus = hd->port;
+	pScsiTm->ChainOffset = 0;
+	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+
+	pScsiTm->Reserved = 0;
+	pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+	pScsiTm->Reserved1 = 0;
+	pScsiTm->MsgFlags = 0;
+
+	for (i = 0; i < 8; i++)
+		pScsiTm->LUN[i] = 0;
+
+	/* Control: No data direction, set task mgmt bit? */
+	for (i = 0; i < 7; i++)
+		pScsiTm->Reserved2[i] = 0;
+
+	pScsiTm->TaskMsgContext = cpu_to_le32(0);
+
+	wmb();
+
+/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
+	mpt_put_msg_frame(hd->ioc->id, mf);
+*/
+/* FIXME!  Check return status! */
+	(void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg);
+
+	wmb();
+
+	return SUCCESS;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_dev_reset - Perform a SCSI TARGET_RESET!  new_eh variant
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *
+ *	(linux Scsi_Host_Template.eh_dev_reset_handler routine)
+ *
+ *	Returns SUCCESS or FAILED.
+ */
+int
+mptscsih_dev_reset(Scsi_Cmnd * SCpnt)
+{
+	MPT_FRAME_HDR	*mf;
+	SCSITaskMgmt_t	*pScsiTm;
+	MPT_SCSI_HOST	*hd;
+	u32		*msg;
+	int		 i;
+
+	printk(KERN_WARNING MYNAM ": Attempting _TARGET_RESET (%p)\n", SCpnt);
+	printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+	hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+
+	if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+		return FAILED;
+	}
+
+	pScsiTm = (SCSITaskMgmt_t *) mf;
+	msg = (u32*)mf;
+
+	pScsiTm->TargetID = SCpnt->target;
+	pScsiTm->Bus = hd->port;
+	pScsiTm->ChainOffset = 0;
+	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+
+	pScsiTm->Reserved = 0;
+	pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+	pScsiTm->Reserved1 = 0;
+	pScsiTm->MsgFlags = 0;
+
+	/* _TARGET_RESET goes to LUN 0 always! */
+	for (i = 0; i < 8; i++)
+		pScsiTm->LUN[i] = 0;
+
+	/* Control: No data direction, set task mgmt bit? */
+	for (i = 0; i < 7; i++)
+		pScsiTm->Reserved2[i] = 0;
+
+	pScsiTm->TaskMsgContext = cpu_to_le32(0);
+
+	wmb();
+
+/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
+	mpt_put_msg_frame(hd->ioc->id, mf);
+*/
+/* FIXME!  Check return status! */
+	(void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg);
+
+	wmb();
+
+	return SUCCESS;
+}
+
+#if 0	/* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_host_reset - Perform a SCSI host adapter RESET!
+ *	new_eh variant
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *
+ *	(linux Scsi_Host_Template.eh_host_reset_handler routine)
+ *
+ *	Returns SUCCESS or FAILED.
+ */
+int
+mptscsih_host_reset(Scsi_Cmnd * SCpnt)
+{
+	return FAILED;
+}
+#endif	/* } */
+
+#else		/* MPT_SCSI old EH stuff... */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_old_abort - Abort linux Scsi_Cmnd routine
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ *
+ *	(linux Scsi_Host_Template.abort routine)
+ *
+ *	Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}.
+ */
+int
+mptscsih_old_abort(Scsi_Cmnd *SCpnt)
+{
+	MPT_SCSI_HOST		*hd;
+	MPT_FRAME_HDR		*mf;
+	struct tq_struct	*ptaskfoo;
+	unsigned long		 flags;
+
+	printk(KERN_WARNING MYNAM ": Scheduling _ABORT SCSI IO (=%p)\n", SCpnt);
+	printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+	if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
+		SCpnt->result = DID_ABORT << 16;
+		SCpnt->scsi_done(SCpnt);
+		return SCSI_ABORT_SUCCESS;
+	}
+
+	/*
+	 *  Check to see if there's already an ABORT queued for this guy.
+	 */
+	mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
+	if (mf != NULL) {
+		return SCSI_ABORT_PENDING;
+	}
+
+	if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+		return SCSI_ABORT_BUSY;
+	}
+
+	/*
+	 *  Add ourselves to (end of) mpt_scsih_taskQ.
+	 *  Check to see if our _bh is running.  If NOT, schedule it.
+	 */
+	dslprintk((KERN_INFO MYNAM ": spinlock#2\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+	mpt_scsih_taskQ_cnt++;
+	/* Yikes - linkage! */
+/*	SCpnt->host_scribble = (unsigned char *)mf;	*/
+	mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+	mf->u.frame.linkage.argp1 = SCpnt;
+	if (! mpt_scsih_taskQ_bh_active) {
+		mpt_scsih_taskQ_bh_active = 1;
+		/*
+		 *  Oh how cute, no alloc/free/mgmt needed if we use
+		 *  (bottom/unused portion of) MPT request frame.
+		 */
+		ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo));
+		ptaskfoo->sync = 0;
+		ptaskfoo->routine = mptscsih_taskmgmt_bh;
+		ptaskfoo->data = SCpnt;
+
+		SCHEDULE_TASK(ptaskfoo);
+	}
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+	return SCSI_ABORT_PENDING;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_old_reset - Perform a SCSI BUS_RESET!
+ *	@SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *	@reset_flags: (not used?)
+ *
+ *	(linux Scsi_Host_Template.reset routine)
+ *
+ *	Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}.
+ */
+int
+mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+	MPT_SCSI_HOST		*hd;
+	MPT_FRAME_HDR		*mf;
+	struct tq_struct	*ptaskfoo;
+	unsigned long		 flags;
+
+	printk(KERN_WARNING MYNAM ": Scheduling _BUS_RESET (=%p)\n", SCpnt);
+	printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+	if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
+		SCpnt->result = DID_RESET << 16;
+		SCpnt->scsi_done(SCpnt);
+		return SCSI_RESET_SUCCESS;
+	}
+
+	/*
+	 *  Check to see if there's already a BUS_RESET queued for this guy.
+	 */
+	mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS);
+	if (mf != NULL) {
+		return SCSI_RESET_PENDING;
+	}
+
+	if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+/*		SCpnt->result = DID_SOFT_ERROR << 16;	*/
+		SCpnt->result = STS_BUSY;
+		SCpnt->scsi_done(SCpnt);
+		return SCSI_RESET_PUNT;
+	}
+
+	/*
+	 *  Add ourselves to (end of) mpt_scsih_taskQ.
+	 *  Check to see if our _bh is running.  If NOT, schedule it.
+	 */
+	dslprintk((KERN_INFO MYNAM ": spinlock#3\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+	mpt_scsih_taskQ_cnt++;
+	/* Yikes - linkage! */
+/*	SCpnt->host_scribble = (unsigned char *)mf;	*/
+	mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+	mf->u.frame.linkage.argp1 = SCpnt;
+	if (! mpt_scsih_taskQ_bh_active) {
+		mpt_scsih_taskQ_bh_active = 1;
+		/*
+		 *  Oh how cute, no alloc/free/mgmt needed if we use
+		 *  (bottom/unused portion of) MPT request frame.
+		 */
+		ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo));
+		ptaskfoo->sync = 0;
+		ptaskfoo->routine = mptscsih_taskmgmt_bh;
+		ptaskfoo->data = SCpnt;
+
+		SCHEDULE_TASK(ptaskfoo);
+	}
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+	return SCSI_RESET_PENDING;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler
+ *	@sc: (unused)
+ *
+ *	This routine (thread) is active whenever there are any outstanding
+ *	SCSI task management requests for a SCSI host adapter.
+ *	IMPORTANT!  This routine is scheduled therefore should never be
+ *	running in ISR context.  i.e., it's safe to sleep here.
+ */
+void
+mptscsih_taskmgmt_bh(void *sc)
+{
+	Scsi_Cmnd	*SCpnt;
+	MPT_FRAME_HDR	*mf;
+	SCSITaskMgmt_t	*pScsiTm;
+	MPT_SCSI_HOST	*hd;
+	u32		 ctx2abort = 0;
+	int		 i;
+	unsigned long	 flags;
+	u8		 task_type;
+
+	dslprintk((KERN_INFO MYNAM ": spinlock#4\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	mpt_scsih_taskQ_bh_active = 1;
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+	while (1) {
+		/*
+		 *  We MUST remove item from taskQ *before* we format the
+		 *  frame as a SCSITaskMgmt request and send it down to the IOC.
+		 */
+		dslprintk((KERN_INFO MYNAM ": spinlock#5\n"));
+		spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+		if (Q_IS_EMPTY(&mpt_scsih_taskQ)) {
+			spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+			break;
+		}
+		mf = mpt_scsih_taskQ.head;
+		Q_DEL_ITEM(&mf->u.frame.linkage);
+		mpt_scsih_taskQ_cnt--;
+		mpt_scsih_active_taskmgmt_mf = mf;
+		spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+		SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1;
+		if (SCpnt == NULL) {
+			printk(KERN_ERR MYNAM ": ERROR: TaskMgmt has NULL SCpnt! (%p:%p)\n", mf, SCpnt);
+			continue;
+		}
+		pScsiTm = (SCSITaskMgmt_t *) mf;
+
+		for (i = 0; i < 8; i++) {
+			pScsiTm->LUN[i] = 0;
+		}
+
+		task_type = mf->u.frame.linkage.arg1;
+		if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+		{
+			printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO! (mf:sc=%p:%p)\n", mf, SCpnt);
+
+			/* Most important!  Set TaskMsgContext to SCpnt's MsgContext!
+			 * (the IO to be ABORT'd)
+			 *
+			 * NOTE: Since we do not byteswap MsgContext, we do not
+			 *	 swap it here either.  It is an opaque cookie to
+			 *	 the controller, so it does not matter. -DaveM
+			 */
+			ctx2abort = SCPNT_TO_MSGCTX(SCpnt);
+			pScsiTm->LUN[1] = SCpnt->lun;
+		}
+		else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
+		{
+			printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET! (against SCSI IO mf:sc=%p:%p)\n", mf, SCpnt);
+		}
+#if 0
+		else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {}
+		else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET) {}
+#endif
+
+		printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+		hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+
+		pScsiTm->TargetID = SCpnt->target;
+		pScsiTm->Bus = hd->port;
+		pScsiTm->ChainOffset = 0;
+		pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+
+		pScsiTm->Reserved = 0;
+		pScsiTm->TaskType = task_type;
+		pScsiTm->Reserved1 = 0;
+		pScsiTm->MsgFlags = 0;
+
+		for (i = 0; i < 7; i++)
+			pScsiTm->Reserved2[i] = 0;
+
+		dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort));
+		pScsiTm->TaskMsgContext = ctx2abort;
+
+		/* Control: No data direction, set task mgmt bit? */
+
+		/*
+		 *  As of MPI v0.10 this request can NOT be sent (normally)
+		 *  via FIFOs.	So we can't:
+		 *		mpt_put_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+		 *  SCSITaskMgmt requests MUST be sent ONLY via
+		 *  Doorbell/handshake now.   :-(
+		 *
+		 *  FIXME!  Check return status!
+		 */
+		(void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), (u32*)mf);
+
+		/* Spin-Wait for TaskMgmt complete!!! */
+		while (mpt_scsih_active_taskmgmt_mf != NULL) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ/2);
+		}
+	}
+
+	dslprintk((KERN_INFO MYNAM ": spinlock#6\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	mpt_scsih_taskQ_bh_active = 0;
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+
+	return;
+}
+
+#endif		/* } */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *	mptscsih_taskmgmt_complete - Callback routine, gets registered to
+ *	Fusion MPT base	driver
+ *	@ioc: Pointer to MPT_ADAPTER structure
+ *	@mf: Pointer to SCSI task mgmt request frame
+ *	@r: Pointer to SCSI task mgmt reply frame
+ *
+ *	This routine is called from mptbase.c::mpt_interrupt() at the completion
+ *	of any SCSI task management request.
+ *	This routine is registered with the MPT (base) driver at driver
+ *	load/init time via the mpt_register() API call.
+ *
+ *	Returns 1 indicating alloc'd request frame ptr should be freed.
+ */
+static int
+mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r)
+{
+	SCSITaskMgmtReply_t	*pScsiTmReply;
+	SCSITaskMgmt_t		*pScsiTmReq;
+	u8			 tmType;
+#ifndef MPT_SCSI_USE_NEW_EH
+	unsigned long		 flags;
+#endif
+
+	dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt completed mf=%p, r=%p\n",
+		 mf, r));
+
+#ifndef MPT_SCSI_USE_NEW_EH
+	dslprintk((KERN_INFO MYNAM ": spinlock#7\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	/* It better be the active one! */
+	if (mf != mpt_scsih_active_taskmgmt_mf) {
+		printk(KERN_ERR MYNAM ": ERROR! Non-active TaskMgmt (=%p) completed!\n", mf);
+		mpt_scsih_active_taskmgmt_mf = NULL;
+		spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+		return 1;
+	}
+
+#ifdef MPT_DEBUG
+	if ((mf == NULL) ||
+	    (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
+		printk(KERN_ERR MYNAM ": ERROR! NULL or BAD TaskMgmt ptr (=%p)!\n", mf);
+		mpt_scsih_active_taskmgmt_mf = NULL;
+		spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+		return 1;
+	}
+#endif
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+#endif
+
+	if (r != NULL) {
+		pScsiTmReply = (SCSITaskMgmtReply_t*)r;
+		pScsiTmReq = (SCSITaskMgmt_t*)mf;
+
+		/* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
+		tmType = pScsiTmReq->TaskType;
+
+		dprintk((KERN_INFO MYNAM ": TaskType = %d\n", tmType));
+		dprintk((KERN_INFO MYNAM ": TerminationCount = %d\n",
+			 le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+		/* Error?  (anything non-zero?) */
+		if (*(u32 *)&pScsiTmReply->Reserved2[0]) {
+			dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) - Oops!\n", tmType));
+			dprintk((KERN_INFO MYNAM ": IOCStatus = %04xh\n",
+				 le16_to_cpu(pScsiTmReply->IOCStatus)));
+			dprintk((KERN_INFO MYNAM ": IOCLogInfo = %08xh\n",
+				 le32_to_cpu(pScsiTmReply->IOCLogInfo)));
+		} else {
+			dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) SUCCESS!\n", tmType));
+		}
+	}
+
+#ifndef MPT_SCSI_USE_NEW_EH
+	/*
+	 *  Signal to _bh thread that we finished.
+	 */
+	dslprintk((KERN_INFO MYNAM ": spinlock#8\n"));
+	spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
+	mpt_scsih_active_taskmgmt_mf = NULL;
+	spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+#endif
+
+	return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	This is anyones guess quite frankly.
+ */
+
+int
+mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
+{
+	int size;
+
+	size = disk->capacity;
+	ip[0] = 64;				/* heads			*/
+	ip[1] = 32;				/* sectors			*/
+	if ((ip[2] = size >> 11) > 1024) {	/* cylinders, test for big disk */
+		ip[0] = 255;			/* heads			*/
+		ip[1] = 63;			/* sectors			*/
+		ip[2] = size / (255 * 63);	/* cylinders			*/
+	}
+	return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *  Private routines...
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* 19991030 -sralston
+ *  Return absolute SCSI data direction:
+ *     1 = _DATA_OUT
+ *     0 = _DIR_NONE
+ *    -1 = _DATA_IN
+ */
+static int
+mptscsih_io_direction(Scsi_Cmnd *cmd)
+{
+	switch (cmd->cmnd[0]) {
+	/*  _DATA_OUT commands	*/
+	case WRITE_6:		case WRITE_10:		case WRITE_12:
+	case WRITE_LONG:	case WRITE_SAME:	case WRITE_BUFFER:
+	case WRITE_VERIFY:	case WRITE_VERIFY_12:
+	case COMPARE:		case COPY:		case COPY_VERIFY:
+	case SEARCH_EQUAL:	case SEARCH_HIGH:	case SEARCH_LOW:
+	case SEARCH_EQUAL_12:	case SEARCH_HIGH_12:	case SEARCH_LOW_12:
+	case MODE_SELECT:	case MODE_SELECT_10:	case LOG_SELECT:
+	case SEND_DIAGNOSTIC:	case CHANGE_DEFINITION: case UPDATE_BLOCK:
+	case SET_WINDOW:	case MEDIUM_SCAN:	case SEND_VOLUME_TAG:
+	case REASSIGN_BLOCKS:
+	case PERSISTENT_RESERVE_OUT:
+	case 0xea:
+		return 1;
+
+	/*  No data transfer commands  */
+	case SEEK_6:		case SEEK_10:
+	case RESERVE:		case RELEASE:
+	case TEST_UNIT_READY:
+	case START_STOP:
+	case ALLOW_MEDIUM_REMOVAL:
+		return 0;
+
+	/*  Conditional data transfer commands	*/
+	case FORMAT_UNIT:
+		if (cmd->cmnd[1] & 0x10)	/* FmtData (data out phase)? */
+			return 1;
+		else
+			return 0;
+
+	case VERIFY:
+		if (cmd->cmnd[1] & 0x02)	/* VERIFY:BYTCHK (data out phase)? */
+			return 1;
+		else
+			return 0;
+
+	case RESERVE_10:
+		if (cmd->cmnd[1] & 0x03)	/* RESERSE:{LongID|Extent} (data out phase)? */
+			return 1;
+		else
+			return 0;
+
+#if 0
+	case REZERO_UNIT:	/* (or REWIND) */
+	case SPACE:
+	case ERASE:		case ERASE_10:
+	case SYNCHRONIZE_CACHE:
+	case LOCK_UNLOCK_CACHE:
+#endif
+
+	/*  Must be data _IN!  */
+	default:
+		return -1;
+	}
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static void
+copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply)
+{
+	MPT_SCSI_DEV	*mpt_sdev = NULL;
+	u32		 sense_count = le32_to_cpu(pScsiReply->SenseCount);
+	char		 devFoo[32];
+	IO_Info_t	 thisIo;
+
+	if (sc && sc->device)
+		mpt_sdev = (MPT_SCSI_DEV*) sc->device->hostdata;
+
+	if (sense_count) {
+		u8 *sense_data;
+		int req_index;
+
+		/* Copy the sense received into the scsi command block. */
+		req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+		sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * 256));
+		memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
+		/* Cache SenseData for this SCSI device! */
+		if (mpt_sdev) {
+			memcpy(mpt_sdev->CachedSense.data, sense_data, sense_count);
+			mpt_sdev->sense_sz = sense_count;
+		}
+	} else {
+		dprintk((KERN_INFO MYNAM ": Hmmm... SenseData len=0! (?)\n"));
+	}
+
+
+	thisIo.cdbPtr = sc->cmnd;
+	thisIo.sensePtr = sc->sense_buffer;
+	thisIo.SCSIStatus = pScsiReply->SCSIStatus;
+	thisIo.DoDisplay = 1;
+	sprintf(devFoo, "ioc%d,scsi%d:%d", hd->ioc->id, sc->target, sc->lun);
+	thisIo.DevIDStr = devFoo;
+/* fubar */
+	thisIo.dataPtr = NULL;
+	thisIo.inqPtr = NULL;
+	if (sc->device) {
+		thisIo.inqPtr = sc->device->vendor-8;		/* FIXME!!! */
+	}
+	(void) mpt_ScsiHost_ErrorReport(&thisIo);
+
+	return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static u32
+SCPNT_TO_MSGCTX(Scsi_Cmnd *sc)
+{
+	MPT_SCSI_HOST *hd;
+	MPT_FRAME_HDR *mf;
+	int i;
+
+	hd = (MPT_SCSI_HOST *) sc->host->hostdata;
+
+	for (i = 0; i < hd->ioc->req_depth; i++) {
+		if (hd->ScsiLookup[i] == sc) {
+			mf = MPT_INDEX_2_MFPTR(hd->ioc, i);
+			return mf->u.frame.hwhdr.msgctxu.MsgContext;
+		}
+	}
+
+	return -1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/* see mptscsih.h */
+
+#ifdef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS
+	static Scsi_Host_Template driver_template = MPT_SCSIHOST;
+#	include "../../scsi/scsi_module.c"
+#endif
+
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
+{
+	u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
+
+	dprintk((KERN_INFO MYNAM ": MPT event (=%02Xh) routed to SCSI host driver!\n", event));
+
+	switch (event) {
+	case MPI_EVENT_UNIT_ATTENTION:			/* 03 */
+		/* FIXME! */
+		break;
+	case MPI_EVENT_IOC_BUS_RESET:			/* 04 */
+		/* FIXME! */
+		break;
+	case MPI_EVENT_EXT_BUS_RESET:			/* 05 */
+		/* FIXME! */
+		break;
+	case MPI_EVENT_LOGOUT:				/* 09 */
+		/* FIXME! */
+		break;
+
+		/*
+		 *  CHECKME! Don't think we need to do
+		 *  anything for these, but...
+		 */
+	case MPI_EVENT_RESCAN:				/* 06 */
+	case MPI_EVENT_LINK_STATUS_CHANGE:		/* 07 */
+	case MPI_EVENT_LOOP_STATE_CHANGE:		/* 08 */
+		/*
+		 *  CHECKME!  Falling thru...
+		 */
+
+	case MPI_EVENT_NONE:				/* 00 */
+	case MPI_EVENT_LOG_DATA:			/* 01 */
+	case MPI_EVENT_STATE_CHANGE:			/* 02 */
+	case MPI_EVENT_EVENT_CHANGE:			/* 0A */
+	default:
+		dprintk((KERN_INFO MYNAM ": Ignoring event (=%02Xh)\n", event));
+		break;
+	}
+
+	return 1;		/* currently means nothing really */
+}
+
+#if 0		/* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *	scsiherr.c - Fusion MPT SCSI Host driver error handling/reporting.
+ *
+ *	drivers/message/fusion/scsiherr.c
+ */
+
+//extern const char	**mpt_ScsiOpcodesPtr;	/* needed by mptscsih.c */
+//extern ASCQ_Table_t	 *mpt_ASCQ_TablePtr;
+//extern int		  mpt_ASCQ_TableSz;
+
+/*  Lie!  */
+#define MYNAM	"mptscsih"
+
+#endif		/* } */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *  Private data...
+ */
+static ASCQ_Table_t *mptscsih_ASCQ_TablePtr;
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* old symsense.c stuff... */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Private data...
+ * To protect ourselves against those that would pass us bogus pointers
+ */
+static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES]
+    = { 0x1F, 0x00, 0x00, 0x00,
+	0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static u8 dummySenseData[SCSI_STD_SENSE_BYTES]
+    = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00 };
+static u8 dummyCDB[16]
+    = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static u8 dummyScsiData[16]
+    = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+#if 0
+static const char *PeripheralDeviceTypeString[32] = {
+	"Direct-access",		/* 00h */
+	"Sequential-access",		/* 01h */
+	"Printer",			/* 02h */
+	"Processor",			/* 03h */
+			/*"Write-Once-Read-Multiple",*/	/* 04h */
+	"WORM",				/* 04h */
+	"CD-ROM",			/* 05h */
+	"Scanner",			/* 06h */
+	"Optical memory",		/* 07h */
+	"Media Changer",		/* 08h */
+	"Communications",		/* 09h */
+	"(Graphics arts pre-press)",	/* 0Ah */
+	"(Graphics arts pre-press)",	/* 0Bh */
+	"Array controller",		/* 0Ch */
+	"Enclosure services",		/* 0Dh */
+	"Simplified direct-access",	/* 0Eh */
+	"Reserved-0Fh",			/* 0Fh */
+	"Reserved-10h",			/* 10h */
+	"Reserved-11h",			/* 11h */
+	"Reserved-12h",			/* 12h */
+	"Reserved-13h",			/* 13h */
+	"Reserved-14h",			/* 14h */
+	"Reserved-15h",			/* 15h */
+	"Reserved-16h",			/* 16h */
+	"Reserved-17h",			/* 17h */
+	"Reserved-18h",			/* 18h */
+	"Reserved-19h",			/* 19h */
+	"Reserved-1Ah",			/* 1Ah */
+	"Reserved-1Bh",			/* 1Bh */
+	"Reserved-1Ch",			/* 1Ch */
+	"Reserved-1Dh",			/* 1Dh */
+	"Reserved-1Eh",			/* 1Eh */
+	"Unknown"			/* 1Fh */
+};
+#endif
+
+static char *ScsiStatusString[] = {
+	"GOOD",					/* 00h */
+	NULL,					/* 01h */
+	"CHECK CONDITION",			/* 02h */
+	NULL,					/* 03h */
+	"CONDITION MET",			/* 04h */
+	NULL,					/* 05h */
+	NULL,					/* 06h */
+	NULL,					/* 07h */
+	"BUSY",					/* 08h */
+	NULL,					/* 09h */
+	NULL,					/* 0Ah */
+	NULL,					/* 0Bh */
+	NULL,					/* 0Ch */
+	NULL,					/* 0Dh */
+	NULL,					/* 0Eh */
+	NULL,					/* 0Fh */
+	"INTERMEDIATE",				/* 10h */
+	NULL,					/* 11h */
+	NULL,					/* 12h */
+	NULL,					/* 13h */
+	"INTERMEDIATE-CONDITION MET",		/* 14h */
+	NULL,					/* 15h */
+	NULL,					/* 16h */
+	NULL,					/* 17h */
+	"RESERVATION CONFLICT",			/* 18h */
+	NULL,					/* 19h */
+	NULL,					/* 1Ah */
+	NULL,					/* 1Bh */
+	NULL,					/* 1Ch */
+	NULL,					/* 1Dh */
+	NULL,					/* 1Eh */
+	NULL,					/* 1Fh */
+	NULL,					/* 20h */
+	NULL,					/* 21h */
+	"COMMAND TERMINATED",			/* 22h */
+	NULL,					/* 23h */
+	NULL,					/* 24h */
+	NULL,					/* 25h */
+	NULL,					/* 26h */
+	NULL,					/* 27h */
+	"TASK SET FULL",			/* 28h */
+	NULL,					/* 29h */
+	NULL,					/* 2Ah */
+	NULL,					/* 2Bh */
+	NULL,					/* 2Ch */
+	NULL,					/* 2Dh */
+	NULL,					/* 2Eh */
+	NULL,					/* 2Fh */
+	"ACA ACTIVE",				/* 30h */
+	NULL
+};
+
+static const char *ScsiCommonOpString[] = {
+	"TEST UNIT READY",			/* 00h */
+	"REZERO UNIT (REWIND)",			/* 01h */
+	NULL,					/* 02h */
+	"REQUEST_SENSE",			/* 03h */
+	"FORMAT UNIT (MEDIUM)",			/* 04h */
+	"READ BLOCK LIMITS",			/* 05h */
+	NULL,					/* 06h */
+	"REASSIGN BLOCKS",			/* 07h */
+	"READ(6)",				/* 08h */
+	NULL,					/* 09h */
+	"WRITE(6)",				/* 0Ah */
+	"SEEK(6)",				/* 0Bh */
+	NULL,					/* 0Ch */
+	NULL,					/* 0Dh */
+	NULL,					/* 0Eh */
+	"READ REVERSE",				/* 0Fh */
+	"WRITE_FILEMARKS",			/* 10h */
+	"SPACE(6)",				/* 11h */
+	"INQUIRY",				/* 12h */
+	NULL
+};
+
+static const char *SenseKeyString[] = {
+	"NO SENSE",				/* 0h */
+	"RECOVERED ERROR",			/* 1h */
+	"NOT READY",				/* 2h */
+	"MEDIUM ERROR",				/* 3h */
+	"HARDWARE ERROR",			/* 4h */
+	"ILLEGAL REQUEST",			/* 5h */
+	"UNIT ATTENTION",			/* 6h */
+	"DATA PROTECT",				/* 7h */
+	"BLANK CHECK",				/* 8h */
+	"VENDOR-SPECIFIC",			/* 9h */
+	"ABORTED COPY",				/* Ah */
+	"ABORTED COMMAND",			/* Bh */
+	"EQUAL (obsolete)",			/* Ch */
+	"VOLUME OVERFLOW",			/* Dh */
+	"MISCOMPARE",				/* Eh */
+	"RESERVED",				/* Fh */
+	NULL
+};
+
+#define SPECIAL_ASCQ(c,q) \
+	(((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70))
+
+#if 0
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *  Sense_Key_Specific() - If Sense_Key_Specific_Valid bit is set,
+ *			   then print additional information via
+ *			   a call to SDMS_SystemAlert().
+ *
+ *  Return: nothing
+ */
+static void Sense_Key_Specific(IO_Info_t *ioop, char *msg1)
+{
+	u8	*sd;
+	u8	 BadValue;
+	u8	 SenseKey;
+	int	 Offset;
+	int	 len = strlen(msg1);
+
+	sd = ioop->sensePtr;
+	if (SD_Additional_Sense_Length(sd) < 8)
+		return;
+
+	SenseKey = SD_Sense_Key(sd);
+
+	if (SD_Sense_Key_Specific_Valid(sd)) {
+		if (SenseKey == SK_ILLEGAL_REQUEST) {
+			Offset = SD_Bad_Byte(sd);
+			if (SD_Was_Illegal_Request(sd)) {
+				BadValue = ioop->cdbPtr[Offset];
+				len += sprintf(msg1+len, "\n  Illegal CDB value=%02Xh found at CDB ",
+						BadValue);
+		} else {
+			BadValue = ioop->dataPtr[Offset];
+			len += sprintf(msg1+len, "\n  Illegal DATA value=%02Xh found at DATA ",
+					BadValue);
+		}
+		len += sprintf(msg1+len, "byte=%02Xh", Offset);
+		if (SD_SKS_Bit_Pointer_Valid(sd))
+			len += sprintf(msg1+len, "/bit=%1Xh", SD_SKS_Bit_Pointer(sd));
+		} else if ((SenseKey == SK_RECOVERED_ERROR) ||
+			   (SenseKey == SK_HARDWARE_ERROR) ||
+			   (SenseKey == SK_MEDIUM_ERROR)) {
+			len += sprintf(msg1+len, "\n  Recovery algorithm Actual_Retry_Count=%02Xh",
+			SD_Actual_Retry_Count(sd));
+		}
+	}
+}
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int dump_cdb(char *foo, unsigned char *cdb)
+{
+	int i, grpCode, cdbLen;
+	int l = 0;
+
+	grpCode = cdb[0] >> 5;
+	if (grpCode < 1)
+		cdbLen = 6;
+	else if (grpCode < 3)
+		cdbLen = 10;
+	else if (grpCode == 5)
+		cdbLen = 12;
+	else
+		cdbLen = 16;
+
+	for (i=0; i < cdbLen; i++)
+		l += sprintf(foo+l, " %02X", cdb[i]);
+
+	return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int dump_sd(char *foo, unsigned char *sd)
+{
+	int snsLen = 8 + SD_Additional_Sense_Length(sd);
+	int l = 0;
+	int i;
+
+	for (i=0; i < MIN(snsLen,18); i++)
+		l += sprintf(foo+l, " %02X", sd[i]);
+	l += sprintf(foo+l, "%s", snsLen>18 ? " ..." : "");
+
+	return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*  Do ASC/ASCQ lookup/grindage to English readable string(s)  */
+static const char * ascq_set_strings_4max(
+		u8 ASC, u8 ASCQ,
+		const char **s1, const char **s2, const char **s3, const char **s4)
+{
+	static const char *asc_04_part1_string = "LOGICAL UNIT ";
+	static const char *asc_04_part2a_string = "NOT READY, ";
+	static const char *asc_04_part2b_string = "IS ";
+	static const char *asc_04_ascq_NN_part3_strings[] = {	/* ASC ASCQ (hex) */
+	  "CAUSE NOT REPORTABLE",				/* 04 00 */
+	  "IN PROCESS OF BECOMING READY",			/* 04 01 */
+	  "INITIALIZING CMD. REQUIRED",				/* 04 02 */
+	  "MANUAL INTERVENTION REQUIRED",			/* 04 03 */
+	  /* Add	" IN PROGRESS" to all the following... */
+	  "FORMAT",						/* 04 04 */
+	  "REBUILD",						/* 04 05 */
+	  "RECALCULATION",					/* 04 06 */
+	  "OPERATION",						/* 04 07 */
+	  "LONG WRITE",						/* 04 08 */
+	  "SELF-TEST",						/* 04 09 */
+	  NULL
+	};
+	static char *asc_04_part4_string = " IN PROGRESS";
+
+	static char *asc_29_ascq_NN_strings[] = {		/* ASC ASCQ (hex) */
+	  "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED",	/* 29 00 */
+	  "POWER ON OCCURRED",					/* 29 01 */
+	  "SCSI BUS RESET OCCURRED",				/* 29 02 */
+	  "BUS DEVICE RESET FUNCTION OCCURRED",			/* 29 03 */
+	  "DEVICE INTERNAL RESET",				/* 29 04 */
+	  "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED",		/* 29 05 */
+	  "TRANSCEIVER MODE CHANGED TO LVD",			/* 29 06 */
+	  NULL
+	};
+	static char *ascq_vendor_uniq = "(Vendor Unique)";
+	static char *ascq_noone = "(no matching ASC/ASCQ description found)";
+	int idx;
+
+	*s1 = *s2 = *s3 = *s4 = "";		/* set'em all to the empty "" string */
+
+	/* CHECKME! Need lock/sem?
+	 *  Update and examine for isense module presense.
+	 */
+	mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr;
+
+	if (mptscsih_ASCQ_TablePtr == NULL) {
+		/* 2nd chances... */
+		if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) {
+			*s1 = asc_04_part1_string;
+			*s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string;
+			*s3 = asc_04_ascq_NN_part3_strings[ASCQ];
+			/* check for " IN PROGRESS" ones */
+			if (ASCQ >= 0x04)
+				*s4 = asc_04_part4_string;
+		} else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1))
+			*s1 = asc_29_ascq_NN_strings[ASCQ];
+		/*
+		 *	else { leave all *s[1-4] values pointing to the empty "" string }
+		 */
+		return *s1;
+	}
+
+	/*
+	 *  Need to check ASC here; if it is "special," then
+	 *  the ASCQ is variable, and indicates failed component number.
+	 *  We must treat the ASCQ as a "don't care" while searching the
+	 *  mptscsih_ASCQ_Table[] by masking it off, and then restoring it later
+	 *  on when we actually need to identify the failed component.
+	 */
+	if (SPECIAL_ASCQ(ASC,ASCQ))
+		ASCQ = 0xFF;
+
+	/*  OK, now search mptscsih_ASCQ_Table[] for a matching entry  */
+	for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++)
+		if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ))
+			return (*s1 = mptscsih_ASCQ_TablePtr[idx].Description);
+
+	if ((ASC >= 0x80) || (ASCQ >= 0x80))
+		*s1 = ascq_vendor_uniq;
+	else
+		*s1 = ascq_noone;
+
+	return *s1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *  SCSI Error Report; desired output format...
+ *---
+SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0)
+  SCSI_Status=02h (CHECK CONDITION)
+  Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady
+  SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00
+  SenseKey=6h (UNIT ATTENTION); FRU=03h
+  ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"
+ *---
+ */
+
+int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop)
+{
+	char		 foo[512];
+	char		 buf2[32];
+	char		*statstr;
+	const char	*opstr;
+	int		 sk		= SD_Sense_Key(ioop->sensePtr);
+	const char	*skstr		= SenseKeyString[sk];
+	unsigned char	 asc		= SD_ASC(ioop->sensePtr);
+	unsigned char	 ascq		= SD_ASCQ(ioop->sensePtr);
+	int		 l;
+
+	/*
+	 *  More quiet mode.
+	 *  Filter out common, repetitive, warning-type errors...  like:
+	 *    POWER ON (06,29/00 or 06,29/01),
+	 *    SPINNING UP (02,04/01),
+	 *    LOGICAL UNIT NOT SUPPORTED (05,25/00), etc.
+	 */
+	if (	(sk==SK_UNIT_ATTENTION	&& asc==0x29 && (ascq==0x00 || ascq==0x01))
+	     || (sk==SK_NOT_READY	&& asc==0x04 && ascq==0x01)
+	     || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00)
+	   )
+	{
+		/* Do nothing! */
+		return 0;
+	}
+
+	/*
+	 *  Protect ourselves...
+	 */
+	if (ioop->cdbPtr == NULL)
+		ioop->cdbPtr = dummyCDB;
+	if (ioop->sensePtr == NULL)
+		ioop->sensePtr = dummySenseData;
+	if (ioop->inqPtr == NULL)
+		ioop->inqPtr = dummyInqData;
+	if (ioop->dataPtr == NULL)
+		ioop->dataPtr = dummyScsiData;
+
+	statstr = NULL;
+	if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) ||
+	    ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) {
+		(void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus);
+		statstr = buf2;
+	}
+
+	opstr = NULL;
+	if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*))
+		opstr = ScsiCommonOpString[ioop->cdbPtr[0]];
+	else if (mpt_ScsiOpcodesPtr)
+		opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]];
+
+	l = sprintf(foo, "SCSI Error Report =-=-= (%s)\n"
+	  "  SCSI_Status=%02Xh (%s)\n"
+	  "  Original_CDB[]:",
+			ioop->DevIDStr,
+			ioop->SCSIStatus,
+			statstr);
+	l += dump_cdb(foo+l, ioop->cdbPtr);
+	if (opstr)
+		l += sprintf(foo+l, " - \"%s\"", opstr);
+	l += sprintf(foo+l, "\n  SenseData[%02Xh]:", 8+SD_Additional_Sense_Length(ioop->sensePtr));
+	l += dump_sd(foo+l, ioop->sensePtr);
+	l += sprintf(foo+l, "\n  SenseKey=%Xh (%s); FRU=%02Xh\n  ASC/ASCQ=%02Xh/%02Xh",
+			sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq );
+
+	{
+		const char	*x1, *x2, *x3, *x4;
+		x1 = x2 = x3 = x4 = "";
+		x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4);
+		if (x1 != NULL) {
+			if (x1[0] != '(')
+				l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4);
+			else
+				l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4);
+		}
+	}
+
+#if 0
+	if (SPECIAL_ASCQ(asc,ascq))
+		l += sprintf(foo+l, " (%02Xh)", ascq);
+#endif
+
+	PrintF(("%s\n", foo));
+
+	return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)