📄 scsi.c
字号:
intscsi_immed_command(ctlr, slave, unit, cdb, buf, len, rd) int ctlr, slave, unit, rd; struct scsi_fmt_cdb *cdb; u_char *buf; u_int len;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; cdb->cdb[1] |= unit << 5; return (scsiicmd(hs, slave, cdb->cdb, cdb->len, buf, len, rd != 0? DATA_IN_PHASE : DATA_OUT_PHASE));}/* * The following routines are test-and-transfer i/o versions of read/write * for things like reading disk labels and writing core dumps. The * routine scsigo should be used for normal data transfers, NOT these * routines. */intscsi_tt_read(ctlr, slave, unit, buf, len, blk, bshift) int ctlr, slave, unit; u_char *buf; u_int len; daddr_t blk; int bshift;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; struct scsi_cdb10 cdb; int stat; int old_wait = scsi_data_wait; scsi_data_wait = 300000; bzero(&cdb, sizeof(cdb)); cdb.cmd = CMD_READ_EXT; cdb.lun = unit; blk >>= bshift; cdb.lbah = blk >> 24; cdb.lbahm = blk >> 16; cdb.lbalm = blk >> 8; cdb.lbal = blk; cdb.lenh = len >> (8 + DEV_BSHIFT + bshift); cdb.lenl = len >> (DEV_BSHIFT + bshift); stat = scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len, DATA_IN_PHASE); scsi_data_wait = old_wait; return (stat);}intscsi_tt_write(ctlr, slave, unit, buf, len, blk, bshift) int ctlr, slave, unit; u_char *buf; u_int len; daddr_t blk; int bshift;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; struct scsi_cdb10 cdb; int stat; int old_wait = scsi_data_wait; scsi_data_wait = 300000; bzero(&cdb, sizeof(cdb)); cdb.cmd = CMD_WRITE_EXT; cdb.lun = unit; blk >>= bshift; cdb.lbah = blk >> 24; cdb.lbahm = blk >> 16; cdb.lbalm = blk >> 8; cdb.lbal = blk; cdb.lenh = len >> (8 + DEV_BSHIFT + bshift); cdb.lenl = len >> (DEV_BSHIFT + bshift); stat = scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len, DATA_OUT_PHASE); scsi_data_wait = old_wait; return (stat);}intscsireq(dq) register struct devqueue *dq;{ register struct devqueue *hq; hq = &scsi_softc[dq->dq_ctlr].sc_sq; insque(dq, hq->dq_back); if (dq->dq_back == hq) return(1); return(0);}intscsiustart(unit) int unit;{ register struct scsi_softc *hs = &scsi_softc[unit]; hs->sc_dq.dq_ctlr = DMA0 | DMA1; hs->sc_flags |= SCSI_HAVEDMA; if (dmareq(&hs->sc_dq)) return(1); return(0);}voidscsistart(unit) int unit;{ register struct devqueue *dq; dq = scsi_softc[unit].sc_sq.dq_forw; (dq->dq_driver->d_go)(dq->dq_unit);}intscsigo(ctlr, slave, unit, bp, cdb, pad) int ctlr, slave, unit; struct buf *bp; struct scsi_fmt_cdb *cdb; int pad;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; volatile register struct scsidevice *hd = (struct scsidevice *)hs->sc_hc->hp_addr; int i, dmaflags; u_char phase, ints, cmd; cdb->cdb[1] |= unit << 5; /* select the SCSI bus (it's an error if bus isn't free) */ if (issue_select(hd, slave, hs->sc_scsi_addr) || wait_for_select(hd)) { if (hs->sc_flags & SCSI_HAVEDMA) { hs->sc_flags &=~ SCSI_HAVEDMA; dmafree(&hs->sc_dq); } return (1); } /* * Wait for a phase change (or error) then let the device * sequence us through command phase (we may have to take * a msg in/out before doing the command). If the disk has * to do a seek, it may be a long time until we get a change * to data phase so, in the absense of an explicit phase * change, we assume data phase will be coming up and tell * the SPC to start a transfer whenever it does. We'll get * a service required interrupt later if this assumption is * wrong. Otherwise we'll get a service required int when * the transfer changes to status phase. */ phase = CMD_PHASE; while (1) { register int wait = scsi_cmd_wait; switch (phase) { case CMD_PHASE: if (ixfer_start(hd, cdb->len, phase, wait)) if (ixfer_out(hd, cdb->len, cdb->cdb)) goto abort; 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 DATA_IN_PHASE: case DATA_OUT_PHASE: goto out; default: printf("scsi%d: unexpected phase %d in go from %d\n", hs->sc_hc->hp_unit, phase, slave); goto abort; } while ((ints = hd->scsi_ints) == 0) { if (--wait < 0) { HIST(sgo_wait, wait) goto abort; } DELAY(1); } HIST(sgo_wait, wait) hd->scsi_ints = ints; if (ints & INTS_SRV_REQ) phase = hd->scsi_psns & PHASE; else if (ints & INTS_CMD_DONE) goto out; else { scsierror(hs, hd, ints); goto abort; } }out: /* * Reset the card dma logic, setup the dma channel then * get the dio part of the card set for a dma xfer. */ hd->scsi_hconf = 0; cmd = CSR_IE; dmaflags = DMAGO_NOINT; if (scsi_pridma) dmaflags |= DMAGO_PRI; if (bp->b_flags & B_READ) dmaflags |= DMAGO_READ; if ((hs->sc_flags & SCSI_DMA32) && ((int)bp->b_un.b_addr & 3) == 0 && (bp->b_bcount & 3) == 0) { cmd |= CSR_DMA32; dmaflags |= DMAGO_LWORD; } else dmaflags |= DMAGO_WORD; dmago(hs->sc_dq.dq_ctlr, bp->b_un.b_addr, bp->b_bcount, dmaflags); if (bp->b_flags & B_READ) { cmd |= CSR_DMAIN; phase = DATA_IN_PHASE; } else phase = DATA_OUT_PHASE; /* * DMA enable bits must be set after size and direction bits. */ hd->scsi_csr = cmd; hd->scsi_csr |= (CSR_DE0 << hs->sc_dq.dq_ctlr); /* * Setup the SPC for the transfer. We don't want to take * first a command complete then a service required interrupt * at the end of the transfer so we try to disable the cmd * complete by setting the transfer counter to more bytes * than we expect. (XXX - This strategy may have to be * modified to deal with devices that return variable length * blocks, e.g., some tape drives.) */ cmd = SCMD_XFR; i = (unsigned)bp->b_bcount; if (pad) { cmd |= SCMD_PAD; /* * XXX - If we don't do this, the last 2 or 4 bytes * (depending on word/lword DMA) of a read get trashed. * It looks like it is necessary for the DMA to complete * before the SPC goes into "pad mode"??? Note: if we * also do this on a write, the request never completes. */ if (bp->b_flags & B_READ) i += 2;#ifdef DEBUG hs->sc_flags |= SCSI_PAD; if (i & 1) printf("scsi%d: odd byte count: %d bytes @ %d\n", ctlr, i, bp->b_cylin);#endif } else i += 4; hd->scsi_tch = i >> 16; hd->scsi_tcm = i >> 8; hd->scsi_tcl = i; hd->scsi_pctl = phase; hd->scsi_tmod = 0; hd->scsi_scmd = cmd; hs->sc_flags |= SCSI_IO; return (0);abort: scsiabort(hs, hd, "go"); hs->sc_flags &=~ SCSI_HAVEDMA; dmafree(&hs->sc_dq); return (1);}voidscsidone(unit) register int unit;{ volatile register struct scsidevice *hd = (struct scsidevice *)scsi_softc[unit].sc_hc->hp_addr;#ifdef DEBUG if (scsi_debug) printf("scsi%d: done called!\n", unit);#endif /* dma operation is done -- turn off card dma */ hd->scsi_csr &=~ (CSR_DE1|CSR_DE0);}intscsiintr(unit) register int unit;{ register struct scsi_softc *hs = &scsi_softc[unit]; volatile register struct scsidevice *hd = (struct scsidevice *)hs->sc_hc->hp_addr; register u_char ints; register struct devqueue *dq; if ((hd->scsi_csr & (CSR_IE|CSR_IR)) != (CSR_IE|CSR_IR)) return (0); ints = hd->scsi_ints; if ((ints & INTS_SRV_REQ) && (hs->sc_flags & SCSI_IO)) { /* * this should be the normal i/o completion case. * get the status & cmd complete msg then let the * device driver look at what happened. */#ifdef DEBUG int len = (hd->scsi_tch << 16) | (hd->scsi_tcm << 8) | hd->scsi_tcl; if (!(hs->sc_flags & SCSI_PAD)) len -= 4; hs->sc_flags &=~ SCSI_PAD;#endif dq = hs->sc_sq.dq_forw; finishxfer(hs, hd, dq->dq_slave); hs->sc_flags &=~ (SCSI_IO|SCSI_HAVEDMA); dmafree(&hs->sc_dq); (dq->dq_driver->d_intr)(dq->dq_unit, hs->sc_stat[0]); } else { /* Something unexpected happened -- deal with it. */ hd->scsi_ints = ints; hd->scsi_csr = 0; scsierror(hs, hd, ints); scsiabort(hs, hd, "intr"); if (hs->sc_flags & SCSI_IO) { hs->sc_flags &=~ (SCSI_IO|SCSI_HAVEDMA); dmafree(&hs->sc_dq); dq = hs->sc_sq.dq_forw; (dq->dq_driver->d_intr)(dq->dq_unit, -1); } } return(1);}voidscsifree(dq) register struct devqueue *dq;{ register struct devqueue *hq; hq = &scsi_softc[dq->dq_ctlr].sc_sq; remque(dq); if ((dq = hq->dq_forw) != hq) (dq->dq_driver->d_start)(dq->dq_unit);}/* * (XXX) The following routine is needed for the SCSI tape driver * to read odd-size records. */#include "st.h"#if NST > 0intscsi_tt_oddio(ctlr, slave, unit, buf, len, b_flags, freedma) int ctlr, slave, unit, b_flags, freedma; u_char *buf; u_int len;{ register struct scsi_softc *hs = &scsi_softc[ctlr]; struct scsi_cdb6 cdb; u_char iphase; int stat;#ifdef DEBUG if (freedma && (hs->sc_flags & SCSI_HAVEDMA) == 0 || !freedma && (hs->sc_flags & SCSI_HAVEDMA)) printf("oddio: freedma (%d) inconsistency (flags=%x)\n", freedma, hs->sc_flags);#endif /* * First free any DMA channel that was allocated. * We can't use DMA to do this transfer. */ if (freedma) { hs->sc_flags &=~ SCSI_HAVEDMA; dmafree(hs->sc_dq); } /* * Initialize command block */ bzero(&cdb, sizeof(cdb)); cdb.lun = unit; cdb.lbam = (len >> 16) & 0xff; cdb.lbal = (len >> 8) & 0xff; cdb.len = len & 0xff; if (buf == 0) { cdb.cmd = CMD_SPACE; cdb.lun |= 0x00; len = 0; iphase = MESG_IN_PHASE; } else if (b_flags & B_READ) { cdb.cmd = CMD_READ; iphase = DATA_IN_PHASE; } else { cdb.cmd = CMD_WRITE; iphase = DATA_OUT_PHASE; } /* * Perform command (with very long delays) */ scsi_delay(30000000); stat = scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len, iphase); scsi_delay(0); return (stat);}#endif#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -