1224 lines
32 KiB
C
1224 lines
32 KiB
C
|
/* $FreeBSD$ */
|
||
|
/*
|
||
|
* FreeBSD/CAM specific routines for LSI '909 FC adapters.
|
||
|
* FreeBSD Version.
|
||
|
*
|
||
|
* Copyright (c) 2000, 2001 by Greg Ansley
|
||
|
*
|
||
|
* 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. 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.
|
||
|
*/
|
||
|
/*
|
||
|
* Additional Copyright (c) 2002 by Matthew Jacob under same license.
|
||
|
*/
|
||
|
|
||
|
#include <dev/mpt/mpt_freebsd.h>
|
||
|
#include <dev/mpt/mpt.h>
|
||
|
#include <machine/stdarg.h>
|
||
|
#include <sys/signal.h>
|
||
|
|
||
|
#if 0
|
||
|
static void mpt_cam_async(void *, u_int32_t, struct cam_path *, void *);
|
||
|
static void mpt_intr_enable(void *);
|
||
|
#endif
|
||
|
static void mpt_poll(struct cam_sim *);
|
||
|
static timeout_t mpttimeout;
|
||
|
static void mpt_action(struct cam_sim *, union ccb *);
|
||
|
|
||
|
void
|
||
|
mpt_cam_attach(struct mpt_softc *mpt)
|
||
|
{
|
||
|
struct cam_devq *devq;
|
||
|
struct cam_sim *sim;
|
||
|
int maxq;
|
||
|
|
||
|
mpt->bus = 0;
|
||
|
maxq = (mpt->mpt_global_credits < MPT_MAX_REQUESTS)?
|
||
|
mpt->mpt_global_credits : MPT_MAX_REQUESTS;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Create the device queue for our SIM(s).
|
||
|
*/
|
||
|
|
||
|
devq = cam_simq_alloc(maxq);
|
||
|
if (devq == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Construct our SIM entry.
|
||
|
*/
|
||
|
sim = cam_sim_alloc(mpt_action, mpt_poll, "mpt", mpt,
|
||
|
mpt->unit, 1, maxq, devq);
|
||
|
if (sim == NULL) {
|
||
|
cam_simq_free(devq);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
mpt->mpt_osinfo.ehook.ich_func = mpt_intr_enable;
|
||
|
mpt->mpt_osinfo.ehook.ich_arg = mpt;
|
||
|
if (config_intrhook_establish(&mpt->mpt_osinfo.ehook) != 0) {
|
||
|
device_printf(mpt->dev,
|
||
|
"could not establish interrupt enable hook\n");
|
||
|
cam_sim_free(sim, TRUE);
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Register exactly one bus */
|
||
|
/* (Duals look like two cards to us) */
|
||
|
if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
|
||
|
cam_sim_free(sim, TRUE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (xpt_create_path(&mpt->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;
|
||
|
}
|
||
|
|
||
|
#if 0 /* GJA Don't think we care about lost devices */
|
||
|
struct ccb_setasync csa;
|
||
|
|
||
|
xpt_setup_ccb(&csa.ccb_h, mpt->path, 5);
|
||
|
csa.ccb_h.func_code = XPT_SASYNC_CB;
|
||
|
csa.event_enable = AC_LOST_DEVICE;
|
||
|
csa.callback = mpt_cam_async;
|
||
|
csa.callback_arg = sim;
|
||
|
xpt_action((union ccb *)&csa);
|
||
|
#endif
|
||
|
mpt->sim = sim;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
mpt_cam_detach(struct mpt_softc *mpt)
|
||
|
{
|
||
|
if (mpt->sim != NULL) {
|
||
|
xpt_free_path(mpt->path);
|
||
|
xpt_bus_deregister(cam_sim_path(mpt->sim));
|
||
|
cam_sim_free(mpt->sim, TRUE);
|
||
|
mpt->sim = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/* Sample routine for processing async events from CAM */
|
||
|
static void
|
||
|
mpt_cam_async(void *cbarg, u_int32_t code, struct cam_path *path, void *arg)
|
||
|
{
|
||
|
struct cam_sim *sim;
|
||
|
struct mpt_softc *mpt;
|
||
|
|
||
|
sim = (struct cam_sim *)cbarg;
|
||
|
mpt = (struct mpt_softc *) cam_sim_softc(sim);
|
||
|
switch (code) {
|
||
|
#if 0 /* GJA */
|
||
|
case AC_LOST_DEVICE:
|
||
|
if (IS_SCSI(mpt)) {
|
||
|
u_int16_t oflags, nflags;
|
||
|
sdparam *sdp = mpt->mpt_param;
|
||
|
int rvf, tgt;
|
||
|
|
||
|
tgt = xpt_path_target_id(path);
|
||
|
rvf = ISP_FW_REVX(mpt->mpt_fwrev);
|
||
|
ISP_LOCK(mpt);
|
||
|
sdp += cam_sim_bus(sim);
|
||
|
mpt->mpt_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->mpt_devparam[tgt].dev_flags;
|
||
|
sdp->mpt_devparam[tgt].dev_flags = nflags;
|
||
|
sdp->mpt_devparam[tgt].dev_update = 1;
|
||
|
(void) mpt_control(mpt, ISPCTL_UPDATE_PARAMS, NULL);
|
||
|
sdp->mpt_devparam[tgt].dev_flags = oflags;
|
||
|
ISP_UNLOCK(mpt);
|
||
|
}
|
||
|
break;
|
||
|
#endif /* 0 GJA */
|
||
|
default:
|
||
|
device_printf(mpt->dev,
|
||
|
"mpt Async Code 0x%x\n", code);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* This routine is used after a system crash to dump core onto the
|
||
|
* swap device.
|
||
|
*/
|
||
|
static void
|
||
|
mpt_poll(struct cam_sim *sim)
|
||
|
{
|
||
|
mpt_intr((struct mpt_softc *) cam_sim_softc(sim));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This routine is called if the 9x9 does not return completion status
|
||
|
* for a command after a CAM specified time.
|
||
|
*/
|
||
|
static void
|
||
|
mpttimeout(void *arg)
|
||
|
{
|
||
|
request_t *req;
|
||
|
union ccb *ccb = arg;
|
||
|
struct mpt_softc *mpt;
|
||
|
|
||
|
mpt = ccb->ccb_h.ccb_mpt_ptr;
|
||
|
req = ccb->ccb_h.ccb_req_ptr;
|
||
|
|
||
|
mpt->timeouts++;
|
||
|
|
||
|
device_printf(mpt->dev, "time out on request index = 0x%02x sequence = 0x%08x\n",
|
||
|
req->index, req->sequence);
|
||
|
mpt_check_doorbell(mpt);
|
||
|
device_printf(mpt->dev, "Status %08X; Mask %08X; Doorbell %08X\n",
|
||
|
mpt_read(mpt, MPT_OFFSET_INTR_STATUS),
|
||
|
mpt_read(mpt, MPT_OFFSET_INTR_MASK),
|
||
|
mpt_read(mpt, MPT_OFFSET_DOORBELL) );
|
||
|
printf( "request state %s\n", mpt_req_state(req->debug));
|
||
|
if (ccb != req->ccb)
|
||
|
printf("time out: ccb %p != req->ccb %p\n",
|
||
|
ccb,req->ccb);
|
||
|
|
||
|
mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf);
|
||
|
|
||
|
req->debug = REQ_TIMEOUT;
|
||
|
req->ccb = NULL;
|
||
|
ccb->ccb_h.status = CAM_CMD_TIMEOUT;
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
xpt_done(ccb);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Callback routine from "bus_dmamap_load" or in simple case called directly.
|
||
|
*
|
||
|
* Takes a list of physical segments and builds the SGL for SCSI IO command
|
||
|
* and forwards the commard to the IOC after one last check that CAM has not
|
||
|
* aborted the transaction.
|
||
|
*/
|
||
|
static void
|
||
|
mpt_execute_req(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
|
||
|
{
|
||
|
int s;
|
||
|
request_t *req;
|
||
|
union ccb *ccb;
|
||
|
struct mpt_softc *mpt;
|
||
|
MSG_SCSI_IO_REQUEST *mpt_req;
|
||
|
SGE_SIMPLE32 *se;
|
||
|
|
||
|
req = (request_t *)arg;
|
||
|
ccb = req->ccb;
|
||
|
|
||
|
mpt = ccb->ccb_h.ccb_mpt_ptr;
|
||
|
req = ccb->ccb_h.ccb_req_ptr;
|
||
|
mpt_req = req->req_vbuf;
|
||
|
|
||
|
if (error == 0 && nseg > MPT_SGL_MAX) {
|
||
|
error = EFBIG;
|
||
|
}
|
||
|
|
||
|
if (error != 0) {
|
||
|
if (error != EFBIG)
|
||
|
device_printf(mpt->dev, "bus_dmamap_load returned %d\n",
|
||
|
error);
|
||
|
if (ccb->ccb_h.status == CAM_REQ_INPROG) {
|
||
|
xpt_freeze_devq(ccb->ccb_h.path, 1);
|
||
|
ccb->ccb_h.status = CAM_DEV_QFRZN;
|
||
|
if (error == EFBIG)
|
||
|
ccb->ccb_h.status |= CAM_REQ_TOO_BIG;
|
||
|
else
|
||
|
ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
|
||
|
}
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
xpt_done(ccb);
|
||
|
mpt_free_request(mpt, req);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (nseg > MPT_NSGL_FIRST(mpt)) {
|
||
|
int i, nleft = nseg;
|
||
|
u_int32_t flags;
|
||
|
bus_dmasync_op_t op;
|
||
|
SGE_CHAIN32 *ce;
|
||
|
|
||
|
mpt_req->DataLength = ccb->csio.dxfer_len;
|
||
|
flags = MPI_SGE_FLAGS_SIMPLE_ELEMENT;
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
|
||
|
flags |= MPI_SGE_FLAGS_HOST_TO_IOC;
|
||
|
|
||
|
se = (SGE_SIMPLE32 *) &mpt_req->SGL;
|
||
|
for (i = 0; i < MPT_NSGL_FIRST(mpt) - 1; i++, se++, dm_segs++) {
|
||
|
u_int32_t tf;
|
||
|
|
||
|
bzero(se, sizeof (*se));
|
||
|
se->Address = dm_segs->ds_addr;
|
||
|
MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len);
|
||
|
tf = flags;
|
||
|
if (i == MPT_NSGL_FIRST(mpt) - 2) {
|
||
|
tf |= MPI_SGE_FLAGS_LAST_ELEMENT;
|
||
|
}
|
||
|
MPI_pSGE_SET_FLAGS(se, tf);
|
||
|
nleft -= 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Tell the IOC where to find the first chain element
|
||
|
*/
|
||
|
mpt_req->ChainOffset = ((char *)se - (char *)mpt_req) >> 2;
|
||
|
|
||
|
/*
|
||
|
* Until we're finished with all segments...
|
||
|
*/
|
||
|
while (nleft) {
|
||
|
int ntodo;
|
||
|
/*
|
||
|
* Construct the chain element that point to the
|
||
|
* next segment.
|
||
|
*/
|
||
|
ce = (SGE_CHAIN32 *) se++;
|
||
|
if (nleft > MPT_NSGL(mpt)) {
|
||
|
ntodo = MPT_NSGL(mpt) - 1;
|
||
|
ce->NextChainOffset = (MPT_RQSL(mpt) -
|
||
|
sizeof (SGE_SIMPLE32)) >> 2;
|
||
|
} else {
|
||
|
ntodo = nleft;
|
||
|
ce->NextChainOffset = 0;
|
||
|
}
|
||
|
ce->Length = ntodo * sizeof (SGE_SIMPLE32);
|
||
|
ce->Address = req->req_pbuf +
|
||
|
((char *)se - (char *)mpt_req);
|
||
|
ce->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT;
|
||
|
for (i = 0; i < ntodo; i++, se++, dm_segs++) {
|
||
|
u_int32_t tf;
|
||
|
|
||
|
bzero(se, sizeof (*se));
|
||
|
se->Address = dm_segs->ds_addr;
|
||
|
MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len);
|
||
|
tf = flags;
|
||
|
if (i == ntodo - 1) {
|
||
|
tf |= MPI_SGE_FLAGS_LAST_ELEMENT;
|
||
|
if (ce->NextChainOffset == 0) {
|
||
|
tf |=
|
||
|
MPI_SGE_FLAGS_END_OF_LIST |
|
||
|
MPI_SGE_FLAGS_END_OF_BUFFER;
|
||
|
}
|
||
|
}
|
||
|
MPI_pSGE_SET_FLAGS(se, tf);
|
||
|
nleft -= 1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
|
||
|
op = BUS_DMASYNC_PREREAD;
|
||
|
else
|
||
|
op = BUS_DMASYNC_PREWRITE;
|
||
|
if (!(ccb->ccb_h.flags & (CAM_SG_LIST_PHYS|CAM_DATA_PHYS))) {
|
||
|
bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op);
|
||
|
}
|
||
|
} else if (nseg > 0) {
|
||
|
int i;
|
||
|
u_int32_t flags;
|
||
|
bus_dmasync_op_t op;
|
||
|
|
||
|
mpt_req->DataLength = ccb->csio.dxfer_len;
|
||
|
flags = MPI_SGE_FLAGS_SIMPLE_ELEMENT;
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
|
||
|
flags |= MPI_SGE_FLAGS_HOST_TO_IOC;
|
||
|
|
||
|
/* Copy the segments into our SG list */
|
||
|
se = (SGE_SIMPLE32 *) &mpt_req->SGL;
|
||
|
for (i = 0; i < nseg; i++, se++, dm_segs++) {
|
||
|
u_int32_t tf;
|
||
|
|
||
|
bzero(se, sizeof (*se));
|
||
|
se->Address = dm_segs->ds_addr;
|
||
|
MPI_pSGE_SET_LENGTH(se, dm_segs->ds_len);
|
||
|
tf = flags;
|
||
|
if (i == nseg - 1) {
|
||
|
tf |=
|
||
|
MPI_SGE_FLAGS_LAST_ELEMENT |
|
||
|
MPI_SGE_FLAGS_END_OF_BUFFER |
|
||
|
MPI_SGE_FLAGS_END_OF_LIST;
|
||
|
}
|
||
|
MPI_pSGE_SET_FLAGS(se, tf);
|
||
|
}
|
||
|
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
|
||
|
op = BUS_DMASYNC_PREREAD;
|
||
|
else
|
||
|
op = BUS_DMASYNC_PREWRITE;
|
||
|
if (!(ccb->ccb_h.flags & (CAM_SG_LIST_PHYS|CAM_DATA_PHYS))) {
|
||
|
bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op);
|
||
|
}
|
||
|
} else {
|
||
|
se = (SGE_SIMPLE32 *) &mpt_req->SGL;
|
||
|
/*
|
||
|
* No data to transfer so we just make a single simple SGL
|
||
|
* with zero length.
|
||
|
*/
|
||
|
MPI_pSGE_SET_FLAGS(se,
|
||
|
(MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER |
|
||
|
MPI_SGE_FLAGS_SIMPLE_ELEMENT | MPI_SGE_FLAGS_END_OF_LIST));
|
||
|
}
|
||
|
|
||
|
s = splcam();
|
||
|
/*
|
||
|
* Last time we need to check if this CCB needs to be aborted.
|
||
|
*/
|
||
|
if (ccb->ccb_h.status != CAM_REQ_INPROG) {
|
||
|
if (nseg && (ccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0)
|
||
|
bus_dmamap_unload(mpt->buffer_dmat, req->dmap);
|
||
|
mpt_free_request(mpt, req);
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
xpt_done(ccb);
|
||
|
splx(s);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
||
|
if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) {
|
||
|
ccb->ccb_h.timeout_ch =
|
||
|
timeout(mpttimeout, (caddr_t)ccb,
|
||
|
(ccb->ccb_h.timeout * hz) / 1000);
|
||
|
} else {
|
||
|
callout_handle_init(&ccb->ccb_h.timeout_ch);
|
||
|
}
|
||
|
|
||
|
if (mpt->verbose > 1)
|
||
|
mpt_print_scsi_io_request(mpt_req);
|
||
|
|
||
|
mpt_send_cmd(mpt, req);
|
||
|
|
||
|
splx(s);
|
||
|
}
|
||
|
|
||
|
/* Convert a CAM SCSI I/O ccb into a MPT request to pass the FC Chip */
|
||
|
/* Including building a Scatter gather list of physical page to transfer */
|
||
|
static int
|
||
|
mpt_start(union ccb *ccb)
|
||
|
{
|
||
|
request_t *req;
|
||
|
struct mpt_softc *mpt;
|
||
|
MSG_SCSI_IO_REQUEST *mpt_req;
|
||
|
struct ccb_scsiio *csio = &ccb->csio;
|
||
|
struct ccb_hdr *ccbh = &ccb->ccb_h;
|
||
|
|
||
|
/* Get the pointer for the physical addapter */
|
||
|
mpt = ccb->ccb_h.ccb_mpt_ptr;
|
||
|
|
||
|
/* Get a request structure off the free list */
|
||
|
if ((req = mpt_get_request(mpt)) == NULL) {
|
||
|
return (CAM_REQUEUE_REQ);
|
||
|
}
|
||
|
|
||
|
/* Link the ccb and the request structure so we can find */
|
||
|
/* the other knowing either the request or the ccb */
|
||
|
req->ccb = ccb;
|
||
|
ccb->ccb_h.ccb_req_ptr = req;
|
||
|
|
||
|
/* Now we build the command for the IOC */
|
||
|
mpt_req = req->req_vbuf;
|
||
|
bzero(mpt_req, sizeof *mpt_req);
|
||
|
|
||
|
mpt_req->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
|
||
|
mpt_req->Bus = mpt->bus;
|
||
|
|
||
|
mpt_req->SenseBufferLength =
|
||
|
(csio->sense_len < MPT_SENSE_SIZE) ?
|
||
|
csio->sense_len : MPT_SENSE_SIZE;
|
||
|
|
||
|
/* We use the message context to find the request structure when we */
|
||
|
/* Get the command competion interrupt from the FC IOC. */
|
||
|
mpt_req->MsgContext = req->index;
|
||
|
|
||
|
/* Which physical device to do the I/O on */
|
||
|
mpt_req->TargetID = ccb->ccb_h.target_id;
|
||
|
mpt_req->LUN[1] = ccb->ccb_h.target_lun;
|
||
|
|
||
|
/* Set the direction of the transfer */
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
|
||
|
mpt_req->Control = MPI_SCSIIO_CONTROL_READ;
|
||
|
else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
|
||
|
mpt_req->Control = MPI_SCSIIO_CONTROL_WRITE;
|
||
|
else
|
||
|
mpt_req->Control = MPI_SCSIIO_CONTROL_NODATATRANSFER;
|
||
|
|
||
|
if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) {
|
||
|
switch(ccb->csio.tag_action) {
|
||
|
case MSG_HEAD_OF_Q_TAG:
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_HEADOFQ;
|
||
|
break;
|
||
|
case MSG_ACA_TASK:
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_ACAQ;
|
||
|
break;
|
||
|
case MSG_ORDERED_Q_TAG:
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_ORDEREDQ;
|
||
|
break;
|
||
|
case MSG_SIMPLE_Q_TAG:
|
||
|
default:
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_SIMPLEQ;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
if (mpt->is_fc)
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_SIMPLEQ;
|
||
|
else
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_UNTAGGED;
|
||
|
}
|
||
|
|
||
|
if (mpt->is_fc == 0 && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) != 0) {
|
||
|
mpt_req->Control |= MPI_SCSIIO_CONTROL_NO_DISCONNECT;
|
||
|
}
|
||
|
|
||
|
/* Copy the scsi command block into place */
|
||
|
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0)
|
||
|
bcopy(csio->cdb_io.cdb_ptr, mpt_req->CDB, csio->cdb_len);
|
||
|
else
|
||
|
bcopy(csio->cdb_io.cdb_bytes, mpt_req->CDB, csio->cdb_len);
|
||
|
|
||
|
mpt_req->CDBLength = csio->cdb_len;
|
||
|
mpt_req->DataLength = csio->dxfer_len;
|
||
|
mpt_req->SenseBufferLowAddr = req->sense_pbuf;
|
||
|
|
||
|
/*
|
||
|
* If we have any data to send with this command,
|
||
|
* map it into bus space.
|
||
|
*/
|
||
|
|
||
|
if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
|
||
|
if ((ccbh->flags & CAM_SCATTER_VALID) == 0) {
|
||
|
/*
|
||
|
* We've been given a pointer to a single buffer.
|
||
|
*/
|
||
|
if ((ccbh->flags & CAM_DATA_PHYS) == 0) {
|
||
|
/*
|
||
|
* Virtual address that needs to translated into
|
||
|
* one or more physical pages.
|
||
|
*/
|
||
|
int s;
|
||
|
int error;
|
||
|
|
||
|
error = bus_dmamap_load(mpt->buffer_dmat,
|
||
|
req->dmap, csio->data_ptr, csio->dxfer_len,
|
||
|
mpt_execute_req, req, 0);
|
||
|
if (error == EINPROGRESS) {
|
||
|
/*
|
||
|
* So as to maintain ordering,
|
||
|
* freeze the controller queue
|
||
|
* until our mapping is
|
||
|
* returned.
|
||
|
*/
|
||
|
xpt_freeze_simq(mpt->sim, 1);
|
||
|
ccbh->status |= CAM_RELEASE_SIMQ;
|
||
|
}
|
||
|
splx(s);
|
||
|
} else {
|
||
|
/*
|
||
|
* We have been given a pointer to single
|
||
|
* physical buffer.
|
||
|
*/
|
||
|
struct bus_dma_segment seg;
|
||
|
seg.ds_addr = (bus_addr_t)csio->data_ptr;
|
||
|
seg.ds_len = csio->dxfer_len;
|
||
|
mpt_execute_req(req, &seg, 1, 0);
|
||
|
}
|
||
|
} else {
|
||
|
/*
|
||
|
* We have been given a list of addresses.
|
||
|
* These case could be easily done but they are not
|
||
|
* currently generated by the CAM subsystem so there
|
||
|
* is no point in wasting the time right now.
|
||
|
*/
|
||
|
struct bus_dma_segment *segs;
|
||
|
if ((ccbh->flags & CAM_SG_LIST_PHYS) == 0) {
|
||
|
mpt_execute_req(req, NULL, 0, EFAULT);
|
||
|
} else {
|
||
|
/* Just use the segments provided */
|
||
|
segs = (struct bus_dma_segment *)csio->data_ptr;
|
||
|
mpt_execute_req(req, segs, csio->sglist_cnt,
|
||
|
(csio->sglist_cnt < MPT_SGL_MAX)?
|
||
|
0 : EFBIG);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
mpt_execute_req(req, NULL, 0, 0);
|
||
|
}
|
||
|
return (CAM_REQ_INPROG);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
mpt_bus_reset(union ccb *ccb)
|
||
|
{
|
||
|
int error;
|
||
|
request_t *req;
|
||
|
struct mpt_softc *mpt;
|
||
|
MSG_SCSI_TASK_MGMT *reset_req;
|
||
|
|
||
|
/* Get the pointer for the physical adapter */
|
||
|
mpt = ccb->ccb_h.ccb_mpt_ptr;
|
||
|
|
||
|
/* Get a request structure off the free list */
|
||
|
if ((req = mpt_get_request(mpt)) == NULL) {
|
||
|
return (CAM_REQUEUE_REQ);
|
||
|
}
|
||
|
|
||
|
/* Link the ccb and the request structure so we can find */
|
||
|
/* the other knowing either the request or the ccb */
|
||
|
req->ccb = ccb;
|
||
|
ccb->ccb_h.ccb_req_ptr = req;
|
||
|
|
||
|
reset_req = req->req_vbuf;
|
||
|
bzero(reset_req, sizeof *reset_req);
|
||
|
|
||
|
reset_req->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
|
||
|
reset_req->MsgContext = req->index;
|
||
|
reset_req->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
|
||
|
if (mpt->is_fc) {
|
||
|
/*
|
||
|
* Should really be TARGET_RESET_OPTION
|
||
|
*/
|
||
|
reset_req->MsgFlags =
|
||
|
MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION;
|
||
|
}
|
||
|
/* Which physical device Reset */
|
||
|
reset_req->TargetID = ccb->ccb_h.target_id;
|
||
|
reset_req->LUN[1] = ccb->ccb_h.target_lun;
|
||
|
|
||
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
||
|
|
||
|
error = mpt_send_handshake_cmd(mpt,
|
||
|
sizeof (MSG_SCSI_TASK_MGMT), reset_req);
|
||
|
if (error) {
|
||
|
device_printf(mpt->dev,
|
||
|
"mpt_bus_reset: mpt_send_handshake return %d\n", error);
|
||
|
return (CAM_REQ_CMP_ERR);
|
||
|
} else {
|
||
|
return (CAM_REQ_CMP);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Process an asynchronous event from the IOC.
|
||
|
*/
|
||
|
void
|
||
|
mpt_notify(struct mpt_softc *mpt, void *vmsg)
|
||
|
{
|
||
|
MSG_DEFAULT_REPLY *dmsg = vmsg;
|
||
|
|
||
|
if (dmsg->Function == MPI_FUNCTION_EVENT_NOTIFICATION) {
|
||
|
MSG_EVENT_NOTIFY_REPLY *msg = vmsg;
|
||
|
switch(msg->Event) {
|
||
|
case MPI_EVENT_LOG_DATA:
|
||
|
/* Some error occured that LSI wants logged */
|
||
|
device_printf(mpt->dev,
|
||
|
"\tEvtLogData: IOCLogInfo: 0x%08x\n",
|
||
|
msg->IOCLogInfo);
|
||
|
device_printf(mpt->dev, "\tEvtLogData: Event Data:");
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < msg->EventDataLength; i++) {
|
||
|
device_printf(mpt->dev,
|
||
|
" %08X", msg->Data[i]);
|
||
|
}
|
||
|
}
|
||
|
device_printf(mpt->dev, "\n");
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_UNIT_ATTENTION:
|
||
|
device_printf(mpt->dev,
|
||
|
"Bus: 0x%02x TargetID: 0x%02x\n",
|
||
|
(msg->Data[0] >> 8) & 0xff, msg->Data[0] & 0xff);
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_IOC_BUS_RESET:
|
||
|
/* We generated a bus reset */
|
||
|
device_printf(mpt->dev, "IOC Bus Reset Port: %d\n",
|
||
|
(msg->Data[0] >> 8) & 0xff);
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_EXT_BUS_RESET:
|
||
|
/* Someone else generated a bus reset */
|
||
|
device_printf(mpt->dev, "Ext Bus Reset\n");
|
||
|
/*
|
||
|
* These replies don't return EventData like the MPI
|
||
|
* spec says they do
|
||
|
*/
|
||
|
/* xpt_async(AC_BUS_RESET, path, NULL); */
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_RESCAN:
|
||
|
/*
|
||
|
* In general this means a device has been added
|
||
|
* to the loop.
|
||
|
*/
|
||
|
device_printf(mpt->dev,
|
||
|
"Rescan Port: %d\n", (msg->Data[0] >> 8) & 0xff);
|
||
|
/* xpt_async(AC_FOUND_DEVICE, path, NULL); */
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_LINK_STATUS_CHANGE:
|
||
|
device_printf(mpt->dev, "Port %d: LinkState: %s\n",
|
||
|
(msg->Data[1] >> 8) & 0xff,
|
||
|
((msg->Data[0] & 0xff) == 0)? "Failed" : "Active");
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_LOOP_STATE_CHANGE:
|
||
|
switch ((msg->Data[0] >> 16) & 0xff) {
|
||
|
case 0x01:
|
||
|
device_printf(mpt->dev,
|
||
|
"Port 0x%x: FC LinkEvent: LIP(%02X,%02X) (Loop Initialization)\n",
|
||
|
(msg->Data[1] >> 8) & 0xff,
|
||
|
(msg->Data[0] >> 8) & 0xff,
|
||
|
(msg->Data[0] ) & 0xff);
|
||
|
switch ((msg->Data[0] >> 8) & 0xff) {
|
||
|
case 0xF7:
|
||
|
if ((msg->Data[0] & 0xff) == 0xF7) {
|
||
|
printf("Device needs AL_PA\n");
|
||
|
} else {
|
||
|
printf("Device %02X doesn't like FC performance\n",
|
||
|
msg->Data[0] & 0xFF);
|
||
|
}
|
||
|
break;
|
||
|
case 0xF8:
|
||
|
if ((msg->Data[0] & 0xff) == 0xF7) {
|
||
|
printf("Device had loop failure at its receiver prior to acquiring AL_PA\n");
|
||
|
} else {
|
||
|
printf("Device %02X detected loop failure at its receiver\n",
|
||
|
msg->Data[0] & 0xFF);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
printf("Device %02X requests that device %02X reset itself\n",
|
||
|
msg->Data[0] & 0xFF,
|
||
|
(msg->Data[0] >> 8) & 0xFF);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 0x02:
|
||
|
device_printf(mpt->dev, "Port 0x%x: FC LinkEvent: LPE(%02X,%02X) (Loop Port Enable)\n",
|
||
|
(msg->Data[1] >> 8) & 0xff, /* Port */
|
||
|
(msg->Data[0] >> 8) & 0xff, /* Character 3 */
|
||
|
(msg->Data[0] ) & 0xff /* Character 4 */
|
||
|
);
|
||
|
break;
|
||
|
case 0x03:
|
||
|
device_printf(mpt->dev, "Port 0x%x: FC LinkEvent: LPB(%02X,%02X) (Loop Port Bypass)\n",
|
||
|
(msg->Data[1] >> 8) & 0xff, /* Port */
|
||
|
(msg->Data[0] >> 8) & 0xff, /* Character 3 */
|
||
|
(msg->Data[0] ) & 0xff /* Character 4 */
|
||
|
);
|
||
|
break;
|
||
|
default:
|
||
|
device_printf(mpt->dev, "Port 0x%x: FC LinkEvent: Unknown FC event (%02X %02X %02X)\n",
|
||
|
(msg->Data[1] >> 8) & 0xff, /* Port */
|
||
|
(msg->Data[0] >> 16) & 0xff, /* Event */
|
||
|
(msg->Data[0] >> 8) & 0xff, /* Character 3 */
|
||
|
(msg->Data[0] ) & 0xff /* Character 4 */
|
||
|
);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MPI_EVENT_LOGOUT:
|
||
|
device_printf(mpt->dev, "FC Logout Port: %d N_PortID: %02X\n",
|
||
|
(msg->Data[1] >> 8) & 0xff,
|
||
|
msg->Data[0]);
|
||
|
break;
|
||
|
case MPI_EVENT_EVENT_CHANGE:
|
||
|
/* This is just an acknowledgement of our
|
||
|
mpt_send_event_request */
|
||
|
break;
|
||
|
default:
|
||
|
device_printf(mpt->dev, "Unknown event %X\n", msg->Event);
|
||
|
}
|
||
|
} else if (dmsg->Function == MPI_FUNCTION_PORT_ENABLE) {
|
||
|
MSG_PORT_ENABLE_REPLY *msg = vmsg;
|
||
|
int index = msg->MsgContext & ~0x80000000;
|
||
|
if (mpt->verbose > 1) {
|
||
|
device_printf(mpt->dev, "enable port reply idx %d\n",
|
||
|
index);
|
||
|
}
|
||
|
if (index >= 0 && index < MPT_MAX_REQUESTS) {
|
||
|
request_t *req = &mpt->requests[index];
|
||
|
req->debug = REQ_DONE;
|
||
|
}
|
||
|
} else {
|
||
|
device_printf(mpt->dev, "unknown mpt_notify: %x\n",
|
||
|
dmsg->Function);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
mpt_done(struct mpt_softc *mpt, u_int32_t reply)
|
||
|
{
|
||
|
int index;
|
||
|
request_t *req;
|
||
|
union ccb *ccb;
|
||
|
MSG_REQUEST_HEADER *mpt_req;
|
||
|
MSG_SCSI_IO_REPLY *mpt_reply;
|
||
|
|
||
|
index = -1; /* Shutup the complier */
|
||
|
|
||
|
if ((reply & MPT_CONTEXT_REPLY) == 0) {
|
||
|
/* context reply */
|
||
|
mpt_reply = NULL;
|
||
|
index = reply & MPT_CONTEXT_MASK;
|
||
|
} else {
|
||
|
unsigned *pReply;
|
||
|
|
||
|
bus_dmamap_sync(mpt->reply_dmat, mpt->reply_dmap,
|
||
|
BUS_DMASYNC_POSTREAD);
|
||
|
/* address reply (Error) */
|
||
|
mpt_reply = MPT_REPLY_PTOV(mpt, reply);
|
||
|
if (mpt->verbose > 1) {
|
||
|
pReply = (unsigned *) mpt_reply;
|
||
|
device_printf(mpt->dev, "Address Reply (index %u)\n",
|
||
|
mpt_reply->MsgContext & 0xffff);
|
||
|
device_printf(mpt->dev, "%08X %08X %08X %08X\n",
|
||
|
pReply[0], pReply[1], pReply[2], pReply[3]);
|
||
|
device_printf(mpt->dev, "%08X %08X %08X %08X\n",
|
||
|
pReply[4], pReply[5], pReply[6], pReply[7]);
|
||
|
device_printf(mpt->dev, "%08X %08X %08X %08X\n\n",
|
||
|
pReply[8], pReply[9], pReply[10], pReply[11]);
|
||
|
}
|
||
|
index = mpt_reply->MsgContext;
|
||
|
}
|
||
|
|
||
|
/* Address reply with MessageContext high bit set */
|
||
|
/* This is most likely a notify message so we try */
|
||
|
/* to process it then free it */
|
||
|
if ((index & 0x80000000) != 0) {
|
||
|
if (mpt_reply != NULL) {
|
||
|
mpt_notify(mpt, mpt_reply);
|
||
|
mpt_free_reply(mpt, (reply << 1));
|
||
|
} else {
|
||
|
device_printf(mpt->dev,
|
||
|
"mpt_done: index 0x%x, NULL reply\n", index);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Did we end up with a valid index into the table? */
|
||
|
if (index < 0 || index >= MPT_MAX_REQUESTS) {
|
||
|
printf("mpt_done: invalid index (%x) in reply\n", index);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
req = &mpt->requests[index];
|
||
|
|
||
|
/* Make sure memory hasn't been trashed */
|
||
|
if (req->index != index) {
|
||
|
printf("mpt_done: corrupted request struct");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Short cut for task management replys; nothing more for us to do */
|
||
|
mpt_req = req->req_vbuf;
|
||
|
if (mpt_req->Function == MPI_FUNCTION_SCSI_TASK_MGMT) {
|
||
|
if (mpt->verbose > 1) {
|
||
|
device_printf(mpt->dev, "mpt_done: TASK MGMT\n");
|
||
|
}
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if (mpt_req->Function == MPI_FUNCTION_PORT_ENABLE) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* At this point it better be a SCSI IO command, but don't
|
||
|
* crash if it isn't
|
||
|
*/
|
||
|
if (mpt_req->Function != MPI_FUNCTION_SCSI_IO_REQUEST) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* Recover the CAM control block from the request structure */
|
||
|
ccb = req->ccb;
|
||
|
|
||
|
/* Can't have had a SCSI command with out a CAM control block */
|
||
|
if (ccb == NULL || (ccb->ccb_h.status & CAM_SIM_QUEUED) == 0) {
|
||
|
device_printf(mpt->dev,
|
||
|
"mpt_done: corrupted ccb, index = 0x%02x seq = 0x%08x",
|
||
|
req->index, req->sequence);
|
||
|
printf(" request state %s\nmpt_request:\n",
|
||
|
mpt_req_state(req->debug));
|
||
|
mpt_print_scsi_io_request((MSG_SCSI_IO_REQUEST *)req->req_vbuf);
|
||
|
|
||
|
if (mpt_reply != NULL) {
|
||
|
printf("\nmpt_done: reply:\n");
|
||
|
mpt_print_reply(MPT_REPLY_PTOV(mpt, reply));
|
||
|
} else {
|
||
|
printf("\nmpt_done: context reply: 0x%08x\n", reply);
|
||
|
}
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
untimeout(mpttimeout, ccb, ccb->ccb_h.timeout_ch);
|
||
|
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
|
||
|
bus_dmasync_op_t op;
|
||
|
|
||
|
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
|
||
|
op = BUS_DMASYNC_POSTREAD;
|
||
|
} else {
|
||
|
op = BUS_DMASYNC_POSTWRITE;
|
||
|
}
|
||
|
bus_dmamap_sync(mpt->buffer_dmat, req->dmap, op);
|
||
|
bus_dmamap_unload(mpt->buffer_dmat, req->dmap);
|
||
|
}
|
||
|
ccb->csio.resid = 0;
|
||
|
|
||
|
if (mpt_reply == NULL) {
|
||
|
/* Context reply; report that the command was successfull */
|
||
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
||
|
ccb->csio.scsi_status = SCSI_STATUS_OK;
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
xpt_done(ccb);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
ccb->csio.scsi_status = mpt_reply->SCSIStatus;
|
||
|
switch(mpt_reply->IOCStatus) {
|
||
|
case MPI_IOCSTATUS_SCSI_DATA_OVERRUN:
|
||
|
ccb->ccb_h.status = CAM_DATA_RUN_ERR | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:
|
||
|
/*
|
||
|
* Yikes, Tagged queue full comes through this path!
|
||
|
*
|
||
|
* So we'll change it to a status error and anything
|
||
|
* that returns status should probably be a status
|
||
|
* error as well.
|
||
|
*/
|
||
|
if (mpt_reply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS) {
|
||
|
ccb->ccb_h.status = CAM_DATA_RUN_ERR | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
}
|
||
|
ccb->csio.resid =
|
||
|
ccb->csio.dxfer_len - mpt_reply->TransferCount;
|
||
|
/* Fall through */
|
||
|
case MPI_IOCSTATUS_SUCCESS:
|
||
|
case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:
|
||
|
switch (ccb->csio.scsi_status) {
|
||
|
case SCSI_STATUS_OK:
|
||
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
||
|
break;
|
||
|
case SCSI_STATUS_QUEUE_FULL:
|
||
|
ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
|
||
|
#if 0
|
||
|
/* XXX This seams to freaze up the device */
|
||
|
/* But without it the tag queue total does */
|
||
|
/* Not get reduced properly! */
|
||
|
xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
|
||
|
ccb->ccb_h.status |= CAM_DEV_QFRZN;
|
||
|
#endif
|
||
|
break;
|
||
|
default:
|
||
|
ccb->ccb_h.status =
|
||
|
CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN;
|
||
|
xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case MPI_IOCSTATUS_BUSY:
|
||
|
case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES:
|
||
|
ccb->ccb_h.status = CAM_BUSY;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_INVALID_BUS:
|
||
|
case MPI_IOCSTATUS_SCSI_INVALID_TARGETID:
|
||
|
case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
|
||
|
ccb->ccb_h.status = CAM_DEV_NOT_THERE | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
|
||
|
ccb->ccb_h.status = CAM_DATA_RUN_ERR | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:
|
||
|
case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:
|
||
|
ccb->ccb_h.status = CAM_UNCOR_PARITY | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:
|
||
|
/* Terminated due to task managment request */
|
||
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
||
|
break;
|
||
|
|
||
|
case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
|
||
|
ccb->ccb_h.status = CAM_UA_TERMIO | CAM_DEV_QFRZN;
|
||
|
break;
|
||
|
/*
|
||
|
case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:
|
||
|
ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
|
||
|
break;
|
||
|
*/
|
||
|
/*
|
||
|
* We always get this when a drive is pulled.
|
||
|
* Just kill the device but not the bus.
|
||
|
*/
|
||
|
case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:
|
||
|
case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:
|
||
|
ccb->ccb_h.status = CAM_SCSI_BUS_RESET | CAM_DEV_QFRZN;
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) != 0) {
|
||
|
if (ccb->ccb_h.flags & (CAM_SENSE_PHYS | CAM_SENSE_PTR)) {
|
||
|
ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
|
||
|
} else {
|
||
|
ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
|
||
|
ccb->csio.sense_resid = mpt_reply->SenseCount;
|
||
|
bcopy(req->sense_vbuf, &ccb->csio.sense_data,
|
||
|
ccb->csio.sense_len);
|
||
|
}
|
||
|
} else if (mpt_reply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
|
||
|
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
|
||
|
ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
|
||
|
}
|
||
|
|
||
|
if ((mpt_reply->MsgFlags & 0x80) == 0)
|
||
|
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
xpt_done(ccb);
|
||
|
|
||
|
done:
|
||
|
/* If IOC done with this request free it up */
|
||
|
if (mpt_reply == NULL || (mpt_reply->MsgFlags & 0x80) == 0)
|
||
|
mpt_free_request(mpt, req);
|
||
|
|
||
|
/* If address reply; give the buffer back to the IOC */
|
||
|
if (mpt_reply != NULL)
|
||
|
mpt_free_reply(mpt, (reply << 1));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
mpt_action(struct cam_sim *sim, union ccb *ccb)
|
||
|
{
|
||
|
int tgt, error;
|
||
|
struct mpt_softc *mpt;
|
||
|
struct ccb_trans_settings *cts;
|
||
|
|
||
|
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("mpt_action\n"));
|
||
|
|
||
|
mpt = (struct mpt_softc *)cam_sim_softc(sim);
|
||
|
|
||
|
ccb->ccb_h.ccb_mpt_ptr = mpt;
|
||
|
|
||
|
switch (ccb->ccb_h.func_code) {
|
||
|
case XPT_RESET_BUS:
|
||
|
if (mpt->verbose > 1)
|
||
|
device_printf(mpt->dev, "XPT_RESET_BUS\n");
|
||
|
error = mpt_bus_reset(ccb);
|
||
|
switch (error) {
|
||
|
case CAM_REQ_INPROG:
|
||
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
||
|
break;
|
||
|
case CAM_REQUEUE_REQ:
|
||
|
/* if (mpt->mpt_osinfo.simqfrozen == 0) */
|
||
|
{
|
||
|
xpt_freeze_simq(sim, 1);
|
||
|
}
|
||
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
||
|
xpt_done(ccb);
|
||
|
break;
|
||
|
|
||
|
case CAM_REQ_CMP:
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
ccb->ccb_h.status |= CAM_REQ_CMP;
|
||
|
xpt_done(ccb);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
||
|
xpt_done(ccb);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
/* Max supported CDB length is 16 bytes */
|
||
|
if (ccb->csio.cdb_len > 16) {
|
||
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
||
|
xpt_done(ccb);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ccb->csio.scsi_status = SCSI_STATUS_OK;
|
||
|
/* XXX GJA MPT_LOCK(mpt); */
|
||
|
error = mpt_start(ccb);
|
||
|
/* XXX GJA MPT_UNLOCK(mpt); */
|
||
|
switch (error) {
|
||
|
case CAM_REQ_INPROG:
|
||
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
||
|
break;
|
||
|
|
||
|
case CAM_REQUEUE_REQ:
|
||
|
/* if (mpt->mpt_osinfo.simqfrozen == 0) */
|
||
|
{
|
||
|
xpt_freeze_simq(sim, 1);
|
||
|
}
|
||
|
/* mpt->mpt_osinfo.simqfrozen |= SIMQFRZ_RESOURCE; */
|
||
|
ccb->ccb_h.status = CAM_REQUEUE_REQ;
|
||
|
xpt_done(ccb);
|
||
|
break;
|
||
|
|
||
|
case CAM_REQ_CMP:
|
||
|
ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
|
||
|
xpt_done(ccb);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
device_printf(mpt->dev, "What's this? 0x%x at %d in file %s\n",
|
||
|
error, __LINE__, __FILE__);
|
||
|
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
|
||
|
xpt_done(ccb);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case XPT_ABORT:
|
||
|
/* Probably ought to impliment this but no one actualy */
|
||
|
/* uses it for anything worthwile. XXX GJA */
|
||
|
ccb->ccb_h.status = CAM_UA_ABORT;
|
||
|
xpt_done(ccb);
|
||
|
break;
|
||
|
|
||
|
case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */
|
||
|
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;
|
||
|
/*
|
||
|
* 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;
|
||
|
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) {
|
||
|
device_printf(mpt->dev, "%d.%d XPT_CALC_GEOMETRY block size 0?\n",
|
||
|
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_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_lun = 7;
|
||
|
cpi->bus_id = cam_sim_bus(sim);
|
||
|
if (mpt->is_fc) {
|
||
|
cpi->max_target = 255;
|
||
|
cpi->hba_misc = PIM_NOBUSRESET;
|
||
|
cpi->initiator_id = cpi->max_target + 1;
|
||
|
cpi->base_transfer_speed = 100000;
|
||
|
cpi->hba_inquiry = PI_TAG_ABLE;
|
||
|
} else {
|
||
|
cpi->initiator_id = 7; /* XXX */
|
||
|
cpi->base_transfer_speed = 3300;
|
||
|
cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
|
||
|
cpi->hba_misc = 0;
|
||
|
cpi->max_target = 15;
|
||
|
}
|
||
|
|
||
|
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
|
||
|
strncpy(cpi->hba_vid, "LSI", 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;
|
||
|
}
|
||
|
}
|