/*
 *  mpt_scst.c
 *
 *  Copyright (C) 2005 Beijing Soul Technology Co., Ltd.
 *  Copyright (C) 2002, 2003, 2004 LSI Logic Corporation
 *  Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
 *  Copyright (C) 2004 Leonid Stoljar
 *
 *  MPT SCSI target mode driver for SCST.
 *
 *  Originally   By: Stephen Shirron
 *  Port to SCST By: Hu Gang <hugang@soulinfo.com>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <scsi/scsi.h>
#include <linux/seq_file.h>
#include <scsi/scsi_host.h>

#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)
#include <linux/pci.h>
#endif

#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#include <scst/scst_debug.h>
#else
#include "scst.h"
#include <scst_debug.h>
#endif

#include "mpt_scst.h"

#define MYNAM "mpt_scst"

#ifdef CONFIG_SCST_TRACING
static int trace_mpi = 0;

#define TRACE_MPI	0x80000000

#endif

#ifdef CONFIG_SCST_DEBUG
static char *mpt_state_string[] = {
	"0",
	"new",
	"need data",
	"data in",
	"data out",
	"processed",
	"NULL",
};
#endif

#ifdef CONFIG_SCST_DEBUG
#define SCST_DEFAULT_MPT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
        TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_MINOR | \
        TRACE_MGMT_DEBUG | TRACE_MINOR | TRACE_SPECIAL)
#else
# ifdef CONFIG_SCST_TRACING
#define SCST_DEFAULT_MPT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
        TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR | TRACE_SPECIAL)
# endif
#endif

static MPT_STM_PRIV *mpt_stm_priv[MPT_MAX_ADAPTERS+1];

static int set_aliases_in_fcportpage1 = 1;
static int num_aliases = 0;
static int stm_context = 0;

static int mpt_stm_adapter_online(MPT_STM_PRIV *priv);
static void mpt_stm_adapter_dispose(MPT_STM_PRIV *priv);
static int mpt_stm_adapter_install(MPT_ADAPTER *ioc);

static int __init _mpt_stm_init(void);

static void stmapp_set_status(MPT_STM_PRIV *priv, CMD *cmd, int status);
static void stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word);
static void stm_cmd_buf_post(MPT_STM_PRIV *priv, int index);

static void stm_tgt_reply_high_pri(MPT_ADAPTER *ioc,
		TargetCmdBufferPostErrorReply_t *rep);
static void stm_target_reply_error(MPT_ADAPTER *ioc, TargetErrorReply_t *rep);
static void stmapp_target_error(MPT_STM_PRIV *priv, u32 reply_word, int index,
		int status, int reason);
static void stm_link_service_reply(MPT_ADAPTER *ioc,
		       LinkServiceBufferPostReply_t *rep);
static void stm_link_service_rsp_reply(MPT_ADAPTER *ioc,
			   LinkServiceRspRequest_t *req, LinkServiceRspReply_t *rep);
static void stmapp_set_sense_info(MPT_STM_PRIV *priv,
		      CMD *cmd, int sense_key, int asc, int ascq);
static void stmapp_srr_adjust_offset(MPT_STM_PRIV *priv, int index);
static void stmapp_srr_convert_ta_to_tss(MPT_STM_PRIV *priv, int index);
static void stmapp_abts_process(MPT_STM_PRIV *priv,
		    int rx_id, LinkServiceBufferPostReply_t *rep, int index);
static int stm_do_config_action(MPT_STM_PRIV *priv,
		     int action, int type, int number, int address, int length,
		     int sleep);
static int stm_get_config_page(MPT_STM_PRIV *priv,
		    int type, int number, int address, int sleep);
static int stm_set_config_page(MPT_STM_PRIV *priv,
		    int type, int number, int address, int sleep);
static void stm_cmd_buf_post_list(MPT_STM_PRIV *priv, int index);
static int stm_send_target_status(MPT_STM_PRIV *priv,
		       u32 reply_word, int index, int flags, int lun, int tag);
static void stm_send_els(MPT_STM_PRIV *priv, LinkServiceBufferPostReply_t *rep,
	     int index, int length);
static void stm_link_serv_buf_post(MPT_STM_PRIV *priv, int index);

static void stm_wait(MPT_STM_PRIV *priv, int milliseconds, int sleep);
static int stm_wait_for(MPT_STM_PRIV *priv, volatile int *flag, int seconds,
	     int sleep);
static void stmapp_srr_process(MPT_STM_PRIV *priv, int rx_id, int r_ctl,
		u32 offset, LinkServiceBufferPostReply_t *rep, int index);
static void stm_set_scsi_port_page1(MPT_STM_PRIV *priv, int sleep);

#ifdef CONFIG_SCST_DEBUG
#define trace_flag mpt_trace_flag
unsigned long mpt_trace_flag = TRACE_FUNCTION | TRACE_OUT_OF_MEM | TRACE_SPECIAL;
#else
# ifdef CONFIG_SCST_TRACING
#define trace_flag mpt_trace_flag
unsigned long mpt_trace_flag = TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_SPECIAL;
# endif
#endif

static int
mpt_target_show(struct seq_file *seq, void *v)
{
	struct mpt_tgt *tgt = (struct mpt_tgt *)seq->private;
	MPT_ADAPTER *ioc = tgt->priv->ioc;
	MPT_STM_PRIV *priv = tgt->priv;

	TRACE_ENTRY();
	TRACE_DBG("priv %p, tgt %p", priv, tgt);

	sBUG_ON(tgt == NULL);
	sBUG_ON(ioc == NULL);

	seq_printf(seq, "ProductID        :0x%04x (%s)\n"
			"Target Enable    :%s\n",
			ioc->facts.ProductID,
			ioc->prod_name,
			tgt->target_enable ? "True" : "False");

#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15))
  	if (ioc->bus_type == SCSI) {
#else
	if (ioc->bus_type == SPI) {
#endif
		int i = 0;
		seq_printf(seq, "Target ID        :%d\n"
				"Capabilities     :0x%x\n"
				"PhysicalInterface:0x%x\n",
				priv->port_id,
				priv->SCSIPortPage0.Capabilities,
				priv->SCSIPortPage0.PhysicalInterface);

		seq_printf(seq, "Configuration    :0x%x\n"
				"OnBusTimerValue  :0x%x\n"
				"TargetConfig     :0x%x\n"
				"IDConfig         :0x%x\n",
				priv->SCSIPortPage1.Configuration,
				priv->SCSIPortPage1.OnBusTimerValue,
				priv->SCSIPortPage1.TargetConfig,
				priv->SCSIPortPage1.IDConfig);

		seq_printf(seq, "PortFlags        :0x%x\n"
				"PortSettings     :0x%x\n",
				priv->SCSIPortPage2.PortFlags,
				priv->SCSIPortPage2.PortSettings);
#if 0
		for (i = 0; i < 16; i++) {
			seq_printf(seq, " DeviceSeting %02d: 0x%x 0x%x 0x%x\n",
					priv->SCSIPortPage2.DeviceSettings[i].Timeout,
					priv->SCSIPortPage2.DeviceSettings[i].SyncFactor,
					priv->SCSIPortPage2.DeviceSettings[i].DeviceFlags);
		}
#endif
		for (i = 0; i < NUM_SCSI_DEVICES; i++) {
			seq_printf(seq, "  Device %02d: 0x%x, 0x%x\n",
					i,
					priv->SCSIDevicePage1[i].RequestedParameters,
					priv->SCSIDevicePage1[i].Configuration);
		}
	}

	if (ioc->bus_type == FC) {
		seq_printf(seq, "WWN              :%08X%08X:%08X%08X\n",
				ioc->fc_port_page0[0].WWNN.High,
				ioc->fc_port_page0[0].WWNN.Low,
				ioc->fc_port_page0[0].WWPN.High,
				ioc->fc_port_page0[0].WWPN.Low);
	}

	TRACE_EXIT();
	return 0;
}

static int
mpt_proc_target_write(struct file *file, const char __user *buf,
			size_t length, loff_t *off)
{

	struct mpt_tgt *tgt = (struct mpt_tgt *)PDE(file->f_dentry->d_inode)->data;
	MPT_ADAPTER *ioc = tgt->priv->ioc;
	int res = 0;
	char tmp[32+1];

	TRACE_ENTRY();
	res = min(32, (int)length);
	if (copy_from_user(tmp, buf, res)) {
		res = -EFAULT;
		goto out;
	}
	tmp[res] = 0;

	TRACE_DBG("buff '%s'", tmp);
	if (strncmp("target:enable", tmp, strlen("target:enable")) == 0) {
		TRACE_DBG("Enable Target, %d, %d", ioc->id, tgt->target_enable);
		if (tgt->target_enable != 1) {
			mpt_stm_adapter_online(mpt_stm_priv[ioc->id]);
			tgt->target_enable = 1;
		}
	}

	if (strncmp("target:disable", tmp, strlen("target:disable")) == 0) {
		TRACE_DBG("Disable Target %d, %d", ioc->id, tgt->target_enable);
		if (tgt->target_enable != 0) {
			/* FIXME */
			tgt->target_enable = 0;
		}
	}

	if (strncmp("target_id:", tmp, strlen("target_id:")) == 0) {
		char *s = tmp + strlen("target_id:");
		int id = simple_strtoul(s, NULL, 0);
		if (id < MPT_MAX_SCSI_DEVICES) {
			if (IsScsi(tgt->priv)) {
				TRACE_DBG("Changing target id to %d\n",
						id);
				tgt->priv->port_id = id;
				stm_set_scsi_port_page1(tgt->priv,
						NO_SLEEP);
			}
		}
	}

out:
	TRACE_EXIT_RES(res);

	return length;
}

static struct scst_proc_data mpt_target_proc_data = {
	SCST_DEF_RW_SEQ_OP(mpt_proc_target_write)
	.show = mpt_target_show,
};

static int mpt_target_detect(struct scst_tgt_template *temp1);
static int mpt_target_release(struct scst_tgt *scst_tgt);
static int stmapp_pending_sense(struct mpt_cmd *mpt_cmd);
static int mpt_xmit_response(struct scst_cmd *scst_cmd);
static void mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv,
		struct scst_cmd *scst_cmd);
static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd);
static void mpt_on_free_cmd(struct scst_cmd *scst_cmd);
static void mpt_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd);
static int mpt_handle_task_mgmt(MPT_STM_PRIV * priv, u32 reply_word,
		int task_mgmt, int lun);
static int mpt_send_cmd_to_scst(struct mpt_cmd *cmd,
	enum scst_exec_context context);

static struct scst_tgt_template tgt_template = {
	.name = MYNAM,
	.sg_tablesize = 128, /* FIXME */
	.use_clustering = 1,
#ifdef DEBUG_WORK_IN_THREAD
	.xmit_response_atomic = 0,
	.rdy_to_xfer_atomic = 0,
#else
	.xmit_response_atomic = 1,
	.rdy_to_xfer_atomic = 1,
#endif
	.detect = mpt_target_detect,
	.release = mpt_target_release,
	.xmit_response = mpt_xmit_response,
	.rdy_to_xfer = mpt_rdy_to_xfer,
	.on_free_cmd = mpt_on_free_cmd,
	.task_mgmt_fn_done = mpt_task_mgmt_fn_done,
};

static inline void
mpt_msg_frame_free(MPT_STM_PRIV *priv, int index)
{
	MPT_ADAPTER *ioc = priv->ioc;
	if (priv->current_mf[index] != NULL) {
		TRACE_DBG("%s: free mf index %d, %p", ioc->name,
				MF_TO_INDEX(priv->current_mf[index]),
				priv->current_mf[index]);
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
		priv->current_mf[index] = NULL;
	}
}

static inline MPT_FRAME_HDR *
mpt_msg_frame_alloc(MPT_ADAPTER *ioc, int index)
{
	MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
	MPT_FRAME_HDR *mf;

	if (index != -1) {
		TRACE_DBG("%s: current_mf %p, index %d",
				ioc->name, priv->current_mf[index], index);
		WARN_ON(priv->current_mf[index] != NULL);
	}

	mf = mpt_get_msg_frame(stm_context, _IOC_ID);

	if (mf == NULL) {
		sBUG_ON(1);
	}

	if (index != -1) {
		priv->current_mf[index] = mf;
	}

	TRACE_DBG("%s: alloc mf index %d, %p, %d", ioc->name,
			MF_TO_INDEX(mf), mf, index);

	return mf;
}

static int _mpt_ada_nums = 0;

static int
mptstm_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    MPT_ADAPTER	*ioc = pci_get_drvdata(pdev);
    int ret = 0;
    struct mpt_tgt *tgt;
    struct proc_dir_entry *p;
    struct proc_dir_entry *root;
    char name[4];

    TRACE_ENTRY();
    ret = mpt_stm_adapter_install(ioc);
    if (ret != 0) {
	    goto out;
    }

    tgt = kmalloc(sizeof(*tgt), GFP_KERNEL);
    TRACE_MEM("kmalloc(GFP_KERNEL) for tgt (%d), %p",
	      sizeof(*tgt), tgt);
    if (tgt == NULL) {
	    TRACE(TRACE_OUT_OF_MEM, "%s",
		  "Allocation of tgt failed");
	    ret = -ENOMEM;
	    goto out;
    }
    memset(tgt, 0, sizeof(*tgt));
    tgt->priv = mpt_stm_priv[ioc->id];
    tgt->target_enable = 0;
    tgt->priv->port_id = 1;
    /* tgt->priv->scsi_port_config = MPI_SCSIPORTPAGE1_TARGCONFIG_INIT_TARG; */
    tgt->priv->scsi_port_config = MPI_SCSIPORTPAGE1_TARGCONFIG_TARG_ONLY;
    /* tgt->priv->scsi_id_config = 0x7; */
    tgt->priv->scsi_id_config = 0;
    atomic_set(&tgt->sess_count, 0);
    init_waitqueue_head(&tgt->waitQ);

    tgt->scst_tgt = scst_register_target(&tgt_template, MYNAM);
    if (tgt->scst_tgt == NULL) {
	    PRINT_ERROR(MYNAM ": scst_register_target() "
			"failed for host %p", pdev);

	    ret = -ENODEV;
	    goto out;
    }

    root = scst_proc_get_tgt_root(&tgt_template);
    scnprintf(name, sizeof(name), "%d", ioc->id);
    mpt_target_proc_data.data = (void *)tgt;
    p = scst_create_proc_entry(root, name,
				&mpt_target_proc_data);
    if (p == NULL) {
	    PRINT_ERROR("Not enough memory to register "
			    "target driver %s entry %s in /proc",
			    tgt_template.name, name);
	    scst_unregister_target(tgt->scst_tgt);
	    ret = -ENOMEM;
	    goto out;
    }

    scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt);
    mpt_stm_priv[ioc->id]->tgt = tgt;
    _mpt_ada_nums ++;

 out:

    TRACE_EXIT_RES(ret);

    return ret;
}

static void
mptstm_remove(struct pci_dev *pdev)
{
    MPT_ADAPTER	*ioc = pci_get_drvdata(pdev);
    MPT_STM_PRIV *priv;

	priv = mpt_stm_priv[ioc->id];
	if (priv != NULL) {
		mpt_stm_adapter_dispose(priv);
	}
}

static struct mpt_pci_driver mptstm_driver = {
    .probe = mptstm_probe,
    .remove = mptstm_remove,
};

/*
 * mpt_target_detect
 *
 * this function is intended to detect the target adapters that are present in
 * the system. Each found adapter should be registered by calling
 * scst_register_target(). The function should return a value >= 0 to signify
 * the number of detected target adapters. A negative value should be
 * returned whenever there is an error.
 */
static int mpt_target_detect(struct scst_tgt_template *templ)
{
	int ret  = 0;

	TRACE_ENTRY();
	ret = _mpt_stm_init();
	if (ret != 0) {
		goto out;
	}

	if (mpt_device_driver_register(&mptstm_driver, MPTSTM_DRIVER)) {
		printk(KERN_WARNING MYNAM
		       ": failed to register for device driver callbacks\n");
		ret = -ENODEV;
		goto out;
	}

	ret = _mpt_ada_nums;

 out:
	TRACE_EXIT_RES(ret);

	return ret;
}

static struct scst_cmd *
_stm_target_command(MPT_STM_PRIV *priv, int reply_word,
		    struct mpt_cmd *mpt_cmd)
{
	u8 *cdb;
	int lun, tag, dl, alias, index, init_index, task_mgmt;
	char alias_lun[32];
	CMD *cmd;
	struct scst_cmd *scst_cmd;
	struct mpt_sess *sess = mpt_cmd->sess;
#ifdef CONFIG_SCST_DEBUG
	MPT_ADAPTER *ioc = priv->ioc;
#endif
	/*
	 * Get the CBD, LUN, tag,  Task Mgmt flags, and data length from the
	 * receive packet
	 */
	TRACE_ENTRY();

	index = GET_IO_INDEX(reply_word);
	init_index = GET_INITIATOR_INDEX(reply_word);

	cmd = &priv->hw->cmd_buf[index];

	if (IsScsi(priv)) {
		SCSI_CMD *scsi_cmd = (SCSI_CMD *)cmd->cmd;

		cdb = scsi_cmd->CDB;
		lun = get2bytes(scsi_cmd->LogicalUnitNumber, 0);
		tag = scsi_cmd->Tag;
		task_mgmt = scsi_cmd->TaskManagementFlags;
		dl = 0;
		/*TRACE_DBG("AliasID %d, %d", scsi_cmd->AliasID, priv->port_id);*/
		if (reply_word & TARGET_MODE_REPLY_ALIAS_MASK) {
			alias = (scsi_cmd->AliasID - priv->port_id) & 15;
			sprintf(alias_lun, "alias %d lun %d", alias, lun);
		} else {
			alias = 0;
			sprintf(alias_lun, "lun %d", lun);
		}
	} else if (IsSas(priv)) {
		SSP_CMD *ssp_cmd = (SSP_CMD *)cmd->cmd;

		cdb = ssp_cmd->CDB;
		lun = get2bytes(ssp_cmd->LogicalUnitNumber, 0);
		if (ssp_cmd->FrameType == SSP_TASK_FRAME) {
			SSP_TASK	*ssp_task = (SSP_TASK *)cmd->cmd;

			tag = ssp_task->ManagedTaskTag;
			task_mgmt = ssp_task->TaskManagementFunction;
		} else {
			tag = ssp_cmd->InitiatorTag;
			task_mgmt = 0;
		}
		dl = 0;
		alias = 0;
		sprintf(alias_lun, "lun %d", lun);
	} else {
		FCP_CMD *fcp_cmd = (FCP_CMD *)cmd->cmd;

		cdb = fcp_cmd->FcpCdb;
		lun = get2bytes(fcp_cmd->FcpLun, 0);
		tag = 0;
		task_mgmt = fcp_cmd->FcpCntl[2];
		dl = be32_to_cpu(fcp_cmd->FcpDl);
		if (reply_word & TARGET_MODE_REPLY_ALIAS_MASK) {
			alias = fcp_cmd->AliasIndex;
			sprintf(alias_lun, "alias %d lun %d", alias, lun);
		} else {
			alias = 0;
			sprintf(alias_lun, "lun %d", lun);
		}
	}

	cmd->reply_word = reply_word;
	cmd->alias = alias;
	cmd->lun = lun;
	cmd->tag = tag;

	TRACE_DBG("%s: cmd %p, re_word %x, alias %x, lun %x, tag %x,"
			"%s, init_idx %d, %p, %d",
			ioc->name, cmd, reply_word, alias, lun, tag, alias_lun,
			init_index, priv->scst_cmd[index], dl);

	mpt_cmd->CMD = cmd;
	{
		uint16_t _lun = lun;
		_lun = swab16(le16_to_cpu(_lun));
		scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&_lun,
				sizeof(_lun), cdb, MPT_MAX_CDB_LEN, SCST_ATOMIC);
	}
	if (scst_cmd == NULL) {
		PRINT_ERROR(MYNAM ": scst_rx_cmd() failed for %p", cmd);
		goto out;
	}
	TRACE_DBG("scst cmd %p, index %d", priv->scst_cmd[index], index);

	WARN_ON(priv->scst_cmd[index] != 0);
	priv->scst_cmd[index] = scst_cmd;

	scst_cmd_set_tag(scst_cmd, tag);
	scst_cmd_set_tgt_priv(scst_cmd, mpt_cmd);

	/* FIXME scst_cmd_set_expected */
out:
	TRACE_EXIT();

	return scst_cmd;
}

static void
mpt_send_busy(struct mpt_cmd *cmd)
{
	stmapp_set_status(cmd->priv, cmd->CMD, STS_BUSY);
}

static void
mpt_alloc_session_done(struct scst_session *scst_sess, void *data, int result)
{
	struct mpt_sess *sess = (struct mpt_sess *) data;
	struct mpt_tgt *tgt = sess->tgt;
	struct mpt_cmd *cmd = NULL;
	int rc = 0;

	TRACE_ENTRY();
	if (result == 0) {
		while (!list_empty(&sess->delayed_cmds)) {
			cmd = list_entry(sess->delayed_cmds.next,
					 typeof(*cmd), delayed_cmds_entry);
			list_del(&cmd->delayed_cmds_entry);
			if (rc == 0)
				rc = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_THREAD);
			if (rc != 0) {
				PRINT_INFO(MYNAM ": Unable to get the command, sending BUSY state %p",
					   cmd);
				mpt_send_busy(cmd);
				kfree(cmd);
			}
		}
	} else {
		PRINT_INFO(MYNAM ": Session initialization failed, "
			   "sending BUSY status to all deferred commands %p",
			   cmd);
		while (!list_empty(&sess->delayed_cmds)) {
			cmd = list_entry(sess->delayed_cmds.next,
					 typeof(*cmd), delayed_cmds_entry);
			list_del(&cmd->delayed_cmds_entry);
			TRACE(TRACE_MGMT, "Command <%p> Busy", cmd);
			mpt_send_busy(cmd);
			kfree(cmd);
		}
		tgt->sess[sess->init_index] = NULL;

		TRACE_MEM("kfree for sess %p", sess);
		kfree(sess);

		if (atomic_dec_and_test(&tgt->sess_count))
				wake_up_all(&tgt->waitQ);
	}

        __clear_bit(MPT_SESS_INITING, &sess->sess_flags);

	TRACE_EXIT();
	return;
}

static int
mpt_send_cmd_to_scst(struct mpt_cmd *cmd, enum scst_exec_context context)
{
	int res = 0;

	TRACE_ENTRY();

	cmd->scst_cmd = _stm_target_command(cmd->priv, cmd->reply_word, cmd);
	if (cmd->scst_cmd == NULL) {
		res = -EFAULT;
		goto out;
	}
#ifdef DEBUG_WORK_IN_THREAD
	context = SCST_CONTEXT_THREAD;
#endif
	scst_cmd_init_done(cmd->scst_cmd, context);

 out:
	TRACE_EXIT_RES(res);

	return res;
}

static void
stm_send_target_status_deferred(MPT_STM_PRIV *priv,
		u32 reply_word, int index)
{
	int ret = 0;
	MPT_ADAPTER         *ioc = priv->ioc;
	MPT_FRAME_HDR       *mf;
	TargetStatusSendRequest_t   *req;

	TRACE_ENTRY();
	mf = priv->status_deferred_mf[index];
	TRACE_DBG("mf %p, index %d", mf, index);
	req = (TargetStatusSendRequest_t *)mf;

	priv->io_state[index] |= IO_STATE_STATUS_SENT;

	priv->current_mf[index] = mf;
	priv->status_deferred_mf[index] = NULL;
	if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
		ret = mpt_send_handshake_request(stm_context, _IOC_ID,
				sizeof(*req), (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, mf);
	}

	TRACE_EXIT_RES(ret);
}

static void
stm_data_done(MPT_ADAPTER *ioc, u32 reply_word,
		struct scst_cmd *scst_cmd, struct mpt_cmd *cmd, int index)
{
	MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];

	TRACE_ENTRY();
	TRACE_DBG("scst cmd %p, index %d, data done",  scst_cmd, index);

	if (scst_cmd_get_resp_data_len(scst_cmd) > 0) {
		TRACE_DBG("clear the data flags <%p>", scst_cmd);
		sBUG_ON(scst_cmd_get_sg_cnt(scst_cmd) == 0);
		pci_unmap_sg(priv->ioc->pcidev,
			scst_cmd_get_sg(scst_cmd),
			scst_cmd_get_sg_cnt(scst_cmd),
			scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
	}
	TRACE_EXIT();
}

void
stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
{
	MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
	int index, init_index;
	enum scst_exec_context context;
	struct scst_cmd *scst_cmd;
	struct mpt_cmd *cmd;
	volatile int *io_state;

	TRACE_ENTRY();

#ifdef DEBUG_WORK_IN_THREAD
	context = SCST_CONTEXT_THREAD;
#else
	context = SCST_CONTEXT_TASKLET;
#endif

	index = GET_IO_INDEX(reply_word);
	init_index = GET_INITIATOR_INDEX(reply_word);
	scst_cmd = priv->scst_cmd[index];
	io_state = priv->io_state + index;

	TRACE_DBG("index %d, state %x, scst cmd %p, current_mf %p",
			index, *io_state, scst_cmd, priv->current_mf[index]);
	/*
	 * if scst_cmd is NULL it show the command buffer not using by
	 * SCST, let parse the CDB
	 */
	if (scst_cmd == NULL) {
		WARN_ON((*io_state & ~IO_STATE_HIGH_PRIORITY) != IO_STATE_POSTED);
		*io_state &= ~IO_STATE_POSTED;

		mpt_msg_frame_free(priv, index);

		stmapp_tgt_command(priv, reply_word);
		goto out;
	}

	cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
	TRACE_DBG("scst cmd %p, index %d, cmd %p, cmd state %s",
		  scst_cmd, index, cmd, mpt_state_string[cmd->state]);

	if (cmd->state == MPT_STATE_NEED_DATA) {
		int rx_status = SCST_RX_STATUS_SUCCESS;

		cmd->state = MPT_STATE_DATA_IN;

		TRACE_DBG("Data received, context %x, rx_status %d",
				context, rx_status);

		sBUG_ON(!(*io_state & IO_STATE_DATA_SENT));
		mpt_msg_frame_free(priv, index);
		if (*io_state & IO_STATE_DATA_SENT) {
			*io_state &= ~IO_STATE_DATA_SENT;
			stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
		}
#if 0
		if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == IO_STATE_AUTO_REPOST) {
			TRACE_DBG("%s", "io state auto repost");
			*io_state = IO_STATE_POSTED;
		} else if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == 0) {
			TRACE_DBG("%s", "io state");
			stm_cmd_buf_post(priv, index);
		}
#endif
		scst_rx_data(scst_cmd, rx_status, context);

		goto out;
	}

	if (*io_state & IO_STATE_STATUS_SENT) {
		/*
		 *  status (and maybe data too) was being sent, so repost the
		 *  command buffer
		 */
		*io_state &= ~IO_STATE_STATUS_SENT;
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
		if (*io_state & IO_STATE_DATA_SENT) {
			*io_state &= ~IO_STATE_DATA_SENT;
			stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
		}
		TRACE_DBG("set priv->scst_cmd[%d] = NULL", index);
		priv->scst_cmd[index] = NULL;
		if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == IO_STATE_AUTO_REPOST) {
			TRACE_DBG("%s", "io state auto repost");
			*io_state = IO_STATE_POSTED;
		} else if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == 0) {
			TRACE_DBG("%s", "io state");
			stm_cmd_buf_post(priv, index);
		}

		/*
		 * figure out how we're handling cached sense data.
		 */
		if (IsScsi(priv)) {
			switch (atomic_read(&priv->pending_sense[init_index])) {
				/* attempt to send status and sense succeeded */
				case MPT_STATUS_SENSE_ATTEMPT:
					atomic_set(&priv->pending_sense[init_index],
						MPT_STATUS_SENSE_IDLE);
					/* ToDo: check and set scst_set_delivery_status(), if necessary */
					scst_tgt_cmd_done(scst_cmd, context);
					break;

				/* we tried to send status and sense
				 * simltaneously and failed.  Prepare to handle
				 * the next command without SCST if it is
				 * REQUEST_SENSE */
				case MPT_STATUS_SENSE_NOT_SENT:
					atomic_set(&priv->pending_sense[init_index],
						MPT_STATUS_SENSE_HANDLE_RQ);
					/* ToDo: check and set scst_set_delivery_status(), if necessary */
					scst_tgt_cmd_done(scst_cmd, context);
					break;

				/* we've handled REQUEST_SENSE ourselves and
				 * we're done with the command.  Clean up */
				case MPT_STATUS_SENSE_HANDLE_RQ:
					TRACE_DBG("%s: clearing pending sense",
							ioc->name);
					atomic_set(&priv->pending_sense[init_index],
							MPT_STATUS_SENSE_IDLE);
					mpt_on_free_cmd(scst_cmd);
					/* scst_cmd alloced in stmapp_pending_sense */
					kfree(scst_cmd);
					break;

				default:
					/* nothing much to do here, we aren't
					 * handling cached sense/status */
					/* ToDo: check and set scst_set_delivery_status(), if necessary */
					scst_tgt_cmd_done(scst_cmd, context);
					break;
			}
		} else {
			/* ToDo: check and set scst_set_delivery_status(), if necessary */
			scst_tgt_cmd_done(scst_cmd, context);
		}

		goto out;
	}

	/*
	 *  data (but not status) was being sent, so if status needs to be
	 *  set now, go ahead and do it; otherwise do nothing
	 */
	if (*io_state & IO_STATE_DATA_SENT) {
		*io_state &= ~IO_STATE_DATA_SENT;
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
		stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
		if (*io_state & IO_STATE_STATUS_DEFERRED) {
			*io_state &= ~IO_STATE_STATUS_DEFERRED;
			stm_send_target_status_deferred(priv, reply_word, index);
		}
		cmd->state = MPT_STATE_PROCESSED;
		goto out;
	}

	/*
	 * just insert into list
	 * bug how can i handle it
	 */
	if (*io_state == 0 && cmd->state == MPT_STATE_NEW) {
		WARN_ON(1);
		goto out;
	}
#if 0
	if (*io_state == IO_STATE_POSTED) {
		TRACE_DBG("%s", "io state posted");
		/*
		 *  command buffer was posted, so we now have a SCSI command
		 */
		*io_state &= ~IO_STATE_POSTED;
		goto out;
	}
#endif
	WARN_ON(1);
 out:

	TRACE_EXIT();
}

static int
mpt_is_task_mgm(MPT_STM_PRIV *priv, u32 reply_word, int *lun)
{
	int task_mgmt = 0, index;
	CMD *cmd;
	//struct mpt_tgt *tgt = priv->tgt;

	TRACE_ENTRY();

	index = GET_IO_INDEX(reply_word);
	cmd = &priv->hw->cmd_buf[index];

	if (IsScsi(priv)) {
		SCSI_CMD *scsi_cmd = (SCSI_CMD *)cmd->cmd;
		task_mgmt = scsi_cmd->TaskManagementFlags;
		*lun = get2bytes(scsi_cmd->LogicalUnitNumber, 0);
	} else if (IsSas(priv)) {
		SSP_CMD *ssp_cmd = (SSP_CMD *)cmd->cmd;
		if (ssp_cmd->FrameType == SSP_TASK_FRAME) {
			SSP_TASK *ssp_task = (SSP_TASK *)cmd->cmd;
			task_mgmt = ssp_task->TaskManagementFunction;
		}
		*lun = get2bytes(ssp_cmd->LogicalUnitNumber, 0);
	} else {
		FCP_CMD *fcp_cmd = (FCP_CMD *)cmd->cmd;
		task_mgmt = fcp_cmd->FcpCntl[2];
		*lun = get2bytes(fcp_cmd->FcpLun, 0);
	}
	TRACE_EXIT_RES(task_mgmt);

	return task_mgmt;
}

static void
stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word)
{
	struct mpt_tgt *tgt = NULL;
	struct mpt_sess *sess = NULL;
	struct mpt_cmd *cmd = NULL;
	int init_index, res = 0, task_mgmt, lun;

	TRACE_ENTRY();

	tgt = priv->tgt;

	task_mgmt = mpt_is_task_mgm(priv, reply_word, &lun);
	if (task_mgmt) {
		mpt_handle_task_mgmt(priv, reply_word, task_mgmt, lun);
	}

	init_index = GET_INITIATOR_INDEX(reply_word);

	if (test_bit(MPT_TGT_SHUTDOWN, &tgt->tgt_flags)) {
		TRACE_DBG("New command while the device %p is shutting down", tgt);
		res = -EFAULT;
		goto out;
	}

	cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
	TRACE_MEM("kmalloc(GFP_ATOMIC) for cmd (%d): %p", sizeof(*cmd), cmd);
	if (cmd == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of cmd failed");
		res = -ENOMEM;
		goto out;
	}

	memset(cmd, 0, sizeof(*cmd));
	cmd->priv = priv;
	cmd->reply_word = reply_word;
	cmd->state = MPT_STATE_NEW;

	sess = tgt->sess[init_index];
	if (sess == NULL) {
		sess = kmalloc(sizeof(*sess), GFP_ATOMIC);
		if (sess == NULL) {
			TRACE(TRACE_OUT_OF_MEM, "%s",
			      "Allocation of sess failed");
			res = -ENOMEM;
			goto out_free_cmd;
		}
		/* WWPN */

		atomic_inc(&tgt->sess_count);
		smp_mb__after_atomic_inc();

		memset(sess, 0, sizeof(*sess));
		sess->tgt = tgt;
		sess->init_index = init_index;
		INIT_LIST_HEAD(&sess->delayed_cmds);

		sess->scst_sess = scst_register_session(tgt->scst_tgt, 1,
							"", sess, sess,
							mpt_alloc_session_done);
		if (sess->scst_sess == NULL) {
			PRINT_ERROR(MYNAM ": scst_register_session failed %p",
				    tgt);
			res = -EFAULT;
			goto out_free_sess;
		}

		__set_bit(MPT_SESS_INITING, &sess->sess_flags);

		tgt->sess[init_index] = sess;

		cmd->sess = sess;
		list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds);
		goto out;
	}

	/* seesion is ready let us do it */
	cmd->sess = sess;
	if (test_bit(MPT_SESS_INITING, &sess->sess_flags)) {
		list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds);
	} else {
		/* if there is pending sense left over from the last command,
		 * we need to send that if this is a REQUEST SENSE command.
		 * Otherwise send the command to SCST */
		if (!stmapp_pending_sense(cmd)) {
			res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_TASKLET);
			/*res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_DIRECT_ATOMIC);*/
			if (res != 0)
				goto out_free_cmd;
		}
	}

 out:
	TRACE_EXIT();
	return;

 out_free_sess:
	TRACE_MEM("kfree for sess %p", sess);
	kfree(sess);

	if (atomic_dec_and_test(&tgt->sess_count))
		wake_up_all(&tgt->waitQ);
	/* go through */
 out_free_cmd:
	TRACE_MEM("kfree for cmd %p", cmd);
	kfree(cmd);
	goto out;
}

/*
 * mpt_target_release
 *
 * this function is
 * intended to free up the resources allocated to the device. The function
 * should return 0 to indicate successful release or a negative value if
 * there are some issues with the release. In the current version of SCST
 * the return value is ignored. Must be defined.
 */
static int mpt_target_release(struct scst_tgt *scst_tgt)
{
	/* FIXME */
	return 0;
}

struct mpt_prm
{
	struct mpt_tgt *tgt;
	uint16_t seg_cnt;
	unsigned short use_sg;
	struct scatterlist *sg;
	unsigned int bufflen;
	void *buffer;
	scst_data_direction data_direction;
	uint16_t rq_result;
	uint16_t scsi_status;
	unsigned char *sense_buffer;
	unsigned int sense_buffer_len;
	struct mpt_cmd *cmd;
};

static inline void
mpt_dump_sge(MPT_SGE *sge, struct scatterlist *sg)
{
	if (sge) {
		void *address = NULL;
		struct page *page = NULL;
		address = bus_to_virt(sge->address);
		page = virt_to_page(address);
		TRACE_DBG("address %p, length %x, count %d, page %p",
				address, sge->length, page_count(page), page);
		TRACE_BUFFER("sge data", address, min(sge->length, (u32)0x10));
	}
	if (sg) {
		TRACE_DBG("sg %p, page %p, %p, offset %d, dma address %x, len %d",
				sg, sg_page(sg), page_address(sg_page(sg)),
				sg->offset, sg->dma_address, sg->length);
		TRACE_BUFFER("sg data", page_address(sg_page(sg)), (u32)0x10);
	}
}

/* FIXME
 *
 * use_sg can not bigger then NUM_SGES
 *
 */
static inline void
mpt_sge_to_sgl(struct mpt_prm *prm, MPT_STM_PRIV *priv, MPT_SGL *sgl)
{
	unsigned int bufflen = prm->bufflen;
	int i;

	TRACE_ENTRY();
	TRACE_DBG("bufflen %d, %p", bufflen, prm->buffer);
	sBUG_ON(prm->use_sg == 0);

	prm->sg = (struct scatterlist *)prm->buffer;
	prm->seg_cnt = pci_map_sg(priv->ioc->pcidev, prm->sg, prm->use_sg,
				   scst_to_tgt_dma_dir(prm->data_direction));

	pci_dma_sync_sg_for_cpu(priv->ioc->pcidev, prm->sg, prm->use_sg,
			scst_to_tgt_dma_dir(prm->data_direction));
	for (i = 0; i < prm->use_sg; i++) {
		sgl->sge[i].length = sg_dma_len(&prm->sg[i]);
		sgl->sge[i].address = sg_dma_address(&prm->sg[i]);

		TRACE_DBG("%d, %d", bufflen, prm->sg[i].length);
		if (bufflen < prm->sg[i].length) {
			sgl->sge[i].length = bufflen;
		}
		mpt_dump_sge(&sgl->sge[i], &prm->sg[i]);
		bufflen -= sgl->sge[i].length;
	}
	pci_dma_sync_sg_for_device(priv->ioc->pcidev, prm->sg, prm->use_sg,
			scst_to_tgt_dma_dir(prm->data_direction));

	sgl->num_sges = prm->seg_cnt;

	TRACE_EXIT();
}

static inline void
mpt_set_sense_info(MPT_STM_PRIV *priv, CMD *cmd, int len, u8 *sense_buf)
{
	u8 *info = NULL;

	TRACE_ENTRY();

	if (IsScsi(priv)) {
		SCSI_RSP    *rsp = (SCSI_RSP *)cmd->rsp;

		rsp->Status = STS_CHECK_CONDITION;
		rsp->Valid |= SCSI_SENSE_LEN_VALID;
		rsp->SenseDataListLength = cpu_to_be32(len);
		info = rsp->SenseData;
		if (rsp->Valid & SCSI_RSP_LEN_VALID) {
			info += be32_to_cpu(rsp->PktFailuresListLength);
		}
	} else if (IsSas(priv)) {
		SSP_RSP     *rsp = (SSP_RSP *)cmd->rsp;

		rsp->Status = STS_CHECK_CONDITION;
		rsp->DataPres |= SSP_SENSE_LEN_VALID;
		rsp->SenseDataLength = cpu_to_be32(len);
		info = rsp->ResponseSenseData;
		if (rsp->DataPres & SSP_RSP_LEN_VALID) {
			info += be32_to_cpu(rsp->ResponseDataLength);
		}
	} else {
		FCP_RSP     *rsp = (FCP_RSP *)cmd->rsp;

		rsp->FcpStatus = STS_CHECK_CONDITION;
		rsp->FcpFlags |= FCP_SENSE_LEN_VALID;
		rsp->FcpSenseLength = cpu_to_be32(len);
		info = rsp->FcpSenseData - sizeof(rsp->FcpResponseData);
		if (rsp->FcpFlags & FCP_RSP_LEN_VALID) {
			info += be32_to_cpu(rsp->FcpResponseLength);
		}
	}

	sBUG_ON(info == NULL);
	memcpy(info, sense_buf, len);
/*out:*/

	TRACE_EXIT();
}

static int
mpt_send_tgt_data(MPT_STM_PRIV *priv, u32 reply_word,
		int index, int flags, int lun, int tag, MPT_SGL *sgl,
		int length, int offset)
{
	MPT_ADAPTER *ioc = priv->ioc;
	TargetAssistRequest_t *req;
	MPT_STM_SIMPLE	*sge_simple;
	MPT_STM_CHAIN	*sge_chain = NULL;
	u32 sge_flags;
	int chain_length, i, j, k, init_index, res = 1;
	dma_addr_t dma_addr;

	TRACE_ENTRY();
	req = (TargetAssistRequest_t *)mpt_msg_frame_alloc(ioc,index);
	memset(req, 0, sizeof(*req));

	if (priv->exiting) {
		flags &= ~TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
	}

	if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
		flags |= TARGET_ASSIST_FLAGS_HIGH_PRIORITY;
		if (flags & TARGET_ASSIST_FLAGS_AUTO_STATUS) {
			flags |= TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
			priv->io_state[index] |= IO_STATE_AUTO_REPOST;
		}
	}

	if (priv->fcp2_capable/* && priv->initiators != NULL*/) {
		init_index = GET_INITIATOR_INDEX(reply_word);
		/*init = priv->initiators[init_index];
		if (init != NULL && init->confirm_capable) {
			flags |= TARGET_ASSIST_FLAGS_CONFIRMED;
		}*/
	}
	TRACE_DBG("flags %x, tag %x, lun %x, offset %x, length %x",
		  flags, tag, lun, offset, length);

	req->StatusCode = 0;
	req->TargetAssistFlags = (u8)flags;
	req->Function = MPI_FUNCTION_TARGET_ASSIST;
	req->QueueTag = (u16)tag;
	req->ReplyWord = cpu_to_le32(reply_word);
	req->LUN[0] = (u8)(lun >> 8);
	req->LUN[1] = (u8)lun;
	req->RelativeOffset = cpu_to_le32(offset);
	req->DataLength = cpu_to_le32(length);
	sge_flags =
		MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
				  MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
	if (flags & TARGET_ASSIST_FLAGS_DATA_DIRECTION)
		sge_flags |= MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_HOST_TO_IOC);
	sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
	for (i = 0, j = 0, k = 0; i < (int)sgl->num_sges; i++, j++) {
		if (k == 0) {
			/* still in mf, haven't chained yet -- do we need to? */
			if (j == priv->num_sge_target_assist) {
				/* yes, we need to chain */
				/* overwrite the last element in the mf with a chain */
				sge_chain = (MPT_STM_CHAIN *)(sge_simple - 1);
				sge_chain->Flags =
					(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
						 MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
				dma_addr = priv->hw_dma +
					((u8 *)priv->hw->cmd_buf[index].chain_sge -
					 (u8 *)priv->hw);
				stm_set_dma_addr(sge_chain->Address, dma_addr);
				/* set the "last element" flag in the mf */
				sge_simple = (MPT_STM_SIMPLE *)(sge_chain - 1);
				sge_simple->FlagsLength |=
					cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT));
				/* redo the last element in the mf */
				sge_simple =
					(MPT_STM_SIMPLE *)priv->hw->cmd_buf[index].chain_sge;
				sge_simple->FlagsLength =
					cpu_to_le32(sgl->sge[i-1].length | sge_flags);
				stm_set_dma_addr(sge_simple->Address, sgl->sge[i-1].address);
				mpt_dump_sge(&sgl->sge[i-1], NULL);
				sge_simple++;
				/* say we've chained */
				req->ChainOffset =
					((u8 *)sge_chain - (u8 *)req) / sizeof(u32);
				j = 1;
				k++;
			}
		} else {
			/* now in chain, do we need to chain again? */
			if (j == priv->num_sge_chain) {
				/* yes, we need to chain */
				/* fix up the previous chain element */
				chain_length = sizeof(MPT_STM_CHAIN) +
					(priv->num_sge_chain - 1) * sizeof(MPT_STM_SIMPLE);
				sge_chain->Length = cpu_to_le16(chain_length);
				sge_chain->NextChainOffset =
					(chain_length - sizeof(MPT_STM_CHAIN)) / sizeof(u32);
				/* overwrite the last element in the chain with another chain */
				sge_chain = (MPT_STM_CHAIN *)(sge_simple - 1);
				sge_chain->Flags =
					(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
						 MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
				dma_addr = priv->hw_dma + ((u8 *)sge_simple - (u8 *)priv->hw);
				stm_set_dma_addr(sge_chain->Address, dma_addr);
				/* set the "last element" flag in the previous chain */
				sge_simple = (MPT_STM_SIMPLE *)(sge_chain - 1);
				sge_simple->FlagsLength |=
					cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT));
				/* redo the last element in the previous chain */
				sge_simple = (MPT_STM_SIMPLE *)(sge_chain + 1);
				sge_simple->FlagsLength =
					cpu_to_le32(sgl->sge[i-1].length | sge_flags);
				stm_set_dma_addr(sge_simple->Address, sgl->sge[i-1].address);
				mpt_dump_sge(&sgl->sge[i-1], NULL);
				sge_simple++;
				/* say we've chained */
				j = 1;
				k++;
			}
		}
		sge_simple->FlagsLength = cpu_to_le32(sgl->sge[i].length | sge_flags);
		stm_set_dma_addr(sge_simple->Address, sgl->sge[i].address);
		mpt_dump_sge(&sgl->sge[i], NULL);
		sge_simple++;
	}
	/* did we chain? */
	if (k != 0) {
		/* fix up the last chain element */
		sge_chain->Length = cpu_to_le16(j * sizeof(MPT_STM_SIMPLE));
		sge_chain->NextChainOffset = 0;
	}
	/* fix up the last element */
	sge_simple--;
	sge_simple->FlagsLength |=
		cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT |
					      MPI_SGE_FLAGS_END_OF_BUFFER |
                     MPI_SGE_FLAGS_END_OF_LIST));
