/* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)mt.c 7.1 (Berkeley) 6/5/86 */ #include "mu.h" #if NMT > 0 /* * TM78/TU78 tape driver * * Original author - ? * Most error recovery bug fixes - ggs (ulysses!ggs) * * OPTIONS: * MTLERRM - Long error message text - twd, Brown University * MTRDREV - `read reverse' error recovery - ggs (ulysses!ggs) * * TODO: * Add odd byte count kludge from VMS driver (?) * Write dump routine */ #include "../machine/pte.h" #include "param.h" #include "systm.h" #include "buf.h" #include "conf.h" #include "dir.h" #include "file.h" #include "user.h" #include "map.h" #include "ioctl.h" #include "mtio.h" #include "cmap.h" #include "uio.h" #include "tty.h" #include "../vax/cpu.h" #include "mbareg.h" #include "mbavar.h" #include "mtreg.h" #define MTTIMEOUT 10000 /* loop limit for controller test */ #define INF 1000000L /* a block number that won't exist */ #define MASKREG(r) ((r) & 0xffff) /* the control registers have 16 bits */ /* Bits for sc_flags */ #define H_WRITTEN 01 /* last operation was a write */ #define H_EOT 02 /* end of tape encountered */ #define H_IEOT 04 /* ignore EOT condition */ /* Bits in minor device */ #define MUUNIT(dev) (minor(dev)&03) #define H_NOREWIND 04 #define H_6250BPI 010 #define MTUNIT(dev) (mutomt[MUUNIT(dev)]) #ifdef MTRDREV int mt_do_readrev = 1; #else int mt_do_readrev = 0; #endif /* Per unit status information */ struct mu_softc { char sc_openf; /* unit is open if != 0 */ char sc_flags; /* state flags */ daddr_t sc_blkno; /* current physical block number */ daddr_t sc_nxrec; /* firewall input block number */ u_short sc_erreg; /* copy of mter or mtner */ u_short sc_dsreg; /* copy of mtds */ short sc_resid; /* residual function count for ioctl */ short sc_dens; /* density code - MT_GCR or zero */ struct mba_device *sc_mi; /* massbus structure for unit */ int sc_slave; /* slave number for unit */ int sc_i_mtas; /* mtas at slave attach time */ int sc_i_mtner; /* mtner at slave attach time */ int sc_i_mtds; /* mtds at slave attach time */ #ifdef MTLERRM char *sc_mesg; /* text for interrupt type code */ char *sc_fmesg; /* text for tape error code */ #endif struct tty *sc_ttyp; /* record user's tty for errors */ } mu_softc[NMU]; struct buf rmtbuf[NMT]; /* data transfer buffer structures */ struct buf cmtbuf[NMT]; /* tape command buffer structures */ struct mba_device *mtinfo[NMT]; /* unit massbus structure pointers */ short mutomt[NMU]; /* tape unit to controller number map */ char mtds_bits[] = MTDS_BITS; /* mtds bit names for error messages */ short mttypes[] = { MBDT_TU78, 0 }; int mtattach(), mtslave(), mtustart(), mtstart(), mtndtint(), mtdtint(); struct mba_driver mtdriver = { mtattach, mtslave, mtustart, mtstart, mtdtint, mtndtint, mttypes, "mt", "mu", mtinfo }; void mtcreset(); /*ARGSUSED*/ mtattach(mi) struct mba_device *mi; { } mtslave(mi, ms, sn) struct mba_device *mi; struct mba_slave *ms; int sn; { register struct mu_softc *sc = &mu_softc[ms->ms_unit]; register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv; int s = spl5(), rtn = 0, i; /* Just in case the controller is ill, reset it. Then issue */ /* a sense operation and wait about a second for it to respond. */ mtcreset(mtaddr); mtaddr->mtas = -1; mtaddr->mtncs[sn] = MT_SENSE|MT_GO; for (i = MTTIMEOUT; i> 0; i--) { DELAY(50); if (MASKREG(mtaddr->mtas) != 0) break; } sc->sc_i_mtas = mtaddr->mtas; sc->sc_i_mtner = mtaddr->mtner; sc->sc_i_mtds = mtaddr->mtds; /* If no response, whimper. If wrong response, call it an */ /* unsolicited interrupt and use mtndtint to log and correct. */ /* Otherwise, note whether this slave exists. */ if (i <= 0) { printf("mt: controller hung\n"); } else if ((mtaddr->mtner & MTER_INTCODE) != MTER_DONE) { (void) mtndtint(mi); } else if (mtaddr->mtds & MTDS_PRES) { sc->sc_mi = mi; sc->sc_slave = sn; mutomt[ms->ms_unit] = mi->mi_unit; rtn = 1; } /* Cancel the interrupt, then wait a little while for it to go away. */ mtaddr->mtas = mtaddr->mtas; DELAY(10); splx(s); return (rtn); } mtopen(dev, flag) dev_t dev; int flag; { register int muunit; register struct mba_device *mi; register struct mu_softc *sc; muunit = MUUNIT(dev); if ( (muunit >= NMU) || ((mi = mtinfo[MTUNIT(dev)]) == 0) || (mi->mi_alive == 0) ) return (ENXIO); if ((sc = &mu_softc[muunit])->sc_openf) return (EBUSY); sc->sc_dens = (minor(dev) & H_6250BPI) ? MT_GCR : 0; mtcommand(dev, MT_SENSE, 1); if ((sc->sc_dsreg & MTDS_ONL) == 0) { uprintf("mu%d: not online\n", muunit); return (EIO); } if ((sc->sc_dsreg & MTDS_AVAIL) == 0) { uprintf("mu%d: not online (port selector)\n", muunit); return (EIO); } if ((flag & FWRITE) && (sc->sc_dsreg & MTDS_FPT)) { uprintf("mu%d: no write ring\n", muunit); return (EIO); } if ( ((sc->sc_dsreg & MTDS_BOT) == 0) && (flag & FWRITE) && ( ( (sc->sc_dens == MT_GCR) && (sc->sc_dsreg & MTDS_PE) ) || ( (sc->sc_dens != MT_GCR) && ((sc->sc_dsreg & MTDS_PE) == 0)))) { uprintf("mu%d: can't change density in mid-tape\n", muunit); return (EIO); } sc->sc_openf = 1; sc->sc_blkno = (daddr_t)0; /* Since cooked I/O may do a read-ahead before a write, trash */ /* on a tape can make the first write fail. Suppress the first */ /* read-ahead unless definitely doing read-write */ sc->sc_nxrec = ((flag & (FTRUNC | FWRITE)) == (FTRUNC | FWRITE)) ? (daddr_t)0 : (daddr_t)INF; sc->sc_flags = 0; sc->sc_ttyp = u.u_ttyp; return (0); } mtclose(dev, flag) register dev_t dev; register int flag; { register struct mu_softc *sc = &mu_softc[MUUNIT(dev)]; if ( ((flag & (FREAD | FWRITE)) == FWRITE) || ( (flag & FWRITE) && (sc->sc_flags & H_WRITTEN) )) mtcommand(dev, MT_CLS|sc->sc_dens, 1); if ((minor(dev) & H_NOREWIND) == 0) mtcommand(dev, MT_REW, 0); sc->sc_openf = 0; } mtcommand(dev, com, count) dev_t dev; int com, count; { register struct buf *bp; register int s; bp = &cmtbuf[MTUNIT(dev)]; s = spl5(); while (bp->b_flags & B_BUSY) { 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_command = com; bp->b_repcnt = count; bp->b_blkno = 0; bp->b_error = 0; mtstrategy(bp); if (count == 0) return; iowait(bp); if (bp->b_flags & B_WANTED) wakeup((caddr_t)bp); bp->b_flags &= B_ERROR; } mtstrategy(bp) register struct buf *bp; { register struct mba_device *mi = mtinfo[MTUNIT(bp->b_dev)]; register struct buf *dp; register int s; /* If this is a data transfer operation, set the resid to a */ /* default value (EOF) to simplify getting it right during */ /* error recovery or bail out. */ if (bp != &cmtbuf[MTUNIT(bp->b_dev)]) bp->b_resid = bp->b_bcount; /* Link this request onto the end of the queue for this */ /* controller, then start I/O if not already active. */ bp->av_forw = NULL; dp = &mi->mi_tab; s = spl5(); if (dp->b_actf == NULL) dp->b_actf = bp; else dp->b_actl->av_forw = bp; dp->b_actl = bp; if (dp->b_active == 0) mbustart(mi); splx(s); } mtustart(mi) register struct mba_device *mi; { register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv; register struct buf *bp = mi->mi_tab.b_actf; register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)]; daddr_t blkno; int count; if (sc->sc_openf < 0) { bp->b_flags |= B_ERROR; return (MBU_NEXT); } if (bp != &cmtbuf[MTUNIT(bp->b_dev)]) { /* Signal "no space" if out of tape unless suppressed */ /* by MTIOCIEOT. */ if ( ((sc->sc_flags & (H_EOT | H_IEOT)) == H_EOT) && ((bp->b_flags & B_READ) == 0) ) { bp->b_flags |= B_ERROR; bp->b_error = ENOSPC; return (MBU_NEXT); } /* special case tests for cooked mode */ if (bp != &rmtbuf[MTUNIT(bp->b_dev)]) { /* seek beyond end of file */ if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) { bp->b_flags |= B_ERROR; bp->b_error = ENXIO; return (MBU_NEXT); } /* This should be end of file, but the buffer */ /* system wants a one-block look-ahead. Humor it. */ if ( (bdbtofsb(bp->b_blkno) == sc->sc_nxrec) && (bp->b_flags & B_READ) ) { clrbuf(bp); return (MBU_NEXT); } /* If writing, mark the next block invalid. */ if ((bp->b_flags & B_READ) == 0) sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1; } } else { /* It's a command, do it now. */ mtaddr->mtncs[MUUNIT(bp->b_dev)] = (bp->b_repcnt<<8)|bp->b_command|MT_GO; return (MBU_STARTED); } /* If raw I/O, or if the tape is positioned correctly for */ /* cooked I/O, set the byte count, unit number and repeat count */ /* then tell the MASSBUS to proceed. Note that a negative */ /* bcount tells mbstart to map the buffer for "read backwards". */ if ( (bp == &rmtbuf[MTUNIT(bp->b_dev)]) || ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) ) { if (mi->mi_tab.b_errcnt == 2) { mtaddr->mtbc = -(bp->b_bcount); mtaddr->mtca = MUUNIT(bp->b_dev); } else { mtaddr->mtbc = bp->b_bcount; mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev); } return (MBU_DODATA); } /* Issue skip operations to position the next block for cooked I/O. */ if (blkno < bdbtofsb(bp->b_blkno)) count = (unsigned)(bdbtofsb(bp->b_blkno) - blkno); else count = (unsigned)(blkno - bdbtofsb(bp->b_blkno)); if (count > 0377) count = 0377; mtaddr->mtncs[MUUNIT(bp->b_dev)] = count | MT_SFORW|MT_GO; return (MBU_STARTED); } mtstart(mi) register struct mba_device *mi; { register struct buf *bp = mi->mi_tab.b_actf; register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)]; if (bp->b_flags & B_READ) if (mi->mi_tab.b_errcnt == 2) return(MT_READREV|MT_GO); else return(MT_READ|MT_GO); else return(MT_WRITE|sc->sc_dens|MT_GO); } mtdtint(mi, mbsr) register struct mba_device *mi; int mbsr; { register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv; register struct buf *bp = mi->mi_tab.b_actf; register struct mu_softc *sc; register int er; /* I'M still NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */ if ((mtaddr->mtca & 3) != MUUNIT(bp->b_dev)) { printf("mt: wrong unit!\n"); mtaddr->mtca = MUUNIT(bp->b_dev); } er = MASKREG(mtaddr->mter); sc = &mu_softc[MUUNIT(bp->b_dev)]; sc->sc_erreg = er; if (bp->b_flags & B_READ) sc->sc_flags &= ~H_WRITTEN; else sc->sc_flags |= H_WRITTEN; switch (er & MTER_INTCODE) { case MTER_EOT: sc->sc_flags |= H_EOT; /* fall into MTER_DONE */ case MTER_DONE: sc->sc_blkno++; if (mi->mi_tab.b_errcnt == 2) { bp->b_bcount = bp->b_resid; bp->b_resid -= MASKREG(mtaddr->mtbc); if ( (bp->b_resid > 0) && (bp != &rmtbuf[MTUNIT(bp->b_dev)]) ) bp->b_flags |= B_ERROR; } else { bp->b_resid = 0; } break; case MTER_SHRTREC: sc->sc_blkno++; bp->b_bcount = bp->b_resid; bp->b_resid -= MASKREG(mtaddr->mtbc); if (bp != &rmtbuf[MTUNIT(bp->b_dev)]) bp->b_flags |= B_ERROR; break; case MTER_RETRY: /* Simple re-try. Since resid is always a copy of the */ /* original byte count, use it to restore the count. */ mi->mi_tab.b_errcnt = 1; bp->b_bcount = bp->b_resid; return(MBD_RETRY); case MTER_RDOPP: /* The controller just decided to read it backwards. */ /* If the controller returns a byte count of zero, */ /* change it to 1, since zero encodes 65536, which */ /* isn't quite what we had in mind. The byte count */ /* may be larger than the size of the input buffer, so */ /* limit the count to the buffer size. After */ /* making the byte count reasonable, set bcount to the */ /* negative of the controller's version of the byte */ /* count so that the start address for the transfer is */ /* set up correctly. */ if (mt_do_readrev) { mi->mi_tab.b_errcnt = 2; if ((bp->b_bcount = MASKREG(mtaddr->mtbc)) == 0) bp->b_bcount = 1; if (bp->b_bcount > bp->b_resid) bp->b_bcount = bp->b_resid; bp->b_bcount = -(bp->b_bcount); return(MBD_RETRY); } else if (MASKREG(mtaddr->mtbc) <= bp->b_resid) { sc->sc_blkno++; bp->b_bcount = bp->b_resid; bp->b_resid -= MASKREG(mtaddr->mtbc); bp->b_flags |= B_ERROR; break; } bp->b_flags |= B_ERROR; /* fall into MTER_LONGREC */ case MTER_LONGREC: sc->sc_blkno++; bp->b_bcount = bp->b_resid; bp->b_resid = 0; bp->b_error = ENOMEM; bp->b_flags |= B_ERROR; break; case MTER_NOTCAP: printf("mu%d: blank tape\n", MUUNIT(bp->b_dev)); goto err; case MTER_TM: /* End of file. Since the default byte count has */ /* already been set, just count the block and proceed. */ sc->sc_blkno++; err: if (bp != &rmtbuf[MTUNIT(bp->b_dev)]) sc->sc_nxrec = bdbtofsb(bp->b_blkno); break; case MTER_OFFLINE: if (sc->sc_openf > 0) { sc->sc_openf = -1; tprintf(sc->sc_ttyp, "mu%d: offline\n", MUUNIT(bp->b_dev)); } bp->b_flags |= B_ERROR; break; case MTER_NOTAVL: if (sc->sc_openf > 0) { sc->sc_openf = -1; tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n", MUUNIT(bp->b_dev)); } bp->b_flags |= B_ERROR; break; case MTER_FPT: tprintf(sc->sc_ttyp, "mu%d: no write ring\n", MUUNIT(bp->b_dev)); bp->b_flags |= B_ERROR; break; case MTER_UNREAD: sc->sc_blkno++; bp->b_bcount = bp->b_resid; bp->b_resid -= MIN(MASKREG(mtaddr->mtbc), bp->b_bcount); /* Code 010 means a garbage record, nothing serious. */ if (((er & MTER_FAILCODE) >> 10) == 010) { tprintf(sc->sc_ttyp, "mu%d: rn=%d bn=%d unreadable record\n", MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno); bp->b_flags |= B_ERROR; break; } /* Anything else might be a hardware problem, */ /* fall into the error report. */ default: /* The bits in sc->sc_dsreg are from the last sense */ /* command. To get the most recent copy, you have to */ /* do a sense at interrupt level, which requires nested */ /* error processing. This is a bit messy, so leave */ /* well enough alone. */ tprintf(sc->sc_ttyp, "mu%d: hard error (data transfer) rn=%d bn=%d mbsr=%b er=%o (octal) ds=%b\n", MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno, mbsr, mbsr_bits, er, MASKREG(sc->sc_dsreg), mtds_bits); #ifdef MTLERRM mtintfail(sc); printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n", er & MTER_INTCODE, sc->sc_mesg, (er & MTER_FAILCODE) >> 10, sc->sc_fmesg); #endif bp->b_flags |= B_ERROR; /* The TM78 manual says to reset the controller after */ /* TM fault B or MASSBUS fault. */ if ( ((er & MTER_INTCODE) == MTER_TMFLTB) || ((er & MTER_INTCODE) == MTER_MBFLT) ) { mtcreset(mtaddr); } } /* Just in case some strange error slipped through, (drive off */ /* line during read-reverse error recovery comes to mind) make */ /* sure the byte count is reasonable. */ if (bp->b_bcount < 0) bp->b_bcount = bp->b_resid; return (MBD_DONE); } mtndtint(mi) register struct mba_device *mi; { register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv; register struct buf *bp = mi->mi_tab.b_actf; register struct mu_softc *sc; register int er, fc; int unit; unit = (mtaddr->mtner >> 8) & 3; er = MASKREG(mtaddr->mtner); sc = &mu_softc[unit]; sc->sc_erreg = er; /* Check for unsolicited interrupts. */ if (bp == 0 || unit != MUUNIT(bp->b_dev)) { /* consistency check */ if ((er & MTER_INTCODE) != MTER_ONLINE) { printf("mt: unit %d unexpected interrupt (non data transfer) er=%o (octal) ds=%b\n", unit, er, MASKREG(sc->sc_dsreg), mtds_bits); #ifdef MTLERRM mtintfail(sc); printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n", er & MTER_INTCODE, sc->sc_mesg, (er & MTER_FAILCODE) >> 10, sc->sc_fmesg); #endif if ( ((er & MTER_INTCODE) == MTER_TMFLTB) || ((er & MTER_INTCODE) == MTER_MBFLT) ) { /* Reset the controller, then set error */ /* status if there was anything active */ /* when the fault occurred. This may */ /* shoot an innocent bystander, but */ /* it's better than letting an error */ /* slip through. */ mtcreset(mtaddr); if (bp != 0) { bp->b_flags |= B_ERROR; return (MBN_DONE); } } } return (MBN_SKIP); } if (bp == 0) return (MBN_SKIP); fc = (mtaddr->mtncs[unit] >> 8) & 0xff; sc->sc_resid = fc; /* Clear the "written" flag after any operation that changes */ /* the position of the tape. */ if ( (bp != &cmtbuf[MTUNIT(bp->b_dev)]) || (bp->b_command != MT_SENSE) ) sc->sc_flags &= ~H_WRITTEN; switch (er & MTER_INTCODE) { case MTER_EOT: sc->sc_flags |= H_EOT; /* fall into MTER_DONE */ case MTER_DONE: /* If this is a command buffer, just update the status. */ if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) { done: if (bp->b_command == MT_SENSE) sc->sc_dsreg = MASKREG(mtaddr->mtds); return (MBN_DONE); } /* It's not a command buffer, must be a cooked I/O */ /* skip operation (perhaps a shaky assumption, but it */ /* wasn't my idea). */ if ((fc = bdbtofsb(bp->b_blkno) - sc->sc_blkno) < 0) sc->sc_blkno -= MIN(0377, -fc); else sc->sc_blkno += MIN(0377, fc); return (MBN_RETRY); case MTER_ONLINE: /* ddj -- shouldn't happen but did */ case MTER_RWDING: return (MBN_SKIP); /* ignore "rewind started" interrupt */ case MTER_NOTCAP: tprintf(sc->sc_ttyp, "mu%d: blank tape\n", MUUNIT(bp->b_dev)); bp->b_flags |= B_ERROR; return (MBN_DONE); case MTER_TM: case MTER_LEOT: /* For an ioctl skip operation, count a tape mark as */ /* a record. If there's anything left to do, update */ /* the repeat count and re-start the command. */ if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) { if ((sc->sc_resid = bp->b_repcnt = fc - 1) == 0) return (MBN_DONE); else return (MBN_RETRY); /* Cooked I/O again. Just update the books and wait */ /* for someone else to return end of file or complain */ /* about a bad seek. */ } else if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) { sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc - 1; sc->sc_blkno = sc->sc_nxrec; } else { sc->sc_nxrec = bdbtofsb(bp->b_blkno) - fc; sc->sc_blkno = sc->sc_nxrec + 1; } return (MBN_RETRY); case MTER_FPT: tprintf(sc->sc_ttyp, "mu%d: no write ring\n", MUUNIT(bp->b_dev)); bp->b_flags |= B_ERROR; return (MBN_DONE); case MTER_OFFLINE: /* If `off line' was intentional, don't complain. */ if ( (bp == &cmtbuf[MTUNIT(bp->b_dev)]) && (bp->b_command == MT_UNLOAD) ) return(MBN_DONE); if (sc->sc_openf > 0) { sc->sc_openf = -1; tprintf(sc->sc_ttyp, "mu%d: offline\n", MUUNIT(bp->b_dev)); } bp->b_flags |= B_ERROR; return (MBN_DONE); case MTER_NOTAVL: if (sc->sc_openf > 0) { sc->sc_openf = -1; tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n", MUUNIT(bp->b_dev)); } bp->b_flags |= B_ERROR; return (MBN_DONE); case MTER_BOT: if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) goto done; /* fall through */ default: tprintf(sc->sc_ttyp, "mu%d: hard error (non data transfer) rn=%d bn=%d er=%o (octal) ds=%b\n", MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno, er, MASKREG(sc->sc_dsreg), mtds_bits); #ifdef MTLERRM mtintfail(sc); printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n", (er & MTER_INTCODE), sc->sc_mesg, (er & MTER_FAILCODE) >> 10, sc->sc_fmesg); #endif if ( ((er & MTER_INTCODE) == MTER_TMFLTB) || ((er & MTER_INTCODE) == MTER_MBFLT) ) { mtcreset(mtaddr); /* reset the controller */ } bp->b_flags |= B_ERROR; return (MBN_DONE); } /* NOTREACHED */ } void mtcreset(mtaddr) register struct mtdevice *mtaddr; { register int i; mtaddr->mtid = MTID_CLR; /* reset the TM78 */ DELAY(200); for (i = MTTIMEOUT; i > 0; i--) { DELAY(50); /* don't nag */ if ((mtaddr->mtid & MTID_RDY) != 0) return; /* exit when ready */ } printf("mt: controller hung\n"); } mtread(dev, uio) dev_t dev; struct uio *uio; { int errno; errno = mtphys(dev, uio); if (errno) return (errno); return (physio(mtstrategy, &rmtbuf[MTUNIT(dev)], dev, B_READ, minphys, uio)); } mtwrite(dev, uio) dev_t dev; struct uio *uio; { int errno; errno = mtphys(dev, uio); if (errno) return (errno); return (physio(mtstrategy, &rmtbuf[MTUNIT(dev)], dev, B_WRITE, minphys, uio)); } mtphys(dev, uio) dev_t dev; struct uio *uio; { register int mtunit; struct mba_device *mi; register int bsize = uio->uio_iov->iov_len; mtunit = MTUNIT(dev); if ( (mtunit >= NMT) || ((mi = mtinfo[mtunit]) == 0) || (mi->mi_alive == 0) ) return (ENXIO); if ( (bsize > 0xffff) /* controller limit */ || (bsize <= 0) ) /* ambiguous */ return (EINVAL); return (0); } /*ARGSUSED*/ mtioctl(dev, cmd, data, flag) dev_t dev; int cmd; caddr_t data; int flag; { register struct mu_softc *sc = &mu_softc[MUUNIT(dev)]; register struct buf *bp = &cmtbuf[MTUNIT(dev)]; register struct mtop *mtop; register struct mtget *mtget; int callcount, fcount; int op; /* We depend on the values and order of the MT codes here. */ static mtops[] = {MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE}; switch (cmd) { /* tape operation */ case MTIOCTOP: mtop = (struct mtop *)data; switch (mtop->mt_op) { case MTWEOF: callcount = mtop->mt_count; fcount = 1; break; case MTFSF: case MTBSF: callcount = mtop->mt_count; fcount = 1; break; case MTFSR: case MTBSR: callcount = 1; fcount = mtop->mt_count; break; case MTREW: case MTOFFL: callcount = 1; fcount = 1; break; default: return (ENXIO); } if ((callcount <= 0) || (fcount <= 0)) return (EINVAL); op = mtops[mtop->mt_op]; if (op == MT_WTM) op |= sc->sc_dens; while (--callcount >= 0) { register int n, fc = fcount; do { n = MIN(fc, 0xff); mtcommand(dev, op, n); n -= sc->sc_resid; fc -= n; switch (mtop->mt_op) { case MTWEOF: sc->sc_blkno += (daddr_t)n; sc->sc_nxrec = sc->sc_blkno - 1; break; case MTOFFL: case MTREW: case MTFSF: sc->sc_blkno = (daddr_t)0; sc->sc_nxrec = (daddr_t)INF; break; case MTBSF: if (sc->sc_resid) { sc->sc_blkno = (daddr_t)0; sc->sc_nxrec = (daddr_t)INF; } else { sc->sc_blkno = (daddr_t)(-1); sc->sc_nxrec = (daddr_t)(-1); } break; case MTFSR: sc->sc_blkno += (daddr_t)n; break; case MTBSR: sc->sc_blkno -= (daddr_t)n; break; } if (sc->sc_resid) break; } while (fc); if (fc) { sc->sc_resid = callcount + fc; if ( (mtop->mt_op == MTFSR) || (mtop->mt_op == MTBSR) ) return (EIO); else break; } if (bp->b_flags & B_ERROR) break; } return (geterror(bp)); /* tape status */ case MTIOCGET: mtget = (struct mtget *)data; mtget->mt_erreg = sc->sc_erreg; mtget->mt_resid = sc->sc_resid; mtcommand(dev, MT_SENSE, 1); /* update drive status */ mtget->mt_dsreg = sc->sc_dsreg; mtget->mt_type = MT_ISMT; break; /* ignore EOT condition */ case MTIOCIEOT: sc->sc_flags |= H_IEOT; break; /* enable EOT condition */ case MTIOCEEOT: sc->sc_flags &= ~H_IEOT; break; default: return (ENXIO); } return (0); } #define DBSIZE 20 mtdump() { register struct mba_device *mi; register struct mba_regs *mp; int blk, num; int start; start = 0; num = maxfree; #define phys(a,b) ((b)((int)(a)&0x7fffffff)) if (mtinfo[0] == 0) return (ENXIO); mi = phys(mtinfo[0], struct mba_device *); mp = phys(mi->mi_hd, struct mba_hd *)->mh_physmba; mp->mba_cr = MBCR_IE; #if lint blk = 0; num = blk; start = num; blk = start; return (0); #endif #ifdef notyet mtaddr = (struct mtdevice *)&mp->mba_drv[mi->mi_drive]; mtaddr->mttc = MTTC_PDP11|MTTC_1600BPI; mtaddr->mtcs1 = MT_DCLR|MT_GO; while (num > 0) { blk = num > DBSIZE ? DBSIZE : num; mtdwrite(start, blk, mtaddr, mp); start += blk; num -= blk; } mteof(mtaddr); mteof(mtaddr); mtwait(mtaddr); if (mtaddr->mtds&MTDS_ERR) return (EIO); mtaddr->mtcs1 = MT_REW|MT_GO; return (0); } mtdwrite(dbuf, num, mtaddr, mp) register dbuf, num; register struct mtdevice *mtaddr; struct mba_regs *mp; { register struct pte *io; register int i; mtwait(mtaddr); io = mp->mba_map; for (i = 0; i < num; i++) *(int *)io++ = dbuf++ | PG_V; mtaddr->mtfc = -(num*NBPG); mp->mba_sr = -1; mp->mba_bcr = -(num*NBPG); mp->mba_var = 0; mtaddr->mtcs1 = MT_WCOM|MT_GO; } mtwait(mtaddr) struct mtdevice *mtaddr; { register s; do s = mtaddr->mtds; while ((s & MTDS_DRY) == 0); } mteof(mtaddr) struct mtdevice *mtaddr; { mtwait(mtaddr); mtaddr->mtcs1 = MT_WEOF|MT_GO; #endif notyet } #ifdef MTLERRM mtintfail(sc) register struct mu_softc *sc; { switch (sc->sc_erreg & MTER_INTCODE) { /* unexpected BOT detected */ case MTER_BOT: sc->sc_mesg = "unexpected BOT"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "tape was at BOT"; break; case 02: sc->sc_fmesg = "BOT seen after tape started"; break; case 03: sc->sc_fmesg = "ARA ID detected"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* unexpected LEOT detected */ case MTER_LEOT: sc->sc_mesg = "unexpected LEOT"; sc->sc_fmesg = ""; break; /* rewinding */ case MTER_RWDING: sc->sc_mesg = "tape rewinding"; sc->sc_fmesg = ""; break; /* not ready */ case MTER_NOTRDY: sc->sc_mesg = "drive not ready"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "TU on-line but not ready"; break; case 02: sc->sc_fmesg = "fatal error has occurred"; break; case 03: sc->sc_fmesg = "access allowed but not really"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* not available */ case MTER_NOTAVL: sc->sc_mesg = "drive not available"; sc->sc_fmesg = ""; break; /* unit does not exist */ case MTER_NONEX: sc->sc_mesg = "unit does not exist"; sc->sc_fmesg = ""; break; /* not capable */ case MTER_NOTCAP: sc->sc_mesg = "not capable"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "no record found within 25 feet"; break; case 02: sc->sc_fmesg = "ID burst neither PE nor GCR"; break; case 03: sc->sc_fmesg = "ARA ID not found"; break; case 04: sc->sc_fmesg = "no gap found after ID burst"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* long tape record */ case MTER_LONGREC: sc->sc_mesg = "long record"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 00: sc->sc_fmesg = "extended sense data not found"; break; case 01: sc->sc_fmesg = "extended sense data updated"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* unreadable */ case MTER_UNREAD: sc->sc_mesg = "unreadable record"; goto code22; /* error */ case MTER_ERROR: sc->sc_mesg = "error"; goto code22; /* EOT error */ case MTER_EOTERR: sc->sc_mesg = "EOT error"; goto code22; /* tape position lost */ case MTER_BADTAPE: sc->sc_mesg = "bad tape"; code22: switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "GCR write error"; break; case 02: sc->sc_fmesg = "GCR read error"; break; case 03: sc->sc_fmesg = "PE read error"; break; case 04: sc->sc_fmesg = "PE write error"; break; case 05: sc->sc_fmesg = "at least 1 bit set in ECCSTA"; break; case 06: sc->sc_fmesg = "PE write error"; break; case 07: sc->sc_fmesg = "GCR write error"; break; case 010: sc->sc_fmesg = "RSTAT contains bad code"; break; case 011: sc->sc_fmesg = "PE write error"; break; case 012: sc->sc_fmesg = "MASSBUS parity error"; break; case 013: sc->sc_fmesg = "invalid data transferred"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* TM fault A */ case MTER_TMFLTA: sc->sc_mesg = "TM fault A"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "illegal command code"; break; case 02: sc->sc_fmesg = "DT command issued when NDT command active"; break; case 03: sc->sc_fmesg = "WMC error"; break; case 04: sc->sc_fmesg = "RUN not received from MASSBUS controller"; break; case 05: sc->sc_fmesg = "mismatch in command read - function routine"; break; case 06: sc->sc_fmesg = "ECC ROM parity error"; break; case 07: sc->sc_fmesg = "XMC ROM parity error"; break; case 010: sc->sc_fmesg = "mismatch in command read - ID burst command"; break; case 011: sc->sc_fmesg = "mismatch in command read - verify ARA burst command"; break; case 012: sc->sc_fmesg = "mismatch in command read - verify ARA ID command"; break; case 013: sc->sc_fmesg = "mismatch in command read - verify gap command"; break; case 014: sc->sc_fmesg = "mismatch in command read - read id burst command"; break; case 015: sc->sc_fmesg = "mismatch in command read - verify ARA ID command"; break; case 016: sc->sc_fmesg = "mismatch in command read - verify gap command"; break; case 017: sc->sc_fmesg = "mismatch in command read - find gap command"; break; case 020: sc->sc_fmesg = "WMC LEFT failed to set"; break; case 021: sc->sc_fmesg = "XL PE set in INTSTA register"; break; case 022: sc->sc_fmesg = "XMC DONE did not set"; break; case 023: sc->sc_fmesg = "WMC ROM PE or RD PE set in WMCERR register"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* TU fault A */ case MTER_TUFLTA: sc->sc_mesg = "TU fault A"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "TU status parity error"; break; case 02: sc->sc_fmesg = "TU command parity error"; break; case 03: sc->sc_fmesg = "rewinding tape went offline"; break; case 04: sc->sc_fmesg = "tape went not ready during DSE"; break; case 05: sc->sc_fmesg = "TU CMD status changed during DSE"; break; case 06: sc->sc_fmesg = "TU never came up to speed"; break; case 07: sc->sc_fmesg = "TU velocity changed"; break; case 010: sc->sc_fmesg = "TU CMD did not load correctly to start tape motion"; break; case 011: sc->sc_fmesg = "TU CMD did not load correctly to set drive density"; break; case 012: sc->sc_fmesg = "TU CMD did not load correctly to start tape motion to write BOT ID"; break; case 013: sc->sc_fmesg = "TU CMD did not load correctly to backup tape to BOT after failing to write BOT ID"; break; case 014: sc->sc_fmesg = "failed to write density ID burst"; break; case 015: sc->sc_fmesg = "failed to write ARA burst"; break; case 016: sc->sc_fmesg = "failed to write ARA ID"; break; case 017: sc->sc_fmesg = "ARA error bit set in MTA status B register"; break; case 021: sc->sc_fmesg = "could not find a gap after ID code was written correctly"; break; case 022: sc->sc_fmesg = "TU CMD did not load correctly to start tape motion to read ID burst"; break; case 023: sc->sc_fmesg = "timeout looking for BOT after detecting ARA ID burst"; break; case 024: sc->sc_fmesg = "failed to write tape mark"; break; case 025: sc->sc_fmesg = "tape never came up to speed while trying to reposition for retry of writing tape mark"; break; case 026: sc->sc_fmesg = "TU CMD did not load correctly to start tape motion in erase gap routine"; break; case 027: sc->sc_fmesg = "could not detect a gap in in erase gap routine"; break; case 030: sc->sc_fmesg = "could not detect a gap after writing record"; break; case 031: sc->sc_fmesg = "read path terminated before entire record was written"; break; case 032: sc->sc_fmesg = "could not find a gap after writing record and read path terminated early"; break; case 033: sc->sc_fmesg = "TU CMD did not load correctly to backup for retry of write tape mark"; break; case 034: sc->sc_fmesg = "TU velocity changed after up to speed while trying to reposition for retry of writing tape mark"; break; case 035: sc->sc_fmesg = "TU CMD did not load correctly to backup to retry a load of BOT ID"; break; case 036: sc->sc_fmesg = "timeout looking for BOT after failing to write BOT ID"; break; case 037: sc->sc_fmesg = "TU velocity changed while writing PE gap before starting to write record"; break; case 040: sc->sc_fmesg = "TU CMD did not load correctly to set PE tape density at start of write BOT ID burst"; break; case 041: sc->sc_fmesg = "TU CMD did not load correctly to set GCR tape density after writing Density ID"; break; case 042: sc->sc_fmesg = "TU CMD did not load correctly to set PE tape density at start of read from BOT"; break; case 043: sc->sc_fmesg = "TU CMD did not load correctly to set GCR tape density after reading a GCR Density ID burst"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* TM fault B */ case MTER_TMFLTB: sc->sc_mesg = "TM fault B"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 00: sc->sc_fmesg = "RST0 interrupt occurred with TM RDY set"; break; case 01: sc->sc_fmesg = "power failed to interrupt"; break; case 02: sc->sc_fmesg = "unknown interrupt on channel 5.5"; break; case 03: sc->sc_fmesg = "unknown interrupt on channel 6.5"; break; case 04: sc->sc_fmesg = "unknown interrupt on channel 7"; break; case 05: sc->sc_fmesg = "unknown interrupt on channel 7.5"; break; case 06: sc->sc_fmesg = "CAS contention retry count expired"; break; case 07: sc->sc_fmesg = "CAS contention error not retryable"; break; case 010: sc->sc_fmesg = "queue error, could not find queue entry"; break; case 011: sc->sc_fmesg = "queue entry already full"; break; case 012: sc->sc_fmesg = "8085 ROM parity error"; break; case 013: case 014: case 015: case 016: case 017: case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027: case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037: case 040: case 041: case 042: case 043: case 044: case 045: case 046: case 047: case 050: case 051: case 052: case 053: case 054: case 055: case 056: case 057: sc->sc_fmesg = "inline test failed"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* MASSBUS fault */ case MTER_MBFLT: sc->sc_mesg = "MB fault"; switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) { case 01: sc->sc_fmesg = "control bus parity error"; break; case 02: sc->sc_fmesg = "illegal register referenced"; break; default: sc->sc_fmesg = "unclassified failure code"; } break; /* keypad entry error */ case MTER_KEYFAIL: sc->sc_mesg = "keypad entry error"; sc->sc_fmesg = ""; break; default: sc->sc_mesg = "unclassified error"; sc->sc_fmesg = ""; break; } } #endif MTLERRM #endif