📄 cy.c
字号:
* This special check is because B_BUSY never * gets cleared in the non-waiting rewind case. */ if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) break; bp->b_flags |= B_WANTED; sleep((caddr_t)bp, PRIBIO); } bp->b_flags = B_BUSY|B_READ; splx(s); bp->b_dev = dev; bp->b_repcnt = count; bp->b_command = com; bp->b_blkno = 0; cystrategy(bp); /* * In case of rewind from close; don't wait. * This is the only case where count can be 0. */ if (count == 0) return; biowait(bp); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags &= B_ERROR;}cystrategy(bp) register struct buf *bp;{ int ycunit = YCUNIT(bp->b_dev); register struct vba_ctlr *vm; register struct buf *dp; int s; /* * Put transfer at end of unit queue. */ dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); dp = &ycutab[ycunit]; bp->av_forw = NULL; vm = ycdinfo[ycunit]->ui_mi; /* BEGIN GROT */ if (bp->b_flags & B_RAW) { if (bp->b_bcount >= CYMAXIO) { uprintf("cy%d: i/o size too large\n", vm->um_ctlr); bp->b_error = EINVAL; bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; biodone(bp); return; } } /* END GROT */ s = spl3(); if (dp->b_actf == NULL) { dp->b_actf = bp; /* * Transport not already active... * put at end of controller queue. */ dp->b_forw = NULL; if (vm->um_tab.b_actf == NULL) vm->um_tab.b_actf = dp; else vm->um_tab.b_actl->b_forw = dp; } else dp->b_actl->av_forw = bp; dp->b_actl = bp; /* * If the controller is not busy, get it going. */ if (vm->um_tab.b_active == 0) cystart(vm); splx(s);}/* * Start activity on a cy controller. */cystart(vm) register struct vba_ctlr *vm;{ register struct buf *bp, *dp; register struct yc_softc *yc; register struct cy_softc *cy; int ycunit; daddr_t blkno; dlog((LOG_INFO, "cystart()\n")); /* * Look for an idle transport on the controller. */loop: if ((dp = vm->um_tab.b_actf) == NULL) return; if ((bp = dp->b_actf) == NULL) { vm->um_tab.b_actf = dp->b_forw; goto loop; } ycunit = YCUNIT(bp->b_dev); yc = &yc_softc[ycunit]; cy = &cy_softc[CYUNIT(bp->b_dev)]; /* * Default is that last command was NOT a write command; * if we do a write command we will notice this in cyintr(). */ yc->yc_lastiow = 0; if (yc->yc_openf < 0 || (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { /* * Have had a hard error on a non-raw tape * or the tape unit is now unavailable (e.g. * taken off line). */ dlog((LOG_INFO, "openf %d command %x status %b\n", yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); bp->b_flags |= B_ERROR; goto next; } if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { /* * Execute control operation with the specified count. * * Set next state; give 5 minutes to complete * rewind or file mark search, or 10 seconds per * iteration (minimum 60 seconds and max 5 minutes) * to complete other ops. */ if (bp->b_command == CY_REW) { vm->um_tab.b_active = SREW; yc->yc_timo = 5*60; } else if (bp->b_command == CY_FSF || bp->b_command == CY_BSF) { vm->um_tab.b_active = SCOM; yc->yc_timo = 5*60; } else { vm->um_tab.b_active = SCOM; yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); } cy->cy_tpb.tprec = htoms(bp->b_repcnt); dlog((LOG_INFO, "bpcmd ")); goto dobpcmd; } /* * For raw I/O, save the current block * number in case we have to retry. */ if (bp->b_flags & B_RAW) { if (vm->um_tab.b_errcnt == 0) { yc->yc_blkno = bp->b_blkno; yc->yc_nxrec = yc->yc_blkno + 1; } } else { /* * Handle boundary cases for operation * on non-raw tapes. */ if (bp->b_blkno > yc->yc_nxrec) { /* * Can't read past known end-of-file. */ bp->b_flags |= B_ERROR; bp->b_error = ENXIO; goto next; } if (bp->b_blkno == yc->yc_nxrec && bp->b_flags&B_READ) { /* * Reading at end of file returns 0 bytes. */ bp->b_resid = bp->b_bcount; clrbuf(bp); goto next; } if ((bp->b_flags&B_READ) == 0) /* * Writing sets EOF. */ yc->yc_nxrec = bp->b_blkno + 1; } if ((blkno = yc->yc_blkno) == bp->b_blkno) { caddr_t addr; int cmd; /* * Choose the appropriate i/o command based on the * transfer size, the estimated block size, * and the controller's internal buffer size. * If the request length is longer than the tape * block length, a buffered read will fail, * thus, we request at most the size that we expect. * We then check for larger records when the read completes. * If we're retrying a read on a raw device because * the original try was a buffer request which failed * due to a record length error, then we force the use * of the raw controller read (YECH!!!!). */ if (bp->b_flags&B_READ) { if (yc->yc_blksize <= cy->cy_bs && vm->um_tab.b_errcnt == 0) cmd = CY_BRCOM; else cmd = CY_RCOM; } else { /* * On write error retries erase the * inter-record gap before rewriting. */ if (vm->um_tab.b_errcnt && vm->um_tab.b_active != SERASED) { vm->um_tab.b_active = SERASE; bp->b_command = CY_ERASE; yc->yc_timo = 60; goto dobpcmd; } cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; } vm->um_tab.b_active = SIO; addr = (caddr_t)vbasetup(bp, &cy->cy_rbuf, 1); cy->cy_tpb.tpcmd = cmd; cy->cy_tpb.tpcontrol = yc->yc_dens; if (cmd == CY_RCOM || cmd == CY_WCOM) cy->cy_tpb.tpcontrol |= CYCW_LOCK; cy->cy_tpb.tpstatus = 0; cy->cy_tpb.tpcount = 0; cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); cy->cy_tpb.tprec = 0; if (cmd == CY_BRCOM) cy->cy_tpb.tpsize = htoms(imin(yc->yc_blksize, (int)bp->b_bcount)); else cy->cy_tpb.tpsize = htoms(bp->b_bcount); cyldmba(cy->cy_tpb.tplink, (caddr_t)0); do uncache(&cy->cy_ccb.cbgate); while (cy->cy_ccb.cbgate == GATE_CLOSED); cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); cy->cy_ccb.cbcw = CBCW_IE; cy->cy_ccb.cbgate = GATE_CLOSED; dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, htoms(cy->cy_tpb.tpsize))); CY_GO(vm->um_addr); return; } /* * Tape positioned incorrectly; set to seek forwards * or backwards to the correct spot. This happens * for raw tapes only on error retries. */ vm->um_tab.b_active = SSEEK; if (blkno < bp->b_blkno) { bp->b_command = CY_SFORW; cy->cy_tpb.tprec = htoms(bp->b_blkno - blkno); } else { bp->b_command = CY_SREV; cy->cy_tpb.tprec = htoms(blkno - bp->b_blkno); } yc->yc_timo = imin(imax((int)(10 * htoms(cy->cy_tpb.tprec)), 60), 5*60);dobpcmd: /* * Do the command in bp. Reverse direction commands * are indicated by having CYCW_REV or'd into their * value. For these we must set the appropriate bit * in the control field. */ if (bp->b_command&CYCW_REV) { cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV;dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); } else { cy->cy_tpb.tpcmd = bp->b_command; cy->cy_tpb.tpcontrol = yc->yc_dens;dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); } cy->cy_tpb.tpstatus = 0; cy->cy_tpb.tpcount = 0; cyldmba(cy->cy_tpb.tplink, (caddr_t)0); do uncache(&cy->cy_ccb.cbgate); while (cy->cy_ccb.cbgate == GATE_CLOSED); cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); cy->cy_ccb.cbcw = CBCW_IE; cy->cy_ccb.cbgate = GATE_CLOSED; dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, htoms(cy->cy_tpb.tprec))); CY_GO(vm->um_addr); return;next: /* * Done with this operation due to error or the * fact that it doesn't do anything. * Dequeue the transfer and continue * processing this slave. */ vm->um_tab.b_errcnt = 0; dp->b_actf = bp->av_forw; biodone(bp); goto loop;}/* * Cy interrupt routine. */cyintr(cyunit) int cyunit;{ struct buf *dp; register struct buf *bp; register struct vba_ctlr *vm = cyminfo[cyunit]; register struct cy_softc *cy; register struct yc_softc *yc; int err; register state; dlog((LOG_INFO, "cyintr(%d)\n", cyunit)); /* * First, turn off the interrupt from the controller * (device uses Multibus non-vectored interrupts...yech). */ cy = &cy_softc[vm->um_ctlr]; cy->cy_ccb.cbcw = CBCW_CLRINT; cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); cy->cy_ccb.cbgate = GATE_CLOSED; CY_GO(vm->um_addr); if ((dp = vm->um_tab.b_actf) == NULL) { dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); return; } bp = dp->b_actf; cy = &cy_softc[cyunit]; cyuncachetpb(cy); yc = &yc_softc[YCUNIT(bp->b_dev)]; /* * If last command was a rewind and tape is * still moving, wait for the operation to complete. */ if (vm->um_tab.b_active == SREW) { vm->um_tab.b_active = SCOM; if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { yc->yc_timo = 5*60; /* 5 minutes */ return; } } /* * An operation completed...record status. */ yc->yc_timo = INF; yc->yc_control = cy->cy_tpb.tpcontrol; yc->yc_status = cy->cy_tpb.tpstatus; yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, yc->yc_status, CYS_BITS, yc->yc_resid)); if ((bp->b_flags&B_READ) == 0) yc->yc_lastiow = 1; state = vm->um_tab.b_active; vm->um_tab.b_active = 0; /* * Check for errors. */ if (cy->cy_tpb.tpstatus&CYS_ERR) { err = cy->cy_tpb.tpstatus&CYS_ERR; dlog((LOG_INFO, "error %d\n", err)); /* * If we hit the end of tape file, update our position. */ if (err == CYER_FM) { yc->yc_status |= CYS_FM; state = SCOM; /* force completion */ cyseteof(bp); /* set blkno and nxrec */ goto opdone; } /* * Fix up errors which occur due to backspacing over * the beginning of the tape. */ if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { yc->yc_status |= CYS_BOT; goto ignoreerr; } /* * If we were reading raw tape and the only error was that the * record was too long, then we don't consider this an error. */ if ((bp->b_flags & (B_READ|B_RAW)) == (B_READ|B_RAW) && err == CYER_STROBE) { /* * Retry reads with the command changed to * a raw read if necessary. Setting b_errcnt * here causes cystart (above) to force a CY_RCOM. */ if (cy->cy_tpb.tpcmd == CY_BRCOM && vm->um_tab.b_errcnt++ == 0) { yc->yc_blkno++; goto opcont; } else goto ignoreerr; } /* * If error is not hard, and this was an i/o operation * retry up to 8 times. */ if (state == SIO && (CYMASK(err) & ((bp->b_flags&B_READ) ? CYER_RSOFT : CYER_WSOFT))) { if (++vm->um_tab.b_errcnt < 7) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -