xref: /csrg-svn/sys/vax/mba/mt.c (revision 40899)
123315Smckusick /*
229271Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
323315Smckusick  * All rights reserved.  The Berkeley software License Agreement
423315Smckusick  * specifies the terms and conditions for redistribution.
523315Smckusick  *
6*40899Ssklower  *	@(#)mt.c	7.7 (Berkeley) 04/12/90
723315Smckusick  */
84736Swnj 
94736Swnj #include "mu.h"
104736Swnj #if NMT > 0
114736Swnj /*
124736Swnj  * TM78/TU78 tape driver
134736Swnj  *
1417214Smckusick  *	Original author - ?
1517214Smckusick  *	Most error recovery bug fixes - ggs (ulysses!ggs)
1634221Sbostic  *	`read reverse' error recovery - ggs (ulysses!ggs)
174736Swnj  *
1817214Smckusick  * OPTIONS:
1917214Smckusick  *	MTLERRM - Long error message text - twd, Brown University
2017214Smckusick  *
214736Swnj  * TODO:
2217214Smckusick  *	Add odd byte count kludge from VMS driver (?)
2317214Smckusick  *	Write dump routine
244736Swnj  */
2517214Smckusick 
2617119Sbloom #include "param.h"
2717119Sbloom #include "systm.h"
2817119Sbloom #include "buf.h"
2917119Sbloom #include "conf.h"
3017119Sbloom #include "file.h"
3117119Sbloom #include "user.h"
32*40899Ssklower #include "proc.h"
3317119Sbloom #include "map.h"
3417119Sbloom #include "ioctl.h"
3517119Sbloom #include "mtio.h"
3617119Sbloom #include "cmap.h"
3718324Sralph #include "tty.h"
3834221Sbostic #include "syslog.h"
394736Swnj 
4034221Sbostic #include "../vax/pte.h"
418471Sroot #include "../vax/cpu.h"
4217119Sbloom #include "mbareg.h"
4317119Sbloom #include "mbavar.h"
4417119Sbloom #include "mtreg.h"
454736Swnj 
4617214Smckusick #define MTTIMEOUT	10000		/* loop limit for controller test */
4717214Smckusick #define	INF		1000000L	/* a block number that won't exist */
4817214Smckusick #define MASKREG(r)	((r) & 0xffff)	/* the control registers have 16 bits */
494736Swnj 
5017214Smckusick /* Bits for sc_flags */
514736Swnj 
5217214Smckusick #define	H_WRITTEN	01		/* last operation was a write */
5317214Smckusick #define H_EOT		02		/* end of tape encountered */
5417214Smckusick #define H_IEOT		04		/* ignore EOT condition */
554736Swnj 
5634221Sbostic int	mt_do_readrev = 1;
5717214Smckusick 
5817214Smckusick /* Per unit status information */
5917214Smckusick 
604736Swnj struct	mu_softc {
6134221Sbostic 	char	sc_openf;	/* unit is open if != 0 */
6234221Sbostic 	char	sc_flags;	/* state flags */
6334221Sbostic 	daddr_t	sc_blkno;	/* current physical block number */
6434221Sbostic 	daddr_t	sc_nxrec;	/* firewall input block number */
6534221Sbostic 	u_short	sc_erreg;	/* copy of mter or mtner */
6634221Sbostic 	u_short	sc_dsreg;	/* copy of mtds */
6734221Sbostic 	short	sc_resid;	/* residual function count for ioctl */
6834221Sbostic 	short	sc_dens;	/* density code - MT_GCR or zero */
6934221Sbostic 	int	sc_i_mtas;	/* mtas at slave attach time */
7034221Sbostic 	int	sc_i_mtner;	/* mtner at slave attach time */
7134221Sbostic 	int	sc_i_mtds;	/* mtds at slave attach time */
72*40899Ssklower 	caddr_t	sc_ctty;	/* record user's tty for errors */
7334221Sbostic 	int	sc_blks;	/* number of I/O operations since open */
7434221Sbostic 	int	sc_softerrs;	/* number of soft I/O errors since open */
754736Swnj } mu_softc[NMU];
764736Swnj 
7717214Smckusick struct	buf	cmtbuf[NMT];		/* tape command buffer structures */
784736Swnj 
7934221Sbostic struct	mba_device *mtinfo[NMT];	/* unit to ctlr structures */
8034221Sbostic struct	mba_slave *muinfo[NMU];		/* unit to slave structures */
8134221Sbostic 
8217214Smckusick char	mtds_bits[] = MTDS_BITS;	/* mtds bit names for error messages */
8317214Smckusick short	mttypes[] = { MBDT_TU78, 0 };
844736Swnj 
8517214Smckusick int	mtattach(), mtslave(), mtustart(), mtstart(), mtndtint(), mtdtint();
8617214Smckusick struct	mba_driver mtdriver =
8717214Smckusick 	{ mtattach, mtslave, mtustart, mtstart, mtdtint, mtndtint,
8817214Smckusick 	  mttypes, "mt", "mu", mtinfo };
8917214Smckusick 
9034221Sbostic /* Bits in minor device */
9134221Sbostic #define	MUUNIT(dev)	(minor(dev)&03)
9234221Sbostic #define	H_NOREWIND	04
9334221Sbostic #define	H_6250BPI	010
9417214Smckusick 
9534221Sbostic #define MTUNIT(dev)	(muinfo[MUUNIT(dev)]->ms_ctlr)
9634221Sbostic 
9734221Sbostic void	mtcreset();
9834221Sbostic 
994736Swnj /*ARGSUSED*/
1004736Swnj mtattach(mi)
1014736Swnj 	struct mba_device *mi;
1024736Swnj {
10334221Sbostic 
10434221Sbostic 	/* void */
1054736Swnj }
1064736Swnj 
1077431Skre mtslave(mi, ms, sn)
1084736Swnj 	struct mba_device *mi;
1094736Swnj 	struct mba_slave *ms;
1107431Skre 	int sn;
1114736Swnj {
1124736Swnj 	register struct mu_softc *sc = &mu_softc[ms->ms_unit];
1134736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
11426377Skarels 	int s = spl5(), rtn = 0, i;
1154736Swnj 
11634221Sbostic 	/*
11734221Sbostic 	 * Just in case the controller is ill, reset it.  Then issue
11834221Sbostic 	 * a sense operation and wait about a second for it to respond.
11934221Sbostic 	 */
12017214Smckusick 	mtcreset(mtaddr);
1214736Swnj 	mtaddr->mtas = -1;
1227431Skre 	mtaddr->mtncs[sn] = MT_SENSE|MT_GO;
12334221Sbostic 	for (i = MTTIMEOUT; i > 0; i--) {
12417214Smckusick 		DELAY(50);
12517214Smckusick 		if (MASKREG(mtaddr->mtas) != 0)
12617214Smckusick 			break;
12717214Smckusick 	}
12817214Smckusick 	sc->sc_i_mtas = mtaddr->mtas;
12917214Smckusick 	sc->sc_i_mtner = mtaddr->mtner;
13017214Smckusick 	sc->sc_i_mtds = mtaddr->mtds;
13117214Smckusick 
13234221Sbostic 	/*
13334221Sbostic 	 * If no response, whimper.  If wrong response, call it an
13434221Sbostic 	 * unsolicited interrupt and use mtndtint to log and correct.
13534221Sbostic 	 * Otherwise, note whether this slave exists.
13634221Sbostic 	 */
13734221Sbostic 	if (i <= 0)
13817214Smckusick 		printf("mt: controller hung\n");
13934221Sbostic 	else if ((mtaddr->mtner & MTER_INTCODE) != MTER_DONE)
14017214Smckusick 		(void) mtndtint(mi);
14134654Skarels 	else if (mtaddr->mtds & MTDS_PRES) {
14234654Skarels 		muinfo[ms->ms_unit] = ms;
1434736Swnj 		rtn = 1;
14434654Skarels 	}
14517214Smckusick 
14634221Sbostic 	/* cancel the interrupt, then wait a little while for it to go away */
1474736Swnj 	mtaddr->mtas = mtaddr->mtas;
14817214Smckusick 	DELAY(10);
1494736Swnj 	splx(s);
1504736Swnj 	return (rtn);
1514736Swnj }
1524736Swnj 
1534736Swnj mtopen(dev, flag)
1544736Swnj 	dev_t dev;
1554736Swnj 	int flag;
1564736Swnj {
1574736Swnj 	register int muunit;
1584736Swnj 	register struct mu_softc *sc;
15934221Sbostic 	register struct mba_slave *ms;
1604736Swnj 
1614736Swnj 	muunit = MUUNIT(dev);
16234221Sbostic 	if (muunit >= NMU || (ms = muinfo[muunit]) == NULL ||
16334221Sbostic 	    ms->ms_alive == 0 || mtinfo[ms->ms_ctlr]->mi_alive == 0)
1648581Sroot 		return (ENXIO);
16517214Smckusick 	if ((sc = &mu_softc[muunit])->sc_openf)
16617214Smckusick 		return (EBUSY);
16734221Sbostic 	sc->sc_openf = 1;
16817214Smckusick 	sc->sc_dens = (minor(dev) & H_6250BPI) ? MT_GCR : 0;
1694736Swnj 	mtcommand(dev, MT_SENSE, 1);
1704736Swnj 	if ((sc->sc_dsreg & MTDS_ONL) == 0) {
1714736Swnj 		uprintf("mu%d: not online\n", muunit);
17234221Sbostic 		sc->sc_openf = 0;
1738581Sroot 		return (EIO);
1744736Swnj 	}
17517214Smckusick 	if ((sc->sc_dsreg & MTDS_AVAIL) == 0) {
17617214Smckusick 		uprintf("mu%d: not online (port selector)\n", muunit);
17734221Sbostic 		sc->sc_openf = 0;
17817214Smckusick 		return (EIO);
17917214Smckusick 	}
18017214Smckusick 	if ((flag & FWRITE) && (sc->sc_dsreg & MTDS_FPT)) {
1814736Swnj 		uprintf("mu%d: no write ring\n", muunit);
18234221Sbostic 		sc->sc_openf = 0;
1838581Sroot 		return (EIO);
1844736Swnj 	}
18534221Sbostic 	if ((sc->sc_dsreg & MTDS_BOT) == 0 && (flag & FWRITE) &&
18634221Sbostic 	    (sc->sc_dens == MT_GCR) != ((sc->sc_dsreg & MTDS_PE) == 0)) {
1874736Swnj 		uprintf("mu%d: can't change density in mid-tape\n", muunit);
18834221Sbostic 		sc->sc_openf = 0;
1898581Sroot 		return (EIO);
1904736Swnj 	}
1914736Swnj 	sc->sc_blkno = (daddr_t)0;
19217214Smckusick 
19334221Sbostic 	/*
19434221Sbostic 	 * Since cooked I/O may do a read-ahead before a write, trash
19534221Sbostic 	 * on a tape can make the first write fail.  Suppress the first
19634221Sbostic 	 * read-ahead unless definitely doing read-write.
19734221Sbostic 	 */
19834221Sbostic 	sc->sc_nxrec = ((flag & (FTRUNC | FWRITE)) == (FTRUNC | FWRITE)) ?
19934221Sbostic 	    (daddr_t)0 : (daddr_t)INF;
2004736Swnj 	sc->sc_flags = 0;
20134221Sbostic 	sc->sc_blks = 0;
20234221Sbostic 	sc->sc_softerrs = 0;
203*40899Ssklower 	sc->sc_ctty = (caddr_t)(u.u_procp->p_flag&SCTTY ?
204*40899Ssklower 			u.u_procp->p_session->s_ttyp : 0);
2058581Sroot 	return (0);
2064736Swnj }
2074736Swnj 
2084736Swnj mtclose(dev, flag)
2094736Swnj 	register dev_t dev;
21017214Smckusick 	register int flag;
2114736Swnj {
2124736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(dev)];
2134736Swnj 
21434221Sbostic 	if ((flag & (FREAD | FWRITE)) == FWRITE ||
21534221Sbostic 	    ((flag & FWRITE) && (sc->sc_flags & H_WRITTEN)))
2164736Swnj 		mtcommand(dev, MT_CLS|sc->sc_dens, 1);
21717214Smckusick 	if ((minor(dev) & H_NOREWIND) == 0)
2184736Swnj 		mtcommand(dev, MT_REW, 0);
21934221Sbostic 	if (sc->sc_blks > 100 && sc->sc_softerrs > sc->sc_blks / 100)
22034221Sbostic 		log(LOG_INFO, "mu%d: %d soft errors in %d blocks\n",
22134221Sbostic 		    MUUNIT(dev), sc->sc_softerrs, sc->sc_blks);
2224736Swnj 	sc->sc_openf = 0;
22340733Skarels 	return (0);
2244736Swnj }
2254736Swnj 
2264736Swnj mtcommand(dev, com, count)
2274736Swnj 	dev_t dev;
2284736Swnj 	int com, count;
2294736Swnj {
2304736Swnj 	register struct buf *bp;
23134221Sbostic 	int s;
2324736Swnj 
2334736Swnj 	bp = &cmtbuf[MTUNIT(dev)];
2345437Sroot 	s = spl5();
23517214Smckusick 	while (bp->b_flags & B_BUSY) {
23634221Sbostic 		if (bp->b_repcnt == 0 && (bp->b_flags & B_DONE))
2374736Swnj 			break;
2384736Swnj 		bp->b_flags |= B_WANTED;
2394736Swnj 		sleep((caddr_t)bp, PRIBIO);
2404736Swnj 	}
2414736Swnj 	bp->b_flags = B_BUSY|B_READ;
2425437Sroot 	splx(s);
2434736Swnj 	bp->b_dev = dev;
2444736Swnj 	bp->b_command = com;
2454736Swnj 	bp->b_repcnt = count;
2464736Swnj 	bp->b_blkno = 0;
24717214Smckusick 	bp->b_error = 0;
2484736Swnj 	mtstrategy(bp);
2494736Swnj 	if (count == 0)
2504736Swnj 		return;
25134221Sbostic 	biowait(bp);
25217214Smckusick 	if (bp->b_flags & B_WANTED)
2534736Swnj 		wakeup((caddr_t)bp);
2544736Swnj 	bp->b_flags &= B_ERROR;
2554736Swnj }
2564736Swnj 
2574736Swnj mtstrategy(bp)
2584736Swnj 	register struct buf *bp;
2594736Swnj {
2604736Swnj 	register struct buf *dp;
26134221Sbostic 	struct mba_device *mi = mtinfo[MTUNIT(bp->b_dev)];
26234221Sbostic 	int s;
2634736Swnj 
26434221Sbostic 	/*
26534221Sbostic 	 * If this is a data transfer operation, set the resid to a
26634221Sbostic 	 * default value (EOF) to simplify getting it right during
26734221Sbostic 	 * error recovery or bail out.
26834221Sbostic 	 */
26917214Smckusick 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)])
27017214Smckusick 		bp->b_resid = bp->b_bcount;
27117214Smckusick 
27234221Sbostic 	/*
27334221Sbostic 	 * Link this request onto the end of the queue for this
27434221Sbostic 	 * controller, then start I/O if not already active.
27534221Sbostic 	 */
2764736Swnj 	bp->av_forw = NULL;
2774736Swnj 	dp = &mi->mi_tab;
2785437Sroot 	s = spl5();
2794736Swnj 	if (dp->b_actf == NULL)
2804736Swnj 		dp->b_actf = bp;
2814736Swnj 	else
2824736Swnj 		dp->b_actl->av_forw = bp;
2834736Swnj 	dp->b_actl = bp;
2844736Swnj 	if (dp->b_active == 0)
2854736Swnj 		mbustart(mi);
2865437Sroot 	splx(s);
2874736Swnj }
2884736Swnj 
2894736Swnj mtustart(mi)
2904736Swnj 	register struct mba_device *mi;
2914736Swnj {
29217214Smckusick 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
2934736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
2944736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
2954736Swnj 	daddr_t blkno;
29626377Skarels 	int count;
2974736Swnj 
2984736Swnj 	if (sc->sc_openf < 0) {
2994736Swnj 		bp->b_flags |= B_ERROR;
3004736Swnj 		return (MBU_NEXT);
3014736Swnj 	}
3024736Swnj 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)]) {
30334221Sbostic 		/*
30434221Sbostic 		 * Data transfer.  If write at end of tape,
30534221Sbostic 		 * signal "no space" unless suppressed
30634221Sbostic 		 * by MTIOCIEOT.
30734221Sbostic 		 */
30834221Sbostic 		if ((sc->sc_flags & (H_EOT | H_IEOT)) == H_EOT &&
30934221Sbostic 		    (bp->b_flags & B_READ) == 0) {
3104736Swnj 			bp->b_flags |= B_ERROR;
31117214Smckusick 			bp->b_error = ENOSPC;
3124736Swnj 			return (MBU_NEXT);
3134736Swnj 		}
31417214Smckusick 
31534221Sbostic 		if (bp->b_flags & B_RAW) {
31634221Sbostic 			/* raw transfer; never seek */
31734221Sbostic 			sc->sc_blkno = bdbtofsb(bp->b_blkno);
31834221Sbostic 			sc->sc_nxrec = sc->sc_blkno + 1;
31934221Sbostic 		} else {
32017214Smckusick 			/* seek beyond end of file */
32117214Smckusick 			if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) {
32217214Smckusick 				bp->b_flags |= B_ERROR;
32317214Smckusick 				bp->b_error = ENXIO;
32417214Smckusick 				return (MBU_NEXT);
32517214Smckusick 			}
32617214Smckusick 
32734221Sbostic 			/*
32834221Sbostic 			 * This should be end of file, but the buffer
32934221Sbostic 			 * system wants a one-block look-ahead.  Humor it.
33034221Sbostic 			 */
33134221Sbostic 			if (bdbtofsb(bp->b_blkno) == sc->sc_nxrec &&
33234221Sbostic 			    bp->b_flags & B_READ) {
33334221Sbostic 				bp->b_resid = bp->b_bcount;
33417214Smckusick 				clrbuf(bp);
33517214Smckusick 				return (MBU_NEXT);
33617214Smckusick 			}
33717214Smckusick 
33817214Smckusick 			/* If writing, mark the next block invalid. */
33917214Smckusick 			if ((bp->b_flags & B_READ) == 0)
34017214Smckusick 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
3414736Swnj 		}
3424736Swnj 	} else {
34317214Smckusick 		/* It's a command, do it now. */
3444736Swnj 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
3454736Swnj 			(bp->b_repcnt<<8)|bp->b_command|MT_GO;
3464736Swnj 		return (MBU_STARTED);
3474736Swnj 	}
34817214Smckusick 
34934221Sbostic 	/*
35034221Sbostic 	 * If raw I/O, or if the tape is positioned correctly for
35134221Sbostic 	 * cooked I/O, set the byte count, unit number and repeat count
35234221Sbostic 	 * then tell the MASSBUS to proceed.  Note that a negative
35334221Sbostic 	 * bcount tells mbstart to map the buffer for "read backwards".
35434221Sbostic 	 */
35534221Sbostic 	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
3564736Swnj 		if (mi->mi_tab.b_errcnt == 2) {
35734221Sbostic 			mtaddr->mtbc = -bp->b_bcount;
3584736Swnj 			mtaddr->mtca = MUUNIT(bp->b_dev);
3594736Swnj 		} else {
3604736Swnj 			mtaddr->mtbc = bp->b_bcount;
3614736Swnj 			mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);
3624736Swnj 		}
3634736Swnj 		return (MBU_DODATA);
3644736Swnj 	}
36517214Smckusick 
36617214Smckusick 	/* Issue skip operations to position the next block for cooked I/O. */
36717214Smckusick 
3687380Ssam 	if (blkno < bdbtofsb(bp->b_blkno))
36934221Sbostic 		count = bdbtofsb(bp->b_blkno) - blkno;
3704736Swnj 	else
37134221Sbostic 		count = blkno - bdbtofsb(bp->b_blkno);
37234221Sbostic 	if ((unsigned)count > 0377)
37326377Skarels 		count = 0377;
37426377Skarels 	mtaddr->mtncs[MUUNIT(bp->b_dev)] = count | MT_SFORW|MT_GO;
3754736Swnj 	return (MBU_STARTED);
3764736Swnj }
3774736Swnj 
3784736Swnj mtstart(mi)
3794736Swnj 	register struct mba_device *mi;
3804736Swnj {
3814736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
3824736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
3834736Swnj 
3844736Swnj 	if (bp->b_flags & B_READ)
3854736Swnj 		if (mi->mi_tab.b_errcnt == 2)
38634221Sbostic 			return (MT_READREV|MT_GO);
3874736Swnj 		else
38834221Sbostic 			return (MT_READ|MT_GO);
3894736Swnj 	else
39034221Sbostic 		return (MT_WRITE|sc->sc_dens|MT_GO);
3914736Swnj }
3924736Swnj 
3934736Swnj mtdtint(mi, mbsr)
3944736Swnj 	register struct mba_device *mi;
3954736Swnj 	int mbsr;
3964736Swnj {
3974736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
3984736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
3994736Swnj 	register struct mu_softc *sc;
40017214Smckusick 	register int er;
4014736Swnj 
40217214Smckusick 	/* I'M still NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
40317214Smckusick 	if ((mtaddr->mtca & 3) != MUUNIT(bp->b_dev)) {
4044736Swnj 		printf("mt: wrong unit!\n");
4054736Swnj 		mtaddr->mtca = MUUNIT(bp->b_dev);
4064736Swnj 	}
40717214Smckusick 
40817214Smckusick 	er = MASKREG(mtaddr->mter);
4094736Swnj 	sc = &mu_softc[MUUNIT(bp->b_dev)];
41017214Smckusick 	sc->sc_erreg = er;
41117214Smckusick 	if (bp->b_flags & B_READ)
41217214Smckusick 		sc->sc_flags &= ~H_WRITTEN;
41317214Smckusick 	else
4144736Swnj 		sc->sc_flags |= H_WRITTEN;
41517214Smckusick 	switch (er & MTER_INTCODE) {
41617214Smckusick 
41717214Smckusick 	case MTER_EOT:
41817214Smckusick 		sc->sc_flags |= H_EOT;
41917214Smckusick 		/* fall into MTER_DONE */
42017214Smckusick 
4214736Swnj 	case MTER_DONE:
42217214Smckusick 		sc->sc_blkno++;
42317214Smckusick 		if (mi->mi_tab.b_errcnt == 2) {
42417214Smckusick 			bp->b_bcount = bp->b_resid;
42517214Smckusick 			bp->b_resid -= MASKREG(mtaddr->mtbc);
42634221Sbostic 			if (bp->b_resid > 0 && (bp->b_flags & B_RAW) == 0)
42717214Smckusick 				bp->b_flags |= B_ERROR;
42834221Sbostic 		} else
42917214Smckusick 			bp->b_resid = 0;
43017214Smckusick 		break;
43117214Smckusick 
43217214Smckusick 	case MTER_SHRTREC:
43317214Smckusick 		sc->sc_blkno++;
43417214Smckusick 		bp->b_bcount = bp->b_resid;
43517214Smckusick 		bp->b_resid -= MASKREG(mtaddr->mtbc);
43634221Sbostic 		if ((bp->b_flags & B_RAW) == 0)
43717214Smckusick 			bp->b_flags |= B_ERROR;
43817214Smckusick 		break;
43917214Smckusick 
44017214Smckusick 	case MTER_RETRY:
44134221Sbostic 		/*
44234221Sbostic 		 * Simple re-try.  Since resid is always a copy of the
44334221Sbostic 		 * original byte count, use it to restore the count.
44434221Sbostic 		 */
44517214Smckusick 		mi->mi_tab.b_errcnt = 1;
44617214Smckusick 		bp->b_bcount = bp->b_resid;
44734221Sbostic 		return (MBD_RETRY);
44817214Smckusick 
44917214Smckusick 	case MTER_RDOPP:
45034221Sbostic 		/*
45134221Sbostic 		 * The controller just decided to read it backwards.
45234221Sbostic 		 * If the controller returns a byte count of zero,
45334221Sbostic 		 * change it to 1, since zero encodes 65536, which
45434221Sbostic 		 * isn't quite what we had in mind.  The byte count
45534221Sbostic 		 * may be larger than the size of the input buffer, so
45634221Sbostic 		 * limit the count to the buffer size.  After
45734221Sbostic 		 * making the byte count reasonable, set bcount to the
45834221Sbostic 		 * negative of the controller's version of the byte
45934221Sbostic 		 * count so that the start address for the transfer is
46034221Sbostic 		 * set up correctly.
46134221Sbostic 		 */
46217214Smckusick 		if (mt_do_readrev) {
46317214Smckusick 			mi->mi_tab.b_errcnt = 2;
46417214Smckusick 			if ((bp->b_bcount = MASKREG(mtaddr->mtbc)) == 0)
46517214Smckusick 				bp->b_bcount = 1;
46617214Smckusick 			if (bp->b_bcount > bp->b_resid)
46717214Smckusick 				bp->b_bcount = bp->b_resid;
46817214Smckusick 			bp->b_bcount = -(bp->b_bcount);
46917214Smckusick 			return(MBD_RETRY);
47017214Smckusick 		} else if (MASKREG(mtaddr->mtbc) <= bp->b_resid) {
47117214Smckusick 			sc->sc_blkno++;
47217214Smckusick 			bp->b_bcount = bp->b_resid;
47317214Smckusick 			bp->b_resid -= MASKREG(mtaddr->mtbc);
47417214Smckusick 			bp->b_flags |= B_ERROR;
47517214Smckusick 			break;
47617214Smckusick 		}
47717214Smckusick 		bp->b_flags |= B_ERROR;
47817214Smckusick 		/* fall into MTER_LONGREC */
47917214Smckusick 
4804736Swnj 	case MTER_LONGREC:
48117214Smckusick 		sc->sc_blkno++;
48217214Smckusick 		bp->b_bcount = bp->b_resid;
4834736Swnj 		bp->b_resid = 0;
48417214Smckusick 		bp->b_error = ENOMEM;
48517214Smckusick 		bp->b_flags |= B_ERROR;
4864736Swnj 		break;
4874736Swnj 
4884736Swnj 	case MTER_NOTCAP:
4894736Swnj 		printf("mu%d: blank tape\n", MUUNIT(bp->b_dev));
4904736Swnj 		goto err;
4914736Swnj 
4924736Swnj 	case MTER_TM:
49334221Sbostic 		/*
49434221Sbostic 		 * End of file.  Since the default byte count has
49534221Sbostic 		 * already been set, just count the block and proceed.
49634221Sbostic 		 */
4974736Swnj 		sc->sc_blkno++;
4984736Swnj 	err:
49934221Sbostic 		sc->sc_nxrec = bdbtofsb(bp->b_blkno);
5004736Swnj 		break;
5014736Swnj 
5024736Swnj 	case MTER_OFFLINE:
5034736Swnj 		if (sc->sc_openf > 0) {
5044736Swnj 			sc->sc_openf = -1;
505*40899Ssklower 			tprintf(sc->sc_ctty, "mu%d: offline\n",
50634221Sbostic 			    MUUNIT(bp->b_dev));
5074736Swnj 		}
5084736Swnj 		bp->b_flags |= B_ERROR;
5094736Swnj 		break;
5104736Swnj 
51117214Smckusick 	case MTER_NOTAVL:
51217214Smckusick 		if (sc->sc_openf > 0) {
51317214Smckusick 			sc->sc_openf = -1;
514*40899Ssklower 			tprintf(sc->sc_ctty, "mu%d: offline (port selector)\n",
51518324Sralph 			    MUUNIT(bp->b_dev));
51617214Smckusick 		}
51717214Smckusick 		bp->b_flags |= B_ERROR;
51817214Smckusick 		break;
51917214Smckusick 
5204736Swnj 	case MTER_FPT:
521*40899Ssklower 		tprintf(sc->sc_ctty, "mu%d: no write ring\n",
52234221Sbostic 		    MUUNIT(bp->b_dev));
5234736Swnj 		bp->b_flags |= B_ERROR;
5244736Swnj 		break;
5254736Swnj 
52617214Smckusick 	case MTER_UNREAD:
52717214Smckusick 		sc->sc_blkno++;
52817214Smckusick 		bp->b_bcount = bp->b_resid;
52917214Smckusick 		bp->b_resid -= MIN(MASKREG(mtaddr->mtbc), bp->b_bcount);
53017214Smckusick 
53134221Sbostic 		/* code 010 means a garbage record, nothing serious. */
53234221Sbostic 		if ((er & MTER_FAILCODE) == (010 << MTER_FSHIFT)) {
533*40899Ssklower 			tprintf(sc->sc_ctty,
53434221Sbostic 			    "mu%d: rn=%d bn=%d unreadable record\n",
53517214Smckusick 			    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno);
53617214Smckusick 			bp->b_flags |= B_ERROR;
53717214Smckusick 			break;
53817214Smckusick 		}
53917214Smckusick 
54034221Sbostic 		/*
54134221Sbostic 		 * Anything else might be a hardware problem,
54234221Sbostic 		 * fall into the error report.
54334221Sbostic 		 */
54417214Smckusick 
5454736Swnj 	default:
54634221Sbostic 		/*
54734221Sbostic 		 * The bits in sc->sc_dsreg are from the last sense
54834221Sbostic 		 * command.  To get the most recent copy, you have to
54934221Sbostic 		 * do a sense at interrupt level, which requires nested
55034221Sbostic 		 * error processing.  This is a bit messy, so leave
55134221Sbostic 		 * well enough alone.
55234221Sbostic 		 */
553*40899Ssklower 		tprintf(sc->sc_ctty, "\
55434221Sbostic mu%d: hard error (data transfer) rn=%d bn=%d mbsr=%b er=0%o ds=%b\n",
55517214Smckusick 		    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
55617214Smckusick 		    mbsr, mbsr_bits, er,
55717214Smckusick 		    MASKREG(sc->sc_dsreg), mtds_bits);
55817214Smckusick #ifdef MTLERRM
55934221Sbostic 		mtintfail(er);
56017214Smckusick #endif
5614736Swnj 		bp->b_flags |= B_ERROR;
56217214Smckusick 
56334221Sbostic 		/*
56434221Sbostic 		 * The TM78 manual says to reset the controller after
56534221Sbostic 		 * TM fault B or MASSBUS fault.
56634221Sbostic 		 */
56734221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
56834221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT)
56917214Smckusick 			mtcreset(mtaddr);
5704736Swnj 	}
57117214Smckusick 
57234221Sbostic 	/*
57334221Sbostic 	 * Just in case some strange error slipped through (drive off
57434221Sbostic 	 * line during read-reverse error recovery comes to mind), make
57534221Sbostic 	 * sure the byte count is reasonable.
57634221Sbostic 	 */
57717214Smckusick 	if (bp->b_bcount < 0)
57817214Smckusick 		bp->b_bcount = bp->b_resid;
57934221Sbostic 
58034221Sbostic 	if ((bp->b_flags & B_ERROR) == 0) {
58134221Sbostic 		/* this counts reverse reads as soft errors */
58234221Sbostic 		sc->sc_blks++;
58334221Sbostic 		if (mi->mi_tab.b_errcnt) /* alternatively, if == 1 */
58434221Sbostic 			sc->sc_softerrs++;
58534221Sbostic 	}
5864736Swnj 	return (MBD_DONE);
5874736Swnj }
5884736Swnj 
5894736Swnj mtndtint(mi)
5904736Swnj 	register struct mba_device *mi;
5914736Swnj {
5924736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
5934736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
5944736Swnj 	register struct mu_softc *sc;
59517214Smckusick 	register int er, fc;
59617214Smckusick 	int unit;
5974736Swnj 
5984736Swnj 	unit = (mtaddr->mtner >> 8) & 3;
5994736Swnj 	er = MASKREG(mtaddr->mtner);
60017214Smckusick 	sc = &mu_softc[unit];
60117214Smckusick 	sc->sc_erreg = er;
60217214Smckusick 
60317214Smckusick 	/* Check for unsolicited interrupts. */
60434221Sbostic 	if (bp == NULL || unit != MUUNIT(bp->b_dev)) {
60534221Sbostic 		if ((er & MTER_INTCODE) == MTER_ONLINE)
60634221Sbostic 			return (MBN_SKIP);
60717214Smckusick 
60834221Sbostic 		printf("mu%d: stray intr (non data transfer) er=0%o ds=%b\n",
60934221Sbostic 		    unit, er, MASKREG(sc->sc_dsreg), mtds_bits);
61017214Smckusick #ifdef MTLERRM
61134221Sbostic 		mtintfail(er);
61217214Smckusick #endif
61334221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
61434221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT) {
61534221Sbostic 			/*
61634221Sbostic 			 * Reset the controller, then set error status
61734221Sbostic 			 * if there was anything active when the fault
61834221Sbostic 			 * occurred.  This may shoot an innocent
61934221Sbostic 			 * bystander, but it's better than letting
62034221Sbostic 			 * an error slip through.
62134221Sbostic 			 */
62234221Sbostic 			mtcreset(mtaddr);
62334221Sbostic 			if (bp != NULL) {
62434221Sbostic 				bp->b_flags |= B_ERROR;
62534221Sbostic 				return (MBN_DONE);
62617214Smckusick 			}
62717214Smckusick 		}
6284736Swnj 		return (MBN_SKIP);
6294736Swnj 	}
63017214Smckusick 
6314736Swnj 	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
6324736Swnj 	sc->sc_resid = fc;
63317214Smckusick 
63434221Sbostic 	/*
63534221Sbostic 	 * Clear the "written" flag after any operation that changes
63634221Sbostic 	 * the position of the tape.
63734221Sbostic 	 */
63834221Sbostic 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)] || bp->b_command != MT_SENSE)
63917214Smckusick 		sc->sc_flags &= ~H_WRITTEN;
64017214Smckusick 
6414736Swnj 	switch (er & MTER_INTCODE) {
64217214Smckusick 
64317214Smckusick 	case MTER_EOT:
64417214Smckusick 		sc->sc_flags |= H_EOT;
64517214Smckusick 		/* fall into MTER_DONE */
64617214Smckusick 
6474736Swnj 	case MTER_DONE:
64817214Smckusick 		/* If this is a command buffer, just update the status.	*/
6494736Swnj 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
6504736Swnj 	done:
6514736Swnj 			if (bp->b_command == MT_SENSE)
6524736Swnj 				sc->sc_dsreg = MASKREG(mtaddr->mtds);
6534736Swnj 			return (MBN_DONE);
6544736Swnj 		}
65517214Smckusick 
65634221Sbostic 		/*
65734221Sbostic 		 * It's not a command buffer, must be a cooked I/O
65834221Sbostic 		 * skip operation (perhaps a shaky assumption, but it
65934221Sbostic 		 * wasn't my idea).
66034221Sbostic 		 */
6617380Ssam 		if ((fc = bdbtofsb(bp->b_blkno) - sc->sc_blkno) < 0)
6626186Ssam 			sc->sc_blkno -= MIN(0377, -fc);
6634736Swnj 		else
6646186Ssam 			sc->sc_blkno += MIN(0377, fc);
6654736Swnj 		return (MBN_RETRY);
6664736Swnj 
66717214Smckusick 	case MTER_ONLINE:		/* ddj -- shouldn't happen but did */
6684736Swnj 	case MTER_RWDING:
6694736Swnj 		return (MBN_SKIP);	/* ignore "rewind started" interrupt */
6704736Swnj 
6714736Swnj 	case MTER_NOTCAP:
672*40899Ssklower 		tprintf(sc->sc_ctty, "mu%d: blank tape\n", MUUNIT(bp->b_dev));
67317214Smckusick 		bp->b_flags |= B_ERROR;
67417214Smckusick 		return (MBN_DONE);
6754736Swnj 
6764736Swnj 	case MTER_TM:
6774736Swnj 	case MTER_LEOT:
67834221Sbostic 		/*
67934221Sbostic 		 * For an ioctl skip operation, count a tape mark as
68034221Sbostic 		 * a record.  If there's anything left to do, update
68134221Sbostic 		 * the repeat count and re-start the command.
68234221Sbostic 		 */
68317214Smckusick 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
68417214Smckusick 			if ((sc->sc_resid = bp->b_repcnt = fc - 1) == 0)
68517214Smckusick 				return (MBN_DONE);
68617214Smckusick 			else
68717214Smckusick 				return (MBN_RETRY);
6884736Swnj 		} else {
68934221Sbostic 			/*
69034221Sbostic 			 * Cooked I/O again.  Just update the books and
69134221Sbostic 			 * wait for someone else to return end of file or
69234221Sbostic 			 * complain about a bad seek.
69334221Sbostic 			 */
69434221Sbostic 			if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
69534221Sbostic 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc - 1;
69634221Sbostic 				sc->sc_blkno = sc->sc_nxrec;
69734221Sbostic 			} else {
69834221Sbostic 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) - fc;
69934221Sbostic 				sc->sc_blkno = sc->sc_nxrec + 1;
70034221Sbostic 			}
7014736Swnj 		}
7024736Swnj 		return (MBN_RETRY);
7034736Swnj 
7044736Swnj 	case MTER_FPT:
705*40899Ssklower 		tprintf(sc->sc_ctty, "mu%d: no write ring\n",
70634221Sbostic 		    MUUNIT(bp->b_dev));
7074736Swnj 		bp->b_flags |= B_ERROR;
7084736Swnj 		return (MBN_DONE);
7094736Swnj 
7104736Swnj 	case MTER_OFFLINE:
71117214Smckusick 		/* If `off line' was intentional, don't complain. */
71234221Sbostic 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)] &&
71334221Sbostic 		    bp->b_command == MT_UNLOAD)
71417214Smckusick 			return(MBN_DONE);
7154736Swnj 		if (sc->sc_openf > 0) {
7164736Swnj 			sc->sc_openf = -1;
717*40899Ssklower 			tprintf(sc->sc_ctty, "mu%d: offline\n",
71834221Sbostic 			    MUUNIT(bp->b_dev));
7194736Swnj 		}
7204736Swnj 		bp->b_flags |= B_ERROR;
7214736Swnj 		return (MBN_DONE);
7224736Swnj 
72317214Smckusick 	case MTER_NOTAVL:
72417214Smckusick 		if (sc->sc_openf > 0) {
72517214Smckusick 			sc->sc_openf = -1;
726*40899Ssklower 			tprintf(sc->sc_ctty, "mu%d: offline (port selector)\n",
72734221Sbostic 			    MUUNIT(bp->b_dev));
72817214Smckusick 		}
72917214Smckusick 		bp->b_flags |= B_ERROR;
73017214Smckusick 		return (MBN_DONE);
73117214Smckusick 
7324736Swnj 	case MTER_BOT:
7334736Swnj 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)])
7344736Swnj 			goto done;
73517214Smckusick 		/* fall through */
73617214Smckusick 
7374736Swnj 	default:
738*40899Ssklower 		tprintf(sc->sc_ctty, "\
73934221Sbostic mu%d: hard error (non data transfer) rn=%d bn=%d er=0%o ds=%b\n",
74017214Smckusick 		    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
74117214Smckusick 		    er, MASKREG(sc->sc_dsreg), mtds_bits);
74217214Smckusick #ifdef MTLERRM
74334221Sbostic 		mtintfail(er);
74417214Smckusick #endif
74534221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
74634221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT)
74717214Smckusick 			mtcreset(mtaddr);	/* reset the controller */
7484736Swnj 		bp->b_flags |= B_ERROR;
7494736Swnj 		return (MBN_DONE);
7504736Swnj 	}
7514736Swnj 	/* NOTREACHED */
7524736Swnj }
7534736Swnj 
75434221Sbostic void
75534221Sbostic mtcreset(mtaddr)
75617214Smckusick 	register struct mtdevice *mtaddr;
75717214Smckusick {
75817214Smckusick 	register int i;
75917214Smckusick 
76017214Smckusick 	mtaddr->mtid = MTID_CLR;		/* reset the TM78 */
76117214Smckusick 	DELAY(200);
76217214Smckusick 	for (i = MTTIMEOUT; i > 0; i--) {
76317214Smckusick 		DELAY(50);			/* don't nag */
76417214Smckusick 		if ((mtaddr->mtid & MTID_RDY) != 0)
76517214Smckusick 			return;			/* exit when ready */
76617214Smckusick 	}
76717214Smckusick 	printf("mt: controller hung\n");
76817214Smckusick }
76917214Smckusick 
7704736Swnj /*ARGSUSED*/
7717637Ssam mtioctl(dev, cmd, data, flag)
7724736Swnj 	dev_t dev;
7734736Swnj 	int cmd;
7747637Ssam 	caddr_t data;
7754736Swnj 	int flag;
7764736Swnj {
7774736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(dev)];
7784736Swnj 	register struct buf *bp = &cmtbuf[MTUNIT(dev)];
77917214Smckusick 	register struct mtop *mtop;
78017214Smckusick 	register struct mtget *mtget;
78117214Smckusick 	int callcount, fcount;
78217214Smckusick 	int op;
78317214Smckusick 
78417214Smckusick 	/* We depend on the values and order of the MT codes here. */
78517214Smckusick 
7864736Swnj 	static mtops[] =
7874736Swnj 	{MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE};
7884736Swnj 
7894736Swnj 	switch (cmd) {
7907637Ssam 
79117214Smckusick 	/* tape operation */
79217214Smckusick 
79317214Smckusick 	case MTIOCTOP:
7948606Sroot 		mtop = (struct mtop *)data;
7958606Sroot 		switch (mtop->mt_op) {
7967637Ssam 
7974736Swnj 		case MTWEOF:
7987637Ssam 			callcount = mtop->mt_count;
7994736Swnj 			fcount = 1;
8004736Swnj 			break;
8017637Ssam 
8024736Swnj 		case MTFSF: case MTBSF:
8037637Ssam 			callcount = mtop->mt_count;
8044736Swnj 			fcount = 1;
8054736Swnj 			break;
8067637Ssam 
8074736Swnj 		case MTFSR: case MTBSR:
8084736Swnj 			callcount = 1;
8097637Ssam 			fcount = mtop->mt_count;
8104736Swnj 			break;
8117637Ssam 
8124736Swnj 		case MTREW: case MTOFFL:
8134736Swnj 			callcount = 1;
8144736Swnj 			fcount = 1;
8154736Swnj 			break;
8167637Ssam 
8174736Swnj 		default:
8188581Sroot 			return (ENXIO);
8194736Swnj 		}
82034221Sbostic 		if (callcount <= 0 || fcount <= 0)
8218581Sroot 			return (EINVAL);
8227637Ssam 		op = mtops[mtop->mt_op];
8234736Swnj 		if (op == MT_WTM)
8244736Swnj 			op |= sc->sc_dens;
8254736Swnj 		while (--callcount >= 0) {
82617214Smckusick 			register int n, fc = fcount;
8274736Swnj 
8284736Swnj 			do {
82917214Smckusick 				n = MIN(fc, 0xff);
8304736Swnj 				mtcommand(dev, op, n);
83117214Smckusick 				n -= sc->sc_resid;
83217214Smckusick 				fc -= n;
83317214Smckusick 				switch (mtop->mt_op) {
83417214Smckusick 
83517214Smckusick 				case MTWEOF:
83617214Smckusick 					sc->sc_blkno += (daddr_t)n;
83717214Smckusick 					sc->sc_nxrec = sc->sc_blkno - 1;
83817214Smckusick 					break;
83917214Smckusick 
84017214Smckusick 				case MTOFFL:
84117214Smckusick 				case MTREW:
84217214Smckusick 				case MTFSF:
84317214Smckusick 					sc->sc_blkno = (daddr_t)0;
84417214Smckusick 					sc->sc_nxrec = (daddr_t)INF;
84517214Smckusick 					break;
84617214Smckusick 
84717214Smckusick 				case MTBSF:
84817214Smckusick 					if (sc->sc_resid) {
84917214Smckusick 						sc->sc_blkno = (daddr_t)0;
85017214Smckusick 						sc->sc_nxrec = (daddr_t)INF;
85117214Smckusick 					} else {
85217214Smckusick 						sc->sc_blkno = (daddr_t)(-1);
85317214Smckusick 						sc->sc_nxrec = (daddr_t)(-1);
85417214Smckusick 					}
85517214Smckusick 					break;
85617214Smckusick 
85717214Smckusick 				case MTFSR:
85817214Smckusick 					sc->sc_blkno += (daddr_t)n;
85917214Smckusick 					break;
86017214Smckusick 
86117214Smckusick 				case MTBSR:
86217214Smckusick 					sc->sc_blkno -= (daddr_t)n;
86317214Smckusick 					break;
86417214Smckusick 				}
86517214Smckusick 				if (sc->sc_resid)
86617214Smckusick 					break;
86717214Smckusick 			} while (fc);
86817214Smckusick 			if (fc) {
86917214Smckusick 				sc->sc_resid = callcount + fc;
87034221Sbostic 				if (mtop->mt_op == MTFSR ||
87134221Sbostic 				    mtop->mt_op == MTBSR)
87217214Smckusick 					return (EIO);
87334221Sbostic 				break;
87417214Smckusick 			}
87517214Smckusick 			if (bp->b_flags & B_ERROR)
8764736Swnj 				break;
8774736Swnj 		}
8788712Sroot 		return (geterror(bp));
8797637Ssam 
88017214Smckusick 	/* tape status */
8814736Swnj 	case MTIOCGET:
8827637Ssam 		mtget = (struct mtget *)data;
8837637Ssam 		mtget->mt_erreg = sc->sc_erreg;
8847637Ssam 		mtget->mt_resid = sc->sc_resid;
8854736Swnj 		mtcommand(dev, MT_SENSE, 1);	/* update drive status */
8867637Ssam 		mtget->mt_dsreg = sc->sc_dsreg;
8877637Ssam 		mtget->mt_type = MT_ISMT;
8888581Sroot 		break;
8897637Ssam 
89017214Smckusick 	/* ignore EOT condition */
89117214Smckusick 	case MTIOCIEOT:
89217214Smckusick 		sc->sc_flags |= H_IEOT;
89317214Smckusick 		break;
89417214Smckusick 
89517214Smckusick 	/* enable EOT condition */
89617214Smckusick 	case MTIOCEEOT:
89717214Smckusick 		sc->sc_flags &= ~H_IEOT;
89817214Smckusick 		break;
89917214Smckusick 
9004736Swnj 	default:
9018581Sroot 		return (ENXIO);
9024736Swnj 	}
9038581Sroot 	return (0);
9044736Swnj }
9054736Swnj 
9064736Swnj #define	DBSIZE	20
9074736Swnj 
9084736Swnj mtdump()
9094736Swnj {
9104736Swnj 	register struct mba_device *mi;
9114736Swnj 	register struct mba_regs *mp;
9124736Swnj 	int blk, num;
9134736Swnj 	int start;
9144736Swnj 
9154736Swnj 	start = 0;
9164736Swnj 	num = maxfree;
9174736Swnj #define	phys(a,b)		((b)((int)(a)&0x7fffffff))
9184736Swnj 	if (mtinfo[0] == 0)
9194736Swnj 		return (ENXIO);
9204736Swnj 	mi = phys(mtinfo[0], struct mba_device *);
9214736Swnj 	mp = phys(mi->mi_hd, struct mba_hd *)->mh_physmba;
9224736Swnj 	mp->mba_cr = MBCR_IE;
9236186Ssam #if lint
9248606Sroot 	blk = 0; num = blk; start = num; blk = start;
9256186Ssam 	return (0);
9266186Ssam #endif
9276186Ssam #ifdef notyet
9284736Swnj 	mtaddr = (struct mtdevice *)&mp->mba_drv[mi->mi_drive];
9294736Swnj 	mtaddr->mttc = MTTC_PDP11|MTTC_1600BPI;
9304736Swnj 	mtaddr->mtcs1 = MT_DCLR|MT_GO;
9314736Swnj 	while (num > 0) {
9324736Swnj 		blk = num > DBSIZE ? DBSIZE : num;
9334736Swnj 		mtdwrite(start, blk, mtaddr, mp);
9344736Swnj 		start += blk;
9354736Swnj 		num -= blk;
9364736Swnj 	}
9374736Swnj 	mteof(mtaddr);
9384736Swnj 	mteof(mtaddr);
9394736Swnj 	mtwait(mtaddr);
9404736Swnj 	if (mtaddr->mtds&MTDS_ERR)
9414736Swnj 		return (EIO);
9424736Swnj 	mtaddr->mtcs1 = MT_REW|MT_GO;
9434736Swnj 	return (0);
9444736Swnj }
9454736Swnj 
9464736Swnj mtdwrite(dbuf, num, mtaddr, mp)
9474736Swnj 	register dbuf, num;
9484736Swnj 	register struct mtdevice *mtaddr;
9494736Swnj 	struct mba_regs *mp;
9504736Swnj {
9514736Swnj 	register struct pte *io;
9524736Swnj 	register int i;
9534736Swnj 
9544736Swnj 	mtwait(mtaddr);
9554736Swnj 	io = mp->mba_map;
9564736Swnj 	for (i = 0; i < num; i++)
9574736Swnj 		*(int *)io++ = dbuf++ | PG_V;
9584736Swnj 	mtaddr->mtfc = -(num*NBPG);
9594736Swnj 	mp->mba_sr = -1;
9604736Swnj 	mp->mba_bcr = -(num*NBPG);
9614736Swnj 	mp->mba_var = 0;
9624736Swnj 	mtaddr->mtcs1 = MT_WCOM|MT_GO;
9634736Swnj }
9644736Swnj 
9654736Swnj mtwait(mtaddr)
9664736Swnj 	struct mtdevice *mtaddr;
9674736Swnj {
9684736Swnj 	register s;
9694736Swnj 
9704736Swnj 	do
9714736Swnj 		s = mtaddr->mtds;
9724736Swnj 	while ((s & MTDS_DRY) == 0);
9734736Swnj }
9744736Swnj 
9754736Swnj mteof(mtaddr)
9764736Swnj 	struct mtdevice *mtaddr;
9774736Swnj {
9784736Swnj 
9794736Swnj 	mtwait(mtaddr);
9804736Swnj 	mtaddr->mtcs1 = MT_WEOF|MT_GO;
9814736Swnj #endif notyet
9824736Swnj }
98317214Smckusick 
98417214Smckusick #ifdef MTLERRM
98534221Sbostic /*
98634221Sbostic  * Failure messages for each failure code, per interrupt code.
98734221Sbostic  * Each table ends with a code of -1 as a default.
98834221Sbostic  */
98934221Sbostic struct fmesg {
99034221Sbostic 	int	f_code;
99134221Sbostic 	char	*f_mesg;
99234221Sbostic };
99317214Smckusick 
99434221Sbostic static char unclass[] = "unclassified failure code";
99517214Smckusick 
99634221Sbostic /* MTER_BOT */
99734221Sbostic static struct fmesg botmsg[] = {
99834221Sbostic 	01,	"tape was at BOT",
99934221Sbostic 	02,	"BOT seen after tape started",
100034221Sbostic 	03,	"ARA ID detected",
100134221Sbostic 	-1,	unclass
100234221Sbostic };
100317214Smckusick 
100434221Sbostic /* MTER_NOTRDY */
100534221Sbostic static struct fmesg notrdymsg[] = {
100634221Sbostic 	01,	"TU on-line but not ready",
100734221Sbostic 	02,	"fatal error has occurred",
100836548Sbostic 	03,	"access allowed but not ready",
100934221Sbostic 	-1,	unclass
101034221Sbostic };
101117214Smckusick 
101234221Sbostic /* MTER_NOTCAP */
101334221Sbostic static struct fmesg notcapmsg[] = {
101434221Sbostic 	01,	"no record found within 25 feet",
101534221Sbostic 	02,	"ID burst neither PE nor GCR",
101634221Sbostic 	03,	"ARA ID not found",
101734221Sbostic 	04,	"no gap found after ID burst",
101834221Sbostic 	-1,	unclass
101934221Sbostic };
102017214Smckusick 
102134221Sbostic /* MTER_LONGREC */
102234221Sbostic static struct fmesg longrecmsg[] = {
102334221Sbostic 	00,	"extended sense data not found",
102434221Sbostic 	01,	"extended sense data updated",
102534221Sbostic 	-1,	unclass
102634221Sbostic };
102717214Smckusick 
102834221Sbostic /* MTER_UNREAD, MTER_ERROR, MTER_EOTERR, MTER_BADTAPE */
102934221Sbostic static struct fmesg code22msg[] = {
103034221Sbostic 	01,	"GCR write error",
103134221Sbostic 	02,	"GCR read error",
103234221Sbostic 	03,	"PE read error",
103334221Sbostic 	04,	"PE write error",
103434221Sbostic 	05,	"at least 1 bit set in ECCSTA",
103534221Sbostic 	06,	"PE write error",
103634221Sbostic 	07,	"GCR write error",
103734221Sbostic 	010,	"RSTAT contains bad code",
103834221Sbostic 	011,	"PE write error",
103934221Sbostic 	012,	"MASSBUS parity error",
104034221Sbostic 	013,	"invalid data transferred",
104134221Sbostic 	-1,	unclass
104234221Sbostic };
104317214Smckusick 
104434221Sbostic /* MTER_TMFLTA */
104534221Sbostic static struct fmesg tmfltamsg[] = {
104634221Sbostic 	01,	"illegal command code",
104734221Sbostic 	02,	"DT command issued when NDT command active",
104834221Sbostic 	03,	"WMC error",
104934221Sbostic 	04,	"RUN not received from MASSBUS controller",
105034221Sbostic 	05,	"mismatch in command read - function routine",
105134221Sbostic 	06,	"ECC ROM parity error",
105234221Sbostic 	07,	"XMC ROM parity error",
105334221Sbostic 	010,	"mismatch in command read - ID burst command",
105434221Sbostic 	011,	"mismatch in command read - verify ARA burst command",
105534221Sbostic 	012,	"mismatch in command read - verify ARA ID command",
105634221Sbostic 	013,	"mismatch in command read - verify gap command",
105734221Sbostic 	014,	"mismatch in command read - read id burst command",
105834221Sbostic 	015,	"mismatch in command read - verify ARA ID command",
105934221Sbostic 	016,	"mismatch in command read - verify gap command",
106034221Sbostic 	017,	"mismatch in command read - find gap command",
106134221Sbostic 	020,	"WMC LEFT failed to set",
106234221Sbostic 	021,	"XL PE set in INTSTA register",
106334221Sbostic 	022,	"XMC DONE did not set",
106434221Sbostic 	023,	"WMC ROM PE or RD PE set in WMCERR register",
106534221Sbostic 	-1,	unclass
106634221Sbostic };
106717214Smckusick 
106834221Sbostic /* MTER_TUFLTA */
106934221Sbostic static struct fmesg tufltamsg[] = {
107034221Sbostic 	01,	"TU status parity error",
107134221Sbostic 	02,	"TU command parity error",
107234221Sbostic 	03,	"rewinding tape went offline",
107334221Sbostic 	04,	"tape went not ready during DSE",
107434221Sbostic 	05,	"TU CMD status changed during DSE",
107534221Sbostic 	06,	"TU never came up to speed",
107634221Sbostic 	07,	"TU velocity changed",
107734221Sbostic 	010,	"TU CMD did not load correctly to start tape motion",
107834221Sbostic 	011,	"TU CMD did not load correctly to set drive density",
107934221Sbostic 	012,	"TU CMD did not load correctly to start tape motion to write BOT ID",
108034221Sbostic 	013,	"TU CMD did not load correctly to backup tape to BOT after failing to write BOT ID",
108134221Sbostic 	014,	"failed to write density ID burst",
108234221Sbostic 	015,	"failed to write ARA burst",
108334221Sbostic 	016,	"failed to write ARA ID",
108434221Sbostic 	017,	"ARA error bit set in MTA status B register",
108534221Sbostic 	021,	"could not find a gap after ID code was written correctly",
108634221Sbostic 	022,	"TU CMD did not load correctly to start tape motion to read ID burst",
108734221Sbostic 	023,	"timeout looking for BOT after detecting ARA ID burst",
108834221Sbostic 	024,	"failed to write tape mark",
108934221Sbostic 	025,	"tape never came up to speed while trying to reposition for retry of writing tape mark",
109034221Sbostic 	026,	"TU CMD did not load correctly to start tape motion in erase gap routine",
109134221Sbostic 	027,	"could not detect a gap in in erase gap routine",
109234221Sbostic 	030,	"could not detect a gap after writing record",
109334221Sbostic 	031,	"read path terminated before entire record was written",
109434221Sbostic 	032,	"could not find a gap after writing record and read path terminated early",
109534221Sbostic 	033,	"TU CMD did not load correctly to backup for retry of write tape mark",
109634221Sbostic 	034,	"TU velocity changed after up to speed while trying to reposition for retry of writing tape mark",
109734221Sbostic 	035,	"TU CMD did not load correctly to backup to retry a load of BOT ID",
109834221Sbostic 	036,	"timeout looking for BOT after failing to write BOT ID",
109934221Sbostic 	037,	"TU velocity changed while writing PE gap before starting to write record",
110034221Sbostic 	040,	"TU CMD did not load correctly to set PE tape density at start of write BOT ID burst",
110134221Sbostic 	041,	"TU CMD did not load correctly to set GCR tape density after writing Density ID",
110234221Sbostic 	042,	"TU CMD did not load correctly to set PE tape density at start of read from BOT",
110334221Sbostic 	043,	"TU CMD did not load correctly to set GCR tape density after reading a GCR Density ID burst",
110434221Sbostic };
110517214Smckusick 
110634221Sbostic /* MTER_TMFLTB */
110734221Sbostic static char inlinetest[] = "inline test failed";
110834221Sbostic static struct fmesg tmfltbmsg[] = {
110934221Sbostic 	00,	"RST0 interrupt occurred with TM RDY set",
111034221Sbostic 	01,	"power failed to interrupt",
111134221Sbostic 	02,	"unknown interrupt on channel 5.5",
111234221Sbostic 	03,	"unknown interrupt on channel 6.5",
111334221Sbostic 	04,	"unknown interrupt on channel 7",
111434221Sbostic 	05,	"unknown interrupt on channel 7.5",
111534221Sbostic 	06,	"CAS contention retry count expired",
111634221Sbostic 	07,	"CAS contention error not retryable",
111734221Sbostic 	010,	"queue error, could not find queue entry",
111834221Sbostic 	011,	"queue entry already full",
111934221Sbostic 	012,	"8085 ROM parity error",
112034221Sbostic 	013,	inlinetest,
112134221Sbostic 	013,	inlinetest,
112234221Sbostic 	014,	inlinetest,
112334221Sbostic 	015,	inlinetest,
112434221Sbostic 	016,	inlinetest,
112534221Sbostic 	017,	inlinetest,
112634221Sbostic 	020,	inlinetest,
112734221Sbostic 	021,	inlinetest,
112834221Sbostic 	022,	inlinetest,
112934221Sbostic 	023,	inlinetest,
113034221Sbostic 	024,	inlinetest,
113134221Sbostic 	025,	inlinetest,
113234221Sbostic 	026,	inlinetest,
113334221Sbostic 	027,	inlinetest,
113434221Sbostic 	030,	inlinetest,
113534221Sbostic 	031,	inlinetest,
113634221Sbostic 	032,	inlinetest,
113734221Sbostic 	033,	inlinetest,
113834221Sbostic 	034,	inlinetest,
113934221Sbostic 	035,	inlinetest,
114034221Sbostic 	036,	inlinetest,
114134221Sbostic 	037,	inlinetest,
114234221Sbostic 	040,	inlinetest,
114334221Sbostic 	041,	inlinetest,
114434221Sbostic 	042,	inlinetest,
114534221Sbostic 	043,	inlinetest,
114634221Sbostic 	044,	inlinetest,
114736548Sbostic 	045,	inlinetest,
114834221Sbostic 	046,	inlinetest,
114934221Sbostic 	047,	inlinetest,
115034221Sbostic 	050,	inlinetest,
115134221Sbostic 	051,	inlinetest,
115234221Sbostic 	052,	inlinetest,
115334221Sbostic 	053,	inlinetest,
115434221Sbostic 	054,	inlinetest,
115534221Sbostic 	055,	inlinetest,
115634221Sbostic 	056,	inlinetest,
115734221Sbostic 	057,	inlinetest,
115834221Sbostic 	-1,	unclass
115934221Sbostic };
116017214Smckusick 
116134221Sbostic /* MTER_MBFLT */
116234221Sbostic static struct fmesg mbfltmsg[] = {
116334221Sbostic 	01,	"control bus parity error",
116434221Sbostic 	02,	"illegal register referenced",
116534221Sbostic 	-1,	unclass
116634221Sbostic };
116717214Smckusick 
116834221Sbostic /*
116934221Sbostic  * MTER_LEOT, MTER_RWDING, NTER_NOTAVL, MTER_NONEX, MTER_KEYFAIL,
117034221Sbostic  * and default: no failure message.
117134221Sbostic  */
117234221Sbostic static struct fmesg nullmsg[] = {
117334221Sbostic 	-1,	""
117434221Sbostic };
117517214Smckusick 
117634221Sbostic /*
117734221Sbostic  * Interrupt code table.
117834221Sbostic  */
117934221Sbostic static struct errmsg {
118034221Sbostic 	int	e_code;
118134221Sbostic 	char	*e_mesg;
118234221Sbostic 	struct	fmesg *e_fmesg;
118334221Sbostic } errmsg[] = {
118434221Sbostic 	MTER_BOT,	"unexpected BOT",	botmsg,
118534221Sbostic 	MTER_LEOT,	"unexpected LEOT",	nullmsg,
118634221Sbostic 	MTER_RWDING,	"tape rewinding",	nullmsg,
118734221Sbostic 	MTER_NOTRDY,	"drive not ready",	notrdymsg,
118834221Sbostic 	MTER_NOTAVL,	"drive not available",	nullmsg,
118934221Sbostic 	MTER_NONEX,	"unit does not exist",	nullmsg,
119034221Sbostic 	MTER_NOTCAP,	"not capable",		notcapmsg,
119134221Sbostic 	MTER_LONGREC,	"long record",		longrecmsg,
119234221Sbostic 	MTER_UNREAD,	"unreadable record",	code22msg,
119334221Sbostic 	MTER_ERROR,	"error",		code22msg,
119434221Sbostic 	MTER_EOTERR,	"EOT error",		code22msg,
119534221Sbostic 	MTER_BADTAPE,	"tape position lost",	code22msg,
119634221Sbostic 	MTER_TMFLTA,	"TM fault A",		tmfltamsg,
119734221Sbostic 	MTER_TUFLTA,	"TU fault A",		tufltamsg,
119834221Sbostic 	MTER_TMFLTB,	"TM fault B",		tmfltbmsg,
119934221Sbostic 	MTER_MBFLT,	"MB fault",		mbfltmsg,
120034221Sbostic 	MTER_KEYFAIL,	"keypad entry error",	nullmsg,
120134221Sbostic 	-1,		"unclassified error",	nullmsg
120234221Sbostic };
120317214Smckusick 
120434221Sbostic /*
120534221Sbostic  * Decode an interrupt-time failure.
120634221Sbostic  */
120734221Sbostic mtintfail(erreg)
120834221Sbostic 	int erreg;
120934221Sbostic {
121034221Sbostic 	register struct errmsg *e;
121134221Sbostic 	register struct fmesg *f;
121234221Sbostic 	register int ecode, fcode;
121317214Smckusick 
121434221Sbostic 	ecode = erreg & MTER_INTCODE;
121534221Sbostic 	fcode = (erreg & MTER_FAILCODE) >> MTER_FSHIFT;
121634221Sbostic 	for (e = errmsg; e->e_code >= 0; e++)
121734221Sbostic 		if (e->e_code == ecode)
121817214Smckusick 			break;
121934221Sbostic 	for (f = e->e_fmesg; f->f_code >= 0; f++)
122034221Sbostic 		if (f->f_code == fcode)
122117214Smckusick 			break;
122234221Sbostic 	printf("    interrupt code = 0%o <%s>\n", ecode, e->e_mesg);
122334221Sbostic 	printf("    failure code = 0%o <%s>\n", fcode, f->f_mesg);
122417214Smckusick }
122534221Sbostic #endif /* MTLERRM */
122634221Sbostic #endif /* NMT > 0 */
1227