/* * Function name: tw_osli_fw_passthru * Description: Builds a fw passthru cmd pkt, and submits it to CL. * * Input: sc -- ptr to OSL internal ctlr context * buf -- ptr to ioctl pkt understood by CL * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_osli_fw_passthru(struct twa_softc *sc, TW_INT8 *buf) { struct tw_osli_req_context *req; struct tw_osli_ioctl_no_data_buf *user_buf = (struct tw_osli_ioctl_no_data_buf *)buf; TW_TIME end_time; TW_UINT32 timeout = 60; TW_UINT32 data_buf_size_adjusted; struct tw_cl_req_packet *req_pkt; struct tw_cl_passthru_req_packet *pt_req; TW_INT32 error; tw_osli_dbg_dprintf(5, sc, "ioctl: passthru"); if ((req = tw_osli_get_request(sc)) == NULL) return(EBUSY); req->req_handle.osl_req_ctxt = req; req->orig_req = buf; req->flags |= TW_OSLI_REQ_FLAGS_PASSTHRU; req_pkt = &(req->req_pkt); req_pkt->status = 0; req_pkt->tw_osl_callback = tw_osl_complete_passthru; /* Let the Common Layer retry the request on cmd queue full. */ req_pkt->flags |= TW_CL_REQ_RETRY_ON_BUSY; pt_req = &(req_pkt->gen_req_pkt.pt_req); /* * Make sure that the data buffer sent to firmware is a * 512 byte multiple in size. */ data_buf_size_adjusted = (user_buf->driver_pkt.buffer_length + (sc->sg_size_factor - 1)) & ~(sc->sg_size_factor - 1); if ((req->length = data_buf_size_adjusted)) { if ((req->data = malloc(data_buf_size_adjusted, TW_OSLI_MALLOC_CLASS, M_WAITOK)) == NULL) { error = ENOMEM; tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2016, "Could not alloc mem for " "fw_passthru data_buf", error); goto fw_passthru_err; } /* Copy the payload. */ if ((error = copyin((TW_VOID *)(user_buf->pdata), req->data, user_buf->driver_pkt.buffer_length)) != 0) { tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2017, "Could not copyin fw_passthru data_buf", error); goto fw_passthru_err; } pt_req->sgl_entries = 1; /* will be updated during mapping */ req->flags |= (TW_OSLI_REQ_FLAGS_DATA_IN | TW_OSLI_REQ_FLAGS_DATA_OUT); } else pt_req->sgl_entries = 0; /* no payload */ pt_req->cmd_pkt = (TW_VOID *)(&(user_buf->cmd_pkt)); pt_req->cmd_pkt_length = sizeof(struct tw_cl_command_packet); if ((error = tw_osli_map_request(req))) goto fw_passthru_err; end_time = tw_osl_get_local_time() + timeout; while (req->state != TW_OSLI_REQ_STATE_COMPLETE) { mtx_lock(req->ioctl_wake_timeout_lock); req->flags |= TW_OSLI_REQ_FLAGS_SLEEPING; error = mtx_sleep(req, req->ioctl_wake_timeout_lock, 0, "twa_passthru", timeout*hz); mtx_unlock(req->ioctl_wake_timeout_lock); if (!(req->flags & TW_OSLI_REQ_FLAGS_SLEEPING)) error = 0; req->flags &= ~TW_OSLI_REQ_FLAGS_SLEEPING; if (! error) { if (((error = req->error_code)) || ((error = (req->state != TW_OSLI_REQ_STATE_COMPLETE))) || ((error = req_pkt->status))) goto fw_passthru_err; break; } if (req_pkt->status) { error = req_pkt->status; goto fw_passthru_err; } if (error == EWOULDBLOCK) { /* Time out! */ if ((!(req->error_code)) && (req->state == TW_OSLI_REQ_STATE_COMPLETE) && (!(req_pkt->status)) ) { #ifdef TW_OSL_DEBUG tw_osli_printf(sc, "request = %p", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x7777, "FALSE Passthru timeout!", req); #endif /* TW_OSL_DEBUG */ error = 0; /* False error */ break; } if (!(tw_cl_is_reset_needed(&(req->ctlr->ctlr_handle)))) { #ifdef TW_OSL_DEBUG tw_osli_printf(sc, "request = %p", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2018, "Passthru request timed out!", req); #else /* TW_OSL_DEBUG */ device_printf((sc)->bus_dev, "Passthru request timed out!\n"); #endif /* TW_OSL_DEBUG */ tw_cl_reset_ctlr(&(req->ctlr->ctlr_handle)); } error = 0; end_time = tw_osl_get_local_time() + timeout; continue; /* * Don't touch req after a reset. It (and any * associated data) will be * unmapped by the callback. */ } /* * Either the request got completed, or we were woken up by a * signal. Calculate the new timeout, in case it was the latter. */ timeout = (end_time - tw_osl_get_local_time()); } /* End of while loop */ /* If there was a payload, copy it back. */ if ((!error) && (req->length)) if ((error = copyout(req->data, user_buf->pdata, user_buf->driver_pkt.buffer_length))) tw_osli_printf(sc, "error = %d", TW_CL_SEVERITY_ERROR_STRING, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2019, "Could not copyout fw_passthru data_buf", error); fw_passthru_err: if (req_pkt->status == TW_CL_ERR_REQ_BUS_RESET) error = EBUSY; user_buf->driver_pkt.os_status = error; /* Free resources. */ if (req->data) free(req->data, TW_OSLI_MALLOC_CLASS); tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); return(error); }
/* * Function name: twa_action * Description: Driver entry point for CAM's use. * * Input: sim -- sim corresponding to the ctlr * ccb -- ptr to CAM request * Output: None * Return value: None */ TW_VOID twa_action(struct cam_sim *sim, union ccb *ccb) { struct twa_softc *sc = (struct twa_softc *)cam_sim_softc(sim); struct ccb_hdr *ccb_h = &(ccb->ccb_h); switch (ccb_h->func_code) { case XPT_SCSI_IO: /* SCSI I/O */ { struct tw_osli_req_context *req; req = tw_osli_get_request(sc); if (req == NULL) { tw_osli_dbg_dprintf(2, sc, "Cannot get request pkt."); /* * Freeze the simq to maintain ccb ordering. The next * ccb that gets completed will unfreeze the simq. */ ccb_h->status &= ~CAM_SIM_QUEUED; ccb_h->status |= CAM_REQUEUE_REQ; xpt_done(ccb); break; } if ((tw_cl_is_reset_needed(&(req->ctlr->ctlr_handle)))) { ccb_h->status &= ~CAM_SIM_QUEUED; ccb_h->status |= CAM_REQUEUE_REQ; xpt_done(ccb); tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); break; } req->req_handle.osl_req_ctxt = req; req->req_handle.is_io = TW_CL_TRUE; req->orig_req = ccb; if (tw_osli_execute_scsi(req, ccb)) tw_osli_req_q_insert_tail(req, TW_OSLI_FREE_Q); break; } case XPT_ABORT: tw_osli_dbg_dprintf(2, sc, "Abort request."); ccb_h->status = CAM_UA_ABORT; xpt_done(ccb); break; case XPT_RESET_BUS: tw_cl_create_event(&(sc->ctlr_handle), TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_FREEBSD_DRIVER, 0x2108, 0x3, TW_CL_SEVERITY_INFO_STRING, "Received Reset Bus request from CAM", " "); tw_cl_set_reset_needed(&(sc->ctlr_handle)); ccb_h->status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: tw_osli_dbg_dprintf(3, sc, "XPT_SET_TRAN_SETTINGS"); /* * This command is not supported, since it's very specific * to SCSI, and we are doing ATA. */ ccb_h->status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; spi->valid = CTS_SPI_VALID_DISC; spi->flags = CTS_SPI_FLAGS_DISC_ENB; scsi->valid = CTS_SCSI_VALID_TQ; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; tw_osli_dbg_dprintf(3, sc, "XPT_GET_TRAN_SETTINGS"); ccb_h->status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: tw_osli_dbg_dprintf(3, sc, "XPT_CALC_GEOMETRY"); cam_calc_geometry(&ccb->ccg, 1/* extended */); xpt_done(ccb); break; case XPT_PATH_INQ: /* Path inquiry -- get twa properties */ { struct ccb_pathinq *path_inq = &ccb->cpi; tw_osli_dbg_dprintf(3, sc, "XPT_PATH_INQ request"); path_inq->version_num = 1; path_inq->hba_inquiry = 0; path_inq->target_sprt = 0; path_inq->hba_misc = 0; path_inq->hba_eng_cnt = 0; path_inq->max_target = TW_CL_MAX_NUM_UNITS; path_inq->max_lun = TW_CL_MAX_NUM_LUNS - 1; path_inq->unit_number = cam_sim_unit(sim); path_inq->bus_id = cam_sim_bus(sim); path_inq->initiator_id = TW_CL_MAX_NUM_UNITS; path_inq->base_transfer_speed = 100000; strlcpy(path_inq->sim_vid, "FreeBSD", SIM_IDLEN); strlcpy(path_inq->hba_vid, "3ware", HBA_IDLEN); strlcpy(path_inq->dev_name, cam_sim_name(sim), DEV_IDLEN); path_inq->transport = XPORT_SPI; path_inq->transport_version = 2; path_inq->protocol = PROTO_SCSI; path_inq->protocol_version = SCSI_REV_2; path_inq->maxio = TW_CL_MAX_IO_SIZE; ccb_h->status = CAM_REQ_CMP; xpt_done(ccb); break; } default: tw_osli_dbg_dprintf(3, sc, "func_code = %x", ccb_h->func_code); ccb_h->status = CAM_REQ_INVALID; xpt_done(ccb); break; } }