#ifdef CONFIG_SCST_TRACING
if(trace_mpi)
{
	u32 *p = (u32 *)req;
	int i;
	//dma_addr_t _data;
	//u8 *_buf;

	TRACE(TRACE_MPI, "%s stm_send_target_data %d",
			ioc->name, index);
	for (i = 0; i < (sizeof(*req) - sizeof(req->SGL)) / 4; i++) {
		TRACE(TRACE_MPI, "%s req[%02x] = %08x",
				ioc->name, i * 4, le32_to_cpu(p[i]));
	}
	TRACE(TRACE_MPI, "%s num_sges = %d, j = %d, k = %d",
			ioc->name, sgl->num_sges, j, k);
	p = (u32 *)&req->SGL;
	for (i = 0; i < ((k != 0) ? priv->num_sge_target_assist : j); i++) {
#if MPT_STM_64_BIT_DMA
	TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x %08x",
				ioc->name, i * 12, le32_to_cpu(p[i*3]),
				le32_to_cpu(p[i*3+1]), le32_to_cpu(p[i*3+2]));
#else
	_data = le32_to_cpu(p[i*2+1]);
	_buf = (u8 *)phys_to_virt(_data);
	TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x,%x,%x,%x,%x,%p",
			ioc->name, i * 8, le32_to_cpu(p[i*2]), le32_to_cpu(p[i*2+1]),
			_buf[0], _buf[1], _buf[2], _buf[3], _buf);
#endif
	}
	p = (u32 *)priv->hw->cmd_buf[index].chain_sge;
	for (i = 0; i < ((k != 0) ? (k - 1) * priv->num_sge_chain + j : 0); i++) {
#if MPT_STM_64_BIT_DMA
	TRACE(TRACE_MPI, "%s chain sgl[%04x] = %08x %08x %08x",
			ioc->name, i * 12, le32_to_cpu(p[i*3]),
			le32_to_cpu(p[i*3+1]), le32_to_cpu(p[i*3+2]));
#else
	_data = le32_to_cpu(p[i*2+1]);
	_buf = (u8 *)phys_to_virt(_data);
	TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x,%x,%x,%x,%x,%p",
			ioc->name, i * 8, le32_to_cpu(p[i*2]), le32_to_cpu(p[i*2+1]),
			_buf[0], _buf[1], _buf[2], _buf[3], _buf);
#endif
		}
	}
#endif
	res = 0;

	priv->io_state[index] |= IO_STATE_DATA_SENT;
	if (flags & TARGET_ASSIST_FLAGS_AUTO_STATUS)
		priv->io_state[index] |= IO_STATE_STATUS_SENT;


	if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
		res =mpt_send_handshake_request(stm_context, _IOC_ID,
					   ioc->req_sz, (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	}

	TRACE_EXIT_RES(res);

	return res;
}

/*
 * calling mpt_send_target_data
 *
 */
static void
mpt_send_target_data(struct mpt_prm *prm, int flags)
{
	MPT_STM_PRIV *priv;
	u32 reply_word;
	int index, lun, tag, length, offset;
	MPT_SGL *sgl;

	TRACE_ENTRY();
	priv = prm->tgt->priv;
	sgl = &priv->sgl;

	mpt_sge_to_sgl(prm, priv, sgl);

	reply_word = prm->cmd->CMD->reply_word;
	index = GET_IO_INDEX(reply_word);

	lun = prm->cmd->CMD->lun;
	tag = prm->cmd->CMD->tag;

	if (prm->data_direction == SCST_DATA_READ) {
		flags |= TARGET_ASSIST_FLAGS_DATA_DIRECTION;
	}

	length = prm->bufflen;
	offset = 0;
#if 0
	TRACE_DBG("priv %p, reply_word %x, index %x, flags %x, lun %x, "
		  "tag %x, sgl %p, length %x, offset %x",
		  priv, reply_word, index, flags, lun, tag,
		  sgl, length, offset);
#endif
	mpt_send_tgt_data(priv, reply_word, index, flags, lun, tag,
			sgl, length, offset);

	TRACE_EXIT();
	return;
}

/*
 * this function checks if we need to handle REQUEST_SENSE on behalf of the
 * target device.  If the sense wasn't able to be sent simultaneously
 * with the status for the last command with a check condition, it needs
 * to either get sent for a REQUEST_SENSE command or forgotten.
 *
 * The pending_sense state and a buffer for holding sense is created for
 * each possible initiator.  The pending_sense state is used to tell if
 * sending sense failed and to track the progress of the following
 * REQUEST_SENSE command.
 *
 * There are four values for the pending_sense state:
 * - STATUS_SENSE_IDLE: no caching of sense data is in progress
 * - STATUS_SENSE_ATTEMPT: an attempt is being made to send status and
 *   sense in the same bus transaction.
 * - STATUS_SENSE_NOT_SENT: the attempt to send simultanous status and
 *   sense failed.
 * - STATUS_SENSE_HANDLE_RQ: the next command from the initiator needs
 *   to be handled by the LSI driver if it is REQUEST_SENSE using cached
 *   sense data, otherwise the cached sense data is ignored and the
 *   command is sent to SCST.
 *
 * In stmapp_pending_sense, if pending_sense state for an initiator ==
 * SENSE_HANDLE_RQ, the incoming command is inspected.  If it is
 * REQUEST_SENSE, the command is handled by the LSI driver without
 * involving SCST.  The cached sense data from the immediately previous
 * command is used.  This sense data was not sent along with the status
 * for that command (see below).  If the command is not REQUEST_SENSE,
 * the cached sense data is discarded and the command is sent to SCST
 * for processing.
 *
 * In stm_send_target_status, sense data about to be sent is saved in a
 * buffer and the pending_sense state for that initiator is set to
 * MPT_STATUS_SENSE_ATTEMPT.  The sense and status are sent to the LSI
 * hardware.
 *
 * If the LSI hardware determines that the sense and status could not be
 * sent in one operation, stm_reply is called with state == STATUS_SENT
 * and with IOCStatus of MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x6B).
 * This condition only happens in the non-packetized SCSI operating mode.
 * When this happens, the pending_sense state is advanced to
 * MPT_STATUS_SENSE_NOT_SENT.
 *
 * In stm_tgt_reply, if io_state == STATUS_SENT:
 * - if pending_sense state == SENSE_ATTEMPT, the status and sense attempt was
 *   successful, so the pending_sense state is set to IDLE and the command
 *   is sent to SCST for completion.
 * - if pending_sense state == SENSE_NOT_SENT, the status and sense attempt
 *   failed.  The pending_sense state is advanced to SENSE_HANDLE_RQ and the
 *   current command is sent to SCST for completion.  The next command from
 *   this initiator will enter stmapp_pending_sense as described above.
 * - if pending_sense state == SENSE_HANDLE_RQ, the REQUEST_SENSE command
 *   handled alone by the LSI driver is done and resources need to be
 *   released.  pending_sense state is set to IDLE.
 * - if pending_sense state == SENSE_IDLE, a normal SCST command is done
 *   and is sent to SCST for completion.
 *
 * Because of this caching sense behaviour, we think we need to turn off
 * tagged command queueing.  The mpt_inquiry_no_tagged_commands function
 * does this by modifying INQUIRY data before sending it over the wire.
 */
static int
stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
{
	int res = 0;
	MPT_STM_PRIV *priv;
	int index = 0;
	int init_index = 0;
	int flags = 0;
	SCSI_CMD *scsi_cmd = NULL;
	CMD *cmd;
	u8 *cdb;
	struct mpt_prm prm = { 0 };
	struct scst_cmd *scst_cmd;
	struct scatterlist sg;

	TRACE_ENTRY();

	priv = mpt_cmd->priv;
	if (IsScsi(priv)) {
		index = GET_IO_INDEX(mpt_cmd->reply_word);
		init_index = GET_INITIATOR_INDEX(mpt_cmd->reply_word);
		if (atomic_read(&priv->pending_sense[init_index]) ==
				MPT_STATUS_SENSE_HANDLE_RQ) {
			cmd = &priv->hw->cmd_buf[index];
			scsi_cmd = (SCSI_CMD *)cmd->cmd;
			cdb = scsi_cmd->CDB;

			if (cdb[0] == REQUEST_SENSE) {
				/* scst_cmd used as a container in stm_tgt_reply,
				 * command doesn't actually go to SCST */
				scst_cmd = kmalloc(sizeof(struct scst_cmd),
						GFP_ATOMIC);
				TRACE_DBG("scst_cmd 0x%p", scst_cmd);
				if (scst_cmd != NULL) {
					cmd->reply_word = mpt_cmd->reply_word;
					if (cmd->reply_word &
						TARGET_MODE_REPLY_ALIAS_MASK) {
						cmd->alias = (scsi_cmd->AliasID -
								priv->port_id) & 15;
					} else {
						cmd->alias = 0;
					}
					cmd->lun = get2bytes(scsi_cmd->LogicalUnitNumber,
							0);
					cmd->tag = scsi_cmd->Tag;
					mpt_cmd->CMD = cmd;

					memset(scst_cmd, 0x00,
						sizeof(struct scst_cmd));
					scst_cmd->resp_data_len = -1;
					memcpy(scst_cmd->cdb, cdb,
							MPT_MAX_CDB_LEN);
					priv->scst_cmd[index] = scst_cmd;
					scst_cmd_set_tag(scst_cmd, cmd->tag);
					scst_cmd_set_tgt_priv(scst_cmd, mpt_cmd);

					TRACE_BUFFER("CDB", cdb, MPT_MAX_CDB_LEN);

					flags = TARGET_ASSIST_FLAGS_AUTO_STATUS;
					prm.cmd = mpt_cmd;
					/* smallest amount of data between
					 * requested length, buffer size,
					 * and cached length */
					prm.bufflen = min((size_t)cdb[4],
						(size_t)SCSI_SENSE_BUFFERSIZE);
					prm.bufflen = min(prm.bufflen,
						(size_t)(priv->pending_sense_buffer[init_index][7]
							 + 8));
					sg_set_page(&sg,
						virt_to_page(priv->pending_sense_buffer[init_index]),
						prm.bufflen,
						offset_in_page(priv->pending_sense_buffer[init_index]));
					prm.buffer = &sg;
					prm.use_sg = 1;
					prm.data_direction = SCST_DATA_READ;
					prm.tgt = priv->tgt->sess[init_index]->tgt;
					prm.cmd->state = MPT_STATE_DATA_OUT;

					TRACE_DBG("%s: sending pending sense",
							priv->ioc->name);
					mpt_send_target_data(&prm, flags);
					res = 1;
				} else {
					/* we couldn't create a scst_cmd, so
					 * we can't do anything.  Send the
					 * command to SCST. */
					atomic_set(&priv->pending_sense[init_index],
							MPT_STATUS_SENSE_IDLE);
				}
			} else {
				/* next command immediately after check
				 * condition is not REQUEST_SENSE, so we can
				 * discard the cached sense and send the
				 * command to SCST. */
				atomic_set(&priv->pending_sense[init_index],
						MPT_STATUS_SENSE_IDLE);
			}
		} else {
			/* we don't need to perform REQUEST_SENSE and can
			 * send the command to SCST */
			atomic_set(&priv->pending_sense[init_index],
					MPT_STATUS_SENSE_IDLE);
		}
	}

	TRACE_EXIT_RES(res);
	return res;
}

/*
 * this function is equivalent to the SCSI queuecommand(). The target should
 * transmit the response data and the status in the struct scst_cmd. See
 * below for details. Must be defined.
 */
static int
mpt_xmit_response(struct scst_cmd *scst_cmd)
{
	int res = SCST_TGT_RES_SUCCESS;
	struct mpt_sess *sess;
	struct mpt_prm prm = { 0 };
	int is_send_status;
	//uint16_t full_req_cnt;
	//int data_sense_flag = 0;

	TRACE_ENTRY();

#ifdef DEBUG_WORK_IN_THREAD
	if (scst_cmd_atomic(scst_cmd))
		return SCST_TGT_RES_NEED_THREAD_CTX;
#endif

	prm.cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
	sess = (struct mpt_sess *)
		scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd));

	prm.sg = NULL;
	prm.bufflen = scst_cmd_get_resp_data_len(scst_cmd);
	prm.buffer = scst_cmd->sg;
	prm.use_sg = scst_cmd->sg_cnt;
	prm.data_direction = scst_cmd_get_data_direction(scst_cmd);
	prm.rq_result = scst_cmd_get_status(scst_cmd);
	prm.sense_buffer = scst_cmd_get_sense_buffer(scst_cmd);
	prm.sense_buffer_len = scst_cmd_get_sense_buffer_len(scst_cmd);
	prm.tgt = sess->tgt;
	prm.seg_cnt = 0;
	is_send_status = scst_cmd_get_is_send_status(scst_cmd);

	TRACE_DBG("rq_result=%x, is_send_status=%x, %x, %d", prm.rq_result,
			is_send_status, prm.bufflen, prm.sense_buffer_len);
	if ((prm.rq_result != 0) && (prm.sense_buffer != NULL))
		TRACE_BUFFER("Sense", prm.sense_buffer, prm.sense_buffer_len);

	if (!is_send_status) {
		/* ToDo, after it's done in SCST */
		PRINT_ERROR(MYNAM ": is_send_status not set: "
			    "feature not implemented %p", scst_cmd);
		res = SCST_TGT_RES_FATAL_ERROR;
		goto out_tgt_free;
	}

	if (test_bit(MPT_SESS_SHUTDOWN, &sess->sess_flags)) {
		TRACE_DBG("cmd %p while session %p is shutting down",
			  prm.cmd, sess);
		res = SCST_TGT_RES_SUCCESS;
		goto out_tgt_free;
	}

	if (SCST_SENSE_VALID(prm.sense_buffer)) {
		mpt_set_sense_info(prm.tgt->priv, prm.cmd->CMD,
				prm.sense_buffer_len, prm.sense_buffer);
	}

	if (scst_cmd_get_resp_data_len(scst_cmd) > 0) {
		int flags = 0;
		if (prm.rq_result == 0) {
			flags |= TARGET_ASSIST_FLAGS_AUTO_STATUS;
		}
		if (scst_get_may_need_dma_sync(scst_cmd)) {
			dma_sync_sg(&(prm.tgt->priv->ioc->pcidev->dev),
				scst_cmd->sg, scst_cmd->sg_cnt,
				scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
		}
		mpt_send_target_data(&prm, flags);

		if (prm.rq_result == 0) {
			goto out;
		}
	}
	{
		int flags = 0;
		u32 reply_word = prm.cmd->CMD->reply_word;
		int index = GET_IO_INDEX(reply_word);
		int lun = prm.cmd->CMD->lun;
		int tag = prm.cmd->CMD->tag;
		MPT_STM_PRIV *priv = prm.tgt->priv;

		if (prm.rq_result == 0) {
			flags |= TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS;
		}

		flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
		priv->io_state[index] |= IO_STATE_AUTO_REPOST;

		TRACE_DBG("scst cmd %p, index %d, flags %d",
				scst_cmd, index, flags);

		stm_send_target_status(priv, reply_word, index,
				flags, lun, tag);
	}

 out:
	TRACE_EXIT_RES(res);

	return res;

 out_tgt_free:
 	/* ToDo: check and set scst_set_delivery_status(), if necessary */
	scst_tgt_cmd_done(scst_cmd, SCST_CONTEXT_SAME);
	goto out;
}

/*
 * modifiy the response for an INQUIRY command to turn off
 * support for tagged command queuing if we're on a SCSI bus.
 * It's doubtful that caching sense data will work correctly
 * if tagging is enabled.
 */
static void
mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv, struct scst_cmd *scst_cmd)
{
	int32_t length;
	uint8_t *address;

	TRACE_ENTRY();

	/*
	 * only modify INQUIRY if we're on a SCSI bus,
	 * and we are handling a standard INQUIRY command
	 * (EVPD = 0)
	 */
	if (IsScsi(priv) && (scst_cmd->cdb[0] == INQUIRY) &&
			!(scst_cmd->cdb[1] & 0x1)) {
		sBUG_ON(scst_cmd->sg_cnt == 0);
		length = scst_get_buf_first(scst_cmd, &address);
		if (length >= 8) {
			TRACE_DBG("clearing BQUE + CMDQUE 0x%p", address);
			address[6] &= ~0x80; /* turn off BQUE */
			address[7] &= ~0x02; /* turn off CMDQUE */
		}
		scst_put_buf(scst_cmd, address);
	}

	TRACE_EXIT();
}

/*
 * this function
 * informs the driver that data buffer corresponding to the said command
 * have now been allocated and it is OK to receive data for this command.
 * This function is necessary because a SCSI target does not have any
 * control over the commands it receives. Most lower-level protocols have a
 * corresponding function which informs the initiator that buffers have
 * been allocated e.g., XFER_RDY in Fibre Channel. After the data is
 * actually received the low-level driver should call scst_rx_data()
 * in order to continue processing this command. Returns one of the
 * SCST_TGT_RES_* constants, described below. Pay attention to
 * "atomic" attribute of the command, which can be get via
 * scst_cmd_get_atomic(): it is true if the function called in the
 * atomic (non-sleeping) context. Must be defined.
 */
static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd)
{
	int res = SCST_TGT_RES_SUCCESS;
	struct mpt_sess *sess;
	/*unsigned long flags = 0;*/
	struct mpt_prm prm = { 0 };

	TRACE_ENTRY();

#ifdef DEBUG_WORK_IN_THREAD
	if (scst_cmd_atomic(scst_cmd))
		return SCST_TGT_RES_NEED_THREAD_CTX;
#endif

	prm.cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
	sess = (struct mpt_sess *)
		scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd));
	mpt_inquiry_no_tagged_commands(sess->tgt->priv, scst_cmd);

	prm.sg = (struct scatterlist *)NULL;
	prm.bufflen = scst_cmd->bufflen;
	prm.buffer = scst_cmd->sg;
	prm.use_sg = scst_cmd->sg_cnt;
	prm.data_direction = scst_cmd_get_data_direction(scst_cmd);
	prm.tgt = sess->tgt;

	if (test_bit(MPT_SESS_SHUTDOWN, &sess->sess_flags)) {
		TRACE_DBG("cmd %p while session %p is shutting down",
			  prm.cmd, sess);
		scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_FATAL,
			     SCST_CONTEXT_SAME);
		res = SCST_TGT_RES_SUCCESS;
		goto out;
	}

	prm.cmd->state = MPT_STATE_NEED_DATA;

	mpt_send_target_data(&prm, 0);

 out:
	TRACE_EXIT_RES(res);

	return res;
}

/*
 * this function
 * called to notify the driver that the command is about to be freed.
 * Necessary, because for aborted commands <bf/xmit_response()/ could not be
 * called. Could be used on IRQ context. Must be defined.
 */
static void mpt_on_free_cmd(struct scst_cmd *scst_cmd)
{
	struct mpt_cmd *cmd =
		(struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);

	TRACE_ENTRY();

	TRACE_DBG("cmd %p, scst_cmd %p", cmd, scst_cmd);

	scst_cmd_set_tgt_priv(scst_cmd, NULL);

#if 1
	memset(cmd, 0, sizeof(*cmd));
#endif
	kfree(cmd);

	TRACE_EXIT();
}

/*
 * this function informs the driver that a received task management
 * function has been completed. Completion status could be get via
 * scst_mgmt_cmd_get_status(). No return value expected. Must be
 * defined, if the target supports task management functionality.
 */
static void
mpt_task_mgmt_fn_done(struct scst_mgmt_cmd *mgmt_cmd)
{
	TRACE_ENTRY();
	WARN_ON(1);
	TRACE_EXIT();
}

static void
mpt_local_task_mgmt(struct mpt_sess *sess, int task_mgmt, int lun)
{
	struct mpt_cmd *cmd, *t;

	TRACE_ENTRY();
	switch (task_mgmt) {
	case IMM_NTFY_TARGET_RESET:
		while (!list_empty(&sess->delayed_cmds)) {
			cmd = list_entry(sess->delayed_cmds.next,
					 typeof(*cmd), delayed_cmds_entry);
			list_del(&cmd->delayed_cmds_entry);
			kfree(cmd);
		}
		break;

	case IMM_NTFY_LUN_RESET1:
	case IMM_NTFY_LUN_RESET2:
	case IMM_NTFY_CLEAR_TS:
	case IMM_NTFY_ABORT_TS1:
	case IMM_NTFY_ABORT_TS2:
		list_for_each_entry_safe(cmd, t, &sess->delayed_cmds,
					 delayed_cmds_entry)
			{
				if (cmd->CMD->lun == lun) {
					list_del(&cmd->delayed_cmds_entry);
					kfree(cmd);
				}
			}
		break;

	case IMM_NTFY_CLEAR_ACA:
	default:
		break;
	}
	TRACE_EXIT();
}

static int
mpt_handle_task_mgmt(MPT_STM_PRIV *priv, u32 reply_word,
		     int task_mgmt, int _lun)
{
	int res = 0, rc = 0;
	struct mpt_mgmt_cmd *mcmd;
	struct mpt_tgt *tgt;
	struct mpt_sess *sess;
	int init_index;
	uint16_t lun = _lun;

	TRACE_ENTRY();

	TRACE_DBG("task_mgmt %d", task_mgmt);
	tgt = priv->tgt;
	init_index = GET_INITIATOR_INDEX(reply_word);

	sess = tgt->sess[init_index];
	if (sess == NULL) {
		TRACE(TRACE_MGMT, "mpt_scst(%s): task mgmt fn %p for "
		      "unexisting session", priv->ioc->name, tgt);
		res = -EFAULT;
		goto out;
	}

	if (test_bit(MPT_SESS_INITING, &sess->sess_flags)) {
		TRACE(TRACE_MGMT, "mpt_scst(%s): task mgmt fn %p for "
		      "inited session", priv->ioc->name, tgt);
		mpt_local_task_mgmt(sess, reply_word, task_mgmt);
		res = -EFAULT;
		goto out;
	}

	mcmd = kmalloc(sizeof(*mcmd), GFP_ATOMIC);
	TRACE_MEM("kmalloc(GFP_ATOMIC) for mcmd (%d): %p",
		  sizeof(*mcmd), mcmd);
	if (mcmd == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of mgmt cmd failed");
		res = -ENOMEM;
		goto out;
	}

	memset(mcmd, 0, sizeof(*mcmd));
	mcmd->sess = sess;
	mcmd->task_mgmt = task_mgmt;

	switch(task_mgmt) {
	case IMM_NTFY_CLEAR_ACA:
		TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_ACA received");
		rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_ACA,
					 (uint8_t *)&lun, sizeof(lun),
					 SCST_ATOMIC, mcmd);
		break;
	case IMM_NTFY_TARGET_RESET:
		TRACE(TRACE_MGMT, "%s", "IMM_NTFY_TARGET_RESET received");
		rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET,
					 (uint8_t *)&lun, sizeof(lun),
					 SCST_ATOMIC, mcmd);
		break;
	case IMM_NTFY_LUN_RESET1:
	case IMM_NTFY_LUN_RESET2:
		TRACE(TRACE_MGMT, "%s", "IMM_NTFY_LUN_RESET received");
		rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET,
					 (uint8_t *)&lun, sizeof(lun),
					 SCST_ATOMIC, mcmd);
		break;
	case IMM_NTFY_CLEAR_TS:
		TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_TS received");
		rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_TASK_SET,
					 (uint8_t *)&lun, sizeof(lun),
					 SCST_ATOMIC, mcmd);
		break;

	case IMM_NTFY_ABORT_TS1:
	case IMM_NTFY_ABORT_TS2:
		TRACE(TRACE_MGMT, "%s", "IMM_NTFY_ABORT_TS received");
		rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET,
					 (uint8_t *)&lun, sizeof(lun),
					 SCST_ATOMIC, mcmd);
		break;

	default:
		PRINT_ERROR("mpt_scst(%s): Unknown task mgmt fn 0x%x",
			    priv->ioc->name, task_mgmt);
		break;
	}
	if (rc != 0) {
		PRINT_ERROR("mpt_scst(%s): scst_rx_mgmt_fn_lun() failed: %d",
			    priv->ioc->name, rc);
		res = -EFAULT;
		goto out_free;
	}

 out:
	TRACE_EXIT_RES(res);
	return res;

 out_free:
	TRACE_MEM("kmem_cache_free for mcmd %p", mcmd);
	kfree(mcmd);
	goto out;
}



/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
 *  called when any target mode reply is received
 *  if mf_req is null, then this is a turbo reply; otherwise it's not
 */
static int
stm_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf_req, MPT_FRAME_HDR *mf_rep)
{
    MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
    MPIDefaultReply_t	*rep = (MPIDefaultReply_t *)mf_rep;
    int	ioc_status;

	TRACE_ENTRY();
	if (mf_req == NULL) {
		TRACE_DBG("%s: got turbo reply, reply %x",
				ioc->name, CAST_PTR_TO_U32(mf_rep));
		/*
		 *  this is a received SCSI command, so go handle it
		 */
		stm_tgt_reply(ioc, CAST_PTR_TO_U32(mf_rep));
		return 0;
	}

#if 0
    if (rep->Function == MPI_FUNCTION_EVENT_NOTIFICATION) {
	/*
	 *  this is an event notification -- do nothing for now
	 *  (this short-cuts the switch() below and avoids the printk)
	 */
	return (0);
    }
#endif
    ioc_status = le16_to_cpu(rep->IOCStatus);

    TRACE_DBG("%s: request %p, reply %p (%02x), %d",
	      ioc->name, mf_req, mf_rep, rep->Function, ioc_status);
    TRACE_DBG("%s: mf index = %d", ioc->name, MF_TO_INDEX(mf_req));

	if (ioc_status & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
		TRACE_DBG("%s Function = %02x, IOCStatus = %04x, IOCLogInfo = %08x",
				ioc->name, rep->Function, ioc_status,
				le32_to_cpu(rep->IOCLogInfo));
	}

    ioc_status &= MPI_IOCSTATUS_MASK;
    switch (rep->Function) {
	case MPI_FUNCTION_CONFIG:
	    /*
	     *  this signals that the config is done
	     */
	    priv->config_pending = 0;
	    memcpy(&priv->config_rep, rep, sizeof(ConfigReply_t));
	    /*
	     *  don't free the message frame, since we're remembering it
	     *  in priv->config_mf, and we'll be using it over and over
	     */
	    return (0);

	case MPI_FUNCTION_PORT_ENABLE:
	    /*
	     *  this signals that the port enable is done
	     */
	    priv->port_enable_loginfo = le32_to_cpu(rep->IOCLogInfo);
	    priv->port_enable_pending = 0;
	    return (1);

	case MPI_FUNCTION_TARGET_CMD_BUFFER_POST:
	case MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST:
	    /*
	     *  this is the response to a command buffer post; if status
	     *  is success, then this just acknowledges the posting of a
	     *  command buffer, so do nothing
	     *
	     *  we can also get here for High Priority I/O (such as getting
	     *  a command while not being allowed to disconnect from the SCSI
	     *  bus), and if we're shutting down
	     */
		if (ioc_status == MPI_IOCSTATUS_SUCCESS) {
			TRACE_EXIT();
			return 1;
		}
		if (priv->target_mode_abort_pending &&
				ioc_status == MPI_IOCSTATUS_TARGET_ABORTED) {
			TRACE_EXIT();
			return (0);
		}
		if (ioc_status == MPI_IOCSTATUS_TARGET_PRIORITY_IO) {
			stm_tgt_reply_high_pri(ioc,
					(TargetCmdBufferPostErrorReply_t *)rep);
			TRACE_EXIT();
			return (0);
		}
		TRACE_DBG(":%s TargetCmdBufPostReq IOCStatus = %04x",
				ioc->name, ioc_status);
		if (ioc_status == MPI_IOCSTATUS_INSUFFICIENT_RESOURCES) {
			/*
			 *  this should never happen since we carefully count
			 *  our resources, but if it does, tolerate it -- don't
			 *  repost the errant command buffer, lest we create an
			 *  endless loop
			 */
			WARN_ON(1);
			return (0);
		}
		if (ioc_status == MPI_IOCSTATUS_TARGET_NO_CONNECTION) {
			printk(KERN_ERR MYNAM
					": %s: Got MPI_IOCSTATUS_TARGET_NO_CONNECTION\n",
					ioc->name);
			return (0);
		}
		if (rep->MsgLength > sizeof(*rep)/sizeof(u32)) {
			TRACE_DBG("MsgLength is %d, %d",
					rep->MsgLength, sizeof(*rep)/sizeof(u32));
			WARN_ON(1);
			/*
			 *  the TargetCmdBufferPostErrorReply and TargetErrorReply
			 *  structures are nearly identical; the exception is that
			 *  the former does not have a TransferCount field, while
			 *  the latter does; add one
			 */
			((TargetErrorReply_t *)rep)->TransferCount = 0;
			stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
			return (0);
		}
		WARN_ON(1);
	    return (1);

	case MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST:
	    /*
	     *  this signals that the command buffer base post is done
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s TargetCmdBufPostBaseReq IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    return (1);

	case MPI_FUNCTION_TARGET_ASSIST:
	    /*
	     *  this is the response to a target assist command; we should
	     *  only get here if an error occurred
	     *
	     *  at this point we need to clean up the remains of the I/O
	     *  and repost the command buffer
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s TargetAssistReq IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
	    return (0);

	case MPI_FUNCTION_TARGET_STATUS_SEND:
	    /*
	     *  this is the response to a target status send command; we should
	     *  only get here if an error occurred
	     *
	     *  at this point we need to clean up the remains of the I/O
	     *  and repost the command buffer
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		/* if this is a MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT
		 * and we're SCSI, only print if we're debugging and
		 * tracing.  This is a normal consequence of attempting
		 * to send sense data and status in the same
		 * transaction.
		 */
		if (IsScsi(priv) &&
			(ioc_status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT)) {
			TRACE_DBG(MYNAM ":%s TargetStatusSendReq IOCStatus = %04x\n",
				ioc->name, ioc_status);
		} else {
			printk(KERN_ERR MYNAM ":%s TargetStatusSendReq IOCStatus = %04x\n",
				ioc->name, ioc_status);
		}
	    }
	    stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
	    return (0);

	case MPI_FUNCTION_TARGET_MODE_ABORT: {
	    TargetModeAbort_t		*req = (TargetModeAbort_t *)mf_req;

	    /*
	     *  this signals that the target mode abort is done
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s TargetModeAbort IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    if (req->AbortType == TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS) {
		priv->target_mode_abort_pending = 0;
	    } else {
		u32		reply_word;
		int		index;
		volatile int	*io_state;

		/*
		 *  a target mode abort has finished, so check to see if
		 *  the I/O was aborted, but there was no error reply for
		 *  that aborted I/O (this will be the case for I/Os that
		 *  have no outstanding target assist or target status send
		 *  at the time of the abort request) -- so pretend that
		 *  the error reply came in with a status indicating that
		 *  the I/O was aborted
		 */
		reply_word = le32_to_cpu(req->ReplyWord);
		index = GET_IO_INDEX(reply_word);
		io_state = priv->io_state + index;
		if ((*io_state & IO_STATE_ABORTED) &&
		    !(*io_state & IO_STATE_DATA_SENT) &&
		    !(*io_state & IO_STATE_STATUS_SENT)) {
		    stmapp_target_error(priv, reply_word, index,
					MPI_IOCSTATUS_TARGET_ABORTED, 0);
		}
		/*
		*  see if we were trying to abort a target assist or target
		*  status send, but the abort didn't work (if the abort had
		*  worked, the flag we're checking would be clear) -- if so,
		*  just clear the various SRR flags, and wait for the initiator
		*  to retry the SRR
		*/
		if (*io_state & IO_STATE_REQUEST_ABORTED) {
		    printk(KERN_ERR MYNAM ":%s index %d: io_state = %x\n",
			   ioc->name, index, *io_state);
		    printk(KERN_ERR MYNAM ":%s   request was not aborted\n",
			   ioc->name);
		    *io_state &= ~IO_STATE_REQUEST_ABORTED;
		    *io_state &= ~IO_STATE_REISSUE_REQUEST;
		    *io_state &= ~IO_STATE_ADJUST_OFFSET;
		    *io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
		    *io_state &= ~IO_STATE_REDO_COMMAND;
		}
	    }
		TRACE_EXIT_RES(1);
	    return (1);
	}

	case MPI_FUNCTION_FC_LINK_SRVC_BUF_POST:
	    /*
	     *  if the length is that of a default reply, then this is the
	     *  response to a link service buffer post -- do nothing except
	     *  report errors (none are expected); otherwise this is a
	     *  received ELS, so go handle it
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		if (priv->link_serv_abort_pending &&
		    ioc_status == MPI_IOCSTATUS_FC_ABORTED) {
		    return (0);
		}
		printk(KERN_ERR MYNAM ":%s FcLinkServBufPostReq IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    if (rep->MsgLength > sizeof(*rep)/sizeof(u32)) {
		stm_link_service_reply(ioc,
				       (LinkServiceBufferPostReply_t *)rep);
		return (0);
	    }
	    return (1);

	case MPI_FUNCTION_FC_LINK_SRVC_RSP:
	    /*
	     *  this is the response to a link service send -- repost the
	     *  link service command buffer
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s FcLinkServRspReq IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    stm_link_service_rsp_reply(ioc,
				       (LinkServiceRspRequest_t *)mf_req,
				       (LinkServiceRspReply_t *)mf_rep);
	    return (1);

	case MPI_FUNCTION_FC_ABORT:
	    /*
	     *  this signals that the target mode abort is done
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s FcAbort IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    priv->link_serv_abort_pending = 0;
	    return (1);

	case MPI_FUNCTION_FC_PRIMITIVE_SEND:
	    /*
	     *  this signals that the FC primitive send is done
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s FcPrimitiveSend IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    priv->fc_primitive_send_pending = 0;
	    return (1);

	case MPI_FUNCTION_FC_EX_LINK_SRVC_SEND:
	    /*
	     *  this signals that the extended link service send is done
	     */
	    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
		printk(KERN_ERR MYNAM ":%s ExLinkServiceSend IOCStatus = %04x\n",
		       ioc->name, ioc_status);
	    }
	    priv->ex_link_service_send_pending = 0;
	    return (1);

	default:
	    /*
	     *  don't understand this reply, so dump to the screen
	     */
	    printk(KERN_ERR MYNAM ":%s got a reply (function %02x) that "
		   "I don't know what to do with\n", ioc->name, rep->Function);
