📄 scsi.c
字号:
static intwait_for_select(hd) volatile register struct scsidevice *hd;{ u_char ints; while ((ints = hd->scsi_ints) == 0) DELAY(1); hd->scsi_ints = ints; return (!(hd->scsi_ssts & SSTS_INITIATOR));}static intixfer_start(hd, len, phase, wait) volatile register struct scsidevice *hd; int len; u_char phase; register int wait;{ hd->scsi_tch = len >> 16; hd->scsi_tcm = len >> 8; hd->scsi_tcl = len; hd->scsi_pctl = phase; hd->scsi_tmod = 0; /*XXX*/ hd->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR; /* wait for xfer to start or svc_req interrupt */ while ((hd->scsi_ssts & SSTS_BUSY) == 0) { if (hd->scsi_ints || --wait < 0) {#ifdef DEBUG if (scsi_debug) printf("ixfer_start fail: i%x, w%d\n", hd->scsi_ints, wait);#endif HIST(ixstart_wait, wait) return (0); } DELAY(1); } HIST(ixstart_wait, wait) return (1);}static intixfer_out(hd, len, buf) volatile register struct scsidevice *hd; int len; register u_char *buf;{ register int wait = scsi_data_wait; for (; len > 0; --len) { while (hd->scsi_ssts & SSTS_DREG_FULL) { if (hd->scsi_ints || --wait < 0) {#ifdef DEBUG if (scsi_debug) printf("ixfer_out fail: l%d i%x w%d\n", len, hd->scsi_ints, wait);#endif HIST(ixout_wait, wait) return (len); } DELAY(1); } hd->scsi_dreg = *buf++; } HIST(ixout_wait, wait) return (0);}static voidixfer_in(hd, len, buf) volatile register struct scsidevice *hd; int len; register u_char *buf;{ register int wait = scsi_data_wait; for (; len > 0; --len) { while (hd->scsi_ssts & SSTS_DREG_EMPTY) { if (hd->scsi_ints || --wait < 0) { while (! (hd->scsi_ssts & SSTS_DREG_EMPTY)) { *buf++ = hd->scsi_dreg; --len; }#ifdef DEBUG if (scsi_debug) printf("ixfer_in fail: l%d i%x w%d\n", len, hd->scsi_ints, wait);#endif HIST(ixin_wait, wait) return; } DELAY(1); } *buf++ = hd->scsi_dreg; } HIST(ixin_wait, wait)}static intmxfer_in(hd, len, buf, phase) volatile register struct scsidevice *hd; register int len; register u_char *buf; register u_char phase;{ register int wait = scsi_cmd_wait; register int i; hd->scsi_tmod = 0; for (i = 0; i < len; ++i) { /* * manual sez: reset ATN before ACK is sent. */ if (hd->scsi_psns & PSNS_ATN) hd->scsi_scmd = SCMD_RST_ATN; /* * wait for the request line (which says the target * wants to give us data). If the phase changes while * we're waiting, we're done. */ while ((hd->scsi_psns & PSNS_REQ) == 0) { if (--wait < 0) { HIST(mxin_wait, wait) return (-1); } if ((hd->scsi_psns & PHASE) != phase || (hd->scsi_ssts & SSTS_INITIATOR) == 0) goto out; DELAY(1); } /* * set ack (which says we're ready for the data, wait for * req to go away (target says data is available), grab the * data, then reset ack (say we've got the data). */ hd->scsi_pctl = phase; hd->scsi_scmd = SCMD_SET_ACK; while (hd->scsi_psns & PSNS_REQ) { if (--wait < 0) { HIST(mxin_wait, wait) return (-2); } DELAY(1); } *buf++ = hd->scsi_temp; hd->scsi_scmd = SCMD_RST_ACK; }out: HIST(mxin_wait, wait) /* * Wait for manual transfer to finish. * Avoids occasional "unexpected phase" errors in finishxfer * formerly addressed by per-slave delays. */ wait = scsi_cmd_wait; while ((hd->scsi_ssts & SSTS_ACTIVE) == SSTS_INITIATOR) { if (--wait < 0) break; DELAY(1); } HIST(mxin2_wait, wait) return (i);}/* * SCSI 'immediate' command: issue a command to some SCSI device * and get back an 'immediate' response (i.e., do programmed xfer * to get the response data). 'cbuf' is a buffer containing a scsi * command of length clen bytes. 'buf' is a buffer of length 'len' * bytes for data. The transfer direction is determined by the device * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the * command must supply no data. 'xferphase' is the bus phase the * caller expects to happen after the command is issued. It should * be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE. */static intscsiicmd(hs, target, cbuf, clen, buf, len, xferphase) struct scsi_softc *hs; int target; u_char *cbuf; int clen; u_char *buf; int len; u_char xferphase;{ volatile register struct scsidevice *hd = (struct scsidevice *)hs->sc_hc->hp_addr; u_char phase, ints; register int wait; /* select the SCSI bus (it's an error if bus isn't free) */ if (issue_select(hd, target, hs->sc_scsi_addr)) return (-1); if (wait_for_select(hd)) return (-1); /* * Wait for a phase change (or error) then let the device * sequence us through the various SCSI phases. */ hs->sc_stat[0] = 0xff; hs->sc_msg[0] = 0xff; phase = CMD_PHASE; while (1) { wait = scsi_cmd_wait; switch (phase) { case CMD_PHASE: if (ixfer_start(hd, clen, phase, wait)) if (ixfer_out(hd, clen, cbuf)) goto abort; phase = xferphase; break; case DATA_IN_PHASE: if (len <= 0) goto abort; wait = scsi_data_wait; if (ixfer_start(hd, len, phase, wait) || !(hd->scsi_ssts & SSTS_DREG_EMPTY)) ixfer_in(hd, len, buf); phase = STATUS_PHASE; break; case DATA_OUT_PHASE: if (len <= 0) goto abort; wait = scsi_data_wait; if (ixfer_start(hd, len, phase, wait)) { if (ixfer_out(hd, len, buf)) goto abort; } phase = STATUS_PHASE; break; case STATUS_PHASE: wait = scsi_data_wait; if (ixfer_start(hd, sizeof(hs->sc_stat), phase, wait) || !(hd->scsi_ssts & SSTS_DREG_EMPTY)) ixfer_in(hd, sizeof(hs->sc_stat), hs->sc_stat); phase = MESG_IN_PHASE; break; case MESG_IN_PHASE: if (ixfer_start(hd, sizeof(hs->sc_msg), phase, wait) || !(hd->scsi_ssts & SSTS_DREG_EMPTY)) { ixfer_in(hd, sizeof(hs->sc_msg), hs->sc_msg); hd->scsi_scmd = SCMD_RST_ACK; } phase = BUS_FREE_PHASE; break; case BUS_FREE_PHASE: goto out; default: printf("scsi%d: unexpected phase %d in icmd from %d\n", hs->sc_hc->hp_unit, phase, target); goto abort; } /* wait for last command to complete */ while ((ints = hd->scsi_ints) == 0) { if (--wait < 0) { HIST(cxin_wait, wait) goto abort; } DELAY(1); } HIST(cxin_wait, wait) hd->scsi_ints = ints; if (ints & INTS_SRV_REQ) phase = hd->scsi_psns & PHASE; else if (ints & INTS_DISCON) goto out; else if ((ints & INTS_CMD_DONE) == 0) { scsierror(hs, hd, ints); goto abort; } }abort: scsiabort(hs, hd, "icmd");out: return (hs->sc_stat[0]);}/* * Finish SCSI xfer command: After the completion interrupt from * a read/write operation, sequence through the final phases in * programmed i/o. This routine is a lot like scsiicmd except we * skip (and don't allow) the select, cmd out and data in/out phases. */static voidfinishxfer(hs, hd, target) struct scsi_softc *hs; volatile register struct scsidevice *hd; int target;{ u_char phase, ints; /* * We specified padding xfer so we ended with either a phase * change interrupt (normal case) or an error interrupt (handled * elsewhere). Reset the board dma logic then try to get the * completion status & command done msg. The reset confuses * the SPC REQ/ACK logic so we have to do any status/msg input * operations via 'manual xfer'. */ if (hd->scsi_ssts & SSTS_BUSY) { int wait = scsi_cmd_wait; /* wait for dma operation to finish */ while (hd->scsi_ssts & SSTS_BUSY) { if (--wait < 0) {#ifdef DEBUG if (scsi_debug) printf("finishxfer fail: ssts %x\n", hd->scsi_ssts);#endif HIST(fxfr_wait, wait) goto abort; } } HIST(fxfr_wait, wait) } hd->scsi_scmd |= SCMD_PROG_XFR; hd->scsi_sctl |= SCTL_CTRLRST; DELAY(1); hd->scsi_sctl &=~ SCTL_CTRLRST; hd->scsi_hconf = 0; /* * The following delay is definitely needed when trying to * write on a write protected disk (in the optical jukebox anyways), * but we shall see if other unexplained machine freezeups * also stop occuring... A value of 5 seems to work but * 10 seems safer considering the potential consequences. */ DELAY(10); hs->sc_stat[0] = 0xff; hs->sc_msg[0] = 0xff; hd->scsi_csr = 0; hd->scsi_ints = ints = hd->scsi_ints; while (1) { phase = hd->scsi_psns & PHASE; switch (phase) { case STATUS_PHASE: if (mxfer_in(hd, sizeof(hs->sc_stat), hs->sc_stat, phase) <= 0) goto abort; break; case MESG_IN_PHASE: if (mxfer_in(hd, sizeof(hs->sc_msg), hs->sc_msg, phase) < 0) goto abort; break; case BUS_FREE_PHASE: return; default: printf("scsi%d: unexpected phase %d in finishxfer from %d\n", hs->sc_hc->hp_unit, phase, target); goto abort; } if (ints = hd->scsi_ints) { hd->scsi_ints = ints; if (ints & INTS_DISCON) return; else if (ints & ~(INTS_SRV_REQ|INTS_CMD_DONE)) { scsierror(hs, hd, ints); break; } } if ((hd->scsi_ssts & SSTS_INITIATOR) == 0) return; }abort: scsiabort(hs, hd, "finishxfer"); hs->sc_stat[0] = 0xfe;}intscsi_test_unit_rdy(ctlr, slave, unit) int ctlr, slave, unit;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY }; cdb.lun = unit; return (scsiicmd(hs, slave, &cdb, sizeof(cdb), (u_char *)0, 0, STATUS_PHASE));}intscsi_request_sense(ctlr, slave, unit, buf, len) int ctlr, slave, unit; u_char *buf; unsigned len;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE }; cdb.lun = unit; cdb.len = len; return (scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len, DATA_IN_PHASE));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -