mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-16 18:21:27 +00:00
The patch below implements the following changes: - Fixes the remaining warnings reported by checkpatch.pl with regard to the use of whitespace in C code. - Adds double qoutes around some #warning messages, such that checkpatch.pl does no longer try to interprete the text behind the #warning preprocessor directive. - Adds an extra conversion to the generate-kernel-patch script that removes the single space from before goto-labels while generating the kernel patch. This patch has been tested as follows: - Verified that svn diff -x -w only shows changes in the #warning preprocessor directives. - Verified the output of svn diff by reading it. - Verified that checkpatch.pl does no longer complain on the use of whitespace (searched through the checkpatch.pl output for the text ' space'). - Verified that the patch generated by the generate-kernel-patch script still applies cleanly to the 2.6.24 kernel, and that the patched kernel still compiles cleanly. Please let me know if I have to resubmit parts of this patch to other SCST authors. Signed-off-by: Bart Van Assche <bart.vanassche@gmail.com> git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@381 d57e44dd-8a1f-0410-8b47-8ef2f437770f
1992 lines
61 KiB
C
1992 lines
61 KiB
C
/* $Id: isp_cb_ops.c,v 1.89 2008/04/15 22:41:03 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"
|
|
#include "exioct.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, __FUNCTION__, __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);
|
|
MEMZERO(isp->isp_osinfo.bins, 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",
|
|
(unsigned long long) isp->isp_intcnt, (unsigned long long) isp->isp_intbogus, (unsigned long long) isp->isp_intmboxc,
|
|
(unsigned long long) isp->isp_intoasync, (unsigned long long) isp->isp_rsltccmplt, (unsigned long long) 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) {
|
|
Scsi_Cmnd *f = isp->isp_osinfo.wqnext;
|
|
copy_info(&info, "WaitQ(%d)", isp->isp_osinfo.wqcnt);
|
|
while (f) {
|
|
copy_info(&info, "->%p", f);
|
|
f = (Scsi_Cmnd *) f->host_scribble;
|
|
}
|
|
copy_info(&info, "\n");
|
|
}
|
|
if (isp->isp_osinfo.dqnext) {
|
|
Scsi_Cmnd *f = isp->isp_osinfo.dqnext;
|
|
copy_info(&info, "DoneQ");
|
|
while (f) {
|
|
copy_info(&info, "->%p", f);
|
|
f = (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", fcp->isp_wwpn, 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].ini_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].ini_map_idx - 1, fcp->portdb[i].port_wwn, 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],
|
|
fcp->portdb[i].port_wwn, 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);
|
|
}
|
|
|
|
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);
|
|
static int isp_qlogic_ext_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
|
|
|
|
dev_t isp_dev;
|
|
struct cdev isp_cdev = {
|
|
.kobj = { .k_name = ISP_NAME, } ,
|
|
.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,
|
|
};
|
|
|
|
struct file_operations hba_api_ioctl_operations = {
|
|
owner: THIS_MODULE,
|
|
ioctl: isp_qlogic_ext_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_perf_test(ispsoftc_t *, isp_perftst_t *);
|
|
|
|
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 (((c & _IOC_TYPEMASK) >> _IOC_TYPESHIFT) == QLMULTIPATH_MAGIC) {
|
|
return (isp_qlogic_ext_ioctl(ip, fp, c, arg));
|
|
}
|
|
|
|
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;
|
|
|
|
MEMZERO(&stats, 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, __FUNCTION__, __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;
|
|
}
|
|
|
|
MEMZERO(&mbs, 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;
|
|
}
|
|
case ISP_FC_PERFTST:
|
|
{
|
|
isp_perftst_t local, *tt = &local;
|
|
|
|
if (COPYIN((void *)arg, tt, sizeof (*tt))) {
|
|
rv = -EFAULT;
|
|
break;
|
|
}
|
|
rv = isp_perf_test(isp, tt);
|
|
break;
|
|
}
|
|
default:
|
|
rv = -EINVAL;
|
|
break;
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
/*
|
|
* SDMI Routines
|
|
*/
|
|
|
|
static int isp_exti_query(EXT_IOCTL *);
|
|
static int isp_exti_setinstance(EXT_IOCTL *);
|
|
static int isp_exti_fcct_passthru(EXT_IOCTL *);
|
|
static int isp_exti_discover_luns(ispsoftc_t *, int, UINT64, UINT64, UINT16 *);
|
|
static void *isp_exti_usrptr(UINT64, UINT16);
|
|
static int isp_exti_passthru(EXT_IOCTL *);
|
|
static int isp_run_cmd(ispsoftc_t *, isp_xcmd_t *);
|
|
|
|
int
|
|
isp_qlogic_ext_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
EXT_IOCTL ext;
|
|
EXT_IOCTL *uext = (EXT_IOCTL *) arg;
|
|
int rval;
|
|
|
|
rval = 0;
|
|
|
|
if (COPYIN(uext, &ext, sizeof (ext))) {
|
|
ext.Status = EXT_STATUS_COPY_ERR;
|
|
ext.DetailStatus = 0;
|
|
goto out;
|
|
}
|
|
|
|
ext.DetailStatus = EXT_STATUS_OK;
|
|
ext.Status = EXT_STATUS_OK;
|
|
|
|
/*
|
|
* Make sure this is a supported command.
|
|
*/
|
|
switch (cmd) {
|
|
case EXT_CC_GET_HBA_CNT:
|
|
case EXT_CC_SETINSTANCE:
|
|
case EXT_CC_QUERY:
|
|
case EXT_SC_QUERY_CHIP:
|
|
case EXT_CC_SEND_FCCT_PASSTHRU:
|
|
case EXT_CC_SEND_SCSI_PASSTHRU:
|
|
break;
|
|
default:
|
|
ext.Status = EXT_STATUS_INVALID_REQUEST;
|
|
ext.DetailStatus = 0;
|
|
break;
|
|
}
|
|
if (ext.Status != EXT_STATUS_OK) {
|
|
goto out;
|
|
}
|
|
|
|
if (memcmp(&ext.Signature, EXT_DEF_REGULAR_SIGNATURE, strlen(EXT_DEF_REGULAR_SIGNATURE)) != 0) {
|
|
printk("%s: bad signature\n", __FUNCTION__);
|
|
ext.Status = EXT_STATUS_INVALID_PARAM;
|
|
goto out;
|
|
}
|
|
|
|
if (ext.Version != EXT_VERSION) {
|
|
printk("%s: bad version %d\n", __FUNCTION__, ext.Version);
|
|
ext.Status = EXT_STATUS_UNSUPPORTED_VERSION;
|
|
goto out;
|
|
}
|
|
/*
|
|
* We only count FC adapters.
|
|
*/
|
|
if (cmd == EXT_CC_GET_HBA_CNT) {
|
|
unsigned int i;
|
|
|
|
ext.Instance = 0;
|
|
for (i = 0; i < MAX_ISP; i++) {
|
|
if (isplist[i] && IS_FC(isplist[i])) {
|
|
ext.Instance++;
|
|
}
|
|
}
|
|
if (COPYOUT(&ext.Instance, &uext->Instance, sizeof (uext->Instance))) {
|
|
ext.Status = EXT_STATUS_COPY_ERR;
|
|
} else {
|
|
ext.Status = EXT_STATUS_OK;
|
|
}
|
|
} else if (cmd == EXT_CC_SETINSTANCE) {
|
|
rval = isp_exti_setinstance(&ext);
|
|
if (rval) {
|
|
goto out;
|
|
}
|
|
if (COPYOUT(&ext.HbaSelect, &uext->HbaSelect, sizeof (uext->HbaSelect))) {
|
|
ext.Status = EXT_STATUS_COPY_ERR;
|
|
} else {
|
|
ext.Status = EXT_STATUS_OK;
|
|
}
|
|
} else if (cmd == EXT_CC_QUERY) {
|
|
rval = isp_exti_query(&ext);
|
|
} else if (cmd == EXT_CC_SEND_FCCT_PASSTHRU) {
|
|
rval = isp_exti_fcct_passthru(&ext);
|
|
} else if (cmd == EXT_CC_SEND_SCSI_PASSTHRU) {
|
|
rval = isp_exti_passthru(&ext);
|
|
} else {
|
|
ext.Status = EXT_STATUS_INVALID_REQUEST;
|
|
}
|
|
|
|
out:
|
|
if (COPYOUT(&ext.Status, &uext->Status, sizeof (ext.Status))) {
|
|
rval = -EFAULT;
|
|
} else if (COPYOUT(&ext.DetailStatus, &uext->DetailStatus, sizeof (ext.Status))) {
|
|
rval = -EFAULT;
|
|
}
|
|
return (rval);
|
|
}
|
|
|
|
static int
|
|
isp_exti_setinstance(EXT_IOCTL *ext)
|
|
{
|
|
unsigned int inst, index;
|
|
|
|
for (inst = index = 0; index < MAX_ISP; index++) {
|
|
if (isplist[index] && IS_FC(isplist[index])) {
|
|
if (inst++ == ext->Instance) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (index >= MAX_ISP) {
|
|
ext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
return (-ENXIO);
|
|
}
|
|
api_isp = isplist[index];
|
|
ext->HbaSelect = api_isp->isp_unit;
|
|
api_channel = 0; /* XXXXXXXXXXXXXXXXXXXXXXX */
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
isp_exti_query(EXT_IOCTL *pext)
|
|
{
|
|
ispsoftc_t *isp = api_isp;
|
|
int cl, i, rval = 0;
|
|
void *outaddr;
|
|
fcparam *fcp;
|
|
fcportdb_t *lp;
|
|
unsigned long flags;
|
|
|
|
if (isp == NULL) {
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
return (0);
|
|
}
|
|
ISP_LOCKU_SOFTC(isp);
|
|
fcp = FCPARAM(isp, api_channel);
|
|
if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate < LOOP_LSCAN_DONE) {
|
|
if (isp_fc_runstate(isp, api_channel, 1000000) < 0) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_PENDING;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
outaddr = isp_exti_usrptr(pext->ResponseAdr, pext->AddrMode);
|
|
|
|
switch (pext->SubCode) {
|
|
case EXT_SC_QUERY_HBA_NODE:
|
|
{
|
|
EXT_HBA_NODE hba;
|
|
|
|
MEMZERO(&hba, sizeof (hba));
|
|
MAKE_NODE_NAME_FROM_WWN(hba.WWNN, fcp->isp_wwnn_nvram);
|
|
MEMCPY(hba.SerialNum, &hba.WWNN[5], 3);
|
|
SNPRINTF((char *)hba.DriverVersion, EXT_DEF_MAX_STR_SIZE, "Linux Version %d.%d; Common Core Code Version %d.%d",
|
|
ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
|
|
ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
|
|
SNPRINTF((char *)hba.FWVersion, EXT_DEF_MAX_STR_SIZE, "%02d.%02d.%02d", isp->isp_fwrev[0], isp->isp_fwrev[1], isp->isp_fwrev[2]);
|
|
hba.OptRomVersion[0] = '0';
|
|
hba.PortCount = 1;
|
|
hba.InterfaceType = EXT_DEF_FC_INTF_TYPE;
|
|
ISP_UNLKU_SOFTC(isp);
|
|
cl = min(pext->ResponseLen, sizeof (hba));
|
|
if (COPYOUT(&hba, outaddr, cl)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case EXT_SC_QUERY_HBA_PORT:
|
|
{
|
|
EXT_HBA_PORT hbp;
|
|
|
|
MEMZERO(&hbp, sizeof (hbp));
|
|
MAKE_NODE_NAME_FROM_WWN(hbp.WWPN, FCPARAM(isp, 0)->isp_wwpn);
|
|
hbp.Id[1] = (fcp->isp_portid >> 16) & 0xff;
|
|
hbp.Id[2] = (fcp->isp_portid >> 8) & 0xff;
|
|
hbp.Id[3] = fcp->isp_portid & 0xff;
|
|
|
|
if (FCPARAM(isp, 0)->role & ISP_ROLE_TARGET) {
|
|
hbp.Type |= EXT_DEF_TARGET_DEV;
|
|
}
|
|
|
|
if (FCPARAM(isp, 0)->role & ISP_ROLE_INITIATOR) {
|
|
hbp.Type |= EXT_DEF_INITIATOR_DEV;
|
|
}
|
|
|
|
hbp.State = EXT_DEF_HBA_OK;
|
|
|
|
if (fcp->isp_topo == TOPO_NL_PORT || fcp->isp_topo == TOPO_FL_PORT) {
|
|
hbp.Mode = EXT_DEF_LOOP_MODE;
|
|
} else {
|
|
hbp.Mode = EXT_DEF_P2P_MODE;
|
|
}
|
|
hbp.Type |= EXT_DEF_FABRIC_DEV;
|
|
|
|
/*
|
|
* Count devices in our port database.
|
|
*/
|
|
for (i = 0; i < MAX_FC_TARG; i++) {
|
|
lp = &fcp->portdb[i];
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
continue;
|
|
}
|
|
if (lp->portid == fcp->isp_portid) {
|
|
continue;
|
|
}
|
|
hbp.DiscPortCount++;
|
|
if (lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) {
|
|
hbp.DiscTargetCount++;
|
|
}
|
|
}
|
|
ISP_UNLKU_SOFTC(isp);
|
|
hbp.DiscPortNameType = EXT_DEF_USE_PORT_NAME;
|
|
hbp.PortSupportedFC4Types = EXT_DEF_FC4_TYPE_SCSI;
|
|
hbp.PortActiveFC4Types = EXT_DEF_FC4_TYPE_SCSI;
|
|
hbp.PortSupportedSpeed = EXT_DEF_PORTSPEED_1GBIT;
|
|
if (IS_23XX(isp)) {
|
|
hbp.PortSupportedSpeed |= EXT_DEF_PORTSPEED_2GBIT;
|
|
} else if (IS_24XX(isp)) {
|
|
hbp.PortSupportedSpeed |= EXT_DEF_PORTSPEED_2GBIT|EXT_DEF_PORTSPEED_4GBIT;
|
|
}
|
|
cl = min(pext->ResponseLen, sizeof (hbp));
|
|
if (COPYOUT(&hbp, outaddr, cl)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case EXT_SC_QUERY_DISC_PORT:
|
|
{
|
|
EXT_DISC_PORT port;
|
|
fcportdb_t *rlp;
|
|
int inst;
|
|
|
|
MEMZERO(&port, sizeof (port));
|
|
rlp = NULL;
|
|
for (inst = i = 0; rlp == NULL && i < MAX_FC_TARG; i++) {
|
|
lp = &fcp->portdb[i];
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
continue;
|
|
}
|
|
if (lp->portid == fcp->isp_portid) {
|
|
continue;
|
|
}
|
|
if (inst != pext->Instance) {
|
|
inst++;
|
|
continue;
|
|
}
|
|
rlp = lp;
|
|
}
|
|
if (rlp == NULL) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
break;
|
|
}
|
|
MAKE_NODE_NAME_FROM_WWN(port.WWPN, rlp->port_wwn);
|
|
MAKE_NODE_NAME_FROM_WWN(port.WWNN, rlp->node_wwn);
|
|
port.Id[1] = (rlp->portid >> 16) & 0xff;
|
|
port.Id[2] = (rlp->portid >> 8) & 0xff;
|
|
port.Id[3] = rlp->portid & 0xff;
|
|
if (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT) {
|
|
port.Type = EXT_DEF_FABRIC_DEV;
|
|
} else {
|
|
port.Type = 0;
|
|
}
|
|
if (rlp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) {
|
|
port.Type |= EXT_DEF_TARGET_DEV;
|
|
}
|
|
if (rlp->roles & (SVC3_INI_ROLE >> SVC3_ROLE_SHIFT)) {
|
|
port.Type |= EXT_DEF_INITIATOR_DEV;
|
|
}
|
|
port.Status = EXT_DEF_HBA_OK;
|
|
port.Bus = isp->isp_host->host_no;
|
|
if (rlp->ini_map_idx) {
|
|
port.TargetId = rlp->ini_map_idx - 1;
|
|
} else {
|
|
port.TargetId = 0;
|
|
}
|
|
ISP_UNLKU_SOFTC(isp);
|
|
cl = min(pext->ResponseLen, sizeof (port));
|
|
if (COPYOUT(&port, outaddr, cl)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case EXT_SC_QUERY_DISC_TGT:
|
|
{
|
|
EXT_DISC_TARGET tgt;
|
|
fcportdb_t *rlp;
|
|
UINT64 wwpn, wwnn;
|
|
int inst;
|
|
|
|
MEMZERO(&tgt, sizeof (tgt));
|
|
rlp = NULL;
|
|
for (inst = i = 0; rlp == NULL && i < MAX_FC_TARG; i++) {
|
|
lp = &fcp->portdb[i];
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
continue;
|
|
}
|
|
if (lp->portid == fcp->isp_portid) {
|
|
continue;
|
|
}
|
|
if ((lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) == 0) {
|
|
continue;
|
|
}
|
|
if (inst != pext->Instance) {
|
|
inst++;
|
|
continue;
|
|
}
|
|
rlp = lp;
|
|
}
|
|
if (rlp == NULL) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
break;
|
|
}
|
|
MAKE_NODE_NAME_FROM_WWN(tgt.WWPN, rlp->port_wwn);
|
|
MAKE_NODE_NAME_FROM_WWN(tgt.WWNN, rlp->node_wwn);
|
|
tgt.Id[1] = (rlp->portid >> 16) & 0xff;
|
|
tgt.Id[2] = (rlp->portid >> 8) & 0xff;
|
|
tgt.Id[3] = rlp->portid & 0xff;
|
|
if (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT) {
|
|
tgt.Type = EXT_DEF_FABRIC_DEV;
|
|
} else {
|
|
tgt.Type = 0;
|
|
}
|
|
if (rlp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) {
|
|
tgt.Type |= EXT_DEF_TARGET_DEV;
|
|
}
|
|
if (rlp->roles & (SVC3_INI_ROLE >> SVC3_ROLE_SHIFT)) {
|
|
tgt.Type |= EXT_DEF_INITIATOR_DEV;
|
|
}
|
|
tgt.Status = EXT_DEF_HBA_OK;
|
|
tgt.Bus = isp->isp_host->host_no;
|
|
if (rlp->ini_map_idx) {
|
|
tgt.TargetId = rlp->ini_map_idx - 1;
|
|
} else {
|
|
tgt.TargetId = 0;
|
|
}
|
|
wwpn = rlp->port_wwn;
|
|
wwnn = rlp->node_wwn;
|
|
ISP_UNLKU_SOFTC(isp);
|
|
rval = isp_exti_discover_luns(isp, api_channel, wwpn, wwnn, &tgt.LunCount);
|
|
if (rval) {
|
|
break;
|
|
}
|
|
cl = min(pext->ResponseLen, sizeof (tgt));
|
|
if (COPYOUT(&tgt, outaddr, cl)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case EXT_SC_QUERY_CHIP:
|
|
{
|
|
EXT_CHIP xc;
|
|
struct pci_dev *pdev = isp->isp_osinfo.device;
|
|
|
|
MEMZERO(&xc, sizeof (xc));
|
|
xc.VendorId = pdev->vendor;
|
|
xc.DeviceId = pdev->device;
|
|
xc.SubVendorId = pdev->subsystem_vendor;
|
|
xc.SubSystemId = pdev->subsystem_device;
|
|
xc.PciBusNumber = pdev->bus->number;
|
|
xc.PciDevFunc = pdev->devfn;
|
|
xc.PciSlotNumber = PCI_SLOT(pdev->devfn);
|
|
xc.DomainNr = pci_domain_nr(pdev->bus);
|
|
xc.InterruptLevel = pdev->irq;
|
|
cl = min(pext->ResponseLen, sizeof (xc));
|
|
if (COPYOUT(&xc, outaddr, cl)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case EXT_SC_QUERY_DISC_LUN:
|
|
default:
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_UNSUPPORTED_SUBCODE;
|
|
break;
|
|
}
|
|
return (rval);
|
|
}
|
|
|
|
|
|
#define IGPOFF 0 /* place CT Request itself is put */
|
|
#define OGPOFF (ISP_FC_SCRLEN >> 1) /* place CT Response itself is put */
|
|
#define ZTXOFF (ISP_FC_SCRLEN - (1 * QENTRY_LEN)) /* place where status entry for CT passthru request ends up */
|
|
#define CTXOFF (ISP_FC_SCRLEN - (2 * QENTRY_LEN)) /* place where CT passthru request is put */
|
|
|
|
static int
|
|
isp_exti_fcct_passthru(EXT_IOCTL *pext)
|
|
{
|
|
ispsoftc_t *isp = api_isp;
|
|
isp_plcmd_t p;
|
|
fcparam *fcp = FCPARAM(isp, 0);
|
|
mbreg_t mbs;
|
|
uint8_t qe[QENTRY_LEN], *scp;
|
|
uint16_t handle;
|
|
unsigned long flags;
|
|
void *localmem = NULL;
|
|
size_t localamt;
|
|
int r;
|
|
int rval = 0;
|
|
|
|
if (isp == NULL) {
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
return (0);
|
|
}
|
|
|
|
if (pext->RequestLen > (ISP_FC_SCRLEN >> 1)) {
|
|
pext->Status = EXT_STATUS_NO_MEMORY;
|
|
return (0);
|
|
}
|
|
if (pext->ResponseLen > ((ISP_FC_SCRLEN >> 1) - (2 * QENTRY_LEN))) {
|
|
pext->Status = EXT_STATUS_NO_MEMORY;
|
|
return (0);
|
|
}
|
|
|
|
localamt = pext->RequestLen;
|
|
if (pext->ResponseLen > localamt) {
|
|
localamt = pext->ResponseLen;
|
|
}
|
|
localmem = isp_kalloc(localamt, GFP_KERNEL);
|
|
if (localmem == NULL) {
|
|
pext->Status = EXT_STATUS_NO_MEMORY;
|
|
return (0);
|
|
}
|
|
pext->Status = EXT_STATUS_OK;
|
|
if (COPYIN(isp_exti_usrptr(pext->RequestAdr, pext->AddrMode), localmem, pext->RequestLen)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* First- check to see if topology is right and things are right otherwise.
|
|
*/
|
|
ISP_LOCKU_SOFTC(isp);
|
|
if (fcp->isp_topo != TOPO_F_PORT && fcp->isp_topo != TOPO_FL_PORT) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_UNSUPPORTED_SUBCODE;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Login into the Management Server
|
|
*/
|
|
p.channel = api_channel;
|
|
p.handle = NIL_HANDLE;
|
|
p.portid = MANAGEMENT_PORT_ID;
|
|
p.flags = PLOGX_FLG_CMD_PLOGI;
|
|
r = isp_control(isp, ISPCTL_PLOGX, &p);
|
|
if (r) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
isp_prt(isp, ISP_LOGWARN, "failed to log into management server (0x%x)", r);
|
|
pext->Status = EXT_STATUS_MS_NO_RESPONSE;
|
|
goto out;
|
|
}
|
|
handle = p.handle;
|
|
|
|
/*
|
|
* Acquire Scratch
|
|
*/
|
|
MEMZERO(qe, QENTRY_LEN);
|
|
if (FC_SCRATCH_ACQUIRE(isp, 0)) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
isp_prt(isp, ISP_LOGWARN, "failed to get FC scratch area");
|
|
pext->Status = EXT_STATUS_BUSY;
|
|
goto out;
|
|
}
|
|
scp = fcp->isp_scratch;
|
|
|
|
MEMCPY(&scp[IGPOFF], localmem, pext->RequestLen);
|
|
MEMORYBARRIER(isp, SYNC_SFORDEV, IGPOFF, pext->RequestLen);
|
|
|
|
/*
|
|
* Build command we're going to use
|
|
*/
|
|
if (IS_24XX(isp)) {
|
|
isp_ct_pt_t *pt;
|
|
|
|
/*
|
|
* Build a Passthrough IOCB in memory.
|
|
*/
|
|
pt = (isp_ct_pt_t *)qe;
|
|
pt->ctp_header.rqs_entry_count = 1;
|
|
pt->ctp_header.rqs_entry_type = RQSTYPE_CT_PASSTHRU;
|
|
pt->ctp_handle = 0xffffffff;
|
|
pt->ctp_nphdl = handle;
|
|
pt->ctp_cmd_cnt = 1;
|
|
pt->ctp_time = 5;
|
|
pt->ctp_rsp_cnt = 1;
|
|
pt->ctp_rsp_bcnt = pext->ResponseLen;
|
|
pt->ctp_cmd_bcnt = pext->RequestLen;
|
|
pt->ctp_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma+IGPOFF);
|
|
pt->ctp_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma+IGPOFF);
|
|
pt->ctp_dataseg[0].ds_count = pext->RequestLen;
|
|
pt->ctp_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma+OGPOFF);
|
|
pt->ctp_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma+OGPOFF);
|
|
pt->ctp_dataseg[1].ds_count = pext->ResponseLen;
|
|
isp_put_ct_pt(isp, pt, (isp_ct_pt_t *) &scp[CTXOFF]);
|
|
|
|
/*
|
|
* Build a EXEC IOCB A64 command that points to the CT passthru command
|
|
*/
|
|
MEMZERO(&mbs, sizeof (mbs));
|
|
mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64;
|
|
mbs.param[1] = QENTRY_LEN;
|
|
mbs.param[2] = DMA_WD1(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[3] = DMA_WD0(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[6] = DMA_WD3(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[7] = DMA_WD2(fcp->isp_scdma + CTXOFF);
|
|
mbs.logval = MBLOGALL;
|
|
MEMORYBARRIER(isp, SYNC_SFORDEV, CTXOFF, 2 * QENTRY_LEN);
|
|
isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
|
|
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
|
|
pext->Status = EXT_STATUS_ERR;
|
|
goto out1;
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_SFORCPU, ZTXOFF, QENTRY_LEN);
|
|
pt = (isp_ct_pt_t *)qe;
|
|
isp_get_ct_pt(isp, (isp_ct_pt_t *) &scp[ZTXOFF], pt);
|
|
/*
|
|
* Let the user application parse any errors
|
|
*/
|
|
} else {
|
|
isp_ms_t *ms;
|
|
/*
|
|
* Build a Passthrough IOCB in memory.
|
|
*/
|
|
ms = (isp_ms_t *)qe;
|
|
ms->ms_header.rqs_entry_count = 1;
|
|
ms->ms_header.rqs_entry_type = RQSTYPE_MS_PASSTHRU;
|
|
ms->ms_handle = 0xffffffff;
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
ms->ms_nphdl = handle;
|
|
} else {
|
|
ms->ms_nphdl = handle << 8;
|
|
}
|
|
ms->ms_cmd_cnt = 1;
|
|
ms->ms_time = 5;
|
|
ms->ms_tot_cnt = 2;
|
|
ms->ms_rsp_bcnt = pext->ResponseLen;
|
|
ms->ms_cmd_bcnt = pext->RequestLen;
|
|
ms->ms_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma+IGPOFF);
|
|
ms->ms_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma+IGPOFF);
|
|
ms->ms_dataseg[0].ds_count = pext->RequestLen;
|
|
ms->ms_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma+OGPOFF);
|
|
ms->ms_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma+OGPOFF);
|
|
ms->ms_dataseg[1].ds_count = pext->ResponseLen;
|
|
isp_put_ms(isp, ms, (isp_ms_t *) &scp[CTXOFF]);
|
|
|
|
/*
|
|
* Build a EXEC IOCB A64 command that points to the MS passthru command
|
|
*/
|
|
MEMZERO(&mbs, sizeof (mbs));
|
|
mbs.param[0] = MBOX_EXEC_COMMAND_IOCB_A64;
|
|
mbs.param[1] = QENTRY_LEN;
|
|
mbs.param[2] = DMA_WD1(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[3] = DMA_WD0(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[6] = DMA_WD3(fcp->isp_scdma + CTXOFF);
|
|
mbs.param[7] = DMA_WD2(fcp->isp_scdma + CTXOFF);
|
|
mbs.logval = MBLOGALL;
|
|
MEMORYBARRIER(isp, SYNC_SFORDEV, CTXOFF, 2 * QENTRY_LEN);
|
|
isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
|
|
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
|
|
pext->Status = EXT_STATUS_ERR;
|
|
goto out1;
|
|
}
|
|
MEMORYBARRIER(isp, SYNC_SFORCPU, ZTXOFF, QENTRY_LEN);
|
|
ms = (isp_ms_t *)qe;
|
|
isp_get_ms(isp, (isp_ms_t *) &scp[ZTXOFF], ms);
|
|
}
|
|
|
|
MEMORYBARRIER(isp, SYNC_SFORCPU, OGPOFF, pext->ResponseLen);
|
|
MEMCPY(localmem, &scp[OGPOFF], pext->ResponseLen);
|
|
|
|
out1:
|
|
|
|
/*
|
|
* Release Scratch
|
|
*/
|
|
FC_SCRATCH_RELEASE(isp, 0);
|
|
|
|
/*
|
|
* Log out of the Management Server
|
|
*/
|
|
p.channel = api_channel;
|
|
p.handle = handle;
|
|
p.portid = MANAGEMENT_PORT_ID;
|
|
p.flags = PLOGX_FLG_CMD_LOGO|PLOGX_FLG_EXPLICIT_LOGO;
|
|
r = isp_control(isp, ISPCTL_PLOGX, &p);
|
|
if (r) {
|
|
isp_prt(isp, ISP_LOGWARN, "failed to log out of management server (0x%x)", r);
|
|
}
|
|
ISP_UNLKU_SOFTC(isp);
|
|
|
|
/*
|
|
* Copy data
|
|
*/
|
|
out:
|
|
if (rval == 0) {
|
|
if (COPYOUT(localmem, isp_exti_usrptr(pext->ResponseAdr, pext->AddrMode), pext->ResponseLen)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
rval = -EFAULT;
|
|
}
|
|
}
|
|
if (localmem) {
|
|
isp_kfree(localmem, localamt);
|
|
}
|
|
return (rval);
|
|
}
|
|
|
|
static void *
|
|
isp_exti_usrptr(UINT64 uaddr, UINT16 mode)
|
|
{
|
|
void *ptr = NULL;
|
|
|
|
#if BITS_PER_LONG == 32
|
|
if (mode == EXT_DEF_ADDR_MODE_32) {
|
|
UINT32 xaddr = uaddr & 0xffffffff;
|
|
ptr = (void *) xaddr;
|
|
}
|
|
#elif BITS_PER_LONG == 64
|
|
ptr = (void *) uaddr;
|
|
#endif
|
|
return (ptr);
|
|
}
|
|
|
|
static int
|
|
isp_exti_passthru(EXT_IOCTL *pext)
|
|
{
|
|
ispsoftc_t *isp = api_isp;
|
|
char *bufp;
|
|
fcportdb_t *lp;
|
|
EXT_FC_SCSI_PASSTHRU fcx;
|
|
uint64_t wwnn = 0LL;
|
|
uint64_t wwpn = 0LL;
|
|
uint32_t portid = (uint32_t) -1;
|
|
isp_xcmd_t cmd;
|
|
int status;
|
|
size_t cpyamt;
|
|
unsigned long flags;
|
|
|
|
if (isp == NULL) {
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
return (0);
|
|
}
|
|
if (COPYIN(isp_exti_usrptr(pext->RequestAdr, pext->AddrMode), &fcx, sizeof (fcx))) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
return (0);
|
|
}
|
|
|
|
if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_SCSI || fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_FABRIC) {
|
|
pext->Status = EXT_STATUS_INVALID_REQUEST;
|
|
return (0);
|
|
}
|
|
|
|
if (isp == NULL) {
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
return (0);
|
|
}
|
|
|
|
switch (pext->SubCode) {
|
|
case EXT_SC_SEND_FC_SCSI_PASSTHRU:
|
|
break;
|
|
case EXT_SC_SEND_SCSI_PASSTHRU:
|
|
case EXT_SC_SCSI3_PASSTHRU:
|
|
default:
|
|
pext->Status = EXT_STATUS_UNSUPPORTED_SUBCODE;
|
|
return (0);
|
|
}
|
|
|
|
MEMZERO(&cmd, sizeof (cmd));
|
|
|
|
if (pext->ResponseLen) {
|
|
bufp = isp_kalloc(pext->ResponseLen, GFP_KERNEL);
|
|
if (bufp == NULL) {
|
|
pext->Status = EXT_STATUS_NO_MEMORY;
|
|
return (0);
|
|
}
|
|
if (fcx.Direction == EXT_DEF_SCSI_PASSTHRU_DATA_OUT) {
|
|
if (COPYIN(isp_exti_usrptr(pext->ResponseAdr, pext->AddrMode), bufp, pext->ResponseLen)) {
|
|
isp_kfree(bufp, pext->ResponseLen);
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
return (0);
|
|
}
|
|
} else {
|
|
cmd.fcd.beg.do_read = 1;
|
|
}
|
|
cmd.fcd.beg.data_length = pext->ResponseLen;
|
|
cmd.fcd.beg.data_ptr = bufp;
|
|
} else {
|
|
bufp = NULL;
|
|
}
|
|
|
|
if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_WWNN) {
|
|
MAKE_WWN_FROM_NODE_NAME(wwnn, fcx.FCScsiAddr.DestAddr.WWNN);
|
|
} else if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_WWPN) {
|
|
MAKE_WWN_FROM_NODE_NAME(wwpn, fcx.FCScsiAddr.DestAddr.WWPN);
|
|
} else if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_PORTID) {
|
|
portid = (fcx.FCScsiAddr.DestAddr.Id[1] << 16) | (fcx.FCScsiAddr.DestAddr.Id[2] << 8) | (fcx.FCScsiAddr.DestAddr.Id[3]);
|
|
}
|
|
/*
|
|
* Make sure we have an entry for this device (handle, portid)
|
|
* so we know how to send the command.
|
|
*/
|
|
ISP_LOCKU_SOFTC(isp);
|
|
for (lp = &FCPARAM(isp, api_channel)->portdb[0]; lp < &FCPARAM(isp, api_channel)->portdb[MAX_FC_TARG]; lp++) {
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
continue;
|
|
}
|
|
if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_WWNN) {
|
|
if (lp->node_wwn == wwnn) {
|
|
break;
|
|
}
|
|
} else if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_WWPN) {
|
|
if (lp->port_wwn == wwpn) {
|
|
break;
|
|
}
|
|
} else if (fcx.FCScsiAddr.DestType == EXT_DEF_DESTTYPE_PORTID) {
|
|
if (lp->portid == portid) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (lp == &FCPARAM(isp, api_channel)->portdb[MAX_FC_TARG]) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
pext->Status = EXT_STATUS_DEV_NOT_FOUND;
|
|
if (bufp) {
|
|
isp_kfree(bufp, pext->ResponseLen);
|
|
}
|
|
return (0);
|
|
}
|
|
wwnn = lp->node_wwn;
|
|
wwpn = lp->port_wwn;
|
|
cmd.handle = lp->handle;
|
|
cmd.portid = lp->portid;
|
|
cmd.channel = api_channel;
|
|
ISP_UNLKU_SOFTC(isp);
|
|
|
|
MEMCPY(cmd.fcd.beg.cdb, fcx.Cdb, min(EXT_DEF_SCSI_PASSTHRU_CDB_LENGTH, sizeof (cmd.fcd.beg.cdb)));
|
|
cmd.lun = fcx.FCScsiAddr.Lun;
|
|
cmd.timeout = fcx.Timeout;
|
|
cpyamt = 0;
|
|
pext->Status = EXT_STATUS_OK;
|
|
|
|
status = isp_run_cmd(isp, &cmd);
|
|
if (status == 0) {
|
|
cpyamt = pext->ResponseLen - cmd.fcd.end.data_residual;
|
|
if (cmd.fcd.end.status == SCSI_CHECK && cmd.fcd.end.sense_length) {
|
|
fcx.SenseLength = min(cmd.fcd.end.sense_length, sizeof (fcx.SenseData));
|
|
MEMCPY(fcx.SenseData, cmd.fcd.end.sense_data, fcx.SenseLength);
|
|
}
|
|
if ((pext->DetailStatus = cmd.fcd.end.status) != SCSI_GOOD) {
|
|
pext->Status = EXT_STATUS_SCSI_STATUS;
|
|
} else if (cpyamt != pext->ResponseLen) {
|
|
pext->Status = EXT_STATUS_DATA_UNDERRUN;
|
|
}
|
|
} else {
|
|
cpyamt = 0;
|
|
pext->Status = EXT_STATUS_ERR;
|
|
}
|
|
|
|
if (bufp && fcx.Direction == EXT_DEF_SCSI_PASSTHRU_DATA_IN && cpyamt) {
|
|
if (cpyamt) {
|
|
if (COPYOUT(bufp, isp_exti_usrptr(pext->ResponseAdr, pext->AddrMode), cpyamt)) {
|
|
pext->Status = EXT_STATUS_COPY_ERR;
|
|
}
|
|
}
|
|
}
|
|
if (bufp) {
|
|
isp_kfree(bufp, pext->ResponseLen);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#define RPT_LUN_SIZE 1024
|
|
|
|
static int
|
|
isp_exti_discover_luns(ispsoftc_t *isp, int chan, UINT64 wwpn, UINT64 wwnn, UINT16 *nluns)
|
|
{
|
|
isp_xcmd_t cmd;
|
|
int status, nent, i, hilun;
|
|
unsigned long flags;
|
|
fcparam *fcp = FCPARAM(isp, chan);
|
|
fcportdb_t *lp;
|
|
uint8_t *bufp;
|
|
|
|
MEMZERO(&cmd, sizeof (isp_xcmd_t));
|
|
ISP_LOCKU_SOFTC(isp);
|
|
for (lp = &fcp->portdb[0]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
continue;
|
|
}
|
|
if (lp->port_wwn == wwpn && lp->node_wwn == wwnn) {
|
|
break;
|
|
}
|
|
}
|
|
if (lp == &fcp->portdb[MAX_FC_TARG]) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
return (-ENODEV);
|
|
}
|
|
if ((lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) == 0) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
*nluns = 0;
|
|
return (0);
|
|
}
|
|
cmd.handle = lp->handle;
|
|
cmd.portid = lp->portid;
|
|
ISP_UNLKU_SOFTC(isp);
|
|
bufp = isp_kzalloc(RPT_LUN_SIZE, GFP_KERNEL);
|
|
if (bufp == NULL) {
|
|
return (-ENOMEM);
|
|
}
|
|
cmd.fcd.beg.data_ptr = bufp;
|
|
cmd.fcd.beg.data_length = RPT_LUN_SIZE;
|
|
cmd.fcd.beg.do_read = 1;
|
|
cmd.fcd.beg.cdb[0] = REPORT_LUNS;
|
|
cmd.fcd.beg.cdb[4] = (RPT_LUN_SIZE >> 24) & 0xff;
|
|
cmd.fcd.beg.cdb[5] = (RPT_LUN_SIZE >> 16) & 0xff;
|
|
cmd.fcd.beg.cdb[6] = (RPT_LUN_SIZE >> 8) & 0xff;
|
|
cmd.fcd.beg.cdb[7] = (RPT_LUN_SIZE) & 0xff;
|
|
cmd.timeout = 30;
|
|
status = isp_run_cmd(isp, &cmd);
|
|
if (status) {
|
|
isp_prt(isp, ISP_LOGWARN, "isp_exti_discover_luns: isp_run_cmd returned %d", status);
|
|
isp_kfree(bufp, RPT_LUN_SIZE);
|
|
return (-EIO);
|
|
}
|
|
nent = (bufp[2] << 8) | bufp[3];
|
|
|
|
|
|
hilun = 0;
|
|
/*
|
|
* This is not *quite* the right way to do this.
|
|
*/
|
|
for (i = 0; i < nent; i++) {
|
|
uint8_t *lunptr = bufp + 8 + (8 * i);
|
|
uint16_t lun;
|
|
lun = lunptr[1];
|
|
if (lunptr[0] & 0x40) {
|
|
lun |= ((lunptr[1] & 0x1f) << 8);
|
|
}
|
|
if (hilun < lun) {
|
|
hilun = lun;
|
|
}
|
|
}
|
|
isp_kfree(bufp, RPT_LUN_SIZE);
|
|
*nluns = hilun + 1;
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
isp_run_cmd_done(struct scsi_cmnd *Cmnd)
|
|
{
|
|
struct semaphore *semap = (struct semaphore *) Cmnd->request_buffer;
|
|
up(semap);
|
|
}
|
|
|
|
static int
|
|
isp_run_cmd(ispsoftc_t *isp, isp_xcmd_t *cmd)
|
|
{
|
|
struct scsi_device *dev = NULL;
|
|
Scsi_Cmnd *Cmnd = NULL;
|
|
struct Scsi_Host *host = NULL;
|
|
uint32_t nxti, optr, handle;
|
|
uint8_t local[QENTRY_LEN];
|
|
ispreq_t *reqp;
|
|
int time, result = 0;
|
|
DECLARE_MUTEX_LOCKED(rsem);
|
|
unsigned long flags;
|
|
|
|
time = cmd->timeout / 1000;
|
|
if (time == 0 && cmd->timeout) {
|
|
time = 1;
|
|
}
|
|
if (IS_24XX(isp) && time > 0x1999) {
|
|
time = 0x1999;
|
|
}
|
|
MEMZERO(local, sizeof (local));
|
|
Cmnd = isp_kzalloc(sizeof (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->request_buffer = &rsem;
|
|
|
|
ISP_LOCKU_SOFTC(isp);
|
|
if (isp_getrqentry(isp, &nxti, &optr, (void *)&reqp)) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
isp_prt(isp, ISP_LOGDEBUG0, "%s: Request Queue Overflow", __FUNCTION__);
|
|
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);
|
|
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, &nxti, optr);
|
|
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_ADD_REQUEST(isp, nxti);
|
|
isp->isp_nactive++;
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
if (result == 0) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
down(&rsem);
|
|
cmd->fcd.end.data_residual = Cmnd->resid;
|
|
cmd->fcd.end.status = Cmnd->SCp.Status;
|
|
if (cmd->fcd.end.status == SCSI_CHECK) {
|
|
MEMCPY(cmd->fcd.end.sense_data, Cmnd->sense_buffer, min(sizeof(cmd->fcd.end.sense_data), sizeof (Cmnd->sense_buffer)));
|
|
}
|
|
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 (Scsi_Cmnd));
|
|
return (result);
|
|
}
|
|
|
|
static int
|
|
isp_perf_test(ispsoftc_t *isp, isp_perftst_t *tt)
|
|
{
|
|
struct scsi_device *dev = NULL;
|
|
Scsi_Cmnd *Cmnd = NULL;
|
|
fcportdb_t *lp;
|
|
struct Scsi_Host *host = NULL;
|
|
uint32_t nxti, optr, handle;
|
|
uint8_t local[QENTRY_LEN];
|
|
int result = 0, i, iswrite, amt;
|
|
void *qep, *qel = local;
|
|
DECLARE_MUTEX_LOCKED(rsem);
|
|
unsigned long flags;
|
|
|
|
if (IS_SCSI(isp)) {
|
|
return (-ENODEV);
|
|
}
|
|
if (tt->target < 0 || tt->target >= MAX_FC_TARG) {
|
|
return (-ENODEV);
|
|
}
|
|
|
|
if (tt->channel < 0 || tt->channel >= isp->isp_nchan) {
|
|
return (-ENODEV);
|
|
}
|
|
|
|
iswrite = 0;
|
|
if (tt->ioszdir) {
|
|
amt = tt->ioszdir;
|
|
if (amt < 0) {
|
|
iswrite = 1;
|
|
amt = -amt;
|
|
}
|
|
if (amt > ISP_FC_SCRLEN) {
|
|
return (-ENOMEM);
|
|
}
|
|
} else {
|
|
amt = 0;
|
|
}
|
|
i = FCPARAM(isp, tt->channel)->isp_ini_map[tt->target] - 1;
|
|
if (i < 0 || i >= MAX_FC_TARG) {
|
|
return (-ENXIO);
|
|
}
|
|
lp = &FCPARAM(isp, tt->channel)->portdb[i];
|
|
if (lp->state != FC_PORTDB_STATE_VALID) {
|
|
return (-ENXIO);
|
|
}
|
|
|
|
MEMZERO(local, sizeof (local));
|
|
Cmnd = isp_kzalloc(sizeof (Scsi_Cmnd), GFP_KERNEL);
|
|
if (Cmnd == NULL) {
|
|
return (-ENOMEM);
|
|
}
|
|
host = isp->isp_osinfo.host;
|
|
dev = isp_kzalloc(sizeof (struct scsi_device), GFP_KERNEL);
|
|
if (dev == NULL) {
|
|
isp_kfree(Cmnd, sizeof (Scsi_Cmnd));
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
Cmnd->device = dev;
|
|
dev->host = host;
|
|
Cmnd->scsi_done = isp_run_cmd_done;
|
|
Cmnd->request_buffer = &rsem;
|
|
Cmnd->cmd_len = 6;
|
|
Cmnd->sc_data_direction = SCSI_DATA_NONE;
|
|
|
|
if (IS_24XX(isp)) {
|
|
ispreqt7_t *t7 = (ispreqt7_t *) local;
|
|
|
|
t7->req_header.rqs_entry_type = RQSTYPE_T7RQS;
|
|
t7->req_header.rqs_entry_count = 1;
|
|
t7->req_task_attribute = FCP_CMND_TASK_ATTR_SIMPLE;
|
|
t7->req_nphdl = lp->handle;
|
|
t7->req_tidlo = lp->portid;
|
|
t7->req_tidhi = lp->portid >> 16;
|
|
if (tt->lun > 256) {
|
|
t7->req_lun[0] = tt->lun >> 8;
|
|
t7->req_lun[0] |= 0x40;
|
|
}
|
|
t7->req_lun[1] = tt->lun & 0xff;
|
|
t7->req_time = 30;
|
|
if (amt) {
|
|
t7->req_seg_count = 1;
|
|
if (iswrite) {
|
|
t7->req_alen_datadir = FCP_CMND_DATA_WRITE;
|
|
t7->req_cdb[0] = 0xa;
|
|
} else {
|
|
t7->req_alen_datadir = FCP_CMND_DATA_READ;
|
|
t7->req_cdb[0] = 0x8;
|
|
}
|
|
t7->req_cdb[4] = amt >> 9;
|
|
t7->req_dl = amt;
|
|
t7->req_dataseg.ds_base = DMA_LO32(FCPARAM(isp, tt->channel)->isp_scdma);
|
|
t7->req_dataseg.ds_basehi = DMA_HI32(FCPARAM(isp, tt->channel)->isp_scdma);
|
|
t7->req_dataseg.ds_count = amt;
|
|
} else {
|
|
t7->req_seg_count = 0;
|
|
}
|
|
t7->req_vpidx = tt->channel;
|
|
} else {
|
|
ispreqt3_t *t3 = (ispreqt3_t *) local;
|
|
|
|
t3->req_header.rqs_entry_type = RQSTYPE_T3RQS;
|
|
t3->req_header.rqs_entry_count = 1;
|
|
t3->req_flags = REQFLAG_STAG;
|
|
|
|
if (amt) {
|
|
if (iswrite) {
|
|
t3->req_flags |= REQFLAG_DATA_OUT;
|
|
t3->req_cdb[0] = 0xa;
|
|
} else {
|
|
t3->req_flags |= REQFLAG_DATA_IN;
|
|
t3->req_cdb[0] = 0x8;
|
|
}
|
|
t3->req_cdb[4] = amt >> 9;
|
|
t3->req_dataseg[0].ds_base = DMA_LO32(FCPARAM(isp, tt->channel)->isp_scdma);
|
|
t3->req_dataseg[0].ds_basehi = DMA_HI32(FCPARAM(isp, tt->channel)->isp_scdma);
|
|
t3->req_dataseg[0].ds_count = amt;
|
|
t3->req_totalcnt = amt;
|
|
}
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
((ispreqt3e_t *)t3)->req_target = lp->handle;
|
|
((ispreqt3e_t *)t3)->req_scclun = tt->lun;
|
|
} else if (ISP_CAP_SCCFW(isp)) {
|
|
t3->req_target = lp->handle;
|
|
t3->req_scclun = tt->lun;
|
|
} else {
|
|
t3->req_target = lp->handle;
|
|
t3->req_lun_trn = tt->lun;
|
|
}
|
|
t3->req_time = 30;
|
|
}
|
|
|
|
for (i = 0; i < tt->count; i++) {
|
|
ISP_LOCKU_SOFTC(isp);
|
|
if (isp_getrqentry(isp, &nxti, &optr, (void *)&qep)) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
__set_current_state(TASK_UNINTERRUPTIBLE);
|
|
(void) schedule_timeout(1);
|
|
i -= 1;
|
|
continue;
|
|
}
|
|
if (i == tt->count - 1) {
|
|
if (isp_save_xs(isp, Cmnd, &handle)) {
|
|
ISP_UNLKU_SOFTC(isp);
|
|
isp_prt(isp, ISP_LOGERR, "out of xflist pointers");
|
|
result = -ENOMEM;
|
|
goto out;
|
|
}
|
|
} else {
|
|
handle = ISP_SPCL_HANDLE;
|
|
}
|
|
((ispreq_t *)local)->req_handle = handle;
|
|
if (IS_24XX(isp)) {
|
|
isp_put_request_t7(isp, qel, qep);
|
|
} else {
|
|
if (ISP_CAP_2KLOGIN(isp)) {
|
|
isp_put_request_t2e(isp, qel, qep);
|
|
} else {
|
|
isp_put_request_t2(isp, qel, qep);
|
|
}
|
|
}
|
|
ISP_ADD_REQUEST(isp, nxti);
|
|
if (handle != ISP_SPCL_HANDLE) {
|
|
isp->isp_nactive++;
|
|
ISP_UNLKU_SOFTC(isp);
|
|
down(&rsem);
|
|
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 (Scsi_Cmnd));
|
|
return (result);
|
|
}
|
|
#endif /* CONFIG_PROC_FS */
|
|
/*
|
|
* vim:ts=4:sw=4:expandtab
|
|
*/
|