xref: /csrg-svn/sys/vax/mba/mt.c (revision 36548)
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*36548Sbostic  *	@(#)mt.c	7.4 (Berkeley) 01/14/89
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 "dir.h"
3117119Sbloom #include "file.h"
3217119Sbloom #include "user.h"
3317119Sbloom #include "map.h"
3417119Sbloom #include "ioctl.h"
3517119Sbloom #include "mtio.h"
3617119Sbloom #include "cmap.h"
3717119Sbloom #include "uio.h"
3818324Sralph #include "tty.h"
3934221Sbostic #include "syslog.h"
404736Swnj 
4134221Sbostic #include "../vax/pte.h"
428471Sroot #include "../vax/cpu.h"
4317119Sbloom #include "mbareg.h"
4417119Sbloom #include "mbavar.h"
4517119Sbloom #include "mtreg.h"
464736Swnj 
4717214Smckusick #define MTTIMEOUT	10000		/* loop limit for controller test */
4817214Smckusick #define	INF		1000000L	/* a block number that won't exist */
4917214Smckusick #define MASKREG(r)	((r) & 0xffff)	/* the control registers have 16 bits */
504736Swnj 
5117214Smckusick /* Bits for sc_flags */
524736Swnj 
5317214Smckusick #define	H_WRITTEN	01		/* last operation was a write */
5417214Smckusick #define H_EOT		02		/* end of tape encountered */
5517214Smckusick #define H_IEOT		04		/* ignore EOT condition */
564736Swnj 
5734221Sbostic int	mt_do_readrev = 1;
5817214Smckusick 
5917214Smckusick /* Per unit status information */
6017214Smckusick 
614736Swnj struct	mu_softc {
6234221Sbostic 	char	sc_openf;	/* unit is open if != 0 */
6334221Sbostic 	char	sc_flags;	/* state flags */
6434221Sbostic 	daddr_t	sc_blkno;	/* current physical block number */
6534221Sbostic 	daddr_t	sc_nxrec;	/* firewall input block number */
6634221Sbostic 	u_short	sc_erreg;	/* copy of mter or mtner */
6734221Sbostic 	u_short	sc_dsreg;	/* copy of mtds */
6834221Sbostic 	short	sc_resid;	/* residual function count for ioctl */
6934221Sbostic 	short	sc_dens;	/* density code - MT_GCR or zero */
7034221Sbostic 	int	sc_i_mtas;	/* mtas at slave attach time */
7134221Sbostic 	int	sc_i_mtner;	/* mtner at slave attach time */
7234221Sbostic 	int	sc_i_mtds;	/* mtds at slave attach time */
7334221Sbostic 	struct	tty *sc_ttyp;	/* record user's tty for errors */
7434221Sbostic 	int	sc_blks;	/* number of I/O operations since open */
7534221Sbostic 	int	sc_softerrs;	/* number of soft I/O errors since open */
764736Swnj } mu_softc[NMU];
774736Swnj 
7817214Smckusick struct	buf	cmtbuf[NMT];		/* tape command buffer structures */
794736Swnj 
8034221Sbostic struct	mba_device *mtinfo[NMT];	/* unit to ctlr structures */
8134221Sbostic struct	mba_slave *muinfo[NMU];		/* unit to slave structures */
8234221Sbostic 
8317214Smckusick char	mtds_bits[] = MTDS_BITS;	/* mtds bit names for error messages */
8417214Smckusick short	mttypes[] = { MBDT_TU78, 0 };
854736Swnj 
8617214Smckusick int	mtattach(), mtslave(), mtustart(), mtstart(), mtndtint(), mtdtint();
8717214Smckusick struct	mba_driver mtdriver =
8817214Smckusick 	{ mtattach, mtslave, mtustart, mtstart, mtdtint, mtndtint,
8917214Smckusick 	  mttypes, "mt", "mu", mtinfo };
9017214Smckusick 
9134221Sbostic /* Bits in minor device */
9234221Sbostic #define	MUUNIT(dev)	(minor(dev)&03)
9334221Sbostic #define	H_NOREWIND	04
9434221Sbostic #define	H_6250BPI	010
9517214Smckusick 
9634221Sbostic #define MTUNIT(dev)	(muinfo[MUUNIT(dev)]->ms_ctlr)
9734221Sbostic 
9834221Sbostic void	mtcreset();
9934221Sbostic 
1004736Swnj /*ARGSUSED*/
1014736Swnj mtattach(mi)
1024736Swnj 	struct mba_device *mi;
1034736Swnj {
10434221Sbostic 
10534221Sbostic 	/* void */
1064736Swnj }
1074736Swnj 
1087431Skre mtslave(mi, ms, sn)
1094736Swnj 	struct mba_device *mi;
1104736Swnj 	struct mba_slave *ms;
1117431Skre 	int sn;
1124736Swnj {
1134736Swnj 	register struct mu_softc *sc = &mu_softc[ms->ms_unit];
1144736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
11526377Skarels 	int s = spl5(), rtn = 0, i;
1164736Swnj 
11734221Sbostic 	/*
11834221Sbostic 	 * Just in case the controller is ill, reset it.  Then issue
11934221Sbostic 	 * a sense operation and wait about a second for it to respond.
12034221Sbostic 	 */
12117214Smckusick 	mtcreset(mtaddr);
1224736Swnj 	mtaddr->mtas = -1;
1237431Skre 	mtaddr->mtncs[sn] = MT_SENSE|MT_GO;
12434221Sbostic 	for (i = MTTIMEOUT; i > 0; i--) {
12517214Smckusick 		DELAY(50);
12617214Smckusick 		if (MASKREG(mtaddr->mtas) != 0)
12717214Smckusick 			break;
12817214Smckusick 	}
12917214Smckusick 	sc->sc_i_mtas = mtaddr->mtas;
13017214Smckusick 	sc->sc_i_mtner = mtaddr->mtner;
13117214Smckusick 	sc->sc_i_mtds = mtaddr->mtds;
13217214Smckusick 
13334221Sbostic 	/*
13434221Sbostic 	 * If no response, whimper.  If wrong response, call it an
13534221Sbostic 	 * unsolicited interrupt and use mtndtint to log and correct.
13634221Sbostic 	 * Otherwise, note whether this slave exists.
13734221Sbostic 	 */
13834221Sbostic 	if (i <= 0)
13917214Smckusick 		printf("mt: controller hung\n");
14034221Sbostic 	else if ((mtaddr->mtner & MTER_INTCODE) != MTER_DONE)
14117214Smckusick 		(void) mtndtint(mi);
14234654Skarels 	else if (mtaddr->mtds & MTDS_PRES) {
14334654Skarels 		muinfo[ms->ms_unit] = ms;
1444736Swnj 		rtn = 1;
14534654Skarels 	}
14617214Smckusick 
14734221Sbostic 	/* cancel the interrupt, then wait a little while for it to go away */
1484736Swnj 	mtaddr->mtas = mtaddr->mtas;
14917214Smckusick 	DELAY(10);
1504736Swnj 	splx(s);
1514736Swnj 	return (rtn);
1524736Swnj }
1534736Swnj 
1544736Swnj mtopen(dev, flag)
1554736Swnj 	dev_t dev;
1564736Swnj 	int flag;
1574736Swnj {
1584736Swnj 	register int muunit;
1594736Swnj 	register struct mu_softc *sc;
16034221Sbostic 	register struct mba_slave *ms;
1614736Swnj 
1624736Swnj 	muunit = MUUNIT(dev);
16334221Sbostic 	if (muunit >= NMU || (ms = muinfo[muunit]) == NULL ||
16434221Sbostic 	    ms->ms_alive == 0 || mtinfo[ms->ms_ctlr]->mi_alive == 0)
1658581Sroot 		return (ENXIO);
16617214Smckusick 	if ((sc = &mu_softc[muunit])->sc_openf)
16717214Smckusick 		return (EBUSY);
16834221Sbostic 	sc->sc_openf = 1;
16917214Smckusick 	sc->sc_dens = (minor(dev) & H_6250BPI) ? MT_GCR : 0;
1704736Swnj 	mtcommand(dev, MT_SENSE, 1);
1714736Swnj 	if ((sc->sc_dsreg & MTDS_ONL) == 0) {
1724736Swnj 		uprintf("mu%d: not online\n", muunit);
17334221Sbostic 		sc->sc_openf = 0;
1748581Sroot 		return (EIO);
1754736Swnj 	}
17617214Smckusick 	if ((sc->sc_dsreg & MTDS_AVAIL) == 0) {
17717214Smckusick 		uprintf("mu%d: not online (port selector)\n", muunit);
17834221Sbostic 		sc->sc_openf = 0;
17917214Smckusick 		return (EIO);
18017214Smckusick 	}
18117214Smckusick 	if ((flag & FWRITE) && (sc->sc_dsreg & MTDS_FPT)) {
1824736Swnj 		uprintf("mu%d: no write ring\n", muunit);
18334221Sbostic 		sc->sc_openf = 0;
1848581Sroot 		return (EIO);
1854736Swnj 	}
18634221Sbostic 	if ((sc->sc_dsreg & MTDS_BOT) == 0 && (flag & FWRITE) &&
18734221Sbostic 	    (sc->sc_dens == MT_GCR) != ((sc->sc_dsreg & MTDS_PE) == 0)) {
1884736Swnj 		uprintf("mu%d: can't change density in mid-tape\n", muunit);
18934221Sbostic 		sc->sc_openf = 0;
1908581Sroot 		return (EIO);
1914736Swnj 	}
1924736Swnj 	sc->sc_blkno = (daddr_t)0;
19317214Smckusick 
19434221Sbostic 	/*
19534221Sbostic 	 * Since cooked I/O may do a read-ahead before a write, trash
19634221Sbostic 	 * on a tape can make the first write fail.  Suppress the first
19734221Sbostic 	 * read-ahead unless definitely doing read-write.
19834221Sbostic 	 */
19934221Sbostic 	sc->sc_nxrec = ((flag & (FTRUNC | FWRITE)) == (FTRUNC | FWRITE)) ?
20034221Sbostic 	    (daddr_t)0 : (daddr_t)INF;
2014736Swnj 	sc->sc_flags = 0;
20234221Sbostic 	sc->sc_blks = 0;
20334221Sbostic 	sc->sc_softerrs = 0;
20418324Sralph 	sc->sc_ttyp = u.u_ttyp;
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;
2234736Swnj }
2244736Swnj 
2254736Swnj mtcommand(dev, com, count)
2264736Swnj 	dev_t dev;
2274736Swnj 	int com, count;
2284736Swnj {
2294736Swnj 	register struct buf *bp;
23034221Sbostic 	int s;
2314736Swnj 
2324736Swnj 	bp = &cmtbuf[MTUNIT(dev)];
2335437Sroot 	s = spl5();
23417214Smckusick 	while (bp->b_flags & B_BUSY) {
23534221Sbostic 		if (bp->b_repcnt == 0 && (bp->b_flags & B_DONE))
2364736Swnj 			break;
2374736Swnj 		bp->b_flags |= B_WANTED;
2384736Swnj 		sleep((caddr_t)bp, PRIBIO);
2394736Swnj 	}
2404736Swnj 	bp->b_flags = B_BUSY|B_READ;
2415437Sroot 	splx(s);
2424736Swnj 	bp->b_dev = dev;
2434736Swnj 	bp->b_command = com;
2444736Swnj 	bp->b_repcnt = count;
2454736Swnj 	bp->b_blkno = 0;
24617214Smckusick 	bp->b_error = 0;
2474736Swnj 	mtstrategy(bp);
2484736Swnj 	if (count == 0)
2494736Swnj 		return;
25034221Sbostic 	biowait(bp);
25117214Smckusick 	if (bp->b_flags & B_WANTED)
2524736Swnj 		wakeup((caddr_t)bp);
2534736Swnj 	bp->b_flags &= B_ERROR;
2544736Swnj }
2554736Swnj 
2564736Swnj mtstrategy(bp)
2574736Swnj 	register struct buf *bp;
2584736Swnj {
2594736Swnj 	register struct buf *dp;
26034221Sbostic 	struct mba_device *mi = mtinfo[MTUNIT(bp->b_dev)];
26134221Sbostic 	int s;
2624736Swnj 
26334221Sbostic 	/*
26434221Sbostic 	 * If this is a data transfer operation, set the resid to a
26534221Sbostic 	 * default value (EOF) to simplify getting it right during
26634221Sbostic 	 * error recovery or bail out.
26734221Sbostic 	 */
26817214Smckusick 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)])
26917214Smckusick 		bp->b_resid = bp->b_bcount;
27017214Smckusick 
27134221Sbostic 	/*
27234221Sbostic 	 * Link this request onto the end of the queue for this
27334221Sbostic 	 * controller, then start I/O if not already active.
27434221Sbostic 	 */
2754736Swnj 	bp->av_forw = NULL;
2764736Swnj 	dp = &mi->mi_tab;
2775437Sroot 	s = spl5();
2784736Swnj 	if (dp->b_actf == NULL)
2794736Swnj 		dp->b_actf = bp;
2804736Swnj 	else
2814736Swnj 		dp->b_actl->av_forw = bp;
2824736Swnj 	dp->b_actl = bp;
2834736Swnj 	if (dp->b_active == 0)
2844736Swnj 		mbustart(mi);
2855437Sroot 	splx(s);
2864736Swnj }
2874736Swnj 
2884736Swnj mtustart(mi)
2894736Swnj 	register struct mba_device *mi;
2904736Swnj {
29117214Smckusick 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
2924736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
2934736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
2944736Swnj 	daddr_t blkno;
29526377Skarels 	int count;
2964736Swnj 
2974736Swnj 	if (sc->sc_openf < 0) {
2984736Swnj 		bp->b_flags |= B_ERROR;
2994736Swnj 		return (MBU_NEXT);
3004736Swnj 	}
3014736Swnj 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)]) {
30234221Sbostic 		/*
30334221Sbostic 		 * Data transfer.  If write at end of tape,
30434221Sbostic 		 * signal "no space" unless suppressed
30534221Sbostic 		 * by MTIOCIEOT.
30634221Sbostic 		 */
30734221Sbostic 		if ((sc->sc_flags & (H_EOT | H_IEOT)) == H_EOT &&
30834221Sbostic 		    (bp->b_flags & B_READ) == 0) {
3094736Swnj 			bp->b_flags |= B_ERROR;
31017214Smckusick 			bp->b_error = ENOSPC;
3114736Swnj 			return (MBU_NEXT);
3124736Swnj 		}
31317214Smckusick 
31434221Sbostic 		if (bp->b_flags & B_RAW) {
31534221Sbostic 			/* raw transfer; never seek */
31634221Sbostic 			sc->sc_blkno = bdbtofsb(bp->b_blkno);
31734221Sbostic 			sc->sc_nxrec = sc->sc_blkno + 1;
31834221Sbostic 		} else {
31917214Smckusick 			/* seek beyond end of file */
32017214Smckusick 			if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) {
32117214Smckusick 				bp->b_flags |= B_ERROR;
32217214Smckusick 				bp->b_error = ENXIO;
32317214Smckusick 				return (MBU_NEXT);
32417214Smckusick 			}
32517214Smckusick 
32634221Sbostic 			/*
32734221Sbostic 			 * This should be end of file, but the buffer
32834221Sbostic 			 * system wants a one-block look-ahead.  Humor it.
32934221Sbostic 			 */
33034221Sbostic 			if (bdbtofsb(bp->b_blkno) == sc->sc_nxrec &&
33134221Sbostic 			    bp->b_flags & B_READ) {
33234221Sbostic 				bp->b_resid = bp->b_bcount;
33317214Smckusick 				clrbuf(bp);
33417214Smckusick 				return (MBU_NEXT);
33517214Smckusick 			}
33617214Smckusick 
33717214Smckusick 			/* If writing, mark the next block invalid. */
33817214Smckusick 			if ((bp->b_flags & B_READ) == 0)
33917214Smckusick 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
3404736Swnj 		}
3414736Swnj 	} else {
34217214Smckusick 		/* It's a command, do it now. */
3434736Swnj 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
3444736Swnj 			(bp->b_repcnt<<8)|bp->b_command|MT_GO;
3454736Swnj 		return (MBU_STARTED);
3464736Swnj 	}
34717214Smckusick 
34834221Sbostic 	/*
34934221Sbostic 	 * If raw I/O, or if the tape is positioned correctly for
35034221Sbostic 	 * cooked I/O, set the byte count, unit number and repeat count
35134221Sbostic 	 * then tell the MASSBUS to proceed.  Note that a negative
35234221Sbostic 	 * bcount tells mbstart to map the buffer for "read backwards".
35334221Sbostic 	 */
35434221Sbostic 	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
3554736Swnj 		if (mi->mi_tab.b_errcnt == 2) {
35634221Sbostic 			mtaddr->mtbc = -bp->b_bcount;
3574736Swnj 			mtaddr->mtca = MUUNIT(bp->b_dev);
3584736Swnj 		} else {
3594736Swnj 			mtaddr->mtbc = bp->b_bcount;
3604736Swnj 			mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);
3614736Swnj 		}
3624736Swnj 		return (MBU_DODATA);
3634736Swnj 	}
36417214Smckusick 
36517214Smckusick 	/* Issue skip operations to position the next block for cooked I/O. */
36617214Smckusick 
3677380Ssam 	if (blkno < bdbtofsb(bp->b_blkno))
36834221Sbostic 		count = bdbtofsb(bp->b_blkno) - blkno;
3694736Swnj 	else
37034221Sbostic 		count = blkno - bdbtofsb(bp->b_blkno);
37134221Sbostic 	if ((unsigned)count > 0377)
37226377Skarels 		count = 0377;
37326377Skarels 	mtaddr->mtncs[MUUNIT(bp->b_dev)] = count | MT_SFORW|MT_GO;
3744736Swnj 	return (MBU_STARTED);
3754736Swnj }
3764736Swnj 
3774736Swnj mtstart(mi)
3784736Swnj 	register struct mba_device *mi;
3794736Swnj {
3804736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
3814736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
3824736Swnj 
3834736Swnj 	if (bp->b_flags & B_READ)
3844736Swnj 		if (mi->mi_tab.b_errcnt == 2)
38534221Sbostic 			return (MT_READREV|MT_GO);
3864736Swnj 		else
38734221Sbostic 			return (MT_READ|MT_GO);
3884736Swnj 	else
38934221Sbostic 		return (MT_WRITE|sc->sc_dens|MT_GO);
3904736Swnj }
3914736Swnj 
3924736Swnj mtdtint(mi, mbsr)
3934736Swnj 	register struct mba_device *mi;
3944736Swnj 	int mbsr;
3954736Swnj {
3964736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
3974736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
3984736Swnj 	register struct mu_softc *sc;
39917214Smckusick 	register int er;
4004736Swnj 
40117214Smckusick 	/* I'M still NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
40217214Smckusick 	if ((mtaddr->mtca & 3) != MUUNIT(bp->b_dev)) {
4034736Swnj 		printf("mt: wrong unit!\n");
4044736Swnj 		mtaddr->mtca = MUUNIT(bp->b_dev);
4054736Swnj 	}
40617214Smckusick 
40717214Smckusick 	er = MASKREG(mtaddr->mter);
4084736Swnj 	sc = &mu_softc[MUUNIT(bp->b_dev)];
40917214Smckusick 	sc->sc_erreg = er;
41017214Smckusick 	if (bp->b_flags & B_READ)
41117214Smckusick 		sc->sc_flags &= ~H_WRITTEN;
41217214Smckusick 	else
4134736Swnj 		sc->sc_flags |= H_WRITTEN;
41417214Smckusick 	switch (er & MTER_INTCODE) {
41517214Smckusick 
41617214Smckusick 	case MTER_EOT:
41717214Smckusick 		sc->sc_flags |= H_EOT;
41817214Smckusick 		/* fall into MTER_DONE */
41917214Smckusick 
4204736Swnj 	case MTER_DONE:
42117214Smckusick 		sc->sc_blkno++;
42217214Smckusick 		if (mi->mi_tab.b_errcnt == 2) {
42317214Smckusick 			bp->b_bcount = bp->b_resid;
42417214Smckusick 			bp->b_resid -= MASKREG(mtaddr->mtbc);
42534221Sbostic 			if (bp->b_resid > 0 && (bp->b_flags & B_RAW) == 0)
42617214Smckusick 				bp->b_flags |= B_ERROR;
42734221Sbostic 		} else
42817214Smckusick 			bp->b_resid = 0;
42917214Smckusick 		break;
43017214Smckusick 
43117214Smckusick 	case MTER_SHRTREC:
43217214Smckusick 		sc->sc_blkno++;
43317214Smckusick 		bp->b_bcount = bp->b_resid;
43417214Smckusick 		bp->b_resid -= MASKREG(mtaddr->mtbc);
43534221Sbostic 		if ((bp->b_flags & B_RAW) == 0)
43617214Smckusick 			bp->b_flags |= B_ERROR;
43717214Smckusick 		break;
43817214Smckusick 
43917214Smckusick 	case MTER_RETRY:
44034221Sbostic 		/*
44134221Sbostic 		 * Simple re-try.  Since resid is always a copy of the
44234221Sbostic 		 * original byte count, use it to restore the count.
44334221Sbostic 		 */
44417214Smckusick 		mi->mi_tab.b_errcnt = 1;
44517214Smckusick 		bp->b_bcount = bp->b_resid;
44634221Sbostic 		return (MBD_RETRY);
44717214Smckusick 
44817214Smckusick 	case MTER_RDOPP:
44934221Sbostic 		/*
45034221Sbostic 		 * The controller just decided to read it backwards.
45134221Sbostic 		 * If the controller returns a byte count of zero,
45234221Sbostic 		 * change it to 1, since zero encodes 65536, which
45334221Sbostic 		 * isn't quite what we had in mind.  The byte count
45434221Sbostic 		 * may be larger than the size of the input buffer, so
45534221Sbostic 		 * limit the count to the buffer size.  After
45634221Sbostic 		 * making the byte count reasonable, set bcount to the
45734221Sbostic 		 * negative of the controller's version of the byte
45834221Sbostic 		 * count so that the start address for the transfer is
45934221Sbostic 		 * set up correctly.
46034221Sbostic 		 */
46117214Smckusick 		if (mt_do_readrev) {
46217214Smckusick 			mi->mi_tab.b_errcnt = 2;
46317214Smckusick 			if ((bp->b_bcount = MASKREG(mtaddr->mtbc)) == 0)
46417214Smckusick 				bp->b_bcount = 1;
46517214Smckusick 			if (bp->b_bcount > bp->b_resid)
46617214Smckusick 				bp->b_bcount = bp->b_resid;
46717214Smckusick 			bp->b_bcount = -(bp->b_bcount);
46817214Smckusick 			return(MBD_RETRY);
46917214Smckusick 		} else if (MASKREG(mtaddr->mtbc) <= bp->b_resid) {
47017214Smckusick 			sc->sc_blkno++;
47117214Smckusick 			bp->b_bcount = bp->b_resid;
47217214Smckusick 			bp->b_resid -= MASKREG(mtaddr->mtbc);
47317214Smckusick 			bp->b_flags |= B_ERROR;
47417214Smckusick 			break;
47517214Smckusick 		}
47617214Smckusick 		bp->b_flags |= B_ERROR;
47717214Smckusick 		/* fall into MTER_LONGREC */
47817214Smckusick 
4794736Swnj 	case MTER_LONGREC:
48017214Smckusick 		sc->sc_blkno++;
48117214Smckusick 		bp->b_bcount = bp->b_resid;
4824736Swnj 		bp->b_resid = 0;
48317214Smckusick 		bp->b_error = ENOMEM;
48417214Smckusick 		bp->b_flags |= B_ERROR;
4854736Swnj 		break;
4864736Swnj 
4874736Swnj 	case MTER_NOTCAP:
4884736Swnj 		printf("mu%d: blank tape\n", MUUNIT(bp->b_dev));
4894736Swnj 		goto err;
4904736Swnj 
4914736Swnj 	case MTER_TM:
49234221Sbostic 		/*
49334221Sbostic 		 * End of file.  Since the default byte count has
49434221Sbostic 		 * already been set, just count the block and proceed.
49534221Sbostic 		 */
4964736Swnj 		sc->sc_blkno++;
4974736Swnj 	err:
49834221Sbostic 		sc->sc_nxrec = bdbtofsb(bp->b_blkno);
4994736Swnj 		break;
5004736Swnj 
5014736Swnj 	case MTER_OFFLINE:
5024736Swnj 		if (sc->sc_openf > 0) {
5034736Swnj 			sc->sc_openf = -1;
50434221Sbostic 			tprintf(sc->sc_ttyp, "mu%d: offline\n",
50534221Sbostic 			    MUUNIT(bp->b_dev));
5064736Swnj 		}
5074736Swnj 		bp->b_flags |= B_ERROR;
5084736Swnj 		break;
5094736Swnj 
51017214Smckusick 	case MTER_NOTAVL:
51117214Smckusick 		if (sc->sc_openf > 0) {
51217214Smckusick 			sc->sc_openf = -1;
51318324Sralph 			tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n",
51418324Sralph 			    MUUNIT(bp->b_dev));
51517214Smckusick 		}
51617214Smckusick 		bp->b_flags |= B_ERROR;
51717214Smckusick 		break;
51817214Smckusick 
5194736Swnj 	case MTER_FPT:
52034221Sbostic 		tprintf(sc->sc_ttyp, "mu%d: no write ring\n",
52134221Sbostic 		    MUUNIT(bp->b_dev));
5224736Swnj 		bp->b_flags |= B_ERROR;
5234736Swnj 		break;
5244736Swnj 
52517214Smckusick 	case MTER_UNREAD:
52617214Smckusick 		sc->sc_blkno++;
52717214Smckusick 		bp->b_bcount = bp->b_resid;
52817214Smckusick 		bp->b_resid -= MIN(MASKREG(mtaddr->mtbc), bp->b_bcount);
52917214Smckusick 
53034221Sbostic 		/* code 010 means a garbage record, nothing serious. */
53134221Sbostic 		if ((er & MTER_FAILCODE) == (010 << MTER_FSHIFT)) {
53234221Sbostic 			tprintf(sc->sc_ttyp,
53334221Sbostic 			    "mu%d: rn=%d bn=%d unreadable record\n",
53417214Smckusick 			    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno);
53517214Smckusick 			bp->b_flags |= B_ERROR;
53617214Smckusick 			break;
53717214Smckusick 		}
53817214Smckusick 
53934221Sbostic 		/*
54034221Sbostic 		 * Anything else might be a hardware problem,
54134221Sbostic 		 * fall into the error report.
54234221Sbostic 		 */
54317214Smckusick 
5444736Swnj 	default:
54534221Sbostic 		/*
54634221Sbostic 		 * The bits in sc->sc_dsreg are from the last sense
54734221Sbostic 		 * command.  To get the most recent copy, you have to
54834221Sbostic 		 * do a sense at interrupt level, which requires nested
54934221Sbostic 		 * error processing.  This is a bit messy, so leave
55034221Sbostic 		 * well enough alone.
55134221Sbostic 		 */
55234221Sbostic 		tprintf(sc->sc_ttyp, "\
55334221Sbostic mu%d: hard error (data transfer) rn=%d bn=%d mbsr=%b er=0%o ds=%b\n",
55417214Smckusick 		    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
55517214Smckusick 		    mbsr, mbsr_bits, er,
55617214Smckusick 		    MASKREG(sc->sc_dsreg), mtds_bits);
55717214Smckusick #ifdef MTLERRM
55834221Sbostic 		mtintfail(er);
55917214Smckusick #endif
5604736Swnj 		bp->b_flags |= B_ERROR;
56117214Smckusick 
56234221Sbostic 		/*
56334221Sbostic 		 * The TM78 manual says to reset the controller after
56434221Sbostic 		 * TM fault B or MASSBUS fault.
56534221Sbostic 		 */
56634221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
56734221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT)
56817214Smckusick 			mtcreset(mtaddr);
5694736Swnj 	}
57017214Smckusick 
57134221Sbostic 	/*
57234221Sbostic 	 * Just in case some strange error slipped through (drive off
57334221Sbostic 	 * line during read-reverse error recovery comes to mind), make
57434221Sbostic 	 * sure the byte count is reasonable.
57534221Sbostic 	 */
57617214Smckusick 	if (bp->b_bcount < 0)
57717214Smckusick 		bp->b_bcount = bp->b_resid;
57834221Sbostic 
57934221Sbostic 	if ((bp->b_flags & B_ERROR) == 0) {
58034221Sbostic 		/* this counts reverse reads as soft errors */
58134221Sbostic 		sc->sc_blks++;
58234221Sbostic 		if (mi->mi_tab.b_errcnt) /* alternatively, if == 1 */
58334221Sbostic 			sc->sc_softerrs++;
58434221Sbostic 	}
5854736Swnj 	return (MBD_DONE);
5864736Swnj }
5874736Swnj 
5884736Swnj mtndtint(mi)
5894736Swnj 	register struct mba_device *mi;
5904736Swnj {
5914736Swnj 	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
5924736Swnj 	register struct buf *bp = mi->mi_tab.b_actf;
5934736Swnj 	register struct mu_softc *sc;
59417214Smckusick 	register int er, fc;
59517214Smckusick 	int unit;
5964736Swnj 
5974736Swnj 	unit = (mtaddr->mtner >> 8) & 3;
5984736Swnj 	er = MASKREG(mtaddr->mtner);
59917214Smckusick 	sc = &mu_softc[unit];
60017214Smckusick 	sc->sc_erreg = er;
60117214Smckusick 
60217214Smckusick 	/* Check for unsolicited interrupts. */
60334221Sbostic 	if (bp == NULL || unit != MUUNIT(bp->b_dev)) {
60434221Sbostic 		if ((er & MTER_INTCODE) == MTER_ONLINE)
60534221Sbostic 			return (MBN_SKIP);
60617214Smckusick 
60734221Sbostic 		printf("mu%d: stray intr (non data transfer) er=0%o ds=%b\n",
60834221Sbostic 		    unit, er, MASKREG(sc->sc_dsreg), mtds_bits);
60917214Smckusick #ifdef MTLERRM
61034221Sbostic 		mtintfail(er);
61117214Smckusick #endif
61234221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
61334221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT) {
61434221Sbostic 			/*
61534221Sbostic 			 * Reset the controller, then set error status
61634221Sbostic 			 * if there was anything active when the fault
61734221Sbostic 			 * occurred.  This may shoot an innocent
61834221Sbostic 			 * bystander, but it's better than letting
61934221Sbostic 			 * an error slip through.
62034221Sbostic 			 */
62134221Sbostic 			mtcreset(mtaddr);
62234221Sbostic 			if (bp != NULL) {
62334221Sbostic 				bp->b_flags |= B_ERROR;
62434221Sbostic 				return (MBN_DONE);
62517214Smckusick 			}
62617214Smckusick 		}
6274736Swnj 		return (MBN_SKIP);
6284736Swnj 	}
62917214Smckusick 
6304736Swnj 	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
6314736Swnj 	sc->sc_resid = fc;
63217214Smckusick 
63334221Sbostic 	/*
63434221Sbostic 	 * Clear the "written" flag after any operation that changes
63534221Sbostic 	 * the position of the tape.
63634221Sbostic 	 */
63734221Sbostic 	if (bp != &cmtbuf[MTUNIT(bp->b_dev)] || bp->b_command != MT_SENSE)
63817214Smckusick 		sc->sc_flags &= ~H_WRITTEN;
63917214Smckusick 
6404736Swnj 	switch (er & MTER_INTCODE) {
64117214Smckusick 
64217214Smckusick 	case MTER_EOT:
64317214Smckusick 		sc->sc_flags |= H_EOT;
64417214Smckusick 		/* fall into MTER_DONE */
64517214Smckusick 
6464736Swnj 	case MTER_DONE:
64717214Smckusick 		/* If this is a command buffer, just update the status.	*/
6484736Swnj 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
6494736Swnj 	done:
6504736Swnj 			if (bp->b_command == MT_SENSE)
6514736Swnj 				sc->sc_dsreg = MASKREG(mtaddr->mtds);
6524736Swnj 			return (MBN_DONE);
6534736Swnj 		}
65417214Smckusick 
65534221Sbostic 		/*
65634221Sbostic 		 * It's not a command buffer, must be a cooked I/O
65734221Sbostic 		 * skip operation (perhaps a shaky assumption, but it
65834221Sbostic 		 * wasn't my idea).
65934221Sbostic 		 */
6607380Ssam 		if ((fc = bdbtofsb(bp->b_blkno) - sc->sc_blkno) < 0)
6616186Ssam 			sc->sc_blkno -= MIN(0377, -fc);
6624736Swnj 		else
6636186Ssam 			sc->sc_blkno += MIN(0377, fc);
6644736Swnj 		return (MBN_RETRY);
6654736Swnj 
66617214Smckusick 	case MTER_ONLINE:		/* ddj -- shouldn't happen but did */
6674736Swnj 	case MTER_RWDING:
6684736Swnj 		return (MBN_SKIP);	/* ignore "rewind started" interrupt */
6694736Swnj 
6704736Swnj 	case MTER_NOTCAP:
67118324Sralph 		tprintf(sc->sc_ttyp, "mu%d: blank tape\n", MUUNIT(bp->b_dev));
67217214Smckusick 		bp->b_flags |= B_ERROR;
67317214Smckusick 		return (MBN_DONE);
6744736Swnj 
6754736Swnj 	case MTER_TM:
6764736Swnj 	case MTER_LEOT:
67734221Sbostic 		/*
67834221Sbostic 		 * For an ioctl skip operation, count a tape mark as
67934221Sbostic 		 * a record.  If there's anything left to do, update
68034221Sbostic 		 * the repeat count and re-start the command.
68134221Sbostic 		 */
68217214Smckusick 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
68317214Smckusick 			if ((sc->sc_resid = bp->b_repcnt = fc - 1) == 0)
68417214Smckusick 				return (MBN_DONE);
68517214Smckusick 			else
68617214Smckusick 				return (MBN_RETRY);
6874736Swnj 		} else {
68834221Sbostic 			/*
68934221Sbostic 			 * Cooked I/O again.  Just update the books and
69034221Sbostic 			 * wait for someone else to return end of file or
69134221Sbostic 			 * complain about a bad seek.
69234221Sbostic 			 */
69334221Sbostic 			if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
69434221Sbostic 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc - 1;
69534221Sbostic 				sc->sc_blkno = sc->sc_nxrec;
69634221Sbostic 			} else {
69734221Sbostic 				sc->sc_nxrec = bdbtofsb(bp->b_blkno) - fc;
69834221Sbostic 				sc->sc_blkno = sc->sc_nxrec + 1;
69934221Sbostic 			}
7004736Swnj 		}
7014736Swnj 		return (MBN_RETRY);
7024736Swnj 
7034736Swnj 	case MTER_FPT:
70434221Sbostic 		tprintf(sc->sc_ttyp, "mu%d: no write ring\n",
70534221Sbostic 		    MUUNIT(bp->b_dev));
7064736Swnj 		bp->b_flags |= B_ERROR;
7074736Swnj 		return (MBN_DONE);
7084736Swnj 
7094736Swnj 	case MTER_OFFLINE:
71017214Smckusick 		/* If `off line' was intentional, don't complain. */
71134221Sbostic 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)] &&
71234221Sbostic 		    bp->b_command == MT_UNLOAD)
71317214Smckusick 			return(MBN_DONE);
7144736Swnj 		if (sc->sc_openf > 0) {
7154736Swnj 			sc->sc_openf = -1;
71634221Sbostic 			tprintf(sc->sc_ttyp, "mu%d: offline\n",
71734221Sbostic 			    MUUNIT(bp->b_dev));
7184736Swnj 		}
7194736Swnj 		bp->b_flags |= B_ERROR;
7204736Swnj 		return (MBN_DONE);
7214736Swnj 
72217214Smckusick 	case MTER_NOTAVL:
72317214Smckusick 		if (sc->sc_openf > 0) {
72417214Smckusick 			sc->sc_openf = -1;
72534221Sbostic 			tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n",
72634221Sbostic 			    MUUNIT(bp->b_dev));
72717214Smckusick 		}
72817214Smckusick 		bp->b_flags |= B_ERROR;
72917214Smckusick 		return (MBN_DONE);
73017214Smckusick 
7314736Swnj 	case MTER_BOT:
7324736Swnj 		if (bp == &cmtbuf[MTUNIT(bp->b_dev)])
7334736Swnj 			goto done;
73417214Smckusick 		/* fall through */
73517214Smckusick 
7364736Swnj 	default:
73734221Sbostic 		tprintf(sc->sc_ttyp, "\
73834221Sbostic mu%d: hard error (non data transfer) rn=%d bn=%d er=0%o ds=%b\n",
73917214Smckusick 		    MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
74017214Smckusick 		    er, MASKREG(sc->sc_dsreg), mtds_bits);
74117214Smckusick #ifdef MTLERRM
74234221Sbostic 		mtintfail(er);
74317214Smckusick #endif
74434221Sbostic 		if ((er & MTER_INTCODE) == MTER_TMFLTB ||
74534221Sbostic 		    (er & MTER_INTCODE) == MTER_MBFLT)
74617214Smckusick 			mtcreset(mtaddr);	/* reset the controller */
7474736Swnj 		bp->b_flags |= B_ERROR;
7484736Swnj 		return (MBN_DONE);
7494736Swnj 	}
7504736Swnj 	/* NOTREACHED */
7514736Swnj }
7524736Swnj 
75334221Sbostic void
75434221Sbostic mtcreset(mtaddr)
75517214Smckusick 	register struct mtdevice *mtaddr;
75617214Smckusick {
75717214Smckusick 	register int i;
75817214Smckusick 
75917214Smckusick 	mtaddr->mtid = MTID_CLR;		/* reset the TM78 */
76017214Smckusick 	DELAY(200);
76117214Smckusick 	for (i = MTTIMEOUT; i > 0; i--) {
76217214Smckusick 		DELAY(50);			/* don't nag */
76317214Smckusick 		if ((mtaddr->mtid & MTID_RDY) != 0)
76417214Smckusick 			return;			/* exit when ready */
76517214Smckusick 	}
76617214Smckusick 	printf("mt: controller hung\n");
76717214Smckusick }
76817214Smckusick 
7694736Swnj /*ARGSUSED*/
7707637Ssam mtioctl(dev, cmd, data, flag)
7714736Swnj 	dev_t dev;
7724736Swnj 	int cmd;
7737637Ssam 	caddr_t data;
7744736Swnj 	int flag;
7754736Swnj {
7764736Swnj 	register struct mu_softc *sc = &mu_softc[MUUNIT(dev)];
7774736Swnj 	register struct buf *bp = &cmtbuf[MTUNIT(dev)];
77817214Smckusick 	register struct mtop *mtop;
77917214Smckusick 	register struct mtget *mtget;
78017214Smckusick 	int callcount, fcount;
78117214Smckusick 	int op;
78217214Smckusick 
78317214Smckusick 	/* We depend on the values and order of the MT codes here. */
78417214Smckusick 
7854736Swnj 	static mtops[] =
7864736Swnj 	{MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE};
7874736Swnj 
7884736Swnj 	switch (cmd) {
7897637Ssam 
79017214Smckusick 	/* tape operation */
79117214Smckusick 
79217214Smckusick 	case MTIOCTOP:
7938606Sroot 		mtop = (struct mtop *)data;
7948606Sroot 		switch (mtop->mt_op) {
7957637Ssam 
7964736Swnj 		case MTWEOF:
7977637Ssam 			callcount = mtop->mt_count;
7984736Swnj 			fcount = 1;
7994736Swnj 			break;
8007637Ssam 
8014736Swnj 		case MTFSF: case MTBSF:
8027637Ssam 			callcount = mtop->mt_count;
8034736Swnj 			fcount = 1;
8044736Swnj 			break;
8057637Ssam 
8064736Swnj 		case MTFSR: case MTBSR:
8074736Swnj 			callcount = 1;
8087637Ssam 			fcount = mtop->mt_count;
8094736Swnj 			break;
8107637Ssam 
8114736Swnj 		case MTREW: case MTOFFL:
8124736Swnj 			callcount = 1;
8134736Swnj 			fcount = 1;
8144736Swnj 			break;
8157637Ssam 
8164736Swnj 		default:
8178581Sroot 			return (ENXIO);
8184736Swnj 		}
81934221Sbostic 		if (callcount <= 0 || fcount <= 0)
8208581Sroot 			return (EINVAL);
8217637Ssam 		op = mtops[mtop->mt_op];
8224736Swnj 		if (op == MT_WTM)
8234736Swnj 			op |= sc->sc_dens;
8244736Swnj 		while (--callcount >= 0) {
82517214Smckusick 			register int n, fc = fcount;
8264736Swnj 
8274736Swnj 			do {
82817214Smckusick 				n = MIN(fc, 0xff);
8294736Swnj 				mtcommand(dev, op, n);
83017214Smckusick 				n -= sc->sc_resid;
83117214Smckusick 				fc -= n;
83217214Smckusick 				switch (mtop->mt_op) {
83317214Smckusick 
83417214Smckusick 				case MTWEOF:
83517214Smckusick 					sc->sc_blkno += (daddr_t)n;
83617214Smckusick 					sc->sc_nxrec = sc->sc_blkno - 1;
83717214Smckusick 					break;
83817214Smckusick 
83917214Smckusick 				case MTOFFL:
84017214Smckusick 				case MTREW:
84117214Smckusick 				case MTFSF:
84217214Smckusick 					sc->sc_blkno = (daddr_t)0;
84317214Smckusick 					sc->sc_nxrec = (daddr_t)INF;
84417214Smckusick 					break;
84517214Smckusick 
84617214Smckusick 				case MTBSF:
84717214Smckusick 					if (sc->sc_resid) {
84817214Smckusick 						sc->sc_blkno = (daddr_t)0;
84917214Smckusick 						sc->sc_nxrec = (daddr_t)INF;
85017214Smckusick 					} else {
85117214Smckusick 						sc->sc_blkno = (daddr_t)(-1);
85217214Smckusick 						sc->sc_nxrec = (daddr_t)(-1);
85317214Smckusick 					}
85417214Smckusick 					break;
85517214Smckusick 
85617214Smckusick 				case MTFSR:
85717214Smckusick 					sc->sc_blkno += (daddr_t)n;
85817214Smckusick 					break;
85917214Smckusick 
86017214Smckusick 				case MTBSR:
86117214Smckusick 					sc->sc_blkno -= (daddr_t)n;
86217214Smckusick 					break;
86317214Smckusick 				}
86417214Smckusick 				if (sc->sc_resid)
86517214Smckusick 					break;
86617214Smckusick 			} while (fc);
86717214Smckusick 			if (fc) {
86817214Smckusick 				sc->sc_resid = callcount + fc;
86934221Sbostic 				if (mtop->mt_op == MTFSR ||
87034221Sbostic 				    mtop->mt_op == MTBSR)
87117214Smckusick 					return (EIO);
87234221Sbostic 				break;
87317214Smckusick 			}
87417214Smckusick 			if (bp->b_flags & B_ERROR)
8754736Swnj 				break;
8764736Swnj 		}
8778712Sroot 		return (geterror(bp));
8787637Ssam 
87917214Smckusick 	/* tape status */
8804736Swnj 	case MTIOCGET:
8817637Ssam 		mtget = (struct mtget *)data;
8827637Ssam 		mtget->mt_erreg = sc->sc_erreg;
8837637Ssam 		mtget->mt_resid = sc->sc_resid;
8844736Swnj 		mtcommand(dev, MT_SENSE, 1);	/* update drive status */
8857637Ssam 		mtget->mt_dsreg = sc->sc_dsreg;
8867637Ssam 		mtget->mt_type = MT_ISMT;
8878581Sroot 		break;
8887637Ssam 
88917214Smckusick 	/* ignore EOT condition */
89017214Smckusick 	case MTIOCIEOT:
89117214Smckusick 		sc->sc_flags |= H_IEOT;
89217214Smckusick 		break;
89317214Smckusick 
89417214Smckusick 	/* enable EOT condition */
89517214Smckusick 	case MTIOCEEOT:
89617214Smckusick 		sc->sc_flags &= ~H_IEOT;
89717214Smckusick 		break;
89817214Smckusick 
8994736Swnj 	default:
9008581Sroot 		return (ENXIO);
9014736Swnj 	}
9028581Sroot 	return (0);
9034736Swnj }
9044736Swnj 
9054736Swnj #define	DBSIZE	20
9064736Swnj 
9074736Swnj mtdump()
9084736Swnj {
9094736Swnj 	register struct mba_device *mi;
9104736Swnj 	register struct mba_regs *mp;
9114736Swnj 	int blk, num;
9124736Swnj 	int start;
9134736Swnj 
9144736Swnj 	start = 0;
9154736Swnj 	num = maxfree;
9164736Swnj #define	phys(a,b)		((b)((int)(a)&0x7fffffff))
9174736Swnj 	if (mtinfo[0] == 0)
9184736Swnj 		return (ENXIO);
9194736Swnj 	mi = phys(mtinfo[0], struct mba_device *);
9204736Swnj 	mp = phys(mi->mi_hd, struct mba_hd *)->mh_physmba;
9214736Swnj 	mp->mba_cr = MBCR_IE;
9226186Ssam #if lint
9238606Sroot 	blk = 0; num = blk; start = num; blk = start;
9246186Ssam 	return (0);
9256186Ssam #endif
9266186Ssam #ifdef notyet
9274736Swnj 	mtaddr = (struct mtdevice *)&mp->mba_drv[mi->mi_drive];
9284736Swnj 	mtaddr->mttc = MTTC_PDP11|MTTC_1600BPI;
9294736Swnj 	mtaddr->mtcs1 = MT_DCLR|MT_GO;
9304736Swnj 	while (num > 0) {
9314736Swnj 		blk = num > DBSIZE ? DBSIZE : num;
9324736Swnj 		mtdwrite(start, blk, mtaddr, mp);
9334736Swnj 		start += blk;
9344736Swnj 		num -= blk;
9354736Swnj 	}
9364736Swnj 	mteof(mtaddr);
9374736Swnj 	mteof(mtaddr);
9384736Swnj 	mtwait(mtaddr);
9394736Swnj 	if (mtaddr->mtds&MTDS_ERR)
9404736Swnj 		return (EIO);
9414736Swnj 	mtaddr->mtcs1 = MT_REW|MT_GO;
9424736Swnj 	return (0);
9434736Swnj }
9444736Swnj 
9454736Swnj mtdwrite(dbuf, num, mtaddr, mp)
9464736Swnj 	register dbuf, num;
9474736Swnj 	register struct mtdevice *mtaddr;
9484736Swnj 	struct mba_regs *mp;
9494736Swnj {
9504736Swnj 	register struct pte *io;
9514736Swnj 	register int i;
9524736Swnj 
9534736Swnj 	mtwait(mtaddr);
9544736Swnj 	io = mp->mba_map;
9554736Swnj 	for (i = 0; i < num; i++)
9564736Swnj 		*(int *)io++ = dbuf++ | PG_V;
9574736Swnj 	mtaddr->mtfc = -(num*NBPG);
9584736Swnj 	mp->mba_sr = -1;
9594736Swnj 	mp->mba_bcr = -(num*NBPG);
9604736Swnj 	mp->mba_var = 0;
9614736Swnj 	mtaddr->mtcs1 = MT_WCOM|MT_GO;
9624736Swnj }
9634736Swnj 
9644736Swnj mtwait(mtaddr)
9654736Swnj 	struct mtdevice *mtaddr;
9664736Swnj {
9674736Swnj 	register s;
9684736Swnj 
9694736Swnj 	do
9704736Swnj 		s = mtaddr->mtds;
9714736Swnj 	while ((s & MTDS_DRY) == 0);
9724736Swnj }
9734736Swnj 
9744736Swnj mteof(mtaddr)
9754736Swnj 	struct mtdevice *mtaddr;
9764736Swnj {
9774736Swnj 
9784736Swnj 	mtwait(mtaddr);
9794736Swnj 	mtaddr->mtcs1 = MT_WEOF|MT_GO;
9804736Swnj #endif notyet
9814736Swnj }
98217214Smckusick 
98317214Smckusick #ifdef MTLERRM
98434221Sbostic /*
98534221Sbostic  * Failure messages for each failure code, per interrupt code.
98634221Sbostic  * Each table ends with a code of -1 as a default.
98734221Sbostic  */
98834221Sbostic struct fmesg {
98934221Sbostic 	int	f_code;
99034221Sbostic 	char	*f_mesg;
99134221Sbostic };
99217214Smckusick 
99334221Sbostic static char unclass[] = "unclassified failure code";
99417214Smckusick 
99534221Sbostic /* MTER_BOT */
99634221Sbostic static struct fmesg botmsg[] = {
99734221Sbostic 	01,	"tape was at BOT",
99834221Sbostic 	02,	"BOT seen after tape started",
99934221Sbostic 	03,	"ARA ID detected",
100034221Sbostic 	-1,	unclass
100134221Sbostic };
100217214Smckusick 
100334221Sbostic /* MTER_NOTRDY */
100434221Sbostic static struct fmesg notrdymsg[] = {
100534221Sbostic 	01,	"TU on-line but not ready",
100634221Sbostic 	02,	"fatal error has occurred",
1007*36548Sbostic 	03,	"access allowed but not ready",
100834221Sbostic 	-1,	unclass
100934221Sbostic };
101017214Smckusick 
101134221Sbostic /* MTER_NOTCAP */
101234221Sbostic static struct fmesg notcapmsg[] = {
101334221Sbostic 	01,	"no record found within 25 feet",
101434221Sbostic 	02,	"ID burst neither PE nor GCR",
101534221Sbostic 	03,	"ARA ID not found",
101634221Sbostic 	04,	"no gap found after ID burst",
101734221Sbostic 	-1,	unclass
101834221Sbostic };
101917214Smckusick 
102034221Sbostic /* MTER_LONGREC */
102134221Sbostic static struct fmesg longrecmsg[] = {
102234221Sbostic 	00,	"extended sense data not found",
102334221Sbostic 	01,	"extended sense data updated",
102434221Sbostic 	-1,	unclass
102534221Sbostic };
102617214Smckusick 
102734221Sbostic /* MTER_UNREAD, MTER_ERROR, MTER_EOTERR, MTER_BADTAPE */
102834221Sbostic static struct fmesg code22msg[] = {
102934221Sbostic 	01,	"GCR write error",
103034221Sbostic 	02,	"GCR read error",
103134221Sbostic 	03,	"PE read error",
103234221Sbostic 	04,	"PE write error",
103334221Sbostic 	05,	"at least 1 bit set in ECCSTA",
103434221Sbostic 	06,	"PE write error",
103534221Sbostic 	07,	"GCR write error",
103634221Sbostic 	010,	"RSTAT contains bad code",
103734221Sbostic 	011,	"PE write error",
103834221Sbostic 	012,	"MASSBUS parity error",
103934221Sbostic 	013,	"invalid data transferred",
104034221Sbostic 	-1,	unclass
104134221Sbostic };
104217214Smckusick 
104334221Sbostic /* MTER_TMFLTA */
104434221Sbostic static struct fmesg tmfltamsg[] = {
104534221Sbostic 	01,	"illegal command code",
104634221Sbostic 	02,	"DT command issued when NDT command active",
104734221Sbostic 	03,	"WMC error",
104834221Sbostic 	04,	"RUN not received from MASSBUS controller",
104934221Sbostic 	05,	"mismatch in command read - function routine",
105034221Sbostic 	06,	"ECC ROM parity error",
105134221Sbostic 	07,	"XMC ROM parity error",
105234221Sbostic 	010,	"mismatch in command read - ID burst command",
105334221Sbostic 	011,	"mismatch in command read - verify ARA burst command",
105434221Sbostic 	012,	"mismatch in command read - verify ARA ID command",
105534221Sbostic 	013,	"mismatch in command read - verify gap command",
105634221Sbostic 	014,	"mismatch in command read - read id burst command",
105734221Sbostic 	015,	"mismatch in command read - verify ARA ID command",
105834221Sbostic 	016,	"mismatch in command read - verify gap command",
105934221Sbostic 	017,	"mismatch in command read - find gap command",
106034221Sbostic 	020,	"WMC LEFT failed to set",
106134221Sbostic 	021,	"XL PE set in INTSTA register",
106234221Sbostic 	022,	"XMC DONE did not set",
106334221Sbostic 	023,	"WMC ROM PE or RD PE set in WMCERR register",
106434221Sbostic 	-1,	unclass
106534221Sbostic };
106617214Smckusick 
106734221Sbostic /* MTER_TUFLTA */
106834221Sbostic static struct fmesg tufltamsg[] = {
106934221Sbostic 	01,	"TU status parity error",
107034221Sbostic 	02,	"TU command parity error",
107134221Sbostic 	03,	"rewinding tape went offline",
107234221Sbostic 	04,	"tape went not ready during DSE",
107334221Sbostic 	05,	"TU CMD status changed during DSE",
107434221Sbostic 	06,	"TU never came up to speed",
107534221Sbostic 	07,	"TU velocity changed",
107634221Sbostic 	010,	"TU CMD did not load correctly to start tape motion",
107734221Sbostic 	011,	"TU CMD did not load correctly to set drive density",
107834221Sbostic 	012,	"TU CMD did not load correctly to start tape motion to write BOT ID",
107934221Sbostic 	013,	"TU CMD did not load correctly to backup tape to BOT after failing to write BOT ID",
108034221Sbostic 	014,	"failed to write density ID burst",
108134221Sbostic 	015,	"failed to write ARA burst",
108234221Sbostic 	016,	"failed to write ARA ID",
108334221Sbostic 	017,	"ARA error bit set in MTA status B register",
108434221Sbostic 	021,	"could not find a gap after ID code was written correctly",
108534221Sbostic 	022,	"TU CMD did not load correctly to start tape motion to read ID burst",
108634221Sbostic 	023,	"timeout looking for BOT after detecting ARA ID burst",
108734221Sbostic 	024,	"failed to write tape mark",
108834221Sbostic 	025,	"tape never came up to speed while trying to reposition for retry of writing tape mark",
108934221Sbostic 	026,	"TU CMD did not load correctly to start tape motion in erase gap routine",
109034221Sbostic 	027,	"could not detect a gap in in erase gap routine",
109134221Sbostic 	030,	"could not detect a gap after writing record",
109234221Sbostic 	031,	"read path terminated before entire record was written",
109334221Sbostic 	032,	"could not find a gap after writing record and read path terminated early",
109434221Sbostic 	033,	"TU CMD did not load correctly to backup for retry of write tape mark",
109534221Sbostic 	034,	"TU velocity changed after up to speed while trying to reposition for retry of writing tape mark",
109634221Sbostic 	035,	"TU CMD did not load correctly to backup to retry a load of BOT ID",
109734221Sbostic 	036,	"timeout looking for BOT after failing to write BOT ID",
109834221Sbostic 	037,	"TU velocity changed while writing PE gap before starting to write record",
109934221Sbostic 	040,	"TU CMD did not load correctly to set PE tape density at start of write BOT ID burst",
110034221Sbostic 	041,	"TU CMD did not load correctly to set GCR tape density after writing Density ID",
110134221Sbostic 	042,	"TU CMD did not load correctly to set PE tape density at start of read from BOT",
110234221Sbostic 	043,	"TU CMD did not load correctly to set GCR tape density after reading a GCR Density ID burst",
110334221Sbostic };
110417214Smckusick 
110534221Sbostic /* MTER_TMFLTB */
110634221Sbostic static char inlinetest[] = "inline test failed";
110734221Sbostic static struct fmesg tmfltbmsg[] = {
110834221Sbostic 	00,	"RST0 interrupt occurred with TM RDY set",
110934221Sbostic 	01,	"power failed to interrupt",
111034221Sbostic 	02,	"unknown interrupt on channel 5.5",
111134221Sbostic 	03,	"unknown interrupt on channel 6.5",
111234221Sbostic 	04,	"unknown interrupt on channel 7",
111334221Sbostic 	05,	"unknown interrupt on channel 7.5",
111434221Sbostic 	06,	"CAS contention retry count expired",
111534221Sbostic 	07,	"CAS contention error not retryable",
111634221Sbostic 	010,	"queue error, could not find queue entry",
111734221Sbostic 	011,	"queue entry already full",
111834221Sbostic 	012,	"8085 ROM parity error",
111934221Sbostic 	013,	inlinetest,
112034221Sbostic 	013,	inlinetest,
112134221Sbostic 	014,	inlinetest,
112234221Sbostic 	015,	inlinetest,
112334221Sbostic 	016,	inlinetest,
112434221Sbostic 	017,	inlinetest,
112534221Sbostic 	020,	inlinetest,
112634221Sbostic 	021,	inlinetest,
112734221Sbostic 	022,	inlinetest,
112834221Sbostic 	023,	inlinetest,
112934221Sbostic 	024,	inlinetest,
113034221Sbostic 	025,	inlinetest,
113134221Sbostic 	026,	inlinetest,
113234221Sbostic 	027,	inlinetest,
113334221Sbostic 	030,	inlinetest,
113434221Sbostic 	031,	inlinetest,
113534221Sbostic 	032,	inlinetest,
113634221Sbostic 	033,	inlinetest,
113734221Sbostic 	034,	inlinetest,
113834221Sbostic 	035,	inlinetest,
113934221Sbostic 	036,	inlinetest,
114034221Sbostic 	037,	inlinetest,
114134221Sbostic 	040,	inlinetest,
114234221Sbostic 	041,	inlinetest,
114334221Sbostic 	042,	inlinetest,
114434221Sbostic 	043,	inlinetest,
114534221Sbostic 	044,	inlinetest,
1146*36548Sbostic 	045,	inlinetest,
114734221Sbostic 	046,	inlinetest,
114834221Sbostic 	047,	inlinetest,
114934221Sbostic 	050,	inlinetest,
115034221Sbostic 	051,	inlinetest,
115134221Sbostic 	052,	inlinetest,
115234221Sbostic 	053,	inlinetest,
115334221Sbostic 	054,	inlinetest,
115434221Sbostic 	055,	inlinetest,
115534221Sbostic 	056,	inlinetest,
115634221Sbostic 	057,	inlinetest,
115734221Sbostic 	-1,	unclass
115834221Sbostic };
115917214Smckusick 
116034221Sbostic /* MTER_MBFLT */
116134221Sbostic static struct fmesg mbfltmsg[] = {
116234221Sbostic 	01,	"control bus parity error",
116334221Sbostic 	02,	"illegal register referenced",
116434221Sbostic 	-1,	unclass
116534221Sbostic };
116617214Smckusick 
116734221Sbostic /*
116834221Sbostic  * MTER_LEOT, MTER_RWDING, NTER_NOTAVL, MTER_NONEX, MTER_KEYFAIL,
116934221Sbostic  * and default: no failure message.
117034221Sbostic  */
117134221Sbostic static struct fmesg nullmsg[] = {
117234221Sbostic 	-1,	""
117334221Sbostic };
117417214Smckusick 
117534221Sbostic /*
117634221Sbostic  * Interrupt code table.
117734221Sbostic  */
117834221Sbostic static struct errmsg {
117934221Sbostic 	int	e_code;
118034221Sbostic 	char	*e_mesg;
118134221Sbostic 	struct	fmesg *e_fmesg;
118234221Sbostic } errmsg[] = {
118334221Sbostic 	MTER_BOT,	"unexpected BOT",	botmsg,
118434221Sbostic 	MTER_LEOT,	"unexpected LEOT",	nullmsg,
118534221Sbostic 	MTER_RWDING,	"tape rewinding",	nullmsg,
118634221Sbostic 	MTER_NOTRDY,	"drive not ready",	notrdymsg,
118734221Sbostic 	MTER_NOTAVL,	"drive not available",	nullmsg,
118834221Sbostic 	MTER_NONEX,	"unit does not exist",	nullmsg,
118934221Sbostic 	MTER_NOTCAP,	"not capable",		notcapmsg,
119034221Sbostic 	MTER_LONGREC,	"long record",		longrecmsg,
119134221Sbostic 	MTER_UNREAD,	"unreadable record",	code22msg,
119234221Sbostic 	MTER_ERROR,	"error",		code22msg,
119334221Sbostic 	MTER_EOTERR,	"EOT error",		code22msg,
119434221Sbostic 	MTER_BADTAPE,	"tape position lost",	code22msg,
119534221Sbostic 	MTER_TMFLTA,	"TM fault A",		tmfltamsg,
119634221Sbostic 	MTER_TUFLTA,	"TU fault A",		tufltamsg,
119734221Sbostic 	MTER_TMFLTB,	"TM fault B",		tmfltbmsg,
119834221Sbostic 	MTER_MBFLT,	"MB fault",		mbfltmsg,
119934221Sbostic 	MTER_KEYFAIL,	"keypad entry error",	nullmsg,
120034221Sbostic 	-1,		"unclassified error",	nullmsg
120134221Sbostic };
120217214Smckusick 
120334221Sbostic /*
120434221Sbostic  * Decode an interrupt-time failure.
120534221Sbostic  */
120634221Sbostic mtintfail(erreg)
120734221Sbostic 	int erreg;
120834221Sbostic {
120934221Sbostic 	register struct errmsg *e;
121034221Sbostic 	register struct fmesg *f;
121134221Sbostic 	register int ecode, fcode;
121217214Smckusick 
121334221Sbostic 	ecode = erreg & MTER_INTCODE;
121434221Sbostic 	fcode = (erreg & MTER_FAILCODE) >> MTER_FSHIFT;
121534221Sbostic 	for (e = errmsg; e->e_code >= 0; e++)
121634221Sbostic 		if (e->e_code == ecode)
121717214Smckusick 			break;
121834221Sbostic 	for (f = e->e_fmesg; f->f_code >= 0; f++)
121934221Sbostic 		if (f->f_code == fcode)
122017214Smckusick 			break;
122134221Sbostic 	printf("    interrupt code = 0%o <%s>\n", ecode, e->e_mesg);
122234221Sbostic 	printf("    failure code = 0%o <%s>\n", fcode, f->f_mesg);
122317214Smckusick }
122434221Sbostic #endif /* MTLERRM */
122534221Sbostic #endif /* NMT > 0 */
1226