|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1988 Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * This code is derived from software contributed to Berkeley by ! 6: * Computer Consoles Inc. ! 7: * ! 8: * Redistribution and use in source and binary forms are permitted ! 9: * provided that the above copyright notice and this paragraph are ! 10: * duplicated in all such forms and that any documentation, ! 11: * advertising materials, and other materials related to such ! 12: * distribution and use acknowledge that the software was developed ! 13: * by the University of California, Berkeley. The name of the ! 14: * University may not be used to endorse or promote products derived ! 15: * from this software without specific prior written permission. ! 16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 17: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 18: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 19: * ! 20: * @(#)cy.c 7.2 (Berkeley) 6/29/88 ! 21: */ ! 22: ! 23: #include "yc.h" ! 24: #if NCY > 0 ! 25: /* ! 26: * Cipher Tapemaster driver. ! 27: */ ! 28: #define CYDEBUG ! 29: #ifdef CYDEBUG ! 30: int cydebug = 0; ! 31: #define dlog(params) if (cydebug) log params ! 32: #else ! 33: #define dlog(params) /* */ ! 34: #endif ! 35: ! 36: #include "param.h" ! 37: #include "systm.h" ! 38: #include "vm.h" ! 39: #include "buf.h" ! 40: #include "file.h" ! 41: #include "dir.h" ! 42: #include "user.h" ! 43: #include "proc.h" ! 44: #include "signal.h" ! 45: #include "uio.h" ! 46: #include "ioctl.h" ! 47: #include "mtio.h" ! 48: #include "errno.h" ! 49: #include "cmap.h" ! 50: #include "kernel.h" ! 51: #include "syslog.h" ! 52: #include "tty.h" ! 53: ! 54: #include "../tahoe/cpu.h" ! 55: #include "../tahoe/mtpr.h" ! 56: #include "../tahoe/pte.h" ! 57: ! 58: #include "../tahoevba/vbavar.h" ! 59: #define CYERROR ! 60: #include "../tahoevba/cyreg.h" ! 61: ! 62: /* ! 63: * There is a ccybuf per tape controller. ! 64: * It is used as the token to pass to the internal routines ! 65: * to execute tape ioctls, and also acts as a lock on the slaves ! 66: * on the controller, since there is only one per controller. ! 67: * In particular, when the tape is rewinding on close we release ! 68: * the user process but any further attempts to use the tape drive ! 69: * before the rewind completes will hang waiting for ccybuf. ! 70: */ ! 71: struct buf ccybuf[NCY]; ! 72: ! 73: int cyprobe(), cyslave(), cyattach(); ! 74: struct buf ycutab[NYC]; ! 75: short yctocy[NYC]; ! 76: struct vba_ctlr *cyminfo[NCY]; ! 77: struct vba_device *ycdinfo[NYC]; ! 78: long cystd[] = { 0 }; ! 79: struct vba_driver cydriver = ! 80: { cyprobe, cyslave, cyattach, 0, cystd, "yc", ycdinfo, "cy", cyminfo }; ! 81: ! 82: /* bits in minor device */ ! 83: #define YCUNIT(dev) (minor(dev)&03) ! 84: #define CYUNIT(dev) (yctocy[YCUNIT(dev)]) ! 85: #define T_NOREWIND 0x04 ! 86: #define T_1600BPI 0x00 /* pseudo */ ! 87: #define T_3200BPI 0x08 /* unused */ ! 88: ! 89: #define INF 1000000L /* close to infinity */ ! 90: ! 91: /* ! 92: * Software state and shared command areas per controller. ! 93: * ! 94: * The i/o intermediate buffer must be allocated in startup() ! 95: * so its address will fit in 20-bits (YECH!!!!!!!!!!!!!!). ! 96: */ ! 97: struct cy_softc { ! 98: int cy_bs; /* controller's buffer size */ ! 99: struct cyscp *cy_scp; /* system configuration block address */ ! 100: struct cyccb cy_ccb; /* channel control block */ ! 101: struct cyscb cy_scb; /* system configuration block */ ! 102: struct cytpb cy_tpb; /* tape parameter block */ ! 103: struct cytpb cy_nop; /* nop parameter block for cyintr */ ! 104: struct vb_buf cy_rbuf; /* vba resources */ ! 105: } cy_softc[NCY]; ! 106: ! 107: /* ! 108: * Software state per tape transport. ! 109: */ ! 110: struct yc_softc { ! 111: char yc_openf; /* lock against multiple opens */ ! 112: char yc_lastiow; /* last operation was a write */ ! 113: short yc_tact; /* timeout is active */ ! 114: long yc_timo; /* time until timeout expires */ ! 115: u_short yc_control; /* copy of last tpcb.tpcontrol */ ! 116: u_short yc_status; /* copy of last tpcb.tpstatus */ ! 117: u_short yc_resid; /* copy of last bc */ ! 118: u_short yc_dens; /* prototype control word with density info */ ! 119: struct tty *yc_ttyp; /* user's tty for errors */ ! 120: daddr_t yc_blkno; /* block number, for block device tape */ ! 121: daddr_t yc_nxrec; /* position of end of tape, if known */ ! 122: int yc_blksize; /* current tape blocksize estimate */ ! 123: int yc_blks; /* number of I/O operations since open */ ! 124: int yc_softerrs; /* number of soft I/O errors since open */ ! 125: } yc_softc[NYC]; ! 126: ! 127: /* ! 128: * States for vm->um_tab.b_active, the per controller state flag. ! 129: * This is used to sequence control in the driver. ! 130: */ ! 131: #define SSEEK 1 /* seeking */ ! 132: #define SIO 2 /* doing seq i/o */ ! 133: #define SCOM 3 /* sending control command */ ! 134: #define SREW 4 /* sending a rewind */ ! 135: #define SERASE 5 /* erase inter-record gap */ ! 136: #define SERASED 6 /* erased inter-record gap */ ! 137: ! 138: /* there's no way to figure these out dynamically? -- yech */ ! 139: struct cyscp *cyscp[] = ! 140: { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; ! 141: #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) ! 142: ! 143: cyprobe(reg, vm) ! 144: caddr_t reg; ! 145: struct vba_ctlr *vm; ! 146: { ! 147: register br, cvec; /* must be r12, r11 */ ! 148: register struct cy_softc *cy; ! 149: int ctlr = vm->um_ctlr; ! 150: ! 151: #ifdef lint ! 152: br = 0; cvec = br; br = cvec; ! 153: cyintr(0); ! 154: #endif ! 155: if (badcyaddr(reg+1)) ! 156: return (0); ! 157: if (ctlr > NCYSCP || cyscp[ctlr] == 0) /* XXX */ ! 158: return (0); ! 159: cy = &cy_softc[ctlr]; ! 160: cy->cy_scp = cyscp[ctlr]; /* XXX */ ! 161: /* ! 162: * Tapemaster controller must have interrupt handler ! 163: * disable interrupt, so we'll just kludge things ! 164: * (stupid multibus non-vectored interrupt crud). ! 165: */ ! 166: if (cyinit(ctlr, reg)) { ! 167: uncache(&cy->cy_tpb.tpcount); ! 168: cy->cy_bs = htoms(cy->cy_tpb.tpcount); ! 169: /* ! 170: * Setup nop parameter block for clearing interrupts. ! 171: */ ! 172: cy->cy_nop.tpcmd = CY_NOP; ! 173: cy->cy_nop.tpcontrol = 0; ! 174: /* ! 175: * Allocate page tables. ! 176: */ ! 177: if (cybuf == 0) { ! 178: printf("no cy buffer!!!\n"); ! 179: return (0); ! 180: } ! 181: cy->cy_rbuf.vb_rawbuf = cybuf + ctlr * CYMAXIO; ! 182: if (vbainit(&cy->cy_rbuf, CYMAXIO, VB_20BIT) == 0) { ! 183: printf("cy%d: vbainit failed\n", ctlr); ! 184: return (0); ! 185: } ! 186: ! 187: br = 0x13, cvec = 0x80; /* XXX */ ! 188: return (sizeof (struct cyccb)); ! 189: } else ! 190: return (0); ! 191: } ! 192: ! 193: /* ! 194: * Check to see if a drive is attached to a controller. ! 195: * Since we can only tell that a drive is there if a tape is loaded and ! 196: * the drive is placed online, we always indicate the slave is present. ! 197: */ ! 198: cyslave(vi, addr) ! 199: struct vba_device *vi; ! 200: caddr_t addr; ! 201: { ! 202: ! 203: #ifdef lint ! 204: vi = vi; addr = addr; ! 205: #endif ! 206: return (1); ! 207: } ! 208: ! 209: cyattach(vi) ! 210: struct vba_device *vi; ! 211: { ! 212: register struct cy_softc *cy; ! 213: int ctlr = vi->ui_mi->um_ctlr; ! 214: ! 215: yctocy[vi->ui_unit] = ctlr; ! 216: cy = &cy_softc[ctlr]; ! 217: if (vi->ui_slave == 0 && cy->cy_bs) ! 218: printf("; %dkb buffer", cy->cy_bs/1024); ! 219: } ! 220: ! 221: /* ! 222: * Initialize the controller after a controller reset or ! 223: * during autoconfigure. All of the system control blocks ! 224: * are initialized and the controller is asked to configure ! 225: * itself for later use. ! 226: */ ! 227: cyinit(ctlr, addr) ! 228: int ctlr; ! 229: register caddr_t addr; ! 230: { ! 231: register struct cy_softc *cy = &cy_softc[ctlr]; ! 232: register int *pte; ! 233: ! 234: /* ! 235: * Initialize the system configuration pointer. ! 236: */ ! 237: /* make kernel writable */ ! 238: pte = (int *)&Sysmap[btop((int)cy->cy_scp &~ KERNBASE)]; ! 239: *pte &= ~PG_PROT; *pte |= PG_KW; ! 240: mtpr(TBIS, cy->cy_scp); ! 241: /* load the correct values in the scp */ ! 242: cy->cy_scp->csp_buswidth = CSP_16BITS; ! 243: cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); ! 244: /* put it back to read-only */ ! 245: *pte &= ~PG_PROT; *pte |= PG_KR; ! 246: mtpr(TBIS, cy->cy_scp); ! 247: ! 248: /* ! 249: * Init system configuration block. ! 250: */ ! 251: cy->cy_scb.csb_fixed = CSB_FIXED; ! 252: /* set pointer to the channel control block */ ! 253: cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); ! 254: ! 255: /* ! 256: * Initialize the chanel control block. ! 257: */ ! 258: cy->cy_ccb.cbcw = CBCW_CLRINT; ! 259: cy->cy_ccb.cbgate = GATE_OPEN; ! 260: /* set pointer to the tape parameter block */ ! 261: cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); ! 262: ! 263: /* ! 264: * Issue a nop cmd and get the internal buffer size for buffered i/o. ! 265: */ ! 266: cy->cy_tpb.tpcmd = CY_NOP; ! 267: cy->cy_tpb.tpcontrol = CYCW_16BITS; ! 268: cy->cy_ccb.cbgate = GATE_CLOSED; ! 269: CY_GO(addr); ! 270: if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { ! 271: uncache(&cy->cy_tpb.tpstatus); ! 272: printf("cy%d: timeout or err during init, status=%b\n", ctlr, ! 273: cy->cy_tpb.tpstatus, CYS_BITS); ! 274: return (0); ! 275: } ! 276: cy->cy_tpb.tpcmd = CY_CONFIG; ! 277: cy->cy_tpb.tpcontrol = CYCW_16BITS; ! 278: cy->cy_ccb.cbgate = GATE_CLOSED; ! 279: CY_GO(addr); ! 280: if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { ! 281: uncache(&cy->cy_tpb.tpstatus); ! 282: printf("cy%d: configuration failure, status=%b\n", ctlr, ! 283: cy->cy_tpb.tpstatus, CYS_BITS); ! 284: return (0); ! 285: } ! 286: return (1); ! 287: } ! 288: ! 289: int cytimer(); ! 290: /* ! 291: * Open the device. Tapes are unique open ! 292: * devices, so we refuse if it is already open. ! 293: * We also check that a tape is available, and ! 294: * don't block waiting here; if you want to wait ! 295: * for a tape you should timeout in user code. ! 296: */ ! 297: cyopen(dev, flag) ! 298: dev_t dev; ! 299: register int flag; ! 300: { ! 301: register int ycunit; ! 302: register struct vba_device *vi; ! 303: register struct yc_softc *yc; ! 304: ! 305: ycunit = YCUNIT(dev); ! 306: if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) ! 307: return (ENXIO); ! 308: if ((yc = &yc_softc[ycunit])->yc_openf) ! 309: return (EBUSY); ! 310: yc->yc_openf = 1; ! 311: #define PACKUNIT(vi) \ ! 312: (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) ! 313: /* no way to select density */ ! 314: yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; ! 315: if (yc->yc_tact == 0) { ! 316: yc->yc_timo = INF; ! 317: yc->yc_tact = 1; ! 318: timeout(cytimer, (caddr_t)dev, 5*hz); ! 319: } ! 320: cycommand(dev, CY_SENSE, 1); ! 321: if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ ! 322: uprintf("cy%d: not online\n", ycunit); ! 323: yc->yc_openf = 0; ! 324: return (EIO); ! 325: } ! 326: if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { ! 327: uprintf("cy%d: no write ring\n", ycunit); ! 328: yc->yc_openf = 0; ! 329: return (EIO); ! 330: } ! 331: yc->yc_blkno = (daddr_t)0; ! 332: yc->yc_nxrec = INF; ! 333: yc->yc_lastiow = 0; ! 334: yc->yc_blksize = CYMAXIO; /* guess > 0 */ ! 335: yc->yc_blks = 0; ! 336: yc->yc_softerrs = 0; ! 337: yc->yc_ttyp = u.u_ttyp; ! 338: return (0); ! 339: } ! 340: ! 341: /* ! 342: * Close tape device. ! 343: * ! 344: * If tape was open for writing or last operation was a write, ! 345: * then write two EOF's and backspace over the last one. ! 346: * Unless this is a non-rewinding special file, rewind the tape. ! 347: * Make the tape available to others. ! 348: */ ! 349: cyclose(dev, flag) ! 350: dev_t dev; ! 351: int flag; ! 352: { ! 353: struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; ! 354: ! 355: if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { ! 356: cycommand(dev, CY_WEOF, 1); /* can't use count with WEOF */ ! 357: cycommand(dev, CY_WEOF, 1); ! 358: cycommand(dev, CY_SREV, 1); ! 359: } ! 360: if ((minor(dev)&T_NOREWIND) == 0) ! 361: /* ! 362: * 0 count means don't hang waiting for rewind complete ! 363: * rather ccybuf stays busy until the operation completes ! 364: * preventing further opens from completing by preventing ! 365: * a CY_SENSE from completing. ! 366: */ ! 367: cycommand(dev, CY_REW, 0); ! 368: if (yc->yc_blks > 10 && yc->yc_softerrs > yc->yc_blks / 10) ! 369: log(LOG_INFO, "yc%d: %d soft errors in %d blocks\n", ! 370: YCUNIT(dev), yc->yc_softerrs, yc->yc_blks); ! 371: dlog((LOG_INFO, "%d soft errors in %d blocks\n", ! 372: yc->yc_softerrs, yc->yc_blks)); ! 373: yc->yc_openf = 0; ! 374: return (0); ! 375: } ! 376: ! 377: /* ! 378: * Execute a command on the tape drive a specified number of times. ! 379: */ ! 380: cycommand(dev, com, count) ! 381: dev_t dev; ! 382: int com, count; ! 383: { ! 384: register struct buf *bp; ! 385: int s; ! 386: ! 387: bp = &ccybuf[CYUNIT(dev)]; ! 388: s = spl3(); ! 389: dlog((LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", ! 390: dev, com, count, bp->b_flags)); ! 391: while (bp->b_flags&B_BUSY) { ! 392: /* ! 393: * This special check is because B_BUSY never ! 394: * gets cleared in the non-waiting rewind case. ! 395: */ ! 396: if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) ! 397: break; ! 398: bp->b_flags |= B_WANTED; ! 399: sleep((caddr_t)bp, PRIBIO); ! 400: } ! 401: bp->b_flags = B_BUSY|B_READ; ! 402: splx(s); ! 403: bp->b_dev = dev; ! 404: bp->b_repcnt = count; ! 405: bp->b_command = com; ! 406: bp->b_blkno = 0; ! 407: cystrategy(bp); ! 408: /* ! 409: * In case of rewind from close; don't wait. ! 410: * This is the only case where count can be 0. ! 411: */ ! 412: if (count == 0) ! 413: return; ! 414: biowait(bp); ! 415: if (bp->b_flags&B_WANTED) ! 416: wakeup((caddr_t)bp); ! 417: bp->b_flags &= B_ERROR; ! 418: } ! 419: ! 420: cystrategy(bp) ! 421: register struct buf *bp; ! 422: { ! 423: int ycunit = YCUNIT(bp->b_dev); ! 424: register struct vba_ctlr *vm; ! 425: register struct buf *dp; ! 426: int s; ! 427: ! 428: /* ! 429: * Put transfer at end of unit queue. ! 430: */ ! 431: dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); ! 432: dp = &ycutab[ycunit]; ! 433: bp->av_forw = NULL; ! 434: vm = ycdinfo[ycunit]->ui_mi; ! 435: /* BEGIN GROT */ ! 436: if (bp->b_flags & B_RAW) { ! 437: if (bp->b_bcount >= CYMAXIO) { ! 438: uprintf("cy%d: i/o size too large\n", vm->um_ctlr); ! 439: bp->b_error = EINVAL; ! 440: bp->b_resid = bp->b_bcount; ! 441: bp->b_flags |= B_ERROR; ! 442: biodone(bp); ! 443: return; ! 444: } ! 445: } ! 446: /* END GROT */ ! 447: s = spl3(); ! 448: if (dp->b_actf == NULL) { ! 449: dp->b_actf = bp; ! 450: /* ! 451: * Transport not already active... ! 452: * put at end of controller queue. ! 453: */ ! 454: dp->b_forw = NULL; ! 455: if (vm->um_tab.b_actf == NULL) ! 456: vm->um_tab.b_actf = dp; ! 457: else ! 458: vm->um_tab.b_actl->b_forw = dp; ! 459: } else ! 460: dp->b_actl->av_forw = bp; ! 461: dp->b_actl = bp; ! 462: /* ! 463: * If the controller is not busy, get it going. ! 464: */ ! 465: if (vm->um_tab.b_active == 0) ! 466: cystart(vm); ! 467: splx(s); ! 468: } ! 469: ! 470: /* ! 471: * Start activity on a cy controller. ! 472: */ ! 473: cystart(vm) ! 474: register struct vba_ctlr *vm; ! 475: { ! 476: register struct buf *bp, *dp; ! 477: register struct yc_softc *yc; ! 478: register struct cy_softc *cy; ! 479: int ycunit; ! 480: daddr_t blkno; ! 481: ! 482: dlog((LOG_INFO, "cystart()\n")); ! 483: /* ! 484: * Look for an idle transport on the controller. ! 485: */ ! 486: loop: ! 487: if ((dp = vm->um_tab.b_actf) == NULL) ! 488: return; ! 489: if ((bp = dp->b_actf) == NULL) { ! 490: vm->um_tab.b_actf = dp->b_forw; ! 491: goto loop; ! 492: } ! 493: ycunit = YCUNIT(bp->b_dev); ! 494: yc = &yc_softc[ycunit]; ! 495: cy = &cy_softc[CYUNIT(bp->b_dev)]; ! 496: /* ! 497: * Default is that last command was NOT a write command; ! 498: * if we do a write command we will notice this in cyintr(). ! 499: */ ! 500: yc->yc_lastiow = 0; ! 501: if (yc->yc_openf < 0 || ! 502: (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { ! 503: /* ! 504: * Have had a hard error on a non-raw tape ! 505: * or the tape unit is now unavailable (e.g. ! 506: * taken off line). ! 507: */ ! 508: dlog((LOG_INFO, "openf %d command %x status %b\n", ! 509: yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); ! 510: bp->b_flags |= B_ERROR; ! 511: goto next; ! 512: } ! 513: if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { ! 514: /* ! 515: * Execute control operation with the specified count. ! 516: * ! 517: * Set next state; give 5 minutes to complete ! 518: * rewind or file mark search, or 10 seconds per ! 519: * iteration (minimum 60 seconds and max 5 minutes) ! 520: * to complete other ops. ! 521: */ ! 522: if (bp->b_command == CY_REW) { ! 523: vm->um_tab.b_active = SREW; ! 524: yc->yc_timo = 5*60; ! 525: } else if (bp->b_command == CY_FSF || ! 526: bp->b_command == CY_BSF) { ! 527: vm->um_tab.b_active = SCOM; ! 528: yc->yc_timo = 5*60; ! 529: } else { ! 530: vm->um_tab.b_active = SCOM; ! 531: yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); ! 532: } ! 533: cy->cy_tpb.tprec = htoms(bp->b_repcnt); ! 534: dlog((LOG_INFO, "bpcmd ")); ! 535: goto dobpcmd; ! 536: } ! 537: /* ! 538: * For raw I/O, save the current block ! 539: * number in case we have to retry. ! 540: */ ! 541: if (bp->b_flags & B_RAW) { ! 542: if (vm->um_tab.b_errcnt == 0) { ! 543: yc->yc_blkno = bp->b_blkno; ! 544: yc->yc_nxrec = yc->yc_blkno + 1; ! 545: } ! 546: } else { ! 547: /* ! 548: * Handle boundary cases for operation ! 549: * on non-raw tapes. ! 550: */ ! 551: if (bp->b_blkno > yc->yc_nxrec) { ! 552: /* ! 553: * Can't read past known end-of-file. ! 554: */ ! 555: bp->b_flags |= B_ERROR; ! 556: bp->b_error = ENXIO; ! 557: goto next; ! 558: } ! 559: if (bp->b_blkno == yc->yc_nxrec && bp->b_flags&B_READ) { ! 560: /* ! 561: * Reading at end of file returns 0 bytes. ! 562: */ ! 563: bp->b_resid = bp->b_bcount; ! 564: clrbuf(bp); ! 565: goto next; ! 566: } ! 567: if ((bp->b_flags&B_READ) == 0) ! 568: /* ! 569: * Writing sets EOF. ! 570: */ ! 571: yc->yc_nxrec = bp->b_blkno + 1; ! 572: } ! 573: if ((blkno = yc->yc_blkno) == bp->b_blkno) { ! 574: caddr_t addr; ! 575: int cmd; ! 576: ! 577: /* ! 578: * Choose the appropriate i/o command based on the ! 579: * transfer size, the estimated block size, ! 580: * and the controller's internal buffer size. ! 581: * If the request length is longer than the tape ! 582: * block length, a buffered read will fail, ! 583: * thus, we request at most the size that we expect. ! 584: * We then check for larger records when the read completes. ! 585: * If we're retrying a read on a raw device because ! 586: * the original try was a buffer request which failed ! 587: * due to a record length error, then we force the use ! 588: * of the raw controller read (YECH!!!!). ! 589: */ ! 590: if (bp->b_flags&B_READ) { ! 591: if (yc->yc_blksize <= cy->cy_bs && ! 592: vm->um_tab.b_errcnt == 0) ! 593: cmd = CY_BRCOM; ! 594: else ! 595: cmd = CY_RCOM; ! 596: } else { ! 597: /* ! 598: * On write error retries erase the ! 599: * inter-record gap before rewriting. ! 600: */ ! 601: if (vm->um_tab.b_errcnt && ! 602: vm->um_tab.b_active != SERASED) { ! 603: vm->um_tab.b_active = SERASE; ! 604: bp->b_command = CY_ERASE; ! 605: yc->yc_timo = 60; ! 606: goto dobpcmd; ! 607: } ! 608: cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; ! 609: } ! 610: vm->um_tab.b_active = SIO; ! 611: addr = (caddr_t)vbasetup(bp, &cy->cy_rbuf, 1); ! 612: cy->cy_tpb.tpcmd = cmd; ! 613: cy->cy_tpb.tpcontrol = yc->yc_dens; ! 614: if (cmd == CY_RCOM || cmd == CY_WCOM) ! 615: cy->cy_tpb.tpcontrol |= CYCW_LOCK; ! 616: cy->cy_tpb.tpstatus = 0; ! 617: cy->cy_tpb.tpcount = 0; ! 618: cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); ! 619: cy->cy_tpb.tprec = 0; ! 620: if (cmd == CY_BRCOM) ! 621: cy->cy_tpb.tpsize = htoms(imin(yc->yc_blksize, ! 622: (int)bp->b_bcount)); ! 623: else ! 624: cy->cy_tpb.tpsize = htoms(bp->b_bcount); ! 625: cyldmba(cy->cy_tpb.tplink, (caddr_t)0); ! 626: do ! 627: uncache(&cy->cy_ccb.cbgate); ! 628: while (cy->cy_ccb.cbgate == GATE_CLOSED); ! 629: cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); ! 630: cy->cy_ccb.cbcw = CBCW_IE; ! 631: cy->cy_ccb.cbgate = GATE_CLOSED; ! 632: dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", ! 633: vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, ! 634: htoms(cy->cy_tpb.tpsize))); ! 635: CY_GO(vm->um_addr); ! 636: return; ! 637: } ! 638: /* ! 639: * Tape positioned incorrectly; set to seek forwards ! 640: * or backwards to the correct spot. This happens ! 641: * for raw tapes only on error retries. ! 642: */ ! 643: vm->um_tab.b_active = SSEEK; ! 644: if (blkno < bp->b_blkno) { ! 645: bp->b_command = CY_SFORW; ! 646: cy->cy_tpb.tprec = htoms(bp->b_blkno - blkno); ! 647: } else { ! 648: bp->b_command = CY_SREV; ! 649: cy->cy_tpb.tprec = htoms(blkno - bp->b_blkno); ! 650: } ! 651: yc->yc_timo = imin(imax((int)(10 * htoms(cy->cy_tpb.tprec)), 60), 5*60); ! 652: dobpcmd: ! 653: /* ! 654: * Do the command in bp. Reverse direction commands ! 655: * are indicated by having CYCW_REV or'd into their ! 656: * value. For these we must set the appropriate bit ! 657: * in the control field. ! 658: */ ! 659: if (bp->b_command&CYCW_REV) { ! 660: cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; ! 661: cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; ! 662: dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); ! 663: } else { ! 664: cy->cy_tpb.tpcmd = bp->b_command; ! 665: cy->cy_tpb.tpcontrol = yc->yc_dens; ! 666: dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); ! 667: } ! 668: cy->cy_tpb.tpstatus = 0; ! 669: cy->cy_tpb.tpcount = 0; ! 670: cyldmba(cy->cy_tpb.tplink, (caddr_t)0); ! 671: do ! 672: uncache(&cy->cy_ccb.cbgate); ! 673: while (cy->cy_ccb.cbgate == GATE_CLOSED); ! 674: cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); ! 675: cy->cy_ccb.cbcw = CBCW_IE; ! 676: cy->cy_ccb.cbgate = GATE_CLOSED; ! 677: dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", ! 678: vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, ! 679: htoms(cy->cy_tpb.tprec))); ! 680: CY_GO(vm->um_addr); ! 681: return; ! 682: next: ! 683: /* ! 684: * Done with this operation due to error or the ! 685: * fact that it doesn't do anything. ! 686: * Dequeue the transfer and continue ! 687: * processing this slave. ! 688: */ ! 689: vm->um_tab.b_errcnt = 0; ! 690: dp->b_actf = bp->av_forw; ! 691: biodone(bp); ! 692: goto loop; ! 693: } ! 694: ! 695: /* ! 696: * Cy interrupt routine. ! 697: */ ! 698: cyintr(cyunit) ! 699: int cyunit; ! 700: { ! 701: struct buf *dp; ! 702: register struct buf *bp; ! 703: register struct vba_ctlr *vm = cyminfo[cyunit]; ! 704: register struct cy_softc *cy; ! 705: register struct yc_softc *yc; ! 706: int err; ! 707: register state; ! 708: ! 709: dlog((LOG_INFO, "cyintr(%d)\n", cyunit)); ! 710: /* ! 711: * First, turn off the interrupt from the controller ! 712: * (device uses Multibus non-vectored interrupts...yech). ! 713: */ ! 714: cy = &cy_softc[vm->um_ctlr]; ! 715: cy->cy_ccb.cbcw = CBCW_CLRINT; ! 716: cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); ! 717: cy->cy_ccb.cbgate = GATE_CLOSED; ! 718: CY_GO(vm->um_addr); ! 719: if ((dp = vm->um_tab.b_actf) == NULL) { ! 720: dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); ! 721: return; ! 722: } ! 723: bp = dp->b_actf; ! 724: cy = &cy_softc[cyunit]; ! 725: cyuncachetpb(cy); ! 726: yc = &yc_softc[YCUNIT(bp->b_dev)]; ! 727: /* ! 728: * If last command was a rewind and tape is ! 729: * still moving, wait for the operation to complete. ! 730: */ ! 731: if (vm->um_tab.b_active == SREW) { ! 732: vm->um_tab.b_active = SCOM; ! 733: if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { ! 734: yc->yc_timo = 5*60; /* 5 minutes */ ! 735: return; ! 736: } ! 737: } ! 738: /* ! 739: * An operation completed...record status. ! 740: */ ! 741: yc->yc_timo = INF; ! 742: yc->yc_control = cy->cy_tpb.tpcontrol; ! 743: yc->yc_status = cy->cy_tpb.tpstatus; ! 744: yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); ! 745: dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", ! 746: cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, ! 747: yc->yc_status, CYS_BITS, yc->yc_resid)); ! 748: if ((bp->b_flags&B_READ) == 0) ! 749: yc->yc_lastiow = 1; ! 750: state = vm->um_tab.b_active; ! 751: vm->um_tab.b_active = 0; ! 752: /* ! 753: * Check for errors. ! 754: */ ! 755: if (cy->cy_tpb.tpstatus&CYS_ERR) { ! 756: err = cy->cy_tpb.tpstatus&CYS_ERR; ! 757: dlog((LOG_INFO, "error %d\n", err)); ! 758: /* ! 759: * If we hit the end of tape file, update our position. ! 760: */ ! 761: if (err == CYER_FM) { ! 762: yc->yc_status |= CYS_FM; ! 763: state = SCOM; /* force completion */ ! 764: cyseteof(bp); /* set blkno and nxrec */ ! 765: goto opdone; ! 766: } ! 767: /* ! 768: * Fix up errors which occur due to backspacing over ! 769: * the beginning of the tape. ! 770: */ ! 771: if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { ! 772: yc->yc_status |= CYS_BOT; ! 773: goto ignoreerr; ! 774: } ! 775: /* ! 776: * If we were reading raw tape and the only error was that the ! 777: * record was too long, then we don't consider this an error. ! 778: */ ! 779: if ((bp->b_flags & (B_READ|B_RAW)) == (B_READ|B_RAW) && ! 780: err == CYER_STROBE) { ! 781: /* ! 782: * Retry reads with the command changed to ! 783: * a raw read if necessary. Setting b_errcnt ! 784: * here causes cystart (above) to force a CY_RCOM. ! 785: */ ! 786: if (cy->cy_tpb.tpcmd == CY_BRCOM && ! 787: vm->um_tab.b_errcnt++ == 0) { ! 788: yc->yc_blkno++; ! 789: goto opcont; ! 790: } else ! 791: goto ignoreerr; ! 792: } ! 793: /* ! 794: * If error is not hard, and this was an i/o operation ! 795: * retry up to 8 times. ! 796: */ ! 797: if (state == SIO && (CYMASK(err) & ! 798: ((bp->b_flags&B_READ) ? CYER_RSOFT : CYER_WSOFT))) { ! 799: if (++vm->um_tab.b_errcnt < 7) { ! 800: yc->yc_blkno++; ! 801: goto opcont; ! 802: } ! 803: } else ! 804: /* ! 805: * Hard or non-i/o errors on non-raw tape ! 806: * cause it to close. ! 807: */ ! 808: if ((bp->b_flags&B_RAW) == 0 && ! 809: yc->yc_openf > 0) ! 810: yc->yc_openf = -1; ! 811: /* ! 812: * Couldn't recover from error. ! 813: */ ! 814: tprintf(yc->yc_ttyp, ! 815: "yc%d: hard error bn%d status=%b, %s\n", YCUNIT(bp->b_dev), ! 816: bp->b_blkno, yc->yc_status, CYS_BITS, ! 817: (err < NCYERROR) ? cyerror[err] : ""); ! 818: bp->b_flags |= B_ERROR; ! 819: goto opdone; ! 820: } else if (cy->cy_tpb.tpcmd == CY_BRCOM) { ! 821: int reclen = htoms(cy->cy_tpb.tprec); ! 822: ! 823: /* ! 824: * If we did a buffered read, check whether the read ! 825: * was long enough. If we asked the controller for less ! 826: * than the user asked for because the previous record ! 827: * was shorter, update our notion of record size ! 828: * and retry. If the record is longer than the buffer, ! 829: * bump the errcnt so the retry will use direct read. ! 830: */ ! 831: if (reclen > yc->yc_blksize && bp->b_bcount > yc->yc_blksize) { ! 832: yc->yc_blksize = reclen; ! 833: if (reclen > cy->cy_bs) ! 834: vm->um_tab.b_errcnt++; ! 835: yc->yc_blkno++; ! 836: goto opcont; ! 837: } ! 838: } ! 839: /* ! 840: * Advance tape control FSM. ! 841: */ ! 842: ignoreerr: ! 843: /* ! 844: * If we hit a tape mark update our position. ! 845: */ ! 846: if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { ! 847: cyseteof(bp); ! 848: goto opdone; ! 849: } ! 850: switch (state) { ! 851: ! 852: case SIO: ! 853: /* ! 854: * Read/write increments tape block number. ! 855: */ ! 856: yc->yc_blkno++; ! 857: yc->yc_blks++; ! 858: if (vm->um_tab.b_errcnt || yc->yc_status & CYS_CR) ! 859: yc->yc_softerrs++; ! 860: yc->yc_blksize = htoms(cy->cy_tpb.tpcount); ! 861: dlog((LOG_ERR, "blocksize %d", yc->yc_blksize)); ! 862: goto opdone; ! 863: ! 864: case SCOM: ! 865: /* ! 866: * For forward/backward space record update current position. ! 867: */ ! 868: if (bp == &ccybuf[CYUNIT(bp->b_dev)]) ! 869: switch ((int)bp->b_command) { ! 870: ! 871: case CY_SFORW: ! 872: yc->yc_blkno -= bp->b_repcnt; ! 873: break; ! 874: ! 875: case CY_SREV: ! 876: yc->yc_blkno += bp->b_repcnt; ! 877: break; ! 878: } ! 879: goto opdone; ! 880: ! 881: case SSEEK: ! 882: yc->yc_blkno = bp->b_blkno; ! 883: goto opcont; ! 884: ! 885: case SERASE: ! 886: /* ! 887: * Completed erase of the inter-record gap due to a ! 888: * write error; now retry the write operation. ! 889: */ ! 890: vm->um_tab.b_active = SERASED; ! 891: goto opcont; ! 892: } ! 893: ! 894: opdone: ! 895: /* ! 896: * Reset error count and remove from device queue. ! 897: */ ! 898: vm->um_tab.b_errcnt = 0; ! 899: dp->b_actf = bp->av_forw; ! 900: /* ! 901: * Save resid and release resources. ! 902: */ ! 903: bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); ! 904: if (bp != &ccybuf[cyunit]) ! 905: vbadone(bp, &cy->cy_rbuf); ! 906: biodone(bp); ! 907: /* ! 908: * Circulate slave to end of controller ! 909: * queue to give other slaves a chance. ! 910: */ ! 911: vm->um_tab.b_actf = dp->b_forw; ! 912: if (dp->b_actf) { ! 913: dp->b_forw = NULL; ! 914: if (vm->um_tab.b_actf == NULL) ! 915: vm->um_tab.b_actf = dp; ! 916: else ! 917: vm->um_tab.b_actl->b_forw = dp; ! 918: } ! 919: if (vm->um_tab.b_actf == 0) ! 920: return; ! 921: opcont: ! 922: cystart(vm); ! 923: } ! 924: ! 925: cytimer(dev) ! 926: int dev; ! 927: { ! 928: register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; ! 929: int s; ! 930: ! 931: if (yc->yc_openf == 0 && yc->yc_timo == INF) { ! 932: yc->yc_tact = 0; ! 933: return; ! 934: } ! 935: if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { ! 936: printf("yc%d: lost interrupt\n", YCUNIT(dev)); ! 937: yc->yc_timo = INF; ! 938: s = spl3(); ! 939: cyintr(CYUNIT(dev)); ! 940: splx(s); ! 941: } ! 942: timeout(cytimer, (caddr_t)dev, 5*hz); ! 943: } ! 944: ! 945: cyseteof(bp) ! 946: register struct buf *bp; ! 947: { ! 948: register int cyunit = CYUNIT(bp->b_dev); ! 949: register struct cy_softc *cy = &cy_softc[cyunit]; ! 950: register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; ! 951: ! 952: if (bp == &ccybuf[cyunit]) { ! 953: if (yc->yc_blkno > bp->b_blkno) { ! 954: /* reversing */ ! 955: yc->yc_nxrec = bp->b_blkno - htoms(cy->cy_tpb.tpcount); ! 956: yc->yc_blkno = yc->yc_nxrec; ! 957: } else { ! 958: yc->yc_blkno = bp->b_blkno + htoms(cy->cy_tpb.tpcount); ! 959: yc->yc_nxrec = yc->yc_blkno - 1; ! 960: } ! 961: return; ! 962: } ! 963: /* eof on read */ ! 964: yc->yc_nxrec = bp->b_blkno; ! 965: } ! 966: ! 967: /*ARGSUSED*/ ! 968: cyioctl(dev, cmd, data, flag) ! 969: caddr_t data; ! 970: dev_t dev; ! 971: { ! 972: int ycunit = YCUNIT(dev); ! 973: register struct yc_softc *yc = &yc_softc[ycunit]; ! 974: register struct buf *bp = &ccybuf[CYUNIT(dev)]; ! 975: register callcount; ! 976: int fcount, op; ! 977: struct mtop *mtop; ! 978: struct mtget *mtget; ! 979: /* we depend of the values and order of the MT codes here */ ! 980: static cyops[] = ! 981: {CY_WEOF,CY_FSF,CY_BSF,CY_SFORW,CY_SREV,CY_REW,CY_OFFL,CY_SENSE}; ! 982: ! 983: switch (cmd) { ! 984: ! 985: case MTIOCTOP: /* tape operation */ ! 986: mtop = (struct mtop *)data; ! 987: switch (op = mtop->mt_op) { ! 988: ! 989: case MTWEOF: ! 990: callcount = mtop->mt_count; ! 991: fcount = 1; ! 992: break; ! 993: ! 994: case MTFSR: case MTBSR: ! 995: callcount = 1; ! 996: fcount = mtop->mt_count; ! 997: break; ! 998: ! 999: case MTFSF: case MTBSF: ! 1000: callcount = mtop->mt_count; ! 1001: fcount = 1; ! 1002: break; ! 1003: ! 1004: case MTREW: case MTOFFL: case MTNOP: ! 1005: callcount = 1; ! 1006: fcount = 1; ! 1007: break; ! 1008: ! 1009: default: ! 1010: return (ENXIO); ! 1011: } ! 1012: if (callcount <= 0 || fcount <= 0) ! 1013: return (EINVAL); ! 1014: while (--callcount >= 0) { ! 1015: #ifdef notdef ! 1016: /* ! 1017: * Gagh, this controller is the pits... ! 1018: */ ! 1019: if (op == MTFSF || op == MTBSF) { ! 1020: do ! 1021: cycommand(dev, cyops[op], 1); ! 1022: while ((bp->b_flags&B_ERROR) == 0 && ! 1023: (yc->yc_status&(CYS_EOT|CYS_BOT|CYS_FM)) == 0); ! 1024: } else ! 1025: #endif ! 1026: cycommand(dev, cyops[op], fcount); ! 1027: dlog((LOG_INFO, ! 1028: "cyioctl: status %x, b_flags %x, resid %d\n", ! 1029: yc->yc_status, bp->b_flags, bp->b_resid)); ! 1030: if ((bp->b_flags&B_ERROR) || ! 1031: (yc->yc_status&(CYS_BOT|CYS_EOT))) ! 1032: break; ! 1033: } ! 1034: bp->b_resid = callcount + 1; ! 1035: return (geterror(bp)); ! 1036: ! 1037: case MTIOCGET: ! 1038: cycommand(dev, CY_SENSE, 1); ! 1039: mtget = (struct mtget *)data; ! 1040: mtget->mt_dsreg = yc->yc_status; ! 1041: mtget->mt_erreg = yc->yc_control; ! 1042: mtget->mt_resid = yc->yc_resid; ! 1043: mtget->mt_type = MT_ISCY; ! 1044: break; ! 1045: ! 1046: default: ! 1047: return (ENXIO); ! 1048: } ! 1049: return (0); ! 1050: } ! 1051: ! 1052: /* ! 1053: * Poll until the controller is ready. ! 1054: */ ! 1055: cywait(cp) ! 1056: register struct cyccb *cp; ! 1057: { ! 1058: register int i = 5000; ! 1059: ! 1060: uncache(&cp->cbgate); ! 1061: while (i-- > 0 && cp->cbgate == GATE_CLOSED) { ! 1062: DELAY(1000); ! 1063: uncache(&cp->cbgate); ! 1064: } ! 1065: return (i <= 0); ! 1066: } ! 1067: ! 1068: /* ! 1069: * Load a 20 bit pointer into a Tapemaster pointer. ! 1070: */ ! 1071: cyldmba(reg, value) ! 1072: register u_char *reg; ! 1073: caddr_t value; ! 1074: { ! 1075: register int v = (int)value; ! 1076: ! 1077: *reg++ = v; ! 1078: *reg++ = v >> 8; ! 1079: *reg++ = 0; ! 1080: *reg = (v&0xf0000) >> 12; ! 1081: } ! 1082: ! 1083: /* ! 1084: * Unconditionally reset all controllers to their initial state. ! 1085: */ ! 1086: cyreset(vba) ! 1087: int vba; ! 1088: { ! 1089: register caddr_t addr; ! 1090: register int ctlr; ! 1091: ! 1092: for (ctlr = 0; ctlr < NCY; ctlr++) ! 1093: if (cyminfo[ctlr] && cyminfo[ctlr]->um_vbanum == vba) { ! 1094: addr = cyminfo[ctlr]->um_addr; ! 1095: CY_RESET(addr); ! 1096: if (!cyinit(ctlr, addr)) { ! 1097: printf("cy%d: reset failed\n", ctlr); ! 1098: cyminfo[ctlr] = NULL; ! 1099: } ! 1100: } ! 1101: } ! 1102: ! 1103: cyuncachetpb(cy) ! 1104: struct cy_softc *cy; ! 1105: { ! 1106: register long *lp = (long *)&cy->cy_tpb; ! 1107: register int i; ! 1108: ! 1109: for (i = 0; i < howmany(sizeof (struct cytpb), sizeof (long)); i++) ! 1110: uncache(lp++); ! 1111: } ! 1112: ! 1113: /* ! 1114: * Dump routine. ! 1115: */ ! 1116: #define DUMPREC (32*1024) ! 1117: cydump(dev) ! 1118: dev_t dev; ! 1119: { ! 1120: register struct cy_softc *cy; ! 1121: register int bs, num, start; ! 1122: register caddr_t addr; ! 1123: int unit = CYUNIT(dev), error; ! 1124: ! 1125: if (unit >= NCY || cyminfo[unit] == 0 || ! 1126: (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) ! 1127: return (ENXIO); ! 1128: if (cywait(&cy->cy_ccb)) ! 1129: return (EFAULT); ! 1130: #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) ! 1131: addr = phys(cyminfo[unit]->um_addr); ! 1132: num = maxfree, start = NBPG*2; ! 1133: while (num > 0) { ! 1134: bs = num > btoc(DUMPREC) ? btoc(DUMPREC) : num; ! 1135: error = cydwrite(cy, start, bs, addr); ! 1136: if (error) ! 1137: return (error); ! 1138: start += bs, num -= bs; ! 1139: } ! 1140: cyweof(cy, addr); ! 1141: cyweof(cy, addr); ! 1142: uncache(&cy->cy_tpb); ! 1143: if (cy->cy_tpb.tpstatus&CYS_ERR) ! 1144: return (EIO); ! 1145: cyrewind(cy, addr); ! 1146: return (0); ! 1147: } ! 1148: ! 1149: cydwrite(cy, pf, npf, addr) ! 1150: register struct cy_softc *cy; ! 1151: int pf, npf; ! 1152: caddr_t addr; ! 1153: { ! 1154: ! 1155: cy->cy_tpb.tpcmd = CY_WCOM; ! 1156: cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; ! 1157: cy->cy_tpb.tpstatus = 0; ! 1158: cy->cy_tpb.tpsize = htoms(npf*NBPG); ! 1159: cyldmba(cy->cy_tpb.tplink, (caddr_t)0); ! 1160: cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); ! 1161: cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); ! 1162: cy->cy_ccb.cbgate = GATE_CLOSED; ! 1163: CY_GO(addr); ! 1164: if (cywait(&cy->cy_ccb)) ! 1165: return (EFAULT); ! 1166: uncache(&cy->cy_tpb); ! 1167: if (cy->cy_tpb.tpstatus&CYS_ERR) ! 1168: return (EIO); ! 1169: return (0); ! 1170: } ! 1171: ! 1172: cyweof(cy, addr) ! 1173: register struct cy_softc *cy; ! 1174: caddr_t addr; ! 1175: { ! 1176: ! 1177: cy->cy_tpb.tpcmd = CY_WEOF; ! 1178: cy->cy_tpb.tpcount = htoms(1); ! 1179: cy->cy_ccb.cbgate = GATE_CLOSED; ! 1180: CY_GO(addr); ! 1181: (void) cywait(&cy->cy_ccb); ! 1182: } ! 1183: ! 1184: cyrewind(cy, addr) ! 1185: register struct cy_softc *cy; ! 1186: caddr_t addr; ! 1187: { ! 1188: ! 1189: cy->cy_tpb.tpcmd = CY_REW; ! 1190: cy->cy_tpb.tpcount = htoms(1); ! 1191: cy->cy_ccb.cbgate = GATE_CLOSED; ! 1192: CY_GO(addr); ! 1193: (void) cywait(&cy->cy_ccb); ! 1194: } ! 1195: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.