X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/d763a2b70244c587a2e4252df255cbfbfbcc3074..6a476a42bd6303b75aac197c0c148da13c182913:/usr/src/sys/vax/uba/tm.c diff --git a/usr/src/sys/vax/uba/tm.c b/usr/src/sys/vax/uba/tm.c index 9f0b3b1126..0474635af1 100644 --- a/usr/src/sys/vax/uba/tm.c +++ b/usr/src/sys/vax/uba/tm.c @@ -1,12 +1,20 @@ -/* tm.c 4.11 %G% */ +/* tm.c 4.36 81/04/15 */ -#include "tm.h" +#include "te.h" +#include "ts.h" #if NTM > 0 /* - * TM tape driver + * TM11/TE10 tape driver + * + * TODO: + * test driver with more than one slave + * test driver with more than one controller + * test reset code + * what happens if you offline tape during rewind? + * test using file system on tape */ -#define DELAY(N) { register int d; d = N; while (--d > 0); } #include "../h/param.h" +#include "../h/systm.h" #include "../h/buf.h" #include "../h/dir.h" #include "../h/conf.h" @@ -14,178 +22,279 @@ #include "../h/file.h" #include "../h/map.h" #include "../h/pte.h" -#include "../h/uba.h" +#include "../h/vm.h" +#include "../h/ubareg.h" +#include "../h/ubavar.h" #include "../h/mtio.h" #include "../h/ioctl.h" -#include "../h/vm.h" #include "../h/cmap.h" #include "../h/cpu.h" #include "../h/tmreg.h" -struct buf ctmbuf; -struct buf rtmbuf; +/* + * There is a ctmbuf per tape controller. + * It is used as the token to pass to the internal routines + * to execute tape ioctls, and also acts as a lock on the slaves + * on the controller, since there is only one per controller. + * In particular, when the tape is rewinding on close we release + * the user process but any further attempts to use the tape drive + * before the rewind completes will hang waiting for ctmbuf. + */ +struct buf ctmbuf[NTM]; -int tmcntrlr(), tmslave(), tmdgo(), tmintr(); -struct uba_dinfo *tmdinfo[NTM]; -struct uba_minfo *tmminfo[NTM]; +/* + * Raw tape operations use rtmbuf. The driver + * notices when rtmbuf is being used and allows the user + * program to continue after errors and read records + * not of the standard length (BSIZE). + */ +struct buf rtmbuf[NTM]; + +/* + * Driver unibus interface routines and variables. + */ +int tmprobe(), tmslave(), tmattach(), tmdgo(), tmintr(); +struct uba_ctlr *tmminfo[NTM]; +struct uba_device *tedinfo[NTE]; +struct buf teutab[NTE]; +short tetotm[NTE]; u_short tmstd[] = { 0772520, 0 }; struct uba_driver tmdriver = - { tmcntrlr, tmslave, tmdgo, 0, tmstd, "tm", tmdinfo, tmminfo }; -int tm_ubinfo; + { tmprobe, tmslave, tmattach, tmdgo, tmstd, "te", tedinfo, "tm", tmminfo, 0 }; /* bits in minor device */ +#define TEUNIT(dev) (minor(dev)&03) +#define TMUNIT(dev) (tetotm[TEUNIT(dev)]) #define T_NOREWIND 04 #define T_1600BPI 08 #define INF (daddr_t)1000000L /* - * Really only handle one tape drive... if you have more than one, - * you can put all these (and some of the above) in a structure, - * change the obvious things, and make tmslave smarter, but - * it is not clear what happens when some drives are transferring while - * others rewind, so we don't pretend that this driver handles multiple - * tape drives. + * Software state per tape transport. + * + * 1. A tape drive is a unique-open device; we refuse opens when it is already. + * 2. We keep track of the current position on a block tape and seek + * before operations by forward/back spacing if necessary. + * 3. We remember if the last operation was a write on a tape, so if a tape + * is open read write and the last thing done is a write we can + * write a standard end of tape mark (two eofs). + * 4. We remember the status registers after the last command, using + * then internally and returning them to the SENSE ioctl. + * 5. We remember the last density the tape was used at. If it is + * not a BOT when we start using it and we are writing, we don't + * let the density be changed. */ -char t_openf; -daddr_t t_blkno; -char t_flags; -daddr_t t_nxrec; -u_short t_erreg; -u_short t_dsreg; -short t_resid; +struct te_softc { + char sc_openf; /* lock against multiple opens */ + char sc_lastiow; /* last op was a write */ + daddr_t sc_blkno; /* block number, for block device tape */ + daddr_t sc_nxrec; /* position of end of tape, if known */ + u_short sc_erreg; /* copy of last erreg */ + u_short sc_dsreg; /* copy of last dsreg */ + short sc_resid; /* copy of last bc */ +#ifdef unneeded + short sc_lastcmd; /* last command to handle direction changes */ +#endif + u_short sc_dens; /* prototype command with density info */ + daddr_t sc_timo; /* time until timeout expires */ + short sc_tact; /* timeout is active */ +} te_softc[NTM]; +#ifdef unneeded +int tmgapsdcnt; /* DEBUG */ +#endif +/* + * States for um->um_tab.b_active, the per controller state flag. + * This is used to sequence control in the driver. + */ #define SSEEK 1 /* seeking */ #define SIO 2 /* doing seq i/o */ #define SCOM 3 /* sending control command */ +#define SREW 4 /* sending a drive rewind */ -#define LASTIOW 1 /* last op was a write */ -#define WAITREW 2 /* someone is waiting for a rewind */ - +#if NTS > 0 +/* + * Kludge to get around fact that we don't really + * check if a ts is there... if there are both tm's and ts's + * declared in the system, then this driver sets havetm to 1 + * if it finds a tm, and ts just pretends there isn't a ts. + */ +int havetm = 0; +#endif /* * Determine if there is a controller for * a tm at address reg. Our goal is to make the * device interrupt. */ -tmcntrlr(um, reg) - struct uba_minfo *um; +tmprobe(reg) caddr_t reg; { - register int br, cvec; + register int br, cvec; /* must be r11,r10; value-result */ - ((struct device *)reg)->tmcs = IENABLE; +#ifdef lint + br = 0; cvec = br; br = cvec; +#endif + ((struct device *)reg)->tmcs = TM_IE; /* - * If this is a tm03/tc11, it ought to have interrupted + * If this is a tm11, it ought to have interrupted * by now, if it isn't (ie: it is a ts04) then we just * hope that it didn't interrupt, so autoconf will ignore it. * Just in case, we will reference one * of the more distant registers, and hope for a machine * check, or similar disaster if this is a ts. + * + * Note: on an 11/780, badaddr will just generate + * a uba error for a ts; but our caller will notice that + * so we won't check for it. */ - if (badaddr(&((struct device *)reg)->tmrd, 2)) + if (badaddr((caddr_t)&((struct device *)reg)->tmrd, 2)) return (0); return (1); } -tmslave(ui, reg, slaveno) - struct uba_dinfo *ui; +/* + * Due to a design flaw, we cannot ascertain if the tape + * exists or not unless it is on line - ie: unless a tape is + * mounted. This is too servere a restriction to bear, + * so all units are assumed to exist. + */ +/*ARGSUSED*/ +tmslave(ui, reg) + struct uba_device *ui; caddr_t reg; { + return (1); +} + +/* + * Record attachment of the unit to the controller. + */ +/*ARGSUSED*/ +tmattach(ui) + struct uba_device *ui; +{ + +#if NTS > 0 + havetm = 1; +#endif /* - * Due to a design flaw, we cannot ascertain if the tape - * exists or not unless it is on line - ie: unless a tape is - * mounted. This is too servere a restriction to bear. - * As we can only handle one tape, we might just as well insist - * that it be slave #0, and just assume that it exists. - * Something better will have to be done if you have two - * tapes on one controller, or two controllers + * Tetotm is used in TMUNIT to index the ctmbuf and rtmbuf + * arrays given a te unit number. */ - if (slaveno != 0 || tmdinfo[0]) - return(0); - return (1); + tetotm[ui->ui_unit] = ui->ui_mi->um_ctlr; } +int tmtimer(); +/* + * Open the device. Tapes are unique open + * devices, so we refuse if it is already open. + * We also check that a tape is available, and + * don't block waiting here; if you want to wait + * for a tape you should timeout in user code. + */ tmopen(dev, flag) dev_t dev; int flag; { - register ds, unit; - register struct uba_dinfo *ui; - - tmminfo[0]->um_tab.b_flags |= B_TAPE; - unit = minor(dev)&03; - if (unit>=NTM || t_openf || !(ui = tmdinfo[minor(dev)&03])->ui_alive) { - u.u_error = ENXIO; /* out of range or open */ - return; - } - tcommand(dev, NOP, 1); - if ((t_erreg&SELR) == 0) { - u.u_error = EIO; /* offline */ + register int teunit; + register struct uba_device *ui; + register struct te_softc *sc; + int olddens, dens; + + teunit = TEUNIT(dev); + if (teunit>=NTE || (sc = &te_softc[teunit])->sc_openf || + (ui = tedinfo[teunit]) == 0 || ui->ui_alive == 0) { + u.u_error = ENXIO; return; } - t_openf = 1; - if (t_erreg&RWS) - tmwaitrws(dev); /* wait for rewind complete */ - while (t_erreg&SDWN) - tcommand(dev, NOP, 1); /* await settle down */ - if ((t_erreg&TUR)==0 || - ((flag&(FREAD|FWRITE)) == FWRITE && (t_erreg&WRL))) { - ((struct device *)ui->ui_addr)->tmcs = DCLR|GO; - u.u_error = EIO; /* offline or write protect */ + olddens = sc->sc_dens; + dens = TM_IE | TM_GO | (ui->ui_slave << 8); + if ((minor(dev) & T_1600BPI) == 0) + dens |= TM_D800; + sc->sc_dens = dens; +get: + tmcommand(dev, TM_SENSE, 1); + if (sc->sc_erreg&TMER_SDWN) { + sleep((caddr_t)&lbolt, PZERO+1); + goto get; } - if (u.u_error != 0) { - t_openf = 0; + sc->sc_dens = olddens; + if ((sc->sc_erreg&(TMER_SELR|TMER_TUR)) != (TMER_SELR|TMER_TUR) || + (flag&FWRITE) && (sc->sc_erreg&TMER_WRL) || + (sc->sc_erreg&TMER_BOT) == 0 && (flag&FWRITE) && + dens != sc->sc_dens) { + /* + * Not online or density switch in mid-tape or write locked. + */ + u.u_error = EIO; return; } - t_blkno = (daddr_t)0; - t_nxrec = INF; - t_flags = 0; - t_openf = 1; -} - -tmwaitrws(dev) - register dev; -{ - register struct device *addr = - (struct device *)tmdinfo[minor(dev)&03]->ui_addr; - - spl5(); - for (;;) { - if ((addr->tmer&RWS) == 0) { - spl0(); /* rewind complete */ - return; - } - t_flags |= WAITREW; - sleep((caddr_t)&t_flags, PRIBIO); + sc->sc_openf = 1; + sc->sc_blkno = (daddr_t)0; + sc->sc_nxrec = INF; + sc->sc_lastiow = 0; + sc->sc_dens = dens; + (void) spl6(); + if (sc->sc_tact == 0) { + sc->sc_timo = INF; + sc->sc_tact = 1; + timeout(tmtimer, dev, 5*hz); } + (void) spl0(); } +/* + * Close tape device. + * + * If tape was open for writing or last operation was + * a write, then write two EOF's and backspace over the last one. + * Unless this is a non-rewinding special file, rewind the tape. + * Make the tape available to others. + */ tmclose(dev, flag) register dev_t dev; register flag; { + register struct te_softc *sc = &te_softc[TEUNIT(dev)]; - if (flag == FWRITE || ((flag&FWRITE) && (t_flags&LASTIOW))) { - tcommand(dev, WEOF, 1); - tcommand(dev, WEOF, 1); - tcommand(dev, SREV, 1); + if (flag == FWRITE || (flag&FWRITE) && sc->sc_lastiow) { + tmcommand(dev, TM_WEOF, 1); + tmcommand(dev, TM_WEOF, 1); + tmcommand(dev, TM_SREV, 1); } if ((minor(dev)&T_NOREWIND) == 0) - tcommand(dev, REW, 1); - t_openf = 0; + /* + * 0 count means don't hang waiting for rewind complete + * rather ctmbuf stays busy until the operation completes + * preventing further opens from completing by + * preventing a TM_SENSE from completing. + */ + tmcommand(dev, TM_REW, 0); + sc->sc_openf = 0; } -tcommand(dev, com, count) +/* + * Execute a command on the tape drive + * a specified number of times. + */ +tmcommand(dev, com, count) dev_t dev; int com, count; { register struct buf *bp; - bp = &ctmbuf; + bp = &ctmbuf[TMUNIT(dev)]; (void) spl5(); while (bp->b_flags&B_BUSY) { + /* + * 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); } @@ -196,257 +305,523 @@ tcommand(dev, com, count) bp->b_command = com; bp->b_blkno = 0; tmstrategy(bp); + /* + * In case of rewind from close, don't wait. + * This is the only case where count can be 0. + */ + if (count == 0) + return; iowait(bp); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags &= B_ERROR; } +/* + * Queue a tape operation. + */ tmstrategy(bp) register struct buf *bp; { - register daddr_t *p; - register struct buf *tmi; - - tmwaitrws(bp->b_dev); - if (bp != &ctmbuf) { - p = &t_nxrec; - if (dbtofsb(bp->b_blkno) > *p) { - bp->b_flags |= B_ERROR; - bp->b_error = ENXIO; /* past EOF */ - iodone(bp); - return; - } else if (dbtofsb(bp->b_blkno) == *p && bp->b_flags&B_READ) { - bp->b_resid = bp->b_bcount; - clrbuf(bp); /* at EOF */ - iodone(bp); - return; - } else if ((bp->b_flags&B_READ) == 0) - *p = dbtofsb(bp->b_blkno) + 1; /* write sets EOF */ - } + int teunit = TEUNIT(bp->b_dev); + register struct uba_ctlr *um; + register struct buf *dp; + + /* + * Put transfer at end of unit queue + */ + dp = &teutab[teunit]; bp->av_forw = NULL; (void) spl5(); - tmi = &tmminfo[0]->um_tab; - if (tmi->b_actf == NULL) - tmi->b_actf = bp; - else - tmi->b_actl->av_forw = bp; - tmi->b_actl = bp; - if (tmi->b_active == 0) - tmstart(); + if (dp->b_actf == NULL) { + dp->b_actf = bp; + /* + * Transport not already active... + * put at end of controller queue. + */ + dp->b_forw = NULL; + um = tedinfo[teunit]->ui_mi; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + } else + dp->b_actl->av_forw = bp; + dp->b_actl = bp; + /* + * If the controller is not busy, get + * it going. + */ + if (um->um_tab.b_active == 0) + tmstart(um); (void) spl0(); } -tmstart() +/* + * Start activity on a tm controller. + */ +tmstart(um) + register struct uba_ctlr *um; { - register struct buf *bp; - register struct uba_dinfo *ui; - register struct device *addr; - register cmd; - register daddr_t blkno; - int s; + register struct buf *bp, *dp; + register struct device *addr = (struct device *)um->um_addr; + register struct te_softc *sc; + register struct uba_device *ui; + int teunit, cmd; + daddr_t blkno; + /* + * Look for an idle transport on the controller. + */ loop: - if ((bp = tmminfo[0]->um_tab.b_actf) == 0) + if ((dp = um->um_tab.b_actf) == NULL) return; - ui = tmdinfo[minor(bp->b_dev)&03]; - addr = (struct device *)ui->ui_addr; - t_dsreg = addr->tmcs; - t_erreg = addr->tmer; - t_resid = addr->tmbc; - t_flags &= ~LASTIOW; - if (t_openf < 0 || (addr->tmcs&CUR) == 0) { - /* t_openf = -1; ??? */ - bp->b_flags |= B_ERROR; /* hard error'ed or !SELR */ + if ((bp = dp->b_actf) == NULL) { + um->um_tab.b_actf = dp->b_forw; + goto loop; + } + teunit = TEUNIT(bp->b_dev); + ui = tedinfo[teunit]; + /* + * Record pre-transfer status (e.g. for TM_SENSE) + */ + sc = &te_softc[teunit]; + addr = (struct device *)um->um_addr; + addr->tmcs = (ui->ui_slave << 8); + sc->sc_dsreg = addr->tmcs; + sc->sc_erreg = addr->tmer; + sc->sc_resid = addr->tmbc; + /* + * Default is that last command was NOT a write command; + * if we do a write command we will notice this in tmintr(). + */ + sc->sc_lastiow = 0; + if (sc->sc_openf < 0 || (addr->tmcs&TM_CUR) == 0) { + /* + * Have had a hard error on a non-raw tape + * or the tape unit is now unavailable + * (e.g. taken off line). + */ + bp->b_flags |= B_ERROR; goto next; } - cmd = IENABLE | GO; - if ((minor(bp->b_dev) & T_1600BPI) == 0) - cmd |= D800; - if (bp == &ctmbuf) { - if (bp->b_command == NOP) - goto next; /* just get status */ - else { - cmd |= bp->b_command; - tmminfo[0]->um_tab.b_active = SCOM; - if (bp->b_command == SFORW || bp->b_command == SREV) - addr->tmbc = bp->b_repcnt; - addr->tmcs = cmd; - return; + if (bp == &ctmbuf[TMUNIT(bp->b_dev)]) { + /* + * Execute control operation with the specified count. + */ + if (bp->b_command == TM_SENSE) + goto next; + /* + * Set next state; give 5 minutes to complete + * rewind, or 10 seconds per iteration (minimum 60 + * seconds and max 5 minute) to complete other ops. + */ + if (bp->b_command == TM_REW) { + um->um_tab.b_active = SREW; + sc->sc_timo = 5 * 60; + } else { + um->um_tab.b_active = SCOM; + sc->sc_timo = min(max(10 * bp->b_repcnt, 60), 5 * 60); } + if (bp->b_command == TM_SFORW || bp->b_command == TM_SREV) + addr->tmbc = bp->b_repcnt; + goto dobpcmd; + } + /* + * The following checks handle boundary cases for operation + * on non-raw tapes. On raw tapes the initialization of + * sc->sc_nxrec by tmphys causes them to be skipped normally + * (except in the case of retries). + */ + if (dbtofsb(bp->b_blkno) > sc->sc_nxrec) { + /* + * Can't read past known end-of-file. + */ + bp->b_flags |= B_ERROR; + bp->b_error = ENXIO; + goto next; } - if ((blkno = t_blkno) == dbtofsb(bp->b_blkno)) { + if (dbtofsb(bp->b_blkno) == sc->sc_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 + */ + sc->sc_nxrec = dbtofsb(bp->b_blkno) + 1; + /* + * If the data transfer command is in the correct place, + * set up all the registers except the csr, and give + * control over to the UNIBUS adapter routines, to + * wait for resources to start the i/o. + */ + if ((blkno = sc->sc_blkno) == dbtofsb(bp->b_blkno)) { addr->tmbc = -bp->b_bcount; - s = spl6(); - if (tm_ubinfo == 0) - tm_ubinfo = ubasetup(ui->ui_ubanum, bp, 1); - splx(s); if ((bp->b_flags&B_READ) == 0) { - if (tmminfo[0]->um_tab.b_errcnt) - cmd |= WIRG; + if (um->um_tab.b_errcnt) + cmd = TM_WIRG; else - cmd |= WCOM; + cmd = TM_WCOM; } else - cmd |= RCOM; - cmd |= (tm_ubinfo >> 12) & 0x30; - tmminfo[0]->um_tab.b_active = SIO; - addr->tmba = tm_ubinfo; - addr->tmcs = cmd; + cmd = TM_RCOM; + um->um_tab.b_active = SIO; + um->um_cmd = sc->sc_dens|cmd; +#ifdef notdef + if (tmreverseop(sc->sc_lastcmd)) + while (addr->tmer & TMER_SDWN) + tmgapsdcnt++; + sc->sc_lastcmd = TM_RCOM; /* will serve */ +#endif + sc->sc_timo = 60; /* premature, but should serve */ + (void) ubago(ui); return; } - tmminfo[0]->um_tab.b_active = SSEEK; + /* + * Tape positioned incorrectly; + * set to seek forwards or backwards to the correct spot. + * This happens for raw tapes only on error retries. + */ + um->um_tab.b_active = SSEEK; if (blkno < dbtofsb(bp->b_blkno)) { - cmd |= SFORW; + bp->b_command = TM_SFORW; addr->tmbc = blkno - dbtofsb(bp->b_blkno); } else { - cmd |= SREV; + bp->b_command = TM_SREV; addr->tmbc = dbtofsb(bp->b_blkno) - blkno; } - addr->tmcs = cmd; + sc->sc_timo = min(max(10 * -addr->tmbc, 60), 5 * 60); +dobpcmd: +#ifdef notdef + /* + * It is strictly necessary to wait for the tape + * to stop before changing directions, but the TC11 + * handles this for us. + */ + if (tmreverseop(sc->sc_lastcmd) != tmreverseop(bp->b_command)) + while (addr->tmer & TM_SDWN) + tmgapsdcnt++; + sc->sc_lastcmd = bp->b_command; +#endif + /* + * Do the command in bp. + */ + addr->tmcs = (sc->sc_dens | bp->b_command); return; next: - ubarelse(ui->ui_ubanum, &tm_ubinfo); - tmminfo[0]->um_tab.b_actf = bp->av_forw; + /* + * Done with this operation due to error or + * the fact that it doesn't do anything. + * Release UBA resources (if any), dequeue + * the transfer and continue processing this slave. + */ + if (um->um_ubinfo) + ubadone(um); + um->um_tab.b_errcnt = 0; + dp->b_actf = bp->av_forw; iodone(bp); goto loop; } -tmdgo() +/* + * The UNIBUS resources we needed have been + * allocated to us; start the device. + */ +tmdgo(um) + register struct uba_ctlr *um; { + register struct device *addr = (struct device *)um->um_addr; + + addr->tmba = um->um_ubinfo; + addr->tmcs = um->um_cmd | ((um->um_ubinfo >> 12) & 0x30); } -tmintr(d) +/* + * Tm interrupt routine. + */ +/*ARGSUSED*/ +tmintr(tm11) + int tm11; { + struct buf *dp; register struct buf *bp; - register struct device *addr = (struct device *)tmdinfo[d]->ui_addr; + register struct uba_ctlr *um = tmminfo[tm11]; + register struct device *addr; + register struct te_softc *sc; + int teunit; register state; - if (t_flags&WAITREW && (addr->tmer&RWS) == 0) { - t_flags &= ~WAITREW; - wakeup((caddr_t)&t_flags); - } - if ((bp = tmminfo[0]->um_tab.b_actf) == NULL) + if ((dp = um->um_tab.b_actf) == NULL) return; - t_dsreg = addr->tmcs; - t_erreg = addr->tmer; - t_resid = addr->tmbc; + bp = dp->b_actf; + teunit = TEUNIT(bp->b_dev); + addr = (struct device *)tedinfo[teunit]->ui_addr; + sc = &te_softc[teunit]; + /* + * If last command was a rewind, and tape is still + * rewinding, wait for the rewind complete interrupt. + */ + if (um->um_tab.b_active == SREW) { + um->um_tab.b_active = SCOM; + if (addr->tmer&TMER_RWS) { + sc->sc_timo = 5*60; /* 5 minutes */ + return; + } + } + /* + * An operation completed... record status + */ + sc->sc_timo = INF; + sc->sc_dsreg = addr->tmcs; + sc->sc_erreg = addr->tmer; + sc->sc_resid = addr->tmbc; if ((bp->b_flags & B_READ) == 0) - t_flags |= LASTIOW; - state = tmminfo[0]->um_tab.b_active; - tmminfo[0]->um_tab.b_active = 0; - if (addr->tmcs&ERROR) { - while(addr->tmer & SDWN) + sc->sc_lastiow = 1; + state = um->um_tab.b_active; + um->um_tab.b_active = 0; + /* + * Check for errors. + */ + if (addr->tmcs&TM_ERR) { + while (addr->tmer & TMER_SDWN) ; /* await settle down */ - if (addr->tmer&EOF) { - tmseteof(bp); /* set blkno and nxrec */ - state = SCOM; + /* + * If we hit the end of the tape file, update our position. + */ + if (addr->tmer&TMER_EOF) { + tmseteof(bp); /* set blkno and nxrec */ + state = SCOM; /* force completion */ + /* + * Stuff bc so it will be unstuffed correctly + * later to get resid. + */ addr->tmbc = -bp->b_bcount; - goto errout; + goto opdone; } - if ((bp->b_flags&B_READ) && (addr->tmer&(HARD|SOFT)) == RLE) - goto out; - if ((addr->tmer&HARD)==0 && state==SIO) { - if (++tmminfo[0]->um_tab.b_errcnt < 7) { - if((addr->tmer&SOFT) == NXM) - printf("TM UBA late error\n"); - t_blkno++; - ubarelse(tmdinfo[d]->ui_ubanum, &tm_ubinfo); - tmstart(); - return; + /* + * 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 == &rtmbuf[TMUNIT(bp->b_dev)] && (bp->b_flags&B_READ) && + (addr->tmer&(TMER_HARD|TMER_SOFT)) == TMER_RLE) + goto ignoreerr; + /* + * If error is not hard, and this was an i/o operation + * retry up to 8 times. + */ + if ((addr->tmer&TMER_HARD)==0 && state==SIO) { + if (++um->um_tab.b_errcnt < 7) { + sc->sc_blkno++; + ubadone(um); + goto opcont; } - } else if (t_openf>0 && bp != &rtmbuf) - t_openf = -1; - deverror(bp, t_erreg, t_dsreg); + } else + /* + * Hard or non-i/o errors on non-raw tape + * cause it to close. + */ + if (sc->sc_openf>0 && bp != &rtmbuf[TMUNIT(bp->b_dev)]) + sc->sc_openf = -1; + /* + * Couldn't recover error + */ + printf("te%d: hard error bn%d er=%b\n", minor(bp->b_dev)&03, + bp->b_blkno, sc->sc_erreg, TMER_BITS); bp->b_flags |= B_ERROR; - state = SIO; + goto opdone; } -out: + /* + * Advance tape control FSM. + */ +ignoreerr: switch (state) { case SIO: - t_blkno++; - /* fall into ... */ + /* + * Read/write increments tape block number + */ + sc->sc_blkno++; + goto opdone; case SCOM: - if (bp == &ctmbuf) { - switch (bp->b_command) { - case SFORW: - t_blkno -= bp->b_repcnt; - break; - - case SREV: - t_blkno += bp->b_repcnt; - break; + /* + * For forward/backward space record update current position. + */ + if (bp == &ctmbuf[TMUNIT(bp->b_dev)]) + switch (bp->b_command) { + + case TM_SFORW: + sc->sc_blkno -= bp->b_repcnt; + break; - default: - if (++bp->b_repcnt < 0) { - tmstart(); /* continue */ - return; - } - } + case TM_SREV: + sc->sc_blkno += bp->b_repcnt; + break; } -errout: - tmminfo[0]->um_tab.b_errcnt = 0; - tmminfo[0]->um_tab.b_actf = bp->av_forw; - bp->b_resid = -addr->tmbc; - ubarelse(tmdinfo[d]->ui_ubanum, &tm_ubinfo); - iodone(bp); - break; + goto opdone; case SSEEK: - t_blkno = dbtofsb(bp->b_blkno); - break; + sc->sc_blkno = dbtofsb(bp->b_blkno); + goto opcont; default: + panic("tmintr"); + } +opdone: + /* + * Reset error count and remove + * from device queue. + */ + um->um_tab.b_errcnt = 0; + dp->b_actf = bp->av_forw; + bp->b_resid = -addr->tmbc; + ubadone(um); + iodone(bp); + /* + * Circulate slave to end of controller + * queue to give other slaves a chance. + */ + um->um_tab.b_actf = dp->b_forw; + if (dp->b_actf) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + } + if (um->um_tab.b_actf == 0) return; +opcont: + tmstart(um); +} + +tmtimer(dev) + int dev; +{ + register struct te_softc *sc = &te_softc[TEUNIT(dev)]; + + if (sc->sc_timo != INF && (sc->sc_timo -= 5) < 0) { + printf("te%d: lost interrupt\n"); + sc->sc_timo = INF; + (void) spl5(); + tmintr(TMUNIT(dev)); + (void) spl0(); } - tmstart(); + timeout(tmtimer, dev, 5*hz); } tmseteof(bp) register struct buf *bp; { + register int teunit = TEUNIT(bp->b_dev); register struct device *addr = - (struct device *)tmdinfo[minor(bp->b_dev)&03]->ui_addr; + (struct device *)tedinfo[teunit]->ui_addr; + register struct te_softc *sc = &te_softc[teunit]; - if (bp == &ctmbuf) { - if (t_blkno > dbtofsb(bp->b_blkno)) { + if (bp == &ctmbuf[TMUNIT(bp->b_dev)]) { + if (sc->sc_blkno > dbtofsb(bp->b_blkno)) { /* reversing */ - t_nxrec = dbtofsb(bp->b_blkno) - addr->tmbc; - t_blkno = t_nxrec; + sc->sc_nxrec = dbtofsb(bp->b_blkno) - addr->tmbc; + sc->sc_blkno = sc->sc_nxrec; } else { /* spacing forward */ - t_blkno = dbtofsb(bp->b_blkno) + addr->tmbc; - t_nxrec = t_blkno - 1; + sc->sc_blkno = dbtofsb(bp->b_blkno) + addr->tmbc; + sc->sc_nxrec = sc->sc_blkno - 1; } return; } /* eof on read */ - t_nxrec = dbtofsb(bp->b_blkno); + sc->sc_nxrec = dbtofsb(bp->b_blkno); } tmread(dev) + dev_t dev; { tmphys(dev); - physio(tmstrategy, &rtmbuf, dev, B_READ, minphys); + if (u.u_error) + return; + physio(tmstrategy, &rtmbuf[TMUNIT(dev)], dev, B_READ, minphys); } tmwrite(dev) + dev_t dev; { tmphys(dev); - physio(tmstrategy, &rtmbuf, dev, B_WRITE, minphys); + if (u.u_error) + return; + physio(tmstrategy, &rtmbuf[TMUNIT(dev)], dev, B_WRITE, minphys); } +/* + * Check that a raw device exists. + * If it does, set up sc_blkno and sc_nxrec + * so that the tape will appear positioned correctly. + */ tmphys(dev) + dev_t dev; { + register int teunit = TEUNIT(dev); register daddr_t a; + register struct te_softc *sc; + register struct uba_device *ui; + if (teunit >= NTE || (ui=tedinfo[teunit]) == 0 || ui->ui_alive == 0) { + u.u_error = ENXIO; + return; + } + sc = &te_softc[teunit]; a = dbtofsb(u.u_offset >> 9); - t_blkno = a; - t_nxrec = a + 1; + sc->sc_blkno = a; + sc->sc_nxrec = a + 1; +} + +tmreset(uban) + int uban; +{ + register struct uba_ctlr *um; + register tm11, teunit; + register struct uba_device *ui; + register struct buf *dp; + + for (tm11 = 0; tm11 < NTM; tm11++) { + if ((um = tmminfo[tm11]) == 0 || um->um_alive == 0 || + um->um_ubanum != uban) + continue; + printf(" tm%d", tm11); + um->um_tab.b_active = 0; + um->um_tab.b_actf = um->um_tab.b_actl = 0; + if (um->um_ubinfo) { + printf("<%d>", (um->um_ubinfo>>28)&0xf); + ubadone(um); + } + ((struct device *)(um->um_addr))->tmcs = TM_DCLR; + for (teunit = 0; teunit < NTE; teunit++) { + if ((ui = tedinfo[teunit]) == 0 || ui->ui_mi != um || + ui->ui_alive == 0) + continue; + dp = &teutab[teunit]; + dp->b_active = 0; + dp->b_forw = 0; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + if (te_softc[teunit].sc_openf > 0) + te_softc[teunit].sc_openf = -1; + } + tmstart(um); + } } /*ARGSUSED*/ @@ -454,21 +829,29 @@ tmioctl(dev, cmd, addr, flag) caddr_t addr; dev_t dev; { + int teunit = TEUNIT(dev); + register struct te_softc *sc = &te_softc[teunit]; + register struct buf *bp = &ctmbuf[TMUNIT(dev)]; register callcount; int fcount; struct mtop mtop; struct mtget mtget; /* we depend of the values and order of the MT codes here */ - static tmops[] = {WEOF, SFORW, SREV, SFORW, SREV, REW, OFFL, NOP}; + static tmops[] = + {TM_WEOF,TM_SFORW,TM_SREV,TM_SFORW,TM_SREV,TM_REW,TM_OFFL,TM_SENSE}; - switch(cmd) { + switch (cmd) { case MTIOCTOP: /* tape operation */ if (copyin((caddr_t)addr, (caddr_t)&mtop, sizeof(mtop))) { u.u_error = EFAULT; return; } switch(mtop.mt_op) { - case MTWEOF: case MTFSF: case MTBSF: + case MTWEOF: + callcount = mtop.mt_count; + fcount = 1; + break; + case MTFSF: case MTBSF: callcount = mtop.mt_count; fcount = INF; break; @@ -484,24 +867,27 @@ tmioctl(dev, cmd, addr, flag) u.u_error = ENXIO; return; } - if (callcount <= 0 || fcount <= 0) + if (callcount <= 0 || fcount <= 0) { u.u_error = ENXIO; - else while (--callcount >= 0) { - tcommand(dev, tmops[mtop.mt_op], fcount); + return; + } + while (--callcount >= 0) { + tmcommand(dev, tmops[mtop.mt_op], fcount); if ((mtop.mt_op == MTFSR || mtop.mt_op == MTBSR) && - ctmbuf.b_resid) { + bp->b_resid) { u.u_error = EIO; break; } - if ((ctmbuf.b_flags&B_ERROR) || t_erreg&BOT) + if ((bp->b_flags&B_ERROR) || sc->sc_erreg&TMER_BOT) break; } - geterror(&ctmbuf); + geterror(bp); return; case MTIOCGET: - mtget.mt_dsreg = t_dsreg; - mtget.mt_erreg = t_erreg; - mtget.mt_resid = t_resid; + mtget.mt_dsreg = sc->sc_dsreg; + mtget.mt_erreg = sc->sc_erreg; + mtget.mt_resid = sc->sc_resid; + mtget.mt_type = MT_ISTM; if (copyout((caddr_t)&mtget, addr, sizeof(mtget))) u.u_error = EFAULT; return; @@ -514,7 +900,7 @@ tmioctl(dev, cmd, addr, flag) tmdump() { - register struct uba_dinfo *ui; + register struct uba_device *ui; register struct uba_regs *up; register struct device *addr; int blk, num; @@ -523,36 +909,33 @@ tmdump() start = 0; num = maxfree; #define phys(a,b) ((b)((int)(a)&0x7fffffff)) - if (tmdinfo[0] == 0) { - printf("dna\n"); - return (-1); - } - ui = phys(tmdinfo[0], struct uba_dinfo *); + if (tedinfo[0] == 0) + return (ENXIO); + ui = phys(tedinfo[0], struct uba_device *); up = phys(ui->ui_hd, struct uba_hd *)->uh_physuba; -#if VAX780 - if (cpu == VAX_780) - ubainit(up); -#endif + ubainit(up); DELAY(1000000); addr = (struct device *)ui->ui_physaddr; tmwait(addr); - addr->tmcs = DCLR | GO; + addr->tmcs = TM_DCLR | TM_GO; while (num > 0) { blk = num > DBSIZE ? DBSIZE : num; tmdwrite(start, blk, addr, up); start += blk; num -= blk; } - tmwait(addr); tmeof(addr); tmeof(addr); - tmrewind(addr); + tmwait(addr); + if (addr->tmcs&TM_ERR) + return (EIO); + addr->tmcs = TM_REW | TM_GO; tmwait(addr); return (0); } -tmdwrite(buf, num, addr, up) - register buf, num; +tmdwrite(dbuf, num, addr, up) + register dbuf, num; register struct device *addr; struct uba_regs *up; { @@ -563,11 +946,11 @@ tmdwrite(buf, num, addr, up) io = up->uba_map; npf = num+1; while (--npf != 0) - *(int *)io++ = (buf++ | (1<tmbc = -(num*NBPG); addr->tmba = 0; - addr->tmcs = WCOM | GO; + addr->tmcs = TM_WCOM | TM_GO; } tmwait(addr) @@ -577,15 +960,7 @@ tmwait(addr) do s = addr->tmcs; - while ((s & CUR) == 0); -} - -tmrewind(addr) - struct device *addr; -{ - - tmwait(addr); - addr->tmcs = REW | GO; + while ((s & TM_CUR) == 0); } tmeof(addr) @@ -593,6 +968,6 @@ tmeof(addr) { tmwait(addr); - addr->tmcs = WEOF | GO; + addr->tmcs = TM_WEOF | TM_GO; } #endif