From: <vl...@us...> - 2006-12-13 11:43:00
|
Revision: 49 http://svn.sourceforge.net/scst/?rev=49&view=rev Author: vlnb Date: 2006-12-13 03:42:59 -0800 (Wed, 13 Dec 2006) Log Message: ----------- LSI/MPT target driver added Modified Paths: -------------- trunk/Makefile trunk/scst/src/Makefile Added Paths: ----------- trunk/mpt/ trunk/mpt/Kconfig trunk/mpt/Makefile trunk/mpt/README trunk/mpt/in-tree/ trunk/mpt/in-tree/Kconfig.diff trunk/mpt/in-tree/Makefile.diff trunk/mpt/mpt_scst.c trunk/mpt/mpt_scst.h trunk/mpt/scsi3.h trunk/scst/kernel/in-tree/Makefile.scsi.Linux-2.6.15.patch Modified: trunk/Makefile =================================================================== --- trunk/Makefile 2006-12-13 11:32:36 UTC (rev 48) +++ trunk/Makefile 2006-12-13 11:42:59 UTC (rev 49) @@ -21,28 +21,34 @@ SCST_DIR=scst QLA_INI_DIR=qla2x00t QLA_DIR=qla2x00t/qla2x00-target +LSI_DIR=mpt all: cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi install: cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi uninstall: cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi clean: cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi extraclean: cd $(SCST_DIR) && $(MAKE) $@ @if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi scst: cd $(SCST_DIR) && $(MAKE) @@ -76,6 +82,21 @@ cd $(QLA_INI_DIR)/.. && $(MAKE) extraclean cd $(QLA_DIR) && $(MAKE) extraclean +lsi: + cd $(LSI_DIR) && $(MAKE) + +lsi_install: + cd $(LSI_DIR) && $(MAKE) install + +lsi_uninstall: + cd $(LSI_DIR) && $(MAKE) uninstall + +lsi_clean: + cd $(LSI_DIR) && $(MAKE) clean + +lsi_extraclean: + cd $(LSI_DIR) && $(MAKE) extraclean + help: @echo " all (the default) : make all" @echo " clean : clean files" @@ -94,9 +115,16 @@ @echo " qla_extraclean : 2.6 qla target: clean + clean dependencies" @echo " qla_install : 2.6 qla target: install" @echo " qla_uninstall : 2.6 qla target: uninstall" + @echo "" + @echo " lsi : make lsi target" + @echo " lsi_clean : lsi target: clean " + @echo " lsi_extraclean : lsi target: clean + clean dependencies" + @echo " lsi_install : lsi target: install" + @echo " lsi_uninstall : lsi target: uninstall" @echo " Notes :" @echo " - install and uninstall must be made as root" .PHONY: all install uninstall clean extraclean help \ qla qla_install qla_uninstall qla_clean qla_extraclean \ + lsi lsi_install lsi_uninstall lsi_clean lsi_extraclean \ scst scst_install scst_uninstall scst_clean scst_extraclean Added: trunk/mpt/Kconfig =================================================================== --- trunk/mpt/Kconfig (rev 0) +++ trunk/mpt/Kconfig 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,10 @@ + +config FUSION_SCST + tristate "Fusion MPT SCST driver" + depends on FUSION + ---help--- + This module enables target mode for use by the SCST middle + level drivers. You will also need the SCST middle level + drivers from http://scst.sf.net/. + + If unsure whether you really want or need this, say N. Added: trunk/mpt/Makefile =================================================================== --- trunk/mpt/Makefile (rev 0) +++ trunk/mpt/Makefile 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,90 @@ +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, version 2 +# of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# +# Main targets: +# all (the default) : make all +# clean : clean files +# extraclean : clean + clean dependencies +# install : install +# uninstall : uninstall +# +# Notes : +# - install and uninstall must be made as root +# + +#SCST_INC_DIR := /usr/local/include/scst +#SCST_DIR := $(SCST_INC_DIR) +SCST_INC_DIR := $(SUBDIRS)/../scst/include +SCST_DIR := $(shell pwd)/../scst/src + +EXTRA_CFLAGS += -I$(SCST_INC_DIR) + +EXTRA_CFLAGS += -DEXTRACHECKS +#EXTRA_CFLAGS += -DTRACING +#EXTRA_CFLAGS += -DDEBUG +#EXTRA_CFLAGS += -DDEBUG_WORK_IN_THREAD + +ifeq ($(KVER),) + ifeq ($(KDIR),) + KDIR := /lib/modules/$(shell uname -r)/build + endif +else + KDIR := /lib/modules/$(KVER)/build +endif + +LSI_INC_DIR := $(KDIR)/drivers/message/fusion +EXTRA_CFLAGS += -I$(LSI_INC_DIR) + +ifneq ($(KERNELRELEASE),) +obj-m := mpt_scst.o + +else + +all: Modules.symvers Module.symvers + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m + +tgt: Modules.symvers Module.symvers + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=n + +install: all + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + modules_install + -depmod -a + +SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) +ifneq ($(SCST_MOD_VERS),) +Modules.symvers: $(SCST_DIR)/Modules.symvers + cp $(SCST_DIR)/Modules.symvers . +else +.PHONY: Modules.symvers +endif + +# It's renamed in 2.6.18 +SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null) +ifneq ($(SCST_MOD_VERS),) +Module.symvers: $(SCST_DIR)/Module.symvers + cp $(SCST_DIR)/Module.symvers . +else +.PHONY: Module.symvers +endif + +uninstall: + rm -f $(INSTALL_DIR)/mpt_scst.ko + -/sbin/depmod -a +endif + +clean: + rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend *~ Modules.symvers Module.symvers + rm -rf .tmp_versions + +extraclean: clean + +.PHONY: all tgt install uninstall clean extraclean Added: trunk/mpt/README =================================================================== --- trunk/mpt/README (rev 0) +++ trunk/mpt/README 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,12 @@ +Target driver for LSI/MPT XXX cards +=================================== + +Version X.X.X, XX XXX 200X +-------------------------- + +This driver was originally developed by Hu Gang and then is developed by +Erik Habbinga <eri...@in...>. It is on the early stage +of development. + +The current maintainer of this driver is Erik. Please send him all +question related to it (CC: scs...@li...). Added: trunk/mpt/in-tree/Kconfig.diff =================================================================== --- trunk/mpt/in-tree/Kconfig.diff (rev 0) +++ trunk/mpt/in-tree/Kconfig.diff 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,10 @@ +--- Kconfig_orig 2006-03-05 22:07:54.000000000 +0300 ++++ Kconfig 2006-12-13 13:00:41.000000000 +0300 +@@ -100,4 +100,7 @@ + + If unsure whether you really want or need this, say N. + ++config FUSION_SCST ++ source "drivers/message/fusion/mpt_scst/Kconfig" ++ + endmenu Added: trunk/mpt/in-tree/Makefile.diff =================================================================== --- trunk/mpt/in-tree/Makefile.diff (rev 0) +++ trunk/mpt/in-tree/Makefile.diff 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,30 @@ +--- Makefile_orig 2006-03-05 22:07:54.000000000 +0300 ++++ Makefile 2006-12-13 14:11:52.000000000 +0300 +@@ -8,7 +8,6 @@ + #EXTRA_CFLAGS += -DMPT_DEBUG_EXIT + #EXTRA_CFLAGS += -DMPT_DEBUG_FAIL + +- + # + # driver/module specifics... + # +@@ -30,9 +29,19 @@ + #CFLAGS_mptctl.o += -DMPT_DEBUG_IOCTL + # + ++SCST_INC_DIR := /usr/local/include/scst ++SCST_DIR := $(SCST_INC_DIR) ++ ++CFLAGS_mpt_scst.o += -I$(SCST_INC_DIR) -I$(src) ++CFLAGS_mpt_scst.o += -DEXTRACHECKS ++#CFLAGS_mpt_scst.o += -DTRACING ++#CFLAGS_mpt_scst.o += -DDEBUG ++#CFLAGS_mpt_scst.o += -DDEBUG_WORK_IN_THREAD ++ + #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-} LSI_LOGIC + + obj-$(CONFIG_FUSION_SPI) += mptbase.o mptscsih.o mptspi.o ++obj-$(CONFIG_FUSION_SCST) += mpt_scst/mpt_scst.o + obj-$(CONFIG_FUSION_FC) += mptbase.o mptscsih.o mptfc.o + obj-$(CONFIG_FUSION_SAS) += mptbase.o mptscsih.o mptsas.o + obj-$(CONFIG_FUSION_CTL) += mptctl.o Added: trunk/mpt/mpt_scst.c =================================================================== --- trunk/mpt/mpt_scst.c (rev 0) +++ trunk/mpt/mpt_scst.c 2006-12-13 11:42:59 UTC (rev 49) @@ -0,0 +1,5241 @@ +/* + * mpt_scst.c + * + * Copyright (C) 2005 Beijing Soul Technology Co., Ltd. + * Copyright (C) 2002, 2003, 2004 LSI Logic Corporation + * Copyright (C) 2004 Vladislav Bolkhovitin <vs...@vl...> + * and Leonid Stoljar + * + * MPT SCSI target mode driver for SCST. + * + * Originally By: Stephen Shirron + * Port to SCST By: Hu Gang <hu...@so...> + * + * 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 <scsi/scsi_host.h> + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +#include <linux/pci.h> +#endif + +#include "scsi_tgt.h" + +#include <scst_debug.h> + +#include <scst_debug.c> +#include <linux/proc_fs.h> + +#include "mpt_scst.h" + +#define MYNAM "mpt_scst" + +#ifdef TRACING +static int trace_mpi = 0; + +#define TRACE_MPI 0x80000000 + +static char *mpt_state_string[] = { + "0", + "new", + "need data", + "data in", + "data out", + "processed", + "NULL", +}; +#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 int +mpt_proc_read(char *buffer, char **start, off_t offset, int length, int *eof, + struct scst_tgt *scst_tgt) +{ + struct mpt_tgt *tgt = scst_tgt_get_tgt_priv(scst_tgt); + MPT_ADAPTER *ioc = tgt->priv->ioc; + int res = 0, len = 0; + MPT_STM_PRIV *priv = tgt->priv; + + TRACE_ENTRY(); + TRACE_DBG("res %d, buffer %p, length %d, %d, priv %p, tgt %p", + res, buffer, length, len, priv, tgt); + + BUG_ON(tgt == NULL); + BUG_ON(ioc == NULL); + + len = snprintf(buffer, length, + "ProductID :0x%04x (%s)\n" + "Target Enable :%s\n", + ioc->facts.ProductID, + ioc->prod_name, + tgt->target_enable ? "True" : "False"); +#define LEN_CHECK(res, buffer, length) \ + TRACE_DBG("res %d, buffer %p, length %d, %d", res, buffer, length, len); \ + res += len; buffer += len; length -= len; \ + if (length <= 0) goto out; + + LEN_CHECK(res, buffer, length) + + if (ioc->bus_type == SCSI) { + int i = 0; + len = snprintf(buffer, length, + "Target ID :%d\n" + "Capabilities :0x%x\n" + "PhysicalInterface:0x%x\n", + tgt->target_id, + priv->SCSIPortPage0.Capabilities, + priv->SCSIPortPage0.PhysicalInterface); + LEN_CHECK(res, buffer, length) + + len = snprintf(buffer, length, + "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); + LEN_CHECK(res, buffer, length); + + len = snprintf(buffer, length, + "PortFlags :0x%x\n" + "PortSettings :0x%x\n", + priv->SCSIPortPage2.PortFlags, + priv->SCSIPortPage2.PortSettings); + LEN_CHECK(res, buffer, length); +#if 0 + for (i = 0; i < 16; i++) { + len = snprintf(buffer, length, + " DeviceSeting %02d: 0x%x 0x%x 0x%x\n", + priv->SCSIPortPage2.DeviceSettings[i].Timeout, + priv->SCSIPortPage2.DeviceSettings[i].SyncFactor, + priv->SCSIPortPage2.DeviceSettings[i].DeviceFlags); + LEN_CHECK(res, buffer, length); + } +#endif + for (i = 0; i < NUM_SCSI_DEVICES; i++) { + len = snprintf(buffer, length, + " Device %02d: 0x%x, 0x%x\n", + i, + priv->SCSIDevicePage1[i].RequestedParameters, + priv->SCSIDevicePage1[i].Configuration); + LEN_CHECK(res, buffer, length); + } + } + + if (ioc->bus_type == FC) { + len = snprintf(buffer, length, + "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); + LEN_CHECK(res, buffer, length); + } +#undef LEN_CHECK +out: + TRACE_EXIT_RES(res); + + return res; +} + +static int +mpt_proc_write(struct file *file, const char *buf, unsigned long length, + struct scst_tgt *scst_tgt) +{ + + struct mpt_tgt *tgt = scst_tgt_get_tgt_priv(scst_tgt); + MPT_ADAPTER *ioc = tgt->priv->ioc; + int res = 0; + char tmp[32+1]; + + TRACE_ENTRY(); + res = min(32, (int)length); + memcpy(tmp, buf, res); + 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; + } + } + +#ifdef DEBUG + if (strncmp("target_id:", tmp, strlen("target_id:")) == 0) { + char *s = tmp + strlen("target_id:"); + TRACE_DBG("target id is '%s'", s); + } +#endif + + TRACE_EXIT_RES(res); + + return length; +} + +static int mpt_proc_info(char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_tgt *tgt, int inout) +{ + if (inout) { + return mpt_proc_write(NULL, buffer, length, tgt); + } + return mpt_proc_read(buffer, start, offset, length, eof, tgt); +} + + +static int mpt_target_detect(struct scst_tgt_template *temp1); +static int mpt_target_release(struct scst_tgt *scst_tgt); +static int mpt_xmit_response(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, int 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, + .proc_info = mpt_proc_info, +}; + +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) { + BUG_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; + + 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->target_id = 0; + atomic_set(&tgt->sess_count, 0); + init_waitqueue_head(&tgt->waitQ); + + tgt->scst_tgt = scst_register(&tgt_template); + if (tgt->scst_tgt == NULL) { + PRINT_ERROR(MYNAM ": scst_register() " + "failed for host %p", pdev); + + ret = -ENODEV; + 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(). 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 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) { + scst_sess_set_tgt_priv(scst_sess, sess); + + 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, int 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]; + uint8_t *buf = NULL; + + 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); + if (scst_cmd_get_sg_cnt(scst_cmd)) { + pci_unmap_sg(priv->ioc->pcidev, + scst_cmd_get_sg(scst_cmd), + scst_cmd_get_sg_cnt(scst_cmd), + scst_to_dma_dir(scst_cmd_get_data_direction(scst_cmd))); + } else { + pci_unmap_single(priv->ioc->pcidev, cmd->dma_handle, + scst_get_buf_first(scst_cmd, &buf), + scst_to_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; + struct scst_cmd *scst_cmd; + struct mpt_cmd *cmd; + volatile int *io_state; + + TRACE_ENTRY(); + + index = GET_IO_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 context = SCST_CONTEXT_TASKLET; + int rx_status = SCST_RX_STATUS_SUCCESS; + + cmd->state = MPT_STATE_DATA_IN; + +#ifdef DEBUG_WORK_IN_THREAD + context = SCST_CONTEXT_THREAD; +#endif + TRACE_DBG("Data received, context %x, rx_status %d", + context, rx_status); + + BUG_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_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 ((*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); + } + + scst_tgt_cmd_done(scst_cmd); + + 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_msg_frame_free(priv, 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, 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; + scst_sess_set_tgt_priv(sess->scst_sess, 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 { + 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, page_address(sg->page), + sg->offset, sg->dma_address, sg->length); + TRACE_BUFFER("sg data", page_address(sg->page), (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; + TRACE_ENTRY(); + TRACE_DBG("bufflen %d, %p", bufflen, prm->buffer); + if (prm->use_sg) { + int i; + prm->sg = (struct scatterlist *)prm->buffer; + prm->seg_cnt = + pci_map_sg(priv->ioc->pcidev, prm->sg, prm->use_sg, + scst_to_dma_dir(prm->data_direction)); + + pci_dma_sync_sg_for_cpu(priv->ioc->pcidev, prm->sg, + prm->use_sg, + scst_to_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_dma_dir(prm->data_direction)); + } else { + prm->cmd->dma_handle = + pci_map_single(priv->ioc->pcidev, prm->buffer, + prm->bufflen, + scst_to_dma_dir(prm->data_direction)); + + pci_dma_sync_single_for_cpu(priv->ioc->pcidev, prm->cmd->dma_handle, prm->bufflen, scst_to_dma_dir(prm->data_direction)); + sgl->sge[0].length = prm->bufflen; + sgl->sge[0].address = virt_to_phys(prm->buffer); + + mpt_dump_sge(&sgl->sge[0], NULL); + pci_dma_sync_single_for_device(priv->ioc->pcidev, prm->cmd->dma_handle, prm->bufflen, scst_to_dma_dir(prm->data_direction)); + + prm->seg_cnt = 1; + } + + 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); + } + } + + BUG_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 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 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 resp_flags; + //uint16_t full_req_cnt; + //int data_sense_flag = 0; + + TRACE_ENTRY(); + 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; + resp_flags = scst_cmd_get_tgt_resp_flags(scst_cmd); + + /* FIXME */ + prm.sense_buffer_len = 14; + + TRACE_DBG("rq_result=%x, resp_flags=%x, %x, %d", prm.rq_result, + resp_flags, prm.bufflen, prm.sense_buffer_len); + if (prm.rq_result != 0) + TRACE_BUFFER("Sense", prm.sense_buffer, prm.sense_buffer_len); + + if ((resp_flags & SCST_TSC_FLAG_STATUS) == 0) { + /* ToDo, after it's done in SCST */ + PRINT_ERROR(MYNAM ": SCST_TSC_FLAG_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: + scst_tgt_cmd_done(scst_cmd); + goto out; +} + +/* + * 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(); + 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 = (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_THREAD); + 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); + MPT_STM_PRIV *priv = cmd->priv; + int index = GET_IO_INDEX(cmd->reply_word); + //MPT_ADAPTER *ioc = priv->ioc; + + TRACE_ENTRY(); + + TRACE_DBG("scst_cmd is %p, cmd %p, %p", + priv->scst_cmd[index], cmd, scst_cmd); + WARN_ON(priv->scst_cmd[index] != scst_cmd); + priv->scst_cmd[index] = NULL; + + 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_TS: + 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_TS: + 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); + ret... [truncated message content] |