/* * TM78/TU78 tape driver * * TODO: * multiple drives per formatter might work, but are untested */ #include "sys/param.h" #include "sys/buf.h" #include "sys/conf.h" #include "sys/file.h" #include "sys/user.h" #include "sys/mbaddr.h" #include "sys/mbsts.h" #include "sys/subaddr.h" #include "sys/mtio.h" #include "sys/tu78.h" /* * hardware stuff */ struct device { int mtcs; /* control/status */ int mter; /* error */ int mtca; /* command address, rec cnt, skp cnt */ int mtmr1; /* maint */ int mtas; /* attention summary */ int mtbc; /* byte count */ int mtdt; /* drive type */ int mtds; /* drive status */ int mtsn; /* serial number */ int mtmr2; /* maint */ int mtmr3; /* maint */ int mtner; /* non-data transfer error */ int mtncs[TM78DRIVES]; /* non-data transfer command */ int mtia; /* internal address */ int mtid; /* internal data */ }; /* mtcs */ #define MT_GO 000001 /* go bit */ #define MT_GCR 000002 /* make generic ops GCR ops */ #define MT_UNLOAD 000004 /* unload tape */ #define MT_REW 000006 /* rewind */ #define MT_SENSE 000010 /* sense */ #define MT_WTM 000014 /* generic write tape mark */ #define MT_SFORW 000020 /* space forward record */ #define MT_SREV 000022 /* space reverse record */ #define MT_SFORWF 000024 /* space forward file */ #define MT_SREVF 000026 /* space reverse file */ #define MT_CLS 000040 /* generic close file */ #define MT_WRITE 000060 /* generic write */ #define MT_READ 000070 /* read forward */ #define MT_READREV 000076 /* read reverse */ /* mtds */ #define MTDS_PRES 0040000 /* tape unit has power */ #define MTDS_ONL 0020000 /* online */ #define MTDS_PE 0004000 /* tape set for phase encoded */ #define MTDS_BOT 0002000 /* tape at BOT */ #define MTDS_FPT 0000400 /* write protected */ #define MTDS_AVAIL 0000200 /* unit available */ /* mtncs */ #define MT_COFF 8 /* bits to shift count */ #define MTNCS_COUNT(c) (((c)>>MT_COFF)&0377) /* repeat count */ #define MAXCOUNT 0377 /* mter */ #define MTER_INTCODE(e) ((int)(e)&0377) /* interrupt code */ #define MTER_DPR 0400 /* drive (formatter) present */ #define MTER_UNIT(e) (((e)>>8)&03) /* interrupting unit in mtner */ /* interrupt codes */ #define MTER_DONE 001 /* operation complete */ #define MTER_TM 002 /* unexpected tape mark */ #define MTER_BOT 003 /* unexpected BOT detected */ #define MTER_EOT 004 /* tape positioned beyond EOT */ #define MTER_LEOT 005 /* unexpected LEOT detected */ #define MTER_NOOP 006 /* no-op completed */ #define MTER_RWDING 007 /* rewinding */ #define MTER_FPT 010 /* write protect error */ #define MTER_NOTRDY 011 /* not ready */ #define MTER_NOTAVL 012 /* not available */ #define MTER_OFFLINE 013 /* offline */ #define MTER_NONEX 014 /* unit does not exist */ #define MTER_NOTCAP 015 /* not capable */ #define MTER_ONLINE 017 /* tape came online */ #define MTER_LONGREC 020 /* long tape record */ #define MTER_SHRTREC 021 /* short tape record */ #define MTER_RETRY 022 /* retry */ #define MTER_RDOPP 023 /* read opposite */ #define MTER_UNREAD 024 /* unreadable */ #define MTER_ERROR 025 /* error */ #define MTER_EOTERR 026 /* EOT error */ #define MTER_BADTAPE 027 /* tape position lost */ #define MTER_TMFLTA 030 /* TM fault A */ #define MTER_TUFLTA 031 /* TU fault A */ #define MTER_TMFLTB 032 /* TM fault B */ #define MTER_MBFLT 034 /* Massbus fault */ #define MTER_KEYFAIL 077 /* keypad entry error */ /* mtid */ #define MTID_RDY 0100000 /* controller ready */ #define MTID_CLR 0040000 /* controller clear */ #define MTTIMEOUT 10000 /* loop limit for controller test */ /* * system stuff */ int tu78open(), tu78close(), tu78read(), tu78write(), tu78ioctl(), tu78strategy(); struct cdevsw tu78cdev = cdinit(tu78open, tu78close, tu78read, tu78write, tu78ioctl); struct bdevsw tu78bdev = bdinit(tu78open, tu78close, tu78strategy, B_TAPE); /* * per-formatter stuff */ extern int tm78cnt; extern struct tm78 tm78[]; extern struct mbaddr tm78addr[]; #define T_BUSY 01 /* controller busy */ #define NOUNIT 0377 /* no slave expected */ /* * per-drive stuff */ extern int tu78cnt; extern struct tu78 tu78[]; extern struct subaddr tu78addr[]; extern struct buf rtu78buf[]; extern struct buf ctu78buf[]; /* * bits in minor device */ #define TUUNIT(dev) (minor(dev)&03) #define H_NOREWIND 04 #define H_6250BPI 010 /* * sc_flags */ #define H_BUSY 01 /* software drive busy */ #define H_EOT 02 /* saw physical end-of-tape */ #define H_WRITTEN 04 /* last operation was a write */ #define H_ERASED 010 /* last write retry was an erase gap */ #define H_OPEN 020 /* device open */ #define H_OFFLINE 040 /* device went offline; no io till close */ #define H_WONLY 0100 /* write-only */ /* * scaling for block magtape blocks */ #define INF 1000000L /* a block number that won't exist */ #define TBLOCK 2 /* tape blocks per b_blkno block */ #define TBSIZE 1024 #define TBSHIFT 10 /* * overloads in struct buf; for tu78cmd */ #define b_repcnt b_bcount #define b_command b_resid tu78open(dev, flag) dev_t dev; int flag; { register int tuunit; register struct tu78 *sc; register struct subaddr *sa; int dens; tuunit = TUUNIT(dev); if (tuunit >= tu78cnt || (sc = &tu78[tuunit])->sc_flags & H_OPEN) { u.u_error = ENXIO; return; } sc->sc_flags |= H_OPEN; sa = &tu78addr[tuunit]; if (tm78init(sa->ctl, sa->unit, tuunit) == 0) { sc->sc_flags &=~ H_OPEN; u.u_error = ENXIO; return; } sc->sc_ctl = &tm78[sa->ctl]; sc->sc_dens = minor(dev) & H_6250BPI ? MT_GCR : 0; sc->sc_slave = sa->unit; sc->sc_flags &=~ H_OFFLINE; /* so we can sense */ tu78cmd(dev, MT_SENSE, 1L); if ((sc->sc_dsreg & MTDS_PRES) == 0) { /* what's the right test? */ printf("tu78 %d absent\n", tuunit); sc->sc_flags &=~ H_OPEN; u.u_error = ENODEV; return; } if ((sc->sc_dsreg & MTDS_ONL) == 0) { sc->sc_flags &=~ H_OPEN; u.u_error = EIO; return; } if ((flag&FWRITE) && (sc->sc_dsreg&MTDS_FPT)) { sc->sc_flags &=~ H_OPEN; u.u_error = EIO; return; } if ((sc->sc_dsreg & MTDS_BOT) == 0 && flag&FWRITE) { dens = sc->sc_dsreg & MTDS_PE ? 0 : MT_GCR; if (sc->sc_dens != dens) { sc->sc_flags &=~ H_OPEN; u.u_error = EIO; return; } } sc->sc_blkno = 0; sc->sc_nxrec = INF; if ((flag & (FREAD|FWRITE)) == FWRITE) sc->sc_flags |= H_WONLY; /* stop block dev readahead */ } tm78init(ctl, slave, dev) register int ctl; int slave, dev; { register struct tm78 *cc; register int i; if (ctl < 0 || slave < 0 || ctl >= tm78cnt || slave >= TM78DRIVES) return (0); cc = &tm78[ctl]; cc->cc_drives[slave] = dev; if (cc->cc_mbaddr) return (1); if ((cc->cc_addr = (struct device *)mbaddr(&tm78addr[ctl])) == 0 || badaddr(&cc->cc_addr->mtcs, sizeof(long)) || (cc->cc_addr->mter & MTER_DPR) == 0) { printf("tm78 %d absent\n", ctl); return (0); } for (i = 0; i < TM78DRIVES; i++) if (i != slave) cc->cc_drives[i] = NOUNIT; if (tm78reset(ctl) == 0) return (0); cc->cc_mbaddr = &tm78addr[ctl]; return (1); } /* * reset the formatter * initially or after some awful error */ tm78reset(ctl) int ctl; { register struct tm78 *cc; register struct device *tmaddr; register struct tu78 *sc; register struct buf *bp; register int i; cc = &tm78[ctl]; tmaddr = cc->cc_addr; tmaddr->mtid = MTID_CLR; while ((sc = cc->cc_actf) != NULL) { cc->cc_actf = sc->sc_next; while ((bp = sc->sc_actf) != NULL) { sc->sc_actf = bp->av_forw; bp->b_flags |= B_ERROR; iodone(bp); } sc->sc_flags &=~ H_BUSY; } cc->cc_flags &=~ T_BUSY; DELAY(200); for (i = 0; i < MTTIMEOUT; i++) { if (tmaddr->mtid & MTID_RDY) return (1); /* done */ DELAY(50); /* wait a bit more */ } printf("tm78 %d: never ready\n", ctl); return (0); } tu78close(dev) register dev_t dev; { register struct tu78 *sc = &tu78[TUUNIT(dev)]; if (sc->sc_flags & (H_WONLY|H_WRITTEN)) tu78cmd(dev, MT_CLS|sc->sc_dens, 1L); if ((minor(dev)&H_NOREWIND) == 0) tu78cmd(dev, MT_REW, 0L); sc->sc_flags &= H_BUSY; } tu78cmd(dev, com, count) dev_t dev; int com; register daddr_t count; { register struct buf *bp; register int some; bp = &ctu78buf[TUUNIT(dev)]; (void) 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; (void) spl0(); bp->b_dev = dev; bp->b_command = com; bp->b_blkno = 0; do { if (count <= MAXCOUNT) some = count; else some = MAXCOUNT; bp->b_repcnt = some; tu78strategy(bp); if (count == 0) { if (bp->b_flags & B_DONE) geterror(bp); return; /* sic */ } iowait(bp); } while (u.u_error == 0 && (count -= some) > 0); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags &=~ B_BUSY; } tu78strategy(bp) register struct buf *bp; { register struct tu78 *sc; sc = &tu78[TUUNIT(bp->b_dev)]; bp->av_forw = NULL; (void) spl5(); if (sc->sc_actf == NULL) sc->sc_actf = bp; else sc->sc_actl->av_forw = bp; sc->sc_actl = bp; if ((sc->sc_flags & H_BUSY) == 0) tu78start(sc); if (sc->sc_ctl->cc_actf && (sc->sc_ctl->cc_flags & T_BUSY) == 0) tm78start(sc->sc_ctl); (void) spl0(); } tu78start(sc) register struct tu78 *sc; { register struct buf *bp; register struct device *tmaddr; register struct tm78 *cc; register daddr_t bno; register int cmd, dist; cc = sc->sc_ctl; loop: if ((bp = sc->sc_actf) == 0) return; tmaddr = cc->cc_addr; sc->sc_flags &= ~H_WRITTEN; if (sc->sc_flags & H_OFFLINE) { bp->b_flags |= B_ERROR; goto done; } /* * command, start it */ if (bp == &ctu78buf[TUUNIT(bp->b_dev)]) { sc->sc_flags |= H_BUSY; cmd = bp->b_command|MT_GO; cmd |= bp->b_repcnt << MT_COFF; #if BABBLE printf("cmd %o\n", cmd); #endif tmaddr->mtncs[sc->sc_slave] = cmd; return; } /* * transfer, see if position needed (wretched block magtape) */ bno = bp->b_blkno/TBLOCK; if (bno > sc->sc_nxrec) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; goto done; } if (bp->b_flags & B_READ /* at EOF, or really just writing? */ && (bno == sc->sc_nxrec || sc->sc_flags & H_WONLY)) { bp->b_resid = bp->b_bcount; goto done; } if ((bp->b_flags&B_READ)==0) sc->sc_nxrec = bno + 1; if (sc->sc_blkno != bno) { /* need position */ sc->sc_flags |= H_BUSY; if (sc->sc_blkno < bno) { sc->sc_flags &=~ H_EOT; dist = bno - sc->sc_blkno; cmd = MT_SREV; } else { dist = sc->sc_blkno - bno; cmd = MT_SFORW; } if (dist > MAXCOUNT) dist = MAXCOUNT; tmaddr->mtncs[sc->sc_slave] = (dist<<MT_COFF)|cmd|MT_GO; return; } /* * time for a transfer */ if ((bp->b_flags&B_READ) == 0 && sc->sc_flags & H_EOT) { bp->b_resid = bp->b_bcount; done: sc->sc_actf = bp->av_forw; iodone(bp); goto loop; } cc = sc->sc_ctl; sc->sc_next = NULL; if (cc->cc_actf == NULL) cc->cc_actf = sc; else cc->cc_actl->sc_next = sc; cc->cc_actl = sc; sc->sc_flags |= H_BUSY; } tm78start(cc) register struct tm78 *cc; { register struct tu78 *sc; int tm78xfer(); if ((sc = cc->cc_actf) == NULL) return; if (sc->sc_actf == NULL) panic("tm78start"); cc->cc_flags |= T_BUSY; mbstart(cc->cc_mbaddr, sc->sc_actf, tm78xfer); } tm78xfer(bp) register struct buf *bp; { register struct tu78 *sc; register struct device *tmaddr; sc = &tu78[TUUNIT(bp->b_dev)]; tmaddr = sc->sc_ctl->cc_addr; tmaddr->mtca = sc->sc_slave; tmaddr->mtbc = bp->b_bcount; #if BABBLE printf("xfer go\n"); #endif if (bp->b_flags & B_READ) tmaddr->mtcs = MT_READ|MT_GO; else tmaddr->mtcs = MT_WRITE|sc->sc_dens|MT_GO; } /* * massbus interrupt * if a transfer was pending, assume it finished; * if attn set, something else finished too */ tm780int(ctl, mbsr, mbbc, attn) int ctl, mbsr, mbbc, attn; { register struct tm78 *cc; cc = &tm78[ctl]; if (cc->cc_addr == NULL) { printf("tm78 %d: stray intr\n", ctl); return; } if (attn) tu78attn(ctl, mbsr, attn); if (cc->cc_flags & T_BUSY) tu78dtint(ctl, mbsr); if (cc->cc_actf) tm78start(cc); } /* * here with an attention: * a boring interrupt, a disaster, or a command finished * don't clear the attn until we've read the registers */ tu78attn(ctl, mbsr, attn) int ctl, mbsr, attn; { register struct tm78 *cc; register struct tu78 *sc; register struct buf *bp; register struct device *tmaddr; register int unit; daddr_t bno; register int i; register long rt; /* register temp to avoid compiler botches */ cc = &tm78[ctl]; tmaddr = cc->cc_addr; rt = tmaddr->mtner; i = MTER_UNIT(rt); #if BABBLE printf("attn er%o ds%o cs%o\n", tmaddr->mtner&0xffff, tmaddr->mtds&0xffff, tmaddr->mtncs[i]&0xffff); #endif switch (MTER_INTCODE(rt)) { case MTER_ONLINE: case MTER_RWDING: tmaddr->mtas = attn; return; /* boring */ case MTER_MBFLT: case MTER_TMFLTB: printf("tm78 %d fault er%o mbsr%o\n", ctl, tmaddr->mtner, mbsr); tm78reset(ctl); tmaddr->mtas = attn; return; } if ((unit = cc->cc_drives[i]) == NOUNIT) { printf("tm78 %d unit %d stray intr er%o\n", ctl, i, tmaddr->mtner); tmaddr->mtas = attn; return; } sc = &tu78[unit]; rt = tmaddr->mtds; sc->sc_dsreg = rt; rt = tmaddr->mtner; sc->sc_erreg = rt; rt = tmaddr->mtncs[i]; sc->sc_resid = MTNCS_COUNT(rt); tmaddr->mtas = attn; if ((bp = sc->sc_actf) == NULL) { printf("tu78 %d: odd intr er%o\n", unit, sc->sc_erreg); return; } switch (MTER_INTCODE(sc->sc_erreg)) { case MTER_EOT: sc->sc_flags |= H_EOT; /* fall through */ case MTER_TM: case MTER_LEOT: case MTER_BOT: case MTER_NOOP: case MTER_DONE: /* * this was a block magtape seek, * or we're closing (so sc_blkno is boring), * or it's raw magtape (ditto) */ bno = bp->b_blkno / TBLOCK; if (sc->sc_blkno > bno) sc->sc_blkno += sc->sc_resid; else sc->sc_blkno -= sc->sc_resid; if (sc->sc_flags & H_EOT) sc->sc_nxrec = sc->sc_blkno; if (bp != &ctu78buf[unit]) sc->sc_flags &=~ H_BUSY; /* done with seek */ break; case MTER_NOTAVL: case MTER_OFFLINE: case MTER_NONEX: if (sc->sc_flags & H_OPEN) sc->sc_flags |= H_OFFLINE; bp->b_error = ENXIO; bp->b_flags |= B_ERROR; break; default: printf("tu78 %d: hard error bn%d er%o mbsr%o\n", unit, bp->b_blkno, sc->sc_erreg, mbsr); bp->b_flags |= B_ERROR; break; } if (sc->sc_flags & H_BUSY) { sc->sc_actf = bp->av_forw; iodone(bp); sc->sc_flags &=~ H_BUSY; } if (sc->sc_actf) tu78start(sc); } tu78dtint(ctl, mbsr) int ctl, mbsr; { register struct tm78 *cc; register struct tu78 *sc; register struct buf *bp; register struct device *tmaddr; register int rt; /* reg temp to avoid compiler botches */ cc = &tm78[ctl]; tmaddr = cc->cc_addr; if ((sc = cc->cc_actf) == NULL || (bp = sc->sc_actf) == NULL) panic("tu78dtint"); rt = tmaddr->mtbc; sc->sc_resid = rt; rt = tmaddr->mter; sc->sc_erreg = rt; #if BABBLE printf("dtint er%o bc%o mbs%o\n", sc->sc_erreg, sc->sc_resid, mbsr); #endif if (mbsr & MBSR_HARD) { printf("tu78 %d: mb error er%o mbsr%o\n", TUUNIT(bp->b_dev), sc->sc_erreg, mbsr); bp->b_flags |= B_ERROR; } else switch (MTER_INTCODE(sc->sc_erreg)) { case MTER_MBFLT: case MTER_TMFLTB: printf("tm78 %d fault er%o mbsr%o\n", ctl, sc->sc_erreg, mbsr); tm78reset(ctl); return; case MTER_EOT: sc->sc_flags |= H_EOT; /* fall through */ case MTER_LEOT: case MTER_TM: bp->b_resid = bp->b_bcount; sc->sc_nxrec = sc->sc_blkno; break; case MTER_LONGREC: sc->sc_blkno++; bp->b_error = ENOMEM; bp->b_flags |= B_ERROR; break; case MTER_SHRTREC: if (bp != &rtu78buf[TUUNIT(bp->b_dev)]) /* block tape */ bp->b_flags |= B_ERROR; /* fall in */ case MTER_DONE: bp->b_resid = bp->b_bcount - sc->sc_resid; sc->sc_blkno++; break; case MTER_RETRY: cc->cc_flags &=~ T_BUSY; break; case MTER_NOTAVL: case MTER_OFFLINE: case MTER_NONEX: if (sc->sc_flags & H_OPEN) sc->sc_flags |= H_OFFLINE; bp->b_error = ENXIO; bp->b_flags |= B_ERROR; break; case MTER_NOTCAP: case MTER_BADTAPE: if (sc->sc_flags & H_OPEN) sc->sc_flags |= H_OFFLINE; /* really position lost */ /* fall through */ default: printf("tu78 %d: hard error bn%d er%o mbsr%o\n", TUUNIT(bp->b_dev), bp->b_blkno, sc->sc_erreg, mbsr); sc->sc_blkno++; bp->b_flags |= B_ERROR; break; } if (cc->cc_flags & T_BUSY) { cc->cc_actf = sc->sc_next; sc->sc_actf = bp->av_forw; iodone(bp); sc->sc_flags &=~ H_BUSY; if (sc->sc_actf) tu78start(sc); cc->cc_flags &=~ T_BUSY; } } tu78read(dev) dev_t dev; { tu78phys(dev); physio(tu78strategy, &rtu78buf[TUUNIT(dev)], dev, B_READ, minphys); } tu78write(dev) dev_t dev; { tu78phys(dev); physio(tu78strategy, &rtu78buf[TUUNIT(dev)], dev, B_WRITE, minphys); } tu78phys(dev) dev_t dev; { register struct tu78 *sc; sc = &tu78[TUUNIT(dev)]; sc->sc_blkno = Lshift(u.u_offset, TBSHIFT); sc->sc_nxrec = sc->sc_blkno + 1; } tu78ioctl(dev, cmd, addr, flag) dev_t dev; int cmd; caddr_t addr; int flag; { struct mtop mtop; switch (cmd) { case MTIOCTOP: /* tape operation */ if (copyin((caddr_t)addr, (caddr_t)&mtop, sizeof(mtop))) { u.u_error = EFAULT; return; } if (mtop.mt_count == 0) mtop.mt_count = 1; switch(mtop.mt_op) { case MTWEOF: tu78cmd(dev, MT_WTM|tu78[TUUNIT(dev)].sc_dens, mtop.mt_count); break; case MTFSF: tu78cmd(dev, MT_SFORWF, mtop.mt_count); break; case MTBSF: tu78cmd(dev, MT_SREVF, mtop.mt_count); break; case MTFSR: tu78cmd(dev, MT_SFORW, mtop.mt_count); break; case MTBSR: tu78cmd(dev, MT_SREV, mtop.mt_count); break; case MTREW: tu78cmd(dev, MT_REW, 1L); break; case MTOFFL: tu78cmd(dev, MT_UNLOAD, 1L); break; default: u.u_error = ENXIO; break; } return; default: u.u_error = ENXIO; return; } }