summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/isp/isp_freebsd.c1096
1 files changed, 1079 insertions, 17 deletions
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c
index ba45c9e..83d3835 100644
--- a/sys/dev/isp/isp_freebsd.c
+++ b/sys/dev/isp/isp_freebsd.c
@@ -33,12 +33,14 @@
* SUCH DAMAGE.
*/
#include <dev/isp/isp_freebsd.h>
+#include <sys/malloc.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 *);
+static void isp_action(struct cam_sim *, union ccb *);
+static struct ispsoftc *isplist = NULL;
/* #define ISP_LUN0_ONLY 1 */
#ifdef DEBUG
int isp_debug = 2;
@@ -139,12 +141,959 @@ isp_attach(struct ispsoftc *isp)
isp->isp_sim2 = sim;
isp->isp_path2 = path;
}
- if (isp->isp_state == ISP_INITSTATE) {
- isp->isp_state = ISP_RUNSTATE;
+ isp->isp_state = ISP_RUNSTATE;
+ if (isplist == NULL) {
+ isplist = isp;
+ } else {
+ struct ispsoftc *tmp = isplist;
+ while (tmp->isp_osinfo.next) {
+ tmp = tmp->isp_osinfo.next;
+ }
+ tmp->isp_osinfo.next = isp;
+ }
+}
+
+
+/*
+ * Put the target mode functions here, because some are inlines
+ */
+
+#ifdef ISP_TARGET_MODE
+#include "targbh.h"
+
+static __inline int is_lun_enabled(struct ispsoftc *, lun_id_t);
+static __inline int are_any_luns_enabled(struct ispsoftc *);
+static __inline tstate_t *get_lun_statep(struct ispsoftc *, lun_id_t);
+static __inline void rls_lun_statep(struct ispsoftc *, tstate_t *);
+static __inline int isp_psema_sig_rqe(struct ispsoftc *);
+static __inline int isp_cv_wait_timed_rqe(struct ispsoftc *, int);
+static __inline void isp_cv_signal_rqe(struct ispsoftc *, int);
+static __inline void isp_vsema_rqe(struct ispsoftc *);
+static cam_status
+create_lun_state(struct ispsoftc *, struct cam_path *, tstate_t **);
+static void destroy_lun_state(struct ispsoftc *, tstate_t *);
+static void isp_en_lun(struct ispsoftc *, union ccb *);
+static cam_status isp_abort_tgt_ccb(struct ispsoftc *, union ccb *);
+static cam_status isp_target_start_ctio(struct ispsoftc *, union ccb *);
+
+
+static int isp_handle_platform_atio(struct ispsoftc *, at_entry_t *);
+static int isp_handle_platform_atio2(struct ispsoftc *, at2_entry_t *);
+static int isp_handle_platform_ctio(struct ispsoftc *, void *);
+
+static __inline int
+is_lun_enabled(struct ispsoftc *isp, lun_id_t lun)
+{
+ tstate_t *tptr;
+ int s = splsoftcam();
+ if ((tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)]) == NULL) {
+ splx(s);
+ return (0);
+ }
+ do {
+ if (tptr->lun == (lun_id_t) lun) {
+ splx(s);
+ return (1);
+ }
+ } while ((tptr = tptr->next) != NULL);
+ splx(s);
+ return (0);
+}
+
+static __inline int
+are_any_luns_enabled(struct ispsoftc *isp)
+{
+ int i;
+ for (i = 0; i < LUN_HASH_SIZE; i++) {
+ if (isp->isp_osinfo.lun_hash[i]) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static __inline tstate_t *
+get_lun_statep(struct ispsoftc *isp, lun_id_t lun)
+{
+ tstate_t *tptr;
+ int s;
+
+ s = splsoftcam();
+ if (lun == CAM_LUN_WILDCARD) {
+ tptr = &isp->isp_osinfo.tsdflt;
+ tptr->hold++;
+ splx(s);
+ return (tptr);
+ } else {
+ tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)];
+ }
+ if (tptr == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ do {
+ if (tptr->lun == lun) {
+ tptr->hold++;
+ splx(s);
+ return (tptr);
+ }
+ } while ((tptr = tptr->next) != NULL);
+ splx(s);
+ return (tptr);
+}
+
+static __inline void
+rls_lun_statep(struct ispsoftc *isp, tstate_t *tptr)
+{
+ if (tptr->hold)
+ tptr->hold--;
+}
+
+static __inline int
+isp_psema_sig_rqe(struct ispsoftc *isp)
+{
+ int s = splcam();
+ while (isp->isp_osinfo.tmflags & TM_BUSY) {
+ isp->isp_osinfo.tmflags |= TM_WANTED;
+ if (tsleep(&isp->isp_osinfo.tmflags, PRIBIO|PCATCH, "i0", 0)) {
+ splx(s);
+ return (-1);
+ }
+ isp->isp_osinfo.tmflags |= TM_BUSY;
+ }
+ splx(s);
+ return (0);
+}
+
+static __inline int
+isp_cv_wait_timed_rqe(struct ispsoftc *isp, int timo)
+{
+ int s = splcam();
+ if (tsleep(&isp->isp_osinfo.rstatus, PRIBIO, "qt1", timo)) {
+ splx(s);
+ return (-1);
}
+ splx(s);
+ return (0);
+}
+
+static __inline void
+isp_cv_signal_rqe(struct ispsoftc *isp, int status)
+{
+ isp->isp_osinfo.rstatus = status;
+ wakeup(&isp->isp_osinfo.rstatus);
+}
+
+static __inline void
+isp_vsema_rqe(struct ispsoftc *isp)
+{
+ int s = splcam();
+ if (isp->isp_osinfo.tmflags & TM_WANTED) {
+ isp->isp_osinfo.tmflags &= ~TM_WANTED;
+ wakeup(&isp->isp_osinfo.tmflags);
+ }
+ isp->isp_osinfo.tmflags &= ~TM_BUSY;
+ splx(s);
+}
+
+static cam_status
+create_lun_state(struct ispsoftc *isp, struct cam_path *path, tstate_t **rslt)
+{
+ int s;
+ cam_status status;
+ lun_id_t lun;
+ tstate_t *tptr, *new;
+
+ lun = xpt_path_lun_id(path);
+ if (lun < 0) {
+ return (CAM_LUN_INVALID);
+ }
+ if (is_lun_enabled(isp, lun)) {
+ return (CAM_LUN_ALRDY_ENA);
+ }
+ new = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT);
+ if (new == NULL) {
+ return (CAM_RESRC_UNAVAIL);
+ }
+ bzero(new, sizeof (tstate_t));
+
+ status = xpt_create_path(&new->owner, NULL, xpt_path_path_id(path),
+ xpt_path_target_id(path), xpt_path_lun_id(path));
+ if (status != CAM_REQ_CMP) {
+ free(new, M_DEVBUF);
+ return (status);
+ }
+ new->lun = lun;
+ SLIST_INIT(&new->atios);
+ SLIST_INIT(&new->inots);
+ new->hold = 1;
+
+ s = splsoftcam();
+ if ((tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)]) == NULL) {
+ isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)] = new;
+ } else {
+ while (tptr->next)
+ tptr = tptr->next;
+ tptr->next = new;
+ }
+ splx(s);
+ *rslt = new;
+ return (CAM_REQ_CMP);
+}
+
+static __inline void
+destroy_lun_state(struct ispsoftc *isp, tstate_t *tptr)
+{
+ tstate_t *lw, *pw;
+ int s;
+
+ s = splsoftcam();
+ if (tptr->hold) {
+ splx(s);
+ return;
+ }
+ pw = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(tptr->lun)];
+ if (pw == NULL) {
+ splx(s);
+ return;
+ } else if (pw->lun == tptr->lun) {
+ isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(tptr->lun)] = pw->next;
+ } else {
+ lw = pw;
+ pw = lw->next;
+ while (pw) {
+ if (pw->lun == tptr->lun) {
+ lw->next = pw->next;
+ break;
+ }
+ lw = pw;
+ pw = pw->next;
+ }
+ if (pw == NULL) {
+ splx(s);
+ return;
+ }
+ }
+ free(tptr, M_DEVBUF);
+ splx(s);
}
static void
+isp_en_lun(struct ispsoftc *isp, union ccb *ccb)
+{
+ const char *lfmt = "Lun now %sabled for target mode\n";
+ struct ccb_en_lun *cel = &ccb->cel;
+ tstate_t *tptr;
+ u_int16_t rstat;
+ int bus, s;
+ lun_id_t lun;
+ target_id_t tgt;
+
+
+ bus = XS_CHANNEL(ccb);
+ tgt = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+
+ /*
+ * First, check to see if we're enabling on fibre channel
+ * and don't yet have a notion of who the heck we are (no
+ * loop yet). We do this by
+ */
+ if (IS_FC(isp) && cel->enable &&
+ (isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) == 0) {
+ int rv;
+ fcparam *fcp = isp->isp_param;
+
+ s = splcam();
+ rv = isp_control(isp, ISPCTL_FCLINK_TEST, NULL);
+ (void) splx(s);
+ if (rv || fcp->isp_fwstate != FW_READY) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("link status not good yet\n");
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ return;
+ }
+ s = splcam();
+ rv = isp_control(isp, ISPCTL_PDB_SYNC, NULL);
+ (void) splx(s);
+ if (rv || fcp->isp_loopstate != LOOP_READY) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("could not get a good port database\n");
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ return;
+ }
+ }
+
+
+ /*
+ * Next check to see whether this is a target/lun wildcard action.
+ *
+ * If so, we enable/disable target mode but don't do any lun enabling.
+ */
+ if (lun == CAM_LUN_WILDCARD && tgt == CAM_TARGET_WILDCARD) {
+ int av;
+ tptr = &isp->isp_osinfo.tsdflt;
+ if (cel->enable) {
+ if (isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) {
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+ ccb->ccb_h.status =
+ xpt_create_path(&tptr->owner, NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ return;
+ }
+ SLIST_INIT(&tptr->atios);
+ SLIST_INIT(&tptr->inots);
+ av = 1;
+ s = splcam();
+ av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av);
+ if (av) {
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_free_path(tptr->owner);
+ splx(s);
+ return;
+ }
+ isp->isp_osinfo.tmflags |= TM_TMODE_ENABLED;
+ splx(s);
+ } else {
+ if ((isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) == 0) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+ if (are_any_luns_enabled(isp)) {
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ return;
+ }
+ av = 0;
+ s = splcam();
+ av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av);
+ if (av) {
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ splx(s);
+ return;
+ }
+ isp->isp_osinfo.tmflags &= ~TM_TMODE_ENABLED;
+ splx(s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+ xpt_print_path(ccb->ccb_h.path);
+ printf(lfmt, (cel->enable) ? "en" : "dis");
+ return;
+ }
+
+ /*
+ * Do some sanity checking first.
+ */
+
+ if (IS_SCSI(isp)) {
+ if (lun < 0 || lun >= 32) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+ if (tgt != CAM_TARGET_WILDCARD &&
+ tgt != ((sdparam *) isp->isp_param)->isp_initiator_id) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ return;
+ }
+ } else {
+#ifdef ISP2100_SCCLUN
+ if (lun < 0 || lun >= 65536) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+#else
+ if (lun < 0 || lun >= 16) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+#endif
+ if (tgt != CAM_TARGET_WILDCARD &&
+ tgt != ((fcparam *) isp->isp_param)->isp_loopid) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ return;
+ }
+ }
+
+
+ if (cel->enable) {
+ ccb->ccb_h.status =
+ create_lun_state(isp, ccb->ccb_h.path, &tptr);
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ return;
+ }
+ } else {
+ tptr = get_lun_statep(isp, lun);
+ if (tptr == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+ }
+
+ if (isp_psema_sig_rqe(isp)) {
+ rls_lun_statep(isp, tptr);
+ if (cel->enable)
+ destroy_lun_state(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ return;
+ }
+
+ s = splcam();
+ if (cel->enable) {
+ u_int32_t seq = isp->isp_osinfo.rollinfo++;
+ rstat = LUN_ERR;
+ if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("isp_lun_cmd failed\n");
+ goto out;
+ }
+ if (isp_cv_wait_timed_rqe(isp, 30 * hz)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("wait for ENABLE LUN timed out\n");
+ goto out;
+ }
+ rstat = isp->isp_osinfo.rstatus;
+ if (rstat != LUN_OK) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("ENABLE LUN returned 0x%x\n", rstat);
+ goto out;
+ }
+ } else {
+ u_int32_t seq;
+
+ seq = isp->isp_osinfo.rollinfo++;
+ rstat = LUN_ERR;
+
+ if (isp_lun_cmd(isp, -RQSTYPE_MODIFY_LUN, bus, tgt, lun, seq)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("isp_lun_cmd failed\n");
+ goto out;
+ }
+ if (isp_cv_wait_timed_rqe(isp, 30 * hz)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("wait for MODIFY LUN timed out\n");
+ goto out;
+ }
+ rstat = isp->isp_osinfo.rstatus;
+ if (rstat != LUN_OK) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("MODIFY LUN returned 0x%x\n", rstat);
+ goto out;
+ }
+ rstat = LUN_ERR;
+ seq = isp->isp_osinfo.rollinfo++;
+
+ if (isp_lun_cmd(isp, -RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("isp_lun_cmd failed\n");
+ goto out;
+ }
+ if (isp_cv_wait_timed_rqe(isp, 30 * hz)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("wait for ENABLE LUN timed out\n");
+ goto out;
+ }
+ rstat = isp->isp_osinfo.rstatus;
+ if (rstat != LUN_OK) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("ENABLE LUN returned 0x%x\n", rstat);
+ goto out;
+ }
+ }
+out:
+ isp_vsema_rqe(isp);
+ splx(s);
+
+ if (rstat != LUN_OK) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("lun %sable failed\n", (cel->enable) ? "en" : "dis");
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ rls_lun_statep(isp, tptr);
+ if (cel->enable)
+ destroy_lun_state(isp, tptr);
+ } else {
+ xpt_print_path(ccb->ccb_h.path);
+ printf(lfmt, (cel->enable) ? "en" : "dis");
+ rls_lun_statep(isp, tptr);
+ if (cel->enable == 0) {
+ destroy_lun_state(isp, tptr);
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+}
+
+static cam_status
+isp_abort_tgt_ccb(struct ispsoftc *isp, union ccb *ccb)
+{
+ tstate_t *tptr;
+ struct ccb_hdr_slist *lp;
+ struct ccb_hdr *curelm;
+ int found;
+ union ccb *accb = ccb->cab.abort_ccb;
+
+ if (accb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ if (IS_FC(isp) && (accb->ccb_h.target_id !=
+ ((fcparam *) isp->isp_param)->isp_loopid)) {
+ return (CAM_PATH_INVALID);
+ } else if (IS_SCSI(isp) && (accb->ccb_h.target_id !=
+ ((sdparam *) isp->isp_param)->isp_initiator_id)) {
+ return (CAM_PATH_INVALID);
+ }
+ }
+ tptr = get_lun_statep(isp, accb->ccb_h.target_lun);
+ if (tptr == NULL) {
+ return (CAM_PATH_INVALID);
+ }
+ if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+ lp = &tptr->atios;
+ } else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) {
+ lp = &tptr->inots;
+ } else {
+ rls_lun_statep(isp, tptr);
+ return (CAM_UA_ABORT);
+ }
+ curelm = SLIST_FIRST(lp);
+ found = 0;
+ if (curelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_REMOVE_HEAD(lp, sim_links.sle);
+ } else {
+ while(curelm != NULL) {
+ struct ccb_hdr *nextelm;
+
+ nextelm = SLIST_NEXT(curelm, sim_links.sle);
+ if (nextelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_NEXT(curelm, sim_links.sle) =
+ SLIST_NEXT(nextelm, sim_links.sle);
+ break;
+ }
+ curelm = nextelm;
+ }
+ }
+ rls_lun_statep(isp, tptr);
+ if (found) {
+ accb->ccb_h.status = CAM_REQ_ABORTED;
+ return (CAM_REQ_CMP);
+ }
+ return(CAM_PATH_INVALID);
+}
+
+static cam_status
+isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb)
+{
+ void *qe;
+ u_int32_t *hp, save_handle;
+ u_int16_t iptr, optr;
+
+ if (isp_getrqentry(isp, &iptr, &optr, &qe)) {
+ PRINTF("%s: Request Queue Overflow in isp_target_start_ctio\n",
+ isp->isp_name);
+ return (CAM_RESRC_UNAVAIL);
+ }
+ MEMZERO(qe, QENTRY_LEN);
+
+ /*
+ * We're either moving data or completing a command here.
+ */
+
+ if (IS_FC(isp)) {
+ ct2_entry_t *cto = qe;
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
+ cto->ct_header.rqs_entry_count = 1;
+ cto->ct_iid = ccb->csio.init_id;
+#ifdef ISP2100_SCCLUN
+ cto->ct_scclun = ccb->ccb_h.target_lun;
+#else
+ cto->ct_lun = ccb->ccb_h.target_lun;
+#endif
+ cto->ct_rxid = ccb->csio.tag_id;
+ cto->ct_flags = CT2_FLAG_MODE0 | CT2_CCINCR;
+ if (ccb->csio.dxfer_len == 0) {
+ cto->ct_flags |= CT2_NO_DATA;
+ }
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
+ cto->ct_flags |= CT2_SENDSTATUS;
+ cto->rsp.m0.ct_scsi_status = ccb->csio.scsi_status;
+ if (ccb->csio.resid) {
+printf("resid %d\n", ccb->csio.resid);
+ cto->ct_resid = ccb->csio.resid;
+ if (ccb->csio.resid < 0)
+ cto->ct_flags |= CT2_DATA_OVER;
+ else
+ cto->ct_flags |= CT2_DATA_UNDER;
+ }
+ /*
+ * If we had Sense Data already,
+ * here's where we'd set it up.
+ */
+ }
+ hp = &cto->ct_reserved;
+ } else {
+ ct_entry_t *cto = qe;
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
+ cto->ct_header.rqs_entry_count = 1;
+ cto->ct_iid = ccb->csio.init_id;
+ cto->ct_tgt = ccb->ccb_h.target_id;
+ cto->ct_lun = ccb->ccb_h.target_lun;
+ cto->ct_tag_type = ccb->csio.tag_action;
+ cto->ct_tag_val = ccb->csio.tag_id;
+ cto->ct_flags = CT_CCINCR;
+ if (ccb->csio.dxfer_len) {
+ cto->ct_flags |= CT_NO_DATA;
+ }
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
+ cto->ct_flags |= CT_SENDSTATUS;
+ cto->ct_scsi_status = ccb->csio.scsi_status;
+ cto->ct_resid = ccb->csio.resid;
+ }
+ hp = &cto->ct_reserved;
+ }
+
+ if (isp_save_xs(isp, (ISP_SCSI_XFER_T *)ccb, hp)) {
+ PRINTF("%s: No XFLIST pointers for isp_target_start_ctio\n",
+ isp->isp_name);
+ return (CAM_RESRC_UNAVAIL);
+ }
+
+
+ /*
+ * Call the dma setup routines for this entry (and any subsequent
+ * CTIOs) if there's data to move, and then tell the f/w it's got
+ * new things to play with. As with ispscsicmd's usage of DMA setup,
+ * any swizzling is done in the machine dependent layer. Because
+ * of this, we put the request onto the queue area first in native
+ * format.
+ */
+
+ save_handle = *hp;
+ switch (ISP_DMASETUP(isp, &ccb->csio, qe, &iptr, optr)) {
+ case CMD_QUEUED:
+ MemoryBarrier();
+ ISP_ADD_REQUEST(isp, iptr);
+ return (CAM_REQ_INPROG);
+
+ case CMD_EAGAIN:
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ isp_destroy_handle(isp, save_handle);
+ return (CAM_RESRC_UNAVAIL);
+
+ default:
+ isp_destroy_handle(isp, save_handle);
+ return (ccb->ccb_h.spriv_field0);
+ }
+}
+
+
+/*
+ * Handle ATIO stuff that the generic code can't.
+ * This means handling CDBs.
+ */
+
+static int
+isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep)
+{
+ tstate_t *tptr;
+ int status;
+ struct ccb_accept_tio *atiop;
+
+ /*
+ * The firmware status (except for the QLTM_SVALID bit)
+ * indicates why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ *
+ * If the DISCONNECTS DISABLED bit is set in the flags field,
+ * we're still connected on the SCSI bus - i.e. the initiator
+ * did not set DiscPriv in the identify message. We don't care
+ * about this so it's ignored.
+ */
+ status = aep->at_status;
+ if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) {
+ /*
+ * Bus Phase Sequence error. We should have sense data
+ * suggested by the f/w. I'm not sure quite yet what
+ * to do about this for CAM.
+ */
+ printf("%s: PHASE ERROR\n", isp->isp_name);
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+ if ((status & ~QLTM_SVALID) != AT_CDB) {
+ printf("%s: bogus atio (0x%x) leaked to platform\n",
+ isp->isp_name, status);
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+
+ tptr = get_lun_statep(isp, aep->at_lun);
+ if (tptr == NULL) {
+ tptr = get_lun_statep(isp, CAM_LUN_WILDCARD);
+ }
+
+ if (tptr == NULL) {
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a BUSY status
+ * instead. This works out okay because the only
+ * time we should, in fact, get this, is in the
+ * case that somebody configured us without the
+ * blackhole driver, so they get what they deserve.
+ */
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+
+ atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
+ if (atiop == NULL) {
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a QUEUE FULL status
+ * instead. This works out okay because the only time we
+ * should, in fact, get this, is in the case that we've
+ * run out of ATIOS.
+ */
+ xpt_print_path(tptr->owner);
+ printf("no ATIOS for lun %d from initiator %d\n",
+ aep->at_lun, aep->at_iid);
+ rls_lun_statep(isp, tptr);
+ if (aep->at_flags & AT_TQAE)
+ isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0);
+ else
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+ SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
+ if (tptr == &isp->isp_osinfo.tsdflt) {
+ atiop->ccb_h.target_id = aep->at_tgt;
+ atiop->ccb_h.target_lun = aep->at_lun;
+ }
+ if (aep->at_flags & AT_NODISC) {
+ xpt_print_path(tptr->owner);
+ printf("incoming command that cannot disconnect\n");
+ }
+
+
+ atiop->init_id = aep->at_iid;
+ atiop->cdb_len = aep->at_cdblen;
+ MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen);
+ atiop->ccb_h.status = CAM_CDB_RECVD;
+ if ((atiop->tag_action = aep->at_tag_type) != 0) {
+ atiop->tag_id = aep->at_tag_val;
+ atiop->ccb_h.status |= CAM_TAG_ACTION_VALID;
+ }
+ xpt_done((union ccb*)atiop);
+ if (isp_tdebug) {
+ xpt_print_path(tptr->owner);
+ printf("CDB[0x%x] from %d for lun %d tag 0x%x tagtype 0x%x\n",
+ aep->at_cdb[0] & 0xff, aep->at_iid, aep->at_lun,
+ aep->at_tag_val & 0xff, aep->at_tag_type);
+ }
+ rls_lun_statep(isp, tptr);
+ return (0);
+}
+
+static int
+isp_handle_platform_atio2(struct ispsoftc *isp, at2_entry_t *aep)
+{
+ tstate_t *tptr;
+ struct ccb_accept_tio *atiop;
+
+ /*
+ * The firmware status (except for the QLTM_SVALID bit)
+ * indicates why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ */
+ if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) {
+ printf("%s: bogus atio (0x%x) leaked to platform\n",
+ isp->isp_name, aep->at_status);
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+
+ tptr = get_lun_statep(isp, aep->at_lun);
+ if (tptr == NULL) {
+ tptr = get_lun_statep(isp, CAM_LUN_WILDCARD);
+ }
+
+ if (tptr == NULL) {
+#if 0
+ /* XXX WE REALLY NEED A HARDWIRED SENSE/INQ CTIO TO USE XXX */
+ u_int32_t ccode = SCSI_STATUS_CHECK | 0x100;
+#if NTARGBH > 0
+ /* Not Ready, Unit Not Self-Configured yet.... */
+ ccode |= (SSD_KEY_NOT_READY << 8) | (0x3E << 24);
+#else
+ /* Illegal Request, Unit Not Self-Configured yet.... */
+ ccode |= (SSD_KEY_ILLEGAL_REQUEST << 8) | (0x25 << 24);
+#endif
+#else
+ u_int32_t ccode = SCSI_STATUS_BUSY;
+#endif
+
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a BUSY status
+ * instead. This works out okay because the only
+ * time we should, in fact, get this, is in the
+ * case that somebody configured us without the
+ * blackhole driver, so they get what they deserve.
+ */
+ isp_endcmd(isp, aep, ccode, 0);
+ return (0);
+ }
+
+ atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
+ if (atiop == NULL) {
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a QUEUE FULL status
+ * instead. This works out okay because the only time we
+ * should, in fact, get this, is in the case that we've
+ * run out of ATIOS.
+ */
+ xpt_print_path(tptr->owner);
+ printf("no ATIOS for lun %d from initiator %d\n",
+ aep->at_lun, aep->at_iid);
+ rls_lun_statep(isp, tptr);
+ if (aep->at_flags & AT_TQAE)
+ isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0);
+ else
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+ SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
+ if (tptr == &isp->isp_osinfo.tsdflt) {
+ atiop->ccb_h.target_id =
+ ((fcparam *)isp->isp_param)->isp_loopid;
+ atiop->ccb_h.target_lun = aep->at_lun;
+ }
+ atiop->init_id = aep->at_iid;
+ atiop->cdb_len = ATIO2_CDBLEN;
+ MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN);
+ atiop->ccb_h.status = CAM_CDB_RECVD;
+ atiop->tag_id = aep->at_rxid;
+ switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) {
+ case ATIO2_TC_ATTR_SIMPLEQ:
+ atiop->tag_action = MSG_SIMPLE_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_HEADOFQ:
+ atiop->tag_action = MSG_HEAD_OF_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_ORDERED:
+ atiop->tag_action = MSG_ORDERED_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_ACAQ: /* ?? */
+ case ATIO2_TC_ATTR_UNTAGGED:
+ default:
+ atiop->tag_action = 0;
+ break;
+ }
+ if (atiop->tag_action != 0) {
+ atiop->ccb_h.status |= CAM_TAG_ACTION_VALID;
+ }
+ xpt_done((union ccb*)atiop);
+ if (isp_tdebug) {
+ xpt_print_path(tptr->owner);
+ printf("CDB[0x%x] from %d for lun %d RX_ID 0x%x tflags 0x%x\n",
+ aep->at_cdb[0] & 0xff, aep->at_iid, aep->at_lun,
+ aep->at_rxid & 0xffff, aep->at_taskflags);
+ }
+ rls_lun_statep(isp, tptr);
+ return (0);
+}
+
+static int
+isp_handle_platform_ctio(struct ispsoftc *isp, void * arg)
+{
+ union ccb *ccb;
+ int sentstatus, ok;
+
+ /*
+ * CTIO and CTIO2 are close enough....
+ */
+
+ ccb = (union ccb *) isp_find_xs(isp, ((ct_entry_t *)arg)->ct_reserved);
+ KASSERT((ccb != NULL), ("null ccb in isp_handle_platform_ctio"));
+ isp_destroy_handle(isp, ((ct_entry_t *)arg)->ct_reserved);
+
+ if (IS_FC(isp)) {
+ ct2_entry_t *ct = arg;
+ sentstatus = ct->ct_flags & CT2_SENDSTATUS;
+ ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
+ if (isp_tdebug) {
+ printf("%s: CTIO2 RX_ID 0x%x sts 0x%x flg 0x%x done\n",
+ isp->isp_name, ct->ct_rxid, ct->ct_status,
+ ct->ct_flags);
+ }
+ } else {
+ ct_entry_t *ct = arg;
+ sentstatus = ct->ct_flags & CT_SENDSTATUS;
+ ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
+ if (isp_tdebug) {
+ printf("%s: CTIO tag 0x%x sts 0x%x flg 0x%x done\n",
+ isp->isp_name, ct->ct_tag_val, ct->ct_status,
+ ct->ct_flags);
+ }
+ }
+
+ /*
+ * We're here either because data transfers are done (and
+ * it's time to send a final status CTIO) or because the final
+ * status CTIO is done. We don't get called for all intermediate
+ * CTIOs that happen for a large data transfer.
+ *
+ * In any case, for this platform, the upper layers figure out
+ * what to do next, so all we do here is collect status and
+ * pass information along.
+ */
+
+ if (sentstatus) {
+ /*
+ * Data transfer done. See if all went okay.
+ */
+ if (ok) {
+ ccb->csio.resid = 0;
+ } else {
+ ccb->csio.resid = ccb->csio.dxfer_len;
+ }
+ }
+
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ }
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ if (isp->isp_osinfo.simqfrozen & SIMQFRZ_RESOURCE) {
+ isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_RESOURCE;
+ if (isp->isp_osinfo.simqfrozen == 0) {
+ if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
+ IDPRINTF(3, ("%s: isp_done -> relsimq\n",
+ isp->isp_name));
+ ccb->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));
+ }
+ }
+ xpt_done(ccb);
+ return (0);
+}
+#endif
+
+static void
isp_cam_async(void *cbarg, u_int32_t code, struct cam_path *path, void *arg)
{
struct cam_sim *sim;
@@ -312,16 +1261,64 @@ isp_action(struct cam_sim *sim, union ccb *ccb)
}
break;
+#ifdef ISP_TARGET_MODE
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;
+ isp_en_lun(isp, ccb);
xpt_done(ccb);
break;
+ case XPT_NOTIFY_ACK: /* recycle notify ack */
+xpt_print_path(ccb->ccb_h.path);
+printf("notify ack\n");
+ case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */
+ case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */
+ {
+ tstate_t *tptr = get_lun_statep(isp, ccb->ccb_h.target_lun);
+ if (tptr == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ s = splsoftcam();
+ if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+ SLIST_INSERT_HEAD(&tptr->atios,
+ &ccb->ccb_h, sim_links.sle);
+ } else {
+ SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h,
+ sim_links.sle);
+ }
+ splx(s);
+ rls_lun_statep(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ break;
+ }
+ case XPT_CONT_TARGET_IO:
+ {
+ s = splcam();
+ ccb->ccb_h.status = isp_target_start_ctio(isp, ccb);
+ if (ccb->ccb_h.status != CAM_REQ_INPROG) {
+ if (isp->isp_osinfo.simqfrozen == 0) {
+ xpt_freeze_simq(sim, 1);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("XPT_CONT_TARGET_IO freeze simq\n");
+ }
+ isp->isp_osinfo.simqfrozen |= SIMQFRZ_RESOURCE;
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_REQUEUE_REQ;
+ xpt_done(ccb);
+ } else {
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+ }
+ splx(s);
+ break;
+ }
+#endif
case XPT_RESET_DEV: /* BDR the specified SCSI device */
- tgt = ccb->ccb_h.target_id; /* XXX: Which Bus? */
+
+ bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path));
+ tgt = ccb->ccb_h.target_id;
+ tgt |= (bus << 16);
+
s = splcam();
error = isp_control(isp, ISPCTL_RESET_DEV, &tgt);
(void) splx(s);
@@ -333,17 +1330,36 @@ isp_action(struct cam_sim *sim, union ccb *ccb)
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;
+ {
+ union ccb *accb = ccb->cab.abort_ccb;
+ switch (accb->ccb_h.func_code) {
+#ifdef ISP_TARGET_MODE
+ case XPT_ACCEPT_TARGET_IO:
+ case XPT_IMMED_NOTIFY:
+ ccb->ccb_h.status = isp_abort_tgt_ccb(isp, ccb);
+ break;
+ case XPT_CONT_TARGET_IO:
+ PRINTF("%s: cannot abort CTIOs yet\n", isp->isp_name);
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ break;
+#endif
+ case XPT_SCSI_IO:
+ s = splcam();
+ error = isp_control(isp, ISPCTL_ABORT_CMD, ccb);
+ (void) splx(s);
+ if (error) {
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ } else {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
}
xpt_done(ccb);
break;
-
+ }
case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */
cts = &ccb->cts;
@@ -352,7 +1368,8 @@ isp_action(struct cam_sim *sim, union ccb *ccb)
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));
+
+ bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
sdp += bus;
#if 0
@@ -569,7 +1586,11 @@ isp_action(struct cam_sim *sim, union ccb *ccb)
struct ccb_pathinq *cpi = &ccb->cpi;
cpi->version_num = 1;
+#ifdef ISP_TARGET_MODE
+ cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO;
+#else
cpi->target_sprt = 0;
+#endif
cpi->hba_eng_cnt = 0;
cpi->max_target = ISP_MAX_TARGETS(isp) - 1;
cpi->max_lun = ISP_MAX_LUNS(isp) - 1;
@@ -840,7 +1861,48 @@ isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg)
break;
}
#endif
+#ifdef ISP_TARGET_MODE
+ case ISPASYNC_TARGET_MESSAGE:
+ {
+ tmd_msg_t *mp = arg;
+ ITDEBUG(1, ("%s: bus %d iid %d tgt %d lun %d ttype %x tval %x"
+ " msg[0]=0x%x\n", isp->isp_name, mp->nt_bus, mp->nt_iid,
+ mp->nt_tgt, mp->nt_lun, mp->nt_tagtype, mp->nt_tagval,
+ mp->nt_msg[0]));
+ break;
+ }
+ case ISPASYNC_TARGET_EVENT:
+ {
+ tmd_event_t *ep = arg;
+ ITDEBUG(1, ("%s: bus %d event code 0x%x\n", isp->isp_name,
+ ep->ev_bus, ep->ev_event));
+ break;
+ }
+ case ISPASYNC_TARGET_ACTION:
+ switch (((isphdr_t *)arg)->rqs_entry_type) {
+ default:
+ printf("%s: event 0x%x for unhandled target action\n",
+ isp->isp_name, ((isphdr_t *)arg)->rqs_entry_type);
+ break;
+ case RQSTYPE_ATIO:
+ rv = isp_handle_platform_atio(isp, (at_entry_t *) arg);
+ break;
+ case RQSTYPE_ATIO2:
+ rv = isp_handle_platform_atio2(isp, (at2_entry_t *)arg);
+ break;
+ case RQSTYPE_CTIO2:
+ case RQSTYPE_CTIO:
+ rv = isp_handle_platform_ctio(isp, arg);
+ break;
+ case RQSTYPE_ENABLE_LUN:
+ case RQSTYPE_MODIFY_LUN:
+ isp_cv_signal_rqe(isp, ((lun_entry_t *)arg)->le_status);
+ break;
+ }
+ break;
+#endif
default:
+ PRINTF("%s: unknown isp_async event %d\n", isp->isp_name, cmd);
rv = -1;
break;
}
OpenPOWER on IntegriCloud