if(1)
{
	    u32 *p = (u32 *)mf_req;
	    int i;

	    for (i = 0; i < 16; i++) {
		printk("%s mf_req[%02x] = %08x\n",
		       ioc->name, i * 4, le32_to_cpu(p[i]));
	    }
}
if(1)
{
	    u32 *p = (u32 *)mf_rep;
	    int i;

	    for (i = 0; i < 16; i++) {
		printk("%s mf_rep[%02x] = %08x\n",
		       ioc->name, i * 4, le32_to_cpu(p[i]));
	    }
}
	    break;
    }
	TRACE_EXIT();
    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_tgt_reply_high_pri(MPT_ADAPTER *ioc, TargetCmdBufferPostErrorReply_t *rep)
{
	MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
	u32			reply_word;
	int			reason;
	int			index;

	TRACE_ENTRY();
	reply_word = le32_to_cpu(rep->ReplyWord);
	reason = rep->PriorityReason;

	index = GET_IO_INDEX(reply_word);

	TRACE_DBG("%s: target reply high priority", ioc->name);
	TRACE_DBG("%s: ReplyWord = %08x, PriorityReason = %02x",
			ioc->name, reply_word, reason);

	priv->io_state[index] |= IO_STATE_HIGH_PRIORITY;
	if (reason == PRIORITY_REASON_NO_DISCONNECT ||
			reason == PRIORITY_REASON_SCSI_TASK_MANAGEMENT) {
		stm_tgt_reply(ioc, reply_word);
		goto out;
	}

	WARN_ON(1);
	if (reason == PRIORITY_REASON_TARGET_BUSY) {
		CMD		*cmd;
		int		lun;
		int		tag;

		priv->io_state[index] &= ~IO_STATE_POSTED;
		cmd = priv->hw->cmd_buf + index;
		if (IsScsi(priv)) {
			SCSI_CMD	*scsi_cmd = (SCSI_CMD *)cmd->cmd;

			lun = get2bytes(scsi_cmd->LogicalUnitNumber, 0);
			tag = scsi_cmd->Tag;
		} else if (IsSas(priv)) {
			SSP_CMD	*ssp_cmd = (SSP_CMD *)cmd->cmd;

			lun = get2bytes(ssp_cmd->LogicalUnitNumber, 0);
			tag = ssp_cmd->InitiatorTag;
		} else {
			FCP_CMD	*fcp_cmd = (FCP_CMD *)cmd->cmd;

			lun = get2bytes(fcp_cmd->FcpLun, 0);
			tag = 0;
		}
		memset(cmd->rsp, 0, sizeof(cmd->rsp));
		stmapp_set_status(priv, cmd, STS_TASK_SET_FULL);
		stm_send_target_status(priv, reply_word, index, 0, lun, tag);
	} else {
		stmapp_target_error(priv, reply_word, index,
				MPI_IOCSTATUS_TARGET_PRIORITY_IO, reason);
	}
out:
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_target_reply_error(MPT_ADAPTER *ioc,
		       TargetErrorReply_t *rep)
{
    MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
    u32			reply_word;
    int			index;
    int			status;
    int			reason;
    volatile int	*io_state;

	TRACE_ENTRY();
    reply_word = le32_to_cpu(rep->ReplyWord);
    status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;

    index = GET_IO_INDEX(reply_word);

    io_state = priv->io_state + index;

	if (status == MPI_IOCSTATUS_TARGET_PRIORITY_IO) {
		reason = rep->PriorityReason;
		*io_state |= IO_STATE_HIGH_PRIORITY;
	} else {
		reason = 0;
	}

    TRACE_DBG("%s: target reply error", ioc->name);
    TRACE_DBG("%s: ReplyWord = %08x, IOCStatus = %04x",
	   ioc->name, reply_word, status);

	if (*io_state & IO_STATE_REQUEST_ABORTED) {
		TRACE_DBG("%s: index %d: io_state = %x",
				ioc->name, index, *io_state);
		TRACE_DBG("%s:   request was aborted", ioc->name);
		*io_state &= ~IO_STATE_REQUEST_ABORTED;
		if (*io_state & IO_STATE_REISSUE_REQUEST) {
			*io_state &= ~IO_STATE_REISSUE_REQUEST;
			TRACE_DBG("%s:   being reissued", ioc->name);
			if (*io_state & IO_STATE_ADJUST_OFFSET) {
				*io_state &= ~IO_STATE_ADJUST_OFFSET;
				stmapp_srr_adjust_offset(priv, index);
			}
			if (*io_state & IO_STATE_CONVERT_TA_TO_TSS) {
				*io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
				stmapp_srr_convert_ta_to_tss(priv, index);
				goto out;
			}
			if (*io_state & IO_STATE_REDO_COMMAND) {
				*io_state &= ~IO_STATE_REDO_COMMAND;
				*io_state &= ~IO_STATE_DATA_SENT;
				*io_state &= ~IO_STATE_STATUS_SENT;
				mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
				stmapp_tgt_command(priv, reply_word);
				goto out;
			}
			mpt_put_msg_frame(stm_context, _IOC_ID, priv->current_mf[index]);
			goto out;
		}
	}

    stmapp_target_error(priv, reply_word, index, status, reason);
out:
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_target_cleanup(MPT_STM_PRIV *priv,
		   int index)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    volatile int	*io_state;

	TRACE_ENTRY();
	io_state = priv->io_state + index;
	if (*io_state & (IO_STATE_DATA_SENT | IO_STATE_STATUS_SENT)) {
		*io_state &= ~IO_STATE_DATA_SENT;
		*io_state &= ~IO_STATE_STATUS_SENT;
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
	}
	if (*io_state & IO_STATE_STATUS_DEFERRED) {
		*io_state &= ~IO_STATE_STATUS_DEFERRED;
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->status_deferred_mf[index]);
	}
	*io_state &= ~IO_STATE_REISSUE_REQUEST;
	*io_state &= ~IO_STATE_ADJUST_OFFSET;
	*io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
	*io_state &= ~IO_STATE_REDO_COMMAND;
	*io_state &= ~IO_STATE_REQUEST_ABORTED;
	*io_state &= ~IO_STATE_INCOMPLETE;
	//  *io_state &= ~IO_STATE_AUTO_REPOST;
	*io_state &= ~IO_STATE_ABORTED;
	*io_state &= ~IO_STATE_POSTED;
	if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == IO_STATE_AUTO_REPOST) {
		*io_state = IO_STATE_POSTED;
	} else if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == 0) {
		stm_cmd_buf_post(priv, index);
	}
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_event_process(MPT_ADAPTER *ioc,
		  EventNotificationReply_t *rep)
{
    MPT_STM_PRIV		*priv = mpt_stm_priv[ioc->id];
    EventDataScsi_t		*scsi_data;
    EventDataLinkStatus_t	*link_status_data;
    EventDataLoopState_t	*loop_state_data;
    EventDataLogout_t		*logout_data;
    EventDataSasPhyLinkStatus_t *sas_phy_link_status_data;
    int				id;
    int				i;
    int				ioc_status;
    int				event;
    int				rate;

	TRACE_ENTRY();
    if (priv == NULL) {
		return (1);
	}

    ioc_status = le16_to_cpu(rep->IOCStatus);
    event = le32_to_cpu(rep->Event);

	if (ioc_status & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
		printk("%s Event = %x, IOCLogInfo = %08x\n",
				ioc->name, event, le32_to_cpu(rep->IOCLogInfo));
	}

    switch (event) {
	case MPI_EVENT_NONE:
	case MPI_EVENT_LOG_DATA:
	case MPI_EVENT_STATE_CHANGE:
	case MPI_EVENT_UNIT_ATTENTION:
	case MPI_EVENT_EVENT_CHANGE:
	case MPI_EVENT_INTEGRATED_RAID:
	case MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE:
	case MPI_EVENT_ON_BUS_TIMER_EXPIRED:
	case MPI_EVENT_QUEUE_FULL:
	case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
	case MPI_EVENT_SAS_SES:
	case MPI_EVENT_PERSISTENT_TABLE_FULL:
	case MPI_EVENT_SAS_DISCOVERY_ERROR:
	    break;

	case MPI_EVENT_RESCAN:
	    printk("%s Rescan\n", ioc->name);
	    break;

	case MPI_EVENT_IOC_BUS_RESET:
	    scsi_data = (EventDataScsi_t *)rep->Data;
	    printk("%s IOC Bus Reset on port %d\n",
		   ioc->name, scsi_data->BusPort);
	    break;

	case MPI_EVENT_EXT_BUS_RESET:
	    scsi_data = (EventDataScsi_t *)rep->Data;
	    printk("%s Ext Bus Reset on port %d\n",
		   ioc->name, scsi_data->BusPort);
	    /*
	     * clear any pending sense flags on bus reset
	     */
	    if (IsScsi(priv)) {
		    for (i = 0; i < NUM_SCSI_DEVICES; i++) {
			    atomic_set(&priv->pending_sense[i],
					    MPT_STATUS_SENSE_IDLE);
		    }
	    }
	    break;

	case MPI_EVENT_LINK_STATUS_CHANGE:
	    link_status_data = (EventDataLinkStatus_t *)rep->Data;
	    printk("%s Link is now %s\n",
		   ioc->name, link_status_data->State ? "Up" : "Down");
	    break;

	case MPI_EVENT_LOGOUT:
	    logout_data = (EventDataLogout_t *)rep->Data;
	    id = le32_to_cpu(logout_data->NPortID);
	    break;

	case MPI_EVENT_LOOP_STATE_CHANGE:
	    loop_state_data = (EventDataLoopState_t *)rep->Data;
	    if (loop_state_data->Type == MPI_EVENT_LOOP_STATE_CHANGE_LIP) {
		printk("%s LIP Reset\n", ioc->name);
		break;
	    } /* fall-through */

	case MPI_EVENT_SAS_PHY_LINK_STATUS:
	    sas_phy_link_status_data = (EventDataSasPhyLinkStatus_t *)rep->Data;
	    rate = (sas_phy_link_status_data->LinkRates &
		    MPI_EVENT_SAS_PLS_LR_CURRENT_MASK) >>
		     MPI_EVENT_SAS_PLS_LR_CURRENT_SHIFT;
	    printk("%s Phy %d Handle %x is now %s\n",
		   ioc->name, sas_phy_link_status_data->PhyNum,
		   le16_to_cpu(sas_phy_link_status_data->DevHandle),
		   rate == MPI_EVENT_SAS_PLS_LR_RATE_UNKNOWN ? "offline" :
		   rate == MPI_EVENT_SAS_PLS_LR_RATE_PHY_DISABLED ? "disabled" :
		   rate == MPI_EVENT_SAS_PLS_LR_RATE_1_5 ? "online at 1.5 Gb" :
		   rate == MPI_EVENT_SAS_PLS_LR_RATE_3_0 ? "online at 3.0 Gb" :
		   "unknown");
	    break;

	default:
	    printk("%s event = %d, ack = %d, length = %d\n",
		   ioc->name, le32_to_cpu(rep->Event),
		   rep->AckRequired, le16_to_cpu(rep->EventDataLength));
	    for (i = 0; i < le16_to_cpu(rep->EventDataLength); i++) {
		printk("%s data[%d] = %08x\n",
		       ioc->name, i, le32_to_cpu(rep->Data[i]));
	    }
	    break;
    }

    if (event == MPI_EVENT_EXT_BUS_RESET) {
#if 0
	if (IsScsi(priv)) {
	    memset(priv->luns->drop, 0, sizeof(priv->luns->drop));
	}
#endif
    }
	TRACE_EXIT();

    return (1);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_reset_process(MPT_ADAPTER *ioc, int phase)
{
    MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
    int			i;

	TRACE_ENTRY();
	if (priv == NULL)
		return (1);

	if (phase == MPT_IOC_PRE_RESET) {
		printk(KERN_ERR MYNAM ":%s IOC will be reset\n",
				ioc->name);
		priv->in_reset = 1;
		priv->config_pending = 0;
		for (i = 0; i < priv->num_cmd_buffers; i++)
			priv->io_state[i] = 0;
	}

	if (phase == MPT_IOC_POST_RESET) {
		printk(KERN_ERR MYNAM ":%s IOC has been reset, restarting now\n",
				ioc->name);
		mpt_stm_adapter_online(priv);
	}

	TRACE_EXIT();

    return (1);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_link_service_reply(MPT_ADAPTER *ioc, LinkServiceBufferPostReply_t *rep)
{
    MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
    FC_ELS		*fc_els_buf;
    int			index;
    int			rctl;
    int			type;
    int			sid;
    int			did;
    int			command;
    int			i;
    u32			wwnnh;
    u32			wwnnl;
    u32			wwpnh;
    u32			wwpnl;
    int			ox_id;
    int			rx_id;
    u32			offset;

	TRACE_ENTRY();
    index = le32_to_cpu(rep->TransactionContext);
    fc_els_buf = &priv->hw->fc_link_serv_buf[index];

    rctl =
	(le32_to_cpu(rep->Rctl_Did) & MPI_FC_RCTL_MASK) >> MPI_FC_RCTL_SHIFT;
    type =
	(le32_to_cpu(rep->Type_Fctl) & MPI_FC_TYPE_MASK) >> MPI_FC_TYPE_SHIFT;
    sid =
	(le32_to_cpu(rep->Csctl_Sid) & MPI_FC_SID_MASK) >> MPI_FC_SID_SHIFT;
    did =
	(le32_to_cpu(rep->Rctl_Did) & MPI_FC_DID_MASK) >> MPI_FC_DID_SHIFT;

    wwnnh = le32_to_cpu(rep->Wwn.NodeNameHigh);
    wwnnl = le32_to_cpu(rep->Wwn.NodeNameLow);
    wwpnh = le32_to_cpu(rep->Wwn.PortNameHigh);
    wwpnl = le32_to_cpu(rep->Wwn.PortNameLow);

    ox_id = le16_to_cpu(rep->Oxid);
    rx_id = le16_to_cpu(rep->Rxid);

    /*
     *  if this is a received PRLI/PRLO, respond by sending our own PRLI/PRLO
     */
    if (rctl == ELS && type == 0x01) {
	command = (be32_to_cpu(fc_els_buf->fc_els[0]) >> 24) & 0xff;
	switch (command) {
	    case PRLI:
		TRACE_DBG("%s: PRLI to %06x from %06x (wwn %08x%08x)",
				ioc->name, did, sid, wwpnh, wwpnl);
		i = be32_to_cpu(fc_els_buf->fc_els[4]);
		fc_els_buf->fc_els[0] = cpu_to_be32(0x02100014);
		fc_els_buf->fc_els[1] = cpu_to_be32(0x08002100);
		fc_els_buf->fc_els[2] = cpu_to_be32(0x00000000);
		fc_els_buf->fc_els[3] = cpu_to_be32(0x00000000);
		fc_els_buf->fc_els[4] = cpu_to_be32(0x00000012);
		if (priv->fcp2_capable)
		    fc_els_buf->fc_els[4] |= cpu_to_be32(0x100);
		priv->els_state[index] = PRLI;
		stm_send_els(priv, rep, index, 20);
		return;

	    case PRLO:
		TRACE_DBG("%s: PRLO to %06x from %06x (wwn %08x%08x)",
			ioc->name, did, sid, wwpnh, wwpnl);
		fc_els_buf->fc_els[0] = cpu_to_be32(0x02100014);
		fc_els_buf->fc_els[1] = cpu_to_be32(0x08000100);
		fc_els_buf->fc_els[2] = cpu_to_be32(0x00000000);
		fc_els_buf->fc_els[3] = cpu_to_be32(0x00000000);
		fc_els_buf->fc_els[4] = cpu_to_be32(0x00000000);
		priv->els_state[index] = PRLO;
		stm_send_els(priv, rep, index, 20);
		return;

	    case RSCN:
		TRACE_DBG("%s: RSCN", ioc->name);
		stm_link_serv_buf_post(priv, index);
		return;

	    default:
		TRACE_DBG("%s: ELS %02x to %06x from %06x (wwn %08x%08x)",
			ioc->name, command, did, sid, wwpnh, wwpnl);
		stm_link_serv_buf_post(priv, index);
		return;
	}
    }

    /*
     *  if this is a received ABTS, respond by aborting the I/O and then
     *  accepting it
     */
    if (rctl == ABTS && type == 0x00) {
	TRACE_DBG("%s: ABTS to %06x from %06x (wwn %08x%08x)",
		ioc->name, did, sid, wwpnh, wwpnl);
	fc_els_buf->fc_els[0] = cpu_to_be32(0x00000000);
	fc_els_buf->fc_els[1] = cpu_to_be32((ox_id << 16) | (rx_id << 0));
	fc_els_buf->fc_els[2] = cpu_to_be32(0x0000ffff);
	rep->Rctl_Did += cpu_to_le32((BA_ACC - ABTS) << MPI_FC_RCTL_SHIFT);
	priv->els_state[index] = ABTS;
	stmapp_abts_process(priv, rx_id, rep, index);
	stm_send_els(priv, rep, index, 12);
	return;
    }

    /*
     *  if this is a received SRR, respond by aborting any current TargetAssist
     *  or TargetStatusSend commands, accepting the SRR, and retransmitting the
     *  requested data or status
     */
    if (rctl == FC4LS && type == 0x08) {
	priv->els_state[index] = FC4LS;
	command = (be32_to_cpu(fc_els_buf->fc_els[0]) >> 24) & 0xff;
	switch (command) {
	    case SRR:
		TRACE_DBG("%s: SRR to %06x from %06x (wwn %08x%08x)",
			ioc->name, did, sid, wwpnh, wwpnl);
		rx_id = be32_to_cpu(fc_els_buf->fc_els[1]) & 0xffff;
		/*
		 *  if the rx_id is out of range, reject this SRR with
		 *  "invalid OX_ID/RX_ID combination"
		 */
		if (rx_id >= priv->num_cmd_buffers) {
		    fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
		    fc_els_buf->fc_els[1] = cpu_to_be32(0x00090300);
		    stm_send_els(priv, rep, index, 8);
		    return;
		}
		i = (be32_to_cpu(fc_els_buf->fc_els[3]) >> 24) & 0xff;
		/*
		 *  if the IU to retransmit is not a recognized IU, reject
		 *  this SRR with "logical error"
		 */
		if (i != 1 && i != 5 && i != 7) {
		    fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
		    fc_els_buf->fc_els[1] = cpu_to_be32(0x00030000);
		    stm_send_els(priv, rep, index, 8);
		    return;
		}
		offset = be32_to_cpu(fc_els_buf->fc_els[2]);
		/*
		 *  go process this SRR further
		 *
		 *  make the call to stm_send_els when any request in progress
		 *  has been aborted
		 *
		 *  note that the address of the LinkServiceBufferPostReply
		 *  that's passed as a parameter to stmapp_abts_process CANNOT
		 *  BE REMEMBERED; its contents must be copied if the call to
		 *  stm_send_els will not be made synchronously
		 */
		stmapp_srr_process(priv, rx_id, i, offset, rep, index);
		return;

	    default:
		TRACE_DBG("%s: FC4LS %02x to %06x from %06x (wwn %08x%08x)",
			ioc->name, command, did, sid, wwpnh, wwpnl);
		/*
		 *  the only FC4LS we recognize is SRR; all others get
		 *  rejected with "command not supported"
		 */
		fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
		fc_els_buf->fc_els[1] = cpu_to_be32(0x000b0000);
		stm_send_els(priv, rep, index, 8);
		return;
	}
    }

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)rep;
		int i;

		for (i = 0; i < 17; i++) {
			TRACE(TRACE_MPI, "%s: fc_els[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
	if(trace_mpi)
	{
		u32 *p = (u32 *)fc_els_buf;
		int i;

		for (i = 0; i < (int)le32_to_cpu(rep->TransferLength) / 4; i++) {
			TRACE(TRACE_MPI, "%s: fc_els_buf[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif

    stm_link_serv_buf_post(priv, index);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_link_service_rsp_reply(MPT_ADAPTER *ioc,
			   LinkServiceRspRequest_t *req,
			   LinkServiceRspReply_t *rep)
{
    MPT_STM_PRIV	*priv = mpt_stm_priv[ioc->id];
    MPT_STM_SIMPLE	*sge_simple;
    int			*p_index;
    int			index;
    int			sid;
    int			did;
    int			els;
    int			init_index;

	TRACE_ENTRY();
    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    p_index = (int *)(sge_simple + 1);
    index = *p_index;
    els = priv->els_state[index];

    sid = (le32_to_cpu(req->Csctl_Sid) & MPI_FC_SID_MASK) >> MPI_FC_SID_SHIFT;
    did = (le32_to_cpu(req->Rctl_Did) & MPI_FC_DID_MASK) >> MPI_FC_DID_SHIFT;
    init_index = le32_to_cpu(rep->InitiatorIndex);
    /*
     *  after our link service reponse has been sent, repost the link service
     *  buffer
     */
    priv->els_state[index] = 0;
    stm_link_serv_buf_post(priv, index);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_cmd_buf_post(MPT_STM_PRIV *priv, int index)
{
    MPT_ADAPTER	*ioc = priv->ioc;
    TargetCmdBufferPostRequest_t *req;
    dma_addr_t	dma_addr;

	TRACE_ENTRY();
	if (priv->exiting) {
		priv->io_state[index] |= IO_STATE_POSTED;
		return;
	}

	if (IsSas(priv)) {
		stm_cmd_buf_post_list(priv, index);
		return;
	}

    /*
     *  get a free message frame, and post a command buffer
     */
    req = (TargetCmdBufferPostRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

#ifdef CMD_BUFFER_POST_FLAGS_HIGH_PRIORITY
	if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
		req->BufferPostFlags = CMD_BUFFER_POST_FLAGS_HIGH_PRIORITY;
	}
#else
	priv->io_state[index] &= ~IO_STATE_HIGH_PRIORITY;
#endif

    req->BufferCount = 1;
    req->Function = MPI_FUNCTION_TARGET_CMD_BUFFER_POST;
    req->BufferLength = sizeof(priv->hw->cmd_buf[index].cmd);
    req->Buffer[0].IoIndex = cpu_to_le16(index);
    dma_addr = priv->hw_dma +
	((u8 *)priv->hw->cmd_buf[index].cmd - (u8 *)priv->hw);
    req->Buffer[0].u.PhysicalAddress64.Low = cpu_to_le32(dma_addr);
#if MPT_STM_64_BIT_DMA
    req->Buffer[0].u.PhysicalAddress64.High = cpu_to_le32((u64)dma_addr>>32);
    req->BufferPostFlags = CMD_BUFFER_POST_FLAGS_64_BIT_ADDR;
#endif

    priv->io_state[index] |= IO_STATE_POSTED;

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi) {
		u32 *p = (u32 *)req;
		int i;

		TRACE(TRACE_MPI, "%s: stm_cmd_buf_post %d", ioc->name, index);
		for (i = 0; i < sizeof(*req) / 4; i++) {
			TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif

	if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
		priv->io_state[index] &= ~IO_STATE_HIGH_PRIORITY;
		mpt_send_handshake_request(stm_context, _IOC_ID,
				sizeof(*req), (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	}

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_cmd_buf_post_base(MPT_STM_PRIV *priv,
		      int post_all)
{
    MPT_ADAPTER				*ioc = priv->ioc;
    TargetCmdBufferPostBaseRequest_t	*req;
    int					i;
    dma_addr_t				dma_addr;

	TRACE_ENTRY();
    req = (TargetCmdBufferPostBaseRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    if (post_all) {
	req->BufferPostFlags = CMD_BUFFER_POST_BASE_FLAGS_AUTO_POST_ALL;
    }
    req->Function = MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST;
    req->TotalCmdBuffers = cpu_to_le16(priv->num_cmd_buffers);
    req->CmdBufferLength = cpu_to_le16(sizeof(priv->hw->cmd_buf[0].cmd));
    req->NextCmdBufferOffset = cpu_to_le16(sizeof(priv->hw->cmd_buf[0]));
    dma_addr = priv->hw_dma +
	((u8 *)priv->hw->cmd_buf[0].cmd - (u8 *)priv->hw);
    req->BaseAddressLow = cpu_to_le32(dma_addr);
#if MPT_STM_64_BIT_DMA
    req->BaseAddressHigh = cpu_to_le32((u64)dma_addr>>32);
#endif

    if (post_all) {
	for (i = 0; i < priv->num_cmd_buffers; i++) {
	    priv->io_state[i] |= IO_STATE_POSTED;
	}
    }

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)req;
		int i;

		TRACE(TRACE_MPI, "%s: stm_cmd_buf_post_base", ioc->name);
		for (i = 0; i < sizeof(*req) / 4; i++) {
			TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif

    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_cmd_buf_post_list(MPT_STM_PRIV *priv,
		      int index)
{
    MPT_ADAPTER				*ioc = priv->ioc;
    TargetCmdBufferPostListRequest_t	*req;

	TRACE_ENTRY();
    req = (TargetCmdBufferPostListRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST;
    req->CmdBufferCount = cpu_to_le16(1);
    req->IoIndex[0] = cpu_to_le16(index);

    priv->io_state[index] |= IO_STATE_POSTED;

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)req;
		int i;

		TRACE(TRACE_MPI, "%s: stm_cmd_buf_post_list %d", ioc->name, index);
		for (i = 0; i < sizeof(*req) / 4; i++) {
			TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif

	mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_link_serv_buf_post(MPT_STM_PRIV *priv, int index)
{
    MPT_ADAPTER				*ioc = priv->ioc;
    LinkServiceBufferPostRequest_t	*req;
    SGETransaction32_t			*sge_trans;
    MPT_STM_SIMPLE			*sge_simple;
    dma_addr_t				dma_addr;

	TRACE_ENTRY();
    req = (LinkServiceBufferPostRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->BufferCount = 1;
    req->Function = MPI_FUNCTION_FC_LINK_SRVC_BUF_POST;
    sge_trans = (SGETransaction32_t *)&req->SGL;
    sge_trans->ContextSize = 4;
    sge_trans->DetailsLength = 0;
    sge_trans->Flags = 0;
    sge_trans->TransactionContext[0] = cpu_to_le32(index);
    sge_simple = (MPT_STM_SIMPLE *)&sge_trans->TransactionDetails[0];
    sge_simple->FlagsLength = cpu_to_le32(sizeof(FC_ELS) |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma +
	((u8 *)priv->hw->fc_link_serv_buf[index].fc_els - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)req;
		int i;

		TRACE(TRACE_MPI, "%s: stm_link_serv_buf_post %d", ioc->name, index);
		for (i = 0; i < sizeof(*req) / 4; i++) {
			TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif
    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_send_target_status(MPT_STM_PRIV *priv,
		       u32 reply_word, int index, int flags, int lun, int tag)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    TargetStatusSendRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    CMD				*cmd;
    int				length;
    int				status;
    int				init_index;
    dma_addr_t			dma_addr;

    TRACE_ENTRY();
    req = (TargetStatusSendRequest_t *)mpt_msg_frame_alloc(ioc,index);
    memset(req, 0, sizeof(*req));

    if (priv->exiting) {
	flags &= ~TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
	priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
    }

    if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
	flags |= TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY;
	flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
	priv->io_state[index] |= IO_STATE_AUTO_REPOST;
    }

    if (priv->fcp2_capable/* && priv->initiators != NULL*/) {
	init_index = GET_INITIATOR_INDEX(reply_word);
	/*init = priv->initiators[init_index];
	if (init != NULL && init->confirm_capable) {
	    flags |= TARGET_STATUS_SEND_FLAGS_CONFIRMED;
	}*/
    }

    cmd = priv->hw->cmd_buf + index;

    if (flags & TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS) {
	length = 0;
	req->StatusCode = 0;
    } else {
	length = 0;
	if (IsScsi(priv)) {
	    SCSI_RSP	*rsp = (SCSI_RSP *)cmd->rsp;
	    size_t sense_size;

	    length += sizeof(*rsp);
	    length -= sizeof(rsp->SenseData);
	    status = rsp->Status;
	    if (rsp->Valid & SCSI_SENSE_LEN_VALID) {
		length += be32_to_cpu(rsp->SenseDataListLength);
		init_index = GET_INITIATOR_INDEX(reply_word);
		/*
		 *  try to avoid a SCSI firmware bug by not using Auto Repost
		 *  here, unless required (High Priority requires it)
		 */
		if (!(priv->io_state[index] & IO_STATE_HIGH_PRIORITY)) {
		    flags &= ~TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
		    priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
		}
		/*
		 * cache sense buffer so we can send it on the next
		 * REQUEST SENSE command if the IOC can't send the
		 * status and sense simultaneously (generating
		 * MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT IOCStatus)
		 */
		sense_size = min(sizeof(rsp->SenseData),
				       (size_t)SCSI_SENSE_BUFFERSIZE);
		TRACE_DBG("caching %d bytes pending sense", sense_size);
		memcpy(priv->pending_sense_buffer[init_index],
				rsp->SenseData, sense_size);
		TRACE_BUFFER("priv->pending_sense_buffer",
				priv->pending_sense_buffer[init_index],
				sense_size);
		atomic_set(&priv->pending_sense[init_index],
				MPT_STATUS_SENSE_ATTEMPT);
	    }
	    if (rsp->Valid & SCSI_RSP_LEN_VALID) {
		length += be32_to_cpu(rsp->PktFailuresListLength);
	    }
	} else if (IsSas(priv)) {
	    SSP_RSP	*rsp = (SSP_RSP *)cmd->rsp;

	    length += sizeof(*rsp);
	    length -= sizeof(rsp->ResponseSenseData);
	    status = rsp->Status;
	    if (rsp->DataPres & SSP_SENSE_LEN_VALID) {
		length += be32_to_cpu(rsp->SenseDataLength);
	    }
	    if (rsp->DataPres & SSP_RSP_LEN_VALID) {
		length += be32_to_cpu(rsp->ResponseDataLength);
	    }
	} else {
	    FCP_RSP	*rsp = (FCP_RSP *)cmd->rsp;

	    length += sizeof(*rsp);
	    length -= sizeof(rsp->FcpSenseData) + sizeof(rsp->FcpResponseData);
	    status = rsp->FcpStatus;
	    if (flags & TARGET_STATUS_SEND_FLAGS_CONFIRMED) {
		rsp->FcpFlags |= FCP_REQUEST_CONFIRM;
	    }
	    if (rsp->FcpFlags & FCP_SENSE_LEN_VALID) {
		length += be32_to_cpu(rsp->FcpSenseLength);
	    }
	    if (rsp->FcpFlags & FCP_RSP_LEN_VALID) {
		length += be32_to_cpu(rsp->FcpResponseLength);
		/* FCP_RSP_LEN_VALID will only be set for Task Mgmt responses */
		/* and Task Mgmt responses can't be confirmed */
		rsp->FcpFlags &= ~FCP_REQUEST_CONFIRM;
		flags &= ~TARGET_STATUS_SEND_FLAGS_CONFIRMED;
	    }
	}
	req->StatusCode = (u8)status;
    }

    req->StatusFlags = (u8)flags;
    req->Function = MPI_FUNCTION_TARGET_STATUS_SEND;
    req->QueueTag = (u16)tag;
    req->ReplyWord = cpu_to_le32(reply_word);
    req->LUN[0] = (u8)(lun >> 8);
    req->LUN[1] = (u8)lun;
    if (length != 0) {
	sge_simple = (MPT_STM_SIMPLE *)&req->StatusDataSGE;
	sge_simple->FlagsLength = cpu_to_le32(length |
	    MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			      MPI_SGE_FLAGS_LAST_ELEMENT |
			      MPI_SGE_FLAGS_END_OF_BUFFER |
			      MPI_SGE_FLAGS_END_OF_LIST |
			      MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			      MPI_SGE_FLAGS_HOST_TO_IOC));
	dma_addr = priv->hw_dma +
	    ((u8 *)priv->hw->cmd_buf[index].rsp - (u8 *)priv->hw);
	stm_set_dma_addr(sge_simple->Address, dma_addr);
    }

    /*
     *  there's a limitation here -- if target data is outstanding, we must
     *  wait for it to finish before we send the target status
     */
    if (priv->io_state[index] & IO_STATE_DATA_SENT) {
	    priv->status_deferred_mf[index] = (MPT_FRAME_HDR *)req;
	    priv->io_state[index] |= IO_STATE_STATUS_DEFERRED;
	    TRACE_EXIT_RES(1);
	    return (1);
    }
    priv->io_state[index] |= IO_STATE_STATUS_SENT;

#ifdef CONFIG_SCST_TRACING
    if(trace_mpi)
    {
	    u32 *p = (u32 *)req;
	    int i;

	    TRACE(TRACE_MPI, "%s: stm_send_target_status %d",
			    ioc->name, index);
	    for (i = 0; i < sizeof(*req) / 4; i++) {
		    TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
				    ioc->name, i * 4, le32_to_cpu(p[i]));
	    }
    }
#endif

    if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
	    mpt_send_handshake_request(stm_context, _IOC_ID,
			    sizeof(*req), (u32 *)req _HS_SLEEP);
    } else {
	    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
    }
    TRACE_EXIT_RES(1);
    return (1);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_send_els(MPT_STM_PRIV *priv,
	     LinkServiceBufferPostReply_t *rep, int index, int length)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    LinkServiceRspRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    int				*p_index;
    dma_addr_t			dma_addr;

	TRACE_ENTRY();
    req = (LinkServiceRspRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->RspLength = (u8)length;
    req->Function = MPI_FUNCTION_FC_LINK_SRVC_RSP;
    memcpy((u8 *)req + 0x0c, (u8 *)rep + 0x1c, 24);
    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(length |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma +
	((u8 *)priv->hw->fc_link_serv_buf[index].fc_els - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    p_index = (int *)(sge_simple + 1);
    *p_index = index;

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)req;
		int i;

		TRACE(TRACE_MPI, "%s: stm_send_els %d", ioc->name, index);
		for (i = 0; i < sizeof(*req) / 4; i++) {
			TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif
    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_port_enable(MPT_STM_PRIV *priv)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    PortEnable_t	*req;
	int ret;

	TRACE_ENTRY();
    req = (PortEnable_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_PORT_ENABLE;

    priv->port_enable_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);

    ret = stm_wait_for(priv, &priv->port_enable_pending, 60, NO_SLEEP);

	TRACE_EXIT_RES(ret);

	return ret;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_target_mode_abort_command(MPT_STM_PRIV *priv,
			      u32 reply_word, int index)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    TargetModeAbort_t	*req;

	TRACE_ENTRY();
    req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->AbortType = TARGET_MODE_ABORT_TYPE_EXACT_IO;
    req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;
    req->ReplyWord = cpu_to_le32(reply_word);

    priv->io_state[index] |= IO_STATE_ABORTED;

	if (IsScsi(priv)) {
		mpt_send_handshake_request(stm_context, _IOC_ID,
				sizeof(*req), (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	}
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_target_mode_abort_request(MPT_STM_PRIV *priv,
			      u32 reply_word,
			      u32 msg_context,
			      int index)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    TargetModeAbort_t	*req;

	TRACE_ENTRY();
    req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->AbortType = TARGET_MODE_ABORT_TYPE_EXACT_IO_REQUEST;
    req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;
    req->ReplyWord = cpu_to_le32(reply_word);
    req->MsgContextToAbort = cpu_to_le32(msg_context);

    priv->io_state[index] |= IO_STATE_REQUEST_ABORTED;

	if (IsScsi(priv)) {
		mpt_send_handshake_request(stm_context, _IOC_ID,
				sizeof(*req), (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	}
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_target_mode_abort_all(MPT_STM_PRIV *priv)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    TargetModeAbort_t	*req;
	int ret;
	TRACE_ENTRY();

    req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->AbortType = TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS;
    req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;

    priv->target_mode_abort_pending = 1;

	if (IsScsi(priv)) {
		mpt_send_handshake_request(stm_context, _IOC_ID,
				sizeof(*req), (u32 *)req _HS_SLEEP);
	} else {
		mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
	}

    ret = stm_wait_for(priv, &priv->target_mode_abort_pending, 60, NO_SLEEP);
	TRACE_EXIT_RES(ret);

	return ret;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_target_mode_abort(MPT_STM_PRIV *priv)
{
#ifdef CONFIG_SCST_DEBUG
    MPT_ADAPTER		*ioc = priv->ioc;
#endif
    int			i;
    int			n;

	TRACE_ENTRY();
	while (1) {
		n = 0;
		for (i = 0; i< priv->num_cmd_buffers; i++) {
			if (priv->io_state[i] & IO_STATE_AUTO_REPOST) {
				n++;
			}
		}

		if (n == 0)
			break;

		TRACE_DBG("%s: %d out of %d commands being auto-reposted, waiting...",
				ioc->name, n, priv->num_cmd_buffers);
		stm_wait(priv, 10, CAN_SLEEP);
	}

	while (1) {
		stm_target_mode_abort_all(priv);

		n = 0;
		for (i = 0; i< priv->num_cmd_buffers; i++) {
			if (priv->io_state[i] & IO_STATE_POSTED) {
				n++;
			}
		}

		if (n == priv->num_cmd_buffers)
			break;

		TRACE_DBG("%s: %d out of %d commands still active, waiting...",
				ioc->name, n, priv->num_cmd_buffers);
		stm_wait(priv, 10, CAN_SLEEP);
	}

	TRACE_EXIT();
    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_link_serv_abort(MPT_STM_PRIV *priv)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    FcAbortRequest_t	*req;
	int ret;

	TRACE_ENTRY();

    req = (FcAbortRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->AbortType = FC_ABORT_TYPE_ALL_FC_BUFFERS;
    req->Function = MPI_FUNCTION_FC_ABORT;

    priv->link_serv_abort_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);

    ret = stm_wait_for(priv, &priv->link_serv_abort_pending, 60, NO_SLEEP);

	TRACE_EXIT_RES(ret);

	return ret;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_reset_link(MPT_STM_PRIV *priv)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    FcPrimitiveSendRequest_t	*req;
	int ret;

	TRACE_ENTRY();
    req = (FcPrimitiveSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->SendFlags = MPI_FC_PRIM_SEND_FLAGS_RESET_LINK;
    req->Function = MPI_FUNCTION_FC_PRIMITIVE_SEND;

    priv->fc_primitive_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);

    ret = stm_wait_for(priv, &priv->fc_primitive_send_pending, 60, NO_SLEEP);
	TRACE_EXIT_RES(ret);

	return ret;
}
#if 0
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_login_port(MPT_STM_PRIV *priv, int port_id, int sleep)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    ExLinkServiceSendRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    u32				*buf;
    int				len, ret;
    dma_addr_t			dma_addr;

	TRACE_ENTRY();
    req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
    req->MsgFlags_Did = cpu_to_le32(port_id);
    req->ElsCommandCode	= cpu_to_le32(PLOGI);

    len = 29 * 4;
    buf = priv->hw->exlink_buf;
    memset(buf, 0, len);

    buf[0] = cpu_to_be32(PLOGI << 24);
    /*
     *  the firmware builds the rest of the PLOGI payload
     */

    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    sge_simple++;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_IOC_TO_HOST));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    priv->ex_link_service_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, mf);

    if (stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep) < 0)
	return (-1);

    req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
    req->MsgFlags_Did = cpu_to_le32(port_id);
    req->ElsCommandCode	= cpu_to_le32(PRLI);

    len = 5 * 4;
    buf = priv->hw->exlink_buf;
    memset(buf, 0, len);

    buf[0] = cpu_to_be32(0x00100014 | (PRLI << 24));
    buf[1] = cpu_to_be32(0x08002000);
    buf[2] = cpu_to_be32(0x00000000);
    buf[3] = cpu_to_be32(0x00000000);
    buf[4] = cpu_to_be32(0x000000b2);

    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    sge_simple++;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_IOC_TO_HOST));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    priv->ex_link_service_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, mf);

    ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);

	TRACE_EXIT_RES(ret);

	return ret;
}

static int
stm_logout_port(MPT_STM_PRIV *priv,
		int port_id, int sleep)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    ExLinkServiceSendRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    u32				*buf;
    int				len, ret;
    dma_addr_t			dma_addr;

	TRACE_ENTRY();
    req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
    req->MsgFlags_Did = cpu_to_le32(port_id);
    req->ElsCommandCode	= cpu_to_le32(LOGO);

    len = 4 * 4;
    buf = priv->hw->exlink_buf;
    memset(buf, 0, len);

    buf[0] = cpu_to_be32(LOGO << 24);
    /*
     *  the firmware builds the rest of the LOGO payload
     */

    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    sge_simple++;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_IOC_TO_HOST));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    priv->ex_link_service_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, mf);

    ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);

	TRACE_EXIT_RES(ret);

	return ret;
}

static int
stm_process_logout_port(MPT_STM_PRIV *priv,
			int port_id, int sleep)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    ExLinkServiceSendRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    u32				*buf;
    int				len, ret;
    dma_addr_t			dma_addr;

	TRACE_ENTRY();
    req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
    req->MsgFlags_Did = cpu_to_le32(port_id);
    req->ElsCommandCode	= cpu_to_le32(PRLO);

    len = 5 * 4;
    buf = priv->hw->exlink_buf;
    memset(buf, 0, len);

    buf[0] = cpu_to_be32(0x00100014 | (PRLO << 24));
    buf[1] = cpu_to_be32(0x08002000);
    buf[2] = cpu_to_be32(0x00000000);
    buf[3] = cpu_to_be32(0x00000000);
    buf[4] = cpu_to_be32(0x00000000);

    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    sge_simple++;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_IOC_TO_HOST));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    priv->ex_link_service_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, mf);

    ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);

	TRACE_EXIT_RES(ret);

	return ret;
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_get_hard_address(MPT_STM_PRIV *priv, int port_id, int *hard_address,
		int sleep)
{
    MPT_ADAPTER			*ioc = priv->ioc;
    ExLinkServiceSendRequest_t	*req;
    MPT_STM_SIMPLE		*sge_simple;
    u32				*buf;
    int				len;
    dma_addr_t			dma_addr;

	TRACE_ENTRY();
    req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc,-1);
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
    req->MsgFlags_Did = cpu_to_le32(port_id);
    req->ElsCommandCode	= cpu_to_le32(ADISC);

    len = 7 * 4;
    buf = priv->hw->exlink_buf;
    memset(buf, 0, len);

    buf[0] = cpu_to_be32(ADISC << 24);
    buf[1] = cpu_to_be32(0x00000000);	/* or get HardALPA from FCPortPage1 */
    buf[2] = cpu_to_be32(priv->wwpn.High);
    buf[3] = cpu_to_be32(priv->wwpn.Low);
    buf[4] = cpu_to_be32(priv->wwnn.High);
    buf[5] = cpu_to_be32(priv->wwnn.Low);
    buf[6] = cpu_to_be32(priv->port_id);

    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_HOST_TO_IOC));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);
    sge_simple++;
    sge_simple->FlagsLength = cpu_to_le32(len |
	MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			  MPI_SGE_FLAGS_LAST_ELEMENT |
			  MPI_SGE_FLAGS_END_OF_BUFFER |
			  MPI_SGE_FLAGS_END_OF_LIST |
			  MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
			  MPI_SGE_FLAGS_IOC_TO_HOST));
    dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    priv->ex_link_service_send_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, mf);

    if (stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep) < 0)
	return (-1);

    if ((be32_to_cpu(buf[0]) >> 24) != LS_ACC)
	return (-2);

    *hard_address = be32_to_cpu(buf[1]);

	TRACE_EXIT();

    return (0);
}
#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_scsi_configuration(MPT_STM_PRIV *priv,
		       int sleep)
{
#ifdef CONFIG_SCST_DEBUG
    MPT_ADAPTER		*ioc = priv->ioc;
#endif
    SCSIPortPage0_t	*ScsiPort0;
    SCSIPortPage2_t	*ScsiPort2;
    int			cap;
    int			wcap;
    int			ncap;
    int			sync;
    int			flags;
    int			i;

    TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 2, 0, sleep)) {
	return (-1);
    }
    ScsiPort2 = &priv->SCSIPortPage2;
	memcpy(&priv->SCSIPortPage2, priv->hw->config_buf, sizeof(SCSIPortPage2_t));

    TRACE_DBG("%s scsi id is %d", ioc->name, priv->port_id);

    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 0, 0, sleep)) {
	return (-1);
    }
    memcpy(&priv->SCSIPortPage0, priv->hw->config_buf, sizeof(SCSIPortPage0_t));
    ScsiPort0 = &priv->SCSIPortPage0;

    cap = le32_to_cpu(ScsiPort0->Capabilities);
    TRACE_DBG("%s target %d capabilities = %08x",
	   ioc->name, priv->port_id, cap);

    stm_set_scsi_port_page1(priv, sleep);

    wcap = cap & ~MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK;
    ncap = wcap & ~MPI_SCSIPORTPAGE0_CAP_WIDE;

    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    memset(priv->SCSIDevicePage1, 0, sizeof(SCSIDevicePage1_t) * NUM_SCSI_DEVICES);

    for (i = 0; i < NUM_SCSI_DEVICES; i++) {
	int wide = 0;
	SCSIDevicePage1_t *ScsiDevice1 = &priv->SCSIDevicePage1[i];
	sync = ScsiPort2->DeviceSettings[i].SyncFactor;
	if (ioc->facts.FWVersion.Word >= 0x01032900) {
		/* these firmware versions don't send the correct
		 * amount of data except at the slowest transfer
		 * factors */
		sync = max(0x32, sync);
		printk("forcing FAST-5 negotiation due to broken fw 0x%08X\n",
				ioc->facts.FWVersion.Word);
	}
	flags = le16_to_cpu(ScsiPort2->DeviceSettings[i].DeviceFlags);
	if (flags & MPI_SCSIPORTPAGE2_DEVICE_WIDE_DISABLE)
	    cap = ncap;
	else {
	    cap = wcap;
	    wide = 1;
	}
	/*cap &= ~MPI_SCSIDEVPAGE1_RP_IU;
	cap &= ~MPI_SCSIDEVPAGE1_RP_DT;
	cap &= ~MPI_SCSIDEVPAGE1_RP_QAS;*/
	ScsiDevice1->RequestedParameters = cpu_to_le32(cap | (sync << 8));
	TRACE_DBG("%s initiator %d parameters = %08x, %s %s",
	       ioc->name, i, le32_to_cpu(ScsiDevice1->RequestedParameters),
		   sync ? "SYNC" : " ",
		   wide ? "WIDE" : " ");
	memcpy(priv->hw->config_buf, ScsiDevice1, sizeof(*ScsiDevice1));
	stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, 1, i, sleep);
	atomic_set(&priv->pending_sense[i], MPT_STATUS_SENSE_IDLE);
    }
    TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stm_set_scsi_port_page1(MPT_STM_PRIV *priv, int sleep)
{
#ifdef CONFIG_SCST_DEBUG
	MPT_ADAPTER		*ioc = priv->ioc;
#endif
	SCSIPortPage1_t *ScsiPort1;
	int i;
	int id = priv->port_id;

	TRACE_ENTRY();

	memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
	memset(&priv->SCSIPortPage1, 0, sizeof(priv->SCSIPortPage1));
	ScsiPort1 = &priv->SCSIPortPage1;
	ScsiPort1->Configuration = cpu_to_le32(id | (1 << (id + 16)));
	for (i = 1; i <= priv->num_aliases; i++) {
		id = (priv->port_id + i) & 15;
		TRACE_DBG("%s alias %d is target %d",
				ioc->name, i, id);
		ScsiPort1->Configuration |= cpu_to_le32(1 << (id + 16));
	}
	ScsiPort1->TargetConfig = priv->scsi_port_config;
	ScsiPort1->IDConfig = priv->scsi_id_config;
	memcpy(priv->hw->config_buf, (u32 *)ScsiPort1, sizeof(*ScsiPort1));
	stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 1, 0, sleep);

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_sas_configuration(MPT_STM_PRIV *priv,
		       int sleep)
{
    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_configuration(MPT_STM_PRIV *priv, int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    U64			wwnn;
    U64			wwpn;
    int			port_id;
    int			protocol;
    int			flags;
    int			current_speed;
    int			port_state;
    int			target;
    char		*attach;
    char		*speed;
    FCPortPage0_t	*FcPort0;
    FCDevicePage0_t	*FcDevice0;
    int			page;

	TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 0, 0, sleep)) {
	return (-1);
    }
    FcPort0 = (FCPortPage0_t *)priv->hw->config_buf;
    flags = le32_to_cpu(FcPort0->Flags) &
	MPI_FCPORTPAGE0_FLAGS_ATTACH_TYPE_MASK;
    current_speed = le32_to_cpu(FcPort0->CurrentSpeed);
    port_state = FcPort0->PortState;

    switch (flags) {
	case MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT:
	    attach = NULL;
	    break;
	case MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT:
	    attach = "point to point";
	    break;
	case MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP:
	    attach = "private loop";
	    break;
	case MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT:
	    attach = "fabric direct attach";
	    break;
	case MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP:
	    attach = "public loop";
	    break;
	default:
	    attach = "unknown";
	    break;
    }

    switch (current_speed) {
	case MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT:
	    speed = "1 Gbaud";
	    break;
	case MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT:
	    speed = "2 Gbaud";
	    break;
	case MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT:
	    speed = "10 Gbaud";
	    break;
	default:
	    speed = "unknown";
	    break;
    }

    if (priv->port_flags != flags ||
	priv->port_speed != current_speed ||
	priv->port_state != port_state) {
	priv->port_flags = flags;
	priv->port_speed = current_speed;
	priv->port_state = port_state;
	priv->device_changed = 1;
	if (attach) {
	    printk("%s link is online, type is %s, speed is %s\n",
		   ioc->name, attach, speed);
	} else {
	    printk("%s link is offline\n", ioc->name);
	}
    }

    wwnn.Low = le32_to_cpu(FcPort0->WWNN.Low);
    wwnn.High = le32_to_cpu(FcPort0->WWNN.High);
    wwpn.Low = le32_to_cpu(FcPort0->WWPN.Low);
    wwpn.High = le32_to_cpu(FcPort0->WWPN.High);
    port_id = le32_to_cpu(FcPort0->PortIdentifier);
    protocol = le32_to_cpu(FcPort0->Flags) & MPI_FCPORTPAGE0_FLAGS_PROT_MASK;

    if (priv->wwpn.Low != wwpn.Low ||
	priv->wwpn.High != wwpn.High ||
	priv->port_id != port_id) {
	priv->wwnn.Low = wwnn.Low;
	priv->wwnn.High = wwnn.High;
	priv->wwpn.Low = wwpn.Low;
	priv->wwpn.High = wwpn.High;
	priv->port_id = port_id;
	priv->protocol = protocol;
	priv->device_changed = 1;
	if (attach) {
	    printk("%s port is wwn %08x%08x, port id %x\n",
		   ioc->name, wwpn.High, wwpn.Low, port_id);
	} else {
	    printk(
		   "%s port is wwn %08x%08x\n",
		   ioc->name, wwpn.High, wwpn.Low);
	}
    }

    page = MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID + 0xffffff;

    while (1) {
	memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
	if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_DEVICE,
				0, page, sleep)) {
	    break;
	}
	FcDevice0 = (FCDevicePage0_t *)priv->hw->config_buf;

	wwnn.Low = le32_to_cpu(FcDevice0->WWNN.Low);
	wwnn.High = le32_to_cpu(FcDevice0->WWNN.High);
	wwpn.Low = le32_to_cpu(FcDevice0->WWPN.Low);
	wwpn.High = le32_to_cpu(FcDevice0->WWPN.High);
	port_id = le32_to_cpu(FcDevice0->PortIdentifier);
	protocol = FcDevice0->Protocol;
	if (FcDevice0->Flags & MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID) {
	    target = FcDevice0->CurrentTargetID;
	} else {
	    target = -1;
	}

