xref: /csrg-svn/sys/vax/uba/ut.c (revision 4746)
1*4746Ssam /*	ut.c	4.2	81/11/06	*/
24744Swnj 
34744Swnj #include "ut.h"
44744Swnj #if NUT > 0
54744Swnj #define UTDEBUG	1
64744Swnj /*
74744Swnj  * System Industries Model 9700 Tape Drive
84744Swnj  *   emulates a TU45 on the UNIBUS
94744Swnj  *
104744Swnj  * TODO:
114744Swnj  *	check out attention processing
124744Swnj  *	try reset code and dump code
134744Swnj  */
144744Swnj #include "../h/param.h"
154744Swnj #include "../h/systm.h"
164744Swnj #include "../h/buf.h"
174744Swnj #include "../h/conf.h"
184744Swnj #include "../h/dir.h"
194744Swnj #include "../h/file.h"
204744Swnj #include "../h/user.h"
214744Swnj #include "../h/map.h"
224744Swnj #include "../h/pte.h"
234744Swnj #include "../h/ubareg.h"
244744Swnj #include "../h/ubavar.h"
254744Swnj #include "../h/mtio.h"
264744Swnj #include "../h/ioctl.h"
274744Swnj #include "../h/cmap.h"
284744Swnj #include "../h/cpu.h"
294744Swnj 
304744Swnj #include "../h/utreg.h"
314744Swnj 
324744Swnj struct	buf	rutbuf[NUT];	/* bufs for raw i/o */
334744Swnj struct	buf	cutbuf[NUT];	/* bufs for control operations */
344744Swnj struct	buf	tjutab[NTJ];	/* bufs for slave queue headers */
354744Swnj 
364744Swnj struct uba_ctlr *utminfo[NUT];
374744Swnj struct uba_device *tjdinfo[NTJ];
384744Swnj int utprobe(), utslave(), utattach(), utdgo();
394744Swnj u_short utstd[] = { 0772440, 0 };
404744Swnj struct uba_driver utdriver =
414744Swnj   { utprobe, utslave, utattach, utdgo, utstd, "tj", tjdinfo, "ut", utminfo, 0 };
424744Swnj 
434744Swnj /* bits in minor device */
444744Swnj #define	TJUNIT(dev)	(minor(dev)&03)
454744Swnj #define	T_NOREWIND	04
464744Swnj #define	T_1600BPI	010
474744Swnj #define	T_6250BPI	020
484744Swnj short	utdens[] = { UT_NRZI, UT_PE, UT_GCR, UT_NRZI };
494744Swnj 
504744Swnj /* slave to controller mapping table */
514744Swnj short	tjtout[NTJ];
524744Swnj #define UTUNIT(dev)	(tjtout[TJUNIT(dev)])
534744Swnj 
544744Swnj #define	INF	(daddr_t)1000000L	/* a block number that wont exist */
554744Swnj 
564744Swnj struct	tj_softc {
574744Swnj 	char	sc_openf;	/* exclusive open */
584744Swnj 	char	sc_lastiow;	/* last I/O operation was a write */
594744Swnj 	daddr_t	sc_blkno;	/* next block to transfer */
604744Swnj 	daddr_t	sc_nxrec;	/* next record on tape */
614744Swnj 	u_short	sc_erreg;	/* image of uter */
624744Swnj 	u_short	sc_dsreg;	/* image of utds */
63*4746Ssam 	u_short	sc_resid;	/* residual from transfer */
644744Swnj 	u_short	sc_dens;	/* sticky selected density */
654744Swnj } tj_softc[NTJ];
664744Swnj 
674744Swnj /*
684744Swnj  * Internal per/slave states found in sc_state
694744Swnj  */
704744Swnj #define	SSEEK		1	/* seeking */
714744Swnj #define	SIO		2	/* doing sequential I/O */
724744Swnj #define	SCOM		3	/* sending a control command */
734744Swnj #define	SREW		4	/* doing a rewind op */
74*4746Ssam #define	SERASE		5	/* erase inter-record gap */
75*4746Ssam #define	SERASED		6	/* erased inter-record gap */
764744Swnj 
774744Swnj #if UTDEBUG
784744Swnj int	utdebug;
794744Swnj #define printd	if (utdebug) printf
804744Swnj #else
814744Swnj #define	printd
824744Swnj #endif
834744Swnj 
844744Swnj /*
854744Swnj  * A NOP should get an interrupt back, if the
864744Swnj  *  device is there.
874744Swnj  */
884744Swnj utprobe(reg)
894744Swnj 	caddr_t reg;
904744Swnj {
914744Swnj 	register int br, cvec;
924744Swnj #ifdef lint
934744Swnj 	br=0; cvec=br; br=cvec;
944744Swnj #endif
95*4746Ssam 	/*
96*4746Ssam 	 * It appears the controller won't interrupt unless the
97*4746Ssam 	 * slave is off-line...this is as bad as the TS-11.
98*4746Ssam 	 */
99*4746Ssam #ifdef notdef
1004744Swnj 	((struct utdevice *) reg)->utcs1 = UT_IE|UT_NOP|UT_GO;
1014744Swnj 	DELAY(10000);
1024744Swnj 	((struct utdevice *) reg)->utcs1 = UT_CLEAR|UT_GO;
103*4746Ssam #else
104*4746Ssam 	br = 0x15;
105*4746Ssam 	cvec = 0164;
1064744Swnj 	return(1);
107*4746Ssam #endif
1084744Swnj }
1094744Swnj 
1104744Swnj /*ARGSUSED*/
1114744Swnj utslave(ui, reg)
1124744Swnj 	struct uba_device *ui;
1134744Swnj 	caddr_t reg;
1144744Swnj {
1154744Swnj 	/*
1164744Swnj 	 * A real TU45 would support the slave present bit
1174744Swnj 	 * int the drive type register, but this thing doesn't,
1184744Swnj 	 * so there's no way to determine if a slave is present or not.
1194744Swnj 	 */
1204744Swnj 	 return(1);
1214744Swnj }
1224744Swnj 
1234744Swnj utattach(ui)
1244744Swnj 	struct uba_device *ui;
1254744Swnj {
1264744Swnj 	tjtout[ui->ui_unit] = ui->ui_mi->um_ctlr;
1274744Swnj }
1284744Swnj 
1294744Swnj /*
1304744Swnj  * Open the device with exclusive access.
1314744Swnj  */
1324744Swnj utopen(dev, flag)
1334744Swnj 	dev_t dev;
1344744Swnj 	int flag;
1354744Swnj {
1364744Swnj 	register int tjunit = TJUNIT(dev);
1374744Swnj 	register struct uba_device *ui;
1384744Swnj 	register struct tj_softc *sc;
1394744Swnj 	int olddens, dens;
1404744Swnj 
1414744Swnj 	if (tjunit >= NTJ || (sc = &tj_softc[tjunit])->sc_openf ||
1424744Swnj 	    (ui = tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) {
1434744Swnj 		u.u_error = ENXIO;
1444744Swnj 		return;
1454744Swnj 	}
1464744Swnj 	olddens = sc->sc_dens;
1474744Swnj 	dens = sc->sc_dens = utdens[(minor(dev)&(T_1600BPI|T_6250BPI))>>3]|
1484744Swnj 				PDP11FMT|(ui->ui_slave&07);
1494744Swnj get:
1504744Swnj 	utcommand(dev, UT_SENSE, 1);
1514744Swnj 	if (sc->sc_dsreg&UTDS_PIP) {
1524744Swnj 		sleep((caddr_t) &lbolt, PZERO+1);
1534744Swnj 		goto get;
1544744Swnj 	}
1554744Swnj 	sc->sc_dens = olddens;
1564744Swnj 	if ((sc->sc_dsreg&UTDS_MOL) == 0) {
1574744Swnj 		uprintf("tj%d: not online\n", tjunit);
1584744Swnj 		u.u_error = EIO;
1594744Swnj 		return;
1604744Swnj 	}
1614744Swnj 	if ((flag&FWRITE) && (sc->sc_dsreg&UTDS_WRL)) {
1624744Swnj 		uprintf("tj%d: no write ring\n", tjunit);
1634744Swnj 		u.u_error = EIO;
1644744Swnj 		return;
1654744Swnj 	}
1664744Swnj 	if ((sc->sc_dsreg&UTDS_BOT) == 0 && (flag&FWRITE) &&
1674744Swnj 	    dens != sc->sc_dens) {
1684744Swnj 		uprintf("tj%d: can't change density in mid-tape\n", tjunit);
1694744Swnj 		u.u_error = EIO;
1704744Swnj 		return;
1714744Swnj 	}
1724744Swnj 	sc->sc_openf = 1;
1734744Swnj 	sc->sc_blkno = (daddr_t)0;
1744744Swnj 	sc->sc_nxrec = INF;
1754744Swnj 	sc->sc_lastiow = 0;
1764744Swnj 	sc->sc_dens = dens;
177*4746Ssam 	/*
178*4746Ssam 	 * For 6250 bpi take exclusive use of the UNIBUS.
179*4746Ssam 	 */
180*4746Ssam 	ui->ui_driver->ud_xclu = (dens&(T_1600BPI|T_6250BPI)) == T_6250BPI;
1814744Swnj }
1824744Swnj 
1834744Swnj utclose(dev, flag)
1844744Swnj 	register dev_t dev;
1854744Swnj 	register flag;
1864744Swnj {
1874744Swnj 	register struct tj_softc *sc = &tj_softc[TJUNIT(dev)];
1884744Swnj 
1894744Swnj 	if (flag == FWRITE || ((flag&FWRITE) && sc->sc_lastiow)) {
1904744Swnj 		utcommand(dev, UT_WEOF, 1);
1914744Swnj 		utcommand(dev, UT_WEOF, 1);
1924744Swnj 		utcommand(dev, UT_SREV, 1);
1934744Swnj 	}
1944744Swnj 	if ((minor(dev)&T_NOREWIND) == 0)
1954744Swnj 		utcommand(dev, UT_REW, 0);
1964744Swnj 	sc->sc_openf = 0;
1974744Swnj }
1984744Swnj 
1994744Swnj utcommand(dev, com, count)
2004744Swnj 	dev_t dev;
2014744Swnj 	int com, count;
2024744Swnj {
2034744Swnj 	register struct buf *bp;
2044744Swnj 
2054744Swnj 	bp = &cutbuf[UTUNIT(dev)];
2064744Swnj 	(void) spl5();
2074744Swnj 	while (bp->b_flags&B_BUSY) {
2084744Swnj 		if(bp->b_repcnt == 0 && (bp->b_flags&B_DONE))
2094744Swnj 			break;
2104744Swnj 		bp->b_flags |= B_WANTED;
2114744Swnj 		sleep((caddr_t)bp, PRIBIO);
2124744Swnj 	}
2134744Swnj 	bp->b_flags = B_BUSY|B_READ;
2144744Swnj 	(void) spl0();
2154744Swnj 	bp->b_dev = dev;
2164744Swnj 	bp->b_command = com;
2174744Swnj 	bp->b_repcnt = count;
2184744Swnj 	bp->b_blkno = 0;
2194744Swnj 	utstrategy(bp);
2204744Swnj 	if (count == 0)
2214744Swnj 		return;
2224744Swnj 	iowait(bp);
2234744Swnj 	if (bp->b_flags&B_WANTED)
2244744Swnj 		wakeup((caddr_t)bp);
2254744Swnj 	bp->b_flags &= B_ERROR;
2264744Swnj }
2274744Swnj 
2284744Swnj /*
2294744Swnj  * Queue a tape operation.
2304744Swnj  */
2314744Swnj utstrategy(bp)
2324744Swnj 	register struct buf *bp;
2334744Swnj {
2344744Swnj 	int tjunit = TJUNIT(bp->b_dev);
2354744Swnj 	register struct uba_ctlr *um;
2364744Swnj 	register struct buf *dp;
2374744Swnj 
2384744Swnj 	/*
2394744Swnj 	 * Put transfer at end of unit queue
2404744Swnj 	 */
2414744Swnj 	dp = &tjutab[tjunit];
2424744Swnj 	bp->av_forw = NULL;
2434744Swnj 	(void) spl5();
2444744Swnj 	if (dp->b_actf == NULL) {
2454744Swnj 		dp->b_actf = bp;
2464744Swnj 		/*
2474744Swnj 		 * Transport not active, so...
2484744Swnj 		 * put at end of controller queue
2494744Swnj 		 */
2504744Swnj 		dp->b_forw = NULL;
2514744Swnj 		um = tjdinfo[tjunit]->ui_mi;
2524744Swnj 		if (um->um_tab.b_actf == NULL)
2534744Swnj 			um->um_tab.b_actf = dp;
2544744Swnj 		else
2554744Swnj 			um->um_tab.b_actl->b_forw = dp;
2564744Swnj 		um->um_tab.b_actl = dp;
2574744Swnj 	} else
2584744Swnj 		dp->b_actl->av_forw = bp;
2594744Swnj 	dp->b_actl = bp;
2604744Swnj 	/*
2614744Swnj 	 * If the controller is not busy, set it going.
2624744Swnj 	 */
263*4746Ssam 	if (um->um_tab.b_state == 0)
2644744Swnj 		utstart(um);
2654744Swnj 	(void) spl0();
2664744Swnj }
2674744Swnj 
2684744Swnj utstart(um)
2694744Swnj 	register struct uba_ctlr *um;
2704744Swnj {
271*4746Ssam 	register struct utdevice *addr;
2724744Swnj 	register struct buf *bp, *dp;
2734744Swnj 	register struct tj_softc *sc;
2744744Swnj 	struct uba_device *ui;
2754744Swnj 	int tjunit;
2764744Swnj 	daddr_t blkno;
2774744Swnj 
2784744Swnj loop:
2794744Swnj 	/*
2804744Swnj 	 * Scan controller queue looking for units with
2814744Swnj 	 * transaction queues to dispatch
2824744Swnj 	 */
2834744Swnj 	if ((dp = um->um_tab.b_actf) == NULL)
2844744Swnj 		return;
2854744Swnj 	if ((bp = dp->b_actf) == NULL) {
2864744Swnj 		um->um_tab.b_actf = dp->b_forw;
2874744Swnj 		goto loop;
2884744Swnj 	}
289*4746Ssam 	addr = (struct utdevice *)um->um_addr;
2904744Swnj 	tjunit = TJUNIT(bp->b_dev);
2914744Swnj 	ui = tjdinfo[tjunit];
2924744Swnj 	sc = &tj_softc[tjunit];
2934744Swnj 	/* note slave select, density, and format were merged on open */
294*4746Ssam 	addr->uttc = sc->sc_dens;
295*4746Ssam 	sc->sc_dsreg = addr->utds;
296*4746Ssam 	sc->sc_erreg = addr->uter;
297*4746Ssam 	/* watch this, sports fans */
298*4746Ssam 	sc->sc_resid = bp->b_flags&B_READ ?
299*4746Ssam 		bp->b_bcount - ((-addr->utfc)&0xffff) : -addr->utwc<<1;
3004744Swnj 	/*
3014744Swnj 	 * Default is that last command was NOT a write command;
3024744Swnj 	 * if we do a write command we will notice this in utintr().
3034744Swnj 	 */
3044744Swnj 	sc->sc_lastiow = 0;
305*4746Ssam 	printd("utstart: cmd=%o openf=%d ds=%b\n", bp->b_command>>1,
306*4746Ssam 		sc->sc_openf, addr->utds, UTDS_BITS);
307*4746Ssam 	if (sc->sc_openf < 0 || (addr->utds&UTDS_MOL) == 0) {
3084744Swnj 		/*
3094744Swnj 		 * Have had a hard error on a non-raw tape
3104744Swnj 		 * or the tape unit is now unavailable
3114744Swnj 		 * (e.g. taken off line).
3124744Swnj 		 */
3134744Swnj 		bp->b_flags |= B_ERROR;
3144744Swnj 		goto next;
3154744Swnj 	}
3164744Swnj 	if (bp == &cutbuf[UTUNIT(bp->b_dev)]) {
3174744Swnj 		/*
3184744Swnj 		 * Execute a control operation with the specified
3194744Swnj 		 * count.
3204744Swnj 		 */
3214744Swnj 		if (bp->b_command == UT_SENSE)
3224744Swnj 			goto next;
3234744Swnj 		/*
3244744Swnj 		 * Set next state; handle timeouts
3254744Swnj 		 */
3264744Swnj 		if (bp->b_command == UT_REW)
327*4746Ssam 			um->um_tab.b_state = SREW;
3284744Swnj 		else
329*4746Ssam 			um->um_tab.b_state = SCOM;
3304744Swnj 		/* NOTE: this depends on the ut command values */
3314744Swnj 		if (bp->b_command >= UT_SFORW && bp->b_command <= UT_SREVF)
332*4746Ssam 			addr->utfc = -bp->b_repcnt;
3334744Swnj 		goto dobpcmd;
3344744Swnj 	}
3354744Swnj 	/*
3364744Swnj 	 * The following checks boundary conditions for operations
3374744Swnj 	 * on non-raw tapes.  On raw tapes the initialization of
3384744Swnj 	 * sc->sc_nxrec by utphys causes them to be skipped normally
3394744Swnj 	 * (except in the case of retries).
3404744Swnj 	 */
3414744Swnj 	if (dbtofsb(bp->b_blkno) > sc->sc_nxrec) {
3424744Swnj 		/* can't read past end of file */
3434744Swnj 		bp->b_flags |= B_ERROR;
3444744Swnj 		bp->b_error = ENXIO;
3454744Swnj 		goto next;
3464744Swnj 	}
3474744Swnj 	if (dbtofsb(bp->b_blkno) == sc->sc_nxrec && (bp->b_flags&B_READ)) {
3484744Swnj 		/* read at eof returns 0 count */
3494744Swnj 		bp->b_resid = bp->b_bcount;
3504744Swnj 		clrbuf(bp);
3514744Swnj 		goto next;
3524744Swnj 	}
3534744Swnj 	if ((bp->b_flags&B_READ) == 0)
3544744Swnj 		sc->sc_nxrec = dbtofsb(bp->b_blkno)+1;
3554744Swnj 	/*
3564744Swnj 	 * If the tape is correctly positioned, set up all the
3574744Swnj 	 * registers but the csr, and give control over to the
3584744Swnj 	 * UNIBUS adaptor routines, to wait for resources to
3594744Swnj 	 * start I/O.
3604744Swnj 	 */
3614744Swnj 	if ((blkno = sc->sc_blkno) == dbtofsb(bp->b_blkno)) {
362*4746Ssam 		addr->utwc = -(((bp->b_bcount)+1)>>1);
363*4746Ssam 		addr->utfc = -bp->b_bcount;
3644744Swnj 		if ((bp->b_flags&B_READ) == 0) {
3654744Swnj 			/*
3664744Swnj 			 * On write error retries erase the
367*4746Ssam 			 * inter-record gap before rewriting.
3684744Swnj 			 */
369*4746Ssam 			if (um->um_tab.b_errcnt) {
370*4746Ssam 				printd("utstart: erase\n");
371*4746Ssam 				if (um->um_tab.b_state != SERASED) {
372*4746Ssam 					um->um_tab.b_state = SERASED;
373*4746Ssam 					addr->utcs1 = UT_ERASE|UT_IE|UT_GO;
374*4746Ssam 					return;
375*4746Ssam 				}
376*4746Ssam 				printd("utstart: erased\n");
377*4746Ssam 			}
378*4746Ssam 			um->um_cmd = UT_WCOM;
3794744Swnj 		} else
3804744Swnj 			um->um_cmd = UT_RCOM;
381*4746Ssam 		um->um_tab.b_state = SIO;
3824744Swnj 		(void) ubago(ui);
3834744Swnj 		return;
3844744Swnj 	}
3854744Swnj 	/*
3864744Swnj 	 * Tape positioned incorrectly; seek forwards or
3874744Swnj 	 * backwards to the correct spot.  This happens for
3884744Swnj 	 * raw tapes only on error retries.
3894744Swnj 	 */
3904744Swnj 	printd("utstart: seek, blkno=%d dbtofsb=%d\n", blkno,
3914744Swnj 		dbtofsb(bp->b_blkno));
392*4746Ssam 	um->um_tab.b_state = SSEEK;
3934744Swnj 	if (blkno < dbtofsb(bp->b_blkno)) {
394*4746Ssam 		addr->utfc = blkno - dbtofsb(bp->b_blkno);
3954744Swnj 		bp->b_command = UT_SFORW;
3964744Swnj 	} else {
397*4746Ssam 		addr->utfc = dbtofsb(bp->b_blkno) - blkno;
3984744Swnj 		bp->b_command = UT_SREV;
3994744Swnj 	}
4004744Swnj 
4014744Swnj dobpcmd:
4024744Swnj 	/*
4034744Swnj 	 * Perform the command setup in bp.
4044744Swnj 	 */
4054744Swnj 	printd("utstart: dobpcmd\n");
406*4746Ssam 	addr->utcs1 = bp->b_command|UT_IE|UT_GO;
4074744Swnj 	return;
4084744Swnj next:
4094744Swnj 	/*
4104744Swnj 	 * Advance to the next command in the slave queue,
4114744Swnj 	 * posting notice and releasing resources as needed.
4124744Swnj 	 */
4134744Swnj 	printd("utstart: next\n");
4144744Swnj 	if (um->um_ubinfo)
4154744Swnj 		ubadone(um);
4164744Swnj 	um->um_tab.b_errcnt = 0;
4174744Swnj 	dp->b_actf = bp->av_forw;
4184744Swnj 	iodone(bp);
4194744Swnj 	goto loop;
4204744Swnj }
4214744Swnj 
4224744Swnj /*
4234744Swnj  * Start operation on controller --
4244744Swnj  * UNIBUS resources have been allocated.
4254744Swnj  */
4264744Swnj utdgo(um)
4274744Swnj 	register struct uba_ctlr *um;
4284744Swnj {
4294744Swnj 	register struct utdevice *addr = (struct utdevice *)um->um_addr;
4304744Swnj 
4314744Swnj 	addr->utba = (u_short) um->um_ubinfo;
4324744Swnj 	addr->utcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x30)|UT_IE|UT_GO;
433*4746Ssam 	printd("utdgo: cs1=%b fc=%x wc=%x\n", addr->utcs1, UT_BITS,
434*4746Ssam 		addr->utfc, addr->utwc);
4354744Swnj }
4364744Swnj 
4374744Swnj /*
4384744Swnj  * Ut interrupt handler
4394744Swnj  */
4404744Swnj /*ARGSUSED*/
4414744Swnj utintr(ut11)
4424744Swnj 	int ut11;
4434744Swnj {
4444744Swnj 	struct buf *dp;
4454744Swnj 	register struct buf *bp;
4464744Swnj 	register struct uba_ctlr *um = utminfo[ut11];
4474744Swnj 	register struct utdevice *addr;
4484744Swnj 	register struct tj_softc *sc;
449*4746Ssam 	u_short tjunit, cs2, cs1;
4504744Swnj 	register state;
4514744Swnj 
4524744Swnj 	if ((dp = um->um_tab.b_actf) == NULL)
4534744Swnj 		return;
4544744Swnj 	bp = dp->b_actf;
4554744Swnj 	tjunit = TJUNIT(bp->b_dev);
4564744Swnj 	addr = (struct utdevice *)tjdinfo[tjunit]->ui_addr;
4574744Swnj 	sc = &tj_softc[tjunit];
4584744Swnj 	/*
4594744Swnj 	 * Record status...
4604744Swnj 	 */
4614744Swnj 	sc->sc_dsreg = addr->utds;
4624744Swnj 	sc->sc_erreg = addr->uter;
463*4746Ssam 	sc->sc_resid = bp->b_flags&B_READ ?
464*4746Ssam 		bp->b_bcount - (-addr->utfc)&0xffff : -addr->utwc<<1;
4654744Swnj 	printd("utintr: state=%d cs1=%b cs2=%b ds=%b er=%b\n",
466*4746Ssam 		um->um_tab.b_state,
4674744Swnj 		((struct utdevice *) addr)->utcs1, UT_BITS,
4684744Swnj 		((struct utdevice *) addr)->utcs2, UTCS2_BITS,
4694744Swnj 		((struct utdevice *) addr)->utds, UTDS_BITS,
4704744Swnj 		((struct utdevice *) addr)->uter, UTER_BITS);
471*4746Ssam 	if ((bp->b_flags&B_READ) == 0)
4724744Swnj 		sc->sc_lastiow = 1;
473*4746Ssam 	state = um->um_tab.b_state;
474*4746Ssam 	um->um_tab.b_state = 0;
4754744Swnj 	/*
4764744Swnj 	 * Check for errors...
4774744Swnj 	 */
4784744Swnj 	if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) {
4794744Swnj 		/*
480*4746Ssam 		 * If we hit a tape mark or EOT update our position.
4814744Swnj 		 */
482*4746Ssam 		if (addr->utds&(UTDS_TM|UTDS_EOT)) {
4834744Swnj 			/*
484*4746Ssam 			 * Set blkno and nxrec
4854744Swnj 			 */
4864744Swnj 			if (bp == &cutbuf[UTUNIT(bp->b_dev)]) {
4874744Swnj 				if (sc->sc_blkno > dbtofsb(bp->b_blkno)) {
4884744Swnj 					sc->sc_nxrec =
4894744Swnj 					      dbtofsb(bp->b_blkno) - addr->utfc;
4904744Swnj 					sc->sc_blkno = sc->sc_nxrec;
4914744Swnj 				} else {
4924744Swnj 					sc->sc_blkno =
4934744Swnj 					      dbtofsb(bp->b_blkno) + addr->utfc;
4944744Swnj 					sc->sc_nxrec = sc->sc_blkno-1;
4954744Swnj 				}
496*4746Ssam 			} else
4974744Swnj 				sc->sc_nxrec = dbtofsb(bp->b_blkno);
4984744Swnj 			state = SCOM;		/* force completion */
4994744Swnj 			addr->utcs1 = UT_CLEAR|UT_GO;
5004744Swnj 			/*
501*4746Ssam 			 * Stuff so we can unstuff later
502*4746Ssam 			 * to get the residual.
5034744Swnj 			 */
504*4746Ssam 			addr->utwc = (-bp->b_bcount)>>1;
5054744Swnj 			addr->utfc = -bp->b_bcount;
506*4746Ssam 			if (sc->sc_dsreg&UTDS_EOT)
507*4746Ssam 				goto harderror;
5084744Swnj 			goto opdone;
5094744Swnj 		}
510*4746Ssam 		cs2 = addr->utcs2;		/* save it for printf below */
511*4746Ssam 		if ((cs1 = addr->utcs1)&UT_TRE)
512*4746Ssam 			addr->utcs2 |= UTCS2_CLR;
5134744Swnj 		addr->utcs1 = UT_CLEAR|UT_GO;	/* must clear ERR bit */
514*4746Ssam 		printd("after clear: cs1=%b er=%b cs2=%b ds=%b\n",
515*4746Ssam 			addr->utcs1, UT_BITS, addr->uter, UTER_BITS,
516*4746Ssam 			addr->utcs2, UTCS2_BITS, addr->utds, UTDS_BITS);
5174744Swnj 		/*
5184744Swnj 		 * If we were reading from a raw tape and the only error
5194744Swnj 		 * was that the record was too long, then we don't consider
5204744Swnj 		 * this an error.
5214744Swnj 		 */
5224744Swnj 		if (bp == &rutbuf[UTUNIT(bp->b_dev)] && (bp->b_flags&B_READ) &&
5234744Swnj 		    (sc->sc_erreg&UTER_FCE))
5244744Swnj 			goto ignoreerr;
5254744Swnj 		/*
526*4746Ssam 		 * Fix up errors which occur due to backspacing "over" the
527*4746Ssam 		 * front of the tape.
528*4746Ssam 		 */
529*4746Ssam 		if ((sc->sc_dsreg&UTDS_BOT) &&
530*4746Ssam 		    (bp->b_command == UT_SREV || bp->b_command == UT_SREV) &&
531*4746Ssam 		    ((sc->sc_erreg &= ~(UTER_NEF|UTER_FCE)) == 0))
532*4746Ssam 			goto opdone;
533*4746Ssam 		/*
5344744Swnj 		 * Retry soft errors up to 8 times
5354744Swnj 		 */
5364744Swnj 		if ((sc->sc_erreg&UTER_HARD) == 0 && state == SIO) {
5374744Swnj 			if (++um->um_tab.b_errcnt < 7) {
5384744Swnj 				sc->sc_blkno++;
5394744Swnj 				ubadone(um);
5404744Swnj 				goto opcont;
5414744Swnj 			}
5424744Swnj 		} else
543*4746Ssam harderror:
5444744Swnj 			/*
5454744Swnj 			 * Hard or non-I/O errors on non-raw tape
546*4746Ssam 			 * cause it to close; also, reading off the
547*4746Ssam 			 * end of the tape.
5484744Swnj 			 */
549*4746Ssam 			if (sc->sc_openf > 0 &&
550*4746Ssam 			    bp != &rutbuf[UTUNIT(bp->b_dev)] ||
551*4746Ssam 			    sc->sc_dsreg&UTDS_EOT)
5524744Swnj 				sc->sc_openf = -1;
5534744Swnj 		/*
5544744Swnj 		 * Couldn't recover error.
5554744Swnj 		 */
556*4746Ssam 		printf("ut%d: hard error bn%d cs1=%b er=%b cs2=%b ds=%b\n",
557*4746Ssam 			tjunit, bp->b_blkno, cs1, UT_BITS, sc->sc_erreg,
558*4746Ssam 			UTER_BITS, cs2, UTCS2_BITS, sc->sc_dsreg, UTDS_BITS);
5594744Swnj 		bp->b_flags |= B_ERROR;
5604744Swnj 		goto opdone;
5614744Swnj 	}
5624744Swnj ignoreerr:
5634744Swnj 	/*
5644744Swnj 	 * Advance tape control FSM.
5654744Swnj 	 */
5664744Swnj 	switch (state) {
5674744Swnj 
5684744Swnj 	case SIO:		/* read/write increments tape block # */
5694744Swnj 		sc->sc_blkno++;
570*4746Ssam 		break;
5714744Swnj 
5724744Swnj 	case SCOM:		/* forw/rev space updates current position */
5734744Swnj 		if (bp == &cutbuf[UTUNIT(bp->b_dev)])
5744744Swnj 		switch (bp->b_command) {
5754744Swnj 
5764744Swnj 		case UT_SFORW:
5774744Swnj 			sc->sc_blkno -= bp->b_repcnt;
5784744Swnj 			break;
5794744Swnj 
5804744Swnj 		case UT_SREV:
5814744Swnj 			sc->sc_blkno += bp->b_repcnt;
5824744Swnj 			break;
5834744Swnj 		}
584*4746Ssam 		break;
5854744Swnj 
5864744Swnj 	case SSEEK:
5874744Swnj 		sc->sc_blkno = dbtofsb(bp->b_blkno);
5884744Swnj 		goto opcont;
5894744Swnj 
590*4746Ssam 	case SERASE:
591*4746Ssam 		/*
592*4746Ssam 		 * Completed erase of the inter-record gap due to a
593*4746Ssam 		 * write error; now retry the write operation.
594*4746Ssam 		 */
595*4746Ssam 		um->um_tab.b_state = SERASED;
596*4746Ssam 		goto opcont;
597*4746Ssam 
598*4746Ssam 	case SREW:			/* clear attention bit */
599*4746Ssam 		addr->utcs1 = UT_CLEAR|UT_GO;
600*4746Ssam 		break;
601*4746Ssam 
6024744Swnj 	default:
603*4746Ssam 		printf("bad state %d\n", state);
6044744Swnj 		panic("utintr");
6054744Swnj 	}
6064744Swnj 
6074744Swnj opdone:
6084744Swnj 	/*
6094744Swnj 	 * Reset error count and remove
6104744Swnj 	 * from device queue
6114744Swnj 	 */
6124744Swnj 	um->um_tab.b_errcnt = 0;
613*4746Ssam 	dp->b_actf = bp->av_forw;
614*4746Ssam 	bp->b_resid = bp->b_command&B_READ ?
615*4746Ssam 		bp->b_bcount - ((-addr->utfc)&0xffff) : -addr->utwc<<1;
6164744Swnj 	ubadone(um);
6174744Swnj 	iodone(bp);
6184744Swnj 	/*
6194744Swnj 	 * Circulate slave to end of controller queue
6204744Swnj 	 * to give other slaves a chance
6214744Swnj 	 */
6224744Swnj 	um->um_tab.b_actf = dp->b_forw;
6234744Swnj 	if (dp->b_actf) {
6244744Swnj 		dp->b_forw = NULL;
6254744Swnj 		if (um->um_tab.b_actf == NULL)
6264744Swnj 			um->um_tab.b_actf = dp;
6274744Swnj 		else
6284744Swnj 			um->um_tab.b_actl->b_forw = dp;
6294744Swnj 		um->um_tab.b_actl = dp;
6304744Swnj 	}
6314744Swnj 	if (um->um_tab.b_actf == 0)
6324744Swnj 		return;
6334744Swnj opcont:
6344744Swnj 	utstart(um);
6354744Swnj }
6364744Swnj 
6374744Swnj /*
6384744Swnj  * Raw interface for a read
6394744Swnj  */
6404744Swnj utread(dev)
6414744Swnj 	dev_t dev;
6424744Swnj {
6434744Swnj 	utphys(dev);
6444744Swnj 	if (u.u_error)
6454744Swnj 		return;
6464744Swnj 	physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_READ, minphys);
6474744Swnj }
6484744Swnj 
6494744Swnj /*
6504744Swnj  * Raw interface for a write
6514744Swnj  */
6524744Swnj utwrite(dev)
6534744Swnj {
6544744Swnj 	utphys(dev);
6554744Swnj 	if (u.u_error)
6564744Swnj 		return;
6574744Swnj 	physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_WRITE, minphys);
6584744Swnj }
6594744Swnj 
6604744Swnj /*
6614744Swnj  * Check for valid device number dev and update our notion
6624744Swnj  * of where we are on the tape
6634744Swnj  */
6644744Swnj utphys(dev)
6654744Swnj 	dev_t dev;
6664744Swnj {
6674744Swnj 	register int tjunit = TJUNIT(dev);
6684744Swnj 	register struct tj_softc *sc;
6694744Swnj 	register struct uba_device *ui;
6704744Swnj 
6714744Swnj 	if (tjunit >= NTJ || (ui=tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) {
6724744Swnj 		u.u_error = ENXIO;
6734744Swnj 		return;
6744744Swnj 	}
6754744Swnj 	sc = &tj_softc[tjunit];
676*4746Ssam 	sc->sc_blkno = dbtofsb(u.u_offset>>9);
677*4746Ssam 	sc->sc_nxrec = sc->sc_blkno+1;
6784744Swnj }
6794744Swnj 
6804744Swnj /*ARGSUSED*/
6814744Swnj utioctl(dev, cmd, addr, flag)
6824744Swnj 	dev_t dev;
6834744Swnj 	caddr_t addr;
6844744Swnj {
6854744Swnj 	register struct tj_softc *sc = &tj_softc[TJUNIT(dev)];
6864744Swnj 	register struct buf *bp = &cutbuf[UTUNIT(dev)];
6874744Swnj 	register callcount;
6884744Swnj 	int fcount;
6894744Swnj 	struct mtop mtop;
6904744Swnj 	struct mtget mtget;
6914744Swnj 	/* we depend of the values and order of the MT codes here */
6924744Swnj 	static utops[] =
6934744Swnj       {UT_WEOF,UT_SFORWF,UT_SREVF,UT_SFORW,UT_SREV,UT_REW,UT_REWOFFL,UT_SENSE};
6944744Swnj 
6954744Swnj 	switch (cmd) {
6964744Swnj 
6974744Swnj 	case MTIOCTOP:
6984744Swnj 		if (copyin((caddr_t)addr, (caddr_t)&mtop, sizeof(mtop))) {
6994744Swnj 			u.u_error = EFAULT;
7004744Swnj 			return;
7014744Swnj 		}
7024744Swnj 		switch(mtop.mt_op) {
7034744Swnj 
7044744Swnj 		case MTWEOF:
7054744Swnj 			callcount = mtop.mt_count;
7064744Swnj 			fcount = 1;
7074744Swnj 			break;
7084744Swnj 
7094744Swnj 		case MTFSF: case MTBSF:
7104744Swnj 		case MTFSR: case MTBSR:
7114744Swnj 			callcount = 1;
7124744Swnj 			fcount = mtop.mt_count;
7134744Swnj 			break;
7144744Swnj 
7154744Swnj 		case MTREW: case MTOFFL: case MTNOP:
7164744Swnj 			callcount = 1;
7174744Swnj 			fcount = 1;
7184744Swnj 			break;
7194744Swnj 
7204744Swnj 		default:
7214744Swnj 			u.u_error = ENXIO;
7224744Swnj 			return;
7234744Swnj 		}
7244744Swnj 		if (callcount <= 0 || fcount <= 0) {
7254744Swnj 			u.u_error = ENXIO;
7264744Swnj 			return;
7274744Swnj 		}
7284744Swnj 		while (--callcount >= 0) {
7294744Swnj 			utcommand(dev, utops[mtop.mt_op], fcount);
730*4746Ssam 			/* note this depends on the mtop values */
731*4746Ssam 			if ((mtop.mt_op >= MTFSF || mtop.mt_op <= MTBSR) &&
7324744Swnj 			    bp->b_resid) {
7334744Swnj 				u.u_error = EIO;
7344744Swnj 				break;
7354744Swnj 			}
7364744Swnj 			if ((bp->b_flags&B_ERROR) || (sc->sc_dsreg&UTDS_BOT))
7374744Swnj 				break;
7384744Swnj 		}
7394744Swnj 		geterror(bp);
7404744Swnj 		return;
7414744Swnj 
7424744Swnj 	case MTIOCGET:
7434744Swnj 		mtget.mt_dsreg = sc->sc_dsreg;
7444744Swnj 		mtget.mt_erreg = sc->sc_erreg;
7454744Swnj 		mtget.mt_resid = sc->sc_resid;
7464744Swnj 		mtget.mt_type = MT_ISUT;
7474744Swnj 		if (copyout((caddr_t)&mtget, addr, sizeof(mtget)))
7484744Swnj 			u.u_error = EFAULT;
7494744Swnj 		return;
7504744Swnj 
7514744Swnj 	default:
7524744Swnj 		u.u_error = ENXIO;
7534744Swnj 	}
7544744Swnj }
7554744Swnj 
7564744Swnj utreset(uban)
7574744Swnj 	int uban;
7584744Swnj {
7594744Swnj 	register struct uba_ctlr *um;
7604744Swnj 	register ut11, tjunit;
7614744Swnj 	register struct uba_device *ui;
7624744Swnj 	register struct buf *dp;
7634744Swnj 
7644744Swnj 	for (ut11 = 0; ut11 < NUT; ut11++) {
7654744Swnj 		if ((um = utminfo[ut11]) == 0 || um->um_alive == 0 ||
7664744Swnj 		   um->um_ubanum != uban)
7674744Swnj 			continue;
7684744Swnj 		printf(" ut%d", ut11);
769*4746Ssam 		um->um_tab.b_state = 0;
7704744Swnj 		um->um_tab.b_actf = um->um_tab.b_actl = 0;
7714744Swnj 		if (um->um_ubinfo) {
7724744Swnj 			printf("<%d>", (um->um_ubinfo>>28)&0xf);
7734744Swnj 			ubadone(um);
7744744Swnj 		}
7754744Swnj 		((struct utdevice *)(um->um_addr))->utcs1 = UT_CLEAR|UT_GO;
776*4746Ssam 		((struct utdevice *)(um->um_addr))->utcs2 |= UTCS2_CLR;
7774744Swnj 		for (tjunit = 0; tjunit < NTJ; tjunit++) {
7784744Swnj 			if ((ui = tjdinfo[tjunit]) == 0 || ui->ui_mi != um ||
7794744Swnj 			    ui->ui_alive == 0)
7804744Swnj 				continue;
7814744Swnj 			dp = &tjutab[tjunit];
782*4746Ssam 			dp->b_state = 0;
7834744Swnj 			dp->b_forw = 0;
7844744Swnj 			if (um->um_tab.b_actf == NULL)
7854744Swnj 				um->um_tab.b_actf = dp;
7864744Swnj 			else
7874744Swnj 				um->um_tab.b_actl->b_forw = dp;
7884744Swnj 			um->um_tab.b_actl = dp;
7894744Swnj 			if (tj_softc[tjunit].sc_openf > 0)
7904744Swnj 				tj_softc[tjunit].sc_openf = -1;
7914744Swnj 		}
7924744Swnj 		utstart(um);
7934744Swnj 	}
7944744Swnj }
7954744Swnj 
7964744Swnj /*
7974744Swnj  * Do a stand-alone core dump to tape --
7984744Swnj  * from here down, routines are used only in dump context
7994744Swnj  */
8004744Swnj #define	DBSIZE	20
8014744Swnj 
8024744Swnj utdump()
8034744Swnj {
8044744Swnj 	register struct uba_device *ui;
8054744Swnj 	register struct uba_regs *up;
806*4746Ssam 	register struct utdevice *addr;
8074744Swnj 	int blk, num = maxfree;
8084744Swnj 	int start = 0;
8094744Swnj 
8104744Swnj #define	phys(a,b)		((b)((int)(a)&0x7fffffff))
8114744Swnj 	if (tjdinfo[0] == 0)
8124744Swnj 		return (ENXIO);
8134744Swnj 	ui = phys(tjdinfo[0], struct uba_device *);
8144744Swnj 	up = phys(ui->ui_hd, struct uba_hd *)->uh_physuba;
8154744Swnj 	ubainit();
8164744Swnj 	DELAY(1000000);
817*4746Ssam 	utwait(addr);
818*4746Ssam 	addr = (struct utdevice *)ui->ui_physaddr;
819*4746Ssam 	/*
820*4746Ssam 	 * Be sure to set the appropriate density here.  We use
821*4746Ssam 	 * 6250, but maybe it should be done at 1600 to insure the
822*4746Ssam 	 * tape can be read by most any other tape drive available.
823*4746Ssam 	 */
824*4746Ssam 	addr->uttc = UT_GCR|PDP11FMT;	/* implicit slave 0 or-ed in */
825*4746Ssam 	addr->utcs1 = UT_CLEAR|UT_GO;
8264744Swnj 	while (num > 0) {
8274744Swnj 		blk = num > DBSIZE ? DBSIZE : num;
828*4746Ssam 		utdwrite(start, blk, addr, up);
829*4746Ssam 		if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE))
830*4746Ssam 			return(EIO);
8314744Swnj 		start += blk;
8324744Swnj 		num -= blk;
8334744Swnj 	}
834*4746Ssam 	uteof(addr);
835*4746Ssam 	uteof(addr);
836*4746Ssam 	utwait(addr);
837*4746Ssam 	if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE))
8384744Swnj 		return(EIO);
839*4746Ssam 	addr->utcs1 = UT_REW|UT_GO;
8404744Swnj 	return (0);
8414744Swnj }
8424744Swnj 
843*4746Ssam utdwrite(dbuf, num, addr, up)
8444744Swnj 	register dbuf, num;
845*4746Ssam 	register struct utdevice *addr;
8464744Swnj 	struct uba_regs *up;
8474744Swnj {
8484744Swnj 	register struct pte *io;
8494744Swnj 	register int npf;
8504744Swnj 
851*4746Ssam 	utwait(addr);
8524744Swnj 	io = up->uba_map;
8534744Swnj 	npf = num + 1;
8544744Swnj 	while (--npf != 0)
8554744Swnj 		*(int *)io++ = (dbuf++ | (1<<UBAMR_DPSHIFT) | UBAMR_MRV);
8564744Swnj 	*(int *)io = 0;
857*4746Ssam 	addr->utwc = -((num*NBPG)>>1);
858*4746Ssam 	addr->utfc = -(num*NBPG);
859*4746Ssam 	addr->utba = 0;
860*4746Ssam 	addr->utcs1 = UT_WCOM|UT_GO;
8614744Swnj }
8624744Swnj 
863*4746Ssam utwait(addr)
864*4746Ssam 	struct utdevice *addr;
8654744Swnj {
8664744Swnj 	register s;
8674744Swnj 
8684744Swnj 	do
869*4746Ssam 		s = addr->utds;
8704744Swnj 	while ((s&UTDS_DRY) == 0);
8714744Swnj }
8724744Swnj 
873*4746Ssam uteof(addr)
874*4746Ssam 	struct utdevice *addr;
8754744Swnj {
8764744Swnj 
877*4746Ssam 	utwait(addr);
878*4746Ssam 	addr->utcs1 = UT_WEOF|UT_GO;
8794744Swnj }
8804744Swnj #endif
881