Files
scst/mpt/mpt_scst.c
Bart Van Assche e2fb6f4080 mpt: Use PDE_DATA if available
Signed-off-by: Sebastian Herbszt <herbszt@gmx.de>


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5217 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2014-01-19 19:26:29 +00:00

5437 lines
153 KiB
C

/*
* mpt_scst.c
*
* Copyright (C) 2005 Beijing Soul Technology Co., Ltd.
* Copyright (C) 2002, 2003, 2004 LSI Logic Corporation
* Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2004 Leonid Stoljar
*
* MPT SCSI target mode driver for SCST.
*
* Originally By: Stephen Shirron
* Port to SCST By: Hu Gang <hugang@soulinfo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <scsi/scsi.h>
#include <linux/seq_file.h>
#include <scsi/scsi_host.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)
#include <linux/pci.h>
#endif
#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#include <scst/scst_debug.h>
#else
#include "scst.h"
#include <scst_debug.h>
#endif
#include "mpt_scst.h"
#define MYNAM "mpt_scst"
#ifdef CONFIG_SCST_TRACING
static int trace_mpi;
#define TRACE_MPI 0x80000000
#endif
#ifdef CONFIG_SCST_DEBUG
static char *mpt_state_string[] = {
"0",
"new",
"need data",
"data in",
"data out",
"processed",
"NULL",
};
#endif
#ifdef CONFIG_SCST_DEBUG
#define SCST_DEFAULT_MPT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_MGMT_DEBUG | TRACE_MINOR | TRACE_SPECIAL)
#else
# ifdef CONFIG_SCST_TRACING
#define SCST_DEFAULT_MPT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR | TRACE_SPECIAL)
# endif
#endif
static MPT_STM_PRIV *mpt_stm_priv[MPT_MAX_ADAPTERS+1];
static int set_aliases_in_fcportpage1 = 1;
static int num_aliases;
static int stm_context;
static int mpt_stm_adapter_online(MPT_STM_PRIV *priv);
static void mpt_stm_adapter_dispose(MPT_STM_PRIV *priv);
static int mpt_stm_adapter_install(MPT_ADAPTER *ioc);
static int __init _mpt_stm_init(void);
static void stmapp_set_status(MPT_STM_PRIV *priv, CMD *cmd, int status);
static void stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word);
static void stm_cmd_buf_post(MPT_STM_PRIV *priv, int index);
static void stm_tgt_reply_high_pri(MPT_ADAPTER *ioc,
TargetCmdBufferPostErrorReply_t *rep);
static void stm_target_reply_error(MPT_ADAPTER *ioc, TargetErrorReply_t *rep);
static void stmapp_target_error(MPT_STM_PRIV *priv, u32 reply_word, int index,
int status, int reason);
static void stm_link_service_reply(MPT_ADAPTER *ioc,
LinkServiceBufferPostReply_t *rep);
static void stm_link_service_rsp_reply(MPT_ADAPTER *ioc,
LinkServiceRspRequest_t *req,
LinkServiceRspReply_t *rep);
static void stmapp_set_sense_info(MPT_STM_PRIV *priv,
CMD *cmd, int sense_key, int asc, int ascq);
static void stmapp_srr_adjust_offset(MPT_STM_PRIV *priv, int index);
static void stmapp_srr_convert_ta_to_tss(MPT_STM_PRIV *priv, int index);
static void stmapp_abts_process(MPT_STM_PRIV *priv, int rx_id,
LinkServiceBufferPostReply_t *rep, int index);
static int stm_do_config_action(MPT_STM_PRIV *priv, int action, int type,
int number, int address, int length, int sleep);
static int stm_get_config_page(MPT_STM_PRIV *priv, int type, int number,
int address, int sleep);
static int stm_set_config_page(MPT_STM_PRIV *priv, int type, int number,
int address, int sleep);
static void stm_cmd_buf_post_list(MPT_STM_PRIV *priv, int index);
static int stm_send_target_status(MPT_STM_PRIV *priv, u32 reply_word,
int index, int flags, int lun, int tag);
static void stm_send_els(MPT_STM_PRIV *priv, LinkServiceBufferPostReply_t *rep,
int index, int length);
static void stm_link_serv_buf_post(MPT_STM_PRIV *priv, int index);
static void stm_wait(MPT_STM_PRIV *priv, int milliseconds, int sleep);
static int stm_wait_for(MPT_STM_PRIV *priv, volatile int *flag, int seconds,
int sleep);
static void stmapp_srr_process(MPT_STM_PRIV *priv, int rx_id, int r_ctl,
u32 offset, LinkServiceBufferPostReply_t *rep,
int index);
static void stm_set_scsi_port_page1(MPT_STM_PRIV *priv, int sleep);
#ifdef CONFIG_SCST_DEBUG
#define trace_flag mpt_trace_flag
static unsigned long mpt_trace_flag = TRACE_FUNCTION | TRACE_OUT_OF_MEM | TRACE_SPECIAL;
#else
# ifdef CONFIG_SCST_TRACING
#define trace_flag mpt_trace_flag
static unsigned long mpt_trace_flag = TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_SPECIAL;
# endif
#endif
#ifdef CONFIG_SCST_PROC
static int mpt_target_show(struct seq_file *seq, void *v)
{
struct mpt_tgt *tgt = (struct mpt_tgt *)seq->private;
MPT_ADAPTER *ioc = tgt->priv->ioc;
MPT_STM_PRIV *priv = tgt->priv;
TRACE_ENTRY();
TRACE_DBG("priv %p, tgt %p", priv, tgt);
sBUG_ON(tgt == NULL);
sBUG_ON(ioc == NULL);
seq_printf(seq, "ProductID :0x%04x (%s)\nTarget Enable :%s\n",
ioc->facts.ProductID, ioc->prod_name,
tgt->target_enable ? "True" : "False");
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15)
if (ioc->bus_type == SCSI) {
#else
if (ioc->bus_type == SPI) {
#endif
int i = 0;
seq_printf(seq, "Target ID :%d\n"
"Capabilities :0x%x\n"
"PhysicalInterface:0x%x\n",
priv->port_id,
priv->SCSIPortPage0.Capabilities,
priv->SCSIPortPage0.PhysicalInterface);
seq_printf(seq, "Configuration :0x%x\n"
"OnBusTimerValue :0x%x\n"
"TargetConfig :0x%x\n"
"IDConfig :0x%x\n",
priv->SCSIPortPage1.Configuration,
priv->SCSIPortPage1.OnBusTimerValue,
priv->SCSIPortPage1.TargetConfig,
priv->SCSIPortPage1.IDConfig);
seq_printf(seq, "PortFlags :0x%x\n"
"PortSettings :0x%x\n",
priv->SCSIPortPage2.PortFlags,
priv->SCSIPortPage2.PortSettings);
#if 0
for (i = 0; i < 16; i++) {
seq_printf(seq, " DeviceSeting %02d: 0x%x 0x%x 0x%x\n",
priv->SCSIPortPage2.DeviceSettings[i].Timeout,
priv->SCSIPortPage2.DeviceSettings[i].SyncFactor,
priv->SCSIPortPage2.DeviceSettings[i].DeviceFlags);
}
#endif
for (i = 0; i < NUM_SCSI_DEVICES; i++) {
seq_printf(seq, " Device %02d: 0x%x, 0x%x\n", i,
priv->SCSIDevicePage1[i].RequestedParameters,
priv->SCSIDevicePage1[i].Configuration);
}
}
if (ioc->bus_type == FC) {
seq_printf(seq, "WWN :%08X%08X:%08X%08X\n",
ioc->fc_port_page0[0].WWNN.High,
ioc->fc_port_page0[0].WWNN.Low,
ioc->fc_port_page0[0].WWPN.High,
ioc->fc_port_page0[0].WWPN.Low);
}
TRACE_EXIT();
return 0;
}
static ssize_t mpt_proc_target_write(struct file *file, const char __user *buf,
size_t length, loff_t *off)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
struct mpt_tgt *tgt = (struct mpt_tgt *)PDE(file->f_dentry->d_inode)->data;
#else
struct mpt_tgt *tgt = (struct mpt_tgt *)PDE_DATA(file->f_dentry->d_inode);
#endif
MPT_ADAPTER *ioc = tgt->priv->ioc;
int res = 0;
char tmp[32+1];
TRACE_ENTRY();
res = min_t(int, 32, length);
if (copy_from_user(tmp, buf, res)) {
res = -EFAULT;
goto out;
}
tmp[res] = 0;
TRACE_DBG("buff '%s'", tmp);
if (strncmp("target:enable", tmp, strlen("target:enable")) == 0) {
TRACE_DBG("Enable Target, %d, %d", ioc->id, tgt->target_enable);
if (tgt->target_enable != 1) {
mpt_stm_adapter_online(mpt_stm_priv[ioc->id]);
tgt->target_enable = 1;
}
}
if (strncmp("target:disable", tmp, strlen("target:disable")) == 0) {
TRACE_DBG("Disable Target %d, %d", ioc->id, tgt->target_enable);
if (tgt->target_enable != 0) {
/* FIXME */
tgt->target_enable = 0;
}
}
if (strncmp("target_id:", tmp, strlen("target_id:")) == 0) {
char *s = tmp + strlen("target_id:");
int id = simple_strtoul(s, NULL, 0);
if (id < MPT_MAX_SCSI_DEVICES) {
if (IsScsi(tgt->priv)) {
TRACE_DBG("Changing target id to %d\n", id);
tgt->priv->port_id = id;
stm_set_scsi_port_page1(tgt->priv, NO_SLEEP);
}
}
}
out:
TRACE_EXIT_RES(res);
return length;
}
static struct scst_proc_data mpt_target_proc_data = {
SCST_DEF_RW_SEQ_OP(mpt_proc_target_write)
.show = mpt_target_show,
};
#endif
static int mpt_target_detect(struct scst_tgt_template *temp1);
static int mpt_target_release(struct scst_tgt *scst_tgt);
static int stmapp_pending_sense(struct mpt_cmd *mpt_cmd);
static int mpt_xmit_response(struct scst_cmd *scst_cmd);
static void mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv,
struct scst_cmd *scst_cmd);
static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd);
static void mpt_on_free_cmd(struct scst_cmd *scst_cmd);
static void mpt_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd);
static int mpt_handle_task_mgmt(MPT_STM_PRIV *priv, u32 reply_word,
int task_mgmt, int lun);
static int mpt_send_cmd_to_scst(struct mpt_cmd *cmd,
enum scst_exec_context context);
static struct scst_tgt_template tgt_template = {
.name = MYNAM,
.sg_tablesize = 128, /* FIXME */
.use_clustering = 1,
#ifdef DEBUG_WORK_IN_THREAD
.xmit_response_atomic = 0,
.rdy_to_xfer_atomic = 0,
#else
.xmit_response_atomic = 1,
.rdy_to_xfer_atomic = 1,
#endif
.detect = mpt_target_detect,
.release = mpt_target_release,
.xmit_response = mpt_xmit_response,
.rdy_to_xfer = mpt_rdy_to_xfer,
.on_free_cmd = mpt_on_free_cmd,
.task_mgmt_fn_done = mpt_task_mgmt_fn_done,
};
static inline void mpt_msg_frame_free(MPT_STM_PRIV *priv, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
if (priv->current_mf[index] != NULL) {
TRACE_DBG("%s: free mf index %d, %p", ioc->name,
MF_TO_INDEX(priv->current_mf[index]),
priv->current_mf[index]);
mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
priv->current_mf[index] = NULL;
}
}
static inline MPT_FRAME_HDR *mpt_msg_frame_alloc(MPT_ADAPTER *ioc, int index)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
MPT_FRAME_HDR *mf;
if (index != -1) {
TRACE_DBG("%s: current_mf %p, index %d",
ioc->name, priv->current_mf[index], index);
WARN_ON(priv->current_mf[index] != NULL);
}
mf = mpt_get_msg_frame(stm_context, _IOC_ID);
sBUG_ON(mf == NULL);
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;
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;
#ifdef CONFIG_SCST_PROC
struct proc_dir_entry *p;
struct proc_dir_entry *root;
char name[4];
#endif
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 (%zd), %p",
sizeof(*tgt), tgt);
if (tgt == NULL) {
TRACE(TRACE_OUT_OF_MEM, "%s",
"Allocation of tgt failed");
ret = -ENOMEM;
goto out;
}
memset(tgt, 0, sizeof(*tgt));
tgt->priv = mpt_stm_priv[ioc->id];
tgt->target_enable = 0;
tgt->priv->port_id = 1;
/* tgt->priv->scsi_port_config = MPI_SCSIPORTPAGE1_TARGCONFIG_INIT_TARG; */
tgt->priv->scsi_port_config = MPI_SCSIPORTPAGE1_TARGCONFIG_TARG_ONLY;
/* tgt->priv->scsi_id_config = 0x7; */
tgt->priv->scsi_id_config = 0;
atomic_set(&tgt->sess_count, 0);
init_waitqueue_head(&tgt->waitQ);
tgt->scst_tgt = scst_register_target(&tgt_template, MYNAM);
if (tgt->scst_tgt == NULL) {
PRINT_ERROR(MYNAM ": scst_register_target() failed for host %p",
pdev);
ret = -ENODEV;
goto out;
}
#ifdef CONFIG_SCST_PROC
root = scst_proc_get_tgt_root(&tgt_template);
scnprintf(name, sizeof(name), "%d", ioc->id);
mpt_target_proc_data.data = (void *)tgt;
p = scst_create_proc_entry(root, name,
&mpt_target_proc_data);
if (p == NULL) {
PRINT_ERROR("Not enough memory to register "
"target driver %s entry %s in /proc",
tgt_template.name, name);
scst_unregister_target(tgt->scst_tgt);
ret = -ENOMEM;
goto out;
}
#endif
scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt);
mpt_stm_priv[ioc->id]->tgt = tgt;
_mpt_ada_nums++;
out:
TRACE_EXIT_RES(ret);
return ret;
}
static void mptstm_remove(struct pci_dev *pdev)
{
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
MPT_STM_PRIV *priv;
priv = mpt_stm_priv[ioc->id];
if (priv != NULL)
mpt_stm_adapter_dispose(priv);
}
static struct mpt_pci_driver mptstm_driver = {
.probe = mptstm_probe,
.remove = mptstm_remove,
};
/*
* mpt_target_detect
*
* this function is intended to detect the target adapters that are present in
* the system. Each found adapter should be registered by calling
* scst_register_target(). The function should return a value >= 0 to signify
* the number of detected target adapters. A negative value should be
* returned whenever there is an error.
*/
static int mpt_target_detect(struct scst_tgt_template *templ)
{
int ret = 0;
TRACE_ENTRY();
ret = _mpt_stm_init();
if (ret != 0)
goto out;
if (mpt_device_driver_register(&mptstm_driver, MPTSTM_DRIVER)) {
printk(KERN_WARNING MYNAM
": failed to register for device driver callbacks\n");
ret = -ENODEV;
goto out;
}
ret = _mpt_ada_nums;
out:
TRACE_EXIT_RES(ret);
return ret;
}
static struct scst_cmd *_stm_target_command(MPT_STM_PRIV *priv, int reply_word,
struct mpt_cmd *mpt_cmd)
{
u8 *cdb;
int lun, tag, dl, alias, index, init_index, task_mgmt;
char alias_lun[32];
CMD *cmd;
struct scst_cmd *scst_cmd;
struct mpt_sess *sess = mpt_cmd->sess;
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
/*
* Get the CBD, LUN, tag, Task Mgmt flags, and data length from the
* receive packet
*/
TRACE_ENTRY();
index = GET_IO_INDEX(reply_word);
init_index = GET_INITIATOR_INDEX(reply_word);
cmd = &priv->hw->cmd_buf[index];
if (IsScsi(priv)) {
SCSI_CMD *scsi_cmd = (SCSI_CMD *)cmd->cmd;
cdb = scsi_cmd->CDB;
lun = get2bytes(scsi_cmd->LogicalUnitNumber, 0);
tag = scsi_cmd->Tag;
task_mgmt = scsi_cmd->TaskManagementFlags;
dl = 0;
/*TRACE_DBG("AliasID %d, %d", scsi_cmd->AliasID, priv->port_id);*/
if (reply_word & TARGET_MODE_REPLY_ALIAS_MASK) {
alias = (scsi_cmd->AliasID - priv->port_id) & 15;
sprintf(alias_lun, "alias %d lun %d", alias, lun);
} else {
alias = 0;
sprintf(alias_lun, "lun %d", lun);
}
} else if (IsSas(priv)) {
SSP_CMD *ssp_cmd = (SSP_CMD *)cmd->cmd;
cdb = ssp_cmd->CDB;
lun = get2bytes(ssp_cmd->LogicalUnitNumber, 0);
if (ssp_cmd->FrameType == SSP_TASK_FRAME) {
SSP_TASK *ssp_task = (SSP_TASK *)cmd->cmd;
tag = ssp_task->ManagedTaskTag;
task_mgmt = ssp_task->TaskManagementFunction;
} else {
tag = ssp_cmd->InitiatorTag;
task_mgmt = 0;
}
dl = 0;
alias = 0;
sprintf(alias_lun, "lun %d", lun);
} else {
FCP_CMD *fcp_cmd = (FCP_CMD *)cmd->cmd;
cdb = fcp_cmd->FcpCdb;
lun = get2bytes(fcp_cmd->FcpLun, 0);
tag = 0;
task_mgmt = fcp_cmd->FcpCntl[2];
dl = be32_to_cpu(fcp_cmd->FcpDl);
if (reply_word & TARGET_MODE_REPLY_ALIAS_MASK) {
alias = fcp_cmd->AliasIndex;
sprintf(alias_lun, "alias %d lun %d", alias, lun);
} else {
alias = 0;
sprintf(alias_lun, "lun %d", lun);
}
}
cmd->reply_word = reply_word;
cmd->alias = alias;
cmd->lun = lun;
cmd->tag = tag;
TRACE_DBG("%s: cmd %p, re_word %x, alias %x, lun %x, tag %x,"
"%s, init_idx %d, %p, %d",
ioc->name, cmd, reply_word, alias, lun, tag, alias_lun,
init_index, priv->scst_cmd[index], dl);
mpt_cmd->CMD = cmd;
{
uint16_t _lun = lun;
_lun = swab16(le16_to_cpu(_lun));
scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&_lun,
sizeof(_lun), cdb, MPT_MAX_CDB_LEN, SCST_ATOMIC);
}
if (scst_cmd == NULL) {
PRINT_ERROR(MYNAM ": scst_rx_cmd() failed for %p", cmd);
goto out;
}
TRACE_DBG("scst cmd %p, index %d", priv->scst_cmd[index], index);
WARN_ON(priv->scst_cmd[index] != NULL);
priv->scst_cmd[index] = scst_cmd;
scst_cmd_set_tag(scst_cmd, tag);
scst_cmd_set_tgt_priv(scst_cmd, mpt_cmd);
/* FIXME scst_cmd_set_expected */
out:
TRACE_EXIT();
return scst_cmd;
}
static void mpt_send_busy(struct mpt_cmd *cmd)
{
stmapp_set_status(cmd->priv, cmd->CMD, STS_BUSY);
}
static void mpt_alloc_session_done(struct scst_session *scst_sess, void *data,
int result)
{
struct mpt_sess *sess = (struct mpt_sess *) data;
struct mpt_tgt *tgt = sess->tgt;
struct mpt_cmd *cmd = NULL;
int rc = 0;
TRACE_ENTRY();
if (result == 0) {
while (!list_empty(&sess->delayed_cmds)) {
cmd = list_entry(sess->delayed_cmds.next,
typeof(*cmd), delayed_cmds_entry);
list_del(&cmd->delayed_cmds_entry);
if (rc == 0)
rc = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_THREAD);
if (rc != 0) {
PRINT_INFO(MYNAM ": Unable to get the command, sending BUSY state %p",
cmd);
mpt_send_busy(cmd);
kfree(cmd);
}
}
__clear_bit(MPT_SESS_INITING, &sess->sess_flags);
} 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);
}
TRACE_EXIT();
return;
}
static int mpt_send_cmd_to_scst(struct mpt_cmd *cmd,
enum scst_exec_context context)
{
int res = 0;
TRACE_ENTRY();
cmd->scst_cmd = _stm_target_command(cmd->priv, cmd->reply_word, cmd);
if (cmd->scst_cmd == NULL) {
res = -EFAULT;
goto out;
}
#ifdef DEBUG_WORK_IN_THREAD
context = SCST_CONTEXT_THREAD;
#endif
scst_cmd_init_done(cmd->scst_cmd, context);
out:
TRACE_EXIT_RES(res);
return res;
}
static void stm_send_target_status_deferred(MPT_STM_PRIV *priv, u32 reply_word,
int index)
{
int ret = 0;
MPT_ADAPTER *ioc = priv->ioc;
MPT_FRAME_HDR *mf;
TargetStatusSendRequest_t *req;
TRACE_ENTRY();
mf = priv->status_deferred_mf[index];
TRACE_DBG("mf %p, index %d", mf, index);
req = (TargetStatusSendRequest_t *)mf;
priv->io_state[index] |= IO_STATE_STATUS_SENT;
priv->current_mf[index] = mf;
priv->status_deferred_mf[index] = NULL;
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
ret = mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
}
TRACE_EXIT_RES(ret);
}
static void stm_data_done(MPT_ADAPTER *ioc, u32 reply_word,
struct scst_cmd *scst_cmd, struct mpt_cmd *cmd,
int index)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
TRACE_ENTRY();
TRACE_DBG("scst cmd %p, index %d, data done", scst_cmd, index);
if (scst_cmd_get_resp_data_len(scst_cmd) > 0) {
TRACE_DBG("clear the data flags <%p>", scst_cmd);
sBUG_ON(scst_cmd_get_sg_cnt(scst_cmd) == 0);
pci_unmap_sg(priv->ioc->pcidev,
scst_cmd_get_sg(scst_cmd),
scst_cmd_get_sg_cnt(scst_cmd),
scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
}
TRACE_EXIT();
}
static void stm_tgt_reply(MPT_ADAPTER *ioc, u32 reply_word)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
int index, init_index;
enum scst_exec_context context;
struct scst_cmd *scst_cmd;
struct mpt_cmd *cmd;
volatile int *io_state;
TRACE_ENTRY();
#ifdef DEBUG_WORK_IN_THREAD
context = SCST_CONTEXT_THREAD;
#else
context = SCST_CONTEXT_TASKLET;
#endif
index = GET_IO_INDEX(reply_word);
init_index = GET_INITIATOR_INDEX(reply_word);
scst_cmd = priv->scst_cmd[index];
io_state = priv->io_state + index;
TRACE_DBG("index %d, state %x, scst cmd %p, current_mf %p",
index, *io_state, scst_cmd, priv->current_mf[index]);
/*
* if scst_cmd is NULL it show the command buffer not using by
* SCST, let parse the CDB
*/
if (scst_cmd == NULL) {
WARN_ON((*io_state & ~IO_STATE_HIGH_PRIORITY) != IO_STATE_POSTED);
*io_state &= ~IO_STATE_POSTED;
mpt_msg_frame_free(priv, index);
stmapp_tgt_command(priv, reply_word);
goto out;
}
cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
TRACE_DBG("scst cmd %p, index %d, cmd %p, cmd state %s",
scst_cmd, index, cmd, mpt_state_string[cmd->state]);
if (cmd->state == MPT_STATE_NEED_DATA) {
int rx_status = SCST_RX_STATUS_SUCCESS;
cmd->state = MPT_STATE_DATA_IN;
TRACE_DBG("Data received, context %x, rx_status %d",
context, rx_status);
sBUG_ON(!(*io_state & IO_STATE_DATA_SENT));
mpt_msg_frame_free(priv, index);
if (*io_state & IO_STATE_DATA_SENT) {
*io_state &= ~IO_STATE_DATA_SENT;
stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
}
#if 0
if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == IO_STATE_AUTO_REPOST) {
TRACE_DBG("%s", "io state auto repost");
*io_state = IO_STATE_POSTED;
} else if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == 0) {
TRACE_DBG("%s", "io state");
stm_cmd_buf_post(priv, index);
}
#endif
scst_rx_data(scst_cmd, rx_status, context);
goto out;
}
if (*io_state & IO_STATE_STATUS_SENT) {
/*
* status (and maybe data too) was being sent, so repost the
* command buffer
*/
*io_state &= ~IO_STATE_STATUS_SENT;
mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
if (*io_state & IO_STATE_DATA_SENT) {
*io_state &= ~IO_STATE_DATA_SENT;
stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
}
TRACE_DBG("set priv->scst_cmd[%d] = NULL", index);
priv->scst_cmd[index] = NULL;
if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == IO_STATE_AUTO_REPOST) {
TRACE_DBG("%s", "io state auto repost");
*io_state = IO_STATE_POSTED;
} else if ((*io_state & ~IO_STATE_HIGH_PRIORITY) == 0) {
TRACE_DBG("%s", "io state");
stm_cmd_buf_post(priv, index);
}
/*
* figure out how we're handling cached sense data.
*/
if (IsScsi(priv)) {
switch (atomic_read(&priv->pending_sense[init_index])) {
/* attempt to send status and sense succeeded */
case MPT_STATUS_SENSE_ATTEMPT:
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
/* ToDo: check and set scst_set_delivery_status(), if necessary */
scst_tgt_cmd_done(scst_cmd, context);
break;
/* we tried to send status and sense
* simltaneously and failed. Prepare to handle
* the next command without SCST if it is
* REQUEST_SENSE */
case MPT_STATUS_SENSE_NOT_SENT:
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_HANDLE_RQ);
/* ToDo: check and set scst_set_delivery_status(), if necessary */
scst_tgt_cmd_done(scst_cmd, context);
break;
/* we've handled REQUEST_SENSE ourselves and
* we're done with the command. Clean up */
case MPT_STATUS_SENSE_HANDLE_RQ:
TRACE_DBG("%s: clearing pending sense",
ioc->name);
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
mpt_on_free_cmd(scst_cmd);
/* scst_cmd alloced in stmapp_pending_sense */
kfree(scst_cmd);
break;
default:
/* nothing much to do here, we aren't
* handling cached sense/status */
/* ToDo: check and set scst_set_delivery_status(), if necessary */
scst_tgt_cmd_done(scst_cmd, context);
break;
}
} else {
/* ToDo: check and set scst_set_delivery_status(), if necessary */
scst_tgt_cmd_done(scst_cmd, context);
}
goto out;
}
/*
* data (but not status) was being sent, so if status needs to be
* set now, go ahead and do it; otherwise do nothing
*/
if (*io_state & IO_STATE_DATA_SENT) {
*io_state &= ~IO_STATE_DATA_SENT;
mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
stm_data_done(ioc, reply_word, scst_cmd, cmd, index);
if (*io_state & IO_STATE_STATUS_DEFERRED) {
*io_state &= ~IO_STATE_STATUS_DEFERRED;
stm_send_target_status_deferred(priv, reply_word, index);
}
cmd->state = MPT_STATE_PROCESSED;
goto out;
}
/*
* just insert into list
* bug how can i handle it
*/
if (*io_state == 0 && cmd->state == MPT_STATE_NEW) {
WARN_ON(1);
goto out;
}
#if 0
if (*io_state == IO_STATE_POSTED) {
TRACE_DBG("%s", "io state posted");
/*
* command buffer was posted, so we now have a SCSI command
*/
*io_state &= ~IO_STATE_POSTED;
goto out;
}
#endif
WARN_ON(1);
out:
TRACE_EXIT();
}
static int mpt_is_task_mgm(MPT_STM_PRIV *priv, u32 reply_word, int *lun)
{
int task_mgmt = 0, index;
CMD *cmd;
/*struct mpt_tgt *tgt = priv->tgt;*/
TRACE_ENTRY();
index = GET_IO_INDEX(reply_word);
cmd = &priv->hw->cmd_buf[index];
if (IsScsi(priv)) {
SCSI_CMD *scsi_cmd = (SCSI_CMD *)cmd->cmd;
task_mgmt = scsi_cmd->TaskManagementFlags;
*lun = get2bytes(scsi_cmd->LogicalUnitNumber, 0);
} else if (IsSas(priv)) {
SSP_CMD *ssp_cmd = (SSP_CMD *)cmd->cmd;
if (ssp_cmd->FrameType == SSP_TASK_FRAME) {
SSP_TASK *ssp_task = (SSP_TASK *)cmd->cmd;
task_mgmt = ssp_task->TaskManagementFunction;
}
*lun = get2bytes(ssp_cmd->LogicalUnitNumber, 0);
} else {
FCP_CMD *fcp_cmd = (FCP_CMD *)cmd->cmd;
task_mgmt = fcp_cmd->FcpCntl[2];
*lun = get2bytes(fcp_cmd->FcpLun, 0);
}
TRACE_EXIT_RES(task_mgmt);
return task_mgmt;
}
static void stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word)
{
struct mpt_tgt *tgt = NULL;
struct mpt_sess *sess = NULL;
struct mpt_cmd *cmd = NULL;
int init_index, res = 0, task_mgmt, lun;
TRACE_ENTRY();
tgt = priv->tgt;
task_mgmt = mpt_is_task_mgm(priv, reply_word, &lun);
if (task_mgmt)
mpt_handle_task_mgmt(priv, reply_word, task_mgmt, lun);
init_index = GET_INITIATOR_INDEX(reply_word);
if (test_bit(MPT_TGT_SHUTDOWN, &tgt->tgt_flags)) {
TRACE_DBG("New command while the device %p is shutting down", tgt);
res = -EFAULT;
goto out;
}
cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
TRACE_MEM("kmalloc(GFP_ATOMIC) for cmd (%zd): %p", sizeof(*cmd), cmd);
if (cmd == NULL) {
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of cmd failed");
res = -ENOMEM;
goto out;
}
memset(cmd, 0, sizeof(*cmd));
cmd->priv = priv;
cmd->reply_word = reply_word;
cmd->state = MPT_STATE_NEW;
sess = tgt->sess[init_index];
if (sess == NULL) {
sess = kmalloc(sizeof(*sess), GFP_ATOMIC);
if (sess == NULL) {
TRACE(TRACE_OUT_OF_MEM, "%s",
"Allocation of sess failed");
res = -ENOMEM;
goto out_free_cmd;
}
/* WWPN */
atomic_inc(&tgt->sess_count);
smp_mb__after_atomic_inc();
memset(sess, 0, sizeof(*sess));
sess->tgt = tgt;
sess->init_index = init_index;
INIT_LIST_HEAD(&sess->delayed_cmds);
sess->scst_sess = scst_register_session(tgt->scst_tgt, 1,
"", sess, sess,
mpt_alloc_session_done);
if (sess->scst_sess == NULL) {
PRINT_ERROR(MYNAM ": scst_register_session failed %p",
tgt);
res = -EFAULT;
goto out_free_sess;
}
__set_bit(MPT_SESS_INITING, &sess->sess_flags);
tgt->sess[init_index] = sess;
cmd->sess = sess;
list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds);
goto out;
}
/* seesion is ready let us do it */
cmd->sess = sess;
if (test_bit(MPT_SESS_INITING, &sess->sess_flags)) {
list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds);
} else {
/* if there is pending sense left over from the last command,
* we need to send that if this is a REQUEST SENSE command.
* Otherwise send the command to SCST */
if (!stmapp_pending_sense(cmd)) {
res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_TASKLET);
/*res = mpt_send_cmd_to_scst(cmd, SCST_CONTEXT_DIRECT_ATOMIC);*/
if (res != 0)
goto out_free_cmd;
}
}
out:
TRACE_EXIT();
return;
out_free_sess:
TRACE_MEM("kfree for sess %p", sess);
kfree(sess);
if (atomic_dec_and_test(&tgt->sess_count))
wake_up_all(&tgt->waitQ);
/* go through */
out_free_cmd:
TRACE_MEM("kfree for cmd %p", cmd);
kfree(cmd);
goto out;
}
/*
* mpt_target_release
*
* this function is
* intended to free up the resources allocated to the device. The function
* should return 0 to indicate successful release or a negative value if
* there are some issues with the release. In the current version of SCST
* the return value is ignored. Must be defined.
*/
static int mpt_target_release(struct scst_tgt *scst_tgt)
{
/* FIXME */
return 0;
}
struct mpt_prm {
struct mpt_tgt *tgt;
uint16_t seg_cnt;
unsigned short use_sg;
struct scatterlist *sg;
unsigned int bufflen;
void *buffer;
scst_data_direction data_direction;
uint16_t rq_result;
uint16_t scsi_status;
unsigned char *sense_buffer;
unsigned int sense_buffer_len;
struct mpt_cmd *cmd;
};
static inline void mpt_dump_sge(MPT_SGE *sge, struct scatterlist *sg)
{
if (sge) {
void *address = NULL;
struct page *page = NULL;
address = bus_to_virt(sge->address);
page = virt_to_page(address);
TRACE_DBG("address %p, length %x, count %d, page %p",
address, sge->length, page_count(page), page);
TRACE_BUFFER("sge data", address, min_t(u32, sge->length, 0x10));
}
if (sg) {
TRACE_DBG("sg %p, page %p, %p, offset %d, dma address %llx, len %d",
sg, sg_page(sg), page_address(sg_page(sg)),
sg->offset, sg->dma_address, sg->length);
TRACE_BUFFER("sg data", page_address(sg_page(sg)), (u32)0x10);
}
}
/* FIXME
*
* use_sg can not bigger then NUM_SGES
*
*/
static inline void mpt_sge_to_sgl(struct mpt_prm *prm, MPT_STM_PRIV *priv,
MPT_SGL *sgl)
{
unsigned int bufflen = prm->bufflen;
int i;
TRACE_ENTRY();
TRACE_DBG("bufflen %d, %p", bufflen, prm->buffer);
sBUG_ON(prm->use_sg == 0);
prm->sg = (struct scatterlist *)prm->buffer;
prm->seg_cnt = pci_map_sg(priv->ioc->pcidev, prm->sg, prm->use_sg,
scst_to_tgt_dma_dir(prm->data_direction));
pci_dma_sync_sg_for_cpu(priv->ioc->pcidev, prm->sg, prm->use_sg,
scst_to_tgt_dma_dir(prm->data_direction));
for (i = 0; i < prm->use_sg; i++) {
sgl->sge[i].length = sg_dma_len(&prm->sg[i]);
sgl->sge[i].address = sg_dma_address(&prm->sg[i]);
TRACE_DBG("%d, %d", bufflen, prm->sg[i].length);
if (bufflen < prm->sg[i].length)
sgl->sge[i].length = bufflen;
mpt_dump_sge(&sgl->sge[i], &prm->sg[i]);
bufflen -= sgl->sge[i].length;
}
pci_dma_sync_sg_for_device(priv->ioc->pcidev, prm->sg, prm->use_sg,
scst_to_tgt_dma_dir(prm->data_direction));
sgl->num_sges = prm->seg_cnt;
TRACE_EXIT();
}
static inline void mpt_set_sense_info(MPT_STM_PRIV *priv, CMD *cmd, int len,
u8 *sense_buf)
{
u8 *info = NULL;
TRACE_ENTRY();
if (IsScsi(priv)) {
SCSI_RSP *rsp = (SCSI_RSP *)cmd->rsp;
rsp->Status = STS_CHECK_CONDITION;
rsp->Valid |= SCSI_SENSE_LEN_VALID;
rsp->SenseDataListLength = cpu_to_be32(len);
info = rsp->SenseData;
if (rsp->Valid & SCSI_RSP_LEN_VALID)
info += be32_to_cpu(rsp->PktFailuresListLength);
} else if (IsSas(priv)) {
SSP_RSP *rsp = (SSP_RSP *)cmd->rsp;
rsp->Status = STS_CHECK_CONDITION;
rsp->DataPres |= SSP_SENSE_LEN_VALID;
rsp->SenseDataLength = cpu_to_be32(len);
info = rsp->ResponseSenseData;
if (rsp->DataPres & SSP_RSP_LEN_VALID)
info += be32_to_cpu(rsp->ResponseDataLength);
} else {
FCP_RSP *rsp = (FCP_RSP *)cmd->rsp;
rsp->FcpStatus = STS_CHECK_CONDITION;
rsp->FcpFlags |= FCP_SENSE_LEN_VALID;
rsp->FcpSenseLength = cpu_to_be32(len);
info = rsp->FcpSenseData - sizeof(rsp->FcpResponseData);
if (rsp->FcpFlags & FCP_RSP_LEN_VALID)
info += be32_to_cpu(rsp->FcpResponseLength);
}
sBUG_ON(info == NULL);
memcpy(info, sense_buf, len);
/*out:*/
TRACE_EXIT();
}
static int mpt_send_tgt_data(MPT_STM_PRIV *priv, u32 reply_word, int index,
int flags, int lun, int tag, MPT_SGL *sgl,
int length, int offset)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetAssistRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
MPT_STM_CHAIN *sge_chain = NULL;
u32 sge_flags;
int chain_length, i, j, k, init_index, res = 1;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (TargetAssistRequest_t *)mpt_msg_frame_alloc(ioc, index);
memset(req, 0, sizeof(*req));
if (priv->exiting)
flags &= ~TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
flags |= TARGET_ASSIST_FLAGS_HIGH_PRIORITY;
if (flags & TARGET_ASSIST_FLAGS_AUTO_STATUS) {
flags |= TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
priv->io_state[index] |= IO_STATE_AUTO_REPOST;
}
}
if (priv->fcp2_capable/* && priv->initiators != NULL*/) {
init_index = GET_INITIATOR_INDEX(reply_word);
/*init = priv->initiators[init_index];
if (init != NULL && init->confirm_capable) {
flags |= TARGET_ASSIST_FLAGS_CONFIRMED;
}*/
}
TRACE_DBG("flags %x, tag %x, lun %x, offset %x, length %x",
flags, tag, lun, offset, length);
req->StatusCode = 0;
req->TargetAssistFlags = (u8)flags;
req->Function = MPI_FUNCTION_TARGET_ASSIST;
req->QueueTag = (u16)tag;
req->ReplyWord = cpu_to_le32(reply_word);
req->LUN[0] = (u8)(lun >> 8);
req->LUN[1] = (u8)lun;
req->RelativeOffset = cpu_to_le32(offset);
req->DataLength = cpu_to_le32(length);
sge_flags =
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
if (flags & TARGET_ASSIST_FLAGS_DATA_DIRECTION)
sge_flags |= MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_HOST_TO_IOC);
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
for (i = 0, j = 0, k = 0; i < (int)sgl->num_sges; i++, j++) {
if (k == 0) {
/* still in mf, haven't chained yet -- do we need to? */
if (j == priv->num_sge_target_assist) {
/* yes, we need to chain */
/* overwrite the last element in the mf with a chain */
sge_chain = (MPT_STM_CHAIN *)(sge_simple - 1);
sge_chain->Flags =
(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
dma_addr = priv->hw_dma +
((u8 *)priv->hw->cmd_buf[index].chain_sge -
(u8 *)priv->hw);
stm_set_dma_addr(sge_chain->Address, dma_addr);
/* set the "last element" flag in the mf */
sge_simple = (MPT_STM_SIMPLE *)(sge_chain - 1);
sge_simple->FlagsLength |=
cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT));
/* redo the last element in the mf */
sge_simple =
(MPT_STM_SIMPLE *)priv->hw->cmd_buf[index].chain_sge;
sge_simple->FlagsLength =
cpu_to_le32(sgl->sge[i-1].length | sge_flags);
stm_set_dma_addr(sge_simple->Address, sgl->sge[i-1].address);
mpt_dump_sge(&sgl->sge[i-1], NULL);
sge_simple++;
/* say we've chained */
req->ChainOffset =
((u8 *)sge_chain - (u8 *)req) / sizeof(u32);
j = 1;
k++;
}
} else {
/* now in chain, do we need to chain again? */
if (j == priv->num_sge_chain) {
/* yes, we need to chain */
/* fix up the previous chain element */
chain_length = sizeof(MPT_STM_CHAIN) +
(priv->num_sge_chain - 1) * sizeof(MPT_STM_SIMPLE);
sge_chain->Length = cpu_to_le16(chain_length);
sge_chain->NextChainOffset =
(chain_length - sizeof(MPT_STM_CHAIN)) / sizeof(u32);
/* overwrite the last element in the chain with another chain */
sge_chain = (MPT_STM_CHAIN *)(sge_simple - 1);
sge_chain->Flags =
(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
dma_addr = priv->hw_dma + ((u8 *)sge_simple - (u8 *)priv->hw);
stm_set_dma_addr(sge_chain->Address, dma_addr);
/* set the "last element" flag in the previous chain */
sge_simple = (MPT_STM_SIMPLE *)(sge_chain - 1);
sge_simple->FlagsLength |=
cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT));
/* redo the last element in the previous chain */
sge_simple = (MPT_STM_SIMPLE *)(sge_chain + 1);
sge_simple->FlagsLength =
cpu_to_le32(sgl->sge[i-1].length | sge_flags);
stm_set_dma_addr(sge_simple->Address, sgl->sge[i-1].address);
mpt_dump_sge(&sgl->sge[i-1], NULL);
sge_simple++;
/* say we've chained */
j = 1;
k++;
}
}
sge_simple->FlagsLength = cpu_to_le32(sgl->sge[i].length | sge_flags);
stm_set_dma_addr(sge_simple->Address, sgl->sge[i].address);
mpt_dump_sge(&sgl->sge[i], NULL);
sge_simple++;
}
/* did we chain? */
if (k != 0) {
/* fix up the last chain element */
sge_chain->Length = cpu_to_le16(j * sizeof(MPT_STM_SIMPLE));
sge_chain->NextChainOffset = 0;
}
/* fix up the last element */
sge_simple--;
sge_simple->FlagsLength |=
cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST));
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
/*dma_addr_t _data;*/
/*u8 *_buf;*/
TRACE(TRACE_MPI, "%s stm_send_target_data %d",
ioc->name, index);
for (i = 0; i < (sizeof(*req) - sizeof(req->SGL)) / 4; i++) {
TRACE(TRACE_MPI, "%s req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
TRACE(TRACE_MPI, "%s num_sges = %d, j = %d, k = %d",
ioc->name, sgl->num_sges, j, k);
p = (u32 *)&req->SGL;
for (i = 0; i < ((k != 0) ? priv->num_sge_target_assist : j); i++) {
#if MPT_STM_64_BIT_DMA
TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x %08x",
ioc->name, i * 12, le32_to_cpu(p[i*3]),
le32_to_cpu(p[i*3+1]), le32_to_cpu(p[i*3+2]));
#else
_data = le32_to_cpu(p[i*2+1]);
_buf = (u8 *)phys_to_virt(_data);
TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x,%x,%x,%x,%x,%p",
ioc->name, i * 8, le32_to_cpu(p[i*2]), le32_to_cpu(p[i*2+1]),
_buf[0], _buf[1], _buf[2], _buf[3], _buf);
#endif
}
p = (u32 *)priv->hw->cmd_buf[index].chain_sge;
for (i = 0; i < ((k != 0) ? (k - 1) * priv->num_sge_chain + j : 0); i++) {
#if MPT_STM_64_BIT_DMA
TRACE(TRACE_MPI, "%s chain sgl[%04x] = %08x %08x %08x",
ioc->name, i * 12, le32_to_cpu(p[i*3]),
le32_to_cpu(p[i*3+1]), le32_to_cpu(p[i*3+2]));
#else
_data = le32_to_cpu(p[i*2+1]);
_buf = (u8 *)phys_to_virt(_data);
TRACE(TRACE_MPI, "%s req sgl[%04x] = %08x %08x,%x,%x,%x,%x,%p",
ioc->name, i * 8, le32_to_cpu(p[i*2]), le32_to_cpu(p[i*2+1]),
_buf[0], _buf[1], _buf[2], _buf[3], _buf);
#endif
}
}
#endif
res = 0;
priv->io_state[index] |= IO_STATE_DATA_SENT;
if (flags & TARGET_ASSIST_FLAGS_AUTO_STATUS)
priv->io_state[index] |= IO_STATE_STATUS_SENT;
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
res = mpt_send_handshake_request(stm_context, _IOC_ID,
ioc->req_sz,
(u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
TRACE_EXIT_RES(res);
return res;
}
/*
* calling mpt_send_target_data
*
*/
static void mpt_send_target_data(struct mpt_prm *prm, int flags)
{
MPT_STM_PRIV *priv;
u32 reply_word;
int index, lun, tag, length, offset;
MPT_SGL *sgl;
TRACE_ENTRY();
priv = prm->tgt->priv;
sgl = &priv->sgl;
mpt_sge_to_sgl(prm, priv, sgl);
reply_word = prm->cmd->CMD->reply_word;
index = GET_IO_INDEX(reply_word);
lun = prm->cmd->CMD->lun;
tag = prm->cmd->CMD->tag;
if (prm->data_direction == SCST_DATA_READ)
flags |= TARGET_ASSIST_FLAGS_DATA_DIRECTION;
length = prm->bufflen;
offset = 0;
#if 0
TRACE_DBG("priv %p, reply_word %x, index %x, flags %x, lun %x, "
"tag %x, sgl %p, length %x, offset %x",
priv, reply_word, index, flags, lun, tag,
sgl, length, offset);
#endif
mpt_send_tgt_data(priv, reply_word, index, flags, lun, tag,
sgl, length, offset);
TRACE_EXIT();
return;
}
/*
* this function checks if we need to handle REQUEST_SENSE on behalf of the
* target device. If the sense wasn't able to be sent simultaneously
* with the status for the last command with a check condition, it needs
* to either get sent for a REQUEST_SENSE command or forgotten.
*
* The pending_sense state and a buffer for holding sense is created for
* each possible initiator. The pending_sense state is used to tell if
* sending sense failed and to track the progress of the following
* REQUEST_SENSE command.
*
* There are four values for the pending_sense state:
* - STATUS_SENSE_IDLE: no caching of sense data is in progress
* - STATUS_SENSE_ATTEMPT: an attempt is being made to send status and
* sense in the same bus transaction.
* - STATUS_SENSE_NOT_SENT: the attempt to send simultanous status and
* sense failed.
* - STATUS_SENSE_HANDLE_RQ: the next command from the initiator needs
* to be handled by the LSI driver if it is REQUEST_SENSE using cached
* sense data, otherwise the cached sense data is ignored and the
* command is sent to SCST.
*
* In stmapp_pending_sense, if pending_sense state for an initiator ==
* SENSE_HANDLE_RQ, the incoming command is inspected. If it is
* REQUEST_SENSE, the command is handled by the LSI driver without
* involving SCST. The cached sense data from the immediately previous
* command is used. This sense data was not sent along with the status
* for that command (see below). If the command is not REQUEST_SENSE,
* the cached sense data is discarded and the command is sent to SCST
* for processing.
*
* In stm_send_target_status, sense data about to be sent is saved in a
* buffer and the pending_sense state for that initiator is set to
* MPT_STATUS_SENSE_ATTEMPT. The sense and status are sent to the LSI
* hardware.
*
* If the LSI hardware determines that the sense and status could not be
* sent in one operation, stm_reply is called with state == STATUS_SENT
* and with IOCStatus of MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x6B).
* This condition only happens in the non-packetized SCSI operating mode.
* When this happens, the pending_sense state is advanced to
* MPT_STATUS_SENSE_NOT_SENT.
*
* In stm_tgt_reply, if io_state == STATUS_SENT:
* - if pending_sense state == SENSE_ATTEMPT, the status and sense attempt was
* successful, so the pending_sense state is set to IDLE and the command
* is sent to SCST for completion.
* - if pending_sense state == SENSE_NOT_SENT, the status and sense attempt
* failed. The pending_sense state is advanced to SENSE_HANDLE_RQ and the
* current command is sent to SCST for completion. The next command from
* this initiator will enter stmapp_pending_sense as described above.
* - if pending_sense state == SENSE_HANDLE_RQ, the REQUEST_SENSE command
* handled alone by the LSI driver is done and resources need to be
* released. pending_sense state is set to IDLE.
* - if pending_sense state == SENSE_IDLE, a normal SCST command is done
* and is sent to SCST for completion.
*
* Because of this caching sense behaviour, we think we need to turn off
* tagged command queueing. The mpt_inquiry_no_tagged_commands function
* does this by modifying INQUIRY data before sending it over the wire.
*/
static int stmapp_pending_sense(struct mpt_cmd *mpt_cmd)
{
int res = 0;
MPT_STM_PRIV *priv;
int index = 0;
int init_index = 0;
int flags = 0;
SCSI_CMD *scsi_cmd = NULL;
CMD *cmd;
u8 *cdb;
struct mpt_prm prm = { NULL };
struct scst_cmd *scst_cmd;
struct scatterlist sg;
TRACE_ENTRY();
priv = mpt_cmd->priv;
if (IsScsi(priv)) {
index = GET_IO_INDEX(mpt_cmd->reply_word);
init_index = GET_INITIATOR_INDEX(mpt_cmd->reply_word);
if (atomic_read(&priv->pending_sense[init_index]) ==
MPT_STATUS_SENSE_HANDLE_RQ) {
cmd = &priv->hw->cmd_buf[index];
scsi_cmd = (SCSI_CMD *)cmd->cmd;
cdb = scsi_cmd->CDB;
if (cdb[0] == REQUEST_SENSE) {
/* scst_cmd used as a container in stm_tgt_reply,
* command doesn't actually go to SCST */
scst_cmd = kmalloc(sizeof(struct scst_cmd),
GFP_ATOMIC);
TRACE_DBG("scst_cmd 0x%p", scst_cmd);
if (scst_cmd != NULL) {
cmd->reply_word = mpt_cmd->reply_word;
if (cmd->reply_word &
TARGET_MODE_REPLY_ALIAS_MASK) {
cmd->alias = (scsi_cmd->AliasID -
priv->port_id) & 15;
} else {
cmd->alias = 0;
}
cmd->lun = get2bytes(scsi_cmd->LogicalUnitNumber,
0);
cmd->tag = scsi_cmd->Tag;
mpt_cmd->CMD = cmd;
memset(scst_cmd, 0x00,
sizeof(struct scst_cmd));
scst_cmd->resp_data_len = -1;
memcpy(scst_cmd->cdb, cdb,
MPT_MAX_CDB_LEN);
priv->scst_cmd[index] = scst_cmd;
scst_cmd_set_tag(scst_cmd, cmd->tag);
scst_cmd_set_tgt_priv(scst_cmd, mpt_cmd);
TRACE_BUFFER("CDB", cdb, MPT_MAX_CDB_LEN);
flags = TARGET_ASSIST_FLAGS_AUTO_STATUS;
prm.cmd = mpt_cmd;
/* smallest amount of data between
* requested length, buffer size,
* and cached length */
prm.bufflen = min_t(size_t, cdb[4],
SCSI_SENSE_BUFFERSIZE);
prm.bufflen = min_t(size_t, prm.bufflen,
priv->pending_sense_buffer[init_index][7]
+ 8);
sg_set_page(&sg,
virt_to_page(priv->pending_sense_buffer[init_index]),
prm.bufflen,
offset_in_page(priv->pending_sense_buffer[init_index]));
prm.buffer = &sg;
prm.use_sg = 1;
prm.data_direction = SCST_DATA_READ;
prm.tgt = priv->tgt->sess[init_index]->tgt;
prm.cmd->state = MPT_STATE_DATA_OUT;
TRACE_DBG("%s: sending pending sense",
priv->ioc->name);
mpt_send_target_data(&prm, flags);
res = 1;
} else {
/* we couldn't create a scst_cmd, so
* we can't do anything. Send the
* command to SCST. */
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
}
} else {
/* next command immediately after check
* condition is not REQUEST_SENSE, so we can
* discard the cached sense and send the
* command to SCST. */
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
}
} else {
/* we don't need to perform REQUEST_SENSE and can
* send the command to SCST */
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_IDLE);
}
}
TRACE_EXIT_RES(res);
return res;
}
/*
* this function is equivalent to the SCSI queuecommand(). The target should
* transmit the response data and the status in the struct scst_cmd. See
* below for details. Must be defined.
*/
static int mpt_xmit_response(struct scst_cmd *scst_cmd)
{
int res = SCST_TGT_RES_SUCCESS;
struct mpt_sess *sess;
struct mpt_prm prm = { NULL };
int is_send_status;
/*uint16_t full_req_cnt;*/
/*int data_sense_flag = 0;*/
TRACE_ENTRY();
#ifdef DEBUG_WORK_IN_THREAD
if (scst_cmd_atomic(scst_cmd))
return SCST_TGT_RES_NEED_THREAD_CTX;
#endif
prm.cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
sess = (struct mpt_sess *)
scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd));
prm.sg = NULL;
prm.bufflen = scst_cmd_get_resp_data_len(scst_cmd);
prm.buffer = scst_cmd->sg;
prm.use_sg = scst_cmd->sg_cnt;
prm.data_direction = scst_cmd_get_data_direction(scst_cmd);
prm.rq_result = scst_cmd_get_status(scst_cmd);
prm.sense_buffer = scst_cmd_get_sense_buffer(scst_cmd);
prm.sense_buffer_len = scst_cmd_get_sense_buffer_len(scst_cmd);
prm.tgt = sess->tgt;
prm.seg_cnt = 0;
is_send_status = scst_cmd_get_is_send_status(scst_cmd);
TRACE_DBG("rq_result=%x, is_send_status=%x, %x, %d", prm.rq_result,
is_send_status, prm.bufflen, prm.sense_buffer_len);
if ((prm.rq_result != 0) && (prm.sense_buffer != NULL))
TRACE_BUFFER("Sense", prm.sense_buffer, prm.sense_buffer_len);
if (!is_send_status) {
/* ToDo, after it's done in SCST */
PRINT_ERROR(MYNAM ": is_send_status not set: "
"feature not implemented %p", scst_cmd);
res = SCST_TGT_RES_FATAL_ERROR;
goto out_tgt_free;
}
if (test_bit(MPT_SESS_SHUTDOWN, &sess->sess_flags)) {
TRACE_DBG("cmd %p while session %p is shutting down",
prm.cmd, sess);
res = SCST_TGT_RES_SUCCESS;
goto out_tgt_free;
}
if (scst_sense_valid(prm.sense_buffer)) {
mpt_set_sense_info(prm.tgt->priv, prm.cmd->CMD,
prm.sense_buffer_len, prm.sense_buffer);
}
if (scst_cmd_get_resp_data_len(scst_cmd) > 0) {
int flags = 0;
if (prm.rq_result == 0)
flags |= TARGET_ASSIST_FLAGS_AUTO_STATUS;
if (scst_get_may_need_dma_sync(scst_cmd)) {
dma_sync_sg_for_cpu(&(prm.tgt->priv->ioc->pcidev->dev),
scst_cmd->sg, scst_cmd->sg_cnt,
scst_to_tgt_dma_dir(scst_cmd_get_data_direction(scst_cmd)));
}
mpt_send_target_data(&prm, flags);
if (prm.rq_result == 0)
goto out;
}
{
int flags = 0;
u32 reply_word = prm.cmd->CMD->reply_word;
int index = GET_IO_INDEX(reply_word);
int lun = prm.cmd->CMD->lun;
int tag = prm.cmd->CMD->tag;
MPT_STM_PRIV *priv = prm.tgt->priv;
if (prm.rq_result == 0)
flags |= TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS;
flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
priv->io_state[index] |= IO_STATE_AUTO_REPOST;
TRACE_DBG("scst cmd %p, index %d, flags %d", scst_cmd, index,
flags);
stm_send_target_status(priv, reply_word, index, flags, lun,
tag);
}
out:
TRACE_EXIT_RES(res);
return res;
out_tgt_free:
/* ToDo: check and set scst_set_delivery_status(), if necessary */
scst_tgt_cmd_done(scst_cmd, SCST_CONTEXT_SAME);
goto out;
}
/*
* modifiy the response for an INQUIRY command to turn off
* support for tagged command queuing if we're on a SCSI bus.
* It's doubtful that caching sense data will work correctly
* if tagging is enabled.
*/
static void
mpt_inquiry_no_tagged_commands(MPT_STM_PRIV *priv, struct scst_cmd *scst_cmd)
{
int32_t length;
uint8_t *address;
TRACE_ENTRY();
/*
* only modify INQUIRY if we're on a SCSI bus,
* and we are handling a standard INQUIRY command
* (EVPD = 0)
*/
if (IsScsi(priv) && (scst_cmd->cdb[0] == INQUIRY) &&
!(scst_cmd->cdb[1] & 0x1)) {
sBUG_ON(scst_cmd->sg_cnt == 0);
length = scst_get_buf_first(scst_cmd, &address);
if (length >= 8) {
TRACE_DBG("clearing BQUE + CMDQUE 0x%p", address);
address[6] &= ~0x80; /* turn off BQUE */
address[7] &= ~0x02; /* turn off CMDQUE */
}
scst_put_buf(scst_cmd, address);
}
TRACE_EXIT();
}
/*
* this function
* informs the driver that data buffer corresponding to the said command
* have now been allocated and it is OK to receive data for this command.
* This function is necessary because a SCSI target does not have any
* control over the commands it receives. Most lower-level protocols have a
* corresponding function which informs the initiator that buffers have
* been allocated e.g., XFER_RDY in Fibre Channel. After the data is
* actually received the low-level driver should call scst_rx_data()
* in order to continue processing this command. Returns one of the
* SCST_TGT_RES_* constants, described below. Pay attention to
* "atomic" attribute of the command, which can be get via
* scst_cmd_get_atomic(): it is true if the function called in the
* atomic (non-sleeping) context. Must be defined.
*/
static int mpt_rdy_to_xfer(struct scst_cmd *scst_cmd)
{
int res = SCST_TGT_RES_SUCCESS;
struct mpt_sess *sess;
/*unsigned long flags = 0;*/
struct mpt_prm prm = { NULL };
TRACE_ENTRY();
#ifdef DEBUG_WORK_IN_THREAD
if (scst_cmd_atomic(scst_cmd))
return SCST_TGT_RES_NEED_THREAD_CTX;
#endif
prm.cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
sess = (struct mpt_sess *)
scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd));
mpt_inquiry_no_tagged_commands(sess->tgt->priv, scst_cmd);
prm.sg = (struct scatterlist *)NULL;
prm.bufflen = scst_cmd->bufflen;
prm.buffer = scst_cmd->sg;
prm.use_sg = scst_cmd->sg_cnt;
prm.data_direction = scst_cmd_get_data_direction(scst_cmd);
prm.tgt = sess->tgt;
if (test_bit(MPT_SESS_SHUTDOWN, &sess->sess_flags)) {
TRACE_DBG("cmd %p while session %p is shutting down",
prm.cmd, sess);
scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_FATAL,
SCST_CONTEXT_SAME);
res = SCST_TGT_RES_SUCCESS;
goto out;
}
prm.cmd->state = MPT_STATE_NEED_DATA;
mpt_send_target_data(&prm, 0);
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* this function
* called to notify the driver that the command is about to be freed.
* Necessary, because for aborted commands <bf/xmit_response()/ could not be
* called. Could be used on IRQ context. Must be defined.
*/
static void mpt_on_free_cmd(struct scst_cmd *scst_cmd)
{
struct mpt_cmd *cmd =
(struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
TRACE_ENTRY();
TRACE_DBG("cmd %p, scst_cmd %p", cmd, scst_cmd);
scst_cmd_set_tgt_priv(scst_cmd, NULL);
#if 1
memset(cmd, 0, sizeof(*cmd));
#endif
kfree(cmd);
TRACE_EXIT();
}
/*
* this function informs the driver that a received task management
* function has been completed. Completion status could be get via
* scst_mgmt_cmd_get_status(). No return value expected. Must be
* defined, if the target supports task management functionality.
*/
static void mpt_task_mgmt_fn_done(struct scst_mgmt_cmd *mgmt_cmd)
{
TRACE_ENTRY();
WARN_ON(1);
TRACE_EXIT();
}
static void mpt_local_task_mgmt(struct mpt_sess *sess, int task_mgmt, int lun)
{
struct mpt_cmd *cmd, *t;
TRACE_ENTRY();
switch (task_mgmt) {
case IMM_NTFY_TARGET_RESET:
while (!list_empty(&sess->delayed_cmds)) {
cmd = list_entry(sess->delayed_cmds.next,
typeof(*cmd), delayed_cmds_entry);
list_del(&cmd->delayed_cmds_entry);
kfree(cmd);
}
break;
case IMM_NTFY_LUN_RESET1:
case IMM_NTFY_LUN_RESET2:
case IMM_NTFY_CLEAR_TS:
case IMM_NTFY_ABORT_TS1:
case IMM_NTFY_ABORT_TS2:
list_for_each_entry_safe(cmd, t, &sess->delayed_cmds,
delayed_cmds_entry) {
if (cmd->CMD->lun == lun) {
list_del(&cmd->delayed_cmds_entry);
kfree(cmd);
}
}
break;
case IMM_NTFY_CLEAR_ACA:
default:
break;
}
TRACE_EXIT();
}
static int mpt_handle_task_mgmt(MPT_STM_PRIV *priv, u32 reply_word,
int task_mgmt, int _lun)
{
int res = 0, rc = 0;
struct mpt_mgmt_cmd *mcmd;
struct mpt_tgt *tgt;
struct mpt_sess *sess;
int init_index;
uint16_t lun = _lun;
TRACE_ENTRY();
TRACE_DBG("task_mgmt %d", task_mgmt);
tgt = priv->tgt;
init_index = GET_INITIATOR_INDEX(reply_word);
sess = tgt->sess[init_index];
if (sess == NULL) {
TRACE(TRACE_MGMT, "mpt_scst(%s): task mgmt fn %p for "
"unexisting session", priv->ioc->name, tgt);
res = -EFAULT;
goto out;
}
if (test_bit(MPT_SESS_INITING, &sess->sess_flags)) {
TRACE(TRACE_MGMT, "mpt_scst(%s): task mgmt fn %p for "
"inited session", priv->ioc->name, tgt);
mpt_local_task_mgmt(sess, reply_word, task_mgmt);
res = -EFAULT;
goto out;
}
mcmd = kmalloc(sizeof(*mcmd), GFP_ATOMIC);
TRACE_MEM("kmalloc(GFP_ATOMIC) for mcmd (%zd): %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,
&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,
&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,
&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,
&lun, sizeof(lun), SCST_ATOMIC, mcmd);
break;
case IMM_NTFY_ABORT_TS1:
case IMM_NTFY_ABORT_TS2:
TRACE(TRACE_MGMT, "%s", "IMM_NTFY_ABORT_TS received");
rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET,
&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, %zd",
rep->MsgLength, sizeof(*rep)/sizeof(u32));
WARN_ON(1);
/*
* the TargetCmdBufferPostErrorReply and TargetErrorReply
* structures are nearly identical; the exception is that
* the former does not have a TransferCount field, while
* the latter does; add one
*/
((TargetErrorReply_t *)rep)->TransferCount = 0;
stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
return 0;
}
WARN_ON(1);
return 1;
case MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST:
/*
* this signals that the command buffer base post is done
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s TargetCmdBufPostBaseReq IOCStatus = %04x\n",
ioc->name, ioc_status);
}
return 1;
case MPI_FUNCTION_TARGET_ASSIST:
/*
* this is the response to a target assist command; we should
* only get here if an error occurred
*
* at this point we need to clean up the remains of the I/O
* and repost the command buffer
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s TargetAssistReq IOCStatus = %04x\n",
ioc->name, ioc_status);
}
stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
return 0;
case MPI_FUNCTION_TARGET_STATUS_SEND:
/*
* this is the response to a target status send command; we should
* only get here if an error occurred
*
* at this point we need to clean up the remains of the I/O
* and repost the command buffer
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
/* if this is a MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT
* and we're SCSI, only print if we're debugging and
* tracing. This is a normal consequence of attempting
* to send sense data and status in the same
* transaction.
*/
if (IsScsi(priv) &&
(ioc_status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT)) {
TRACE_DBG(MYNAM ":%s TargetStatusSendReq IOCStatus = %04x\n",
ioc->name, ioc_status);
} else {
printk(KERN_ERR MYNAM ":%s TargetStatusSendReq IOCStatus = %04x\n",
ioc->name, ioc_status);
}
}
stm_target_reply_error(ioc, (TargetErrorReply_t *)rep);
return 0;
case MPI_FUNCTION_TARGET_MODE_ABORT: {
TargetModeAbort_t *req = (TargetModeAbort_t *)mf_req;
/*
* this signals that the target mode abort is done
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s TargetModeAbort IOCStatus = %04x\n",
ioc->name, ioc_status);
}
if (req->AbortType == TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS) {
priv->target_mode_abort_pending = 0;
} else {
u32 reply_word;
int index;
volatile int *io_state;
/*
* a target mode abort has finished, so check to see if
* the I/O was aborted, but there was no error reply for
* that aborted I/O (this will be the case for I/Os that
* have no outstanding target assist or target status send
* at the time of the abort request) -- so pretend that
* the error reply came in with a status indicating that
* the I/O was aborted
*/
reply_word = le32_to_cpu(req->ReplyWord);
index = GET_IO_INDEX(reply_word);
io_state = priv->io_state + index;
if ((*io_state & IO_STATE_ABORTED) &&
!(*io_state & IO_STATE_DATA_SENT) &&
!(*io_state & IO_STATE_STATUS_SENT)) {
stmapp_target_error(priv, reply_word, index,
MPI_IOCSTATUS_TARGET_ABORTED, 0);
}
/*
* see if we were trying to abort a target assist or target
* status send, but the abort didn't work (if the abort had
* worked, the flag we're checking would be clear) -- if so,
* just clear the various SRR flags, and wait for the initiator
* to retry the SRR
*/
if (*io_state & IO_STATE_REQUEST_ABORTED) {
printk(KERN_ERR MYNAM ":%s index %d: io_state = %x\n",
ioc->name, index, *io_state);
printk(KERN_ERR MYNAM ":%s request was not aborted\n",
ioc->name);
*io_state &= ~IO_STATE_REQUEST_ABORTED;
*io_state &= ~IO_STATE_REISSUE_REQUEST;
*io_state &= ~IO_STATE_ADJUST_OFFSET;
*io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
*io_state &= ~IO_STATE_REDO_COMMAND;
}
}
TRACE_EXIT_RES(1);
return 1;
}
case MPI_FUNCTION_FC_LINK_SRVC_BUF_POST:
/*
* if the length is that of a default reply, then this is the
* response to a link service buffer post -- do nothing except
* report errors (none are expected); otherwise this is a
* received ELS, so go handle it
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
if (priv->link_serv_abort_pending &&
ioc_status == MPI_IOCSTATUS_FC_ABORTED) {
return 0;
}
printk(KERN_ERR MYNAM ":%s FcLinkServBufPostReq IOCStatus = %04x\n",
ioc->name, ioc_status);
}
if (rep->MsgLength > sizeof(*rep)/sizeof(u32)) {
stm_link_service_reply(ioc,
(LinkServiceBufferPostReply_t *)rep);
return 0;
}
return 1;
case MPI_FUNCTION_FC_LINK_SRVC_RSP:
/*
* this is the response to a link service send -- repost the
* link service command buffer
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s FcLinkServRspReq IOCStatus = %04x\n",
ioc->name, ioc_status);
}
stm_link_service_rsp_reply(ioc,
(LinkServiceRspRequest_t *)mf_req,
(LinkServiceRspReply_t *)mf_rep);
return 1;
case MPI_FUNCTION_FC_ABORT:
/*
* this signals that the target mode abort is done
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s FcAbort IOCStatus = %04x\n",
ioc->name, ioc_status);
}
priv->link_serv_abort_pending = 0;
return 1;
case MPI_FUNCTION_FC_PRIMITIVE_SEND:
/*
* this signals that the FC primitive send is done
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s FcPrimitiveSend IOCStatus = %04x\n",
ioc->name, ioc_status);
}
priv->fc_primitive_send_pending = 0;
return 1;
case MPI_FUNCTION_FC_EX_LINK_SRVC_SEND:
/*
* this signals that the extended link service send is done
*/
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM ":%s ExLinkServiceSend IOCStatus = %04x\n",
ioc->name, ioc_status);
}
priv->ex_link_service_send_pending = 0;
return 1;
default:
/*
* don't understand this reply, so dump to the screen
*/
printk(KERN_ERR MYNAM ":%s got a reply (function %02x) that "
"I don't know what to do with\n", ioc->name, rep->Function);
if (1) {
u32 *p = (u32 *)mf_req;
int i;
for (i = 0; i < 16; i++) {
printk(KERN_ERR "%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(KERN_ERR "%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(KERN_DEBUG "%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(KERN_DEBUG "%s Rescan\n", ioc->name);
break;
case MPI_EVENT_IOC_BUS_RESET:
scsi_data = (EventDataScsi_t *)rep->Data;
printk(KERN_DEBUG "%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(KERN_DEBUG "%s Ext Bus Reset on port %d\n",
ioc->name, scsi_data->BusPort);
/*
* clear any pending sense flags on bus reset
*/
if (IsScsi(priv)) {
for (i = 0; i < NUM_SCSI_DEVICES; i++) {
atomic_set(&priv->pending_sense[i],
MPT_STATUS_SENSE_IDLE);
}
}
break;
case MPI_EVENT_LINK_STATUS_CHANGE:
link_status_data = (EventDataLinkStatus_t *)rep->Data;
printk(KERN_DEBUG "%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(KERN_DEBUG "%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(KERN_DEBUG "%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(KERN_DEBUG "%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(KERN_DEBUG "%s data[%d] = %08x\n",
ioc->name, i, le32_to_cpu(rep->Data[i]));
}
break;
}
if (event == MPI_EVENT_EXT_BUS_RESET) {
#if 0
if (IsScsi(priv))
memset(priv->luns->drop, 0, sizeof(priv->luns->drop));
#endif
}
TRACE_EXIT();
return 1;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_reset_process(MPT_ADAPTER *ioc, int phase)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
int i;
TRACE_ENTRY();
if (priv == NULL)
return 1;
if (phase == MPT_IOC_PRE_RESET) {
printk(KERN_ERR MYNAM ":%s IOC will be reset\n",
ioc->name);
priv->in_reset = 1;
priv->config_pending = 0;
for (i = 0; i < priv->num_cmd_buffers; i++)
priv->io_state[i] = 0;
}
if (phase == MPT_IOC_POST_RESET) {
printk(KERN_ERR MYNAM ":%s IOC has been reset, restarting now\n",
ioc->name);
mpt_stm_adapter_online(priv);
}
TRACE_EXIT();
return 1;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_link_service_reply(MPT_ADAPTER *ioc,
LinkServiceBufferPostReply_t *rep)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
FC_ELS *fc_els_buf;
int index;
int rctl;
int type;
int sid;
int did;
int command;
int i;
u32 wwnnh;
u32 wwnnl;
u32 wwpnh;
u32 wwpnl;
int ox_id;
int rx_id;
u32 offset;
TRACE_ENTRY();
index = le32_to_cpu(rep->TransactionContext);
fc_els_buf = &priv->hw->fc_link_serv_buf[index];
rctl =
(le32_to_cpu(rep->Rctl_Did) & MPI_FC_RCTL_MASK) >> MPI_FC_RCTL_SHIFT;
type =
(le32_to_cpu(rep->Type_Fctl) & MPI_FC_TYPE_MASK) >> MPI_FC_TYPE_SHIFT;
sid =
(le32_to_cpu(rep->Csctl_Sid) & MPI_FC_SID_MASK) >> MPI_FC_SID_SHIFT;
did =
(le32_to_cpu(rep->Rctl_Did) & MPI_FC_DID_MASK) >> MPI_FC_DID_SHIFT;
wwnnh = le32_to_cpu(rep->Wwn.NodeNameHigh);
wwnnl = le32_to_cpu(rep->Wwn.NodeNameLow);
wwpnh = le32_to_cpu(rep->Wwn.PortNameHigh);
wwpnl = le32_to_cpu(rep->Wwn.PortNameLow);
ox_id = le16_to_cpu(rep->Oxid);
rx_id = le16_to_cpu(rep->Rxid);
/*
* if this is a received PRLI/PRLO, respond by sending our own PRLI/PRLO
*/
if (rctl == ELS && type == 0x01) {
command = (be32_to_cpu(fc_els_buf->fc_els[0]) >> 24) & 0xff;
switch (command) {
case PRLI:
TRACE_DBG("%s: PRLI to %06x from %06x (wwn %08x%08x)",
ioc->name, did, sid, wwpnh, wwpnl);
i = be32_to_cpu(fc_els_buf->fc_els[4]);
fc_els_buf->fc_els[0] = cpu_to_be32(0x02100014);
fc_els_buf->fc_els[1] = cpu_to_be32(0x08002100);
fc_els_buf->fc_els[2] = cpu_to_be32(0x00000000);
fc_els_buf->fc_els[3] = cpu_to_be32(0x00000000);
fc_els_buf->fc_els[4] = cpu_to_be32(0x00000012);
if (priv->fcp2_capable)
fc_els_buf->fc_els[4] |= cpu_to_be32(0x100);
priv->els_state[index] = PRLI;
stm_send_els(priv, rep, index, 20);
return;
case PRLO:
TRACE_DBG("%s: PRLO to %06x from %06x (wwn %08x%08x)",
ioc->name, did, sid, wwpnh, wwpnl);
fc_els_buf->fc_els[0] = cpu_to_be32(0x02100014);
fc_els_buf->fc_els[1] = cpu_to_be32(0x08000100);
fc_els_buf->fc_els[2] = cpu_to_be32(0x00000000);
fc_els_buf->fc_els[3] = cpu_to_be32(0x00000000);
fc_els_buf->fc_els[4] = cpu_to_be32(0x00000000);
priv->els_state[index] = PRLO;
stm_send_els(priv, rep, index, 20);
return;
case RSCN:
TRACE_DBG("%s: RSCN", ioc->name);
stm_link_serv_buf_post(priv, index);
return;
default:
TRACE_DBG("%s: ELS %02x to %06x from %06x (wwn %08x%08x)",
ioc->name, command, did, sid, wwpnh, wwpnl);
stm_link_serv_buf_post(priv, index);
return;
}
}
/*
* if this is a received ABTS, respond by aborting the I/O and then
* accepting it
*/
if (rctl == ABTS && type == 0x00) {
TRACE_DBG("%s: ABTS to %06x from %06x (wwn %08x%08x)",
ioc->name, did, sid, wwpnh, wwpnl);
fc_els_buf->fc_els[0] = cpu_to_be32(0x00000000);
fc_els_buf->fc_els[1] = cpu_to_be32((ox_id << 16) | (rx_id << 0));
fc_els_buf->fc_els[2] = cpu_to_be32(0x0000ffff);
rep->Rctl_Did += cpu_to_le32((BA_ACC - ABTS) << MPI_FC_RCTL_SHIFT);
priv->els_state[index] = ABTS;
stmapp_abts_process(priv, rx_id, rep, index);
stm_send_els(priv, rep, index, 12);
return;
}
/*
* if this is a received SRR, respond by aborting any current TargetAssist
* or TargetStatusSend commands, accepting the SRR, and retransmitting the
* requested data or status
*/
if (rctl == FC4LS && type == 0x08) {
priv->els_state[index] = FC4LS;
command = (be32_to_cpu(fc_els_buf->fc_els[0]) >> 24) & 0xff;
switch (command) {
case SRR:
TRACE_DBG("%s: SRR to %06x from %06x (wwn %08x%08x)",
ioc->name, did, sid, wwpnh, wwpnl);
rx_id = be32_to_cpu(fc_els_buf->fc_els[1]) & 0xffff;
/*
* if the rx_id is out of range, reject this SRR with
* "invalid OX_ID/RX_ID combination"
*/
if (rx_id >= priv->num_cmd_buffers) {
fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
fc_els_buf->fc_els[1] = cpu_to_be32(0x00090300);
stm_send_els(priv, rep, index, 8);
return;
}
i = (be32_to_cpu(fc_els_buf->fc_els[3]) >> 24) & 0xff;
/*
* if the IU to retransmit is not a recognized IU, reject
* this SRR with "logical error"
*/
if (i != 1 && i != 5 && i != 7) {
fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
fc_els_buf->fc_els[1] = cpu_to_be32(0x00030000);
stm_send_els(priv, rep, index, 8);
return;
}
offset = be32_to_cpu(fc_els_buf->fc_els[2]);
/*
* go process this SRR further
*
* make the call to stm_send_els when any request in progress
* has been aborted
*
* note that the address of the LinkServiceBufferPostReply
* that's passed as a parameter to stmapp_abts_process CANNOT
* BE REMEMBERED; its contents must be copied if the call to
* stm_send_els will not be made synchronously
*/
stmapp_srr_process(priv, rx_id, i, offset, rep, index);
return;
default:
TRACE_DBG("%s: FC4LS %02x to %06x from %06x (wwn %08x%08x)",
ioc->name, command, did, sid, wwpnh, wwpnl);
/*
* the only FC4LS we recognize is SRR; all others get
* rejected with "command not supported"
*/
fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
fc_els_buf->fc_els[1] = cpu_to_be32(0x000b0000);
stm_send_els(priv, rep, index, 8);
return;
}
}
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)rep;
int i;
for (i = 0; i < 17; i++) {
TRACE(TRACE_MPI, "%s: fc_els[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
if (trace_mpi) {
u32 *p = (u32 *)fc_els_buf;
int i;
for (i = 0; i < (int)le32_to_cpu(rep->TransferLength) / 4; i++) {
TRACE(TRACE_MPI, "%s: fc_els_buf[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
stm_link_serv_buf_post(priv, index);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_link_service_rsp_reply(MPT_ADAPTER *ioc,
LinkServiceRspRequest_t *req,
LinkServiceRspReply_t *rep)
{
MPT_STM_PRIV *priv = mpt_stm_priv[ioc->id];
MPT_STM_SIMPLE *sge_simple;
int *p_index;
int index;
int sid;
int did;
int els;
int init_index;
TRACE_ENTRY();
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
p_index = (int *)(sge_simple + 1);
index = *p_index;
els = priv->els_state[index];
sid = (le32_to_cpu(req->Csctl_Sid) & MPI_FC_SID_MASK) >> MPI_FC_SID_SHIFT;
did = (le32_to_cpu(req->Rctl_Did) & MPI_FC_DID_MASK) >> MPI_FC_DID_SHIFT;
init_index = le32_to_cpu(rep->InitiatorIndex);
/*
* after our link service reponse has been sent, repost the link service
* buffer
*/
priv->els_state[index] = 0;
stm_link_serv_buf_post(priv, index);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_cmd_buf_post(MPT_STM_PRIV *priv, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetCmdBufferPostRequest_t *req;
dma_addr_t dma_addr;
TRACE_ENTRY();
if (priv->exiting) {
priv->io_state[index] |= IO_STATE_POSTED;
return;
}
if (IsSas(priv)) {
stm_cmd_buf_post_list(priv, index);
return;
}
/*
* get a free message frame, and post a command buffer
*/
req = (TargetCmdBufferPostRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
#ifdef CMD_BUFFER_POST_FLAGS_HIGH_PRIORITY
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY)
req->BufferPostFlags = CMD_BUFFER_POST_FLAGS_HIGH_PRIORITY;
#else
priv->io_state[index] &= ~IO_STATE_HIGH_PRIORITY;
#endif
req->BufferCount = 1;
req->Function = MPI_FUNCTION_TARGET_CMD_BUFFER_POST;
req->BufferLength = sizeof(priv->hw->cmd_buf[index].cmd);
req->Buffer[0].IoIndex = cpu_to_le16(index);
dma_addr = priv->hw_dma +
((u8 *)priv->hw->cmd_buf[index].cmd - (u8 *)priv->hw);
req->Buffer[0].u.PhysicalAddress64.Low = cpu_to_le32(dma_addr);
#if MPT_STM_64_BIT_DMA
req->Buffer[0].u.PhysicalAddress64.High = cpu_to_le32((u64)dma_addr>>32);
req->BufferPostFlags = CMD_BUFFER_POST_FLAGS_64_BIT_ADDR;
#endif
priv->io_state[index] |= IO_STATE_POSTED;
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_cmd_buf_post %d", ioc->name, index);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
priv->io_state[index] &= ~IO_STATE_HIGH_PRIORITY;
mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_cmd_buf_post_base(MPT_STM_PRIV *priv, int post_all)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetCmdBufferPostBaseRequest_t *req;
int i;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (TargetCmdBufferPostBaseRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
if (post_all)
req->BufferPostFlags = CMD_BUFFER_POST_BASE_FLAGS_AUTO_POST_ALL;
req->Function = MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST;
req->TotalCmdBuffers = cpu_to_le16(priv->num_cmd_buffers);
req->CmdBufferLength = cpu_to_le16(sizeof(priv->hw->cmd_buf[0].cmd));
req->NextCmdBufferOffset = cpu_to_le16(sizeof(priv->hw->cmd_buf[0]));
dma_addr = priv->hw_dma +
((u8 *)priv->hw->cmd_buf[0].cmd - (u8 *)priv->hw);
req->BaseAddressLow = cpu_to_le32(dma_addr);
#if MPT_STM_64_BIT_DMA
req->BaseAddressHigh = cpu_to_le32((u64)dma_addr>>32);
#endif
if (post_all)
for (i = 0; i < priv->num_cmd_buffers; i++)
priv->io_state[i] |= IO_STATE_POSTED;
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_cmd_buf_post_base", ioc->name);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_cmd_buf_post_list(MPT_STM_PRIV *priv, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetCmdBufferPostListRequest_t *req;
TRACE_ENTRY();
req = (TargetCmdBufferPostListRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST;
req->CmdBufferCount = cpu_to_le16(1);
req->IoIndex[0] = cpu_to_le16(index);
priv->io_state[index] |= IO_STATE_POSTED;
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_cmd_buf_post_list %d", ioc->name, index);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_link_serv_buf_post(MPT_STM_PRIV *priv, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
LinkServiceBufferPostRequest_t *req;
SGETransaction32_t *sge_trans;
MPT_STM_SIMPLE *sge_simple;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (LinkServiceBufferPostRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->BufferCount = 1;
req->Function = MPI_FUNCTION_FC_LINK_SRVC_BUF_POST;
sge_trans = (SGETransaction32_t *)&req->SGL;
sge_trans->ContextSize = 4;
sge_trans->DetailsLength = 0;
sge_trans->Flags = 0;
sge_trans->TransactionContext[0] = cpu_to_le32(index);
sge_simple = (MPT_STM_SIMPLE *)&sge_trans->TransactionDetails[0];
sge_simple->FlagsLength = cpu_to_le32(sizeof(FC_ELS) |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma +
((u8 *)priv->hw->fc_link_serv_buf[index].fc_els - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_link_serv_buf_post %d", ioc->name, index);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_send_target_status(MPT_STM_PRIV *priv, u32 reply_word, int index,
int flags, int lun, int tag)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetStatusSendRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
CMD *cmd;
int length;
int status;
int init_index;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (TargetStatusSendRequest_t *)mpt_msg_frame_alloc(ioc, index);
memset(req, 0, sizeof(*req));
if (priv->exiting) {
flags &= ~TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER;
priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
}
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
flags |= TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY;
flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
priv->io_state[index] |= IO_STATE_AUTO_REPOST;
}
if (priv->fcp2_capable/* && priv->initiators != NULL*/) {
init_index = GET_INITIATOR_INDEX(reply_word);
/*init = priv->initiators[init_index];
if (init != NULL && init->confirm_capable) {
flags |= TARGET_STATUS_SEND_FLAGS_CONFIRMED;
}*/
}
cmd = priv->hw->cmd_buf + index;
if (flags & TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS) {
length = 0;
req->StatusCode = 0;
} else {
length = 0;
if (IsScsi(priv)) {
SCSI_RSP *rsp = (SCSI_RSP *)cmd->rsp;
size_t sense_size;
length += sizeof(*rsp);
length -= sizeof(rsp->SenseData);
status = rsp->Status;
if (rsp->Valid & SCSI_SENSE_LEN_VALID) {
length += be32_to_cpu(rsp->SenseDataListLength);
init_index = GET_INITIATOR_INDEX(reply_word);
/*
* try to avoid a SCSI firmware bug by not using Auto Repost
* here, unless required (High Priority requires it)
*/
if (!(priv->io_state[index] & IO_STATE_HIGH_PRIORITY)) {
flags &= ~TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
priv->io_state[index] &= ~IO_STATE_AUTO_REPOST;
}
/*
* cache sense buffer so we can send it on the next
* REQUEST SENSE command if the IOC can't send the
* status and sense simultaneously (generating
* MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT IOCStatus)
*/
sense_size = min(sizeof(rsp->SenseData),
(size_t)SCSI_SENSE_BUFFERSIZE);
TRACE_DBG("caching %zd bytes pending sense", sense_size);
memcpy(priv->pending_sense_buffer[init_index],
rsp->SenseData, sense_size);
TRACE_BUFFER("priv->pending_sense_buffer",
priv->pending_sense_buffer[init_index],
sense_size);
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_ATTEMPT);
}
if (rsp->Valid & SCSI_RSP_LEN_VALID)
length += be32_to_cpu(rsp->PktFailuresListLength);
} else if (IsSas(priv)) {
SSP_RSP *rsp = (SSP_RSP *)cmd->rsp;
length += sizeof(*rsp);
length -= sizeof(rsp->ResponseSenseData);
status = rsp->Status;
if (rsp->DataPres & SSP_SENSE_LEN_VALID)
length += be32_to_cpu(rsp->SenseDataLength);
if (rsp->DataPres & SSP_RSP_LEN_VALID)
length += be32_to_cpu(rsp->ResponseDataLength);
} else {
FCP_RSP *rsp = (FCP_RSP *)cmd->rsp;
length += sizeof(*rsp);
length -= sizeof(rsp->FcpSenseData) + sizeof(rsp->FcpResponseData);
status = rsp->FcpStatus;
if (flags & TARGET_STATUS_SEND_FLAGS_CONFIRMED)
rsp->FcpFlags |= FCP_REQUEST_CONFIRM;
if (rsp->FcpFlags & FCP_SENSE_LEN_VALID)
length += be32_to_cpu(rsp->FcpSenseLength);
if (rsp->FcpFlags & FCP_RSP_LEN_VALID) {
length += be32_to_cpu(rsp->FcpResponseLength);
/* FCP_RSP_LEN_VALID will only be set for Task Mgmt responses */
/* and Task Mgmt responses can't be confirmed */
rsp->FcpFlags &= ~FCP_REQUEST_CONFIRM;
flags &= ~TARGET_STATUS_SEND_FLAGS_CONFIRMED;
}
}
req->StatusCode = (u8)status;
}
req->StatusFlags = (u8)flags;
req->Function = MPI_FUNCTION_TARGET_STATUS_SEND;
req->QueueTag = (u16)tag;
req->ReplyWord = cpu_to_le32(reply_word);
req->LUN[0] = (u8)(lun >> 8);
req->LUN[1] = (u8)lun;
if (length != 0) {
sge_simple = (MPT_STM_SIMPLE *)&req->StatusDataSGE;
sge_simple->FlagsLength =
cpu_to_le32(length |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma +
((u8 *)priv->hw->cmd_buf[index].rsp - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
}
/*
* there's a limitation here -- if target data is outstanding, we must
* wait for it to finish before we send the target status
*/
if (priv->io_state[index] & IO_STATE_DATA_SENT) {
priv->status_deferred_mf[index] = (MPT_FRAME_HDR *)req;
priv->io_state[index] |= IO_STATE_STATUS_DEFERRED;
TRACE_EXIT_RES(1);
return 1;
}
priv->io_state[index] |= IO_STATE_STATUS_SENT;
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_send_target_status %d",
ioc->name, index);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
if (priv->io_state[index] & IO_STATE_HIGH_PRIORITY) {
mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
TRACE_EXIT_RES(1);
return 1;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_send_els(MPT_STM_PRIV *priv, LinkServiceBufferPostReply_t *rep,
int index, int length)
{
MPT_ADAPTER *ioc = priv->ioc;
LinkServiceRspRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
int *p_index;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (LinkServiceRspRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->RspLength = (u8)length;
req->Function = MPI_FUNCTION_FC_LINK_SRVC_RSP;
memcpy((u8 *)req + 0x0c, (u8 *)rep + 0x1c, 24);
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength = cpu_to_le32(length |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma +
((u8 *)priv->hw->fc_link_serv_buf[index].fc_els - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
p_index = (int *)(sge_simple + 1);
*p_index = index;
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)req;
int i;
TRACE(TRACE_MPI, "%s: stm_send_els %d", ioc->name, index);
for (i = 0; i < sizeof(*req) / 4; i++) {
TRACE(TRACE_MPI, "%s: req[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_port_enable(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc = priv->ioc;
PortEnable_t *req;
int ret;
TRACE_ENTRY();
req = (PortEnable_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_PORT_ENABLE;
priv->port_enable_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
ret = stm_wait_for(priv, &priv->port_enable_pending, 60, NO_SLEEP);
TRACE_EXIT_RES(ret);
return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_target_mode_abort_command(MPT_STM_PRIV *priv, u32 reply_word,
int index)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetModeAbort_t *req;
TRACE_ENTRY();
req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->AbortType = TARGET_MODE_ABORT_TYPE_EXACT_IO;
req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;
req->ReplyWord = cpu_to_le32(reply_word);
priv->io_state[index] |= IO_STATE_ABORTED;
if (IsScsi(priv)) {
mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_target_mode_abort_request(MPT_STM_PRIV *priv, u32 reply_word,
u32 msg_context, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetModeAbort_t *req;
TRACE_ENTRY();
req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->AbortType = TARGET_MODE_ABORT_TYPE_EXACT_IO_REQUEST;
req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;
req->ReplyWord = cpu_to_le32(reply_word);
req->MsgContextToAbort = cpu_to_le32(msg_context);
priv->io_state[index] |= IO_STATE_REQUEST_ABORTED;
if (IsScsi(priv)) {
mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_target_mode_abort_all(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc = priv->ioc;
TargetModeAbort_t *req;
int ret;
TRACE_ENTRY();
req = (TargetModeAbort_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->AbortType = TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS;
req->Function = MPI_FUNCTION_TARGET_MODE_ABORT;
priv->target_mode_abort_pending = 1;
if (IsScsi(priv)) {
mpt_send_handshake_request(stm_context, _IOC_ID,
sizeof(*req), (u32 *)req _HS_SLEEP);
} else {
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
}
ret = stm_wait_for(priv, &priv->target_mode_abort_pending, 60,
NO_SLEEP);
TRACE_EXIT_RES(ret);
return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_target_mode_abort(MPT_STM_PRIV *priv)
{
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
int i;
int n;
TRACE_ENTRY();
while (1) {
n = 0;
for (i = 0; i < priv->num_cmd_buffers; i++)
if (priv->io_state[i] & IO_STATE_AUTO_REPOST)
n++;
if (n == 0)
break;
TRACE_DBG("%s: %d out of %d commands being auto-reposted, waiting...",
ioc->name, n, priv->num_cmd_buffers);
stm_wait(priv, 10, CAN_SLEEP);
}
while (1) {
stm_target_mode_abort_all(priv);
n = 0;
for (i = 0; i < priv->num_cmd_buffers; i++)
if (priv->io_state[i] & IO_STATE_POSTED)
n++;
if (n == priv->num_cmd_buffers)
break;
TRACE_DBG("%s: %d out of %d commands still active, waiting...",
ioc->name, n, priv->num_cmd_buffers);
stm_wait(priv, 10, CAN_SLEEP);
}
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_link_serv_abort(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc = priv->ioc;
FcAbortRequest_t *req;
int ret;
TRACE_ENTRY();
req = (FcAbortRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->AbortType = FC_ABORT_TYPE_ALL_FC_BUFFERS;
req->Function = MPI_FUNCTION_FC_ABORT;
priv->link_serv_abort_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
ret = stm_wait_for(priv, &priv->link_serv_abort_pending, 60, NO_SLEEP);
TRACE_EXIT_RES(ret);
return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_reset_link(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc = priv->ioc;
FcPrimitiveSendRequest_t *req;
int ret;
TRACE_ENTRY();
req = (FcPrimitiveSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->SendFlags = MPI_FC_PRIM_SEND_FLAGS_RESET_LINK;
req->Function = MPI_FUNCTION_FC_PRIMITIVE_SEND;
priv->fc_primitive_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
ret = stm_wait_for(priv, &priv->fc_primitive_send_pending, 60, NO_SLEEP);
TRACE_EXIT_RES(ret);
return ret;
}
#if 0
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_login_port(MPT_STM_PRIV *priv, int port_id, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ExLinkServiceSendRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
u32 *buf;
int len, ret;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
req->MsgFlags_Did = cpu_to_le32(port_id);
req->ElsCommandCode = cpu_to_le32(PLOGI);
len = 29 * 4;
buf = priv->hw->exlink_buf;
memset(buf, 0, len);
buf[0] = cpu_to_be32(PLOGI << 24);
/*
* the firmware builds the rest of the PLOGI payload
*/
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength = cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
sge_simple++;
sge_simple->FlagsLength = cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_IOC_TO_HOST));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
priv->ex_link_service_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
if (stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep) < 0)
return -1;
req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
req->MsgFlags_Did = cpu_to_le32(port_id);
req->ElsCommandCode = cpu_to_le32(PRLI);
len = 5 * 4;
buf = priv->hw->exlink_buf;
memset(buf, 0, len);
buf[0] = cpu_to_be32(0x00100014 | (PRLI << 24));
buf[1] = cpu_to_be32(0x08002000);
buf[2] = cpu_to_be32(0x00000000);
buf[3] = cpu_to_be32(0x00000000);
buf[4] = cpu_to_be32(0x000000b2);
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
sge_simple++;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_IOC_TO_HOST));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
priv->ex_link_service_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);
TRACE_EXIT_RES(ret);
return ret;
}
static int stm_logout_port(MPT_STM_PRIV *priv, int port_id, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ExLinkServiceSendRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
u32 *buf;
int len, ret;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
req->MsgFlags_Did = cpu_to_le32(port_id);
req->ElsCommandCode = cpu_to_le32(LOGO);
len = 4 * 4;
buf = priv->hw->exlink_buf;
memset(buf, 0, len);
buf[0] = cpu_to_be32(LOGO << 24);
/*
* the firmware builds the rest of the LOGO payload
*/
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
sge_simple++;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_IOC_TO_HOST));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
priv->ex_link_service_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);
TRACE_EXIT_RES(ret);
return ret;
}
static int stm_process_logout_port(MPT_STM_PRIV *priv, int port_id, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ExLinkServiceSendRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
u32 *buf;
int len, ret;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
req->MsgFlags_Did = cpu_to_le32(port_id);
req->ElsCommandCode = cpu_to_le32(PRLO);
len = 5 * 4;
buf = priv->hw->exlink_buf;
memset(buf, 0, len);
buf[0] = cpu_to_be32(0x00100014 | (PRLO << 24));
buf[1] = cpu_to_be32(0x08002000);
buf[2] = cpu_to_be32(0x00000000);
buf[3] = cpu_to_be32(0x00000000);
buf[4] = cpu_to_be32(0x00000000);
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
sge_simple++;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_IOC_TO_HOST));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
priv->ex_link_service_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
ret = stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep);
TRACE_EXIT_RES(ret);
return ret;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_get_hard_address(MPT_STM_PRIV *priv, int port_id,
int *hard_address, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ExLinkServiceSendRequest_t *req;
MPT_STM_SIMPLE *sge_simple;
u32 *buf;
int len;
dma_addr_t dma_addr;
TRACE_ENTRY();
req = (ExLinkServiceSendRequest_t *)mpt_msg_frame_alloc(ioc, -1);
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_FC_EX_LINK_SRVC_SEND;
req->MsgFlags_Did = cpu_to_le32(port_id);
req->ElsCommandCode = cpu_to_le32(ADISC);
len = 7 * 4;
buf = priv->hw->exlink_buf;
memset(buf, 0, len);
buf[0] = cpu_to_be32(ADISC << 24);
buf[1] = cpu_to_be32(0x00000000); /* or get HardALPA from FCPortPage1 */
buf[2] = cpu_to_be32(priv->wwpn.High);
buf[3] = cpu_to_be32(priv->wwpn.Low);
buf[4] = cpu_to_be32(priv->wwnn.High);
buf[5] = cpu_to_be32(priv->wwnn.Low);
buf[6] = cpu_to_be32(priv->port_id);
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
sge_simple++;
sge_simple->FlagsLength =
cpu_to_le32(len |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING |
MPI_SGE_FLAGS_IOC_TO_HOST));
dma_addr = priv->hw_dma + ((u8 *)priv->hw->exlink_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
priv->ex_link_service_send_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, mf);
if (stm_wait_for(priv, &priv->ex_link_service_send_pending, 5, sleep) < 0)
return -1;
if ((be32_to_cpu(buf[0]) >> 24) != LS_ACC)
return -2;
*hard_address = be32_to_cpu(buf[1]);
TRACE_EXIT();
return 0;
}
#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_scsi_configuration(MPT_STM_PRIV *priv, int sleep)
{
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
SCSIPortPage0_t *ScsiPort0;
SCSIPortPage2_t *ScsiPort2;
int cap;
int wcap;
int ncap;
int sync;
int flags;
int i;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 2, 0, sleep))
return -1;
ScsiPort2 = &priv->SCSIPortPage2;
memcpy(&priv->SCSIPortPage2, priv->hw->config_buf, sizeof(SCSIPortPage2_t));
TRACE_DBG("%s scsi id is %d", ioc->name, priv->port_id);
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 0, 0, sleep))
return -1;
memcpy(&priv->SCSIPortPage0, priv->hw->config_buf, sizeof(SCSIPortPage0_t));
ScsiPort0 = &priv->SCSIPortPage0;
cap = le32_to_cpu(ScsiPort0->Capabilities);
TRACE_DBG("%s target %d capabilities = %08x",
ioc->name, priv->port_id, cap);
stm_set_scsi_port_page1(priv, sleep);
wcap = cap & ~MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK;
ncap = wcap & ~MPI_SCSIPORTPAGE0_CAP_WIDE;
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
memset(priv->SCSIDevicePage1, 0, sizeof(SCSIDevicePage1_t) * NUM_SCSI_DEVICES);
for (i = 0; i < NUM_SCSI_DEVICES; i++) {
int wide = 0;
SCSIDevicePage1_t *ScsiDevice1 = &priv->SCSIDevicePage1[i];
sync = ScsiPort2->DeviceSettings[i].SyncFactor;
if (ioc->facts.FWVersion.Word >= 0x01032900) {
/* these firmware versions don't send the correct
* amount of data except at the slowest transfer
* factors */
sync = max(0x32, sync);
printk(KERN_ERR "forcing FAST-5 negotiation due to broken fw 0x%08X\n",
ioc->facts.FWVersion.Word);
}
flags = le16_to_cpu(ScsiPort2->DeviceSettings[i].DeviceFlags);
if (flags & MPI_SCSIPORTPAGE2_DEVICE_WIDE_DISABLE) {
cap = ncap;
} else {
cap = wcap;
wide = 1;
}
/*cap &= ~MPI_SCSIDEVPAGE1_RP_IU;
cap &= ~MPI_SCSIDEVPAGE1_RP_DT;
cap &= ~MPI_SCSIDEVPAGE1_RP_QAS;*/
ScsiDevice1->RequestedParameters = cpu_to_le32(cap | (sync << 8));
TRACE_DBG("%s initiator %d parameters = %08x, %s %s",
ioc->name, i, le32_to_cpu(ScsiDevice1->RequestedParameters),
sync ? "SYNC" : " ",
wide ? "WIDE" : " ");
memcpy(priv->hw->config_buf, ScsiDevice1, sizeof(*ScsiDevice1));
stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, 1, i, sleep);
atomic_set(&priv->pending_sense[i], MPT_STATUS_SENSE_IDLE);
}
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stm_set_scsi_port_page1(MPT_STM_PRIV *priv, int sleep)
{
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
SCSIPortPage1_t *ScsiPort1;
int i;
int id = priv->port_id;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
memset(&priv->SCSIPortPage1, 0, sizeof(priv->SCSIPortPage1));
ScsiPort1 = &priv->SCSIPortPage1;
ScsiPort1->Configuration = cpu_to_le32(id | (1 << (id + 16)));
for (i = 1; i <= priv->num_aliases; i++) {
id = (priv->port_id + i) & 15;
TRACE_DBG("%s alias %d is target %d",
ioc->name, i, id);
ScsiPort1->Configuration |= cpu_to_le32(1 << (id + 16));
}
ScsiPort1->TargetConfig = priv->scsi_port_config;
ScsiPort1->IDConfig = priv->scsi_id_config;
memcpy(priv->hw->config_buf, (u32 *)ScsiPort1, sizeof(*ScsiPort1));
stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_SCSI_PORT, 1, 0, sleep);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_sas_configuration(MPT_STM_PRIV *priv, int sleep)
{
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_configuration(MPT_STM_PRIV *priv, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
U64 wwnn;
U64 wwpn;
int port_id;
int protocol;
int flags;
int current_speed;
int port_state;
int target;
char *attach;
char *speed;
FCPortPage0_t *FcPort0;
FCDevicePage0_t *FcDevice0;
int page;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 0, 0, sleep))
return -1;
FcPort0 = (FCPortPage0_t *)priv->hw->config_buf;
flags = le32_to_cpu(FcPort0->Flags) &
MPI_FCPORTPAGE0_FLAGS_ATTACH_TYPE_MASK;
current_speed = le32_to_cpu(FcPort0->CurrentSpeed);
port_state = FcPort0->PortState;
switch (flags) {
case MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT:
attach = NULL;
break;
case MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT:
attach = "point to point";
break;
case MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP:
attach = "private loop";
break;
case MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT:
attach = "fabric direct attach";
break;
case MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP:
attach = "public loop";
break;
default:
attach = "unknown";
break;
}
switch (current_speed) {
case MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT:
speed = "1 Gbaud";
break;
case MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT:
speed = "2 Gbaud";
break;
case MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT:
speed = "10 Gbaud";
break;
default:
speed = "unknown";
break;
}
if (priv->port_flags != flags ||
priv->port_speed != current_speed ||
priv->port_state != port_state) {
priv->port_flags = flags;
priv->port_speed = current_speed;
priv->port_state = port_state;
priv->device_changed = 1;
if (attach)
printk(KERN_INFO "%s link is online, type is %s, speed is %s\n",
ioc->name, attach, speed);
else
printk(KERN_INFO "%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(KERN_INFO "%s port is wwn %08x%08x, port id %x\n",
ioc->name, wwpn.High, wwpn.Low, port_id);
else
printk(KERN_INFO "%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(KERN_INFO "%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(KERN_ERR "%s ADISC failed!\n", ioc->name);
else
printk(KERN_INFO "%s port id's %x hard address is %x\n",
ioc->name, port_id, i);
#endif
page = MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID + port_id;
}
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_enable_els(MPT_STM_PRIV *priv, int els, int sleep)
{
FCPortPage8_t *FcPort8;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 8, 0, sleep))
return -1;
FcPort8 = (FCPortPage8_t *)priv->hw->config_buf;
/* clear the ELS bit */
FcPort8->BitVector[els / 32] &= ~cpu_to_le32(1 << (els & 31));
if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 8, 0, sleep))
return -1;
TRACE_EXIT();
return 0;
}
#if 0
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_enable_immediate_errors(MPT_STM_PRIV *priv, int sleep)
{
FCPortPage1_t *FcPort1;
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
/* set the Immediate Error Reply bit */
FcPort1->Flags |= cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY);
if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_enable_target_mode_oxid(MPT_STM_PRIV *priv, int sleep)
{
FCPortPage1_t *FcPort1;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
/* set the Target Mode OX_ID bit */
FcPort1->Flags |= cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_TARGET_MODE_OXID);
if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_set_wwn(MPT_STM_PRIV *priv, WwnFormat_t *wwn, int sleep)
{
FCPortPage1_t *FcPort1;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
/* set the WWPN and WWNN */
FcPort1->NoSEEPROMWWPN.Low = cpu_to_le32(wwn->PortNameLow);
FcPort1->NoSEEPROMWWPN.High = cpu_to_le32(wwn->PortNameHigh);
FcPort1->NoSEEPROMWWNN.Low = cpu_to_le32(wwn->NodeNameLow);
FcPort1->NoSEEPROMWWNN.High = cpu_to_le32(wwn->NodeNameHigh);
/* set the Ignore SEEPROM WWNs bit */
FcPort1->Flags |=
cpu_to_le32(MPI_FCPORTPAGE1_FLAGS_FORCE_USE_NOSEEPROM_WWNS);
if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
stm_reset_link(priv);
TRACE_EXIT();
return 0;
}
#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_fc_enable_aliases(MPT_STM_PRIV *priv, int num_aliases, int sleep)
{
FCPortPage1_t *FcPort1;
TRACE_ENTRY();
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
FcPort1 = (FCPortPage1_t *)priv->hw->config_buf;
if (set_aliases_in_fcportpage1) {
/* set the number of aliases requested */
FcPort1->NumRequestedAliases = (u8)num_aliases;
} else {
/* make sure the value in the page is low enough */
if (FcPort1->NumRequestedAliases > NUM_ALIASES)
FcPort1->NumRequestedAliases = NUM_ALIASES;
}
if (num_aliases > 0)
FcPort1->TopologyConfig = MPI_FCPORTPAGE1_TOPOLOGY_NLPORT;
if (stm_set_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 1, 0, sleep))
return -1;
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_get_config_page(MPT_STM_PRIV *priv, int type, int number,
int address, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ConfigReply_t *rep;
int ioc_status;
int i;
int length;
TRACE_ENTRY();
rep = &priv->config_rep;
memset(rep, 0, sizeof(*rep));
i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_HEADER,
type, number, address, 0, sleep);
if (i) {
if (!priv->in_reset)
printk(KERN_ERR MYNAM
":%s timed out getting config page header\n", ioc->name);
return -1;
}
if (priv->in_reset) {
printk(KERN_ERR MYNAM
":%s reset while getting config page header\n", ioc->name);
return -1;
}
ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
if (type > MPI_CONFIG_PAGETYPE_EXTENDED)
length = le16_to_cpu(rep->ExtPageLength);
else
length = rep->Header.PageLength;
if (ioc_status != MPI_IOCSTATUS_SUCCESS || length == 0) {
if (ioc_status != MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
printk(KERN_ERR MYNAM
":%s failed to get config page header\n", ioc->name);
printk(KERN_ERR MYNAM
":%s IOCStatus = %04x, PageLength = %x\n",
ioc->name, ioc_status, length);
printk(KERN_ERR MYNAM
":%s type = %d, number = %d, address = %x\n",
ioc->name, type, number, address);
}
return -1;
}
i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_READ_CURRENT,
type, number, address, length, sleep);
if (i) {
if (!priv->in_reset) {
printk(KERN_ERR MYNAM
":%s timed out getting config page = %d\n", ioc->name, type);
}
return -1;
}
if (priv->in_reset) {
printk(KERN_ERR MYNAM
":%s reset while getting config page\n", ioc->name);
return -1;
}
ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
if ((type == 6 && number == 0) || (type == 18 && number == 0)) {
/* no error messages, please! */
} else {
printk(KERN_ERR MYNAM
":%s failed to get config page\n", ioc->name);
printk(KERN_ERR MYNAM
":%s IOCStatus = %04x, PageLength = %x\n",
ioc->name, ioc_status, length);
printk(KERN_ERR MYNAM
":%s type = %d, number = %d, address = %x\n",
ioc->name, type, number, address);
}
return -1;
}
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)priv->hw->config_buf;
int i;
TRACE(TRACE_MPI, "%s config page %02x/%02x/%08x read",
ioc->name, type, number, address);
for (i = 0; i < length; i++) {
TRACE(TRACE_MPI, "%s page[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_set_config_page(MPT_STM_PRIV *priv, int type, int number,
int address, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
ConfigReply_t *rep;
int ioc_status;
int i;
int length;
TRACE_ENTRY();
rep = &priv->config_rep;
memset(rep, 0, sizeof(*rep));
i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_HEADER,
type, number, address, 0, sleep);
if (i) {
if (!priv->in_reset) {
printk(KERN_ERR MYNAM
":%s timed out getting config page header\n", ioc->name);
}
return -1;
}
if (priv->in_reset) {
printk(KERN_ERR MYNAM
":%s reset while getting config page header\n", ioc->name);
return -1;
}
ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
if (type > MPI_CONFIG_PAGETYPE_EXTENDED)
length = le16_to_cpu(rep->ExtPageLength);
else
length = rep->Header.PageLength;
if (ioc_status != MPI_IOCSTATUS_SUCCESS || length == 0) {
if (ioc_status != MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
printk(KERN_ERR MYNAM
":%s failed to get config page header\n", ioc->name);
printk(KERN_ERR MYNAM
":%s IOCStatus = %04x, PageLength = %x\n",
ioc->name, ioc_status, length);
printk(KERN_ERR MYNAM
":%s type = %d, number = %d, address = %x\n",
ioc->name, type, number, address);
}
return -1;
}
*(ConfigPageHeader_t *)priv->hw->config_buf = rep->Header;
i = stm_do_config_action(priv, MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT,
type, number, address, length, sleep);
if (i) {
if (!priv->in_reset)
printk(KERN_ERR MYNAM
":%s timed out setting config page\n", ioc->name);
return -1;
}
if (priv->in_reset) {
printk(KERN_ERR MYNAM
":%s reset while setting config page\n", ioc->name);
return -1;
}
ioc_status = le16_to_cpu(rep->IOCStatus) & MPI_IOCSTATUS_MASK;
if (ioc_status != MPI_IOCSTATUS_SUCCESS) {
printk(KERN_ERR MYNAM
":%s failed to set config page\n", ioc->name);
printk(KERN_ERR MYNAM
":%s IOCStatus = %04x, PageLength = %x\n",
ioc->name, ioc_status, length);
printk(KERN_ERR MYNAM
":%s type = %d, number = %d, address = %x\n",
ioc->name, type, number, address);
printk(KERN_ERR MYNAM
":%s Header = %08x\n",
ioc->name, le32_to_cpu(*(u32 *)priv->hw->config_buf));
return -1;
}
#ifdef CONFIG_SCST_TRACING
if (trace_mpi) {
u32 *p = (u32 *)priv->hw->config_buf;
int i;
TRACE(TRACE_MPI, "%s config page %02x/%02x/%08x written",
ioc->name, type, number, address);
for (i = 0; i < length; i++) {
TRACE(TRACE_MPI, "%s page[%02x] = %08x",
ioc->name, i * 4, le32_to_cpu(p[i]));
}
}
#endif
TRACE_EXIT();
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int stm_do_config_action(MPT_STM_PRIV *priv, int action, int type,
int number, int address, int length, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
MPT_FRAME_HDR *mf;
Config_t *req;
MPT_STM_SIMPLE *sge_simple;
dma_addr_t dma_addr;
int ret;
TRACE_ENTRY();
if (priv->in_reset) {
printk(KERN_ERR MYNAM ":%s reset while doing config action %x\n",
ioc->name, action);
return -1;
}
/*
* get a message frame, and send the config action request
*/
mf = priv->config_mf;
if (mf == NULL) {
mf = mpt_msg_frame_alloc(ioc, -1);
if (mf == NULL) {
printk(KERN_ERR MYNAM
":%s failed to get message frame\n", ioc->name);
return -1;
} else {
TRACE_DBG(
"%s in stm_do_config_action, got mf index %d",
ioc->name, MF_TO_INDEX(mf));
priv->config_mf = mf;
}
}
req = (Config_t *)mf;
memset(req, 0, sizeof(*req));
req->Function = MPI_FUNCTION_CONFIG;
req->Action = (u8)action;
if (action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
req->Header = *(ConfigPageHeader_t *)priv->hw->config_buf;
} else {
if (type > MPI_CONFIG_PAGETYPE_EXTENDED) {
req->Header.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
req->ExtPageType = (u8)type;
req->ExtPageLength = cpu_to_le16(length);
} else {
req->Header.PageType = (u8)type;
}
req->Header.PageNumber = (u8)number;
req->Header.PageLength = (u8)length;
}
req->PageAddress = cpu_to_le32(address);
if (length) {
sge_simple = (MPT_STM_SIMPLE *)&req->PageBufferSGE;
sge_simple->FlagsLength = cpu_to_le32((length * 4) |
MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING));
if (action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
sge_simple->FlagsLength |=
cpu_to_le32(MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_HOST_TO_IOC));
}
dma_addr = priv->hw_dma + ((u8 *)priv->hw->config_buf - (u8 *)priv->hw);
stm_set_dma_addr(sge_simple->Address, dma_addr);
}
#if 1
priv->config_pending = 1;
mpt_put_msg_frame(stm_context, _IOC_ID, (MPT_FRAME_HDR *)req);
ret = stm_wait_for(priv, &priv->config_pending, 10, sleep);
#else
ret = mpt_handshake_req_reply_wait(ioc, sizeof(*req), (u32 *)req,
sizeof(priv->config_rep),
(u16 *)&priv->config_rep, 10, sleep);
#endif
TRACE_EXIT_RES(ret);
return ret;
}
static void stm_wait(MPT_STM_PRIV *priv, int milliseconds, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
TRACE_ENTRY();
if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
printk(KERN_ERR MYNAM
":%s IOC is not operational (doorbell = %x)\n",
ioc->name, mpt_GetIocState(ioc, 0));
} else {
if (sleep == CAN_SLEEP) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(milliseconds);
} else {
#ifndef __linux__
if (priv->poll_enabled)
_mpt_poll(priv->ioc);
#endif
mdelay(milliseconds);
}
}
TRACE_EXIT();
}
static int
stm_wait_for(MPT_STM_PRIV *priv, volatile int *flag, int seconds, int sleep)
{
MPT_ADAPTER *ioc = priv->ioc;
int i;
TRACE_ENTRY();
for (i = 0; i < seconds * ((sleep == CAN_SLEEP) ? HZ : 1000); i++) {
if (!(*flag))
return 0;
if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
printk(KERN_ERR MYNAM
":%s IOC is not operational (doorbell = %x)\n",
ioc->name, mpt_GetIocState(ioc, 0));
return -1;
}
if (sleep == CAN_SLEEP) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
} else {
#ifndef __linux__
if (priv->poll_enabled)
_mpt_poll(priv->ioc);
#endif
mdelay(1);
}
}
/* timed out, so return failure */
printk(KERN_ERR MYNAM ":%s timed out in stm_wait_for!\n", ioc->name);
TRACE_EXIT();
return -1;
}
static int __init _mpt_stm_init(void)
{
static char function_name[sizeof(__func__)];
int i;
TRACE_ENTRY();
if (function_name[0] == '\0')
strcpy(function_name, __func__);
for (i = 0; i < MPT_MAX_ADAPTERS; i++)
mpt_stm_priv[i] = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
/*
* See also patch "mptfusion: Extra debug prints added relavent to
* Device missing delay error handling" (commit ID
* 213aaca3e5727f3eb56002b04a1405db34a54ed8).
*/
stm_context = mpt_register(stm_reply, MPTSTM_DRIVER);
#else
stm_context = mpt_register(stm_reply, MPTSTM_DRIVER, function_name);
#endif
if (stm_context < 0) {
printk(KERN_ERR MYNAM
": failed to register with MPT driver core\n");
return -EBUSY;
}
if (mpt_event_register(stm_context, stm_event_process)) {
printk(KERN_WARNING MYNAM
": failed to register for event notification\n");
}
if (mpt_reset_register(stm_context, stm_reset_process)) {
printk(KERN_WARNING MYNAM
": failed to register for reset process\n");
}
TRACE_DBG(": assigned context of %d", stm_context);
TRACE_EXIT();
return 0;
}
static int mpt_stm_adapter_install(MPT_ADAPTER *ioc)
{
MPT_STM_PRIV *priv;
int max_aliases;
TRACE_ENTRY();
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL) {
printk(KERN_ERR MYNAM
":%s failed to allocate private structure\n", ioc->name);
return -ENOMEM;
}
memset(priv, 0, sizeof(*priv));
if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET)
priv->enable_target_mode = 1;
priv->ioc = ioc;
priv->num_sge_chain = ioc->req_sz / sizeof(MPT_STM_SIMPLE);
priv->num_sge_target_assist = (ioc->req_sz -
offsetof(TargetAssistRequest_t, SGL)) / sizeof(MPT_STM_SIMPLE);
priv->num_cmd_buffers = NUM_CMD_BUFFERS;
if (priv->num_cmd_buffers > ioc->pfacts[0].MaxPostedCmdBuffers)
priv->num_cmd_buffers = ioc->pfacts[0].MaxPostedCmdBuffers;
if (priv->num_cmd_buffers > ioc->req_depth - 16)
priv->num_cmd_buffers = ioc->req_depth - 16;
priv->num_els_buffers = NUM_ELS_BUFFERS;
priv->poll_enabled = 1;
priv->hw = pci_alloc_consistent(ioc->pcidev, sizeof(*priv->hw),
&priv->hw_dma);
if (priv->hw == NULL) {
printk(KERN_ERR MYNAM
":%s failed to allocate hardware structure\n", ioc->name);
kfree(priv);
return -1;
}
memset(priv->hw, 0, sizeof(*priv->hw));
printk(KERN_INFO ":%s priv = %p, priv->hw = %p, priv->hw_dma = %llx\n",
ioc->name, priv, priv->hw, (u64)priv->hw_dma);
mpt_stm_priv[ioc->id] = priv;
max_aliases = 0;
if (IsScsi(priv))
max_aliases = 14;
if (IsFc(priv)) {
memset(priv->hw->config_buf, 0, sizeof(priv->hw->config_buf));
if (!stm_get_config_page(priv, MPI_CONFIG_PAGETYPE_FC_PORT, 0, 0,
NO_SLEEP)) {
max_aliases =
((FCPortPage0_t *)priv->hw->config_buf)->MaxAliasesSupported;
}
}
if (num_aliases < max_aliases)
priv->num_aliases = num_aliases;
else
priv->num_aliases = max_aliases;
TRACE_EXIT();
return 0;
}
static int mpt_stm_adapter_online(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc;
int i;
TRACE_ENTRY();
ioc = priv->ioc;
priv->fcp2_capable = 0;
#ifdef MPT_STM_ALLOW_FCP2
switch (ioc->pcidev->device) {
case MPI_MANUFACTPAGE_DEVICEID_FC919:
case MPI_MANUFACTPAGE_DEVICEID_FC929:
if (ioc->facts.FWVersion.Word >= 0x01630002) {
/* firmware version 1.99.00.02 (and later) is FCP-2 capable */
priv->fcp2_capable = 1;
}
break;
case MPI_MANUFACTPAGE_DEVICEID_FC919X:
case MPI_MANUFACTPAGE_DEVICEID_FC929X:
if (ioc->facts.FWVersion.Word >= 0x01013200) {
/* firmware version 1.01.50 (and later) is FCP-2 capable */
priv->fcp2_capable = 1;
}
break;
case MPI_MANUFACTPAGE_DEVICEID_FC939X:
case MPI_MANUFACTPAGE_DEVICEID_FC949X:
priv->fcp2_capable = 1;
break;
default:
break;
}
#endif
priv->port_enable_pending = 0;
priv->target_mode_abort_pending = 0;
priv->link_serv_abort_pending = 0;
priv->fc_primitive_send_pending = 0;
priv->config_pending = 0;
priv->config_mf = NULL;
priv->port_flags = 0;
priv->port_speed = 0;
priv->in_reset = 0;
priv->poll_enabled = 1;
for (i = 0; i < priv->num_cmd_buffers; i++)
priv->io_state[i] = 0;
if (IsScsi(priv))
stm_scsi_configuration(priv, NO_SLEEP);
if (IsSas(priv))
stm_sas_configuration(priv, NO_SLEEP);
if (priv->enable_target_mode) {
if (IsSas(priv)) {
stm_cmd_buf_post_base(priv, 1);
} else {
for (i = 0; i < priv->num_cmd_buffers; i++)
stm_cmd_buf_post(priv, i);
}
if (IsFc(priv)) {
for (i = 0; i < priv->num_els_buffers; i++)
stm_link_serv_buf_post(priv, i);
stm_fc_enable_els(priv, RSCN, NO_SLEEP);
#ifdef STMAPP_VERIFY_OXIDS
stm_fc_enable_target_mode_oxid(priv, NO_SLEEP);
#endif
stm_fc_enable_aliases(priv, priv->num_aliases, NO_SLEEP);
}
}
stm_port_enable(priv);
if (IsFc(priv))
stm_reset_link(priv);
if (IsFc(priv)) {
/* wait up to 5 seconds for the link to come up */
for (i = 0; i < 50; i++) {
stm_fc_configuration(priv, NO_SLEEP);
if (priv->port_flags != MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT)
break;
mdelay(100);
}
}
TRACE_EXIT();
return 0;
}
static void _mpt_stm_exit(void)
{
TRACE_ENTRY();
if (stm_context > 0) {
mpt_reset_deregister(stm_context);
mpt_event_deregister(stm_context);
mpt_device_driver_deregister(MPTSTM_DRIVER);
mpt_deregister(stm_context);
stm_context = 0;
}
TRACE_EXIT();
}
static void mpt_stm_adapter_dispose(MPT_STM_PRIV *priv)
{
MPT_ADAPTER *ioc;
TRACE_ENTRY();
priv->exiting = 1;
ioc = priv->ioc;
if (mpt_GetIocState(ioc, 1) == MPI_IOC_STATE_OPERATIONAL) {
if (priv->enable_target_mode && priv->tgt->target_enable) {
stm_target_mode_abort(priv);
if (IsFc(priv))
stm_link_serv_abort(priv);
}
}
mpt_stm_priv[ioc->id] = NULL;
if (priv->hw != NULL)
pci_free_consistent(ioc->pcidev, sizeof(*priv->hw),
priv->hw, priv->hw_dma);
if (priv->config_mf != NULL)
mpt_free_msg_frame(_HANDLE_IOC_ID, priv->config_mf);
kfree(priv);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_set_status(MPT_STM_PRIV *priv, CMD *cmd, int status)
{
TRACE_ENTRY();
if (IsScsi(priv)) {
SCSI_RSP *rsp = (SCSI_RSP *)cmd->rsp;
rsp->Status = (u8)status;
} else if (IsSas(priv)) {
SSP_RSP *rsp = (SSP_RSP *)cmd->rsp;
rsp->Status = (u8)status;
} else {
FCP_RSP *rsp = (FCP_RSP *)cmd->rsp;
rsp->FcpStatus = (u8)status;
}
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_abts_process(MPT_STM_PRIV *priv, int rx_id,
LinkServiceBufferPostReply_t *rep, int index)
{
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
volatile int *io_state;
CMD *cmd;
TRACE_ENTRY();
io_state = priv->io_state + rx_id;
if (*io_state & IO_STATE_ABORTED)
return;
if (*io_state & IO_STATE_POSTED)
return;
cmd = priv->hw->cmd_buf + rx_id;
TRACE_DBG("%s index %d: io_state = %x",
ioc->name, rx_id, *io_state);
TRACE_DBG("%s reply_word = %x, alias = %d, lun = %d, tag = %x",
ioc->name, cmd->reply_word, cmd->alias, cmd->lun, cmd->tag);
/*
* if we are processing an SRR, there could be some other flags set
* in io_state that we need to get rid of; ABTS overrides SRR
*/
*io_state &= ~IO_STATE_REQUEST_ABORTED;
*io_state &= ~IO_STATE_REISSUE_REQUEST;
*io_state &= ~IO_STATE_ADJUST_OFFSET;
*io_state &= ~IO_STATE_CONVERT_TA_TO_TSS;
*io_state &= ~IO_STATE_REDO_COMMAND;
/*
* if we get here, the firmware thinks a command is active,
* so it should be aborted
*/
TRACE_DBG("%s index %d needs to be aborted", ioc->name, rx_id);
stm_target_mode_abort_command(priv, cmd->reply_word, rx_id);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_srr_process(MPT_STM_PRIV *priv, int rx_id, int r_ctl,
u32 offset, LinkServiceBufferPostReply_t *rep,
int index)
{
#ifdef CONFIG_SCST_DEBUG
MPT_ADAPTER *ioc = priv->ioc;
#endif
FC_ELS *fc_els_buf;
volatile int *io_state;
u32 msg_context;
CMD *cmd;
int need_abort = 0;
u32 rel_off;
u32 dat_len;
u32 adjust;
u32 block_size = 512;
TRACE_ENTRY();
fc_els_buf = &priv->hw->fc_link_serv_buf[index];
io_state = priv->io_state + rx_id;
if (*io_state & IO_STATE_ABORTED) {
/*
* the initiator wants to continue an I/O that we're aborting;
* just accept this SRR, and continue aborting
*/
fc_els_buf->fc_els[0] = cpu_to_be32(0x02000000);
stm_send_els(priv, rep, index, 4);
return;
}
if (*io_state & IO_STATE_POSTED) {
/*
* the firmware should prevent this from happening, but if it does,
* reject this SRR with "invalid OX_ID/RX_ID combination"
*/
fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
fc_els_buf->fc_els[1] = cpu_to_be32(0x00090300);
stm_send_els(priv, rep, index, 8);
return;
}
cmd = priv->hw->cmd_buf + rx_id;
TRACE_DBG("%s index %d: r_ctl = %x, io_state = %x",
ioc->name, rx_id, r_ctl, *io_state);
TRACE_DBG("%s reply_word = %x, alias = %d, lun = %d, tag = %x",
ioc->name, cmd->reply_word, cmd->alias, cmd->lun, cmd->tag);
if (*io_state & (IO_STATE_DATA_SENT | IO_STATE_STATUS_SENT)) {
/*
* if we get here, the firmware thinks a request is active,
* so it should be aborted
*/
TRACE_DBG("%s index %d needs to be aborted",
ioc->name, rx_id);
if (*io_state & IO_STATE_DATA_SENT) {
TargetAssistRequest_t *req;
req = (TargetAssistRequest_t *)priv->current_mf[rx_id];
msg_context = le32_to_cpu(req->MsgContext);
rel_off = le32_to_cpu(req->RelativeOffset);
dat_len = le32_to_cpu(req->DataLength);
TRACE_DBG("%s SRR offset = %x, TA offset = %x, TA length = %x",
ioc->name, offset, rel_off, dat_len);
if (r_ctl == 1 || r_ctl == 5) {
if (offset < rel_off && (offset % block_size) == 0) {
TRACE_DBG("%s request can be reissued",
ioc->name);
adjust = rel_off + dat_len - offset;
*io_state |= IO_STATE_INCOMPLETE;
*io_state |= IO_STATE_REDO_COMMAND;
*io_state |= IO_STATE_REISSUE_REQUEST;
need_abort = 1;
}
if (offset >= rel_off && offset <= rel_off + dat_len) {
TRACE_DBG("%s request can be reissued",
ioc->name);
if (offset != rel_off) {
if (offset != rel_off + dat_len) {
/*cmd->offset = offset;*/
*io_state |= IO_STATE_ADJUST_OFFSET;
} else {
*io_state |= IO_STATE_CONVERT_TA_TO_TSS;
}
}
*io_state |= IO_STATE_REISSUE_REQUEST;
need_abort = 1;
}
} else if (r_ctl == 7) {
if (*io_state & IO_STATE_STATUS_SENT) {
TRACE_DBG("%s request can be reissued",
ioc->name);
*io_state |= IO_STATE_CONVERT_TA_TO_TSS;
*io_state |= IO_STATE_REISSUE_REQUEST;
need_abort = 1;
}
if (*io_state & IO_STATE_STATUS_DEFERRED) {
TRACE_DBG("%s request can be reissued",
ioc->name);
*io_state |= IO_STATE_REISSUE_REQUEST;
need_abort = 1;
}
} else {
TRACE_DBG("%s request cannot be reissued",
ioc->name);
}
} else {
TargetStatusSendRequest_t *req;
req = (TargetStatusSendRequest_t *)priv->current_mf[rx_id];
msg_context = le32_to_cpu(req->MsgContext);
if (r_ctl == 7) {
TRACE_DBG("%s request can be reissued",
ioc->name);
*io_state |= IO_STATE_REISSUE_REQUEST;
need_abort = 1;
} else {
TRACE_DBG("%s request cannot be reissued",
ioc->name);
}
}
if (need_abort) {
stm_target_mode_abort_request(priv, cmd->reply_word, msg_context,
rx_id);
}
}
if (*io_state & IO_STATE_REISSUE_REQUEST) {
/*
* if we can continue this I/O, accept this SRR
*/
fc_els_buf->fc_els[0] = cpu_to_be32(0x02000000);
stm_send_els(priv, rep, index, 4);
} else {
/*
* we can't continue the I/O, so reject this SRR with "unable to
* supply requested data"
*/
fc_els_buf->fc_els[0] = cpu_to_be32(0x01000000);
fc_els_buf->fc_els[1] = cpu_to_be32(0x00092a00);
stm_send_els(priv, rep, index, 8);
}
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_srr_convert_ta_to_tss(MPT_STM_PRIV *priv, int index)
{
MPT_ADAPTER *ioc = priv->ioc;
volatile int *io_state;
CMD *cmd;
u32 reply_word;
int lun;
int tag;
int flags;
TRACE_ENTRY();
io_state = priv->io_state + index;
cmd = priv->hw->cmd_buf + index;
reply_word = cmd->reply_word;
lun = cmd->lun;
tag = cmd->tag;
*io_state &= ~IO_STATE_DATA_SENT;
*io_state &= ~IO_STATE_STATUS_SENT;
mpt_free_msg_frame(_HANDLE_IOC_ID, priv->current_mf[index]);
flags = TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS;
if (*io_state & IO_STATE_AUTO_REPOST)
flags |= TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER;
stm_send_target_status(priv, reply_word, index, flags, lun, tag);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_srr_adjust_offset(MPT_STM_PRIV *priv, int index)
{
CMD *cmd;
TargetAssistRequest_t *req;
u32 old_offset;
u32 new_offset = 0;
u32 offset;
MPT_STM_SIMPLE *sge_simple;
MPT_STM_CHAIN *sge_chain = NULL;
u32 flags_length;
int type;
int old_length;
int new_length;
int length;
dma_addr_t dma_addr;
int n;
TRACE_ENTRY();
cmd = priv->hw->cmd_buf + index;
req = (TargetAssistRequest_t *)priv->current_mf[index];
old_offset = le32_to_cpu(req->RelativeOffset);
/* walk the SGL, skipping along until we get to the right offset */
offset = old_offset;
sge_simple = (MPT_STM_SIMPLE *)&req->SGL;
n = 0;
while (1) {
flags_length = le32_to_cpu(sge_simple->FlagsLength);
type = MPI_SGE_GET_FLAGS(flags_length) & MPI_SGE_FLAGS_ELEMENT_MASK;
if (type == MPI_SGE_FLAGS_CHAIN_ELEMENT) {
if (sge_chain == NULL) {
sge_chain = (MPT_STM_CHAIN *)sge_simple;
stm_get_dma_addr(dma_addr, sge_chain->Address);
sge_simple = (MPT_STM_SIMPLE *)
((u8 *)priv->hw + (dma_addr - priv->hw_dma));
} else {
sge_chain = (MPT_STM_CHAIN *)sge_simple;
sge_simple = (MPT_STM_SIMPLE *)(sge_chain + 1);
}
n = 0;
} else {
length = MPI_SGE_LENGTH(flags_length);
if (offset + length > new_offset)
break;
offset += length;
sge_simple++;
n++;
}
}
/* fix up the current SGE */
flags_length -= new_offset - offset;
sge_simple->FlagsLength = le32_to_cpu(flags_length);
stm_get_dma_addr(dma_addr, sge_simple->Address);
dma_addr += new_offset - offset;
stm_set_dma_addr(sge_simple->Address, dma_addr);
/* if we have skipped any SGEs, we need to use a chain to point to */
/* the new "first" SGE */
if (sge_simple != (MPT_STM_SIMPLE *)&req->SGL) {
/* see if we've already walked past a chain */
if (sge_chain == NULL) {
/* all we have to do here is move the SGEs in the request frame */
memmove(&req->SGL, sge_simple,
(priv->num_sge_target_assist - n) *
sizeof(MPT_STM_SIMPLE));
if (req->ChainOffset != 0)
req->ChainOffset -= n * sizeof(MPT_STM_SIMPLE) / sizeof(u32);
} else {
/* we have to build a chain on top of the first SGE */
length = le16_to_cpu(sge_chain->Length) -
n * sizeof(MPT_STM_SIMPLE);
offset = sge_chain->NextChainOffset;
sge_chain = (MPT_STM_CHAIN *)&req->SGL;
sge_chain->Length = cpu_to_le16(length);
sge_chain->NextChainOffset = (u8)offset;
sge_chain->Flags =
(u8)(MPI_SGE_FLAGS_CHAIN_ELEMENT |
MPI_SGE_FLAGS_MPT_STM_ADDRESSING);
dma_addr = priv->hw_dma + ((u8 *)sge_simple - (u8 *)priv->hw);
stm_set_dma_addr(sge_chain->Address, dma_addr);
req->ChainOffset = (u32 *)sge_chain - (u32 *)req;
}
}
/* fix up the offset and length */
old_length = le32_to_cpu(req->DataLength);
new_length = old_length - (new_offset - old_offset);
req->RelativeOffset = cpu_to_le32(new_offset);
req->DataLength = cpu_to_le32(new_length);
TRACE_EXIT();
}
static void stmapp_target_error_prioprity_io(MPT_STM_PRIV *priv, u32 reply_word,
int index, int status, int reason,
int lun, int tag)
{
MPT_ADAPTER *ioc = priv->ioc;
struct mpt_cmd *mpt_cmd;
struct scst_cmd *scst_cmd;
volatile int *io_state;
CMD *cmd;
/*
* we want stm_target_cleanup to do everything except repost the
* command buffer, so fake it out a bit
*/
TRACE_ENTRY();
io_state = priv->io_state + index;
*io_state |= IO_STATE_AUTO_REPOST;
scst_cmd = priv->scst_cmd[index];
mpt_cmd = (struct mpt_cmd *)scst_cmd_get_tgt_priv(scst_cmd);
mpt_cmd->state = MPT_STATE_PROCESSED;
stm_target_cleanup(priv, index);
*io_state = IO_STATE_HIGH_PRIORITY;
printk(KERN_ERR MYNAM ": HIGH_PRIORITY %s\n", __func__);
cmd = priv->hw->cmd_buf + index;
memset(cmd->rsp, 0, sizeof(cmd->rsp));
switch (reason) {
case PRIORITY_REASON_CMD_PARITY_ERR:
printk(KERN_ERR MYNAM ":%s "
"detected parity error during Command phase\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x47, 0x00);
break;
case PRIORITY_REASON_MSG_OUT_PARITY_ERR:
printk(KERN_ERR MYNAM ":%s "
"detected parity error during Message Out phase\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x43, 0x00);
break;
case PRIORITY_REASON_CMD_CRC_ERR:
printk(KERN_ERR MYNAM ":%s "
"detected CRC error while receiving CMD_IU\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x47, 0x03);
break;
case PRIORITY_REASON_PROTOCOL_ERR:
printk(KERN_ERR MYNAM ":%s "
"received Initiator Detected Error message\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x48, 0x00);
break;
case PRIORITY_REASON_DATA_OUT_PARITY_ERR:
printk(KERN_ERR MYNAM ":%s "
"detected parity error during Data Out phase\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x47, 0x02);
break;
case PRIORITY_REASON_DATA_OUT_CRC_ERR:
printk(KERN_ERR MYNAM ":%s "
"detected CRC error during Data Out phase\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x47, 0x01);
break;
default:
printk(KERN_ERR MYNAM ":%s unknown PriorityReason = %x\n",
ioc->name, reason);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x00, 0x00);
}
stm_send_target_status(priv, reply_word, index, 0, lun, tag);
TRACE_EXIT();
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void stmapp_target_error(MPT_STM_PRIV *priv, u32 reply_word, int index,
int status, int reason)
{
MPT_ADAPTER *ioc = priv->ioc;
volatile int *io_state;
CMD *cmd;
int lun = 0;
int tag = 0;
int init_index = 0;
TRACE_ENTRY();
TRACE_DBG("%s target error, index %d, status %x, reason %x",
ioc->name, index, status, reason);
io_state = priv->io_state + index;
init_index = GET_INITIATOR_INDEX(reply_word);
if (*io_state & IO_STATE_DATA_SENT) {
TargetAssistRequest_t *req;
req = (TargetAssistRequest_t *)priv->current_mf[index];
lun = get2bytes(req->LUN, 0);
tag = req->QueueTag;
}
if (*io_state & IO_STATE_STATUS_SENT) {
TargetStatusSendRequest_t *req;
req = (TargetStatusSendRequest_t *)priv->current_mf[index];
lun = get2bytes(req->LUN, 0);
tag = req->QueueTag;
}
/*
* if the status is Target Priority I/O, the I/O is still
* active and a response is needed
*/
if (status == MPI_IOCSTATUS_TARGET_PRIORITY_IO) {
stmapp_target_error_prioprity_io(priv, reply_word, index, status,
reason, lun, tag);
return;
}
/*
* if the status is Target Transfer Count Mismatch, Target Data Offset
* Error, Target Too Much Write Data, Target IU Too Short, EEDP Guard
* Error, EEDP Reference Tag Error, or EEDP Application Tag Error, thes
* I/O is still active and a response is needed
*/
if (status == MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH ||
status == MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR ||
status == MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA ||
status == MPI_IOCSTATUS_TARGET_IU_TOO_SHORT ||
status == MPI_IOCSTATUS_EEDP_GUARD_ERROR ||
status == MPI_IOCSTATUS_EEDP_REF_TAG_ERROR ||
status == MPI_IOCSTATUS_EEDP_APP_TAG_ERROR) {
/*
* we want stm_target_cleanup to do everything except repost the
* command buffer, so fake it out a bit
*/
*io_state |= IO_STATE_AUTO_REPOST;
stm_target_cleanup(priv, index);
*io_state = 0;
cmd = priv->hw->cmd_buf + index;
memset(cmd->rsp, 0, sizeof(cmd->rsp));
switch (status) {
case MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
printk(KERN_ERR MYNAM ":%s transfer count mismatch\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x4b, 0x00);
break;
case MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
printk(KERN_ERR MYNAM ":%s data offset error\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x4b, 0x05);
break;
case MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
printk(KERN_ERR MYNAM ":%s too much write data\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x4b, 0x02);
break;
case MPI_IOCSTATUS_TARGET_IU_TOO_SHORT:
printk(KERN_ERR MYNAM ":%s IU too short\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x0e, 0x01);
break;
case MPI_IOCSTATUS_EEDP_GUARD_ERROR:
printk(KERN_ERR MYNAM ":%s EEDP Guard Error\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x10, 0x01);
break;
case MPI_IOCSTATUS_EEDP_REF_TAG_ERROR:
printk(KERN_ERR MYNAM ":%s EEDP Reference Tag Error\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x10, 0x03);
break;
case MPI_IOCSTATUS_EEDP_APP_TAG_ERROR:
printk(KERN_ERR MYNAM ":%s EEDP Application Tag Error\n",
ioc->name);
stmapp_set_sense_info(priv, cmd,
SK_ABORTED_COMMAND, 0x10, 0x02);
break;
}
stm_send_target_status(priv, reply_word, index, 0, lun, tag);
return;
}
/*
* the SCSI firmware has a bug, where if the Status Data Not Sent error
* is returned, and the original command requested Auto Repost, the
* command buffer is reposted, even though an error is generated; so,
* ignore the error here, so that we don't post this command buffer again
* (that is, treat this as a successful completion, which is what it is)
*/
/*
* Allow STATUS_SENT status to go through also, this is the
* result of an attempt to send a check condition with
* attached sense bytes.
* The IOC knows it can't send status and sense over a
* traditional SCSI cable (if non-packetized), so we should
* treat this as a successful completion, manually repost the
* command to the IOC, and free the SCST command.
*/
if (IsScsi(priv) && (status == MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT)) {
if ((*io_state & IO_STATE_AUTO_REPOST) ||
(*io_state & IO_STATE_STATUS_SENT)) {
/* if we know we were attempting to send status and sense
* simultaneously, indicate that we failed */
if (atomic_read(&priv->pending_sense[init_index]) ==
MPT_STATUS_SENSE_ATTEMPT) {
atomic_set(&priv->pending_sense[init_index],
MPT_STATUS_SENSE_NOT_SENT);
}
stm_tgt_reply(ioc, reply_word);
TRACE_EXIT();
return;
}
}
*io_state &= ~IO_STATE_AUTO_REPOST;
stm_target_cleanup(priv, index);
TRACE_EXIT();
}
static void stmapp_set_sense_info(MPT_STM_PRIV *priv, CMD *cmd, int sense_key,
int asc, int ascq)
{
u8 *info;
TRACE_ENTRY();
if (IsScsi(priv)) {
SCSI_RSP *rsp = (SCSI_RSP *)cmd->rsp;
rsp->Status = STS_CHECK_CONDITION;
rsp->Valid |= SCSI_SENSE_LEN_VALID;
rsp->SenseDataListLength = cpu_to_be32(14);
info = rsp->SenseData;
if (rsp->Valid & SCSI_RSP_LEN_VALID)
info += be32_to_cpu(rsp->PktFailuresListLength);
} else if (IsSas(priv)) {
SSP_RSP *rsp = (SSP_RSP *)cmd->rsp;
rsp->Status = STS_CHECK_CONDITION;
rsp->DataPres |= SSP_SENSE_LEN_VALID;
rsp->SenseDataLength = cpu_to_be32(14);
info = rsp->ResponseSenseData;
if (rsp->DataPres & SSP_RSP_LEN_VALID)
info += be32_to_cpu(rsp->ResponseDataLength);
} else {
FCP_RSP *rsp = (FCP_RSP *)cmd->rsp;
rsp->FcpStatus = STS_CHECK_CONDITION;
rsp->FcpFlags |= FCP_SENSE_LEN_VALID;
rsp->FcpSenseLength = cpu_to_be32(14);
info = rsp->FcpSenseData - sizeof(rsp->FcpResponseData);
if (rsp->FcpFlags & FCP_RSP_LEN_VALID)
info += be32_to_cpu(rsp->FcpResponseLength);
}
info[0] = 0x70;
info[2] = (u8)sense_key;
info[7] = 6;
info[12] = (u8)asc;
info[13] = (u8)ascq;
TRACE_EXIT();
}
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
#define MPT_PROC_LOG_ENTRY_NAME "trace_level"
#ifdef CONFIG_SCST_PROC
#include <linux/proc_fs.h>
static int mpt_log_info_show(struct seq_file *seq, void *v)
{
int res = 0;
TRACE_ENTRY();
res = scst_proc_log_entry_read(seq, trace_flag, NULL);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t mpt_proc_log_entry_write(struct file *file,
const char __user *buf,
size_t length, loff_t *off)
{
int res = 0;
TRACE_ENTRY();
res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
SCST_DEFAULT_MPT_LOG_FLAGS, NULL);
TRACE_EXIT_RES(res);
return res;
}
static struct scst_proc_data mpt_log_proc_data = {
SCST_DEF_RW_SEQ_OP(mpt_proc_log_entry_write)
.show = mpt_log_info_show,
};
#endif
#endif
static int mpt_proc_log_entry_build(struct scst_tgt_template *templ)
{
int res = 0;
#ifdef CONFIG_SCST_PROC
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
struct proc_dir_entry *p, *root;
TRACE_ENTRY();
root = scst_proc_get_tgt_root(templ);
if (root) {
mpt_log_proc_data.data = (void *)templ->name;
p = scst_create_proc_entry(root, MPT_PROC_LOG_ENTRY_NAME,
&mpt_log_proc_data);
if (p == NULL) {
PRINT_ERROR("Not enough memory to register "
"target driver %s entry %s in /proc",
templ->name, MPT_PROC_LOG_ENTRY_NAME);
res = -ENOMEM;
goto out;
}
}
out:
TRACE_EXIT_RES(res);
#endif
#endif
return res;
}
static void mpt_proc_log_entry_clean(struct scst_tgt_template *templ)
{
#ifdef CONFIG_SCST_PROC
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
struct proc_dir_entry *root;
TRACE_ENTRY();
root = scst_proc_get_tgt_root(templ);
if (root)
remove_proc_entry(MPT_PROC_LOG_ENTRY_NAME, root);
TRACE_EXIT();
#endif
#endif
}
static int __init mpt_target_init(void)
{
int res = 0;
TRACE_ENTRY();
res = scst_register_target_template(&tgt_template);
if (res < 0)
goto out;
res = mpt_proc_log_entry_build(&tgt_template);
if (res < 0)
goto out_unreg_target;
out:
TRACE_EXIT_RES(res);
return res;
out_unreg_target:
scst_unregister_target_template(&tgt_template);
goto out;
}
static void __exit mpt_target_exit(void)
{
TRACE_ENTRY();
mpt_proc_log_entry_clean(&tgt_template);
scst_unregister_target_template(&tgt_template);
_mpt_stm_exit();
TRACE_EXIT();
return;
}
module_init(mpt_target_init);
module_exit(mpt_target_exit);
MODULE_AUTHOR("Hu Gang <hugang@soulinfo.com>");
MODULE_DESCRIPTION("Fusion MPT SCSI Target Mode Driver for SCST Version 0.1");
MODULE_LICENSE("GPL");