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