xref: /original-bsd/sys/vax/uba/ts.c (revision d72cfc4f)
10b53eb1aSmckusick /*
248374bc6Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
30b53eb1aSmckusick  * All rights reserved.  The Berkeley software License Agreement
40b53eb1aSmckusick  * specifies the terms and conditions for redistribution.
50b53eb1aSmckusick  *
6*d72cfc4fStorek  *	@(#)ts.c	7.15 (Berkeley) 02/27/92
70b53eb1aSmckusick  */
8496a8a15Swnj 
99826bf71Swnj #include "ts.h"
10496a8a15Swnj #if NTS > 0
11496a8a15Swnj /*
12496a8a15Swnj  * TS11 tape driver
1361711025Swnj  *
1407424adbSbostic  * NB: This driver takes advantage of the fact that there is only one
1507424adbSbostic  *	drive per controller.
1607424adbSbostic  *
1761711025Swnj  * TODO:
1807424adbSbostic  *	test dump code
19496a8a15Swnj  */
20880d6a2dSbostic #include "sys/param.h"
21880d6a2dSbostic #include "sys/systm.h"
22880d6a2dSbostic #include "sys/buf.h"
23880d6a2dSbostic #include "sys/conf.h"
24880d6a2dSbostic #include "sys/errno.h"
25880d6a2dSbostic #include "sys/file.h"
26880d6a2dSbostic #include "sys/map.h"
27880d6a2dSbostic #include "sys/vm.h"
28880d6a2dSbostic #include "sys/ioctl.h"
29880d6a2dSbostic #include "sys/mtio.h"
30880d6a2dSbostic #include "sys/cmap.h"
31880d6a2dSbostic #include "sys/uio.h"
32880d6a2dSbostic #include "sys/syslog.h"
33880d6a2dSbostic #include "sys/tprintf.h"
34496a8a15Swnj 
35880d6a2dSbostic #include "../include/pte.h"
36880d6a2dSbostic #include "../include/cpu.h"
3725c89027Sbloom #include "ubareg.h"
3825c89027Sbloom #include "ubavar.h"
3925c89027Sbloom #include "tsreg.h"
40496a8a15Swnj 
4161711025Swnj /*
4261711025Swnj  * There is a ctsbuf per tape controller.
4361711025Swnj  * It is used as the token to pass to the internal routines
4461711025Swnj  * to execute tape ioctls.
4561711025Swnj  * In particular, when the tape is rewinding on close we release
4661711025Swnj  * the user process but any further attempts to use the tape drive
4761711025Swnj  * before the rewind completes will hang waiting for ctsbuf.
4861711025Swnj  */
4961711025Swnj struct	buf	ctsbuf[NTS];
50496a8a15Swnj 
5161711025Swnj /*
5261711025Swnj  * Driver unibus interface routines and variables.
5361711025Swnj  */
5461711025Swnj int	tsprobe(), tsslave(), tsattach(), tsdgo(), tsintr();
5561711025Swnj struct	uba_ctlr *tsminfo[NTS];
5661711025Swnj struct	uba_device *tsdinfo[NTS];
57397f88ceSroot struct buf	tsutab[NTS];
5861711025Swnj u_short	tsstd[] = { 0772520, 0 };
5907424adbSbostic /* need all these even though controller == drive */
6061711025Swnj struct	uba_driver zsdriver =
6107424adbSbostic  { tsprobe, tsslave, tsattach, tsdgo, tsstd, "ts", tsdinfo, "zs", tsminfo };
62496a8a15Swnj 
6361711025Swnj /* bits in minor device */
6461711025Swnj #define	TSUNIT(dev)	(minor(dev)&03)
6561711025Swnj #define	T_NOREWIND	04
6607424adbSbostic #define	T_1600BPI	010
67496a8a15Swnj 
6861711025Swnj #define	INF	(daddr_t)1000000L
69496a8a15Swnj 
7061711025Swnj /*
7161711025Swnj  * Software state per tape transport.
7261711025Swnj  * Also contains hardware state in message packets.
7361711025Swnj  *
7461711025Swnj  * 1. A tape drive is a unique-open device; we refuse opens when it is already.
7561711025Swnj  * 2. We keep track of the current position on a block tape and seek
7661711025Swnj  *    before operations by forward/back spacing if necessary.
7761711025Swnj  * 3. We remember if the last operation was a write on a tape, so if a tape
7861711025Swnj  *    is open read write and the last thing done is a write we can
7961711025Swnj  *    write a standard end of tape mark (two eofs).
8061711025Swnj  * 4. We remember the status registers after the last command, using
8161711025Swnj  *    then internally and returning them to the SENSE ioctl.
8261711025Swnj  */
8307424adbSbostic struct	ts_tsdata {		/* data shared with ts11 controller */
8407424adbSbostic 	struct	ts_cmd t_cmd;	/* the command packet (must be first) */
8507424adbSbostic 	struct	ts_sts t_sts;	/* status packet, for returned status */
8607424adbSbostic 	struct	ts_char t_char;	/* characteristics packet */
8707424adbSbostic };
8861711025Swnj struct	ts_softc {
8961711025Swnj 	char	sc_openf;	/* lock against multiple opens */
9061711025Swnj 	char	sc_lastiow;	/* last op was a write */
914a2c0eeaSwnj 	short	sc_resid;	/* copy of last bc */
9261711025Swnj 	daddr_t	sc_blkno;	/* block number, for block device tape */
9361711025Swnj 	daddr_t	sc_nxrec;	/* position of end of tape, if known */
9407424adbSbostic 	struct	ts_tsdata sc_ts;/* command and status packets */
9507424adbSbostic 	struct	ts_tsdata *sc_ubaddr; /* Unibus address of tsdata structure */
96ae117955Sroot 	u_short	sc_uba;		/* Unibus addr of cmd pkt for tsdb */
9707424adbSbostic 	short	sc_density;	/* value |'ed into char_mode for TC13 density */
98bf89ced6Smarc 	tpr_t	sc_tpr;		/* tprintf handle */
99edb110f3Skarels 	int	sc_blks;	/* number of I/O operations since open */
100edb110f3Skarels 	int	sc_softerrs;	/* number of soft I/O errors since open */
10161711025Swnj } ts_softc[NTS];
102496a8a15Swnj 
10361711025Swnj /*
10461711025Swnj  * States for um->um_tab.b_active, the per controller state flag.
10561711025Swnj  * This is used to sequence control in the driver.
10661711025Swnj  */
10761711025Swnj #define	SSEEK	1		/* seeking */
10861711025Swnj #define	SIO	2		/* doing seq i/o */
10961711025Swnj #define	SCOM	3		/* sending control command */
11061711025Swnj #define	SREW	4		/* sending a drive rewind */
111496a8a15Swnj 
11261711025Swnj /*
11361711025Swnj  * Determine if there is a controller for
11461711025Swnj  * a ts at address reg.  Our goal is to make the
11561711025Swnj  * device interrupt.
11661711025Swnj  */
1179ecf70a5Sroot /*ARGSUSED*/
tsprobe(reg,ctlr,um)11807424adbSbostic tsprobe(reg, ctlr, um)
11961711025Swnj 	caddr_t reg;
12007424adbSbostic 	int ctlr;
12107424adbSbostic 	struct uba_ctlr *um;
12261711025Swnj {
12361711025Swnj 	register int br, cvec;		/* must be r11,r10; value-result */
12407424adbSbostic 	register struct tsdevice *addr = (struct tsdevice *)reg;
12507424adbSbostic 	register struct ts_softc *sc;
12607424adbSbostic 	register int i;
127496a8a15Swnj 
12861711025Swnj #ifdef lint
12961711025Swnj 	br = 0; cvec = br; br = cvec;
130da26c61fSwnj 	tsintr(0);
13161711025Swnj #endif
13207424adbSbostic 	addr->tssr = 0;		/* initialize subsystem */
133397f88ceSroot 	DELAY(100);
13407424adbSbostic 	if ((addr->tssr & TS_NBA) == 0)
135410fb73dSroot 		return (0);
13607424adbSbostic 
13707424adbSbostic 	/*
13807424adbSbostic 	 * Make it interrupt.
13907424adbSbostic 	 * TS_SETCHR|TS_IE alone refuses to interrupt for me.
14007424adbSbostic 	 */
14107424adbSbostic 	sc = &ts_softc[ctlr];
142*d72cfc4fStorek 	tsmap(sc, numuba);
14307424adbSbostic 	i = (int)&sc->sc_ubaddr->t_char;
14407424adbSbostic 	sc->sc_ts.t_cmd.c_loba = i;
14507424adbSbostic 	sc->sc_ts.t_cmd.c_hiba = (i >> 16) & 3;
14607424adbSbostic 	sc->sc_ts.t_cmd.c_size = sizeof(struct ts_char);
14707424adbSbostic 	sc->sc_ts.t_cmd.c_cmd = TS_ACK | TS_SETCHR;
14807424adbSbostic 	sc->sc_ts.t_char.char_addr = (int)&sc->sc_ubaddr->t_sts;
14907424adbSbostic 	sc->sc_ts.t_char.char_size = sizeof(struct ts_sts);
15007424adbSbostic 	sc->sc_ts.t_char.char_mode = 0;	/* mode is unimportant */
15107424adbSbostic 	addr->tsdb = sc->sc_uba;
15207424adbSbostic 	DELAY(20000);
15307424adbSbostic 	sc->sc_ts.t_cmd.c_cmd = TS_ACK | TS_CVC | TS_IE | TS_SENSE;
15407424adbSbostic 	sc->sc_ts.t_cmd.c_repcnt = 1;
15507424adbSbostic 	addr->tsdb = sc->sc_uba;
15607424adbSbostic 	DELAY(20000);
157*d72cfc4fStorek 	/*
158*d72cfc4fStorek 	 * The controller should have interrupted by now, but some do not,
159*d72cfc4fStorek 	 * even if the delays above are extended to many seconds.  If the
160*d72cfc4fStorek 	 * vector is still unknown, we assume the drive is present at
161*d72cfc4fStorek 	 * the usual vector.
162*d72cfc4fStorek 	 */
163*d72cfc4fStorek 	if (cvec == 0 || cvec == 0x200)	{
164*d72cfc4fStorek 		cvec = (int)reg & 7 ? 0260 : 0224;
165*d72cfc4fStorek 		br = 0x15;
166*d72cfc4fStorek 	}
16734fcfd6bSkre 	return (sizeof(struct tsdevice));
16861711025Swnj }
169496a8a15Swnj 
17061711025Swnj /*
17107424adbSbostic  * Map in the command, status, and characteristics packet.  We
17207424adbSbostic  * make them contiguous to keep overhead down.  This also sets
17307424adbSbostic  * sc_uba (which then never changes).
17407424adbSbostic  */
tsmap(sc,uban)175*d72cfc4fStorek tsmap(sc, uban)
17607424adbSbostic 	register struct ts_softc *sc;
177*d72cfc4fStorek 	int uban;
17807424adbSbostic {
17907424adbSbostic 	register int i;
18007424adbSbostic 
18107424adbSbostic 	i = uballoc(uban, (caddr_t)&sc->sc_ts, sizeof(sc->sc_ts), 0);
18207424adbSbostic 	i = UBAI_ADDR(i);
18307424adbSbostic 	sc->sc_ubaddr = (struct ts_tsdata *)i;
18407424adbSbostic 	/*
18507424adbSbostic 	 * Note that i == the Unibus address of the command packet,
18607424adbSbostic 	 * and that it is a multiple of 4 (guaranteed by the compiler).
18707424adbSbostic 	 */
18807424adbSbostic 	sc->sc_uba = i + ((i >> 16) & 3);
18907424adbSbostic }
19007424adbSbostic 
19107424adbSbostic /*
19261711025Swnj  * TS11 only supports one drive per controller;
19361711025Swnj  * check for ui_slave == 0.
19461711025Swnj  */
19561711025Swnj /*ARGSUSED*/
19661711025Swnj tsslave(ui, reg)
19761711025Swnj 	struct uba_device *ui;
19861711025Swnj 	caddr_t reg;
19961711025Swnj {
200496a8a15Swnj 
20107424adbSbostic 	return (ui->ui_slave == 0);	/* non-zero slave not allowed */
20261711025Swnj }
203496a8a15Swnj 
20461711025Swnj /*
20561711025Swnj  * Record attachment of the unit to the controller.
20661711025Swnj  */
20761711025Swnj /*ARGSUSED*/
20861711025Swnj tsattach(ui)
20961711025Swnj 	struct uba_device *ui;
21061711025Swnj {
211496a8a15Swnj 
21207424adbSbostic 	/* void */
21361711025Swnj }
214496a8a15Swnj 
21561711025Swnj /*
21661711025Swnj  * Open the device.  Tapes are unique open
21761711025Swnj  * devices, so we refuse if it is already open.
21861711025Swnj  */
tsopen(dev,flag)219496a8a15Swnj tsopen(dev, flag)
22061711025Swnj 	dev_t dev;
22161711025Swnj 	int flag;
222496a8a15Swnj {
22307424adbSbostic 	register int tsunit = TSUNIT(dev);
22461711025Swnj 	register struct uba_device *ui;
22561711025Swnj 	register struct ts_softc *sc;
226496a8a15Swnj 
2277a535476Skarels 	if (tsunit >= NTS || (ui = tsdinfo[tsunit]) == 0 || ui->ui_alive == 0)
2285daf0196Sroot 		return (ENXIO);
22907424adbSbostic 	if ((sc = &ts_softc[ui->ui_ctlr])->sc_openf)
2307a535476Skarels 		return (EBUSY);
231edb110f3Skarels 	sc->sc_openf = 1;
23207424adbSbostic 	sc->sc_density = (minor(dev) & T_1600BPI) ? TS_NRZI : 0;
23307424adbSbostic 	tscommand(dev, TS_SENSE, 1);
23407424adbSbostic 	if (ctsbuf[tsunit].b_flags & B_ERROR)
23507424adbSbostic 		/*
23607424adbSbostic 		 * Try it again in case it went off-line
23707424adbSbostic 		 */
23807424adbSbostic 		tscommand(dev, TS_SENSE, 1);
23907424adbSbostic 	if (tsinit(ui->ui_ctlr)) {
240edb110f3Skarels 		sc->sc_openf = 0;
2415daf0196Sroot 		return (ENXIO);
242edb110f3Skarels 	}
24307424adbSbostic 	if ((sc->sc_ts.t_sts.s_xs0&TS_ONL) == 0) {
244edb110f3Skarels 		sc->sc_openf = 0;
245a44e57d5Sroot 		uprintf("ts%d: not online\n", tsunit);
2465daf0196Sroot 		return (EIO);
247a44e57d5Sroot 	}
24807424adbSbostic 	if ((flag&FWRITE) && (sc->sc_ts.t_sts.s_xs0&TS_WLK)) {
249edb110f3Skarels 		sc->sc_openf = 0;
250a44e57d5Sroot 		uprintf("ts%d: no write ring\n", tsunit);
2515daf0196Sroot 		return (EIO);
252496a8a15Swnj 	}
25361711025Swnj 	sc->sc_blkno = (daddr_t)0;
25461711025Swnj 	sc->sc_nxrec = INF;
25561711025Swnj 	sc->sc_lastiow = 0;
256edb110f3Skarels 	sc->sc_blks = 0;
257edb110f3Skarels 	sc->sc_softerrs = 0;
258bf89ced6Smarc 	sc->sc_tpr = tprintf_open();
2595daf0196Sroot 	return (0);
260496a8a15Swnj }
261496a8a15Swnj 
26261711025Swnj /*
26361711025Swnj  * Close tape device.
26461711025Swnj  *
26561711025Swnj  * If tape was open for writing or last operation was
26661711025Swnj  * a write, then write two EOF's and backspace over the last one.
26761711025Swnj  * Unless this is a non-rewinding special file, rewind the tape.
26861711025Swnj  * Make the tape available to others.
26961711025Swnj  */
tsclose(dev,flag)270496a8a15Swnj tsclose(dev, flag)
27161711025Swnj 	register dev_t dev;
27207424adbSbostic 	register int flag;
273496a8a15Swnj {
27461711025Swnj 	register struct ts_softc *sc = &ts_softc[TSUNIT(dev)];
275496a8a15Swnj 
27661711025Swnj 	if (flag == FWRITE || (flag&FWRITE) && sc->sc_lastiow) {
27761711025Swnj 		tscommand(dev, TS_WEOF, 1);
27861711025Swnj 		tscommand(dev, TS_WEOF, 1);
27961711025Swnj 		tscommand(dev, TS_SREV, 1);
280496a8a15Swnj 	}
28161711025Swnj 	if ((minor(dev)&T_NOREWIND) == 0)
28261711025Swnj 		/*
28361711025Swnj 		 * 0 count means don't hang waiting for rewind complete
28461711025Swnj 		 * rather ctsbuf stays busy until the operation completes
28561711025Swnj 		 * preventing further opens from completing by
28661711025Swnj 		 * preventing a TS_SENSE from completing.
28761711025Swnj 		 */
28861711025Swnj 		tscommand(dev, TS_REW, 0);
289edb110f3Skarels 	if (sc->sc_blks > 100 && sc->sc_softerrs > sc->sc_blks / 100)
290edb110f3Skarels 		log(LOG_INFO, "ts%d: %d soft errors in %d blocks\n",
291edb110f3Skarels 		    TSUNIT(dev), sc->sc_softerrs, sc->sc_blks);
292bf89ced6Smarc 	tprintf_close(sc->sc_tpr);
29361711025Swnj 	sc->sc_openf = 0;
29415ca0f5aSmarc 	return (0);
295496a8a15Swnj }
296496a8a15Swnj 
29761711025Swnj /*
29807424adbSbostic  * Initialize a TS11.  Set device characteristics.
29961711025Swnj  */
tsinit(ctlr)30007424adbSbostic tsinit(ctlr)
30107424adbSbostic 	register int ctlr;
30261711025Swnj {
30307424adbSbostic 	register struct ts_softc *sc = &ts_softc[ctlr];
30407424adbSbostic 	register struct uba_ctlr *um = tsminfo[ctlr];
305397f88ceSroot 	register struct tsdevice *addr = (struct tsdevice *)um->um_addr;
30661711025Swnj 	register int i;
30761711025Swnj 
30807424adbSbostic 	if (addr->tssr & (TS_NBA|TS_OFL) || sc->sc_ts.t_sts.s_xs0 & TS_BOT) {
30961711025Swnj 		addr->tssr = 0;		/* subsystem initialize */
31061711025Swnj 		tswait(addr);
31107424adbSbostic 		i = (int)&sc->sc_ubaddr->t_char;
31207424adbSbostic 		sc->sc_ts.t_cmd.c_loba = i;
31307424adbSbostic 		sc->sc_ts.t_cmd.c_hiba = (i >> 16) & 3;
31407424adbSbostic 		sc->sc_ts.t_cmd.c_size = sizeof(struct ts_char);
31507424adbSbostic 		sc->sc_ts.t_cmd.c_cmd = TS_ACK | TS_CVC | TS_SETCHR;
31607424adbSbostic 		sc->sc_ts.t_char.char_addr = (int)&sc->sc_ubaddr->t_sts;
31707424adbSbostic 		sc->sc_ts.t_char.char_size = sizeof(struct ts_sts);
31807424adbSbostic 		sc->sc_ts.t_char.char_mode = TS_ESS | TS_EAI | TS_ERI |
31907424adbSbostic 			/* TS_ENB | */ sc->sc_density;
32061711025Swnj 		addr->tsdb = sc->sc_uba;
32161711025Swnj 		tswait(addr);
3224a2c0eeaSwnj 		if (addr->tssr & TS_NBA)
3234a2c0eeaSwnj 			return(1);
32461711025Swnj 	}
32561711025Swnj 	return(0);
32661711025Swnj }
32761711025Swnj 
32861711025Swnj /*
32961711025Swnj  * Execute a command on the tape drive
33061711025Swnj  * a specified number of times.
33161711025Swnj  */
tscommand(dev,com,count)33261711025Swnj tscommand(dev, com, count)
33361711025Swnj 	dev_t dev;
33461711025Swnj 	int com, count;
335496a8a15Swnj {
336496a8a15Swnj 	register struct buf *bp;
337a268cb0dSroot 	register int s;
33807424adbSbostic 	int didsleep = 0;
339496a8a15Swnj 
34061711025Swnj 	bp = &ctsbuf[TSUNIT(dev)];
341a268cb0dSroot 	s = spl5();
342496a8a15Swnj 	while (bp->b_flags&B_BUSY) {
34361711025Swnj 		/*
34461711025Swnj 		 * This special check is because B_BUSY never
34561711025Swnj 		 * gets cleared in the non-waiting rewind case.
34661711025Swnj 		 */
34761711025Swnj 		if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE))
34861711025Swnj 			break;
349496a8a15Swnj 		bp->b_flags |= B_WANTED;
350496a8a15Swnj 		sleep((caddr_t)bp, PRIBIO);
35107424adbSbostic 		didsleep = 1;
352496a8a15Swnj 	}
353496a8a15Swnj 	bp->b_flags = B_BUSY|B_READ;
354a268cb0dSroot 	splx(s);
35507424adbSbostic 	if (didsleep)
35607424adbSbostic 		(void) tsinit(tsdinfo[TSUNIT(dev)]->ui_ctlr);
35761711025Swnj 	bp->b_dev = dev;
35861711025Swnj 	bp->b_repcnt = count;
35961711025Swnj 	bp->b_command = com;
36061711025Swnj 	bp->b_blkno = 0;
361496a8a15Swnj 	tsstrategy(bp);
36261711025Swnj 	/*
36361711025Swnj 	 * In case of rewind from close, don't wait.
36461711025Swnj 	 * This is the only case where count can be 0.
36561711025Swnj 	 */
36661711025Swnj 	if (count == 0)
36761711025Swnj 		return;
36807424adbSbostic 	biowait(bp);
369496a8a15Swnj 	if (bp->b_flags&B_WANTED)
370496a8a15Swnj 		wakeup((caddr_t)bp);
37161711025Swnj 	bp->b_flags &= B_ERROR;
372496a8a15Swnj }
373496a8a15Swnj 
37461711025Swnj /*
37561711025Swnj  * Queue a tape operation.
37661711025Swnj  */
tsstrategy(bp)377496a8a15Swnj tsstrategy(bp)
378496a8a15Swnj 	register struct buf *bp;
379496a8a15Swnj {
38007424adbSbostic 	register int tsunit = TSUNIT(bp->b_dev);
38161711025Swnj 	register struct uba_ctlr *um;
3824a2c0eeaSwnj 	register struct buf *dp;
38307424adbSbostic 	int s;
384496a8a15Swnj 
38561711025Swnj 	/*
38661711025Swnj 	 * Put transfer at end of controller queue
38761711025Swnj 	 */
388496a8a15Swnj 	bp->av_forw = NULL;
38961711025Swnj 	um = tsdinfo[tsunit]->ui_mi;
390397f88ceSroot 	dp = &tsutab[tsunit];
39107424adbSbostic 	s = spl5();
3924a2c0eeaSwnj 	if (dp->b_actf == NULL)
3934a2c0eeaSwnj 		dp->b_actf = bp;
394496a8a15Swnj 	else
3954a2c0eeaSwnj 		dp->b_actl->av_forw = bp;
3964a2c0eeaSwnj 	dp->b_actl = bp;
3974a2c0eeaSwnj 	um->um_tab.b_actf = um->um_tab.b_actl = dp;
39861711025Swnj 	/*
39961711025Swnj 	 * If the controller is not busy, get
40061711025Swnj 	 * it going.
40161711025Swnj 	 */
40261711025Swnj 	if (um->um_tab.b_active == 0)
40361711025Swnj 		tsstart(um);
404a268cb0dSroot 	splx(s);
405496a8a15Swnj }
406496a8a15Swnj 
40761711025Swnj /*
40861711025Swnj  * Start activity on a ts controller.
40961711025Swnj  */
tsstart(um)41061711025Swnj tsstart(um)
41161711025Swnj 	register struct uba_ctlr *um;
412496a8a15Swnj {
413496a8a15Swnj 	register struct buf *bp;
414397f88ceSroot 	register struct tsdevice *addr = (struct tsdevice *)um->um_addr;
41561711025Swnj 	register struct ts_softc *sc;
41661711025Swnj 	register struct uba_device *ui;
41707424adbSbostic 	register int tsunit;
41807424adbSbostic 	int cmd;
419496a8a15Swnj 	daddr_t blkno;
420496a8a15Swnj 
42161711025Swnj 	/*
42261711025Swnj 	 * Start the controller if there is something for it to do.
42361711025Swnj 	 */
424496a8a15Swnj loop:
4258f0c8485Sbostic 	if ((bp = um->um_tab.b_actf->b_actf) == NULL) {
4268f0c8485Sbostic 		um->um_tab.b_active = 0;
427496a8a15Swnj 		return;
4288f0c8485Sbostic 	}
42961711025Swnj 	tsunit = TSUNIT(bp->b_dev);
43061711025Swnj 	ui = tsdinfo[tsunit];
43161711025Swnj 	sc = &ts_softc[tsunit];
43261711025Swnj 	/*
43361711025Swnj 	 * Default is that last command was NOT a write command;
43407424adbSbostic 	 * if we finish a write command we will notice this in tsintr().
43561711025Swnj 	 */
43696f07b01Swnj 	sc->sc_lastiow = 0;
43761711025Swnj 	if (sc->sc_openf < 0 || (addr->tssr&TS_OFL)) {
43861711025Swnj 		/*
43961711025Swnj 		 * Have had a hard error on a non-raw tape
44061711025Swnj 		 * or the tape unit is now unavailable
44161711025Swnj 		 * (e.g. taken off line).
44261711025Swnj 		 */
443496a8a15Swnj 		bp->b_flags |= B_ERROR;
44461711025Swnj 		goto next;
44561711025Swnj 	}
44607424adbSbostic 	if (bp == &ctsbuf[tsunit]) {
44761711025Swnj 		/*
44861711025Swnj 		 * Execute control operation with the specified count.
44961711025Swnj 		 */
45061711025Swnj 		um->um_tab.b_active =
45161711025Swnj 		    bp->b_command == TS_REW ? SREW : SCOM;
45207424adbSbostic 		sc->sc_ts.t_cmd.c_repcnt = bp->b_repcnt;
45361711025Swnj 		goto dobpcmd;
45461711025Swnj 	}
45561711025Swnj 	/*
45607424adbSbostic 	 * For raw I/O, save the current block
45707424adbSbostic 	 * number in case we have to retry.
45807424adbSbostic 	 */
459de98104dSbostic 	if (bp->b_flags & B_RAW) {
46007424adbSbostic 		if (um->um_tab.b_errcnt == 0)
46107424adbSbostic 			sc->sc_blkno = bdbtofsb(bp->b_blkno);
46207424adbSbostic 	} else {
46307424adbSbostic 		/*
46407424adbSbostic 		 * Handle boundary cases for operation
46507424adbSbostic 		 * on non-raw tapes.
46661711025Swnj 		 */
467fa47873fSsam 		if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) {
46861711025Swnj 			/*
46961711025Swnj 			 * Can't read past known end-of-file.
47061711025Swnj 			 */
47161711025Swnj 			bp->b_flags |= B_ERROR;
47261711025Swnj 			bp->b_error = ENXIO;
47361711025Swnj 			goto next;
47461711025Swnj 		}
475fa47873fSsam 		if (bdbtofsb(bp->b_blkno) == sc->sc_nxrec &&
47661711025Swnj 		    bp->b_flags&B_READ) {
47761711025Swnj 			/*
47861711025Swnj 			 * Reading at end of file returns 0 bytes.
47961711025Swnj 			 */
48061711025Swnj 			bp->b_resid = bp->b_bcount;
48161711025Swnj 			clrbuf(bp);
48261711025Swnj 			goto next;
48361711025Swnj 		}
48461711025Swnj 		if ((bp->b_flags&B_READ) == 0)
48561711025Swnj 			/*
48661711025Swnj 			 * Writing sets EOF
48761711025Swnj 			 */
488fa47873fSsam 			sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
48907424adbSbostic 	}
49007424adbSbostic 
49161711025Swnj 	/*
49261711025Swnj 	 * If the data transfer command is in the correct place,
49361711025Swnj 	 * set up all the registers except the csr, and give
49461711025Swnj 	 * control over to the UNIBUS adapter routines, to
49561711025Swnj 	 * wait for resources to start the i/o.
49661711025Swnj 	 */
497fa47873fSsam 	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
49807424adbSbostic 		sc->sc_ts.t_cmd.c_size = bp->b_bcount;
49961711025Swnj 		if ((bp->b_flags&B_READ) == 0)
50061711025Swnj 			cmd = TS_WCOM;
50161711025Swnj 		else
50261711025Swnj 			cmd = TS_RCOM;
50361711025Swnj 		if (um->um_tab.b_errcnt)
50461711025Swnj 			cmd |= TS_RETRY;
50561711025Swnj 		um->um_tab.b_active = SIO;
50607424adbSbostic 		sc->sc_ts.t_cmd.c_cmd = TS_ACK | TS_CVC | TS_IE | cmd;
50761711025Swnj 		(void) ubago(ui);
50861711025Swnj 		return;
50961711025Swnj 	}
51061711025Swnj 	/*
51161711025Swnj 	 * Tape positioned incorrectly;
51261711025Swnj 	 * set to seek forwards or backwards to the correct spot.
51361711025Swnj 	 * This happens for raw tapes only on error retries.
51461711025Swnj 	 */
51561711025Swnj 	um->um_tab.b_active = SSEEK;
516fa47873fSsam 	if (blkno < bdbtofsb(bp->b_blkno)) {
51761711025Swnj 		bp->b_command = TS_SFORW;
51807424adbSbostic 		sc->sc_ts.t_cmd.c_repcnt = bdbtofsb(bp->b_blkno) - blkno;
51961711025Swnj 	} else {
52061711025Swnj 		bp->b_command = TS_SREV;
52107424adbSbostic 		sc->sc_ts.t_cmd.c_repcnt = blkno - bdbtofsb(bp->b_blkno);
52261711025Swnj 	}
52361711025Swnj dobpcmd:
52461711025Swnj 	/*
52561711025Swnj 	 * Do the command in bp.
52661711025Swnj 	 */
52707424adbSbostic 	sc->sc_ts.t_cmd.c_cmd = TS_ACK | TS_CVC | TS_IE | bp->b_command;
52861711025Swnj 	addr->tsdb = sc->sc_uba;
52961711025Swnj 	return;
530496a8a15Swnj 
531496a8a15Swnj next:
53261711025Swnj 	/*
53361711025Swnj 	 * Done with this operation due to error or
53461711025Swnj 	 * the fact that it doesn't do anything.
53561711025Swnj 	 * Release UBA resources (if any), dequeue
53661711025Swnj 	 * the transfer and continue processing this slave.
53761711025Swnj 	 */
53861711025Swnj 	if (um->um_ubinfo)
53961711025Swnj 		ubadone(um);
54061711025Swnj 	um->um_tab.b_errcnt = 0;
5414a2c0eeaSwnj 	um->um_tab.b_actf->b_actf = bp->av_forw;
54207424adbSbostic 	biodone(bp);
543496a8a15Swnj 	goto loop;
544496a8a15Swnj }
545496a8a15Swnj 
54661711025Swnj /*
54761711025Swnj  * The UNIBUS resources we needed have been
54861711025Swnj  * allocated to us; start the device.
54961711025Swnj  */
tsdgo(um)55061711025Swnj tsdgo(um)
55161711025Swnj 	register struct uba_ctlr *um;
55261711025Swnj {
55361711025Swnj 	register struct ts_softc *sc = &ts_softc[um->um_ctlr];
5544a2c0eeaSwnj 	register int i;
55561711025Swnj 
556fbea7ab0Skarels 	/*
557fbea7ab0Skarels 	 * The uba code uses byte-offset mode if using bdp;
558fbea7ab0Skarels 	 * mask off the low bit here.
559fbea7ab0Skarels 	 */
56007424adbSbostic 	i = UBAI_ADDR(um->um_ubinfo);
561fbea7ab0Skarels 	if (UBAI_BDP(um->um_ubinfo))
562fbea7ab0Skarels 		i &= ~1;
56307424adbSbostic 	sc->sc_ts.t_cmd.c_loba = i;
56407424adbSbostic 	sc->sc_ts.t_cmd.c_hiba = (i >> 16) & 3;
56507424adbSbostic 	((struct tsdevice *)um->um_addr)->tsdb = sc->sc_uba;
56661711025Swnj }
56761711025Swnj 
56861711025Swnj /*
56961711025Swnj  * Ts interrupt routine.
57061711025Swnj  */
57161711025Swnj /*ARGSUSED*/
tsintr(tsunit)57207424adbSbostic tsintr(tsunit)
57307424adbSbostic 	register int tsunit;
574496a8a15Swnj {
575496a8a15Swnj 	register struct buf *bp;
57607424adbSbostic 	register struct uba_ctlr *um;
577397f88ceSroot 	register struct tsdevice *addr;
57861711025Swnj 	register struct ts_softc *sc;
57907424adbSbostic 	register int state;
58007424adbSbostic 
581ef1739d4Stef #ifdef QBA
58207424adbSbostic 	(void) spl5();
583d6876171Skridle #endif
58407424adbSbostic 	um = tsdinfo[tsunit]->ui_mi;
5854a2c0eeaSwnj 	if ((bp = um->um_tab.b_actf->b_actf) == NULL)
586496a8a15Swnj 		return;
58707424adbSbostic 	addr = (struct tsdevice *)um->um_addr;
58861711025Swnj 	/*
58961711025Swnj 	 * If last command was a rewind, and tape is still
59061711025Swnj 	 * rewinding, wait for the rewind complete interrupt.
59161711025Swnj 	 *
59261711025Swnj 	 * SHOULD NEVER GET AN INTERRUPT IN THIS STATE.
59361711025Swnj 	 */
59461711025Swnj 	if (um->um_tab.b_active == SREW) {
59561711025Swnj 		um->um_tab.b_active = SCOM;
59661711025Swnj 		if ((addr->tssr&TS_SSR) == 0)
597496a8a15Swnj 			return;
598496a8a15Swnj 	}
59961711025Swnj 	/*
60061711025Swnj 	 * An operation completed... record status
60161711025Swnj 	 */
60207424adbSbostic 	sc = &ts_softc[um->um_ctlr];
60361711025Swnj 	if ((bp->b_flags & B_READ) == 0)
60461711025Swnj 		sc->sc_lastiow = 1;
60561711025Swnj 	state = um->um_tab.b_active;
60661711025Swnj 	/*
60761711025Swnj 	 * Check for errors.
60861711025Swnj 	 */
60961711025Swnj 	if (addr->tssr&TS_SC) {
61061711025Swnj 		switch (addr->tssr & TS_TC) {
61161711025Swnj 		case TS_UNREC:		/* unrecoverable */
61261711025Swnj 		case TS_FATAL:		/* fatal error */
61361711025Swnj 		case TS_RECNM:		/* recoverable, no motion */
614496a8a15Swnj 			break;
61507424adbSbostic 		case TS_ATTN:		/* attention */
61607424adbSbostic 			if (sc->sc_ts.t_sts.s_xs0 & TS_VCK) {
61707424adbSbostic 				/* volume check - may have changed tapes */
61807424adbSbostic 				bp->b_flags |= B_ERROR;
61907424adbSbostic 				goto ignoreerr;
62007424adbSbostic 			}
62107424adbSbostic 			break;
622496a8a15Swnj 
62361711025Swnj 		case TS_SUCC:		/* success termination */
62407424adbSbostic 			printf("ts%d: success\n", tsunit);
62561711025Swnj 			goto ignoreerr;
62661711025Swnj 
62761711025Swnj 		case TS_ALERT:		/* tape status alert */
62861711025Swnj 			/*
62961711025Swnj 			 * If we hit the end of the tape file,
63061711025Swnj 			 * update our position.
63161711025Swnj 			 */
63207424adbSbostic 			if (sc->sc_ts.t_sts.s_xs0 & (TS_TMK|TS_EOT)) {
63361711025Swnj 				tsseteof(bp);	/* set blkno and nxrec */
63461711025Swnj 				state = SCOM;	/* force completion */
63561711025Swnj 				/*
63661711025Swnj 				 * Stuff bc so it will be unstuffed correctly
63761711025Swnj 				 * later to get resid.
63861711025Swnj 				 */
63907424adbSbostic 				sc->sc_ts.t_sts.s_rbpcr = bp->b_bcount;
64061711025Swnj 				goto opdone;
641496a8a15Swnj 			}
64261711025Swnj 			/*
64307424adbSbostic 			 * If we were reading raw tape and the record was too
64407424adbSbostic 			 * long or too short, we don't consider this an error.
64561711025Swnj 			 */
646de98104dSbostic 			if ((bp->b_flags & (B_READ|B_RAW)) == (B_READ|B_RAW) &&
64707424adbSbostic 			    sc->sc_ts.t_sts.s_xs0&(TS_RLS|TS_RLL))
64861711025Swnj 				goto ignoreerr;
64907424adbSbostic 			/* FALLTHROUGH */
65007424adbSbostic 
65161711025Swnj 		case TS_RECOV:		/* recoverable, tape moved */
65261711025Swnj 			/*
65361711025Swnj 			 * If this was an i/o operation retry up to 8 times.
65461711025Swnj 			 */
65561711025Swnj 			if (state == SIO) {
65661711025Swnj 				if (++um->um_tab.b_errcnt < 7) {
65761711025Swnj 					ubadone(um);
65861711025Swnj 					goto opcont;
65961711025Swnj 				} else
66061711025Swnj 					sc->sc_blkno++;
66161711025Swnj 			} else {
66261711025Swnj 				/*
66361711025Swnj 				 * Non-i/o errors on non-raw tape
66461711025Swnj 				 * cause it to close.
66561711025Swnj 				 */
666de98104dSbostic 				if ((bp->b_flags&B_RAW) == 0 &&
667de98104dSbostic 				    sc->sc_openf > 0)
66861711025Swnj 					sc->sc_openf = -1;
66961711025Swnj 			}
67061711025Swnj 			break;
67161711025Swnj 
67261711025Swnj 		case TS_REJECT:		/* function reject */
67307424adbSbostic 			if (state == SIO && sc->sc_ts.t_sts.s_xs0 & TS_WLE)
674bf89ced6Smarc 				tprintf(sc->sc_tpr, "ts%d: write locked\n",
67507424adbSbostic 				    tsunit);
67607424adbSbostic 			if ((sc->sc_ts.t_sts.s_xs0 & TS_ONL) == 0)
677bf89ced6Smarc 				tprintf(sc->sc_tpr, "ts%d: offline\n",
67807424adbSbostic 				    tsunit);
67961711025Swnj 			break;
68061711025Swnj 		}
68161711025Swnj 		/*
68261711025Swnj 		 * Couldn't recover error
68361711025Swnj 		 */
684bf89ced6Smarc 		tprintf(sc->sc_tpr, "ts%d: hard error bn%d tssr=%b xs0=%b",
68507424adbSbostic 		    tsunit, bp->b_blkno, addr->tssr, TSSR_BITS,
68607424adbSbostic 		    sc->sc_ts.t_sts.s_xs0, TSXS0_BITS);
68707424adbSbostic 		if (sc->sc_ts.t_sts.s_xs1)
688bf89ced6Smarc 			tprintf(sc->sc_tpr, " xs1=%b", sc->sc_ts.t_sts.s_xs1,
68949365b14Sralph 			    TSXS1_BITS);
69007424adbSbostic 		if (sc->sc_ts.t_sts.s_xs2)
691bf89ced6Smarc 			tprintf(sc->sc_tpr, " xs2=%b", sc->sc_ts.t_sts.s_xs2,
69249365b14Sralph 			    TSXS2_BITS);
69307424adbSbostic 		if (sc->sc_ts.t_sts.s_xs3)
694bf89ced6Smarc 			tprintf(sc->sc_tpr, " xs3=%b", sc->sc_ts.t_sts.s_xs3,
69549365b14Sralph 			    TSXS3_BITS);
696bf89ced6Smarc 		tprintf(sc->sc_tpr, "\n");
69761711025Swnj 		bp->b_flags |= B_ERROR;
69861711025Swnj 		goto opdone;
69961711025Swnj 	}
70061711025Swnj 	/*
70161711025Swnj 	 * Advance tape control FSM.
70261711025Swnj 	 */
70361711025Swnj ignoreerr:
704496a8a15Swnj 	switch (state) {
70561711025Swnj 
706496a8a15Swnj 	case SIO:
70761711025Swnj 		/*
70861711025Swnj 		 * Read/write increments tape block number
70961711025Swnj 		 */
71061711025Swnj 		sc->sc_blkno++;
711edb110f3Skarels 		sc->sc_blks++;
712edb110f3Skarels 		if (um->um_tab.b_errcnt)
713edb110f3Skarels 			sc->sc_softerrs++;
71461711025Swnj 		goto opdone;
71561711025Swnj 
716496a8a15Swnj 	case SCOM:
71761711025Swnj 		/*
71861711025Swnj 		 * For forward/backward space record update current position.
71961711025Swnj 		 */
72007424adbSbostic 		if (bp == &ctsbuf[tsunit])
721885758c4Skarels 			switch ((int)bp->b_command) {
72261711025Swnj 
72361711025Swnj 			case TS_SFORW:
72461711025Swnj 				sc->sc_blkno += bp->b_repcnt;
725496a8a15Swnj 				break;
726496a8a15Swnj 
72761711025Swnj 			case TS_SREV:
72861711025Swnj 				sc->sc_blkno -= bp->b_repcnt;
729496a8a15Swnj 				break;
73061711025Swnj 			}
73161711025Swnj 		goto opdone;
73261711025Swnj 
73361711025Swnj 	case SSEEK:
734fa47873fSsam 		sc->sc_blkno = bdbtofsb(bp->b_blkno);
73561711025Swnj 		goto opcont;
736496a8a15Swnj 
737496a8a15Swnj 	default:
73861711025Swnj 		panic("tsintr");
739496a8a15Swnj 	}
74061711025Swnj opdone:
74161711025Swnj 	/*
74261711025Swnj 	 * Reset error count and remove
74361711025Swnj 	 * from device queue.
74461711025Swnj 	 */
74561711025Swnj 	um->um_tab.b_errcnt = 0;
7464a2c0eeaSwnj 	um->um_tab.b_actf->b_actf = bp->av_forw;
74707424adbSbostic 	bp->b_resid = sc->sc_ts.t_sts.s_rbpcr;
74861711025Swnj 	ubadone(um);
74907424adbSbostic 	biodone(bp);
75007424adbSbostic 	if (um->um_tab.b_actf->b_actf == 0) {
75107424adbSbostic 		um->um_tab.b_active = 0;
75261711025Swnj 		return;
75307424adbSbostic 	}
75461711025Swnj opcont:
75561711025Swnj 	tsstart(um);
756496a8a15Swnj }
75761711025Swnj 
tsseteof(bp)75861711025Swnj tsseteof(bp)
75961711025Swnj 	register struct buf *bp;
76061711025Swnj {
76161711025Swnj 	register int tsunit = TSUNIT(bp->b_dev);
76207424adbSbostic 	register struct ts_softc *sc = &ts_softc[tsdinfo[tsunit]->ui_ctlr];
76361711025Swnj 
76407424adbSbostic 	if (bp == &ctsbuf[tsunit]) {
765fa47873fSsam 		if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
76661711025Swnj 			/* reversing */
76707424adbSbostic 			sc->sc_nxrec = bdbtofsb(bp->b_blkno) -
76807424adbSbostic 				sc->sc_ts.t_sts.s_rbpcr;
76961711025Swnj 			sc->sc_blkno = sc->sc_nxrec;
77061711025Swnj 		} else {
77161711025Swnj 			/* spacing forward */
77207424adbSbostic 			sc->sc_blkno = bdbtofsb(bp->b_blkno) +
77307424adbSbostic 				sc->sc_ts.t_sts.s_rbpcr;
77461711025Swnj 			sc->sc_nxrec = sc->sc_blkno - 1;
775496a8a15Swnj 		}
77661711025Swnj 		return;
77761711025Swnj 	}
77861711025Swnj 	/* eof on read */
779fa47873fSsam 	sc->sc_nxrec = bdbtofsb(bp->b_blkno);
780496a8a15Swnj }
781496a8a15Swnj 
tsreset(uban)78261711025Swnj tsreset(uban)
78361711025Swnj 	int uban;
7846a55db31Swnj {
78561711025Swnj 	register struct uba_ctlr *um;
786397f88ceSroot 	register struct uba_device *ui;
78707424adbSbostic 	register int ts11, i;
7886a55db31Swnj 
78961711025Swnj 	for (ts11 = 0; ts11 < NTS; ts11++) {
79061711025Swnj 		if ((um = tsminfo[ts11]) == 0 || um->um_alive == 0 ||
79161711025Swnj 		    um->um_ubanum != uban)
79261711025Swnj 			continue;
79361711025Swnj 		printf(" ts%d", ts11);
79461711025Swnj 		um->um_tab.b_active = 0;
79561711025Swnj 		um->um_tab.b_actf = um->um_tab.b_actl = 0;
796397f88ceSroot 		if (ts_softc[ts11].sc_openf > 0)
79761711025Swnj 			ts_softc[ts11].sc_openf = -1;
79861711025Swnj 		if (um->um_ubinfo) {
79907424adbSbostic 			printf("<%d>", UBAI_BDP(um->um_ubinfo));
80029797bd5Ssam 			um->um_ubinfo = 0;
8016a55db31Swnj 		}
80207424adbSbostic 		/*
80307424adbSbostic 		 * tsdinfo should be 1-to-1 with tsminfo, but someone
80407424adbSbostic 		 * might have screwed up the config file:
80507424adbSbostic 		 */
80607424adbSbostic 		for (i = 0; i < NTS; i++) {
80707424adbSbostic 			if ((ui = tsdinfo[i]) != NULL &&
80807424adbSbostic 			    ui->ui_alive && ui->ui_mi == um) {
80907424adbSbostic 				um->um_tab.b_actf = um->um_tab.b_actl =
81007424adbSbostic 				    &tsutab[i];
81107424adbSbostic 				break;
812397f88ceSroot 			}
81307424adbSbostic 		}
814*d72cfc4fStorek 		tsmap(&ts_softc[ts11], uban);
81583685355Sroot 		(void) tsinit(ts11);
81661711025Swnj 		tsstart(um);
81761711025Swnj 	}
8186a55db31Swnj }
8196a55db31Swnj 
82061711025Swnj /*ARGSUSED*/
tsioctl(dev,cmd,data,flag)82150acaa3cSsam tsioctl(dev, cmd, data, flag)
82250acaa3cSsam 	caddr_t data;
82361711025Swnj 	dev_t dev;
8246a55db31Swnj {
82561711025Swnj 	int tsunit = TSUNIT(dev);
82607424adbSbostic 	register struct ts_softc *sc = &ts_softc[tsdinfo[tsunit]->ui_ctlr];
82761711025Swnj 	register struct buf *bp = &ctsbuf[TSUNIT(dev)];
82807424adbSbostic 	register int callcount;
8291ba79fc0Ssklower 	int fcount, error = 0;
83050acaa3cSsam 	struct mtop *mtop;
83150acaa3cSsam 	struct mtget *mtget;
83261711025Swnj 	/* we depend of the values and order of the MT codes here */
83307424adbSbostic 	static int tsops[] =
83496f07b01Swnj 	 {TS_WEOF,TS_SFORWF,TS_SREVF,TS_SFORW,TS_SREV,TS_REW,TS_OFFL,TS_SENSE};
8356a55db31Swnj 
83661711025Swnj 	switch (cmd) {
83750acaa3cSsam 
83861711025Swnj 	case MTIOCTOP:	/* tape operation */
83950acaa3cSsam 		mtop = (struct mtop *)data;
84050acaa3cSsam 		switch (mtop->mt_op) {
84150acaa3cSsam 
84261711025Swnj 		case MTWEOF:
84350acaa3cSsam 			callcount = mtop->mt_count;
84461711025Swnj 			fcount = 1;
84561711025Swnj 			break;
84650acaa3cSsam 
84761711025Swnj 		case MTFSF: case MTBSF:
84861711025Swnj 		case MTFSR: case MTBSR:
84961711025Swnj 			callcount = 1;
85050acaa3cSsam 			fcount = mtop->mt_count;
85161711025Swnj 			break;
85250acaa3cSsam 
85361711025Swnj 		case MTREW: case MTOFFL: case MTNOP:
85461711025Swnj 			callcount = 1;
85561711025Swnj 			fcount = 1;
85661711025Swnj 			break;
85750acaa3cSsam 
85861711025Swnj 		default:
8595daf0196Sroot 			return (ENXIO);
86061711025Swnj 		}
8615daf0196Sroot 		if (callcount <= 0 || fcount <= 0)
8625daf0196Sroot 			return (EINVAL);
86361711025Swnj 		while (--callcount >= 0) {
86450acaa3cSsam 			tscommand(dev, tsops[mtop->mt_op], fcount);
86550acaa3cSsam 			if ((mtop->mt_op == MTFSR || mtop->mt_op == MTBSR) &&
866fef218f0Sroot 			    bp->b_resid)
8675daf0196Sroot 				return (EIO);
86807424adbSbostic 			if ((bp->b_flags&B_ERROR) ||
86907424adbSbostic 			    sc->sc_ts.t_sts.s_xs0&TS_BOT)
87061711025Swnj 				break;
87161711025Swnj 		}
8721ba79fc0Ssklower 		if (bp->b_flags&B_ERROR)
8731ba79fc0Ssklower 			if ((error = bp->b_error)==0)
8741ba79fc0Ssklower 				return (EIO);
8751ba79fc0Ssklower 		return (error);
87650acaa3cSsam 
87761711025Swnj 	case MTIOCGET:
87850acaa3cSsam 		mtget = (struct mtget *)data;
87950acaa3cSsam 		mtget->mt_dsreg = 0;
88007424adbSbostic 		mtget->mt_erreg = sc->sc_ts.t_sts.s_xs0;
88150acaa3cSsam 		mtget->mt_resid = sc->sc_resid;
88250acaa3cSsam 		mtget->mt_type = MT_ISTS;
8835daf0196Sroot 		break;
88450acaa3cSsam 
88561711025Swnj 	default:
8865daf0196Sroot 		return (ENXIO);
88761711025Swnj 	}
8885daf0196Sroot 	return (0);
8896a55db31Swnj }
8906a55db31Swnj 
89161711025Swnj #define	DBSIZE	20
8926a55db31Swnj 
tsdump(dev)89307424adbSbostic tsdump(dev)
89407424adbSbostic 	dev_t dev;
89561711025Swnj {
89661711025Swnj 	register struct uba_device *ui;
89707424adbSbostic 	register struct uba_regs *uba;
898397f88ceSroot 	register struct tsdevice *addr;
89907424adbSbostic 	register int i;
90007424adbSbostic 	register struct pte *io;
90107424adbSbostic 	int blk, num, unit, reg, start;
90207424adbSbostic 	u_short db;
90307424adbSbostic 	struct ts_tsdata *tc, *tc_ubaddr;
90461711025Swnj 
90507424adbSbostic 	unit = TSUNIT(dev);
90607424adbSbostic 	if (unit >= NTS)
90761711025Swnj 		return (ENXIO);
90807424adbSbostic #define	phys(a,b)	((b)((int)(a)&0x7fffffff))
90907424adbSbostic 	ui = phys(tsdinfo[unit], struct uba_device *);
91007424adbSbostic 	if (ui->ui_alive == 0)
91107424adbSbostic 		return (ENXIO);
91207424adbSbostic 	uba = phys(ui->ui_hd, struct uba_hd *)->uh_physuba;
91307424adbSbostic 	ubainit(uba);
914397f88ceSroot 	addr = (struct tsdevice *)ui->ui_physaddr;
91507424adbSbostic 
91607424adbSbostic 	/* map a ts_tsdata structure */
91707424adbSbostic 	tc = phys(&ts_softc[0].sc_ts, struct ts_tsdata *);
91807424adbSbostic 	num = btoc(sizeof(struct ts_tsdata)) + 1;
91907424adbSbostic 	io = &uba->uba_map[reg = NUBMREG - num];
92007424adbSbostic 	for (i = 0; i < num; i++)
92107424adbSbostic 		*(int *)io++ = UBAMR_MRV | (btop(tc) + i);
92207424adbSbostic 	i = (((int)tc & PGOFSET) | (reg << 9));
92307424adbSbostic 	tc_ubaddr = (struct ts_tsdata *)i;
92407424adbSbostic 	db = i + ((i >> 16) & 3);
92507424adbSbostic 
92607424adbSbostic 	/* init the drive */
92761711025Swnj 	addr->tssr = 0;
92861711025Swnj 	tswait(addr);
92907424adbSbostic 	if ((addr->tssr & (TS_NBA|TS_OFL)) != TS_NBA)
93007424adbSbostic 		return (EFAULT);
93107424adbSbostic 
93207424adbSbostic 	/* set characteristics */
93307424adbSbostic 	i = (int)&tc_ubaddr->t_char;
93407424adbSbostic 	tc->t_cmd.c_loba = i;
93507424adbSbostic 	tc->t_cmd.c_hiba = (i >> 16) & 3;
93607424adbSbostic 	tc->t_cmd.c_size = sizeof(struct ts_char);
93707424adbSbostic 	tc->t_cmd.c_cmd = TS_ACK | TS_CVC | TS_SETCHR;
93807424adbSbostic 	tc->t_char.char_addr = (int)&tc_ubaddr->t_sts;
93907424adbSbostic 	tc->t_char.char_size = sizeof(struct ts_sts);
94007424adbSbostic 	tc->t_char.char_mode = TS_ESS;
94107424adbSbostic 	addr->tsdb = db;
94261711025Swnj 	tswait(addr);
94307424adbSbostic 	if (addr->tssr & TS_NBA)
94407424adbSbostic 		return (ENXIO);
94507424adbSbostic 
94607424adbSbostic 	/* dump */
94707424adbSbostic 	tc->t_cmd.c_cmd = TS_ACK | TS_WCOM;
94807424adbSbostic 	tc->t_cmd.c_repcnt = 1;
94907424adbSbostic 	num = maxfree;
95007424adbSbostic 	for (start = 0, num = maxfree; num > 0; start += blk, num -= blk) {
95107424adbSbostic 		blk = num > DBSIZE ? DBSIZE : num;
95207424adbSbostic 		io = uba->uba_map;
95307424adbSbostic 		for (i = 0; i < blk; i++)
95407424adbSbostic 			*(int *)io++ = UBAMR_MRV | (1 << UBAMR_DPSHIFT) |
95507424adbSbostic 				(start + i);
95607424adbSbostic 		*(int *)io = 0;
95707424adbSbostic 		addr->tsdb = db;
95807424adbSbostic 		tswait(addr);
95907424adbSbostic 	}
96007424adbSbostic 
96107424adbSbostic 	/* eof */
96207424adbSbostic 	tc->t_cmd.c_cmd = TS_WEOF;
96307424adbSbostic 	addr->tsdb = db;
96407424adbSbostic 	tswait(addr);
96507424adbSbostic 	addr->tsdb = db;
96607424adbSbostic 	tswait(addr);
96707424adbSbostic 
96861711025Swnj 	if (addr->tssr&TS_SC)
96961711025Swnj 		return (EIO);
97061711025Swnj 	addr->tssr = 0;
97161711025Swnj 	tswait(addr);
97261711025Swnj 	return (0);
9736a55db31Swnj }
9746a55db31Swnj 
tswait(addr)97561711025Swnj tswait(addr)
976397f88ceSroot 	register struct tsdevice *addr;
9776a55db31Swnj {
9786a55db31Swnj 
97907424adbSbostic 	while ((addr->tssr & TS_SSR) == 0)
98007424adbSbostic 		/* void */;
9816a55db31Swnj }
9826a55db31Swnj 
98307424adbSbostic #endif /* NTS > 0 */
984