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