From 58c3c9893aecbd1aeb1c85facbc28ebd93771c94 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 13 Dec 2006 11:42:59 +0000 Subject: [PATCH] LSI/MPT target driver added git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@49 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 28 + mpt/Kconfig | 10 + mpt/Makefile | 90 + mpt/README | 12 + mpt/in-tree/Kconfig.diff | 10 + mpt/in-tree/Makefile.diff | 30 + mpt/mpt_scst.c | 5241 +++++++++++++++++ mpt/mpt_scst.h | 366 ++ mpt/scsi3.h | 718 +++ .../in-tree/Makefile.scsi.Linux-2.6.15.patch | 10 + scst/src/Makefile | 12 + 11 files changed, 6527 insertions(+) create mode 100644 mpt/Kconfig create mode 100644 mpt/Makefile create mode 100644 mpt/README create mode 100644 mpt/in-tree/Kconfig.diff create mode 100644 mpt/in-tree/Makefile.diff create mode 100644 mpt/mpt_scst.c create mode 100644 mpt/mpt_scst.h create mode 100644 mpt/scsi3.h create mode 100644 scst/kernel/in-tree/Makefile.scsi.Linux-2.6.15.patch diff --git a/Makefile b/Makefile index f22419d61..dfecb959d 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ qla_extraclean: 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 @@ help: @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 diff --git a/mpt/Kconfig b/mpt/Kconfig new file mode 100644 index 000000000..030976006 --- /dev/null +++ b/mpt/Kconfig @@ -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. diff --git a/mpt/Makefile b/mpt/Makefile new file mode 100644 index 000000000..cbc5d6ba7 --- /dev/null +++ b/mpt/Makefile @@ -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 diff --git a/mpt/README b/mpt/README new file mode 100644 index 000000000..925cf141c --- /dev/null +++ b/mpt/README @@ -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 . 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: scst-devel@lists.sourceforge.net). diff --git a/mpt/in-tree/Kconfig.diff b/mpt/in-tree/Kconfig.diff new file mode 100644 index 000000000..1f611f6d1 --- /dev/null +++ b/mpt/in-tree/Kconfig.diff @@ -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 diff --git a/mpt/in-tree/Makefile.diff b/mpt/in-tree/Makefile.diff new file mode 100644 index 000000000..cd2ad3900 --- /dev/null +++ b/mpt/in-tree/Makefile.diff @@ -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 diff --git a/mpt/mpt_scst.c b/mpt/mpt_scst.c new file mode 100644 index 000000000..95ad7b0c3 --- /dev/null +++ b/mpt/mpt_scst.c @@ -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 + * and Leonid Stoljar + * + * MPT SCSI target mode driver for SCST. + * + * Originally By: Stephen Shirron + * Port to SCST By: Hu Gang + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +#include +#endif + +#include "scsi_tgt.h" + +#include + +#include +#include + +#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 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); + 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) { + 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); + 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 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,index); + 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 = (u16)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 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 + + priv->current_mf[index] = NULL; + 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 = (u16)priv->num_cmd_buffers; + req->CmdBufferLength = sizeof(priv->hw->cmd_buf[0].cmd); + req->NextCmdBufferOffset = 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 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,index); + memset(req, 0, sizeof(*req)); + + req->Function = MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST; + req->CmdBufferCount = 1; + req->IoIndex[0] = (u16)cpu_to_le16(index); + + priv->io_state[index] |= IO_STATE_POSTED; + +#ifdef 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,index); + 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 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 + priv->current_mf[index] = NULL; + 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; + MPT_FRAME_HDR *mf = priv->current_mf[index]; + + TRACE_ENTRY(); + if (priv->io_state[index] & IO_STATE_DATA_SENT) { + priv->current_mf[index] = NULL; + } + 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; + + 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; + } + } + 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->current_mf[index] = mf; + 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 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,index); + 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 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 + priv->current_mf[index] = NULL; + 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,index); + 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,index); + 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 TRACING + 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,index); + 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,index); + 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,index); + 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,index); + 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,index); + 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 TRACING + MPT_ADAPTER *ioc = priv->ioc; +#endif + SCSIPortPage0_t *ScsiPort0; + SCSIPortPage1_t *ScsiPort1; + SCSIPortPage2_t *ScsiPort2; + int id; + 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)); + + id = 0;//le32_to_cpu(ScsiPort2->PortSettings) & MPI_SCSIPORTPAGE2_PORT_HOST_ID_MASK; + TRACE_DBG("%s scsi id is %d", ioc->name, id); + priv->port_id = 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, id, cap); + + 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 = MPI_SCSIPORTPAGE1_TARGCONFIG_INIT_TARG; + ScsiPort1->TargetConfig = MPI_SCSIPORTPAGE1_TARGCONFIG_TARG_ONLY; +// ScsiPort1->IDConfig = 0x7; + memcpy(priv->hw->config_buf, (u32 *)ScsiPort1, sizeof(*ScsiPort1)); + stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 1, 0, 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; + 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); + } + TRACE_EXIT(); + + return (0); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +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 = 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\n", ioc->name); + } + 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) { + /* 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 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 = 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 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 = (u16)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); + + mpt_stm_index = 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); + if (priv != NULL) + kfree(priv); + 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; + } + 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; + } + + mpt_stm_index = 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 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 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; + + mpt_msg_frame_free(priv, index); + /* stm_target_cleanup(priv, index); */ + *io_state = IO_STATE_HIGH_PRIORITY; + + 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; + + TRACE_ENTRY(); + TRACE_DBG("%s target error, index %d, status %x, reason %x", + ioc->name, index, status, reason); + + io_state = priv->io_state + index; + + 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) + */ + if (IsScsi(priv) && status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT) { + if (*io_state & IO_STATE_AUTO_REPOST) { + 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(); +} + +#define MPT_PROC_LOG_ENTRY_NAME "trace_level" +#ifdef TRACING +static struct scst_proc_log mpt_spec_tbl[] = +{ + { TRACE_MPI, "mpt_mpi" }, + { 0, NULL } +}; +#endif + +static int mpt_proc_log_entry_read(char *buffer, char **start, + off_t offset, int length, int *eof, + void *data) +{ + int res = 0; + + TRACE_ENTRY(); +#ifdef TRACING + res = scst_proc_log_entry_read(buffer, start, offset, length, eof, + data, trace_flag, mpt_spec_tbl); +#endif + + TRACE_EXIT_RES(res); + return res; +} + +static int mpt_proc_log_entry_write(struct file *file, const char *buf, + unsigned long length, void *data) +{ + int res = 0; + + TRACE_ENTRY(); + +#ifdef TRACING + res = scst_proc_log_entry_write(file, buf, length, data, + &trace_flag, (TRACE_FUNCTION | TRACE_OUT_OF_MEM | TRACE_MGMT | \ + TRACE_MINOR | TRACE_SPECIAL), mpt_spec_tbl); +#endif + + TRACE_EXIT_RES(res); + return res; +} + +static int mpt_proc_log_entry_build(struct scst_tgt_template *templ) +{ + int res = 0; + struct proc_dir_entry *p, *root; + + TRACE_ENTRY(); + root = scst_proc_get_tgt_root(templ); + + if (root) { + p = create_proc_read_entry(MPT_PROC_LOG_ENTRY_NAME, + S_IFREG | S_IRUGO | S_IWUSR, root, + mpt_proc_log_entry_read, + (void *)templ->name); + 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; + } + p->write_proc = mpt_proc_log_entry_write; + } +out: + + TRACE_EXIT_RES(res); + + return res; +} + +static void mpt_proc_log_entry_clean(struct scst_tgt_template *templ) +{ + 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(); +} + +static int __init mpt_target_init(void) +{ + int res = 0; + + TRACE_ENTRY(); + + if (scst_register_target_template(&tgt_template) < 0) { + res = -ENODEV; + 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; +} + +#ifdef DEBUG +unsigned long trace_flag = TRACE_FUNCTION | TRACE_OUT_OF_MEM | TRACE_SPECIAL; +#else +# ifdef TRACING +unsigned long trace_flag = TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_SPECIAL; +# endif +#endif + +module_init(mpt_target_init); +module_exit(mpt_target_exit); + +MODULE_AUTHOR("Hu Gang "); +MODULE_DESCRIPTION("Fusion MPT SCSI Target Mode Driver for SCST Version 0.1"); +MODULE_LICENSE("GPL"); diff --git a/mpt/mpt_scst.h b/mpt/mpt_scst.h new file mode 100644 index 000000000..830d555fe --- /dev/null +++ b/mpt/mpt_scst.h @@ -0,0 +1,366 @@ +#ifndef __MPT_SCST_H +#define __MPT_SCST_H + +#if defined(MODULE) && !defined(__GENKSYMS__) +#include +#include +#endif + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#endif + +#define _HS_SLEEP ,0 +#define _IOC_ID ioc +#define _HANDLE_IOC_ID ioc + +#ifndef MPT_STM_64_BIT_DMA /* determines the size of DMA addresses */ +#define MPT_STM_64_BIT_DMA 1 +#endif + +#include "linux_compat.h" +#include "mptbase.h" + +#ifndef MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED +#define MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED 0x24000002 +#endif + +#define MF_TO_INDEX(mf) le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx) + +#include "scsi3.h" + +/*****************************************************************************/ +#ifdef MPI_STM_IO_DEBUG +#define dioprintk printk +#else +#define dioprintk printk +#endif + +typedef MPI_TARGET_FCP_CMD_BUFFER FCP_CMD; + +typedef MPI_TARGET_SCSI_SPI_CMD_BUFFER SCSI_CMD; + +#define SSP_CMD_FRAME 0x06 +#define SSP_TASK_FRAME 0x16 + +typedef MPI_TARGET_SSP_CMD_BUFFER SSP_CMD; +typedef MPI_TARGET_SSP_TASK_BUFFER SSP_TASK; + +#define FCP_REQUEST_CONFIRM (1<<4) +#define FCP_RESID_UNDER (1<<3) +#define FCP_RESID_OVER (1<<2) +#define FCP_SENSE_LEN_VALID (1<<1) +#define FCP_RSP_LEN_VALID (1<<0) + +typedef struct _FCP_RSP /* this struct is wrong in rev 1.02.04 of mpi_targ.h */ +{ + U8 Reserved0[8]; /* 00h */ + U8 Reserved1[2]; /* 08h */ + U8 FcpFlags; /* 0Ah */ + U8 FcpStatus; /* 0Bh */ + U32 FcpResid; /* 0Ch */ + U32 FcpSenseLength; /* 10h */ + U32 FcpResponseLength; /* 14h */ + U8 FcpResponseData[8]; /* 18h */ + U8 FcpSenseData[32]; /* Pad to 64 bytes */ /* 20h */ +} FCP_RSP; + +#define SCSI_SENSE_LEN_VALID (1<<1) +#define SCSI_RSP_LEN_VALID (1<<0) + +typedef MPI_TARGET_SCSI_SPI_STATUS_IU SCSI_RSP; + +#define SSP_SENSE_LEN_VALID (1<<1) +#define SSP_RSP_LEN_VALID (1<<0) + +typedef MPI_TARGET_SSP_RSP_IU SSP_RSP; + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* + * Fusion MPT STM private structures + */ + +#define IsFc(priv) \ + (priv->ioc->pfacts[0].PortType == MPI_PORTFACTS_PORTTYPE_FC) +#define IsScsi(priv) \ + (priv->ioc->pfacts[0].PortType == MPI_PORTFACTS_PORTTYPE_SCSI) +#define IsSas(priv) \ + (priv->ioc->pfacts[0].PortType == MPI_PORTFACTS_PORTTYPE_SAS) + +#define ABORT_ALL (-1) + +#define NUM_CMD_BUFFERS 128 +#define NUM_ELS_BUFFERS 64 + +#define NUM_ALIASES 0 /* 0 to 125, hardware restriction */ + +#define ELS 0x22 +#define FC4LS 0x32 +#define ABTS 0x81 +#define BA_ACC 0x84 + +#define LS_RJT 0x01 +#define LS_ACC 0x02 +#define PLOGI 0x03 +#define LOGO 0x05 +#define SRR 0x14 +#define PRLI 0x20 +#define PRLO 0x21 +#define ADISC 0x52 +#define RSCN 0x61 + +typedef struct _MPT_SGE +{ + u32 length; +#if MPT_STM_64_BIT_DMA + u64 address; +#else + u32 address; +#endif +} MPT_SGE; + +#define NUM_SGES 64 +#define NUM_CHAINS (NUM_SGES/8) /* one chain for every 8 SGEs */ + +typedef struct _CMD { + u8 cmd[64]; + u8 rsp[64]; + MPT_SGE chain_sge[NUM_SGES+NUM_CHAINS]; + u32 reply_word; + int alias; + int lun; + int tag; +} CMD; + +typedef struct _FC_ELS { + u32 fc_els[32]; +} FC_ELS; + +typedef struct _MPT_STM_HW { + CMD cmd_buf[NUM_CMD_BUFFERS]; + FC_ELS fc_link_serv_buf[NUM_ELS_BUFFERS]; + u32 config_buf[256]; + u32 ctsend_buf[256]; + u32 exlink_buf[32]; +} MPT_STM_HW; + +typedef struct _MPT_SGL +{ + u32 num_sges; + MPT_SGE sge[NUM_SGES]; +} MPT_SGL; + +typedef struct _MPT_STM_PRIV +{ + MPT_ADAPTER *ioc; + int enable_target_mode; + int fcp2_capable; + int num_sge_chain; + int num_sge_target_assist; + int num_cmd_buffers; + int num_els_buffers; + int num_aliases; + MPT_STM_HW *hw; + dma_addr_t hw_dma; + U64 wwnn; + U64 wwpn; + int port_id; + int protocol; + volatile int port_flags; + volatile int port_speed; + volatile int port_state; + volatile int device_changed; + int port_enable_loginfo; + volatile int port_enable_pending; + volatile int target_mode_abort_pending; + volatile int link_serv_abort_pending; + volatile int fc_primitive_send_pending; + volatile int ex_link_service_send_pending; + volatile int config_pending; + volatile int in_reset; + volatile int poll_enabled; + volatile int exiting; + MPT_FRAME_HDR *config_mf; + ConfigReply_t config_rep; + volatile int io_state[NUM_CMD_BUFFERS]; + volatile int els_state[NUM_ELS_BUFFERS]; + MPT_FRAME_HDR *current_mf[NUM_CMD_BUFFERS]; + MPT_FRAME_HDR *status_deferred_mf[NUM_CMD_BUFFERS]; + MPT_SGL sgl; + SCSIPortPage0_t SCSIPortPage0; + SCSIPortPage1_t SCSIPortPage1; + SCSIPortPage2_t SCSIPortPage2; +#define NUM_SCSI_DEVICES 16 + SCSIDevicePage1_t SCSIDevicePage1[NUM_SCSI_DEVICES]; + struct mpt_tgt *tgt; + struct scst_cmd *scst_cmd[NUM_CMD_BUFFERS]; +} MPT_STM_PRIV; + +#define IO_STATE_POSTED 0x1 +#define IO_STATE_DATA_SENT 0x2 +#define IO_STATE_STATUS_SENT 0x4 +#define IO_STATE_STATUS_DEFERRED 0x8 +#define IO_STATE_INCOMPLETE 0x10 +#define IO_STATE_AUTO_REPOST 0x20 +#define IO_STATE_ABORTED 0x40 +#define IO_STATE_HIGH_PRIORITY 0x80 +#define IO_STATE_REQUEST_ABORTED 0x100 +#define IO_STATE_REISSUE_REQUEST 0x200 +#define IO_STATE_ADJUST_OFFSET 0x400 +#define IO_STATE_CONVERT_TA_TO_TSS 0x800 +#define IO_STATE_REDO_COMMAND 0x1000 + +#define get2bytes(x, y) ((x[y] << 8) + x[y+1]) +#define get3bytes(x, y) ((x[y] << 16) + (x[y+1] << 8) + x[y+2]) +#define get4bytes(x, y) ((x[y] << 24) + (x[y+1] << 16) + (x[y+2] << 8) + x[y+3]) +#define get8bytes(x, y) (((u64)get4bytes(x, y) << 32) + get4bytes(x, y+4)) + +#define InitiatorIndex_0100 Reserved_0100_InitiatorIndex +#define FWVersion_0101 Reserved_0101_FWVersion +#define EventDataSasPhyLinkStatus_t MpiEventDataSasPhyLinkStatus_t + +#ifndef MPI_FCPORTPAGE1_FLAGS_FORCE_USE_NOSEEPROM_WWNS +#define MPI_FCPORTPAGE1_FLAGS_FORCE_USE_NOSEEPROM_WWNS (0x02000000) +#endif + +#ifndef PRIORITY_REASON_TARGET_BUSY +#define PRIORITY_REASON_TARGET_BUSY (0x09) +#endif + +#if MPT_STM_64_BIT_DMA +#define MPT_STM_SIMPLE SGESimple64_t +#define MPT_STM_CHAIN SGEChain64_t +#define MPI_SGE_FLAGS_MPT_STM_ADDRESSING MPI_SGE_FLAGS_64_BIT_ADDRESSING +#define stm_get_dma_addr(x, y) \ + x = le32_to_cpu(y.Low); \ + if (sizeof(dma_addr_t) == sizeof(u64)) \ + x |= (u64)le32_to_cpu(y.High)<<32; +#define stm_set_dma_addr(x, y) \ + x.Low = cpu_to_le32(y); \ + if (sizeof(dma_addr_t) == sizeof(u64)) \ + x.High = cpu_to_le32((u64)y>>32); \ + else \ + x.High = 0; +#else +#define MPT_STM_SIMPLE SGESimple32_t +#define MPT_STM_CHAIN SGEChain32_t +#define MPI_SGE_FLAGS_MPT_STM_ADDRESSING MPI_SGE_FLAGS_32_BIT_ADDRESSING +#define stm_get_dma_addr(x, y) \ + x = le32_to_cpu(y); +#define stm_set_dma_addr(x, y) \ + x = cpu_to_le32(y); +#endif + +#ifndef MPT_MAX_ADAPTERS +#define MPT_MAX_ADAPTERS 18 +#endif + +#ifndef MPI_MANUFACTPAGE_DEVICEID_FC949X +#define MPI_MANUFACTPAGE_DEVICEID_FC949X 0x640 +#endif +#ifndef MPI_MANUFACTPAGE_DEVICEID_FC939X +#define MPI_MANUFACTPAGE_DEVICEID_FC939X 0x642 +#endif + +#ifndef MPI_IOCSTATUS_EEDP_GUARD_ERROR +#define MPI_IOCSTATUS_EEDP_GUARD_ERROR 0x4d +#endif +#ifndef MPI_IOCSTATUS_EEDP_REF_TAG_ERROR +#define MPI_IOCSTATUS_EEDP_REF_TAG_ERROR 0x4e +#endif +#ifndef MPI_IOCSTATUS_EEDP_APP_TAG_ERROR +#define MPI_IOCSTATUS_EEDP_APP_TAG_ERROR 0x4f +#endif + +#define MPT_MAX_CDB_LEN 16 +#define MPT_TIMEOUT 30 + +/* Immediate notify status constants */ +#define IMM_NTFY_LIP_RESET MPI_EVENT_LOOP_STATE_CHANGE +#define IMM_NTFY_IOCB_OVERFLOW 0x0016 +#define IMM_NTFY_ABORT_TASK 0x0020 +#define IMM_NTFY_PORT_LOGOUT MPI_EVENT_LOGOUT +#define IMM_NTFY_PORT_CONFIG MPI_EVENT_LINK_STATUS_CHANGE +#define IMM_NTFY_GLBL_TPRLO MPI_EVENT_LINK_STATUS_CHANGE +#define IMM_NTFY_GLBL_LOGO MPI_EVENT_LINK_STATUS_CHANGE +#define IMM_NTFY_RESOURCE 0x0034 +#define IMM_NTFY_MSG_RX 0x0036 + +/* Immediate notify task flags */ +#define IMM_NTFY_ABORT_TS 0x01 +#define IMM_NTFY_CLEAR_TS 0x04 +#define IMM_NTFY_LUN_RESET1 0x08 +#define IMM_NTFY_LUN_RESET2 0x10 +#define IMM_NTFY_TARGET_RESET 0x20 +#define IMM_NTFY_CLEAR_ACA 0x40 + +/* Command's states */ +#define MPT_STATE_NEW 1 /* New command and SCST processes it */ +#define MPT_STATE_NEED_DATA 2 /* SCST needs data to process */ +#define MPT_STATE_DATA_IN 3 /* Data arrived and SCST processes it */ +#define MPT_STATE_DATA_OUT 4 +#define MPT_STATE_PROCESSED 5 /* SCST done processing */ + +/* Target's flags */ +#define MPT_TGT_SHUTDOWN 0 /* The driver is being released */ +#define MPT_TGT_ENABLE_64BIT_ADDR 1 /* 64-bits PCI addressing anables */ + +/* Session's flags */ +#define MPT_SESS_INITING 0 /* The session is being unregistered */ +#define MPT_SESS_SHUTDOWN 1 /* The session is being unregistered */ + +struct mpt_cmd +{ + struct mpt_sess *sess; + struct scst_cmd *scst_cmd; + MPT_STM_PRIV *priv; + CMD *CMD; + u32 reply_word; + struct list_head delayed_cmds_entry; + int state; + dma_addr_t dma_handle; +}; + +struct mpt_sess +{ + struct scst_session *scst_sess; + struct mpt_tgt *tgt; + int init_index; + unsigned long sess_flags; + struct list_head delayed_cmds; +}; + +struct mpt_tgt +{ + struct scst_tgt *scst_tgt; + MPT_STM_PRIV *priv; + int datasegs_per_cmd, datasegs_per_cont; + unsigned long tgt_flags; + atomic_t sess_count; + wait_queue_head_t waitQ; + struct mpt_sess *sess[256]; + int target_enable; + int target_id; +}; + +struct mpt_mgmt_cmd +{ + struct mpt_sess *sess; + int task_mgmt; +}; + +#endif diff --git a/mpt/scsi3.h b/mpt/scsi3.h new file mode 100644 index 000000000..0d9330a52 --- /dev/null +++ b/mpt/scsi3.h @@ -0,0 +1,718 @@ +/* + * linux/drivers/message/fusion/scsi3.h + * SCSI-3 definitions and macros. + * (Ultimately) SCSI-3 definitions; for now, inheriting + * SCSI-2 definitions. + * + * Copyright (c) 1996-2004 Steven J. Ralston + * Written By: Steven J. Ralston (19960517) + * (mailto:sjralston1@netscape.net) + * (mailto:mpt_linux_developer@lsil.com) + * + * $Id: scst-0.9.5-lsi-2.6.15.patch,v 1.1 2006/12/02 23:40:42 ehabbinga Exp $ + */ + +#ifndef SCSI3_H_INCLUDED +#define SCSI3_H_INCLUDED +/***************************************************************************/ + +/**************************************************************************** + * + * Includes + */ +#ifdef __KERNEL__ +#include +#else + #ifndef U_STUFF_DEFINED + #define U_STUFF_DEFINED + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif +#endif + +/**************************************************************************** + * + * Defines + */ + +/* + * SCSI Commands + */ +#define CMD_TestUnitReady 0x00 +#define CMD_RezeroUnit 0x01 /* direct-access devices */ +#define CMD_Rewind 0x01 /* sequential-access devices */ +#define CMD_RequestSense 0x03 +#define CMD_FormatUnit 0x04 +#define CMD_ReassignBlock 0x07 +#define CMD_Read6 0x08 +#define CMD_Write6 0x0A +#define CMD_WriteFilemark 0x10 +#define CMD_Space 0x11 +#define CMD_Inquiry 0x12 +#define CMD_ModeSelect6 0x15 +#define CMD_ModeSense6 0x1A +#define CMD_Reserve6 0x16 +#define CMD_Release6 0x17 +#define CMD_Erase 0x19 +#define CMD_StartStopUnit 0x1B /* direct-access devices */ +#define CMD_LoadUnload 0x1B /* sequential-access devices */ +#define CMD_ReceiveDiagnostic 0x1C +#define CMD_SendDiagnostic 0x1D +#define CMD_ReadCapacity 0x25 +#define CMD_Read10 0x28 +#define CMD_Write10 0x2A +#define CMD_WriteVerify 0x2E +#define CMD_Verify 0x2F +#define CMD_SynchronizeCache 0x35 +#define CMD_ReadDefectData 0x37 +#define CMD_WriteBuffer 0x3B +#define CMD_ReadBuffer 0x3C +#define CMD_ReadLong 0x3E +#define CMD_WriteLong 0x3F +#define CMD_WriteSame 0x41 +#define CMD_LogSelect 0x4C +#define CMD_LogSense 0x4D +#define CMD_ModeSelect10 0x55 +#define CMD_Reserve10 0x56 +#define CMD_Release10 0x57 +#define CMD_ModeSense10 0x5A +#define CMD_PersistReserveIn 0x5E +#define CMD_PersistReserveOut 0x5F +#define CMD_Read16 0x88 +#define CMD_Write16 0x8A +#define CMD_WriteVerify16 0x8E +#define CMD_WriteSame16 0x93 +#define CMD_ServiceActionIn 0x9E +#define CMD_ServiceActionOut 0x9F +#define CMD_ReportLuns 0xA0 +#define CMD_Read12 0xA8 +#define CMD_Write12 0xAA +#define CMD_WriteVerify12 0xAE + +/* + * Control byte field + */ +#define CONTROL_BYTE_NACA_BIT 0x04 +#define CONTROL_BYTE_Flag_BIT 0x02 +#define CONTROL_BYTE_Link_BIT 0x01 + +/* + * SCSI Messages + */ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_IDERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINKED_CMD_COMPLETE 0x0a +#define MSG_LCMD_COMPLETE_W_FLG 0x0b +#define MSG_BUS_DEVICE_RESET 0x0c +#define MSG_ABORT_TAG 0x0d +#define MSG_CLEAR_QUEUE 0x0e +#define MSG_INITIATE_RECOVERY 0x0f + +#define MSG_RELEASE_RECOVRY 0x10 +#define MSG_TERMINATE_IO 0x11 + +#define MSG_SIMPLE_QUEUE 0x20 +#define MSG_HEAD_OF_QUEUE 0x21 +#define MSG_ORDERED_QUEUE 0x22 +#define MSG_IGNORE_WIDE_RESIDUE 0x23 + +#define MSG_IDENTIFY 0x80 +#define MSG_IDENTIFY_W_DISC 0xc0 + +/* + * SCSI Phases + */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MSG_OUT 0x06 +#define PHS_MSG_IN 0x07 + +/* + * Statuses + */ +#define STS_GOOD 0x00 +#define STS_CHECK_CONDITION 0x02 +#define STS_CONDITION_MET 0x04 +#define STS_BUSY 0x08 +#define STS_INTERMEDIATE 0x10 +#define STS_INTERMEDIATE_CONDITION_MET 0x14 +#define STS_RESERVATION_CONFLICT 0x18 +#define STS_COMMAND_TERMINATED 0x22 +#define STS_TASK_SET_FULL 0x28 +#define STS_QUEUE_FULL 0x28 +#define STS_ACA_ACTIVE 0x30 + +#define STS_VALID_MASK 0x3e + +#define SCSI_STATUS(x) ((x) & STS_VALID_MASK) + +/* + * SCSI QTag Types + */ +#define QTAG_SIMPLE 0x20 +#define QTAG_HEAD_OF_Q 0x21 +#define QTAG_ORDERED 0x22 + +/* + * SCSI Sense Key Definitons + */ +#define SK_NO_SENSE 0x00 +#define SK_RECOVERED_ERROR 0x01 +#define SK_NOT_READY 0x02 +#define SK_MEDIUM_ERROR 0x03 +#define SK_HARDWARE_ERROR 0x04 +#define SK_ILLEGAL_REQUEST 0x05 +#define SK_UNIT_ATTENTION 0x06 +#define SK_DATA_PROTECT 0x07 +#define SK_BLANK_CHECK 0x08 +#define SK_VENDOR_SPECIFIC 0x09 +#define SK_COPY_ABORTED 0x0a +#define SK_ABORTED_COMMAND 0x0b +#define SK_EQUAL 0x0c +#define SK_VOLUME_OVERFLOW 0x0d +#define SK_MISCOMPARE 0x0e +#define SK_RESERVED 0x0f + + + +#define SCSI_MAX_INQUIRY_BYTES 96 +#define SCSI_STD_INQUIRY_BYTES 36 + +#undef USE_SCSI_COMPLETE_INQDATA +/* + * Structure definition for SCSI Inquiry Data + * + * NOTE: The following structure is 96 bytes in size + * iff USE_SCSI_COMPLETE_INQDATA IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_INQDATA is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 36 bytes in size. + * THE CHOICE IS YOURS! + */ +typedef struct SCSI_Inquiry_Data +{ +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 InqByte[SCSI_MAX_INQUIRY_BYTES]; +#else + u8 InqByte[SCSI_STD_INQUIRY_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u32 Periph_Device_Type : 5, + Periph_Qualifier : 3, + Device_Type_Modifier : 7, + Removable_Media : 1, + ANSI_Version : 3, + ECMA_Version : 3, + ISO_Version : 2, + Response_Data_Format : 4, + reserved_0 : 3, + AERC : 1 ; + u32 Additional_Length : 8, + reserved_1 :16, + SftReset : 1, + CmdQue : 1, + reserved_2 : 1, + Linked : 1, + Sync : 1, + WBus16 : 1, + WBus32 : 1, + RelAdr : 1 ; + u8 Vendor_ID[8]; + u8 Product_ID[16]; + u8 Revision_Level [4]; +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 Vendor_Specific[20]; + u8 reserved_3[40]; +#endif + * + */ + +} SCSI_Inquiry_Data_t; + +#define INQ_PERIPHINFO_BYTE 0 +#define INQ_Periph_Qualifier_MASK 0xe0 +#define INQ_Periph_Device_Type_MASK 0x1f + +#define INQ_Peripheral_Qualifier(inqp) \ + (int)((*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Qualifier_MASK) >> 5) +#define INQ_Peripheral_Device_Type(inqp) \ + (int)(*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Device_Type_MASK) + + +#define INQ_DEVTYPEMOD_BYTE 1 +#define INQ_RMB_BIT 0x80 +#define INQ_Device_Type_Modifier_MASK 0x7f + +#define INQ_Removable_Medium(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_RMB_BIT) +#define INQ_Device_Type_Modifier(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_Device_Type_Modifier_MASK) + + +#define INQ_VERSIONINFO_BYTE 2 +#define INQ_ISO_Version_MASK 0xc0 +#define INQ_ECMA_Version_MASK 0x38 +#define INQ_ANSI_Version_MASK 0x07 + +#define INQ_ISO_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ISO_Version_MASK) +#define INQ_ECMA_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ECMA_Version_MASK) +#define INQ_ANSI_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ANSI_Version_MASK) + + +#define INQ_BYTE3 3 +#define INQ_AERC_BIT 0x80 +#define INQ_TrmTsk_BIT 0x40 +#define INQ_NormACA_BIT 0x20 +#define INQ_RDF_MASK 0x0F + +#define INQ_AER_Capable(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_AERC_BIT) +#define INQ_TrmTsk(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_TrmTsk_BIT) +#define INQ_NormACA(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_NormACA_BIT) +#define INQ_Response_Data_Format(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_RDF_MASK) + + +#define INQ_CAPABILITY_BYTE 7 +#define INQ_RelAdr_BIT 0x80 +#define INQ_WBus32_BIT 0x40 +#define INQ_WBus16_BIT 0x20 +#define INQ_Sync_BIT 0x10 +#define INQ_Linked_BIT 0x08 + /* INQ_Reserved BIT 0x40 */ +#define INQ_CmdQue_BIT 0x02 +#define INQ_SftRe_BIT 0x01 + +#define IS_RelAdr_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_RelAdr_BIT) +#define IS_WBus32_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus32_BIT) +#define IS_WBus16_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus16_BIT) +#define IS_Sync_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Sync_BIT) +#define IS_Linked_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Linked_BIT) +#define IS_CmdQue_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_CmdQue_BIT) +#define IS_SftRe_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_SftRe_BIT) + +#define INQ_Width_BITS \ + (INQ_WBus32_BIT | INQ_WBus16_BIT) +#define IS_Wide_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Width_BITS) + + +/* + * SCSI peripheral device types + */ +#define SCSI_TYPE_DAD 0x00 /* Direct Access Device */ +#define SCSI_TYPE_SAD 0x01 /* Sequential Access Device */ +#define SCSI_TYPE_TAPE SCSI_TYPE_SAD +#define SCSI_TYPE_PRT 0x02 /* Printer */ +#define SCSI_TYPE_PROC 0x03 /* Processor */ +#define SCSI_TYPE_WORM 0x04 +#define SCSI_TYPE_CDROM 0x05 +#define SCSI_TYPE_SCAN 0x06 /* Scanner */ +#define SCSI_TYPE_OPTICAL 0x07 /* Magneto/Optical */ +#define SCSI_TYPE_CHANGER 0x08 +#define SCSI_TYPE_COMM 0x09 /* Communications device */ +#define SCSI_TYPE_UNKNOWN 0x1f +#define SCSI_TYPE_UNCONFIGURED_LUN 0x7f + +#define SCSI_TYPE_MAX_KNOWN SCSI_TYPE_COMM + +/* + * Peripheral Qualifiers + */ +#define DEVICE_PRESENT 0x00 +#define LUN_NOT_PRESENT 0x01 +#define LUN_NOT_SUPPORTED 0x03 + +/* + * ANSI Versions + */ +#ifndef SCSI_1 +#define SCSI_1 0x01 +#endif +#ifndef SCSI_2 +#define SCSI_2 0x02 +#endif +#ifndef SCSI_3 +#define SCSI_3 0x03 +#endif + + +#define SCSI_MAX_SENSE_BYTES 255 +#define SCSI_STD_SENSE_BYTES 18 +#define SCSI_PAD_SENSE_BYTES (SCSI_MAX_SENSE_BYTES - SCSI_STD_SENSE_BYTES) + +#undef USE_SCSI_COMPLETE_SENSE +/* + * Structure definition for SCSI Sense Data + * + * NOTE: The following structure is 255 bytes in size + * iiff USE_SCSI_COMPLETE_SENSE IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_SENSE is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 19 bytes in size. + * THE CHOICE IS YOURS! + * + */ +typedef struct SCSI_Sense_Data +{ +#ifdef USE_SCSI_COMPLETE_SENSE + u8 SenseByte[SCSI_MAX_SENSE_BYTES]; +#else + u8 SenseByte[SCSI_STD_SENSE_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u8 Error_Code :4, // 0x00 + Error_Class :3, + Valid :1 + ; + u8 Segment_Number // 0x01 + ; + u8 Sense_Key :4, // 0x02 + Reserved :1, + Incorrect_Length_Indicator:1, + End_Of_Media :1, + Filemark :1 + ; + u8 Information_MSB; // 0x03 + u8 Information_Byte2; // 0x04 + u8 Information_Byte1; // 0x05 + u8 Information_LSB; // 0x06 + u8 Additional_Length; // 0x07 + + u32 Command_Specific_Information; // 0x08 - 0x0b + + u8 Additional_Sense_Code; // 0x0c + u8 Additional_Sense_Code_Qualifier; // 0x0d + u8 Field_Replaceable_Unit_Code; // 0x0e + u8 Illegal_Req_Bit_Pointer :3, // 0x0f + Illegal_Req_Bit_Valid :1, + Illegal_Req_Reserved :2, + Illegal_Req_Cmd_Data :1, + Sense_Key_Specific_Valid :1 + ; + u16 Sense_Key_Specific_Data; // 0x10 - 0x11 + +#ifdef USE_SCSI_COMPLETE_SENSE + u8 Additional_Sense_Data[SCSI_PAD_SENSE_BYTES]; +#else + u8 Additional_Sense_Data[1]; +#endif + * + */ + +} SCSI_Sense_Data_t; + + +#define SD_ERRCODE_BYTE 0 +#define SD_Valid_BIT 0x80 +#define SD_Error_Code_MASK 0x7f +#define SD_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Valid_BIT) +#define SD_Error_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Error_Code_MASK) + + +#define SD_SEGNUM_BYTE 1 +#define SD_Segment_Number(sdp) (int)(*((u8*)(sdp)+SD_SEGNUM_BYTE)) + + +#define SD_SENSEKEY_BYTE 2 +#define SD_Filemark_BIT 0x80 +#define SD_EOM_BIT 0x40 +#define SD_ILI_BIT 0x20 +#define SD_Sense_Key_MASK 0x0f +#define SD_Filemark(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Filemark_BIT) +#define SD_EOM(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_EOM_BIT) +#define SD_ILI(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_ILI_BIT) +#define SD_Sense_Key(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Sense_Key_MASK) + + +#define SD_INFO3_BYTE 3 +#define SD_INFO2_BYTE 4 +#define SD_INFO1_BYTE 5 +#define SD_INFO0_BYTE 6 +#define SD_Information3(sdp) (int)(*((u8*)(sdp)+SD_INFO3_BYTE)) +#define SD_Information2(sdp) (int)(*((u8*)(sdp)+SD_INFO2_BYTE)) +#define SD_Information1(sdp) (int)(*((u8*)(sdp)+SD_INFO1_BYTE)) +#define SD_Information0(sdp) (int)(*((u8*)(sdp)+SD_INFO0_BYTE)) + + +#define SD_ADDL_LEN_BYTE 7 +#define SD_Additional_Sense_Length(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_LEN_BYTE)) +#define SD_Addl_Sense_Len SD_Additional_Sense_Length + + +#define SD_CMD_SPECIFIC3_BYTE 8 +#define SD_CMD_SPECIFIC2_BYTE 9 +#define SD_CMD_SPECIFIC1_BYTE 10 +#define SD_CMD_SPECIFIC0_BYTE 11 +#define SD_Cmd_Specific_Info3(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC3_BYTE)) +#define SD_Cmd_Specific_Info2(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC2_BYTE)) +#define SD_Cmd_Specific_Info1(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC1_BYTE)) +#define SD_Cmd_Specific_Info0(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC0_BYTE)) + + +#define SD_ADDL_SENSE_CODE_BYTE 12 +#define SD_Additional_Sense_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_BYTE)) +#define SD_Addl_Sense_Code SD_Additional_Sense_Code +#define SD_ASC SD_Additional_Sense_Code + + +#define SD_ADDL_SENSE_CODE_QUAL_BYTE 13 +#define SD_Additional_Sense_Code_Qualifier(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_QUAL_BYTE)) +#define SD_Addl_Sense_Code_Qual SD_Additional_Sense_Code_Qualifier +#define SD_ASCQ SD_Additional_Sense_Code_Qualifier + + +#define SD_FIELD_REPL_UNIT_CODE_BYTE 14 +#define SD_Field_Replaceable_Unit_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_FIELD_REPL_UNIT_CODE_BYTE)) +#define SD_Field_Repl_Unit_Code SD_Field_Replaceable_Unit_Code +#define SD_FRUC SD_Field_Replaceable_Unit_Code +#define SD_FRU SD_Field_Replaceable_Unit_Code + + +/* + * Sense-Key Specific offsets and macros. + */ +#define SD_SKS2_BYTE 15 +#define SD_SKS_Valid_BIT 0x80 +#define SD_SKS_Cmd_Data_BIT 0x40 +#define SD_SKS_Bit_Ptr_Valid_BIT 0x08 +#define SD_SKS_Bit_Ptr_MASK 0x07 +#define SD_SKS1_BYTE 16 +#define SD_SKS0_BYTE 17 +#define SD_Sense_Key_Specific_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Valid_BIT) +#define SD_SKS_Valid SD_Sense_Key_Specific_Valid +#define SD_SKS_CDB_Error(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Cmd_Data_BIT) +#define SD_Was_Illegal_Request SD_SKS_CDB_Error +#define SD_SKS_Bit_Pointer_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_Valid_BIT) +#define SD_SKS_Bit_Pointer(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_MASK) +#define SD_Field_Pointer(sdp) \ + (int)( ((u16)(*((u8*)(sdp)+SD_SKS1_BYTE)) << 8) \ + + *((u8*)(sdp)+SD_SKS0_BYTE) ) +#define SD_Bad_Byte SD_Field_Pointer +#define SD_Actual_Retry_Count SD_Field_Pointer +#define SD_Progress_Indication SD_Field_Pointer + +/* + * Mode Sense Write Protect Mask + */ +#define WRITE_PROTECT_MASK 0X80 + +/* + * Medium Type Codes + */ +#define OPTICAL_DEFAULT 0x00 +#define OPTICAL_READ_ONLY_MEDIUM 0x01 +#define OPTICAL_WRITE_ONCE_MEDIUM 0x02 +#define OPTICAL_READ_WRITABLE_MEDIUM 0x03 +#define OPTICAL_RO_OR_WO_MEDIUM 0x04 +#define OPTICAL_RO_OR_RW_MEDIUM 0x05 +#define OPTICAL_WO_OR_RW_MEDIUM 0x06 + + + +/* + * Structure definition for READ6, WRITE6 (6-byte CDB) + */ +typedef struct SCSI_RW6_CDB +{ + u32 OpCode :8, + LBA_HI :5, /* 5 MSBit's of the LBA */ + Lun :3, + LBA_MID :8, /* NOTE: total of 21 bits in LBA */ + LBA_LO :8 ; /* Max LBA = 0x001fffff */ + u8 BlockCount; + u8 Control; +} SCSI_RW6_t; + +#define MAX_RW6_LBA ((u32)0x001fffff) + +/* + * Structure definition for READ10, WRITE10 (10-byte CDB) + * + * NOTE: ParityCheck bit is applicable only for VERIFY and WRITE VERIFY for + * the ADP-92 DAC only. In the SCSI2 spec. this same bit is defined as a + * FUA (forced unit access) bit for READs and WRITEs. Since this driver + * does not use the FUA, this bit is defined as it is used by the ADP-92. + * Also, for READ CAPACITY, only the OpCode field is used. + */ +typedef struct SCSI_RW10_CDB +{ + u8 OpCode; + u8 Reserved1; + u32 LBA; + u8 Reserved2; + u16 BlockCount; + u8 Control; +} SCSI_RW10_t; + +#define PARITY_CHECK 0x08 /* parity check bit - byte[1], bit 3 */ + + /* + * Structure definition for data returned by READ CAPACITY cmd; + * READ CAPACITY data + */ + typedef struct READ_CAP_DATA + { + u32 MaxLBA; + u32 BlockBytes; + } SCSI_READ_CAP_DATA_t, *pSCSI_READ_CAP_DATA_t; + + +/* + * Structure definition for FORMAT UNIT CDB (6-byte CDB) + */ +typedef struct _SCSI_FORMAT_UNIT +{ + u8 OpCode; + u8 Reserved1; + u8 VendorSpecific; + u16 Interleave; + u8 Control; +} SCSI_FORMAT_UNIT_t; + +/* + * Structure definition for REQUEST SENSE (6-byte CDB) + */ +typedef struct _SCSI_REQUEST_SENSE +{ + u8 OpCode; + u8 Reserved1; + u8 Reserved2; + u8 Reserved3; + u8 AllocLength; + u8 Control; +} SCSI_REQ_SENSE_t; + +/* + * Structure definition for REPORT LUNS (12-byte CDB) + */ +typedef struct _SCSI_REPORT_LUNS +{ + u8 OpCode; + u8 Reserved1[5]; + u32 AllocationLength; + u8 Reserved2; + u8 Control; +} SCSI_REPORT_LUNS_t, *pSCSI_REPORT_LUNS_t; + + /* + * (per-level) LUN information bytes + */ +/* + * Following doesn't work on ARMCC compiler + * [apparently] because it pads every struct + * to be multiple of 4 bytes! + * So SCSI_LUN_LEVELS_t winds up being 16 + * bytes instead of 8! + * + typedef struct LUN_INFO + { + u8 AddrMethod_plus_LunOrBusNumber; + u8 LunOrTarget; + } SCSI_LUN_INFO_t, *pSCSI_LUN_INFO_t; + + typedef struct LUN_LEVELS + { + SCSI_LUN_INFO_t LUN_0; + SCSI_LUN_INFO_t LUN_1; + SCSI_LUN_INFO_t LUN_2; + SCSI_LUN_INFO_t LUN_3; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; +*/ + /* + * All 4 levels (8 bytes) of LUN information + */ + typedef struct LUN_LEVELS + { + u8 LVL1_AddrMethod_plus_LunOrBusNumber; + u8 LVL1_LunOrTarget; + u8 LVL2_AddrMethod_plus_LunOrBusNumber; + u8 LVL2_LunOrTarget; + u8 LVL3_AddrMethod_plus_LunOrBusNumber; + u8 LVL3_LunOrTarget; + u8 LVL4_AddrMethod_plus_LunOrBusNumber; + u8 LVL4_LunOrTarget; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; + + /* + * Structure definition for data returned by REPORT LUNS cmd; + * LUN reporting parameter list format + */ + typedef struct LUN_REPORT + { + u32 LunListLength; + u32 Reserved; + SCSI_LUN_LEVELS_t LunInfo[1]; + } SCSI_LUN_REPORT_t, *pSCSI_LUN_REPORT_t; + +/**************************************************************************** + * + * Externals + */ + +/**************************************************************************** + * + * Public Typedefs & Related Defines + */ + +/**************************************************************************** + * + * Macros (embedded, above) + */ + +/**************************************************************************** + * + * Public Variables + */ + +/**************************************************************************** + * + * Public Prototypes (module entry points) + */ + + +/***************************************************************************/ +#endif diff --git a/scst/kernel/in-tree/Makefile.scsi.Linux-2.6.15.patch b/scst/kernel/in-tree/Makefile.scsi.Linux-2.6.15.patch new file mode 100644 index 000000000..7a740f10f --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scsi.Linux-2.6.15.patch @@ -0,0 +1,10 @@ +--- Makefile.orig 2006-12-11 14:29:39.000000000 -0700 ++++ Makefile 2006-12-11 14:29:52.000000000 -0700 +@@ -81,6 +81,7 @@ + obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o + obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o + obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/ ++obj-$(CONFIG_SCSI_TARGET) += scsi_tgt/ + obj-$(CONFIG_SCSI_LPFC) += lpfc/ + obj-$(CONFIG_SCSI_PAS16) += pas16.o + obj-$(CONFIG_SCSI_SEAGATE) += seagate.o diff --git a/scst/src/Makefile b/scst/src/Makefile index 2c30e1446..1f0805eff 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -49,6 +49,10 @@ all: scst: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=n +MODS_VERS := $(shell ls Modules.symvers 2>/dev/null) +# It's renamed in 2.6.18 +MOD_VERS := $(shell ls Module.symvers 2>/dev/null) + install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=m \ modules_install @@ -56,6 +60,14 @@ install: all install -m 644 ../include/scsi_tgt.h $(INSTALL_DIR_H) install -m 644 ../include/scst_debug.h $(INSTALL_DIR_H) install -m 644 ../include/scst_debug.c $(INSTALL_DIR_H) +ifneq ($(MODS_VERS),) + rm -f $(INSTALL_DIR_H)/Module.symvers + install -m 644 Modules.symvers $(INSTALL_DIR_H) +endif +ifneq ($(MOD_VERS),) + rm -f $(INSTALL_DIR_H)/Modules.symvers + install -m 644 Module.symvers $(INSTALL_DIR_H) +endif -depmod -a uninstall: