freebsd-nq/sys/dev/isp/isp_freebsd.c
Matt Jacob f9e908dcf2 Clean up lun width determination based upon f/w revisions
for the parallel SCSI cards (4.55..4.65 :: 8.55..8.65).
1999-12-20 01:35:04 +00:00

860 lines
24 KiB
C

/* $FreeBSD$ */
/*
* Platform (FreeBSD) dependent common attachment code for Qlogic adapters.
*
*---------------------------------------
* Copyright (c) 1997, 1998, 1999 by Matthew Jacob
* NASA/Ames Research Center
* 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 immediately at the beginning of the file, without modification,
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
*/
#include <dev/isp/isp_freebsd.h>
static void isp_cam_async(void *, u_int32_t, struct cam_path *, void *);
static void isp_poll(struct cam_sim *);
static void isp_action(struct cam_sim *, union ccb *);
static void isp_relsim(void *);
/* #define ISP_LUN0_ONLY 1 */
#ifdef DEBUG
int isp_debug = 2;
#elif defined(CAMDEBUG) || defined(DIAGNOSTIC)
int isp_debug = 1;
#else
int isp_debug = 0;
#endif
void
isp_attach(struct ispsoftc *isp)
{
int primary, secondary;
struct ccb_setasync csa;
struct cam_devq *devq;
struct cam_sim *sim;
struct cam_path *path;
/*
* Establish (in case of 12X0) which bus is the primary.
*/
primary = 0;
secondary = 1;
/*
* Create the device queue for our SIM(s).
*/
devq = cam_simq_alloc(isp->isp_maxcmds);
if (devq == NULL) {
return;
}
/*
* Construct our SIM entry.
*/
sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp,
isp->isp_unit, 1, isp->isp_maxcmds, devq);
if (sim == NULL) {
cam_simq_free(devq);
return;
}
if (xpt_bus_register(sim, primary) != CAM_SUCCESS) {
cam_sim_free(sim, TRUE);
return;
}
if (xpt_create_path(&path, NULL, cam_sim_path(sim),
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
xpt_bus_deregister(cam_sim_path(sim));
cam_sim_free(sim, TRUE);
return;
}
xpt_setup_ccb(&csa.ccb_h, path, 5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_LOST_DEVICE;
csa.callback = isp_cam_async;
csa.callback_arg = sim;
xpt_action((union ccb *)&csa);
isp->isp_sim = sim;
isp->isp_path = path;
/*
* If we have a second channel, construct SIM entry for that.
*/
if (IS_DUALBUS(isp)) {
sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp,
isp->isp_unit, 1, isp->isp_maxcmds, devq);
if (sim == NULL) {
xpt_bus_deregister(cam_sim_path(isp->isp_sim));
xpt_free_path(isp->isp_path);
cam_simq_free(devq);
return;
}
if (xpt_bus_register(sim, secondary) != CAM_SUCCESS) {
xpt_bus_deregister(cam_sim_path(isp->isp_sim));
xpt_free_path(isp->isp_path);
cam_sim_free(sim, TRUE);
return;
}
if (xpt_create_path(&path, NULL, cam_sim_path(sim),
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
xpt_bus_deregister(cam_sim_path(isp->isp_sim));
xpt_free_path(isp->isp_path);
xpt_bus_deregister(cam_sim_path(sim));
cam_sim_free(sim, TRUE);
return;
}
xpt_setup_ccb(&csa.ccb_h, path, 5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_LOST_DEVICE;
csa.callback = isp_cam_async;
csa.callback_arg = sim;
xpt_action((union ccb *)&csa);
isp->isp_sim2 = sim;
isp->isp_path2 = path;
}
if (isp->isp_state == ISP_INITSTATE) {
isp->isp_state = ISP_RUNSTATE;
}
}
static void
isp_cam_async(void *cbarg, u_int32_t code, struct cam_path *path, void *arg)
{
struct cam_sim *sim;
struct ispsoftc *isp;
sim = (struct cam_sim *)cbarg;
isp = (struct ispsoftc *) cam_sim_softc(sim);
switch (code) {
case AC_LOST_DEVICE:
if (IS_SCSI(isp)) {
u_int16_t oflags, nflags;
sdparam *sdp = isp->isp_param;
int s, rvf, tgt;
tgt = xpt_path_target_id(path);
rvf = ISP_FW_REVX(isp->isp_fwrev);
s = splcam();
sdp += cam_sim_bus(sim);
isp->isp_update |= (1 << cam_sim_bus(sim));
nflags = DPARM_SAFE_DFLT;
if (rvf >= ISP_FW_REV(7, 55, 0) ||
(ISP_FW_REV(4, 55, 0) <= rvf &&
(rvf < ISP_FW_REV(5, 0, 0)))) {
nflags |= DPARM_NARROW | DPARM_ASYNC;
}
oflags = sdp->isp_devparam[tgt].dev_flags;
sdp->isp_devparam[tgt].dev_flags = nflags;
sdp->isp_devparam[tgt].dev_update = 1;
(void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL);
sdp->isp_devparam[tgt].dev_flags = oflags;
(void) splx(s);
}
break;
default:
printf("%s: isp_attach Async Code 0x%x\n", isp->isp_name, code);
break;
}
}
static void
isp_poll(struct cam_sim *sim)
{
isp_intr((struct ispsoftc *) cam_sim_softc(sim));
}
static void
isp_relsim(void *arg)
{
struct ispsoftc *isp = arg;
int s = splcam();
if (isp->isp_osinfo.simqfrozen & SIMQFRZ_TIMED) {
int wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_TIMED;
isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_TIMED;
if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) {
xpt_release_simq(isp->isp_sim, 1);
IDPRINTF(3, ("%s: timed relsimq\n", isp->isp_name));
}
}
splx(s);
}
static void
isp_action(struct cam_sim *sim, union ccb *ccb)
{
int s, bus, tgt, error;
struct ispsoftc *isp;
struct ccb_trans_settings *cts;
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n"));
isp = (struct ispsoftc *)cam_sim_softc(sim);
ccb->ccb_h.sim_priv.entries[0].field = 0;
ccb->ccb_h.sim_priv.entries[1].ptr = isp;
if (isp->isp_state != ISP_RUNSTATE &&
ccb->ccb_h.func_code == XPT_SCSI_IO) {
s = splcam();
DISABLE_INTS(isp);
isp_init(isp);
if (isp->isp_state != ISP_INITSTATE) {
(void) splx(s);
/*
* Lie. Say it was a selection timeout.
*/
ccb->ccb_h.status = CAM_SEL_TIMEOUT;
ccb->ccb_h.status |= CAM_DEV_QFRZN;
xpt_freeze_devq(ccb->ccb_h.path, 1);
xpt_done(ccb);
return;
}
isp->isp_state = ISP_RUNSTATE;
ENABLE_INTS(isp);
(void) splx(s);
}
IDPRINTF(4, ("%s: isp_action code %x\n", isp->isp_name,
ccb->ccb_h.func_code));
switch (ccb->ccb_h.func_code) {
case XPT_SCSI_IO: /* Execute the requested I/O operation */
/*
* Do a couple of preliminary checks...
*/
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) {
if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) {
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
break;
}
}
#ifdef DIAGNOSTIC
if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) {
ccb->ccb_h.status = CAM_PATH_INVALID;
} else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) {
ccb->ccb_h.status = CAM_PATH_INVALID;
}
if (ccb->ccb_h.status == CAM_PATH_INVALID) {
printf("%s: invalid tgt/lun (%d.%d) in XPT_SCSI_IO\n",
isp->isp_name, ccb->ccb_h.target_id,
ccb->ccb_h.target_lun);
xpt_done(ccb);
break;
}
#endif
((struct ccb_scsiio *) ccb)->scsi_status = SCSI_STATUS_OK;
s = splcam();
DISABLE_INTS(isp);
error = ispscsicmd((ISP_SCSI_XFER_T *) ccb);
ENABLE_INTS(isp);
splx(s);
switch (error) {
case CMD_QUEUED:
ccb->ccb_h.status |= CAM_SIM_QUEUED;
break;
case CMD_RQLATER:
if (isp->isp_osinfo.simqfrozen == 0) {
IDPRINTF(3, ("%s: RQLATER freeze simq\n",
isp->isp_name));
isp->isp_osinfo.simqfrozen |= SIMQFRZ_TIMED;
timeout(isp_relsim, isp, 500);
xpt_freeze_simq(sim, 1);
}
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_REQUEUE_REQ;
xpt_done(ccb);
break;
case CMD_EAGAIN:
if (isp->isp_osinfo.simqfrozen == 0) {
xpt_freeze_simq(sim, 1);
IDPRINTF(3, ("%s: EAGAIN freeze simq\n",
isp->isp_name));
}
isp->isp_osinfo.simqfrozen |= SIMQFRZ_RESOURCE;
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_REQUEUE_REQ;
xpt_done(ccb);
break;
case CMD_COMPLETE:
isp_done((struct ccb_scsiio *) ccb);
break;
default:
printf("%s: What's this? 0x%x at %d in file %s\n",
isp->isp_name, error, __LINE__, __FILE__);
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
xpt_done(ccb);
}
break;
case XPT_EN_LUN: /* Enable LUN as a target */
case XPT_TARGET_IO: /* Execute target I/O request */
case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */
case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
break;
case XPT_RESET_DEV: /* BDR the specified SCSI device */
tgt = ccb->ccb_h.target_id; /* XXX: Which Bus? */
s = splcam();
error = isp_control(isp, ISPCTL_RESET_DEV, &tgt);
(void) splx(s);
if (error) {
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
} else {
ccb->ccb_h.status = CAM_REQ_CMP;
}
xpt_done(ccb);
break;
case XPT_ABORT: /* Abort the specified CCB */
s = splcam();
error = isp_control(isp, ISPCTL_ABORT_CMD, ccb);
(void) splx(s);
if (error) {
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
} else {
ccb->ccb_h.status = CAM_REQ_CMP;
}
xpt_done(ccb);
break;
case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */
cts = &ccb->cts;
tgt = cts->ccb_h.target_id;
s = splcam();
if (IS_SCSI(isp)) {
sdparam *sdp = isp->isp_param;
u_int16_t *dptr;
int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
sdp += bus;
#if 0
if (cts->flags & CCB_TRANS_CURRENT_SETTINGS)
dptr = &sdp->isp_devparam[tgt].cur_dflags;
else
dptr = &sdp->isp_devparam[tgt].dev_flags;
#else
/*
* We always update (internally) from dev_flags
* so any request to change settings just gets
* vectored to that location.
*/
dptr = &sdp->isp_devparam[tgt].dev_flags;
#endif
/*
* Note that these operations affect the
* the goal flags (dev_flags)- not
* the current state flags. Then we mark
* things so that the next operation to
* this HBA will cause the update to occur.
*/
if (cts->valid & CCB_TRANS_DISC_VALID) {
if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) {
*dptr |= DPARM_DISC;
} else {
*dptr &= ~DPARM_DISC;
}
}
if (cts->valid & CCB_TRANS_TQ_VALID) {
if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) {
*dptr |= DPARM_TQING;
} else {
*dptr &= ~DPARM_TQING;
}
}
if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) {
switch (cts->bus_width) {
case MSG_EXT_WDTR_BUS_16_BIT:
*dptr |= DPARM_WIDE;
break;
default:
*dptr &= ~DPARM_WIDE;
}
}
/*
* Any SYNC RATE of nonzero and SYNC_OFFSET
* of nonzero will cause us to go to the
* selected (from NVRAM) maximum value for
* this device. At a later point, we'll
* allow finer control.
*/
if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) &&
(cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) &&
(cts->sync_offset > 0)) {
*dptr |= DPARM_SYNC;
} else {
*dptr &= ~DPARM_SYNC;
}
*dptr |= DPARM_SAFE_DFLT;
if (bootverbose || isp->isp_dblev >= 3)
printf("%s: %d.%d set %s period 0x%x offset "
"0x%x flags 0x%x\n", isp->isp_name, bus,
tgt,
(cts->flags & CCB_TRANS_CURRENT_SETTINGS)?
"current" : "user",
sdp->isp_devparam[tgt].sync_period,
sdp->isp_devparam[tgt].sync_offset,
sdp->isp_devparam[tgt].dev_flags);
sdp->isp_devparam[tgt].dev_update = 1;
isp->isp_update |= (1 << bus);
(void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL);
}
(void) splx(s);
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
case XPT_GET_TRAN_SETTINGS:
cts = &ccb->cts;
tgt = cts->ccb_h.target_id;
if (IS_FC(isp)) {
/*
* a lot of normal SCSI things don't make sense.
*/
cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB;
cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
/*
* How do you measure the width of a high
* speed serial bus? Well, in bytes.
*
* Offset and period make no sense, though, so we set
* (above) a 'base' transfer speed to be gigabit.
*/
cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
} else {
sdparam *sdp = isp->isp_param;
u_int16_t dval, pval, oval;
int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
sdp += bus;
if (cts->flags & CCB_TRANS_CURRENT_SETTINGS) {
s = splcam();
/*
* First do a refresh to see if things
* have changed recently!
*/
sdp->isp_devparam[tgt].dev_refresh = 1;
isp->isp_update |= (1 << bus);
(void) isp_control(isp, ISPCTL_UPDATE_PARAMS,
NULL);
(void) splx(s);
dval = sdp->isp_devparam[tgt].cur_dflags;
oval = sdp->isp_devparam[tgt].cur_offset;
pval = sdp->isp_devparam[tgt].cur_period;
} else {
dval = sdp->isp_devparam[tgt].dev_flags;
oval = sdp->isp_devparam[tgt].sync_offset;
pval = sdp->isp_devparam[tgt].sync_period;
}
s = splcam();
cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
if (dval & DPARM_DISC) {
cts->flags |= CCB_TRANS_DISC_ENB;
}
if (dval & DPARM_TQING) {
cts->flags |= CCB_TRANS_TAG_ENB;
}
if (dval & DPARM_WIDE) {
cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
} else {
cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
}
cts->valid = CCB_TRANS_BUS_WIDTH_VALID |
CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
if ((dval & DPARM_SYNC) && oval != 0) {
cts->sync_period = pval;
cts->sync_offset = oval;
cts->valid |=
CCB_TRANS_SYNC_RATE_VALID |
CCB_TRANS_SYNC_OFFSET_VALID;
}
splx(s);
if (bootverbose || isp->isp_dblev >= 3)
printf("%s: %d.%d get %s period 0x%x offset "
"0x%x flags 0x%x\n", isp->isp_name, bus,
tgt,
(cts->flags & CCB_TRANS_CURRENT_SETTINGS)?
"current" : "user", pval, oval, dval);
}
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
case XPT_CALC_GEOMETRY:
{
struct ccb_calc_geometry *ccg;
u_int32_t secs_per_cylinder;
u_int32_t size_mb;
ccg = &ccb->ccg;
if (ccg->block_size == 0) {
printf("%s: %d.%d XPT_CALC_GEOMETRY block size 0?\n",
isp->isp_name, ccg->ccb_h.target_id,
ccg->ccb_h.target_lun);
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
break;
}
size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size);
if (size_mb > 1024) {
ccg->heads = 255;
ccg->secs_per_track = 63;
} else {
ccg->heads = 64;
ccg->secs_per_track = 32;
}
secs_per_cylinder = ccg->heads * ccg->secs_per_track;
ccg->cylinders = ccg->volume_size / secs_per_cylinder;
ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
}
case XPT_RESET_BUS: /* Reset the specified bus */
bus = cam_sim_bus(sim);
s = splcam();
error = isp_control(isp, ISPCTL_RESET_BUS, &bus);
(void) splx(s);
if (error)
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
else {
if (cam_sim_bus(sim) && isp->isp_path2 != NULL)
xpt_async(AC_BUS_RESET, isp->isp_path2, NULL);
else if (isp->isp_path != NULL)
xpt_async(AC_BUS_RESET, isp->isp_path, NULL);
ccb->ccb_h.status = CAM_REQ_CMP;
}
xpt_done(ccb);
break;
case XPT_TERM_IO: /* Terminate the I/O process */
/* Does this need to be implemented? */
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
break;
case XPT_PATH_INQ: /* Path routing inquiry */
{
struct ccb_pathinq *cpi = &ccb->cpi;
cpi->version_num = 1;
cpi->target_sprt = 0;
cpi->hba_eng_cnt = 0;
cpi->max_target = ISP_MAX_TARGETS(isp) - 1;
cpi->max_lun = ISP_MAX_LUNS(isp) - 1;
cpi->bus_id = cam_sim_bus(sim);
if (IS_FC(isp)) {
cpi->hba_misc = PIM_NOBUSRESET;
/*
* Because our loop ID can shift from time to time,
* make our initiator ID out of range of our bus.
*/
cpi->initiator_id = cpi->max_target + 1;
/*
* Set base transfer capabilities for Fibre Channel.
* Technically not correct because we don't know
* what media we're running on top of- but we'll
* look good if we always say 100MB/s.
*/
cpi->base_transfer_speed = 100000;
cpi->hba_inquiry = PI_TAG_ABLE;
} else {
sdparam *sdp = isp->isp_param;
sdp += cam_sim_bus(xpt_path_sim(cpi->ccb_h.path));
cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
cpi->hba_misc = 0;
cpi->initiator_id = sdp->isp_initiator_id;
cpi->base_transfer_speed = 3300;
}
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN);
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
cpi->unit_number = cam_sim_unit(sim);
cpi->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb);
break;
}
default:
ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(ccb);
break;
}
}
#define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB)
void
isp_done(struct ccb_scsiio *sccb)
{
struct ispsoftc *isp = XS_ISP(sccb);
if (XS_NOERR(sccb))
XS_SETERR(sccb, CAM_REQ_CMP);
sccb->ccb_h.status &= ~CAM_STATUS_MASK;
sccb->ccb_h.status |= sccb->ccb_h.spriv_field0;
if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP &&
(sccb->scsi_status != SCSI_STATUS_OK)) {
sccb->ccb_h.status &= ~CAM_STATUS_MASK;
sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
}
sccb->ccb_h.status &= ~CAM_SIM_QUEUED;
if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
sccb->ccb_h.status |= CAM_DEV_QFRZN;
xpt_freeze_devq(sccb->ccb_h.path, 1);
if (sccb->scsi_status != SCSI_STATUS_OK)
IDPRINTF(3, ("%s: fdevq %d.%d %x %x\n",
isp->isp_name, sccb->ccb_h.target_id,
sccb->ccb_h.target_lun, sccb->ccb_h.status,
sccb->scsi_status));
}
}
/*
* If we were frozen waiting resources, clear that we were frozen
* waiting for resources. If we are no longer frozen, and the devq
* isn't frozen, mark the completing CCB to have the XPT layer
* release the simq.
*/
if (isp->isp_osinfo.simqfrozen & SIMQFRZ_RESOURCE) {
isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_RESOURCE;
if (isp->isp_osinfo.simqfrozen == 0) {
if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
IDPRINTF(3, ("%s: isp_done -> relsimq\n",
isp->isp_name));
sccb->ccb_h.status |= CAM_RELEASE_SIMQ;
} else {
IDPRINTF(3, ("%s: isp_done -> devq frozen\n",
isp->isp_name));
}
} else {
IDPRINTF(3, ("%s: isp_done -> simqfrozen = %x\n",
isp->isp_name, isp->isp_osinfo.simqfrozen));
}
}
if (CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB) &&
(sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
xpt_print_path(sccb->ccb_h.path);
printf("cam completion status 0x%x\n", sccb->ccb_h.status);
}
xpt_done((union ccb *) sccb);
}
int
isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg)
{
int bus, rv = 0;
switch (cmd) {
case ISPASYNC_NEW_TGT_PARAMS:
{
int flags, tgt;
sdparam *sdp = isp->isp_param;
struct ccb_trans_settings neg;
struct cam_path *tmppath;
tgt = *((int *)arg);
bus = (tgt >> 16) & 0xffff;
tgt &= 0xffff;
sdp += bus;
if (xpt_create_path(&tmppath, NULL,
cam_sim_path(bus? isp->isp_sim2 : isp->isp_sim),
tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
xpt_print_path(isp->isp_path);
printf("isp_async cannot make temp path for "
"target %d bus %d\n", tgt, bus);
rv = -1;
break;
}
flags = sdp->isp_devparam[tgt].cur_dflags;
neg.valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
if (flags & DPARM_DISC) {
neg.flags |= CCB_TRANS_DISC_ENB;
}
if (flags & DPARM_TQING) {
neg.flags |= CCB_TRANS_TAG_ENB;
}
neg.valid |= CCB_TRANS_BUS_WIDTH_VALID;
neg.bus_width = (flags & DPARM_WIDE)?
MSG_EXT_WDTR_BUS_8_BIT : MSG_EXT_WDTR_BUS_16_BIT;
neg.sync_period = sdp->isp_devparam[tgt].cur_period;
neg.sync_offset = sdp->isp_devparam[tgt].cur_offset;
if (flags & DPARM_SYNC) {
neg.valid |=
CCB_TRANS_SYNC_RATE_VALID |
CCB_TRANS_SYNC_OFFSET_VALID;
}
IDPRINTF(3, ("%s: NEW_TGT_PARAMS bus %d tgt %d period "
"0x%x offset 0x%x flags 0x%x\n", isp->isp_name,
bus, tgt, neg.sync_period, neg.sync_offset, flags));
xpt_setup_ccb(&neg.ccb_h, tmppath, 1);
xpt_async(AC_TRANSFER_NEG, tmppath, &neg);
xpt_free_path(tmppath);
break;
}
case ISPASYNC_BUS_RESET:
bus = *((int *)arg);
printf("%s: SCSI bus reset on bus %d detected\n",
isp->isp_name, bus);
if (bus > 0 && isp->isp_path2) {
xpt_async(AC_BUS_RESET, isp->isp_path2, NULL);
} else if (isp->isp_path) {
xpt_async(AC_BUS_RESET, isp->isp_path, NULL);
}
break;
case ISPASYNC_LOOP_DOWN:
if (isp->isp_path) {
if (isp->isp_osinfo.simqfrozen == 0) {
IDPRINTF(3, ("%s: loop down freeze simq\n",
isp->isp_name));
xpt_freeze_simq(isp->isp_sim, 1);
}
isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN;
}
printf("%s: Loop DOWN\n", isp->isp_name);
break;
case ISPASYNC_LOOP_UP:
if (isp->isp_path) {
int wasfrozen =
isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN;
isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN;
if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) {
xpt_release_simq(isp->isp_sim, 1);
IDPRINTF(3, ("%s: loop up release simq\n",
isp->isp_name));
}
}
printf("%s: Loop UP\n", isp->isp_name);
break;
case ISPASYNC_PDB_CHANGED:
{
const char *fmt = "%s: Target %d (Loop 0x%x) Port ID 0x%x "
"role %s %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x\n";
const static char *roles[4] = {
"(none)", "Target", "Initiator", "Target/Initiator"
};
char *ptr;
fcparam *fcp = isp->isp_param;
int tgt = *((int *) arg);
struct lportdb *lp = &fcp->portdb[tgt];
if (lp->valid) {
ptr = "arrived";
} else {
ptr = "disappeared";
}
printf(fmt, isp->isp_name, tgt, lp->loopid, lp->portid,
roles[lp->roles & 0x3], ptr,
(u_int32_t) (lp->port_wwn >> 32),
(u_int32_t) (lp->port_wwn & 0xffffffffLL),
(u_int32_t) (lp->node_wwn >> 32),
(u_int32_t) (lp->node_wwn & 0xffffffffLL));
break;
}
case ISPASYNC_CHANGE_NOTIFY:
printf("%s: Name Server Database Changed\n", isp->isp_name);
break;
#ifdef ISP2100_FABRIC
case ISPASYNC_FABRIC_DEV:
{
int target;
struct lportdb *lp;
sns_scrsp_t *resp = (sns_scrsp_t *) arg;
u_int32_t portid;
u_int64_t wwn;
fcparam *fcp = isp->isp_param;
rv = -1;
portid =
(((u_int32_t) resp->snscb_port_id[0]) << 16) |
(((u_int32_t) resp->snscb_port_id[1]) << 8) |
(((u_int32_t) resp->snscb_port_id[2]));
wwn =
(((u_int64_t)resp->snscb_portname[0]) << 56) |
(((u_int64_t)resp->snscb_portname[1]) << 48) |
(((u_int64_t)resp->snscb_portname[2]) << 40) |
(((u_int64_t)resp->snscb_portname[3]) << 32) |
(((u_int64_t)resp->snscb_portname[4]) << 24) |
(((u_int64_t)resp->snscb_portname[5]) << 16) |
(((u_int64_t)resp->snscb_portname[6]) << 8) |
(((u_int64_t)resp->snscb_portname[7]));
printf("%s: type 0x%x@portid 0x%x 0x%08x%08x\n", isp->isp_name,
resp->snscb_port_type, portid,
((u_int32_t) (wwn >> 32)), ((u_int32_t) wwn));
if (resp->snscb_port_type != 2) {
rv = 0;
break;
}
for (target = FC_SNS_ID+1; target < MAX_FC_TARG; target++) {
lp = &fcp->portdb[target];
if (lp->port_wwn == wwn)
break;
}
if (target < MAX_FC_TARG) {
rv = 0;
break;
}
for (target = FC_SNS_ID+1; target < MAX_FC_TARG; target++) {
lp = &fcp->portdb[target];
if (lp->port_wwn == 0)
break;
}
if (target == MAX_FC_TARG) {
printf("%s: no more space for fabric devices\n",
isp->isp_name);
break;
}
lp->port_wwn = lp->node_wwn = wwn;
lp->portid = portid;
rv = 0;
break;
}
#endif
default:
rv = -1;
break;
}
return (rv);
}
/*
* Locks are held before coming here.
*/
void
isp_uninit(struct ispsoftc *isp)
{
ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
DISABLE_INTS(isp);
}