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