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