/*
 *  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.29.4.1 2001/09/18 03:22:30 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>
#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);
MODULE_LICENSE("GPL");


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

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;
} 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_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
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(sc->host->host_lock, flags);
			sc->scsi_done(sc);
			spin_unlock_irqrestore(sc->host->host_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 (pScsiReq->CDB[0] == INQUIRY) {
				sc->result = (DID_OK << 16);
				break;
			}

			/* 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(sc->host->host_lock, flags);
		sc->scsi_done(sc);
		spin_unlock_irqrestore(sc->host->host_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);

		if ((ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER)) <= 0) {
			printk(KERN_ERR MYNAM ": Failed to register callback1 with MPT base driver\n");
			return mpt_scsi_hosts;
		}
		if ((ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER)) <= 0) {
			printk(KERN_ERR MYNAM ": Failed to register callback2 with MPT base driver\n");
			return mpt_scsi_hosts;
		}

#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! */
		}

		if (mpt_reset_register(ScsiDoneCtx, mptscsih_ioc_reset) == 0) {
			dprintk((KERN_INFO MYNAM ": Registered for IOC reset 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->pfacts[0].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!
			 *
			 * BUG FIX!  20010618 -sralston & pdelaney
			 * FC919 testing was encountering "duplicate" FC devices,
			 * as it turns out because the 919 was returning 512
			 * for PortFacts.MaxDevices, causing a wraparound effect
			 * in SCSI IO requests.  So instead of using:
			 *     sh->max_id = this->pfacts[0].MaxDevices - 1
			 * we'll use a definitive max here.
			 */
			sh->max_id = MPT_MAX_FC_DEVICES;

			sh->this_id = this->pfacts[0].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_reset_deregister(ScsiDoneCtx);
			dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n"));

			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, 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(SCpnt->host->host_lock);	*/
			SCpnt->scsi_done(SCpnt);
/*			spin_unlock(SCpnt->host->host_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));
	}

	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;
	unsigned long	 flags;

	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);
	if (ctx2abort == -1) {
		printk(KERN_ERR MYNAM ": ERROR - ScsiLookup fail(#2) for SCpnt=%p\n", SCpnt);
		SCpnt->result = DID_SOFT_ERROR << 16;
		spin_lock_irqsave(SCpnt->host->host_lock, flags);
		SCpnt->scsi_done(SCpnt);
		spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
		mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
	} else {
		dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort));
		pScsiTm->TaskMsgContext = ctx2abort;


		/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
			mpt_put_msg_frame(hd->ioc->id, mf);
		*/
		if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
					sizeof(SCSITaskMgmt_t), msg))
		    != 0) {
			printk(KERN_WARNING MYNAM
					": WARNING[2] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
					i, mf, SCpnt);
			SCpnt->result = DID_SOFT_ERROR << 16;
			spin_lock_irqsave(SCpnt->host->host_lock, flags);
			SCpnt->scsi_done(SCpnt);
			spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
			mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
		}
	}

	//return SUCCESS;
	return FAILED;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
 *	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;
	unsigned long	 flags;

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

/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
	mpt_put_msg_frame(hd->ioc->id, mf);
*/
/* FIXME!  Check return status! */
	if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
				sizeof(SCSITaskMgmt_t), msg))
	    != 0) {
		printk(KERN_WARNING MYNAM
				": WARNING[3] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
				i, mf, SCpnt);
		SCpnt->result = DID_SOFT_ERROR << 16;
		spin_lock_irqsave(SCpnt->host->host_lock, flags);
		SCpnt->scsi_done(SCpnt);
		spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
		mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
	}

	//return SUCCESS;
	return FAILED;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
 *	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;
	unsigned long	 flags;

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

/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
	mpt_put_msg_frame(hd->ioc->id, mf);
*/
/* FIXME!  Check return status! */
	if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
				sizeof(SCSITaskMgmt_t), msg))
	    != 0) {
		printk(KERN_WARNING MYNAM
				": WARNING[4] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
				i, mf, SCpnt);
		SCpnt->result = DID_SOFT_ERROR << 16;
		spin_lock_irqsave(SCpnt->host->host_lock, flags);
		SCpnt->scsi_done(SCpnt);
		spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
		mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
	}

	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) {
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(HZ/4);

		/*
		 *  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;
		}
		hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
		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=%p:sc=%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);
			if (ctx2abort == -1) {
				printk(KERN_ERR MYNAM ": ERROR - ScsiLookup fail(#1) for SCpnt=%p\n", SCpnt);
				SCpnt->result = DID_SOFT_ERROR << 16;
				spin_lock_irqsave(SCpnt->host->host_lock, flags);
				SCpnt->scsi_done(SCpnt);
				spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
				mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
				continue;
			}
			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=%p:sc=%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));

		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.   :-(
		 */
		if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
					sizeof(SCSITaskMgmt_t), (u32*) mf))
		    != 0) {
			printk(KERN_WARNING MYNAM ": WARNING[1] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n", i, mf, SCpnt);
			SCpnt->result = DID_SOFT_ERROR << 16;
			spin_lock_irqsave(SCpnt->host->host_lock, flags);
			SCpnt->scsi_done(SCpnt);
			spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
			mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
		} else {
			/* Spin-Wait for TaskMgmt complete!!! */
			while (mpt_scsih_active_taskmgmt_mf != NULL) {
				current->state = TASK_INTERRUPTIBLE;
				schedule_timeout(HZ/4);
			}
		}
	}

	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_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
{
	dprintk((KERN_INFO MYNAM ": IOC %s_reset routed to SCSI host driver!\n",
			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));

	if (reset_phase == MPT_IOC_PRE_RESET) {
		/* FIXME! Do pre-reset cleanup */
	} else {
		/* FIXME! Do post-reset cleanup */
	}

	return 1;		/* currently means nothing really */
}

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

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

