diff options
Diffstat (limited to 'sys/dev/mpt/mpt_freebsd.c')
-rw-r--r-- | sys/dev/mpt/mpt_freebsd.c | 1223 |
1 files changed, 1223 insertions, 0 deletions
diff --git a/sys/dev/mpt/mpt_freebsd.c b/sys/dev/mpt/mpt_freebsd.c new file mode 100644 index 0000000..5fb6115 --- /dev/null +++ b/sys/dev/mpt/mpt_freebsd.c @@ -0,0 +1,1223 @@ +/* $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; + } +} |