#if 0
	printk("%s using ADISC to get hard address of port id %x\n",
	       ioc->name, port_id);
	if (stm_get_hard_address(priv, port_id, &i, sleep))
	    printk("%s ADISC failed!\n", ioc->name);
	else
	    printk("%s port id's %x hard address is %x\n",
		   ioc->name, port_id, i);
#endif

	page = MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID + port_id;

    }
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_enable_els(MPT_STM_PRIV *priv,
		  int els, int sleep)
{
    FCPortPage8_t	*FcPort8;
	TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 8, 0, sleep)) {
	return (-1);
    }
    FcPort8 = (FCPortPage8_t *)priv->hw->config_buf;
    /* clear the ELS bit */
    FcPort8->BitVector[els / 32] &= ~cpu_to_le32(1 << (els & 31));
    if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 8, 0, sleep)) {
	return (-1);
    }
	TRACE_EXIT();

    return (0);
}
#if 0
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_enable_immediate_errors(MPT_STM_PRIV *priv,
			       int sleep)
{
    FCPortPage1_t	*FcPort1;

    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
    FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
    /* set the Immediate Error Reply bit */
    FcPort1->Flags |= cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY);
    if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_enable_target_mode_oxid(MPT_STM_PRIV *priv,
			       int sleep)
{
    FCPortPage1_t	*FcPort1;
	TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
    FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
    /* set the Target Mode OX_ID bit */
    FcPort1->Flags |= cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_TARGET_MODE_OXID);
    if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_set_wwn(MPT_STM_PRIV *priv,
	       WwnFormat_t *wwn,
	       int sleep)
{
    FCPortPage1_t	*FcPort1;
	TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
    FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
    /* set the WWPN and WWNN */
    FcPort1->NoSEEPROMWWPN.Low = cpu_to_le32(wwn->PortNameLow);
    FcPort1->NoSEEPROMWWPN.High = cpu_to_le32(wwn->PortNameHigh);
    FcPort1->NoSEEPROMWWNN.Low = cpu_to_le32(wwn->NodeNameLow);
    FcPort1->NoSEEPROMWWNN.High = cpu_to_le32(wwn->NodeNameHigh);
    /* set the Ignore SEEPROM WWNs bit */
    FcPort1->Flags |=
	cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_FORCE_USE_NOSEEPROM_WWNS);
    if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }

    stm_reset_link(priv);
	TRACE_EXIT();
    return (0);
}
#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_fc_enable_aliases(MPT_STM_PRIV *priv, int num_aliases, int sleep)
{
    FCPortPage1_t	*FcPort1;
	TRACE_ENTRY();
    memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
    if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
    FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
    if (set_aliases_in_fcportpage1) {
	/* set the number of aliases requested */
	FcPort1->NumRequestedAliases = (u8)num_aliases;
    } else {
	/* make sure the value in the page is low enough */
	if (FcPort1->NumRequestedAliases > NUM_ALIASES) {
	    FcPort1->NumRequestedAliases = NUM_ALIASES;
	}
    }

    if (num_aliases > 0) {
	FcPort1->TopologyConfig = MPI_FCPORTPAGE1_TOPOLOGY_NLPORT;
    }
    if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep)) {
	return (-1);
    }
	TRACE_EXIT();
    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_get_config_page(MPT_STM_PRIV *priv, int type, int number, int address,
		    int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    ConfigReply_t	*rep;
    int			ioc_status;
    int			i;
    int			length;

	TRACE_ENTRY();
    rep = &priv->config_rep;
    memset(rep, 0, sizeof(*rep));

    i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_HEADER,
			     type, number, address, 0, sleep);
    if (i) {
	if (!priv->in_reset) {
	    printk(KERN_ERR MYNAM
		   ":%s timed out getting config page header\n", ioc->name);
	}
	return (-1);
    }

    if (priv->in_reset) {
	printk(KERN_ERR MYNAM
	       ":%s reset while getting config page header\n", ioc->name);
	return (-1);
    }

    ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
    if (type > MPI_CONFIG_PAGETYPE_EXTENDED) {
	length = le16_to_cpu(rep->ExtPageLength);
    } else {
	length = rep->Header.PageLength;
    }
    if (ioc_status != MPI_IOCSTATUS_SUCCESS || length == 0) {
	if (ioc_status != MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
	    printk(KERN_ERR MYNAM
		   ":%s failed to get config page header\n", ioc->name);
	    printk(KERN_ERR MYNAM
		   ":%s   IOCStatus = %04x, PageLength = %x\n",
		   ioc->name, ioc_status, length);
	    printk(KERN_ERR MYNAM
		   ":%s   type = %d, number = %d, address = %x\n",
		   ioc->name, type, number, address);
	}
	return (-1);
    }

    i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_READ_CURRENT,
			     type, number, address, length, sleep);
    if (i) {
	if (!priv->in_reset) {
	    printk(KERN_ERR MYNAM
		   ":%s timed out getting config page = %d\n", ioc->name, type);
	}
	return (-1);
    }

    if (priv->in_reset) {
	printk(KERN_ERR MYNAM
	       ":%s reset while getting config page\n", ioc->name);
	return (-1);
    }

    ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
	if ((type == 6 && number == 0) || (type == 18 && number == 0)) {
	    /* no error messages, please! */
	} else {
	    printk(KERN_ERR MYNAM
		   ":%s failed to get config page\n", ioc->name);
	    printk(KERN_ERR MYNAM
		   ":%s   IOCStatus = %04x, PageLength = %x\n",
		   ioc->name, ioc_status, length);
	    printk(KERN_ERR MYNAM
		   ":%s   type = %d, number = %d, address = %x\n",
		   ioc->name, type, number, address);
	}
	return (-1);
    }

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)priv->hw->config_buf;
		int i;

		TRACE(TRACE_MPI, "%s config page %02x/%02x/%08x read",
				ioc->name, type, number, address);
		for (i = 0; i < length; i++) {
			TRACE(TRACE_MPI, "%s page[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_set_config_page(MPT_STM_PRIV *priv,
		    int type,
		    int number,
		    int address,
		    int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    ConfigReply_t	*rep;
    int			ioc_status;
    int			i;
    int			length;

	TRACE_ENTRY();
    rep = &priv->config_rep;
    memset(rep, 0, sizeof(*rep));

    i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_HEADER,
			     type, number, address, 0, sleep);
    if (i) {
	if (!priv->in_reset) {
	    printk(KERN_ERR MYNAM
		   ":%s timed out getting config page header\n", ioc->name);
	}
	return (-1);
    }

    if (priv->in_reset) {
	printk(KERN_ERR MYNAM
	       ":%s reset while getting config page header\n", ioc->name);
	return (-1);
    }

    ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
    if (type > MPI_CONFIG_PAGETYPE_EXTENDED) {
	length = le16_to_cpu(rep->ExtPageLength);
    } else {
	length = rep->Header.PageLength;
    }
    if (ioc_status != MPI_IOCSTATUS_SUCCESS || length == 0) {
	if (ioc_status != MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
	    printk(KERN_ERR MYNAM
		   ":%s failed to get config page header\n", ioc->name);
	    printk(KERN_ERR MYNAM
		   ":%s   IOCStatus = %04x, PageLength = %x\n",
		   ioc->name, ioc_status, length);
	    printk(KERN_ERR MYNAM
		   ":%s   type = %d, number = %d, address = %x\n",
		   ioc->name, type, number, address);
	}
	return (-1);
    }

    *(ConfigPageHeader_t *)priv->hw->config_buf = rep->Header;

    i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT,
			     type, number, address, length, sleep);
    if (i) {
	if (!priv->in_reset) {
	    printk(KERN_ERR MYNAM
		   ":%s timed out setting config page\n", ioc->name);
	}
	return (-1);
    }

    if (priv->in_reset) {
	printk(KERN_ERR MYNAM
	       ":%s reset while setting config page\n", ioc->name);
	return (-1);
    }

    ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
    if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
	printk(KERN_ERR MYNAM
	       ":%s failed to set config page\n", ioc->name);
	printk(KERN_ERR MYNAM
	       ":%s   IOCStatus = %04x, PageLength = %x\n",
	       ioc->name, ioc_status, length);
	printk(KERN_ERR MYNAM
	       ":%s   type = %d, number = %d, address = %x\n",
	       ioc->name, type, number, address);
	printk(KERN_ERR MYNAM
	       ":%s   Header = %08x\n",
	       ioc->name, le32_to_cpu(*(u32 *)priv->hw->config_buf));
	return (-1);
    }

