/******************************************************************************** * Handle completion of a command submitted via CAM. * Completion for extended cdb */ static void amr_cam_complete_extcdb(struct amr_command *ac) { struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data; struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private; struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr; /* XXX note that we're ignoring ac->ac_status - good idea? */ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, aep->ap_scsi_status); /* * Hide disks from CAM so that they're not picked up and treated as 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not managed", we * could add handling for that to allow disks to be selectively visible. */ if ((aep->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) { bzero(csio->data_ptr, csio->dxfer_len); if (aep->ap_scsi_status == 0xf0) { csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; } else { csio->ccb_h.status = CAM_DEV_NOT_THERE; } } else { /* handle passthrough SCSI status */ switch(aep->ap_scsi_status) { case 0: /* completed OK */ csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: csio->ccb_h.status = CAM_REQ_CMP_ERR; break; } } free(aep, M_DEVBUF); if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); xpt_done((union ccb *)csio); amr_releasecmd(ac); }
/********************************************************************** * Handle completion of a command submitted via CAM. */ static void amr_cam_complete(struct amr_command *ac) { struct amr_passthrough *ap; struct amr_ext_passthrough *aep; struct ccb_scsiio *csio; struct scsi_inquiry_data *inq; int scsi_status, cdb0; ap = &ac->ac_ccb->ccb_pthru; aep = &ac->ac_ccb->ccb_epthru; csio = (struct ccb_scsiio *)ac->ac_private; inq = (struct scsi_inquiry_data *)csio->data_ptr; if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) scsi_status = aep->ap_scsi_status; else scsi_status = ap->ap_scsi_status; debug(1, "status 0x%x AP scsi_status 0x%x", ac->ac_status, scsi_status); /* Make sure the status is sane */ if ((ac->ac_status != AMR_STATUS_SUCCESS) && (scsi_status == 0)) { csio->ccb_h.status = CAM_REQ_CMP_ERR; goto out; } /* * Hide disks from CAM so that they're not picked up and treated as * 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not * managed", we could add handling for that to allow disks to be * selectively visible. */ /* handle passthrough SCSI status */ switch(scsi_status) { case 0: /* completed OK */ if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) cdb0 = aep->ap_cdb[0]; else cdb0 = ap->ap_cdb[0]; if ((cdb0 == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) inq->device = (inq->device & 0xe0) | T_NODEVICE; csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); else bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: /* * Non-zero LUNs are already filtered, so there's no need * to return CAM_DEV_NOT_THERE. */ csio->ccb_h.status = CAM_SEL_TIMEOUT; break; } out: if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); mtx_lock(&ac->ac_sc->amr_list_lock); xpt_done((union ccb *)csio); amr_releasecmd(ac); mtx_unlock(&ac->ac_sc->amr_list_lock); }
/*********************************************************************** * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI * command. */ static int amr_cam_command(struct amr_softc *sc, struct amr_command **acp) { struct amr_command *ac; struct amr_passthrough *ap; struct amr_ext_passthrough *aep; struct ccb_scsiio *csio; int bus, target, error; error = 0; ac = NULL; ap = NULL; aep = NULL; /* check to see if there is a ccb for us to work with */ if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL) goto out; /* get bus/target, XXX validate against protected devices? */ bus = csio->ccb_h.sim_priv.entries[0].field; target = csio->ccb_h.target_id; /* * Build a passthrough command. */ /* construct command */ if ((ac = amr_alloccmd(sc)) == NULL) { error = ENOMEM; goto out; } /* construct passthrough */ if (sc->support_ext_cdb ) { aep = &ac->ac_ccb->ccb_epthru; aep->ap_timeout = 2; aep->ap_ars = 1; aep->ap_request_sense_length = 14; aep->ap_islogical = 0; aep->ap_channel = bus; aep->ap_scsi_id = target; aep->ap_logical_drive_no = csio->ccb_h.target_lun; aep->ap_cdb_length = csio->cdb_len; aep->ap_data_transfer_length = csio->dxfer_len; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, aep->ap_cdb, csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, aep->ap_cdb, csio->cdb_len); } /* * we leave the data s/g list and s/g count to the map routine * later */ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep->ap_cdb[0], aep->ap_cdb_length, csio->dxfer_len, aep->ap_channel, aep->ap_scsi_id, aep->ap_logical_drive_no); } else { ap = &ac->ac_ccb->ccb_pthru; ap->ap_timeout = 0; ap->ap_ars = 1; ap->ap_request_sense_length = 14; ap->ap_islogical = 0; ap->ap_channel = bus; ap->ap_scsi_id = target; ap->ap_logical_drive_no = csio->ccb_h.target_lun; ap->ap_cdb_length = csio->cdb_len; ap->ap_data_transfer_length = csio->dxfer_len; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb, csio->cdb_len); } /* * we leave the data s/g list and s/g count to the map routine * later */ debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0], ap->ap_cdb_length, csio->dxfer_len, ap->ap_channel, ap->ap_scsi_id, ap->ap_logical_drive_no); } ac->ac_flags |= AMR_CMD_CCB; ac->ac_data = csio->data_ptr; ac->ac_length = csio->dxfer_len; if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) ac->ac_flags |= AMR_CMD_DATAIN; if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) ac->ac_flags |= AMR_CMD_DATAOUT; ac->ac_private = csio; ac->ac_complete = amr_cam_complete; if ( sc->support_ext_cdb ) { ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS; } else { ac->ac_mailbox.mb_command = AMR_CMD_PASS; } out: if (error != 0) { if (ac != NULL) amr_releasecmd(ac); if (csio != NULL) /* put it back and try again later */ amr_requeue_ccb(sc, (union ccb *)csio); } *acp = ac; return(error); }