Files
scst/qla_isp/linux/isp_cb_ops.c
Dotan Barak 6880b800ef Major rewrite of DMA mapping code to reduce code duplication
and to allow some debug and enhancements that will be easier
soon.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@679 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2009-02-23 00:13:25 +00:00

933 lines
29 KiB
C

/* $Id: isp_cb_ops.c,v 1.98 2009/02/01 23:49:55 mjacob Exp $ */
/*
* Copyright (c) 1997-2008 by Matthew Jacob
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* Alternatively, this software may be distributed under the terms of the
* the GNU Public License ("GPL") with platforms where the prevalant license
* is the GNU Public License:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of The Version 2 GNU General Public License as published
* by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* Matthew Jacob
* Feral Software
* 421 Laurel Avenue
* Menlo Park, CA 94025
* USA
*
* gplbsd at feral com
*/
/*
* Qlogic ISP Host Adapter procfs and open/close entry points
* proc safe pretty print code courtesy of Gerard Roudier (groudier@free.fr)
*/
#include "isp_linux.h"
#include "isp_ioctl.h"
#ifdef CONFIG_PROC_FS
/*
* 'safe' proc pretty print code
*/
struct info_str {
char *buffer;
int length;
off_t offset;
int pos;
};
static void
copy_mem_info(struct info_str *info, char *data, int len)
{
if (info->pos + len > info->offset + info->length) {
len = info->offset + info->length - info->pos;
}
if (info->pos + len < info->offset) {
info->pos += len;
return;
}
if (info->pos < info->offset) {
off_t partial;
partial = info->offset - info->pos;
data += partial;
info->pos += partial;
len -= partial;
}
if (len > 0) {
memcpy(info->buffer, data, len);
info->pos += len;
info->buffer += len;
}
}
static int copy_info(struct info_str *, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
static int
copy_info(struct info_str *info, const char *fmt, ...)
{
va_list args;
char buf[256];
int len;
va_start(args, fmt);
len = vsprintf(buf, fmt, args);
va_end(args);
copy_mem_info(info, buf, len);
return (len);
}
int
isplinux_proc_info(struct Scsi_Host *shp, char *buf, char **st, off_t off, int len, int io)
{
int i;
struct info_str info;
ispsoftc_t *isp = NULL;
unsigned long flags;
for (i = 0; i < MAX_ISP; i++) {
isp = isplist[i];
if (isp == NULL) {
continue;
}
if (isp->isp_host->host_no == shp->host_no) {
break;
}
}
if (isp == NULL) {
return (-ENODEV);
}
if (io) {
buf[len] = 0;
io = -ENOSYS;
if (strncmp(buf, "debug=", 6) == 0) {
unsigned long debug;
char *p = &buf[6], *q;
debug = simple_strtoul(p, &q, 16);
if (q == &buf[6]) {
isp_prt(isp, ISP_LOGERR, "Garbled Debug Line '%s'", buf);
return (-EINVAL);
}
isp_prt(isp, ISP_LOGINFO, "setting debug level to 0x%lx", debug);
ISP_LOCKU_SOFTC(isp);
isp->isp_dblev = debug;
ISP_UNLKU_SOFTC(isp);
io = len;
} else if (strncmp(buf, "rescan", 6) == 0) {
if (IS_FC(isp)) {
for (io = 0; io < isp->isp_nchan; io++) {
isp_thread_event(isp, ISP_THREAD_FC_RESCAN, FCPARAM(isp, io), 1, __func__, __LINE__);
}
io = len;
}
} else if (strncmp(buf, "lip", 3) == 0) {
if (IS_FC(isp)) {
ISP_LOCKU_SOFTC(isp);
(void) isp_control(isp, ISPCTL_SEND_LIP, 0);
ISP_UNLKU_SOFTC(isp);
io = len;
}
} else if (strncmp(buf, "busreset=", 9) == 0) {
char *p = &buf[6], *q;
int bus = (int) simple_strtoul(p, &q, 16);
if (q == &buf[6]) {
isp_prt(isp, ISP_LOGERR, "Garbled Bus Reset Line '%s'", buf);
return (-EINVAL);
}
ISP_LOCKU_SOFTC(isp);
(void) isp_control(isp, ISPCTL_RESET_BUS, bus);
ISP_UNLKU_SOFTC(isp);
io = len;
} else if (strncmp(buf, "devreset=", 9) == 0) {
char *p = &buf[6], *q;
int dev = (int) simple_strtoul(p, &q, 16);
if (q == &buf[6]) {
isp_prt(isp, ISP_LOGERR, "Garbled Dev Reset Line '%s'", buf);
return (-EINVAL);
}
/* always bus 0 */
ISP_LOCKU_SOFTC(isp);
(void) isp_control(isp, ISPCTL_RESET_DEV, 0, dev);
ISP_UNLKU_SOFTC(isp);
io = len;
} else if (strncmp(buf, "reset", 5) == 0) {
ISP_LOCKU_SOFTC(isp);
isp_reinit(isp);
ISP_UNLKU_SOFTC(isp);
io = len;
} else if (strncmp(buf, "bins", 4) == 0) {
ISP_LOCKU_SOFTC(isp);
memset(isp->isp_osinfo.bins, 0, sizeof (isp->isp_osinfo.bins));
ISP_UNLKU_SOFTC(isp);
io = len;
}
return (io);
}
ISP_LOCKU_SOFTC(isp);
if (st) {
*st = buf;
}
info.buffer = buf;
info.length = len;
info.offset = off;
info.pos = 0;
copy_info(&info, (char *)isplinux_info(isp->isp_host));
#ifdef HBA_VERSION
copy_info(&info, "\n HBA Version %s, built %s, %s", HBA_VERSION, __DATE__, __TIME__);
#endif
copy_info(&info, "\n DEVID %x\n", isp->isp_osinfo.device_id);
copy_info(&info,
" Interrupt Stats:\n"
" total=0x%016llx bogus=0x%016llx\n"
" MboxC=0x%016llx async=0x%016llx\n"
" CRslt=0x%016llx CPost=0x%016llx\n"
" RspnsCHiWater=0x%04x FastPostC_Hiwater=0x%04x\n",
(ull) isp->isp_intcnt, (ull) isp->isp_intbogus, (ull) isp->isp_intmboxc,
(ull) isp->isp_intoasync, (ull) isp->isp_rsltccmplt, (ull) isp->isp_fphccmplt,
isp->isp_rscchiwater, isp->isp_fpcchiwater);
copy_info(&info,
" Request In %d Request Out %d Result %d Nactv %d"
" HiWater %u QAVAIL %d WtQHi %d\n",
isp->isp_reqidx, isp->isp_reqodx, isp->isp_residx, isp->isp_nactive,
isp->isp_osinfo.hiwater, ISP_QAVAIL(isp),
isp->isp_osinfo.wqhiwater);
for (i = 0; i < isp->isp_maxcmds; i++) {
if (isp->isp_xflist[i]) {
copy_info(&info, " %d:%p", i, isp->isp_xflist[i]);
}
}
#ifdef ISP_TARGET_MODE
copy_info(&info, "\n");
for (i = 0; i < isp->isp_maxcmds; i++) {
if (isp->isp_tgtlist[i]) {
copy_info(&info, " %d:%p", i, isp->isp_tgtlist[i]);
}
}
#endif
copy_info(&info, "\n");
if (isp->isp_osinfo.wqnext) {
struct scsi_cmnd *f = isp->isp_osinfo.wqnext;
copy_info(&info, "WaitQ(%d)", isp->isp_osinfo.wqcnt);
while (f) {
copy_info(&info, "->%p", f);
f = (struct scsi_cmnd *) f->host_scribble;
}
copy_info(&info, "\n");
}
if (isp->isp_osinfo.dqnext) {
struct scsi_cmnd *f = isp->isp_osinfo.dqnext;
copy_info(&info, "DoneQ");
while (f) {
copy_info(&info, "->%p", f);
f = (struct scsi_cmnd *) f->host_scribble;
}
copy_info(&info, "\n");
}
// copy_info(&info, "blocked %d qfdelay %d\n", isp->isp_blocked, isp->isp_qfdelay);
copy_info(&info, "\nxfer bins:\n");
copy_info(&info, " <=1024 4096 32768 65536 131072 262144 524288 >0.5M\n");
/* 123456 123456 123456 123456 123456 123456 123456 123456 */
for (i = 0; i < 8; i++) {
copy_info(&info, "% 7ld", isp->isp_osinfo.bins[i]);
}
copy_info(&info, "\n");
#ifdef ISP_TARGET_MODE
if (isp->isp_osinfo.cmds_started || isp->isp_osinfo.cmds_completed) {
copy_info(&info, "\n");
copy_info(&info, "Target cmds started: %llu; Target cmds completed ok: %llu", isp->isp_osinfo.cmds_started, isp->isp_osinfo.cmds_completed);
copy_info(&info, "\n");
}
#endif
if (IS_FC(isp)) {
int chan;
for (chan = 0; chan < isp->isp_nchan; chan++) {
fcparam *fcp = FCPARAM(isp, chan);
copy_info(&info,
"Self Channel %d:\nHandle ID 0x%x PortID 0x%06x FW State 0x%x Loop State 0x%x\n", chan,
fcp->isp_loopid, fcp->isp_portid, fcp->isp_fwstate, fcp->isp_loopstate);
copy_info(&info, "Port WWN 0x%016llx Node WWN 0x%016llx\n\n", (ull) fcp->isp_wwpn, (ull)fcp->isp_wwnn);
copy_info(&info, "FC devices in port database:\n");
for (i = 0; i < MAX_FC_TARG; i++) {
if (fcp->portdb[i].state != FC_PORTDB_STATE_VALID) {
continue;
}
if (fcp->portdb[i].dev_map_idx) {
copy_info(&info, "\tdbidx %d handle 0x%x PortID 0x%06x role %s (target %d)\n\tPort WWN 0x%016llx Node WWN 0x%016llx\n\n",
i, fcp->portdb[i].handle, fcp->portdb[i].portid, isp_class3_roles[fcp->portdb[i].roles],
fcp->portdb[i].dev_map_idx - 1, (ull) fcp->portdb[i].port_wwn, (ull) fcp->portdb[i].node_wwn);
} else {
copy_info(&info, "\tdbidx %d handle 0x%x PortID 0x%06x role %s\n\tPort WWN 0x%016llx Node WWN 0x%016llx\n\n",
i, fcp->portdb[i].handle, fcp->portdb[i].portid, isp_class3_roles[fcp->portdb[i].roles],
(ull) fcp->portdb[i].port_wwn, (ull) fcp->portdb[i].node_wwn);
}
}
}
} else {
sdparam *sdp = (sdparam *)isp->isp_param;
copy_info(&info, "Initiator ID: %d\n", sdp->isp_initiator_id);
copy_info(&info, "Target Flag Period Offset\n");
for (i = 0; i < MAX_TARGETS; i++) {
copy_info(&info, "%6d: 0x%04x 0x%04x 0x%x\n", i, sdp->isp_devparam[i].actv_flags, sdp->isp_devparam[i].actv_offset,
sdp->isp_devparam[i].actv_period);
}
if (IS_DUALBUS(isp)) {
sdp++;
copy_info(&info, "\nInitiator ID: %d, Channel B\n", sdp->isp_initiator_id);
copy_info(&info,
"Target CurFlag DevFlag Period Offset B-Channel\n");
for (i = 0; i < MAX_TARGETS; i++) {
copy_info(&info, "%6d: 0x%04x 0x%04x 0x%x\n", i, sdp->isp_devparam[i].actv_flags, sdp->isp_devparam[i].actv_offset,
sdp->isp_devparam[i].actv_period);
}
}
}
ISP_UNLKU_SOFTC(isp);
return (info.pos > info.offset ? info.pos - info.offset : 0);
}
#endif /* CONFIG_PROC_FS */
static int isp_open(struct inode *, struct file *);
static int isp_close(struct inode *, struct file *);
static int isp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
dev_t isp_dev;
struct cdev isp_cdev = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
.kobj = { .k_name = ISP_NAME, },
#endif
.owner = THIS_MODULE
};
ISP_CLASS *isp_class;
struct file_operations isp_ioctl_operations = {
.owner = THIS_MODULE,
.open = isp_open,
.release = isp_close,
.ioctl = isp_ioctl,
};
static int
isp_open(struct inode *ip, struct file *fp)
{
const int minor = iminor(ip);
int i;
ispsoftc_t *isp = NULL;
for (i = 0; i < MAX_ISP; i++) {
if (isplist[i] && isplist[i]->isp_unit == minor) {
isp = isplist[i];
break;
}
}
if (isp == NULL) {
return (-ENXIO);
}
if (isp->isp_isopen) {
return (-EBUSY);
}
isp->isp_isopen = 1;
fp->private_data = isp;
return (0);
}
static int
isp_close(struct inode *ip, struct file *fp)
{
ispsoftc_t *isp = fp->private_data;
isp->isp_isopen = 0;
return (0);
}
static int
isp_ioctl(struct inode *ip, struct file *fp, unsigned int c, unsigned long arg)
{
ispsoftc_t *isp = fp->private_data;
int i, rv, inarg, outarg;
fcparam *fcp;
uint16_t loopid, chan;
mbreg_t mbs;
unsigned long flags;
if (isp == (ispsoftc_t *)NULL) {
return -ENXIO;
}
if (IS_SCSI(isp)) {
switch (c) {
case ISP_SDBLEV:
case ISP_RESCAN:
case ISP_GETROLE:
case ISP_SETROLE:
case ISP_RESETHBA:
break;
default:
return (-EINVAL);
}
fcp = NULL;
} else {
fcp = isp->isp_param;
}
rv = 0;
isp_prt(isp, ISP_LOGDEBUG1, "isp_ioctl: cmd=%x", c);
switch (c) {
case ISP_GET_STATS:
{
isp_stats_t stats;
memset(&stats, 0, sizeof stats);
stats.isp_stat_version = ISP_STATS_VERSION;
stats.isp_type = isp->isp_type;
stats.isp_revision = isp->isp_revision;
ISP_LOCK_SOFTC(isp);
stats.isp_stats[ISP_INTCNT] = isp->isp_intcnt;
stats.isp_stats[ISP_INTBOGUS] = isp->isp_intbogus;
stats.isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc;
stats.isp_stats[ISP_INGOASYNC] = isp->isp_intoasync;
stats.isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt;
stats.isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt;
stats.isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater;
stats.isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater;
ISP_UNLK_SOFTC(isp);
if (COPYOUT(&stats, (void *)arg, sizeof (stats))) {
rv = -EFAULT;
}
break;
}
case ISP_CLR_STATS:
ISP_LOCK_SOFTC(isp);
isp->isp_intcnt = 0;
isp->isp_intbogus = 0;
isp->isp_intmboxc = 0;
isp->isp_intoasync = 0;
isp->isp_rsltccmplt = 0;
isp->isp_fphccmplt = 0;
isp->isp_rscchiwater = 0;
isp->isp_fpcchiwater = 0;
ISP_UNLK_SOFTC(isp);
break;
case ISP_SDBLEV:
if (COPYIN((void *)arg, &inarg, sizeof (inarg))) {
rv = -EFAULT;
break;
}
outarg = isp->isp_dblev;
isp->isp_dblev = inarg;
if (COPYOUT(&outarg, (void *)arg, sizeof (outarg))) {
rv = -EFAULT;
break;
}
break;
case ISP_RESCAN:
if (IS_FC(isp)) {
for (i = 0; i < isp->isp_nchan; i++) {
FCPARAM(isp, i)->isp_loopstate = LOOP_PDB_RCVD;
isp_thread_event(isp, ISP_THREAD_FC_RESCAN, FCPARAM(isp, i), 0, __func__, __LINE__);
}
}
break;
case ISP_GETROLE:
if (COPYIN((void *)arg, &inarg, sizeof (inarg))) {
rv = -EFAULT;
break;
}
chan = inarg >> 16;
if (chan >= isp->isp_nchan) {
rv = -ENXIO;
break;
}
if (IS_FC(isp)) {
outarg = FCPARAM(isp, chan)->role;
} else {
outarg = SDPARAM(isp, chan)->role;
}
if (COPYOUT(&outarg, (void *)arg, sizeof (outarg))) {
rv = -EFAULT;
break;
}
break;
case ISP_SETROLE:
if (COPYIN((void *)arg, &inarg, sizeof (inarg))) {
rv = -EFAULT;
break;
}
chan = inarg >> 16;
if (chan >= isp->isp_nchan) {
rv = -ENXIO;
break;
}
inarg &= 0xffff;
if (inarg & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) {
rv = -EINVAL;
break;
}
/*
* Check to see if we're already in that role.
*/
if (IS_FC(isp)) {
if (FCPARAM(isp, chan)->role == inarg) {
break;
}
outarg = FCPARAM(isp, chan)->role;
if (COPYOUT(&outarg, (void *)arg, sizeof (outarg))) {
rv = -EFAULT;
break;
}
FCPARAM(isp, chan)->role = inarg;
SET_DEFAULT_ROLE(isp, chan, FCPARAM(isp, chan)->role);
} else {
if (SDPARAM(isp, chan)->role == inarg) {
break;
}
outarg = SDPARAM(isp, chan)->role;
if (COPYOUT(&outarg, (void *)arg, sizeof (outarg))) {
rv = -EFAULT;
break;
}
SDPARAM(isp, chan)->role = inarg;
SET_DEFAULT_ROLE(isp, chan, SDPARAM(isp, chan)->role);
}
break;
case ISP_RESETHBA:
{
ISP_LOCK_SOFTC(isp);
isp_reset(isp);
ISP_UNLK_SOFTC(isp);
break;
}
case ISP_FC_LIP:
if (COPYIN((void *)arg, &chan, sizeof (chan))) {
chan = 0;
}
ISP_LOCK_SOFTC(isp);
if (isp_control(isp, ISPCTL_SEND_LIP, chan)) {
rv = -EIO;
}
ISP_UNLK_SOFTC(isp);
break;
case ISP_FC_GETDINFO:
{
struct isp_fc_device local, *ifc = &local;
fcportdb_t *lp;
if (IS_SCSI(isp)) {
rv = -EINVAL;
break;
}
if (COPYIN((void *)arg, ifc, sizeof (*ifc))) {
rv = -EFAULT;
break;
}
if (ifc->chan >= isp->isp_nchan) {
rv = -EINVAL;
break;
}
if (ifc->loopid >= MAX_FC_TARG) {
rv = -EINVAL;
break;
}
ISP_LOCK_SOFTC(isp);
lp = &FCPARAM(isp, ifc->chan)->portdb[ifc->loopid];
if (lp->state == FC_PORTDB_STATE_VALID || lp->target_mode) {
ifc->role = lp->roles;
ifc->loopid = lp->handle;
ifc->portid = lp->portid;
ifc->node_wwn = lp->node_wwn;
ifc->port_wwn = lp->port_wwn;
} else {
rv = -ENODEV;
}
ISP_UNLK_SOFTC(isp);
if (rv == 0) {
if (COPYOUT((void *)ifc, (void *)arg, sizeof (*ifc))) {
rv = -EFAULT;
}
}
break;
}
case ISP_FC_GETHINFO:
{
struct isp_hba_device local, *hba = &local;
if (COPYIN((void *)arg, hba, sizeof (*hba))) {
rv = -EFAULT;
break;
}
chan = hba->fc_channel;
if (chan >= isp->isp_nchan) {
rv = -EINVAL;
break;
}
ISP_LOCK_SOFTC(isp);
hba->fc_nchannels = isp->isp_nchan;
hba->fc_nports = ISP_CAP_2KLOGIN(isp)? 2048 : 256;
hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev);
hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev);
hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev);
hba->fc_speed = FCPARAM(isp, chan)->isp_gbspeed;
hba->fc_topology = FCPARAM(isp, chan)->isp_topo + 1;
hba->fc_loopid = FCPARAM(isp, chan)->isp_loopid;
hba->nvram_node_wwn = FCPARAM(isp, chan)->isp_wwnn_nvram;
hba->nvram_port_wwn = FCPARAM(isp, chan)->isp_wwpn_nvram;
hba->active_node_wwn = FCPARAM(isp, chan)->isp_wwnn;
hba->active_port_wwn = FCPARAM(isp, chan)->isp_wwpn;
ISP_UNLK_SOFTC(isp);
if (COPYOUT(hba, (void *)arg, sizeof (*hba))) {
rv = -EFAULT;
break;
}
break;
}
case ISP_TSK_MGMT:
{
int needmarker;
struct isp_fc_tsk_mgmt local, *fct = (struct isp_fc_tsk_mgmt *) &local;
if (IS_SCSI(isp)) {
rv = -EINVAL;
break;
}
if (COPYIN((void *)arg, fct, sizeof (*fct))) {
rv = -EFAULT;
break;
}
memset(&mbs, 0, sizeof (mbs));
needmarker = 0;
loopid = fct->loopid;
if (ISP_CAP_2KLOGIN(isp) == 0) {
loopid <<= 8;
}
switch (fct->action) {
case IPT_CLEAR_ACA:
mbs.param[0] = MBOX_CLEAR_ACA;
mbs.param[1] = loopid;
mbs.param[2] = fct->lun;
break;
case IPT_TARGET_RESET:
mbs.param[0] = MBOX_TARGET_RESET;
mbs.param[1] = loopid;
needmarker = 1;
break;
case IPT_LUN_RESET:
mbs.param[0] = MBOX_LUN_RESET;
mbs.param[1] = loopid;
mbs.param[2] = fct->lun;
needmarker = 1;
break;
case IPT_CLEAR_TASK_SET:
mbs.param[0] = MBOX_CLEAR_TASK_SET;
mbs.param[1] = loopid;
mbs.param[2] = fct->lun;
needmarker = 1;
break;
case IPT_ABORT_TASK_SET:
mbs.param[0] = MBOX_ABORT_TASK_SET;
mbs.param[1] = loopid;
mbs.param[2] = fct->lun;
needmarker = 1;
break;
default:
rv = -EINVAL;
break;
}
if (rv == 0) {
mbs.logval = MBLOGALL;
mbs.timeout = 2000000;
ISP_LOCKU_SOFTC(isp);
if (needmarker) {
ISP_SET_SENDMARKER(isp, 0, 1);
}
rv = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
ISP_UNLKU_SOFTC(isp);
if (rv) {
rv = -EIO;
}
}
break;
}
case ISP_FC_GETDLIST:
{
isp_dlist_t *ua;
uint16_t nph, nphe, count, chan, lim;
struct wwnpair pair, *uptr;
if (IS_SCSI(isp)) {
rv = -EINVAL;
break;
}
ua = (isp_dlist_t *)arg;
uptr = &ua->wwns[0];
if (COPYIN((void *)&ua->count, &lim, sizeof (lim))) {
rv = -EFAULT;
break;
}
if (COPYIN((void *)&ua->channel, &chan, sizeof (chan))) {
rv = -EFAULT;
break;
}
if (ISP_CAP_2KLOGIN(isp)) {
nphe = NPH_MAX_2K;
} else {
nphe = NPH_MAX;
}
for (count = 0, nph = 0; count < lim && nph != nphe; nph++) {
ISP_LOCKU_SOFTC(isp);
rv = isp_control(isp, ISPCTL_GET_NAMES, chan, nph, &pair.wwnn, &pair.wwpn);
ISP_UNLKU_SOFTC(isp);
if (rv || (pair.wwpn == INI_NONE && pair.wwnn == INI_NONE)) {
rv = 0;
continue;
}
if (COPYOUT(&pair, (void *)uptr++, sizeof (pair))) {
rv = -EFAULT;
break;
}
count++;
}
if (rv == 0) {
if (COPYOUT(&count, (void *)&ua->count, sizeof (count))) {
rv = -EFAULT;
}
}
break;
}
default:
rv = -EINVAL;
break;
}
return (rv);
}
#if 0
static void
isp_run_cmd_done(struct scsi_cmnd *Cmnd)
{
struct semaphore *semap = (struct semaphore *) Cmnd->SCp.ptr;
up(semap);
}
static int
isp_run_cmd(ispsoftc_t *isp, isp_xcmd_t *cmd)
{
struct scsi_device *dev = NULL;
struct scsi_cmnd *Cmnd = NULL;
struct Scsi_Host *host = NULL;
uint32_t handle;
uint8_t local[QENTRY_LEN];
ispreq_t *reqp;
void *qep;
int time, result = 0;
struct semaphore rsem;
unsigned long flags;
time = cmd->timeout / 1000;
if (time == 0 && cmd->timeout) {
time = 1;
}
if (IS_24XX(isp) && time > 0x1999) {
time = 0x1999;
}
memset(local, 0, sizeof (local));
Cmnd = isp_kzalloc(sizeof (struct scsi_cmnd), GFP_KERNEL);
if (Cmnd == NULL) {
result = -ENOMEM;
goto out;
}
host = isp->isp_osinfo.host;
dev = isp_kzalloc(sizeof (struct scsi_device), GFP_KERNEL);
if (dev == NULL) {
result = -ENOMEM;
goto out;
}
Cmnd->device = dev;
dev->host = host;
Cmnd->scsi_done = isp_run_cmd_done;
Cmnd->SCp.ptr = (char *)&rsem;
sema_init(&rsem, 0);
ISP_LOCKU_SOFTC(isp);
qep = isp_getrqentry(isp);
if (qep == NULL) {
ISP_UNLKU_SOFTC(isp);
isp_prt(isp, ISP_LOGDEBUG0, "%s: Request Queue Overflow", __func__);
result = -ENOMEM;
goto out;
}
reqp = (ispreq_t *) local;
reqp->req_header.rqs_entry_count = 1;
if (isp_save_xs(isp, Cmnd, &handle)) {
ISP_UNLKU_SOFTC(isp);
isp_prt(isp, ISP_LOGDEBUG0, "out of xflist pointers");
result = -ENOMEM;
goto out;
}
reqp->req_handle = handle;
/*
* Now see if we need to synchronize the ISP with respect to anything.
* We do dual duty here (cough) for synchronizing for busses other
* than which we got here to send a command to.
*/
if (IS_24XX(isp)) {
ispreqt7_t *t7 = (ispreqt7_t *) local;
reqp->req_header.rqs_entry_type = RQSTYPE_T7RQS;
t7->req_task_attribute = FCP_CMND_TASK_ATTR_SIMPLE;
t7->req_nphdl = cmd->handle;
t7->req_tidlo = cmd->portid;
t7->req_tidhi = cmd->portid >> 16;
if (cmd->lun > 256) {
t7->req_lun[0] = cmd->lun >> 8;
t7->req_lun[0] |= 0x40;
}
t7->req_lun[1] = cmd->lun;
MEMCPY(t7->req_cdb, cmd->fcd.beg.cdb, min(sizeof (t7->req_cdb), sizeof (cmd->fcd.beg.cdb)));
Cmnd->cmd_len = sizeof(t7->req_cdb);
t7->req_time = time;
t7->req_vpidx = cmd->channel;
} else if (IS_FC(isp)) {
ispreqt2_t *t2 = (ispreqt2_t *) local;
reqp->req_header.rqs_entry_type = RQSTYPE_T2RQS;
t2->req_flags = REQFLAG_STAG;
if (ISP_CAP_2KLOGIN(isp)) {
((ispreqt2e_t *)reqp)->req_target = cmd->handle;
((ispreqt2e_t *)reqp)->req_scclun = cmd->lun;
} else if (ISP_CAP_SCCFW(isp)) {
t2->req_target = cmd->handle;
t2->req_scclun = cmd->lun;
} else {
t2->req_target = cmd->handle;
t2->req_lun_trn = cmd->lun;
}
MEMCPY(t2->req_cdb, cmd->fcd.beg.cdb, min(sizeof (t2->req_cdb), sizeof (cmd->fcd.beg.cdb)));
Cmnd->cmd_len = sizeof(t2->req_cdb);
t2->req_time = time;
} else {
reqp->req_header.rqs_entry_type = RQSTYPE_REQUEST;
reqp->req_flags = REQFLAG_STAG;
reqp->req_target = cmd->handle;
reqp->req_lun_trn = cmd->lun;
reqp->req_cdblen = 12;
MEMCPY(reqp->req_cdb, cmd->fcd.beg.cdb, min(sizeof (reqp->req_cdb), sizeof (cmd->fcd.beg.cdb)));
Cmnd->cmd_len = 12;
reqp->req_time = time;
}
MEMCPY(Cmnd->cmnd, cmd->fcd.beg.cdb, Cmnd->cmd_len);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
Cmnd->request_bufflen = cmd->fcd.beg.data_length;
Cmnd->request_buffer = cmd->fcd.beg.data_ptr;
if (Cmnd->request_bufflen && Cmnd->request_buffer) {
if (cmd->fcd.beg.do_read) {
Cmnd->sc_data_direction = SCSI_DATA_READ;
} else {
Cmnd->sc_data_direction = SCSI_DATA_WRITE;
}
} else {
Cmnd->sc_data_direction = SCSI_DATA_NONE;
}
result = ISP_DMASETUP(isp, Cmnd, reqp);
switch (result) {
default:
isp_prt(isp, ISP_LOGWARN, "isp_run_cmd: dma setup returned %d", result);
result = -EIO;
break;
case CMD_EAGAIN:
result = -ENOMEM;
break;
case CMD_QUEUED:
ISP_SYNC_REQUEST(isp);
isp->isp_nactive++;
result = 0;
break;
}
#else
isp_prt(isp, ISP_LOGERR, "do not have a kernel map thingie yet");
result = -EFAULT;
#endif
if (result == 0) {
ISP_UNLKU_SOFTC(isp);
down(&rsem);
cmd->fcd.end.data_residual = XS_GET_RESID(Cmnd);
cmd->fcd.end.status = Cmnd->SCp.Status;
if (cmd->fcd.end.status == SCSI_CHECK) {
MEMCPY(cmd->fcd.end.sense_data, XS_SNSP(Cmnd), min(sizeof(cmd->fcd.end.sense_data), XS_SNSLEN(Cmnd)));
}
if (host_byte(Cmnd->result) != DID_OK) {
result = -EIO;
}
ISP_LOCKU_SOFTC(isp);
ISP_DMAFREE(isp, Cmnd, handle);
}
isp_destroy_handle(isp, handle);
ISP_UNLKU_SOFTC(isp);
out:
if (dev) {
isp_kfree(dev, sizeof (struct scsi_device));
}
isp_kfree(Cmnd, sizeof (struct scsi_cmnd));
return (result);
}
#endif
/*
* vim:ts=4:sw=4:expandtab
*/