#ifdef CONFIG_SCST_TRACING
	if(trace_mpi)
	{
		u32 *p = (u32 *)priv->hw->config_buf;
		int i;

		TRACE(TRACE_MPI, "%s config page %02x/%02x/%08x written",
				ioc->name, type, number, address);
		for (i = 0; i < length; i++) {
			TRACE(TRACE_MPI, "%s page[%02x] = %08x",
					ioc->name, i * 4, le32_to_cpu(p[i]));
		}
	}
#endif
	TRACE_EXIT();

    return (0);
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
stm_do_config_action(MPT_STM_PRIV *priv, int action,
		     int type, int number, int address, int length, int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    MPT_FRAME_HDR	*mf;
    Config_t		*req;
    MPT_STM_SIMPLE	*sge_simple;
    dma_addr_t		dma_addr;
	int ret;

	TRACE_ENTRY();
    if (priv->in_reset) {
	printk(KERN_ERR MYNAM ":%s reset while doing config action %x\n",
	       ioc->name, action);
	return (-1);
    }

    /*
     *  get a message frame, and send the config action request
     */
    mf = priv->config_mf;
    if (mf == NULL) {
	mf = mpt_msg_frame_alloc(ioc,-1);
	if (mf == NULL) {
	    printk(KERN_ERR MYNAM
		   ":%s failed to get message frame\n", ioc->name);
	    return (-1);
	} else {
	    TRACE_DBG(
		    "%s in stm_do_config_action, got mf index %d",
		    ioc->name, MF_TO_INDEX(mf));
	    priv->config_mf = mf;
	}
    }

    req = (Config_t *)mf;
    memset(req, 0, sizeof(*req));

    req->Function = MPI_FUNCTION_CONFIG;
    req->Action = (u8)action;
    if (action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
	action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
	req->Header = *(ConfigPageHeader_t *)priv->hw->config_buf;
    } else {
	if (type > MPI_CONFIG_PAGETYPE_EXTENDED) {
	    req->Header.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
	    req->ExtPageType = (u8)type;
	    req->ExtPageLength = cpu_to_le16(length);
	} else {
	    req->Header.PageType = (u8)type;
	}
	req->Header.PageNumber = (u8)number;
	req->Header.PageLength = (u8)length;
    }
    req->PageAddress = cpu_to_le32(address);
    if (length) {
	sge_simple = (MPT_STM_SIMPLE *)&req->PageBufferSGE;
	sge_simple->FlagsLength = cpu_to_le32((length * 4) |
	    MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
			      MPI_SGE_FLAGS_LAST_ELEMENT |
			      MPI_SGE_FLAGS_END_OF_BUFFER |
			      MPI_SGE_FLAGS_END_OF_LIST |
			      MPI_SGE_FLAGS_MPT_STM_ADDRESSING));
	if (action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
	    action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
	    sge_simple->FlagsLength |=
		cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_HOST_TO_IOC));
	}
	dma_addr = priv->hw_dma + ((u8 *)priv->hw->config_buf - (u8 *)priv->hw);
	stm_set_dma_addr(sge_simple->Address, dma_addr);
    }

#if 1
    priv->config_pending = 1;

    mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);

    ret = (stm_wait_for(priv, &priv->config_pending, 10, sleep));
#else
    ret = (mpt_handshake_req_reply_wait(ioc, sizeof(*req), (u32 *)req,
					 sizeof(priv->config_rep),
					 (u16 *)&priv->config_rep, 10, sleep));
#endif
	TRACE_EXIT_RES(ret);

	return ret;
}

static void
stm_wait(MPT_STM_PRIV *priv, int milliseconds, int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;

	TRACE_ENTRY();
	if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
		printk(KERN_ERR MYNAM
				":%s IOC is not operational (doorbell = %x)\n",
				ioc->name, mpt_GetIocState(ioc, 0));
	} else {
		if (sleep == CAN_SLEEP) {
			set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout(milliseconds);
		} else {
#ifndef __linux__
			if (priv->poll_enabled) {
				_mpt_poll(priv->ioc);
			}
#endif
			mdelay(milliseconds);
		}
	}
	TRACE_EXIT();
}

static int
stm_wait_for(MPT_STM_PRIV *priv, volatile int *flag, int seconds, int sleep)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    int			i;

	TRACE_ENTRY();
	for (i = 0; i < seconds * ((sleep == CAN_SLEEP) ? HZ : 1000); i++) {
		if (!(*flag)) {
			return (0);
		}
		if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
			printk(KERN_ERR MYNAM
					":%s IOC is not operational (doorbell = %x)\n",
					ioc->name, mpt_GetIocState(ioc, 0));
			return (-1);
		}
		if (sleep == CAN_SLEEP) {
			set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout(1);
		} else {
#ifndef __linux__
			if (priv->poll_enabled) {
				_mpt_poll(priv->ioc);
			}
#endif
			mdelay(1);
		}
	}

    /* timed out, so return failure */

    printk(KERN_ERR MYNAM ":%s timed out in stm_wait_for!\n", ioc->name);
	TRACE_EXIT();

    return (-1);
}

static int __init
_mpt_stm_init(void)
{
	int i;

	TRACE_ENTRY();

	for (i = 0; i < MPT_MAX_ADAPTERS; i++) {
		mpt_stm_priv[i] = NULL;
	}

	stm_context = mpt_register(stm_reply, MPTSTM_DRIVER);
	if (stm_context < 0) {
		printk(KERN_ERR MYNAM
				": failed to register with MPT driver core\n");
		return (-EBUSY);
	}

	if (mpt_event_register(stm_context, stm_event_process)) {
		printk(KERN_WARNING MYNAM
				": failed to register for event notification\n");
	}

	if (mpt_reset_register(stm_context, stm_reset_process)) {
		printk(KERN_WARNING MYNAM
				": failed to register for reset process\n");
	}

	TRACE_DBG(": assigned context of %d", stm_context);

	TRACE_EXIT();

	return 0;
}

static int
mpt_stm_adapter_install(MPT_ADAPTER *ioc)
{
    MPT_STM_PRIV		*priv;
    int				max_aliases;

	TRACE_ENTRY();
	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
	if (priv == NULL) {
		printk(KERN_ERR MYNAM
				":%s failed to allocate private structure\n", ioc->name);
		return (-1);
	}
	memset(priv, 0, sizeof(*priv));
	if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET) {
		priv->enable_target_mode = 1;
	}

	priv->ioc = ioc;

	priv->num_sge_chain = ioc->req_sz / sizeof(MPT_STM_SIMPLE);
	priv->num_sge_target_assist = (ioc->req_sz -
			offsetof(TargetAssistRequest_t, SGL)) / sizeof(MPT_STM_SIMPLE);

	priv->num_cmd_buffers = NUM_CMD_BUFFERS;
	if (priv->num_cmd_buffers > ioc->pfacts[0].MaxPostedCmdBuffers) {
		priv->num_cmd_buffers = ioc->pfacts[0].MaxPostedCmdBuffers;
	}
	if (priv->num_cmd_buffers > ioc->req_depth - 16) {
		priv->num_cmd_buffers = ioc->req_depth - 16;
	}
	priv->num_els_buffers = NUM_ELS_BUFFERS;

	priv->poll_enabled = 1;

	priv->hw = pci_alloc_consistent(ioc->pcidev, sizeof(*priv->hw),
			&priv->hw_dma);
	if (priv->hw == NULL) {
		printk(KERN_ERR MYNAM
				":%s failed to allocate hardware structure\n", ioc->name);
		kfree(priv);
		return (-1);
	}
	memset(priv->hw, 0, sizeof(*priv->hw));
	printk(KERN_INFO ":%s priv = %p, priv->hw = %p, priv->hw_dma = %llx\n",
			ioc->name, priv, priv->hw, (u64)priv->hw_dma);

	mpt_stm_priv[ioc->id] = priv;

	max_aliases = 0;
	if (IsScsi(priv)) {
		max_aliases = 14;
	}
	if (IsFc(priv)) {
		memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
		if (!stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 0, 0,
					NO_SLEEP)) {
			max_aliases =
				((FCPortPage0_t *)priv->hw->config_buf)->MaxAliasesSupported;
		}
	}

	if (num_aliases < max_aliases) {
		priv->num_aliases = num_aliases;
	} else {
		priv->num_aliases = max_aliases;
	}

	TRACE_EXIT();

    return (0);
}

static int
mpt_stm_adapter_online(MPT_STM_PRIV *priv)
{
    MPT_ADAPTER		*ioc;
    int			i;

	TRACE_ENTRY();
    ioc = priv->ioc;

    priv->fcp2_capable = 0;
#ifdef MPT_STM_ALLOW_FCP2
    switch (ioc->pcidev->device) {
	case MPI_MANUFACTPAGE_DEVICEID_FC919:
	case MPI_MANUFACTPAGE_DEVICEID_FC929:
	    if (ioc->facts.FWVersion.Word >= 0x01630002) {
		/* firmware version 1.99.00.02 (and later) is FCP-2 capable */
		priv->fcp2_capable = 1;
	    }
	    break;
	case MPI_MANUFACTPAGE_DEVICEID_FC919X:
	case MPI_MANUFACTPAGE_DEVICEID_FC929X:
	    if (ioc->facts.FWVersion.Word >= 0x01013200) {
		/* firmware version 1.01.50 (and later) is FCP-2 capable */
		priv->fcp2_capable = 1;
	    }
	    break;
	case MPI_MANUFACTPAGE_DEVICEID_FC939X:
	case MPI_MANUFACTPAGE_DEVICEID_FC949X:
	    priv->fcp2_capable = 1;
	    break;
	default:
	    break;
    }
#endif

    priv->port_enable_pending = 0;
    priv->target_mode_abort_pending = 0;
    priv->link_serv_abort_pending = 0;
    priv->fc_primitive_send_pending = 0;

    priv->config_pending = 0;
    priv->config_mf = NULL;

    priv->port_flags = 0;
    priv->port_speed = 0;

    priv->in_reset = 0;
    priv->poll_enabled = 1;

    for (i = 0; i < priv->num_cmd_buffers; i++)
	priv->io_state[i] = 0;

    if (IsScsi(priv)) {
	stm_scsi_configuration(priv, NO_SLEEP);
    }

    if (IsSas(priv)) {
	stm_sas_configuration(priv, NO_SLEEP);
    }

	if (priv->enable_target_mode) {
		if (IsSas(priv)) {
			stm_cmd_buf_post_base(priv, 1);
		} else {
			for (i = 0; i < priv->num_cmd_buffers; i++) {
				stm_cmd_buf_post(priv, i);
			}
		}

		if (IsFc(priv)) {
			for (i = 0; i < priv->num_els_buffers; i++) {
				stm_link_serv_buf_post(priv, i);
			}

			stm_fc_enable_els(priv, RSCN, NO_SLEEP);

#ifdef STMAPP_VERIFY_OXIDS
			stm_fc_enable_target_mode_oxid(priv, NO_SLEEP);
#endif

			stm_fc_enable_aliases(priv, priv->num_aliases, NO_SLEEP);
		}
	}

    stm_port_enable(priv);

    if (IsFc(priv)) {
	stm_reset_link(priv);
    }

    if (IsFc(priv)) {
	/* wait up to 5 seconds for the link to come up */
	for (i = 0; i < 50; i++) {
	    stm_fc_configuration(priv, NO_SLEEP);
	    if (priv->port_flags != MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT) {
		break;
	    }
	    mdelay(100);
	}
    }
	TRACE_EXIT();

    return (0);
}

static void
_mpt_stm_exit(void)
{
	TRACE_ENTRY();
	if (stm_context > 0) {
		mpt_reset_deregister(stm_context);
		mpt_event_deregister(stm_context);

		mpt_device_driver_deregister(MPTSTM_DRIVER);

		mpt_deregister(stm_context);
		stm_context = 0;
	}

	TRACE_EXIT();
}

static void
mpt_stm_adapter_dispose(MPT_STM_PRIV *priv)
{
	MPT_ADAPTER		*ioc;

	TRACE_ENTRY();
	priv->exiting = 1;

	ioc = priv->ioc;

	if (mpt_GetIocState(ioc, 1) == MPI_IOC_STATE_OPERATIONAL) {
		if (priv->enable_target_mode && priv->tgt->target_enable) {
			stm_target_mode_abort(priv);

			if (IsFc(priv)) {
				stm_link_serv_abort(priv);
			}
		}
	}

	mpt_stm_priv[ioc->id] = NULL;
	if (priv->hw != NULL) {
		pci_free_consistent(ioc->pcidev, sizeof(*priv->hw),
				priv->hw, priv->hw_dma);
	}
	if (priv->config_mf != NULL) {
		mpt_free_msg_frame(_HANDLE_IOC_ID, priv->config_mf);
	}
	kfree(priv);

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_set_status(MPT_STM_PRIV *priv, CMD *cmd, int status)
{
	TRACE_ENTRY();
    if (IsScsi(priv)) {
	SCSI_RSP	*rsp = (SCSI_RSP *)cmd->rsp;

	rsp->Status = (u8)status;
    } else if (IsSas(priv)) {
	SSP_RSP		*rsp = (SSP_RSP *)cmd->rsp;

	rsp->Status = (u8)status;
    } else {
	FCP_RSP		*rsp = (FCP_RSP *)cmd->rsp;

	rsp->FcpStatus = (u8)status;
    }
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_abts_process(MPT_STM_PRIV *priv,
		    int rx_id, LinkServiceBufferPostReply_t *rep, int index)
{
#ifdef CONFIG_SCST_DEBUG
    MPT_ADAPTER		*ioc = priv->ioc;
#endif
    volatile int	*io_state;
    CMD			*cmd;

	TRACE_ENTRY();
	io_state = priv->io_state + rx_id;

	if (*io_state & IO_STATE_ABORTED) {
		return;
	}

	if (*io_state & IO_STATE_POSTED) {
		return;
	}

	cmd = priv->hw->cmd_buf + rx_id;

	TRACE_DBG("%s index %d: io_state = %x",
			ioc->name, rx_id, *io_state);
	TRACE_DBG("%s reply_word = %x, alias = %d, lun = %d, tag = %x",
			ioc->name, cmd->reply_word, cmd->alias, cmd->lun, cmd->tag);

	/*
	 *  if we are processing an SRR, there could be some other flags set
	 *  in io_state that we need to get rid of; ABTS overrides SRR
	 */
	*io_state &= ~IO_STATE_REQUEST_ABORTED;
    *io_state &= ~IO_STATE_REISSUE_REQUEST;
    *io_state &= ~IO_STATE_ADJUST_OFFSET;
    *io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
    *io_state &= ~IO_STATE_REDO_COMMAND;

    /*
     *  if we get here, the firmware thinks a command is active,
     *  so it should be aborted
     */
    TRACE_DBG("%s index %d needs to be aborted", ioc->name, rx_id);
    stm_target_mode_abort_command(priv, cmd->reply_word, rx_id);

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_srr_process(MPT_STM_PRIV *priv, int rx_id, int r_ctl, u32 offset,
		   LinkServiceBufferPostReply_t *rep, int index)
{
#ifdef CONFIG_SCST_DEBUG
    MPT_ADAPTER		*ioc = priv->ioc;
#endif
    FC_ELS		*fc_els_buf;
    volatile int	*io_state;
    u32			msg_context;
    CMD			*cmd;
    int			need_abort = 0;
    u32			rel_off;
    u32			dat_len;
    u32			adjust;
    u32			block_size = 512;

	TRACE_ENTRY();
    fc_els_buf = &priv->hw->fc_link_serv_buf[index];
    io_state = priv->io_state + rx_id;

    if (*io_state & IO_STATE_ABORTED) {
	/*
	 *  the initiator wants to continue an I/O that we're aborting;
	 *  just accept this SRR, and continue aborting
	 */
	fc_els_buf->fc_els[0] = cpu_to_be32(0x02000000);
	stm_send_els(priv, rep, index, 4);
	return;
    }

    if (*io_state & IO_STATE_POSTED) {
	/*
	 *  the firmware should prevent this from happening, but if it does,
	 *  reject this SRR with "invalid OX_ID/RX_ID combination"
	 */
	fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
	fc_els_buf->fc_els[1] = cpu_to_be32(0x00090300);
	stm_send_els(priv, rep, index, 8);
	return;
    }

    cmd = priv->hw->cmd_buf + rx_id;

    TRACE_DBG("%s index %d: r_ctl = %x, io_state = %x",
	    ioc->name, rx_id, r_ctl, *io_state);
    TRACE_DBG("%s reply_word = %x, alias = %d, lun = %d, tag = %x",
	    ioc->name, cmd->reply_word, cmd->alias, cmd->lun, cmd->tag);

    if (*io_state & (IO_STATE_DATA_SENT | IO_STATE_STATUS_SENT)) {
	/*
	 *  if we get here, the firmware thinks a request is active,
	 *  so it should be aborted
	 */
	TRACE_DBG("%s index %d needs to be aborted",
		ioc->name, rx_id);
	if (*io_state & IO_STATE_DATA_SENT) {
	    TargetAssistRequest_t	*req;

	    req = (TargetAssistRequest_t *)priv->current_mf[rx_id];
	    msg_context = le32_to_cpu(req->MsgContext);
	    rel_off = le32_to_cpu(req->RelativeOffset);
	    dat_len = le32_to_cpu(req->DataLength);
	    TRACE_DBG("%s SRR offset = %x, TA offset = %x, TA length = %x",
		    ioc->name, offset, rel_off, dat_len);
	    if (r_ctl == 1 || r_ctl == 5) {
		if (offset < rel_off && (offset % block_size) == 0) {
		    TRACE_DBG("%s request can be reissued",
			    ioc->name);
		    adjust = rel_off + dat_len - offset;
		    *io_state |= IO_STATE_INCOMPLETE;
		    *io_state |= IO_STATE_REDO_COMMAND;
		    *io_state |= IO_STATE_REISSUE_REQUEST;
		    need_abort = 1;
		}
		if (offset >= rel_off && offset <= rel_off + dat_len) {
		    TRACE_DBG("%s request can be reissued",
			    ioc->name);
		    if (offset != rel_off) {
			if (offset != rel_off + dat_len) {
			    //cmd->offset = offset;
			    *io_state |= IO_STATE_ADJUST_OFFSET;
			} else {
			    *io_state |= IO_STATE_CONVERT_TA_TO_TSS;
			}
		    }
		    *io_state |= IO_STATE_REISSUE_REQUEST;
		    need_abort = 1;
		}
	    } else if (r_ctl == 7) {
		if (*io_state & IO_STATE_STATUS_SENT) {
		    TRACE_DBG("%s request can be reissued",
			    ioc->name);
		    *io_state |= IO_STATE_CONVERT_TA_TO_TSS;
		    *io_state |= IO_STATE_REISSUE_REQUEST;
		    need_abort = 1;
		}
		if (*io_state & IO_STATE_STATUS_DEFERRED) {
		    TRACE_DBG("%s request can be reissued",
			    ioc->name);
		    *io_state |= IO_STATE_REISSUE_REQUEST;
		    need_abort = 1;
		}
	    } else {
		TRACE_DBG("%s request cannot be reissued",
			ioc->name);
	    }
	} else {
	    TargetStatusSendRequest_t	*req;

	    req = (TargetStatusSendRequest_t *)priv->current_mf[rx_id];
	    msg_context = le32_to_cpu(req->MsgContext);
	    if (r_ctl == 7) {
		TRACE_DBG("%s request can be reissued",
			ioc->name);
		*io_state |= IO_STATE_REISSUE_REQUEST;
		need_abort = 1;
	    } else {
		TRACE_DBG("%s request cannot be reissued",
			ioc->name);
	    }
	}
	if (need_abort) {
	    stm_target_mode_abort_request(priv, cmd->reply_word, msg_context,
					  rx_id);
	}
    }

    if (*io_state & IO_STATE_REISSUE_REQUEST) {
	/*
	 *  if we can continue this I/O, accept this SRR
	 */
	fc_els_buf->fc_els[0] = cpu_to_be32(0x02000000);
	stm_send_els(priv, rep, index, 4);
    } else {
	/*
	 *  we can't continue the I/O, so reject this SRR with "unable to
	 *  supply requested data"
	 */
	fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
	fc_els_buf->fc_els[1] = cpu_to_be32(0x00092a00);
	stm_send_els(priv, rep, index, 8);
    }
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_srr_convert_ta_to_tss(MPT_STM_PRIV *priv, int index)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    volatile int	*io_state;
    CMD			*cmd;
    u32			reply_word;
    int			lun;
    int			tag;
    int			flags;

	TRACE_ENTRY();
    io_state = priv->io_state + index;

    cmd = priv->hw->cmd_buf + index;

    reply_word = cmd->reply_word;
    lun = cmd->lun;
    tag = cmd->tag;

    *io_state &= ~IO_STATE_DATA_SENT;
    *io_state &= ~IO_STATE_STATUS_SENT;
    mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);

    flags = TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS;
    if (*io_state & IO_STATE_AUTO_REPOST) {
	flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
    }
    stm_send_target_status(priv, reply_word, index, flags, lun, tag);
	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_srr_adjust_offset(MPT_STM_PRIV *priv, int index)
{
    CMD				*cmd;
    TargetAssistRequest_t	*req;
    u32				old_offset;
    u32				new_offset = 0;
    u32				offset;
    MPT_STM_SIMPLE		*sge_simple;
    MPT_STM_CHAIN		*sge_chain = NULL;
    u32				flags_length;
    int				type;
    int				old_length;
    int				new_length;
    int				length;
    dma_addr_t			dma_addr;
    int				n;

	TRACE_ENTRY();
    cmd = priv->hw->cmd_buf + index;
    req = (TargetAssistRequest_t *)priv->current_mf[index];

    old_offset = le32_to_cpu(req->RelativeOffset);

    /* walk the SGL, skipping along until we get to the right offset */
    offset = old_offset;
    sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
    n = 0;
    while (1) {
	flags_length = le32_to_cpu(sge_simple->FlagsLength);
	type = MPI_SGE_GET_FLAGS(flags_length) & MPI_SGE_FLAGS_ELEMENT_MASK;
	if (type == MPI_SGE_FLAGS_CHAIN_ELEMENT) {
	    if (sge_chain == NULL) {
		sge_chain = (MPT_STM_CHAIN *)sge_simple;
		stm_get_dma_addr(dma_addr, sge_chain->Address);
		sge_simple = (MPT_STM_SIMPLE *)
		    ((u8 *)priv->hw + (dma_addr - priv->hw_dma));
	    } else {
		sge_chain = (MPT_STM_CHAIN *)sge_simple;
		sge_simple = (MPT_STM_SIMPLE *)(sge_chain + 1);
	    }
	    n = 0;
	} else {
	    length = MPI_SGE_LENGTH(flags_length);
	    if (offset + length > new_offset) {
		break;
	    }
	    offset += length;
	    sge_simple++;
	    n++;
	}
    }

    /* fix up the current SGE */
    flags_length -= new_offset - offset;
    sge_simple->FlagsLength = le32_to_cpu(flags_length);
    stm_get_dma_addr(dma_addr, sge_simple->Address);
    dma_addr += new_offset - offset;
    stm_set_dma_addr(sge_simple->Address, dma_addr);

    /* if we have skipped any SGEs, we need to use a chain to point to */
    /* the new "first" SGE */
    if (sge_simple != (MPT_STM_SIMPLE *)&req->SGL) {
	/* see if we've already walked past a chain */
	if (sge_chain == NULL) {
	    /* all we have to do here is move the SGEs in the request frame */
	    memmove(&req->SGL, sge_simple,
		    (priv->num_sge_target_assist - n) *
			sizeof(MPT_STM_SIMPLE));
	    if (req->ChainOffset != 0) {
		req->ChainOffset -= n * sizeof(MPT_STM_SIMPLE) / sizeof(u32);
	    }
	} else {
	    /* we have to build a chain on top of the first SGE */
	    length = le16_to_cpu(sge_chain->Length) -
		n * sizeof(MPT_STM_SIMPLE);
	    offset = sge_chain->NextChainOffset;
	    sge_chain = (MPT_STM_CHAIN *)&req->SGL;
	    sge_chain->Length = cpu_to_le16(length);
	    sge_chain->NextChainOffset = (u8)offset;
	    sge_chain->Flags =
		(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
		     MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
	    dma_addr = priv->hw_dma + ((u8 *)sge_simple - (u8 *)priv->hw);
	    stm_set_dma_addr(sge_chain->Address, dma_addr);
	    req->ChainOffset = (u32 *)sge_chain - (u32 *)req;
	}
    }

    /* fix up the offset and length */
    old_length = le32_to_cpu(req->DataLength);
    new_length = old_length - (new_offset - old_offset);

    req->RelativeOffset = cpu_to_le32(new_offset);
    req->DataLength = cpu_to_le32(new_length);
	TRACE_EXIT();
}

static void
stmapp_target_error_prioprity_io(MPT_STM_PRIV *priv,
		u32 reply_word, int index, int status, int reason,
		int lun, int tag)
{
    MPT_ADAPTER		*ioc = priv->ioc;
	struct mpt_cmd *mpt_cmd;
	struct scst_cmd *scst_cmd;
    volatile int	*io_state;
    CMD			*cmd;

	/*
	 *  we want stm_target_cleanup to do everything except repost the
	 *  command buffer, so fake it out a bit
	 */
	TRACE_ENTRY();
	io_state = priv->io_state + index;
	*io_state |= IO_STATE_AUTO_REPOST;
	scst_cmd = priv->scst_cmd[index];
	mpt_cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
	mpt_cmd->state = MPT_STATE_PROCESSED;

	stm_target_cleanup(priv, index);
	*io_state = IO_STATE_HIGH_PRIORITY;
printk(KERN_ERR MYNAM ": HIGH_PRIORITY %s\n", __FUNCTION__);

	cmd = priv->hw->cmd_buf + index;
	memset(cmd->rsp, 0, sizeof(cmd->rsp));

	switch (reason) {
		case PRIORITY_REASON_CMD_PARITY_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"detected parity error during Command phase\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x47, 0x00);
			break;
		case PRIORITY_REASON_MSG_OUT_PARITY_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"detected parity error during Message Out phase\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x43, 0x00);
			break;
		case PRIORITY_REASON_CMD_CRC_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"detected CRC error while receiving CMD_IU\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x47, 0x03);
			break;
		case PRIORITY_REASON_PROTOCOL_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"received Initiator Detected Error message\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x48, 0x00);
			break;
		case PRIORITY_REASON_DATA_OUT_PARITY_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"detected parity error during Data Out phase\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x47, 0x02);
			break;
		case PRIORITY_REASON_DATA_OUT_CRC_ERR:
			printk(KERN_ERR MYNAM ":%s "
					"detected CRC error during Data Out phase\n",
					ioc->name);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x47, 0x01);
			break;
		default:
			printk(KERN_ERR MYNAM ":%s unknown PriorityReason = %x\n",
					ioc->name, reason);
			stmapp_set_sense_info(priv, cmd,
					SK_ABORTED_COMMAND, 0x00, 0x00);
	}

	stm_send_target_status(priv, reply_word, index, 0, lun, tag);

	TRACE_EXIT();
}

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
stmapp_target_error(MPT_STM_PRIV *priv,
		    u32 reply_word, int index, int status, int reason)
{
    MPT_ADAPTER		*ioc = priv->ioc;
    volatile int	*io_state;
    CMD			*cmd;
    int			lun = 0;
    int			tag = 0;
    int			init_index = 0;

    TRACE_ENTRY();
    TRACE_DBG("%s target error, index %d, status %x, reason %x",
	    ioc->name, index, status, reason);

    io_state = priv->io_state + index;
    init_index = GET_INITIATOR_INDEX(reply_word);

    if (*io_state & IO_STATE_DATA_SENT) {
	TargetAssistRequest_t	*req;

	req = (TargetAssistRequest_t *)priv->current_mf[index];
	lun = get2bytes(req->LUN, 0);
	tag = req->QueueTag;
    }

    if (*io_state & IO_STATE_STATUS_SENT) {
	TargetStatusSendRequest_t	*req;

	req = (TargetStatusSendRequest_t *)priv->current_mf[index];
	lun = get2bytes(req->LUN, 0);
	tag = req->QueueTag;
    }

    /*
     *  if the status is Target Priority I/O, the I/O is still
     *  active and a response is needed
     */
    if (status == MPI_IOCSTATUS_TARGET_PRIORITY_IO) {
		stmapp_target_error_prioprity_io(priv, reply_word, index, status,
				reason, lun, tag);
		return;
    }

    /*
     *  if the status is Target Transfer Count Mismatch, Target Data Offset
     *  Error, Target Too Much Write Data, Target IU Too Short, EEDP Guard
     *  Error, EEDP Reference Tag Error, or EEDP Application Tag Error, thes
     *  I/O is still active and a response is needed
     */
    if (status == MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH ||
	status == MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR   ||
	status == MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA ||
	status == MPI_IOCSTATUS_TARGET_IU_TOO_SHORT ||
	status == MPI_IOCSTATUS_EEDP_GUARD_ERROR ||
	status == MPI_IOCSTATUS_EEDP_REF_TAG_ERROR ||
	status == MPI_IOCSTATUS_EEDP_APP_TAG_ERROR) {
	/*
	 *  we want stm_target_cleanup to do everything except repost the
	 *  command buffer, so fake it out a bit
	 */
	*io_state |= IO_STATE_AUTO_REPOST;
	stm_target_cleanup(priv, index);
	*io_state = 0;

	cmd = priv->hw->cmd_buf + index;
	memset(cmd->rsp, 0, sizeof(cmd->rsp));

	switch (status) {
	    case MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
		printk(KERN_ERR MYNAM ":%s transfer count mismatch\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x4b, 0x00);
		break;
	    case MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
		printk(KERN_ERR MYNAM ":%s data offset error\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x4b, 0x05);
		break;
	    case MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
		printk(KERN_ERR MYNAM ":%s too much write data\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x4b, 0x02);
		break;
	    case MPI_IOCSTATUS_TARGET_IU_TOO_SHORT:
		printk(KERN_ERR MYNAM ":%s IU too short\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x0e, 0x01);
		break;
	    case MPI_IOCSTATUS_EEDP_GUARD_ERROR:
		printk(KERN_ERR MYNAM ":%s EEDP Guard Error\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x10, 0x01);
		break;
	    case MPI_IOCSTATUS_EEDP_REF_TAG_ERROR:
		printk(KERN_ERR MYNAM ":%s EEDP Reference Tag Error\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x10, 0x03);
		break;
	    case MPI_IOCSTATUS_EEDP_APP_TAG_ERROR:
		printk(KERN_ERR MYNAM ":%s EEDP Application Tag Error\n",
		       ioc->name);
		stmapp_set_sense_info(priv, cmd,
				      SK_ABORTED_COMMAND, 0x10, 0x02);
		break;
	}

	stm_send_target_status(priv, reply_word, index, 0, lun, tag);
	return;
    }

    /*
     *  the SCSI firmware has a bug, where if the Status Data Not Sent error
     *  is returned, and the original command requested Auto Repost, the
     *  command buffer is reposted, even though an error is generated; so,
     *  ignore the error here, so that we don't post this command buffer again
     *  (that is, treat this as a successful completion, which is what it is)
     */
    /*
     *  Allow STATUS_SENT status to go through also, this is the
     *  result of an attempt to send a check condition with
     *  attached sense bytes.
     *  The IOC knows it can't send status and sense over a
     *  traditional SCSI cable (if non-packetized), so we should
     *  treat this as a successful completion, manually repost the
     *  command to the IOC, and free the SCST command.
     */
    if (IsScsi(priv) && (status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT)) {
	    if ((*io_state & IO_STATE_AUTO_REPOST) ||
		(*io_state & IO_STATE_STATUS_SENT)) {
		    /* if we know we were attempting to send status and sense
		     * simultaneously, indicate that we failed */
		    if (atomic_read(&priv->pending_sense[init_index]) ==
				    MPT_STATUS_SENSE_ATTEMPT) {
			    atomic_set(&priv->pending_sense[init_index],
				MPT_STATUS_SENSE_NOT_SENT);
		    }
		    stm_tgt_reply(ioc, reply_word);
		    TRACE_EXIT();
		    return;
	    }
    }

    *io_state &= ~IO_STATE_AUTO_REPOST;
    stm_target_cleanup(priv, index);
    TRACE_EXIT();
}

static void
stmapp_set_sense_info(MPT_STM_PRIV *priv,
		      CMD *cmd,
		      int sense_key,
		      int asc,
		      int ascq)
{
    u8	*info;

	TRACE_ENTRY();
    if (IsScsi(priv)) {
	SCSI_RSP	*rsp = (SCSI_RSP *)cmd->rsp;

	rsp->Status = STS_CHECK_CONDITION;
	rsp->Valid |= SCSI_SENSE_LEN_VALID;
	rsp->SenseDataListLength = cpu_to_be32(14);
	info = rsp->SenseData;
	if (rsp->Valid & SCSI_RSP_LEN_VALID) {
	    info += be32_to_cpu(rsp->PktFailuresListLength);
	}
    } else if (IsSas(priv)) {
	SSP_RSP		*rsp = (SSP_RSP *)cmd->rsp;

	rsp->Status = STS_CHECK_CONDITION;
	rsp->DataPres |= SSP_SENSE_LEN_VALID;
	rsp->SenseDataLength = cpu_to_be32(14);
	info = rsp->ResponseSenseData;
	if (rsp->DataPres & SSP_RSP_LEN_VALID) {
	    info += be32_to_cpu(rsp->ResponseDataLength);
	}
    } else {
	FCP_RSP		*rsp = (FCP_RSP *)cmd->rsp;

	rsp->FcpStatus = STS_CHECK_CONDITION;
	rsp->FcpFlags |= FCP_SENSE_LEN_VALID;
	rsp->FcpSenseLength = cpu_to_be32(14);
	info = rsp->FcpSenseData - sizeof(rsp->FcpResponseData);
	if (rsp->FcpFlags & FCP_RSP_LEN_VALID) {
	    info += be32_to_cpu(rsp->FcpResponseLength);
	}
    }

    info[0] = 0x70;
    info[2] = (u8)sense_key;
    info[7] = 6;

    info[12] = (u8)asc;
    info[13] = (u8)ascq;
	TRACE_EXIT();
}

#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)

#define MPT_PROC_LOG_ENTRY_NAME "trace_level"

#include <linux/proc_fs.h>

static int mpt_log_info_show(struct seq_file *seq, void *v)
{
	int res = 0;

	TRACE_ENTRY();

	res = scst_proc_log_entry_read(seq, trace_flag, NULL);

	TRACE_EXIT_RES(res);
	return res;
}

static int mpt_proc_log_entry_write(struct file *file, const char __user *buf,
					size_t length, loff_t *off)
{
	int res = 0;

	TRACE_ENTRY();

	res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
		SCST_DEFAULT_MPT_LOG_FLAGS, NULL);

	TRACE_EXIT_RES(res);
	return res;
}

static struct scst_proc_data mpt_log_proc_data = {
	SCST_DEF_RW_SEQ_OP(mpt_proc_log_entry_write)
	.show = mpt_log_info_show,
};
#endif

static int mpt_proc_log_entry_build(struct scst_tgt_template *templ)
{
	int res = 0;
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
	struct proc_dir_entry *p, *root;

	TRACE_ENTRY();

	root = scst_proc_get_tgt_root(templ);
	if (root) {

		mpt_log_proc_data.data = (void *)templ->name;
		p = scst_create_proc_entry(root, MPT_PROC_LOG_ENTRY_NAME,
					&mpt_log_proc_data);
		if (p == NULL) {
			PRINT_ERROR("Not enough memory to register "
					"target driver %s entry %s in /proc",
					templ->name, MPT_PROC_LOG_ENTRY_NAME);
			res = -ENOMEM;
			goto out;
		}

	}
out:

	TRACE_EXIT_RES(res);
#endif
	return res;
}

static void mpt_proc_log_entry_clean(struct scst_tgt_template *templ)
{
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
	struct proc_dir_entry *root;

	TRACE_ENTRY();

	root = scst_proc_get_tgt_root(templ);

	if (root) {
		remove_proc_entry(MPT_PROC_LOG_ENTRY_NAME, root);
	}
	TRACE_EXIT();
#endif
}

static int __init mpt_target_init(void)
{
	int res = 0;

	TRACE_ENTRY();

	res = scst_register_target_template(&tgt_template);
	if (res < 0)
		goto out;

	res = mpt_proc_log_entry_build(&tgt_template);
	if (res < 0) {
		goto out_unreg_target;
	}

 out:
	TRACE_EXIT_RES(res);

	return res;

out_unreg_target:
	scst_unregister_target_template(&tgt_template);
	goto out;
}

static void __exit mpt_target_exit(void)
{
	TRACE_ENTRY();

	mpt_proc_log_entry_clean(&tgt_template);
	scst_unregister_target_template(&tgt_template);
	_mpt_stm_exit();

	TRACE_EXIT();
	return;
}

module_init(mpt_target_init);
module_exit(mpt_target_exit);

MODULE_AUTHOR("Hu Gang <hugang@soulinfo.com>");
MODULE_DESCRIPTION("Fusion MPT SCSI Target Mode Driver for SCST Version 0.1");
MODULE_LICENSE("GPL");
