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