Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
0880b18e | 2 | * Copyright (c) 1982, 1986 Regents of the University of California. |
da7c5cc6 KM |
3 | * All rights reserved. The Berkeley software License Agreement |
4 | * specifies the terms and conditions for redistribution. | |
5 | * | |
c457503f | 6 | * @(#)ut.c 7.2 (Berkeley) %G% |
da7c5cc6 | 7 | */ |
b02cd1e9 | 8 | |
7c6c07eb | 9 | #include "tj.h" |
b02cd1e9 | 10 | #if NUT > 0 |
b02cd1e9 BJ |
11 | /* |
12 | * System Industries Model 9700 Tape Drive | |
13 | * emulates a TU45 on the UNIBUS | |
14 | * | |
15 | * TODO: | |
16 | * check out attention processing | |
17 | * try reset code and dump code | |
18 | */ | |
ff360633 JB |
19 | #include "param.h" |
20 | #include "systm.h" | |
21 | #include "buf.h" | |
22 | #include "conf.h" | |
23 | #include "dir.h" | |
24 | #include "file.h" | |
25 | #include "user.h" | |
26 | #include "map.h" | |
27 | #include "ioctl.h" | |
28 | #include "mtio.h" | |
29 | #include "cmap.h" | |
30 | #include "uio.h" | |
31 | #include "kernel.h" | |
fe23e54e | 32 | #include "tty.h" |
c457503f | 33 | #include "syslog.h" |
b02cd1e9 | 34 | |
c457503f | 35 | #include "../machine/pte.h" |
896962b1 | 36 | #include "../vax/cpu.h" |
ff360633 JB |
37 | #include "ubareg.h" |
38 | #include "ubavar.h" | |
39 | #include "utreg.h" | |
b02cd1e9 BJ |
40 | |
41 | struct buf rutbuf[NUT]; /* bufs for raw i/o */ | |
42 | struct buf cutbuf[NUT]; /* bufs for control operations */ | |
43 | struct buf tjutab[NTJ]; /* bufs for slave queue headers */ | |
44 | ||
45 | struct uba_ctlr *utminfo[NUT]; | |
46 | struct uba_device *tjdinfo[NTJ]; | |
407582e3 | 47 | int utprobe(), utslave(), utattach(), utdgo(), utintr(), uttimer(); |
b02cd1e9 BJ |
48 | u_short utstd[] = { 0772440, 0 }; |
49 | struct uba_driver utdriver = | |
50 | { utprobe, utslave, utattach, utdgo, utstd, "tj", tjdinfo, "ut", utminfo, 0 }; | |
51 | ||
6b0f2767 SL |
52 | #define MASKREG(reg) ((reg)&0xffff) |
53 | ||
b02cd1e9 BJ |
54 | /* bits in minor device */ |
55 | #define TJUNIT(dev) (minor(dev)&03) | |
56 | #define T_NOREWIND 04 | |
57 | #define T_1600BPI 010 | |
58 | #define T_6250BPI 020 | |
59 | short utdens[] = { UT_NRZI, UT_PE, UT_GCR, UT_NRZI }; | |
60 | ||
61 | /* slave to controller mapping table */ | |
62 | short tjtout[NTJ]; | |
63 | #define UTUNIT(dev) (tjtout[TJUNIT(dev)]) | |
64 | ||
65 | #define INF (daddr_t)1000000L /* a block number that wont exist */ | |
66 | ||
67 | struct tj_softc { | |
68 | char sc_openf; /* exclusive open */ | |
69 | char sc_lastiow; /* last I/O operation was a write */ | |
70 | daddr_t sc_blkno; /* next block to transfer */ | |
71 | daddr_t sc_nxrec; /* next record on tape */ | |
72 | u_short sc_erreg; /* image of uter */ | |
73 | u_short sc_dsreg; /* image of utds */ | |
c36079e2 | 74 | u_short sc_resid; /* residual from transfer */ |
b02cd1e9 | 75 | u_short sc_dens; /* sticky selected density */ |
407582e3 BJ |
76 | daddr_t sc_timo; /* time until timeout expires */ |
77 | short sc_tact; /* timeout is active flag */ | |
fe23e54e | 78 | struct tty *sc_ttyp; /* record user's tty for errors */ |
c457503f MK |
79 | int sc_blks; /* number of I/O operations since open */ |
80 | int sc_softerrs; /* number of soft I/O errors since open */ | |
b02cd1e9 BJ |
81 | } tj_softc[NTJ]; |
82 | ||
83 | /* | |
84 | * Internal per/slave states found in sc_state | |
85 | */ | |
86 | #define SSEEK 1 /* seeking */ | |
87 | #define SIO 2 /* doing sequential I/O */ | |
88 | #define SCOM 3 /* sending a control command */ | |
89 | #define SREW 4 /* doing a rewind op */ | |
c36079e2 SL |
90 | #define SERASE 5 /* erase inter-record gap */ |
91 | #define SERASED 6 /* erased inter-record gap */ | |
b02cd1e9 | 92 | |
89b8a44c | 93 | /*ARGSUSED*/ |
b02cd1e9 BJ |
94 | utprobe(reg) |
95 | caddr_t reg; | |
96 | { | |
97 | register int br, cvec; | |
98 | #ifdef lint | |
99 | br=0; cvec=br; br=cvec; | |
89b8a44c | 100 | utintr(0); |
b02cd1e9 | 101 | #endif |
c36079e2 | 102 | /* |
e090266d BJ |
103 | * The SI documentation says you must set the RDY bit |
104 | * (even though it's read-only) to force an interrupt. | |
c36079e2 | 105 | */ |
e090266d | 106 | ((struct utdevice *) reg)->utcs1 = UT_IE|UT_NOP|UT_RDY; |
b02cd1e9 | 107 | DELAY(10000); |
9c0adba0 | 108 | return (sizeof (struct utdevice)); |
b02cd1e9 BJ |
109 | } |
110 | ||
111 | /*ARGSUSED*/ | |
112 | utslave(ui, reg) | |
113 | struct uba_device *ui; | |
114 | caddr_t reg; | |
115 | { | |
116 | /* | |
117 | * A real TU45 would support the slave present bit | |
118 | * int the drive type register, but this thing doesn't, | |
119 | * so there's no way to determine if a slave is present or not. | |
120 | */ | |
121 | return(1); | |
122 | } | |
123 | ||
124 | utattach(ui) | |
125 | struct uba_device *ui; | |
126 | { | |
127 | tjtout[ui->ui_unit] = ui->ui_mi->um_ctlr; | |
128 | } | |
129 | ||
130 | /* | |
131 | * Open the device with exclusive access. | |
132 | */ | |
133 | utopen(dev, flag) | |
134 | dev_t dev; | |
135 | int flag; | |
136 | { | |
137 | register int tjunit = TJUNIT(dev); | |
138 | register struct uba_device *ui; | |
139 | register struct tj_softc *sc; | |
140 | int olddens, dens; | |
2311123d | 141 | register int s; |
b02cd1e9 | 142 | |
1e6047bb | 143 | if (tjunit >= NTJ || (ui = tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) |
7da157da | 144 | return (ENXIO); |
1e6047bb MK |
145 | if ((sc = &tj_softc[tjunit])->sc_openf) |
146 | return (EBUSY); | |
c457503f | 147 | sc->sc_openf = 1; |
b02cd1e9 | 148 | olddens = sc->sc_dens; |
7da157da BJ |
149 | dens = sc->sc_dens = |
150 | utdens[(minor(dev)&(T_1600BPI|T_6250BPI))>>3]| | |
151 | PDP11FMT|(ui->ui_slave&07); | |
b02cd1e9 BJ |
152 | get: |
153 | utcommand(dev, UT_SENSE, 1); | |
154 | if (sc->sc_dsreg&UTDS_PIP) { | |
3b1e560f | 155 | sleep((caddr_t)&lbolt, PZERO+1); |
b02cd1e9 BJ |
156 | goto get; |
157 | } | |
158 | sc->sc_dens = olddens; | |
159 | if ((sc->sc_dsreg&UTDS_MOL) == 0) { | |
c457503f | 160 | sc->sc_openf = 0; |
b02cd1e9 | 161 | uprintf("tj%d: not online\n", tjunit); |
7da157da | 162 | return (EIO); |
b02cd1e9 BJ |
163 | } |
164 | if ((flag&FWRITE) && (sc->sc_dsreg&UTDS_WRL)) { | |
c457503f | 165 | sc->sc_openf = 0; |
b02cd1e9 | 166 | uprintf("tj%d: no write ring\n", tjunit); |
7da157da | 167 | return (EIO); |
b02cd1e9 BJ |
168 | } |
169 | if ((sc->sc_dsreg&UTDS_BOT) == 0 && (flag&FWRITE) && | |
170 | dens != sc->sc_dens) { | |
c457503f | 171 | sc->sc_openf = 0; |
b02cd1e9 | 172 | uprintf("tj%d: can't change density in mid-tape\n", tjunit); |
7da157da | 173 | return (EIO); |
b02cd1e9 | 174 | } |
b02cd1e9 BJ |
175 | sc->sc_blkno = (daddr_t)0; |
176 | sc->sc_nxrec = INF; | |
177 | sc->sc_lastiow = 0; | |
c457503f MK |
178 | sc->sc_blks = 0; |
179 | sc->sc_softerrs = 0; | |
b02cd1e9 | 180 | sc->sc_dens = dens; |
fe23e54e | 181 | sc->sc_ttyp = u.u_ttyp; |
c36079e2 SL |
182 | /* |
183 | * For 6250 bpi take exclusive use of the UNIBUS. | |
184 | */ | |
185 | ui->ui_driver->ud_xclu = (dens&(T_1600BPI|T_6250BPI)) == T_6250BPI; | |
8011f5df | 186 | s = splclock(); |
407582e3 BJ |
187 | if (sc->sc_tact == 0) { |
188 | sc->sc_timo = INF; | |
189 | sc->sc_tact = 1; | |
190 | timeout(uttimer, (caddr_t)dev, 5*hz); | |
191 | } | |
2311123d | 192 | splx(s); |
7da157da | 193 | return (0); |
b02cd1e9 BJ |
194 | } |
195 | ||
196 | utclose(dev, flag) | |
197 | register dev_t dev; | |
198 | register flag; | |
199 | { | |
200 | register struct tj_softc *sc = &tj_softc[TJUNIT(dev)]; | |
201 | ||
202 | if (flag == FWRITE || ((flag&FWRITE) && sc->sc_lastiow)) { | |
203 | utcommand(dev, UT_WEOF, 1); | |
204 | utcommand(dev, UT_WEOF, 1); | |
205 | utcommand(dev, UT_SREV, 1); | |
206 | } | |
207 | if ((minor(dev)&T_NOREWIND) == 0) | |
208 | utcommand(dev, UT_REW, 0); | |
c457503f MK |
209 | if (sc->sc_blks > 100 && sc->sc_softerrs > sc->sc_blks / 100) |
210 | log(LOG_INFO, "tj%d: %d soft errors in %d blocks\n", | |
211 | TJUNIT(dev), sc->sc_softerrs, sc->sc_blks); | |
b02cd1e9 BJ |
212 | sc->sc_openf = 0; |
213 | } | |
214 | ||
215 | utcommand(dev, com, count) | |
216 | dev_t dev; | |
217 | int com, count; | |
218 | { | |
219 | register struct buf *bp; | |
2311123d | 220 | register int s; |
b02cd1e9 BJ |
221 | |
222 | bp = &cutbuf[UTUNIT(dev)]; | |
2311123d | 223 | s = spl5(); |
b02cd1e9 BJ |
224 | while (bp->b_flags&B_BUSY) { |
225 | if(bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) | |
226 | break; | |
227 | bp->b_flags |= B_WANTED; | |
228 | sleep((caddr_t)bp, PRIBIO); | |
229 | } | |
230 | bp->b_flags = B_BUSY|B_READ; | |
2311123d | 231 | splx(s); |
b02cd1e9 BJ |
232 | bp->b_dev = dev; |
233 | bp->b_command = com; | |
234 | bp->b_repcnt = count; | |
235 | bp->b_blkno = 0; | |
236 | utstrategy(bp); | |
237 | if (count == 0) | |
238 | return; | |
239 | iowait(bp); | |
240 | if (bp->b_flags&B_WANTED) | |
241 | wakeup((caddr_t)bp); | |
242 | bp->b_flags &= B_ERROR; | |
243 | } | |
244 | ||
245 | /* | |
246 | * Queue a tape operation. | |
247 | */ | |
248 | utstrategy(bp) | |
249 | register struct buf *bp; | |
250 | { | |
251 | int tjunit = TJUNIT(bp->b_dev); | |
252 | register struct uba_ctlr *um; | |
253 | register struct buf *dp; | |
254 | ||
255 | /* | |
256 | * Put transfer at end of unit queue | |
257 | */ | |
258 | dp = &tjutab[tjunit]; | |
259 | bp->av_forw = NULL; | |
6c94015c | 260 | um = tjdinfo[tjunit]->ui_mi; |
b02cd1e9 BJ |
261 | (void) spl5(); |
262 | if (dp->b_actf == NULL) { | |
263 | dp->b_actf = bp; | |
264 | /* | |
265 | * Transport not active, so... | |
266 | * put at end of controller queue | |
267 | */ | |
268 | dp->b_forw = NULL; | |
b02cd1e9 BJ |
269 | if (um->um_tab.b_actf == NULL) |
270 | um->um_tab.b_actf = dp; | |
271 | else | |
272 | um->um_tab.b_actl->b_forw = dp; | |
273 | um->um_tab.b_actl = dp; | |
274 | } else | |
275 | dp->b_actl->av_forw = bp; | |
276 | dp->b_actl = bp; | |
277 | /* | |
278 | * If the controller is not busy, set it going. | |
279 | */ | |
c36079e2 | 280 | if (um->um_tab.b_state == 0) |
b02cd1e9 BJ |
281 | utstart(um); |
282 | (void) spl0(); | |
283 | } | |
284 | ||
285 | utstart(um) | |
286 | register struct uba_ctlr *um; | |
287 | { | |
c36079e2 | 288 | register struct utdevice *addr; |
b02cd1e9 BJ |
289 | register struct buf *bp, *dp; |
290 | register struct tj_softc *sc; | |
291 | struct uba_device *ui; | |
292 | int tjunit; | |
293 | daddr_t blkno; | |
294 | ||
295 | loop: | |
296 | /* | |
297 | * Scan controller queue looking for units with | |
298 | * transaction queues to dispatch | |
299 | */ | |
300 | if ((dp = um->um_tab.b_actf) == NULL) | |
301 | return; | |
302 | if ((bp = dp->b_actf) == NULL) { | |
303 | um->um_tab.b_actf = dp->b_forw; | |
304 | goto loop; | |
305 | } | |
c36079e2 | 306 | addr = (struct utdevice *)um->um_addr; |
b02cd1e9 BJ |
307 | tjunit = TJUNIT(bp->b_dev); |
308 | ui = tjdinfo[tjunit]; | |
309 | sc = &tj_softc[tjunit]; | |
310 | /* note slave select, density, and format were merged on open */ | |
c36079e2 SL |
311 | addr->uttc = sc->sc_dens; |
312 | sc->sc_dsreg = addr->utds; | |
313 | sc->sc_erreg = addr->uter; | |
6b0f2767 | 314 | sc->sc_resid = MASKREG(addr->utfc); |
b02cd1e9 BJ |
315 | /* |
316 | * Default is that last command was NOT a write command; | |
317 | * if we do a write command we will notice this in utintr(). | |
318 | */ | |
319 | sc->sc_lastiow = 0; | |
c36079e2 | 320 | if (sc->sc_openf < 0 || (addr->utds&UTDS_MOL) == 0) { |
b02cd1e9 BJ |
321 | /* |
322 | * Have had a hard error on a non-raw tape | |
323 | * or the tape unit is now unavailable | |
324 | * (e.g. taken off line). | |
325 | */ | |
326 | bp->b_flags |= B_ERROR; | |
327 | goto next; | |
328 | } | |
329 | if (bp == &cutbuf[UTUNIT(bp->b_dev)]) { | |
330 | /* | |
331 | * Execute a control operation with the specified | |
332 | * count. | |
333 | */ | |
334 | if (bp->b_command == UT_SENSE) | |
335 | goto next; | |
6b0f2767 SL |
336 | if (bp->b_command == UT_SFORW && (addr->utds & UTDS_EOT)) { |
337 | bp->b_resid = bp->b_bcount; | |
338 | goto next; | |
339 | } | |
b02cd1e9 BJ |
340 | /* |
341 | * Set next state; handle timeouts | |
342 | */ | |
407582e3 | 343 | if (bp->b_command == UT_REW) { |
c36079e2 | 344 | um->um_tab.b_state = SREW; |
407582e3 BJ |
345 | sc->sc_timo = 5*60; |
346 | } else { | |
c36079e2 | 347 | um->um_tab.b_state = SCOM; |
407582e3 BJ |
348 | sc->sc_timo = imin(imax(10*(int)-bp->b_repcnt,60),5*60); |
349 | } | |
b02cd1e9 BJ |
350 | /* NOTE: this depends on the ut command values */ |
351 | if (bp->b_command >= UT_SFORW && bp->b_command <= UT_SREVF) | |
c36079e2 | 352 | addr->utfc = -bp->b_repcnt; |
b02cd1e9 BJ |
353 | goto dobpcmd; |
354 | } | |
355 | /* | |
356 | * The following checks boundary conditions for operations | |
357 | * on non-raw tapes. On raw tapes the initialization of | |
358 | * sc->sc_nxrec by utphys causes them to be skipped normally | |
359 | * (except in the case of retries). | |
360 | */ | |
fb739082 | 361 | if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) { |
b02cd1e9 BJ |
362 | /* can't read past end of file */ |
363 | bp->b_flags |= B_ERROR; | |
364 | bp->b_error = ENXIO; | |
365 | goto next; | |
366 | } | |
fb739082 | 367 | if (bdbtofsb(bp->b_blkno) == sc->sc_nxrec && (bp->b_flags&B_READ)) { |
b02cd1e9 BJ |
368 | /* read at eof returns 0 count */ |
369 | bp->b_resid = bp->b_bcount; | |
370 | clrbuf(bp); | |
371 | goto next; | |
372 | } | |
373 | if ((bp->b_flags&B_READ) == 0) | |
fb739082 | 374 | sc->sc_nxrec = bdbtofsb(bp->b_blkno)+1; |
b02cd1e9 BJ |
375 | /* |
376 | * If the tape is correctly positioned, set up all the | |
377 | * registers but the csr, and give control over to the | |
378 | * UNIBUS adaptor routines, to wait for resources to | |
379 | * start I/O. | |
380 | */ | |
fb739082 | 381 | if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) { |
c36079e2 SL |
382 | addr->utwc = -(((bp->b_bcount)+1)>>1); |
383 | addr->utfc = -bp->b_bcount; | |
b02cd1e9 BJ |
384 | if ((bp->b_flags&B_READ) == 0) { |
385 | /* | |
386 | * On write error retries erase the | |
c36079e2 | 387 | * inter-record gap before rewriting. |
b02cd1e9 | 388 | */ |
c36079e2 | 389 | if (um->um_tab.b_errcnt) { |
c36079e2 | 390 | if (um->um_tab.b_state != SERASED) { |
64614526 | 391 | um->um_tab.b_state = SERASE; |
407582e3 | 392 | sc->sc_timo = 60; |
c36079e2 SL |
393 | addr->utcs1 = UT_ERASE|UT_IE|UT_GO; |
394 | return; | |
395 | } | |
c36079e2 | 396 | } |
6b0f2767 SL |
397 | if (addr->utds & UTDS_EOT) { |
398 | bp->b_resid = bp->b_bcount; | |
399 | um->um_tab.b_state = 0; | |
400 | goto next; | |
401 | } | |
c36079e2 | 402 | um->um_cmd = UT_WCOM; |
b02cd1e9 BJ |
403 | } else |
404 | um->um_cmd = UT_RCOM; | |
407582e3 | 405 | sc->sc_timo = 60; |
c36079e2 | 406 | um->um_tab.b_state = SIO; |
b02cd1e9 BJ |
407 | (void) ubago(ui); |
408 | return; | |
409 | } | |
410 | /* | |
411 | * Tape positioned incorrectly; seek forwards or | |
412 | * backwards to the correct spot. This happens for | |
413 | * raw tapes only on error retries. | |
414 | */ | |
c36079e2 | 415 | um->um_tab.b_state = SSEEK; |
fb739082 SL |
416 | if (blkno < bdbtofsb(bp->b_blkno)) { |
417 | addr->utfc = blkno - bdbtofsb(bp->b_blkno); | |
b02cd1e9 BJ |
418 | bp->b_command = UT_SFORW; |
419 | } else { | |
fb739082 | 420 | addr->utfc = bdbtofsb(bp->b_blkno) - blkno; |
b02cd1e9 BJ |
421 | bp->b_command = UT_SREV; |
422 | } | |
407582e3 | 423 | sc->sc_timo = imin(imax(10 * -addr->utfc, 60), 5*60); |
b02cd1e9 BJ |
424 | |
425 | dobpcmd: | |
426 | /* | |
427 | * Perform the command setup in bp. | |
428 | */ | |
c36079e2 | 429 | addr->utcs1 = bp->b_command|UT_IE|UT_GO; |
b02cd1e9 BJ |
430 | return; |
431 | next: | |
432 | /* | |
433 | * Advance to the next command in the slave queue, | |
434 | * posting notice and releasing resources as needed. | |
435 | */ | |
b02cd1e9 BJ |
436 | if (um->um_ubinfo) |
437 | ubadone(um); | |
438 | um->um_tab.b_errcnt = 0; | |
439 | dp->b_actf = bp->av_forw; | |
440 | iodone(bp); | |
441 | goto loop; | |
442 | } | |
443 | ||
444 | /* | |
445 | * Start operation on controller -- | |
446 | * UNIBUS resources have been allocated. | |
447 | */ | |
448 | utdgo(um) | |
449 | register struct uba_ctlr *um; | |
450 | { | |
451 | register struct utdevice *addr = (struct utdevice *)um->um_addr; | |
452 | ||
453 | addr->utba = (u_short) um->um_ubinfo; | |
6b0f2767 | 454 | addr->utcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300)|UT_IE|UT_GO; |
b02cd1e9 BJ |
455 | } |
456 | ||
457 | /* | |
458 | * Ut interrupt handler | |
459 | */ | |
460 | /*ARGSUSED*/ | |
461 | utintr(ut11) | |
462 | int ut11; | |
463 | { | |
464 | struct buf *dp; | |
465 | register struct buf *bp; | |
466 | register struct uba_ctlr *um = utminfo[ut11]; | |
467 | register struct utdevice *addr; | |
468 | register struct tj_softc *sc; | |
c36079e2 | 469 | u_short tjunit, cs2, cs1; |
b02cd1e9 BJ |
470 | register state; |
471 | ||
472 | if ((dp = um->um_tab.b_actf) == NULL) | |
473 | return; | |
474 | bp = dp->b_actf; | |
475 | tjunit = TJUNIT(bp->b_dev); | |
476 | addr = (struct utdevice *)tjdinfo[tjunit]->ui_addr; | |
477 | sc = &tj_softc[tjunit]; | |
478 | /* | |
479 | * Record status... | |
480 | */ | |
da9ebd7c | 481 | sc->sc_timo = INF; |
b02cd1e9 BJ |
482 | sc->sc_dsreg = addr->utds; |
483 | sc->sc_erreg = addr->uter; | |
6b0f2767 | 484 | sc->sc_resid = MASKREG(addr->utfc); |
c36079e2 | 485 | if ((bp->b_flags&B_READ) == 0) |
b02cd1e9 | 486 | sc->sc_lastiow = 1; |
c36079e2 SL |
487 | state = um->um_tab.b_state; |
488 | um->um_tab.b_state = 0; | |
b02cd1e9 BJ |
489 | /* |
490 | * Check for errors... | |
491 | */ | |
492 | if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) { | |
64614526 BJ |
493 | /* |
494 | * To clear the ERR bit, we must issue a drive clear | |
495 | * command, and to clear the TRE bit we must set the | |
496 | * controller clear bit. | |
497 | */ | |
498 | cs2 = addr->utcs2; | |
499 | if ((cs1 = addr->utcs1)&UT_TRE) | |
500 | addr->utcs2 |= UTCS2_CLR; | |
501 | /* is this dangerous ?? */ | |
502 | while ((addr->utcs1&UT_RDY) == 0) | |
503 | ; | |
504 | addr->utcs1 = UT_CLEAR|UT_GO; | |
b02cd1e9 | 505 | /* |
6b0f2767 SL |
506 | * If we were reading at 1600 or 6250 bpi and the error |
507 | * was corrected, then don't consider this an error. | |
b02cd1e9 | 508 | */ |
452f8154 | 509 | if (sc->sc_erreg & UTER_COR && (bp->b_flags & B_READ) && |
6b0f2767 | 510 | (addr->uttc & UTTC_DEN) != UT_NRZI) { |
fe23e54e | 511 | tprintf(sc->sc_ttyp, |
6b0f2767 SL |
512 | "ut%d: soft error bn%d cs1=%b er=%b cs2=%b ds=%b\n", |
513 | tjunit, bp->b_blkno, cs1, UT_BITS, sc->sc_erreg, | |
514 | UTER_BITS, cs2, UTCS2_BITS, sc->sc_dsreg, UTDS_BITS); | |
515 | sc->sc_erreg &= ~UTER_COR; | |
b02cd1e9 | 516 | } |
b02cd1e9 BJ |
517 | /* |
518 | * If we were reading from a raw tape and the only error | |
519 | * was that the record was too long, then we don't consider | |
520 | * this an error. | |
521 | */ | |
522 | if (bp == &rutbuf[UTUNIT(bp->b_dev)] && (bp->b_flags&B_READ) && | |
523 | (sc->sc_erreg&UTER_FCE)) | |
6b0f2767 | 524 | sc->sc_erreg &= ~UTER_FCE; |
aaff71ec | 525 | if (sc->sc_erreg == 0) |
b02cd1e9 | 526 | goto ignoreerr; |
c36079e2 | 527 | /* |
6b0f2767 SL |
528 | * Fix up errors which occur due to backspacing |
529 | * "over" the front of the tape. | |
c36079e2 | 530 | */ |
6b0f2767 | 531 | if ((sc->sc_dsreg & UTDS_BOT) && bp->b_command == UT_SREV && |
c36079e2 SL |
532 | ((sc->sc_erreg &= ~(UTER_NEF|UTER_FCE)) == 0)) |
533 | goto opdone; | |
b02cd1e9 BJ |
534 | /* |
535 | * Retry soft errors up to 8 times | |
536 | */ | |
537 | if ((sc->sc_erreg&UTER_HARD) == 0 && state == SIO) { | |
538 | if (++um->um_tab.b_errcnt < 7) { | |
539 | sc->sc_blkno++; | |
540 | ubadone(um); | |
541 | goto opcont; | |
542 | } | |
6b0f2767 SL |
543 | } |
544 | /* | |
545 | * Hard or non-I/O errors on non-raw tape | |
546 | * cause it to close. | |
547 | */ | |
548 | if (sc->sc_openf > 0 && bp != &rutbuf[UTUNIT(bp->b_dev)]) | |
549 | sc->sc_openf = -1; | |
b02cd1e9 BJ |
550 | /* |
551 | * Couldn't recover error. | |
552 | */ | |
fe23e54e RC |
553 | tprintf(sc->sc_ttyp, |
554 | "ut%d: hard error bn%d cs1=%b er=%b cs2=%b ds=%b\n", | |
c36079e2 SL |
555 | tjunit, bp->b_blkno, cs1, UT_BITS, sc->sc_erreg, |
556 | UTER_BITS, cs2, UTCS2_BITS, sc->sc_dsreg, UTDS_BITS); | |
b02cd1e9 BJ |
557 | bp->b_flags |= B_ERROR; |
558 | goto opdone; | |
559 | } | |
6b0f2767 | 560 | |
b02cd1e9 | 561 | ignoreerr: |
6b0f2767 SL |
562 | /* |
563 | * If we hit a tape mark update our position. | |
564 | */ | |
565 | if (sc->sc_dsreg & UTDS_TM && bp->b_flags & B_READ) { | |
566 | /* | |
567 | * Set blkno and nxrec | |
568 | */ | |
569 | if (bp == &cutbuf[UTUNIT(bp->b_dev)]) { | |
570 | if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) { | |
571 | sc->sc_nxrec = | |
572 | bdbtofsb(bp->b_blkno) - addr->utfc; | |
573 | sc->sc_blkno = sc->sc_nxrec; | |
574 | } else { | |
575 | sc->sc_blkno = | |
576 | bdbtofsb(bp->b_blkno) + addr->utfc; | |
577 | sc->sc_nxrec = sc->sc_blkno-1; | |
578 | } | |
579 | } else | |
580 | sc->sc_nxrec = bdbtofsb(bp->b_blkno); | |
581 | /* | |
582 | * Note: if we get a tape mark on a read, the | |
583 | * frame count register will be zero, so b_resid | |
584 | * will be calculated correctly below. | |
585 | */ | |
586 | goto opdone; | |
587 | } | |
b02cd1e9 BJ |
588 | /* |
589 | * Advance tape control FSM. | |
590 | */ | |
591 | switch (state) { | |
592 | ||
593 | case SIO: /* read/write increments tape block # */ | |
594 | sc->sc_blkno++; | |
c457503f MK |
595 | sc->sc_blks++; |
596 | if (um->um_tab.b_errcnt) | |
597 | sc->sc_softerrs++; | |
c36079e2 | 598 | break; |
b02cd1e9 | 599 | |
6b0f2767 | 600 | case SCOM: /* motion commands update current position */ |
b02cd1e9 | 601 | if (bp == &cutbuf[UTUNIT(bp->b_dev)]) |
9418508d | 602 | switch ((int)bp->b_command) { |
b02cd1e9 BJ |
603 | |
604 | case UT_SFORW: | |
605 | sc->sc_blkno -= bp->b_repcnt; | |
606 | break; | |
607 | ||
608 | case UT_SREV: | |
609 | sc->sc_blkno += bp->b_repcnt; | |
610 | break; | |
6b0f2767 SL |
611 | |
612 | case UT_REWOFFL: | |
613 | addr->utcs1 = UT_CLEAR|UT_GO; | |
614 | break; | |
b02cd1e9 | 615 | } |
c36079e2 | 616 | break; |
b02cd1e9 BJ |
617 | |
618 | case SSEEK: | |
fb739082 | 619 | sc->sc_blkno = bdbtofsb(bp->b_blkno); |
b02cd1e9 BJ |
620 | goto opcont; |
621 | ||
c36079e2 SL |
622 | case SERASE: |
623 | /* | |
624 | * Completed erase of the inter-record gap due to a | |
625 | * write error; now retry the write operation. | |
626 | */ | |
627 | um->um_tab.b_state = SERASED; | |
628 | goto opcont; | |
629 | ||
630 | case SREW: /* clear attention bit */ | |
631 | addr->utcs1 = UT_CLEAR|UT_GO; | |
632 | break; | |
633 | ||
b02cd1e9 | 634 | default: |
c36079e2 | 635 | printf("bad state %d\n", state); |
b02cd1e9 BJ |
636 | panic("utintr"); |
637 | } | |
638 | ||
639 | opdone: | |
640 | /* | |
641 | * Reset error count and remove | |
642 | * from device queue | |
643 | */ | |
644 | um->um_tab.b_errcnt = 0; | |
c36079e2 | 645 | dp->b_actf = bp->av_forw; |
6b0f2767 SL |
646 | /* |
647 | * For read command, frame count register contains | |
648 | * actual length of tape record. Otherwise, it | |
649 | * holds negative residual count. | |
650 | */ | |
651 | if (state == SIO && um->um_cmd == UT_RCOM) { | |
652 | bp->b_resid = 0; | |
653 | if (bp->b_bcount > MASKREG(addr->utfc)) | |
654 | bp->b_resid = bp->b_bcount - MASKREG(addr->utfc); | |
655 | } else | |
656 | bp->b_resid = MASKREG(-addr->utfc); | |
b02cd1e9 BJ |
657 | ubadone(um); |
658 | iodone(bp); | |
659 | /* | |
660 | * Circulate slave to end of controller queue | |
661 | * to give other slaves a chance | |
662 | */ | |
663 | um->um_tab.b_actf = dp->b_forw; | |
664 | if (dp->b_actf) { | |
665 | dp->b_forw = NULL; | |
666 | if (um->um_tab.b_actf == NULL) | |
667 | um->um_tab.b_actf = dp; | |
668 | else | |
669 | um->um_tab.b_actl->b_forw = dp; | |
670 | um->um_tab.b_actl = dp; | |
671 | } | |
672 | if (um->um_tab.b_actf == 0) | |
673 | return; | |
674 | opcont: | |
675 | utstart(um); | |
407582e3 BJ |
676 | } |
677 | ||
678 | /* | |
679 | * Watchdog timer routine. | |
680 | */ | |
681 | uttimer(dev) | |
682 | int dev; | |
683 | { | |
684 | register struct tj_softc *sc = &tj_softc[TJUNIT(dev)]; | |
afa0df56 | 685 | register short x; |
407582e3 BJ |
686 | |
687 | if (sc->sc_timo != INF && (sc->sc_timo -= 5) < 0) { | |
d6357dcf | 688 | printf("tj%d: lost interrupt\n", TJUNIT(dev)); |
407582e3 | 689 | sc->sc_timo = INF; |
afa0df56 | 690 | x = spl5(); |
407582e3 | 691 | utintr(UTUNIT(dev)); |
afa0df56 | 692 | (void) splx(x); |
407582e3 BJ |
693 | } |
694 | timeout(uttimer, (caddr_t)dev, 5*hz); | |
b02cd1e9 BJ |
695 | } |
696 | ||
697 | /* | |
698 | * Raw interface for a read | |
699 | */ | |
740e4029 | 700 | utread(dev, uio) |
b02cd1e9 | 701 | dev_t dev; |
740e4029 | 702 | struct uio *uio; |
b02cd1e9 | 703 | { |
0cd5eac7 | 704 | int errno; |
740e4029 | 705 | |
0cd5eac7 BJ |
706 | errno = utphys(dev, uio); |
707 | if (errno) | |
708 | return (errno); | |
709 | return (physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_READ, minphys, uio)); | |
b02cd1e9 BJ |
710 | } |
711 | ||
712 | /* | |
713 | * Raw interface for a write | |
714 | */ | |
002227dd | 715 | utwrite(dev, uio) |
740e4029 | 716 | dev_t dev; |
002227dd | 717 | struct uio *uio; |
b02cd1e9 | 718 | { |
0cd5eac7 BJ |
719 | int errno; |
720 | ||
721 | errno = utphys(dev, uio); | |
722 | if (errno) | |
723 | return (errno); | |
724 | return (physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_WRITE, minphys, uio)); | |
b02cd1e9 BJ |
725 | } |
726 | ||
727 | /* | |
728 | * Check for valid device number dev and update our notion | |
729 | * of where we are on the tape | |
730 | */ | |
740e4029 | 731 | utphys(dev, uio) |
b02cd1e9 | 732 | dev_t dev; |
740e4029 | 733 | struct uio *uio; |
b02cd1e9 BJ |
734 | { |
735 | register int tjunit = TJUNIT(dev); | |
736 | register struct tj_softc *sc; | |
737 | register struct uba_device *ui; | |
b02cd1e9 | 738 | |
002227dd BJ |
739 | if (tjunit >= NTJ || (ui=tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) |
740 | return (ENXIO); | |
b02cd1e9 | 741 | sc = &tj_softc[tjunit]; |
002227dd | 742 | sc->sc_blkno = bdbtofsb(uio->uio_offset>>9); |
c36079e2 | 743 | sc->sc_nxrec = sc->sc_blkno+1; |
002227dd | 744 | return (0); |
b02cd1e9 BJ |
745 | } |
746 | ||
747 | /*ARGSUSED*/ | |
942f05a9 | 748 | utioctl(dev, cmd, data, flag) |
b02cd1e9 | 749 | dev_t dev; |
942f05a9 | 750 | caddr_t data; |
b02cd1e9 BJ |
751 | { |
752 | register struct tj_softc *sc = &tj_softc[TJUNIT(dev)]; | |
753 | register struct buf *bp = &cutbuf[UTUNIT(dev)]; | |
754 | register callcount; | |
755 | int fcount; | |
942f05a9 SL |
756 | struct mtop *mtop; |
757 | struct mtget *mtget; | |
b02cd1e9 BJ |
758 | /* we depend of the values and order of the MT codes here */ |
759 | static utops[] = | |
760 | {UT_WEOF,UT_SFORWF,UT_SREVF,UT_SFORW,UT_SREV,UT_REW,UT_REWOFFL,UT_SENSE}; | |
761 | ||
762 | switch (cmd) { | |
763 | ||
764 | case MTIOCTOP: | |
942f05a9 SL |
765 | mtop = (struct mtop *)data; |
766 | switch(mtop->mt_op) { | |
b02cd1e9 BJ |
767 | |
768 | case MTWEOF: | |
b02cd1e9 BJ |
769 | case MTFSF: case MTBSF: |
770 | case MTFSR: case MTBSR: | |
d8ec6d6b SL |
771 | callcount = mtop->mt_count; |
772 | fcount = 1; | |
b02cd1e9 BJ |
773 | break; |
774 | ||
775 | case MTREW: case MTOFFL: case MTNOP: | |
776 | callcount = 1; | |
777 | fcount = 1; | |
778 | break; | |
779 | ||
780 | default: | |
7da157da | 781 | return (ENXIO); |
b02cd1e9 | 782 | } |
7da157da BJ |
783 | if (callcount <= 0 || fcount <= 0) |
784 | return (EINVAL); | |
b02cd1e9 | 785 | while (--callcount >= 0) { |
942f05a9 | 786 | utcommand(dev, utops[mtop->mt_op], fcount); |
b02cd1e9 BJ |
787 | if ((bp->b_flags&B_ERROR) || (sc->sc_dsreg&UTDS_BOT)) |
788 | break; | |
789 | } | |
a1edc12b | 790 | return (geterror(bp)); |
b02cd1e9 BJ |
791 | |
792 | case MTIOCGET: | |
942f05a9 SL |
793 | mtget = (struct mtget *)data; |
794 | mtget->mt_dsreg = sc->sc_dsreg; | |
795 | mtget->mt_erreg = sc->sc_erreg; | |
796 | mtget->mt_resid = sc->sc_resid; | |
797 | mtget->mt_type = MT_ISUT; | |
7da157da | 798 | break; |
b02cd1e9 BJ |
799 | |
800 | default: | |
7da157da | 801 | return (ENXIO); |
b02cd1e9 | 802 | } |
7da157da | 803 | return (0); |
b02cd1e9 BJ |
804 | } |
805 | ||
806 | utreset(uban) | |
807 | int uban; | |
808 | { | |
809 | register struct uba_ctlr *um; | |
810 | register ut11, tjunit; | |
811 | register struct uba_device *ui; | |
812 | register struct buf *dp; | |
813 | ||
814 | for (ut11 = 0; ut11 < NUT; ut11++) { | |
815 | if ((um = utminfo[ut11]) == 0 || um->um_alive == 0 || | |
816 | um->um_ubanum != uban) | |
817 | continue; | |
818 | printf(" ut%d", ut11); | |
c36079e2 | 819 | um->um_tab.b_state = 0; |
b02cd1e9 BJ |
820 | um->um_tab.b_actf = um->um_tab.b_actl = 0; |
821 | if (um->um_ubinfo) { | |
822 | printf("<%d>", (um->um_ubinfo>>28)&0xf); | |
96b997eb | 823 | um->um_ubinfo = 0; |
b02cd1e9 BJ |
824 | } |
825 | ((struct utdevice *)(um->um_addr))->utcs1 = UT_CLEAR|UT_GO; | |
c36079e2 | 826 | ((struct utdevice *)(um->um_addr))->utcs2 |= UTCS2_CLR; |
b02cd1e9 BJ |
827 | for (tjunit = 0; tjunit < NTJ; tjunit++) { |
828 | if ((ui = tjdinfo[tjunit]) == 0 || ui->ui_mi != um || | |
829 | ui->ui_alive == 0) | |
830 | continue; | |
831 | dp = &tjutab[tjunit]; | |
c36079e2 | 832 | dp->b_state = 0; |
b02cd1e9 BJ |
833 | dp->b_forw = 0; |
834 | if (um->um_tab.b_actf == NULL) | |
835 | um->um_tab.b_actf = dp; | |
836 | else | |
837 | um->um_tab.b_actl->b_forw = dp; | |
838 | um->um_tab.b_actl = dp; | |
839 | if (tj_softc[tjunit].sc_openf > 0) | |
840 | tj_softc[tjunit].sc_openf = -1; | |
841 | } | |
842 | utstart(um); | |
843 | } | |
844 | } | |
845 | ||
846 | /* | |
847 | * Do a stand-alone core dump to tape -- | |
848 | * from here down, routines are used only in dump context | |
849 | */ | |
850 | #define DBSIZE 20 | |
851 | ||
852 | utdump() | |
853 | { | |
854 | register struct uba_device *ui; | |
855 | register struct uba_regs *up; | |
c36079e2 | 856 | register struct utdevice *addr; |
b02cd1e9 BJ |
857 | int blk, num = maxfree; |
858 | int start = 0; | |
859 | ||
860 | #define phys(a,b) ((b)((int)(a)&0x7fffffff)) | |
861 | if (tjdinfo[0] == 0) | |
862 | return (ENXIO); | |
863 | ui = phys(tjdinfo[0], struct uba_device *); | |
864 | up = phys(ui->ui_hd, struct uba_hd *)->uh_physuba; | |
89b8a44c | 865 | ubainit(up); |
b02cd1e9 | 866 | DELAY(1000000); |
c36079e2 | 867 | addr = (struct utdevice *)ui->ui_physaddr; |
89b8a44c | 868 | utwait(addr); |
c36079e2 SL |
869 | /* |
870 | * Be sure to set the appropriate density here. We use | |
871 | * 6250, but maybe it should be done at 1600 to insure the | |
872 | * tape can be read by most any other tape drive available. | |
873 | */ | |
874 | addr->uttc = UT_GCR|PDP11FMT; /* implicit slave 0 or-ed in */ | |
875 | addr->utcs1 = UT_CLEAR|UT_GO; | |
b02cd1e9 BJ |
876 | while (num > 0) { |
877 | blk = num > DBSIZE ? DBSIZE : num; | |
c36079e2 SL |
878 | utdwrite(start, blk, addr, up); |
879 | if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) | |
880 | return(EIO); | |
b02cd1e9 BJ |
881 | start += blk; |
882 | num -= blk; | |
883 | } | |
c36079e2 SL |
884 | uteof(addr); |
885 | uteof(addr); | |
886 | utwait(addr); | |
887 | if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) | |
b02cd1e9 | 888 | return(EIO); |
c36079e2 | 889 | addr->utcs1 = UT_REW|UT_GO; |
b02cd1e9 BJ |
890 | return (0); |
891 | } | |
892 | ||
c36079e2 | 893 | utdwrite(dbuf, num, addr, up) |
b02cd1e9 | 894 | register dbuf, num; |
c36079e2 | 895 | register struct utdevice *addr; |
b02cd1e9 BJ |
896 | struct uba_regs *up; |
897 | { | |
898 | register struct pte *io; | |
899 | register int npf; | |
900 | ||
c36079e2 | 901 | utwait(addr); |
b02cd1e9 BJ |
902 | io = up->uba_map; |
903 | npf = num + 1; | |
904 | while (--npf != 0) | |
905 | *(int *)io++ = (dbuf++ | (1<<UBAMR_DPSHIFT) | UBAMR_MRV); | |
906 | *(int *)io = 0; | |
c36079e2 SL |
907 | addr->utwc = -((num*NBPG)>>1); |
908 | addr->utfc = -(num*NBPG); | |
909 | addr->utba = 0; | |
910 | addr->utcs1 = UT_WCOM|UT_GO; | |
b02cd1e9 BJ |
911 | } |
912 | ||
c36079e2 SL |
913 | utwait(addr) |
914 | struct utdevice *addr; | |
b02cd1e9 BJ |
915 | { |
916 | register s; | |
917 | ||
918 | do | |
c36079e2 | 919 | s = addr->utds; |
b02cd1e9 BJ |
920 | while ((s&UTDS_DRY) == 0); |
921 | } | |
922 | ||
c36079e2 SL |
923 | uteof(addr) |
924 | struct utdevice *addr; | |
b02cd1e9 BJ |
925 | { |
926 | ||
c36079e2 SL |
927 | utwait(addr); |
928 | addr->utcs1 = UT_WEOF|UT_GO; | |
b02cd1e9 BJ |
929 | } |
930 | #endif |