134487Skarels /* 234487Skarels * Copyright (c) 1988 Regents of the University of California. 334487Skarels * All rights reserved. 434487Skarels * 534487Skarels * This code is derived from software contributed to Berkeley by 634487Skarels * Computer Consoles Inc. 734487Skarels * 834487Skarels * Redistribution and use in source and binary forms are permitted 934487Skarels * provided that this notice is preserved and that due credit is given 1034487Skarels * to the University of California at Berkeley. The name of the University 1134487Skarels * may not be used to endorse or promote products derived from this 1234487Skarels * software without specific prior written permission. This software 1334487Skarels * is provided ``as is'' without express or implied warranty. 1434487Skarels * 15*34507Skarels * @(#)cy.c 1.17 (Berkeley) 05/26/88 1634487Skarels */ 1724000Ssam 1825979Ssam #include "yc.h" 1925675Ssam #if NCY > 0 2024000Ssam /* 2125675Ssam * Cipher Tapemaster driver. 2224000Ssam */ 2330371Skarels #define CYDEBUG 2430371Skarels #ifdef CYDEBUG 2525675Ssam int cydebug = 0; 2630371Skarels #define dlog(params) if (cydebug) log params 2730371Skarels #else 2830371Skarels #define dlog(params) /* */ 2930371Skarels #endif 3024000Ssam 3125675Ssam #include "param.h" 3225675Ssam #include "systm.h" 3325675Ssam #include "vm.h" 3425675Ssam #include "buf.h" 3525675Ssam #include "file.h" 3625675Ssam #include "dir.h" 3725675Ssam #include "user.h" 3825675Ssam #include "proc.h" 3925675Ssam #include "signal.h" 4025675Ssam #include "uio.h" 4125675Ssam #include "ioctl.h" 4225675Ssam #include "mtio.h" 4325675Ssam #include "errno.h" 4425675Ssam #include "cmap.h" 4525979Ssam #include "kernel.h" 4625979Ssam #include "syslog.h" 4730294Ssam #include "tty.h" 4824000Ssam 4929952Skarels #include "../tahoe/cpu.h" 5029952Skarels #include "../tahoe/mtpr.h" 5129952Skarels #include "../tahoe/pte.h" 5229952Skarels 5325675Ssam #include "../tahoevba/vbavar.h" 5425979Ssam #define CYERROR 5525675Ssam #include "../tahoevba/cyreg.h" 5624000Ssam 5725979Ssam /* 5825979Ssam * There is a ccybuf per tape controller. 5925979Ssam * It is used as the token to pass to the internal routines 6025979Ssam * to execute tape ioctls, and also acts as a lock on the slaves 6125979Ssam * on the controller, since there is only one per controller. 6225979Ssam * In particular, when the tape is rewinding on close we release 6325979Ssam * the user process but any further attempts to use the tape drive 6425979Ssam * before the rewind completes will hang waiting for ccybuf. 6525979Ssam */ 6625979Ssam struct buf ccybuf[NCY]; 6724000Ssam 6825979Ssam int cyprobe(), cyslave(), cyattach(); 6925979Ssam struct buf ycutab[NYC]; 7025979Ssam short yctocy[NYC]; 7125675Ssam struct vba_ctlr *cyminfo[NCY]; 7225979Ssam struct vba_device *ycdinfo[NYC]; 7325857Ssam long cystd[] = { 0 }; 7425857Ssam struct vba_driver cydriver = 7525979Ssam { cyprobe, cyslave, cyattach, 0, cystd, "yc", ycdinfo, "cy", cyminfo }; 7624000Ssam 7725979Ssam /* bits in minor device */ 7825979Ssam #define YCUNIT(dev) (minor(dev)&03) 7925979Ssam #define CYUNIT(dev) (yctocy[YCUNIT(dev)]) 8025979Ssam #define T_NOREWIND 0x04 8130371Skarels #define T_1600BPI 0x00 /* pseudo */ 8230371Skarels #define T_3200BPI 0x08 /* unused */ 8325979Ssam 8425979Ssam #define INF 1000000L /* close to infinity */ 8525979Ssam 8624000Ssam /* 8725979Ssam * Software state and shared command areas per controller. 8825979Ssam * 8930719Skarels * The i/o intermediate buffer must be allocated in startup() 9030719Skarels * so its address will fit in 20-bits (YECH!!!!!!!!!!!!!!). 9124000Ssam */ 9225979Ssam struct cy_softc { 9325979Ssam int cy_bs; /* controller's buffer size */ 9425979Ssam struct cyscp *cy_scp; /* system configuration block address */ 9525979Ssam struct cyccb cy_ccb; /* channel control block */ 9625979Ssam struct cyscb cy_scb; /* system configuration block */ 9725979Ssam struct cytpb cy_tpb; /* tape parameter block */ 9825979Ssam struct cytpb cy_nop; /* nop parameter block for cyintr */ 9930719Skarels struct vb_buf cy_rbuf; /* vba resources */ 10025979Ssam } cy_softc[NCY]; 10124000Ssam 10225979Ssam /* 10325979Ssam * Software state per tape transport. 10425979Ssam */ 10525979Ssam struct yc_softc { 10625979Ssam char yc_openf; /* lock against multiple opens */ 10725979Ssam char yc_lastiow; /* last operation was a write */ 10825979Ssam short yc_tact; /* timeout is active */ 10925979Ssam long yc_timo; /* time until timeout expires */ 11025979Ssam u_short yc_control; /* copy of last tpcb.tpcontrol */ 11125979Ssam u_short yc_status; /* copy of last tpcb.tpstatus */ 11225979Ssam u_short yc_resid; /* copy of last bc */ 11325979Ssam u_short yc_dens; /* prototype control word with density info */ 11425979Ssam struct tty *yc_ttyp; /* user's tty for errors */ 11525979Ssam daddr_t yc_blkno; /* block number, for block device tape */ 11625979Ssam daddr_t yc_nxrec; /* position of end of tape, if known */ 11730371Skarels int yc_blksize; /* current tape blocksize estimate */ 11830371Skarels int yc_blks; /* number of I/O operations since open */ 11930371Skarels int yc_softerrs; /* number of soft I/O errors since open */ 12025979Ssam } yc_softc[NYC]; 12124000Ssam 12224000Ssam /* 12325979Ssam * States for vm->um_tab.b_active, the per controller state flag. 12425979Ssam * This is used to sequence control in the driver. 12524000Ssam */ 12625979Ssam #define SSEEK 1 /* seeking */ 12725979Ssam #define SIO 2 /* doing seq i/o */ 12825979Ssam #define SCOM 3 /* sending control command */ 12925979Ssam #define SREW 4 /* sending a rewind */ 13025979Ssam #define SERASE 5 /* erase inter-record gap */ 13125979Ssam #define SERASED 6 /* erased inter-record gap */ 13224000Ssam 13325979Ssam /* there's no way to figure these out dynamically? -- yech */ 13425979Ssam struct cyscp *cyscp[] = 13525979Ssam { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; 13625979Ssam #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) 13725979Ssam 13825857Ssam cyprobe(reg, vm) 13925857Ssam caddr_t reg; 14025857Ssam struct vba_ctlr *vm; 14125675Ssam { 14225857Ssam register br, cvec; /* must be r12, r11 */ 14330371Skarels register struct cy_softc *cy; 14430371Skarels int ctlr = vm->um_ctlr; 14525675Ssam 14630294Ssam #ifdef lint 14730294Ssam br = 0; cvec = br; br = cvec; 14830294Ssam cyintr(0); 14930294Ssam #endif 15025857Ssam if (badcyaddr(reg+1)) 15125675Ssam return (0); 15230371Skarels if (ctlr > NCYSCP || cyscp[ctlr] == 0) /* XXX */ 15330371Skarels return (0); 15430371Skarels cy = &cy_softc[ctlr]; 15530371Skarels cy->cy_scp = cyscp[ctlr]; /* XXX */ 15625979Ssam /* 15725979Ssam * Tapemaster controller must have interrupt handler 15825979Ssam * disable interrupt, so we'll just kludge things 15925979Ssam * (stupid multibus non-vectored interrupt crud). 16025979Ssam */ 16130371Skarels if (cyinit(ctlr, reg)) { 16230371Skarels uncache(&cy->cy_tpb.tpcount); 16330371Skarels cy->cy_bs = htoms(cy->cy_tpb.tpcount); 16430371Skarels /* 16530371Skarels * Setup nop parameter block for clearing interrupts. 16630371Skarels */ 16730371Skarels cy->cy_nop.tpcmd = CY_NOP; 16830371Skarels cy->cy_nop.tpcontrol = 0; 16930371Skarels /* 17030371Skarels * Allocate page tables. 17130371Skarels */ 17230719Skarels if (cybuf == 0) { 17330719Skarels printf("no cy buffer!!!\n"); 17430719Skarels return (0); 17530719Skarels } 17630719Skarels cy->cy_rbuf.vb_rawbuf = cybuf + ctlr * CYMAXIO; 17731737Skarels if (vbainit(&cy->cy_rbuf, CYMAXIO, VB_20BIT) == 0) { 17831737Skarels printf("cy%d: vbainit failed\n", ctlr); 17931737Skarels return (0); 18031737Skarels } 18130371Skarels 18230371Skarels br = 0x13, cvec = 0x80; /* XXX */ 18330371Skarels return (sizeof (struct cyccb)); 18430371Skarels } else 18530371Skarels return (0); 18625675Ssam } 18725675Ssam 18824000Ssam /* 18925857Ssam * Check to see if a drive is attached to a controller. 19025857Ssam * Since we can only tell that a drive is there if a tape is loaded and 19125857Ssam * the drive is placed online, we always indicate the slave is present. 19224000Ssam */ 19325857Ssam cyslave(vi, addr) 19425857Ssam struct vba_device *vi; 19525857Ssam caddr_t addr; 19624000Ssam { 19725857Ssam 19825857Ssam #ifdef lint 19925857Ssam vi = vi; addr = addr; 20025857Ssam #endif 20125857Ssam return (1); 20225857Ssam } 20325857Ssam 20425857Ssam cyattach(vi) 20525857Ssam struct vba_device *vi; 20625857Ssam { 20725979Ssam register struct cy_softc *cy; 20825979Ssam int ctlr = vi->ui_mi->um_ctlr; 20925857Ssam 21025979Ssam yctocy[vi->ui_unit] = ctlr; 21125979Ssam cy = &cy_softc[ctlr]; 21230371Skarels if (vi->ui_slave == 0 && cy->cy_bs) 21330371Skarels printf("; %dkb buffer", cy->cy_bs/1024); 21425857Ssam } 21525857Ssam 21625857Ssam /* 21725857Ssam * Initialize the controller after a controller reset or 21825857Ssam * during autoconfigure. All of the system control blocks 21925857Ssam * are initialized and the controller is asked to configure 22025857Ssam * itself for later use. 22125857Ssam */ 22230371Skarels cyinit(ctlr, addr) 22325979Ssam int ctlr; 22430371Skarels register caddr_t addr; 22525857Ssam { 22625979Ssam register struct cy_softc *cy = &cy_softc[ctlr]; 22725675Ssam register int *pte; 22824000Ssam 22924000Ssam /* 23025675Ssam * Initialize the system configuration pointer. 23124000Ssam */ 23225675Ssam /* make kernel writable */ 23330719Skarels pte = (int *)&Sysmap[btop((int)cy->cy_scp &~ KERNBASE)]; 23425675Ssam *pte &= ~PG_PROT; *pte |= PG_KW; 23525979Ssam mtpr(TBIS, cy->cy_scp); 23625675Ssam /* load the correct values in the scp */ 23725979Ssam cy->cy_scp->csp_buswidth = CSP_16BITS; 23825979Ssam cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); 23925675Ssam /* put it back to read-only */ 24025675Ssam *pte &= ~PG_PROT; *pte |= PG_KR; 24125979Ssam mtpr(TBIS, cy->cy_scp); 24225675Ssam 24324000Ssam /* 24425675Ssam * Init system configuration block. 24524000Ssam */ 24630371Skarels cy->cy_scb.csb_fixed = CSB_FIXED; 24725675Ssam /* set pointer to the channel control block */ 24825979Ssam cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); 24925675Ssam 25024000Ssam /* 25125675Ssam * Initialize the chanel control block. 25224000Ssam */ 25325979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 25425979Ssam cy->cy_ccb.cbgate = GATE_OPEN; 25525675Ssam /* set pointer to the tape parameter block */ 25625979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 25725675Ssam 25824000Ssam /* 25925979Ssam * Issue a nop cmd and get the internal buffer size for buffered i/o. 26024000Ssam */ 26125979Ssam cy->cy_tpb.tpcmd = CY_NOP; 26225979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 26325979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 26425979Ssam CY_GO(addr); 26525979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 26625979Ssam uncache(&cy->cy_tpb.tpstatus); 26725979Ssam printf("cy%d: timeout or err during init, status=%b\n", ctlr, 26825979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 26925675Ssam return (0); 27025675Ssam } 27125979Ssam cy->cy_tpb.tpcmd = CY_CONFIG; 27225979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 27325979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 27425979Ssam CY_GO(addr); 27525979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 27625979Ssam uncache(&cy->cy_tpb.tpstatus); 27725979Ssam printf("cy%d: configuration failure, status=%b\n", ctlr, 27825979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 27925675Ssam return (0); 28025675Ssam } 28125675Ssam return (1); 28224000Ssam } 28324000Ssam 28425979Ssam int cytimer(); 28525979Ssam /* 28625979Ssam * Open the device. Tapes are unique open 28725979Ssam * devices, so we refuse if it is already open. 28825979Ssam * We also check that a tape is available, and 28925979Ssam * don't block waiting here; if you want to wait 29025979Ssam * for a tape you should timeout in user code. 29125979Ssam */ 29225675Ssam cyopen(dev, flag) 29325979Ssam dev_t dev; 29425675Ssam register int flag; 29525675Ssam { 29625979Ssam register int ycunit; 29725979Ssam register struct vba_device *vi; 29825979Ssam register struct yc_softc *yc; 29925675Ssam 30025979Ssam ycunit = YCUNIT(dev); 30125979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 30225675Ssam return (ENXIO); 30325979Ssam if ((yc = &yc_softc[ycunit])->yc_openf) 30425979Ssam return (EBUSY); 30530371Skarels yc->yc_openf = 1; 30625979Ssam #define PACKUNIT(vi) \ 30725979Ssam (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) 30825979Ssam /* no way to select density */ 30925979Ssam yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; 31030371Skarels if (yc->yc_tact == 0) { 31130371Skarels yc->yc_timo = INF; 31230371Skarels yc->yc_tact = 1; 31330371Skarels timeout(cytimer, (caddr_t)dev, 5*hz); 31430371Skarels } 31525979Ssam cycommand(dev, CY_SENSE, 1); 31625979Ssam if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ 31734285Skarels uprintf("cy%d: not online\n", ycunit); 31830439Skarels yc->yc_openf = 0; 31930872Skarels return (EIO); 32025675Ssam } 32125979Ssam if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { 32234285Skarels uprintf("cy%d: no write ring\n", ycunit); 32330439Skarels yc->yc_openf = 0; 32430872Skarels return (EIO); 32525675Ssam } 32625979Ssam yc->yc_blkno = (daddr_t)0; 32725979Ssam yc->yc_nxrec = INF; 32825979Ssam yc->yc_lastiow = 0; 32930869Skarels yc->yc_blksize = CYMAXIO; /* guess > 0 */ 33030371Skarels yc->yc_blks = 0; 33130371Skarels yc->yc_softerrs = 0; 33225979Ssam yc->yc_ttyp = u.u_ttyp; 33325675Ssam return (0); 33425675Ssam } 33525675Ssam 33625979Ssam /* 33725979Ssam * Close tape device. 33825979Ssam * 33925979Ssam * If tape was open for writing or last operation was a write, 34025979Ssam * then write two EOF's and backspace over the last one. 34125979Ssam * Unless this is a non-rewinding special file, rewind the tape. 34225979Ssam * Make the tape available to others. 34325979Ssam */ 34425675Ssam cyclose(dev, flag) 34525979Ssam dev_t dev; 34630371Skarels int flag; 34725675Ssam { 34830371Skarels struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 34925675Ssam 35025979Ssam if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { 35134285Skarels cycommand(dev, CY_WEOF, 1); /* can't use count with WEOF */ 35234285Skarels cycommand(dev, CY_WEOF, 1); 35325979Ssam cycommand(dev, CY_SREV, 1); 35425675Ssam } 35525979Ssam if ((minor(dev)&T_NOREWIND) == 0) 35625979Ssam /* 35725979Ssam * 0 count means don't hang waiting for rewind complete 35825979Ssam * rather ccybuf stays busy until the operation completes 35925979Ssam * preventing further opens from completing by preventing 36025979Ssam * a CY_SENSE from completing. 36125979Ssam */ 36225979Ssam cycommand(dev, CY_REW, 0); 36330371Skarels if (yc->yc_blks > 10 && yc->yc_softerrs > yc->yc_blks / 10) 36430371Skarels log(LOG_INFO, "yc%d: %d soft errors in %d blocks\n", 36530371Skarels YCUNIT(dev), yc->yc_softerrs, yc->yc_blks); 36630371Skarels dlog((LOG_INFO, "%d soft errors in %d blocks\n", 36730371Skarels yc->yc_softerrs, yc->yc_blks)); 36825979Ssam yc->yc_openf = 0; 36930719Skarels return (0); 37025675Ssam } 37125675Ssam 37224000Ssam /* 37325979Ssam * Execute a command on the tape drive a specified number of times. 37424000Ssam */ 37525979Ssam cycommand(dev, com, count) 37625979Ssam dev_t dev; 37725979Ssam int com, count; 37824000Ssam { 37925979Ssam register struct buf *bp; 38025675Ssam int s; 38125675Ssam 38225979Ssam bp = &ccybuf[CYUNIT(dev)]; 38325675Ssam s = spl3(); 38430371Skarels dlog((LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", 38530371Skarels dev, com, count, bp->b_flags)); 38625979Ssam while (bp->b_flags&B_BUSY) { 38725979Ssam /* 38825979Ssam * This special check is because B_BUSY never 38925979Ssam * gets cleared in the non-waiting rewind case. 39025979Ssam */ 39125979Ssam if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 39225979Ssam break; 39325979Ssam bp->b_flags |= B_WANTED; 39425979Ssam sleep((caddr_t)bp, PRIBIO); 39525675Ssam } 39625979Ssam bp->b_flags = B_BUSY|B_READ; 39725675Ssam splx(s); 39825979Ssam bp->b_dev = dev; 39925979Ssam bp->b_repcnt = count; 40025979Ssam bp->b_command = com; 40125979Ssam bp->b_blkno = 0; 40225979Ssam cystrategy(bp); 40325979Ssam /* 40425979Ssam * In case of rewind from close; don't wait. 40525979Ssam * This is the only case where count can be 0. 40625979Ssam */ 40725979Ssam if (count == 0) 40825979Ssam return; 40930371Skarels biowait(bp); 41025979Ssam if (bp->b_flags&B_WANTED) 41125979Ssam wakeup((caddr_t)bp); 41225979Ssam bp->b_flags &= B_ERROR; 41324000Ssam } 41424000Ssam 41525675Ssam cystrategy(bp) 41625675Ssam register struct buf *bp; 41725675Ssam { 41825979Ssam int ycunit = YCUNIT(bp->b_dev); 41925979Ssam register struct vba_ctlr *vm; 42025979Ssam register struct buf *dp; 42125675Ssam int s; 42225675Ssam 42325979Ssam /* 42425979Ssam * Put transfer at end of unit queue. 42525979Ssam */ 42630371Skarels dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); 42725979Ssam dp = &ycutab[ycunit]; 42825675Ssam bp->av_forw = NULL; 42925979Ssam vm = ycdinfo[ycunit]->ui_mi; 43025979Ssam /* BEGIN GROT */ 431*34507Skarels if (bp->b_flags & B_RAW) { 43230869Skarels if (bp->b_bcount >= CYMAXIO) { 43325979Ssam uprintf("cy%d: i/o size too large\n", vm->um_ctlr); 43430869Skarels bp->b_error = EINVAL; 43525979Ssam bp->b_resid = bp->b_bcount; 43625979Ssam bp->b_flags |= B_ERROR; 43730371Skarels biodone(bp); 43825675Ssam return; 43925675Ssam } 44024000Ssam } 44125979Ssam /* END GROT */ 44225675Ssam s = spl3(); 44325979Ssam if (dp->b_actf == NULL) { 44425979Ssam dp->b_actf = bp; 44525979Ssam /* 44625979Ssam * Transport not already active... 44725979Ssam * put at end of controller queue. 44825979Ssam */ 44925979Ssam dp->b_forw = NULL; 45025979Ssam if (vm->um_tab.b_actf == NULL) 45125979Ssam vm->um_tab.b_actf = dp; 45225979Ssam else 45325979Ssam vm->um_tab.b_actl->b_forw = dp; 45425979Ssam } else 45525979Ssam dp->b_actl->av_forw = bp; 45625979Ssam dp->b_actl = bp; 45725979Ssam /* 45825979Ssam * If the controller is not busy, get it going. 45925979Ssam */ 46025979Ssam if (vm->um_tab.b_active == 0) 46125979Ssam cystart(vm); 46224000Ssam splx(s); 46324000Ssam } 46424000Ssam 46524000Ssam /* 46625979Ssam * Start activity on a cy controller. 46724000Ssam */ 46825979Ssam cystart(vm) 46925979Ssam register struct vba_ctlr *vm; 47024000Ssam { 47125979Ssam register struct buf *bp, *dp; 47225979Ssam register struct yc_softc *yc; 47325979Ssam register struct cy_softc *cy; 47425979Ssam int ycunit; 47525979Ssam daddr_t blkno; 47624000Ssam 47730371Skarels dlog((LOG_INFO, "cystart()\n")); 47825979Ssam /* 47925979Ssam * Look for an idle transport on the controller. 48025979Ssam */ 48125979Ssam loop: 48225979Ssam if ((dp = vm->um_tab.b_actf) == NULL) 48325675Ssam return; 48425979Ssam if ((bp = dp->b_actf) == NULL) { 48525979Ssam vm->um_tab.b_actf = dp->b_forw; 48625979Ssam goto loop; 48725675Ssam } 48825979Ssam ycunit = YCUNIT(bp->b_dev); 48925979Ssam yc = &yc_softc[ycunit]; 49025979Ssam cy = &cy_softc[CYUNIT(bp->b_dev)]; 49125979Ssam /* 49225979Ssam * Default is that last command was NOT a write command; 49325979Ssam * if we do a write command we will notice this in cyintr(). 49425979Ssam */ 49525979Ssam yc->yc_lastiow = 0; 49625979Ssam if (yc->yc_openf < 0 || 49725979Ssam (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { 49825979Ssam /* 49925979Ssam * Have had a hard error on a non-raw tape 50025979Ssam * or the tape unit is now unavailable (e.g. 50125979Ssam * taken off line). 50225979Ssam */ 50330371Skarels dlog((LOG_INFO, "openf %d command %x status %b\n", 50430371Skarels yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); 50525979Ssam bp->b_flags |= B_ERROR; 50625979Ssam goto next; 50725675Ssam } 50825979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { 50925979Ssam /* 51025979Ssam * Execute control operation with the specified count. 51125979Ssam * 51225979Ssam * Set next state; give 5 minutes to complete 51325979Ssam * rewind or file mark search, or 10 seconds per 51425979Ssam * iteration (minimum 60 seconds and max 5 minutes) 51525979Ssam * to complete other ops. 51625979Ssam */ 51725979Ssam if (bp->b_command == CY_REW) { 51825979Ssam vm->um_tab.b_active = SREW; 51925979Ssam yc->yc_timo = 5*60; 52030869Skarels } else if (bp->b_command == CY_FSF || 52130869Skarels bp->b_command == CY_BSF) { 52230869Skarels vm->um_tab.b_active = SCOM; 52330869Skarels yc->yc_timo = 5*60; 52425979Ssam } else { 52525979Ssam vm->um_tab.b_active = SCOM; 52625979Ssam yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); 52725979Ssam } 52825979Ssam cy->cy_tpb.tprec = htoms(bp->b_repcnt); 52930719Skarels dlog((LOG_INFO, "bpcmd ")); 53025979Ssam goto dobpcmd; 53124000Ssam } 53225979Ssam /* 533*34507Skarels * For raw I/O, save the current block 534*34507Skarels * number in case we have to retry. 53525979Ssam */ 536*34507Skarels if (bp->b_flags & B_RAW) { 537*34507Skarels if (vm->um_tab.b_errcnt == 0) { 538*34507Skarels yc->yc_blkno = bp->b_blkno; 539*34507Skarels yc->yc_nxrec = yc->yc_blkno + 1; 540*34507Skarels } 541*34507Skarels } else { 54225979Ssam /* 543*34507Skarels * Handle boundary cases for operation 544*34507Skarels * on non-raw tapes. 54525979Ssam */ 546*34507Skarels if (bp->b_blkno > yc->yc_nxrec) { 547*34507Skarels /* 548*34507Skarels * Can't read past known end-of-file. 549*34507Skarels */ 550*34507Skarels bp->b_flags |= B_ERROR; 551*34507Skarels bp->b_error = ENXIO; 552*34507Skarels goto next; 553*34507Skarels } 554*34507Skarels if (bp->b_blkno == yc->yc_nxrec && bp->b_flags&B_READ) { 555*34507Skarels /* 556*34507Skarels * Reading at end of file returns 0 bytes. 557*34507Skarels */ 558*34507Skarels bp->b_resid = bp->b_bcount; 559*34507Skarels clrbuf(bp); 560*34507Skarels goto next; 561*34507Skarels } 562*34507Skarels if ((bp->b_flags&B_READ) == 0) 563*34507Skarels /* 564*34507Skarels * Writing sets EOF. 565*34507Skarels */ 566*34507Skarels yc->yc_nxrec = bp->b_blkno + 1; 56724000Ssam } 56830719Skarels if ((blkno = yc->yc_blkno) == bp->b_blkno) { 56925979Ssam caddr_t addr; 57025979Ssam int cmd; 57125675Ssam 57225979Ssam /* 57325979Ssam * Choose the appropriate i/o command based on the 57430371Skarels * transfer size, the estimated block size, 57530371Skarels * and the controller's internal buffer size. 57630869Skarels * If the request length is longer than the tape 57730869Skarels * block length, a buffered read will fail, 57830869Skarels * thus, we request at most the size that we expect. 57930869Skarels * We then check for larger records when the read completes. 58025979Ssam * If we're retrying a read on a raw device because 58125979Ssam * the original try was a buffer request which failed 58225979Ssam * due to a record length error, then we force the use 58325979Ssam * of the raw controller read (YECH!!!!). 58425979Ssam */ 58525979Ssam if (bp->b_flags&B_READ) { 58630869Skarels if (yc->yc_blksize <= cy->cy_bs && 58730869Skarels vm->um_tab.b_errcnt == 0) 58830869Skarels cmd = CY_BRCOM; 58930869Skarels else 59025979Ssam cmd = CY_RCOM; 59125979Ssam } else { 59225979Ssam /* 59325979Ssam * On write error retries erase the 59425979Ssam * inter-record gap before rewriting. 59525979Ssam */ 59625979Ssam if (vm->um_tab.b_errcnt && 59725979Ssam vm->um_tab.b_active != SERASED) { 59825979Ssam vm->um_tab.b_active = SERASE; 59925979Ssam bp->b_command = CY_ERASE; 60025979Ssam yc->yc_timo = 60; 60125979Ssam goto dobpcmd; 60225675Ssam } 60325979Ssam cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; 60425675Ssam } 60525979Ssam vm->um_tab.b_active = SIO; 60630719Skarels addr = (caddr_t)vbasetup(bp, &cy->cy_rbuf, 1); 60725979Ssam cy->cy_tpb.tpcmd = cmd; 60825979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 60925979Ssam if (cmd == CY_RCOM || cmd == CY_WCOM) 61025979Ssam cy->cy_tpb.tpcontrol |= CYCW_LOCK; 61125979Ssam cy->cy_tpb.tpstatus = 0; 61225979Ssam cy->cy_tpb.tpcount = 0; 61325979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); 61425979Ssam cy->cy_tpb.tprec = 0; 61530869Skarels if (cmd == CY_BRCOM) 61634487Skarels cy->cy_tpb.tpsize = htoms(imin(yc->yc_blksize, 61734487Skarels (int)bp->b_bcount)); 61830371Skarels else 61930371Skarels cy->cy_tpb.tpsize = htoms(bp->b_bcount); 62025979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 62125979Ssam do 62225979Ssam uncache(&cy->cy_ccb.cbgate); 62325979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 62425979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 62525979Ssam cy->cy_ccb.cbcw = CBCW_IE; 62625979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 62730371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", 62825979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 62930371Skarels htoms(cy->cy_tpb.tpsize))); 63025979Ssam CY_GO(vm->um_addr); 63125979Ssam return; 63224000Ssam } 63325979Ssam /* 63425979Ssam * Tape positioned incorrectly; set to seek forwards 63525979Ssam * or backwards to the correct spot. This happens 63625979Ssam * for raw tapes only on error retries. 63725979Ssam */ 63825979Ssam vm->um_tab.b_active = SSEEK; 63930719Skarels if (blkno < bp->b_blkno) { 64025979Ssam bp->b_command = CY_SFORW; 64130719Skarels cy->cy_tpb.tprec = htoms(bp->b_blkno - blkno); 64225979Ssam } else { 64325979Ssam bp->b_command = CY_SREV; 64430719Skarels cy->cy_tpb.tprec = htoms(blkno - bp->b_blkno); 64524000Ssam } 64634487Skarels yc->yc_timo = imin(imax((int)(10 * htoms(cy->cy_tpb.tprec)), 60), 5*60); 64725979Ssam dobpcmd: 64825979Ssam /* 64925979Ssam * Do the command in bp. Reverse direction commands 65025979Ssam * are indicated by having CYCW_REV or'd into their 65125979Ssam * value. For these we must set the appropriate bit 65225979Ssam * in the control field. 65325979Ssam */ 65425979Ssam if (bp->b_command&CYCW_REV) { 65525979Ssam cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; 65625979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; 65730719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 65825979Ssam } else { 65925979Ssam cy->cy_tpb.tpcmd = bp->b_command; 66025979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 66130719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 66224000Ssam } 66325979Ssam cy->cy_tpb.tpstatus = 0; 66425979Ssam cy->cy_tpb.tpcount = 0; 66525979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 66625979Ssam do 66725979Ssam uncache(&cy->cy_ccb.cbgate); 66825979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 66925979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 67025979Ssam cy->cy_ccb.cbcw = CBCW_IE; 67125979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 67230371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", 67325979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 67430371Skarels htoms(cy->cy_tpb.tprec))); 67525979Ssam CY_GO(vm->um_addr); 67625979Ssam return; 67725979Ssam next: 67825979Ssam /* 67925979Ssam * Done with this operation due to error or the 68030719Skarels * fact that it doesn't do anything. 68130719Skarels * Dequeue the transfer and continue 68225979Ssam * processing this slave. 68325979Ssam */ 68425979Ssam vm->um_tab.b_errcnt = 0; 68525979Ssam dp->b_actf = bp->av_forw; 68630371Skarels biodone(bp); 68725979Ssam goto loop; 68825675Ssam } 68925675Ssam 69025675Ssam /* 69125979Ssam * Cy interrupt routine. 69225675Ssam */ 69330719Skarels cyintr(cyunit) 69430719Skarels int cyunit; 69525675Ssam { 69625979Ssam struct buf *dp; 69724000Ssam register struct buf *bp; 69830719Skarels register struct vba_ctlr *vm = cyminfo[cyunit]; 69925979Ssam register struct cy_softc *cy; 70025979Ssam register struct yc_softc *yc; 70130719Skarels int err; 70225979Ssam register state; 70324000Ssam 70430719Skarels dlog((LOG_INFO, "cyintr(%d)\n", cyunit)); 70525979Ssam /* 70625979Ssam * First, turn off the interrupt from the controller 70725979Ssam * (device uses Multibus non-vectored interrupts...yech). 70825979Ssam */ 70925979Ssam cy = &cy_softc[vm->um_ctlr]; 71025979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 71130294Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); 71225979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 71325979Ssam CY_GO(vm->um_addr); 71425979Ssam if ((dp = vm->um_tab.b_actf) == NULL) { 71530371Skarels dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); 71624000Ssam return; 71724000Ssam } 71825979Ssam bp = dp->b_actf; 71925979Ssam cy = &cy_softc[cyunit]; 72025979Ssam cyuncachetpb(cy); 72130294Ssam yc = &yc_softc[YCUNIT(bp->b_dev)]; 72225979Ssam /* 72325984Ssam * If last command was a rewind and tape is 72425984Ssam * still moving, wait for the operation to complete. 72525979Ssam */ 72625979Ssam if (vm->um_tab.b_active == SREW) { 72725979Ssam vm->um_tab.b_active = SCOM; 72825979Ssam if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { 72925979Ssam yc->yc_timo = 5*60; /* 5 minutes */ 73025979Ssam return; 73124000Ssam } 73224000Ssam } 73325979Ssam /* 73425979Ssam * An operation completed...record status. 73525979Ssam */ 73625979Ssam yc->yc_timo = INF; 73725979Ssam yc->yc_control = cy->cy_tpb.tpcontrol; 73825979Ssam yc->yc_status = cy->cy_tpb.tpstatus; 73925979Ssam yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 74030371Skarels dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", 74125979Ssam cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, 74230371Skarels yc->yc_status, CYS_BITS, yc->yc_resid)); 74325979Ssam if ((bp->b_flags&B_READ) == 0) 74425979Ssam yc->yc_lastiow = 1; 74525979Ssam state = vm->um_tab.b_active; 74625979Ssam vm->um_tab.b_active = 0; 74725979Ssam /* 74825979Ssam * Check for errors. 74925979Ssam */ 75025979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) { 75125979Ssam err = cy->cy_tpb.tpstatus&CYS_ERR; 75230371Skarels dlog((LOG_INFO, "error %d\n", err)); 75325979Ssam /* 75425979Ssam * If we hit the end of tape file, update our position. 75525979Ssam */ 75625979Ssam if (err == CYER_FM) { 75725979Ssam yc->yc_status |= CYS_FM; 75825979Ssam state = SCOM; /* force completion */ 75925979Ssam cyseteof(bp); /* set blkno and nxrec */ 76025979Ssam goto opdone; 76125979Ssam } 76225979Ssam /* 76325979Ssam * Fix up errors which occur due to backspacing over 76425979Ssam * the beginning of the tape. 76525979Ssam */ 76625979Ssam if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { 76725979Ssam yc->yc_status |= CYS_BOT; 76825979Ssam goto ignoreerr; 76925979Ssam } 77025979Ssam /* 77125979Ssam * If we were reading raw tape and the only error was that the 77225979Ssam * record was too long, then we don't consider this an error. 77325979Ssam */ 774*34507Skarels if ((bp->b_flags & (B_READ|B_RAW)) == (B_READ|B_RAW) && 77525979Ssam err == CYER_STROBE) { 77625979Ssam /* 77730371Skarels * Retry reads with the command changed to 77830371Skarels * a raw read if necessary. Setting b_errcnt 77925979Ssam * here causes cystart (above) to force a CY_RCOM. 78025979Ssam */ 78130869Skarels if (cy->cy_tpb.tpcmd == CY_BRCOM && 78230719Skarels vm->um_tab.b_errcnt++ == 0) { 78330371Skarels yc->yc_blkno++; 78430371Skarels goto opcont; 78530371Skarels } else 78625979Ssam goto ignoreerr; 78725979Ssam } 78825979Ssam /* 78925979Ssam * If error is not hard, and this was an i/o operation 79025979Ssam * retry up to 8 times. 79125979Ssam */ 79234285Skarels if (state == SIO && (CYMASK(err) & 79334285Skarels ((bp->b_flags&B_READ) ? CYER_RSOFT : CYER_WSOFT))) { 79425979Ssam if (++vm->um_tab.b_errcnt < 7) { 79525979Ssam yc->yc_blkno++; 79625979Ssam goto opcont; 79725979Ssam } 79825979Ssam } else 79925979Ssam /* 80025979Ssam * Hard or non-i/o errors on non-raw tape 80125979Ssam * cause it to close. 80225979Ssam */ 803*34507Skarels if ((bp->b_flags&B_RAW) == 0 && 804*34507Skarels yc->yc_openf > 0) 80525979Ssam yc->yc_openf = -1; 80625979Ssam /* 80725979Ssam * Couldn't recover from error. 80825979Ssam */ 80925979Ssam tprintf(yc->yc_ttyp, 81030371Skarels "yc%d: hard error bn%d status=%b, %s\n", YCUNIT(bp->b_dev), 81130371Skarels bp->b_blkno, yc->yc_status, CYS_BITS, 81230371Skarels (err < NCYERROR) ? cyerror[err] : ""); 81325979Ssam bp->b_flags |= B_ERROR; 81425979Ssam goto opdone; 81530869Skarels } else if (cy->cy_tpb.tpcmd == CY_BRCOM) { 81630869Skarels int reclen = htoms(cy->cy_tpb.tprec); 81730869Skarels 81830869Skarels /* 81930869Skarels * If we did a buffered read, check whether the read 82030869Skarels * was long enough. If we asked the controller for less 82130869Skarels * than the user asked for because the previous record 82230869Skarels * was shorter, update our notion of record size 82330869Skarels * and retry. If the record is longer than the buffer, 82430869Skarels * bump the errcnt so the retry will use direct read. 82530869Skarels */ 82630869Skarels if (reclen > yc->yc_blksize && bp->b_bcount > yc->yc_blksize) { 82730869Skarels yc->yc_blksize = reclen; 82830869Skarels if (reclen > cy->cy_bs) 82930869Skarels vm->um_tab.b_errcnt++; 83030869Skarels yc->yc_blkno++; 83130869Skarels goto opcont; 83230869Skarels } 83324000Ssam } 83425979Ssam /* 83525979Ssam * Advance tape control FSM. 83625979Ssam */ 83725979Ssam ignoreerr: 83825979Ssam /* 83925979Ssam * If we hit a tape mark update our position. 84025979Ssam */ 84125979Ssam if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { 84225979Ssam cyseteof(bp); 84325979Ssam goto opdone; 84425675Ssam } 84525979Ssam switch (state) { 84624000Ssam 84725979Ssam case SIO: 84825979Ssam /* 84925979Ssam * Read/write increments tape block number. 85025979Ssam */ 85125979Ssam yc->yc_blkno++; 85230371Skarels yc->yc_blks++; 85330371Skarels if (vm->um_tab.b_errcnt || yc->yc_status & CYS_CR) 85430371Skarels yc->yc_softerrs++; 85530371Skarels yc->yc_blksize = htoms(cy->cy_tpb.tpcount); 85630371Skarels dlog((LOG_ERR, "blocksize %d", yc->yc_blksize)); 85725979Ssam goto opdone; 85824000Ssam 85925979Ssam case SCOM: 86025979Ssam /* 86125979Ssam * For forward/backward space record update current position. 86225979Ssam */ 86330294Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) 86430294Ssam switch ((int)bp->b_command) { 86524000Ssam 86630294Ssam case CY_SFORW: 86730294Ssam yc->yc_blkno -= bp->b_repcnt; 86830294Ssam break; 86924000Ssam 87030294Ssam case CY_SREV: 87130294Ssam yc->yc_blkno += bp->b_repcnt; 87230294Ssam break; 87330294Ssam } 87425979Ssam goto opdone; 87525979Ssam 87625979Ssam case SSEEK: 87730719Skarels yc->yc_blkno = bp->b_blkno; 87825979Ssam goto opcont; 87924000Ssam 88025979Ssam case SERASE: 88125979Ssam /* 88225979Ssam * Completed erase of the inter-record gap due to a 88325979Ssam * write error; now retry the write operation. 88425979Ssam */ 88525979Ssam vm->um_tab.b_active = SERASED; 88625979Ssam goto opcont; 88724000Ssam } 88825675Ssam 88925979Ssam opdone: 89025979Ssam /* 89125979Ssam * Reset error count and remove from device queue. 89225979Ssam */ 89325979Ssam vm->um_tab.b_errcnt = 0; 89425979Ssam dp->b_actf = bp->av_forw; 89525979Ssam /* 89625979Ssam * Save resid and release resources. 89725979Ssam */ 89825979Ssam bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 89930719Skarels if (bp != &ccybuf[cyunit]) 90030719Skarels vbadone(bp, &cy->cy_rbuf); 90130371Skarels biodone(bp); 90225979Ssam /* 90325979Ssam * Circulate slave to end of controller 90425979Ssam * queue to give other slaves a chance. 90525979Ssam */ 90625979Ssam vm->um_tab.b_actf = dp->b_forw; 90725979Ssam if (dp->b_actf) { 90825979Ssam dp->b_forw = NULL; 90925979Ssam if (vm->um_tab.b_actf == NULL) 91025979Ssam vm->um_tab.b_actf = dp; 91125979Ssam else 91225979Ssam vm->um_tab.b_actl->b_forw = dp; 91324000Ssam } 91425979Ssam if (vm->um_tab.b_actf == 0) 91524000Ssam return; 91625979Ssam opcont: 91725979Ssam cystart(vm); 91824000Ssam } 91924000Ssam 92025979Ssam cytimer(dev) 92125979Ssam int dev; 92224000Ssam { 92325979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 92425979Ssam int s; 92524000Ssam 92630371Skarels if (yc->yc_openf == 0 && yc->yc_timo == INF) { 92730371Skarels yc->yc_tact = 0; 92830371Skarels return; 92930371Skarels } 93025979Ssam if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { 93125979Ssam printf("yc%d: lost interrupt\n", YCUNIT(dev)); 93225979Ssam yc->yc_timo = INF; 93325979Ssam s = spl3(); 93425979Ssam cyintr(CYUNIT(dev)); 93525979Ssam splx(s); 93624000Ssam } 93725979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 93824000Ssam } 93924000Ssam 94025979Ssam cyseteof(bp) 94125979Ssam register struct buf *bp; 94224000Ssam { 94325979Ssam register int cyunit = CYUNIT(bp->b_dev); 94425979Ssam register struct cy_softc *cy = &cy_softc[cyunit]; 94525979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; 94624000Ssam 94725979Ssam if (bp == &ccybuf[cyunit]) { 94830719Skarels if (yc->yc_blkno > bp->b_blkno) { 94925979Ssam /* reversing */ 95030719Skarels yc->yc_nxrec = bp->b_blkno - htoms(cy->cy_tpb.tpcount); 95125979Ssam yc->yc_blkno = yc->yc_nxrec; 95225979Ssam } else { 95330719Skarels yc->yc_blkno = bp->b_blkno + htoms(cy->cy_tpb.tpcount); 95425979Ssam yc->yc_nxrec = yc->yc_blkno - 1; 95524000Ssam } 95625675Ssam return; 95725675Ssam } 95825979Ssam /* eof on read */ 95930719Skarels yc->yc_nxrec = bp->b_blkno; 96024000Ssam } 96124000Ssam 96225675Ssam /*ARGSUSED*/ 96325675Ssam cyioctl(dev, cmd, data, flag) 96425979Ssam caddr_t data; 96525675Ssam dev_t dev; 96625675Ssam { 96725979Ssam int ycunit = YCUNIT(dev); 96825979Ssam register struct yc_softc *yc = &yc_softc[ycunit]; 96925979Ssam register struct buf *bp = &ccybuf[CYUNIT(dev)]; 97025979Ssam register callcount; 97125979Ssam int fcount, op; 97225979Ssam struct mtop *mtop; 97325979Ssam struct mtget *mtget; 97425979Ssam /* we depend of the values and order of the MT codes here */ 97525979Ssam static cyops[] = 97630371Skarels {CY_WEOF,CY_FSF,CY_BSF,CY_SFORW,CY_SREV,CY_REW,CY_OFFL,CY_SENSE}; 97725675Ssam 97825675Ssam switch (cmd) { 97925675Ssam 98025979Ssam case MTIOCTOP: /* tape operation */ 98125979Ssam mtop = (struct mtop *)data; 98225979Ssam switch (op = mtop->mt_op) { 98325675Ssam 98425979Ssam case MTWEOF: 98530371Skarels callcount = mtop->mt_count; 98630371Skarels fcount = 1; 98730371Skarels break; 98830371Skarels 98925979Ssam case MTFSR: case MTBSR: 99030371Skarels callcount = 1; 99130371Skarels fcount = mtop->mt_count; 99230371Skarels break; 99330371Skarels 99425979Ssam case MTFSF: case MTBSF: 99525979Ssam callcount = mtop->mt_count; 99625979Ssam fcount = 1; 99725979Ssam break; 99825675Ssam 99925979Ssam case MTREW: case MTOFFL: case MTNOP: 100025979Ssam callcount = 1; 100125979Ssam fcount = 1; 100225979Ssam break; 100325675Ssam 100425979Ssam default: 100525979Ssam return (ENXIO); 100625979Ssam } 100725979Ssam if (callcount <= 0 || fcount <= 0) 100825979Ssam return (EINVAL); 100925979Ssam while (--callcount >= 0) { 101030371Skarels #ifdef notdef 101125979Ssam /* 101225979Ssam * Gagh, this controller is the pits... 101325979Ssam */ 101425979Ssam if (op == MTFSF || op == MTBSF) { 101525979Ssam do 101625979Ssam cycommand(dev, cyops[op], 1); 101725979Ssam while ((bp->b_flags&B_ERROR) == 0 && 101825979Ssam (yc->yc_status&(CYS_EOT|CYS_BOT|CYS_FM)) == 0); 101925979Ssam } else 102030371Skarels #endif 102125979Ssam cycommand(dev, cyops[op], fcount); 102230371Skarels dlog((LOG_INFO, 102330371Skarels "cyioctl: status %x, b_flags %x, resid %d\n", 102430371Skarels yc->yc_status, bp->b_flags, bp->b_resid)); 102525979Ssam if ((bp->b_flags&B_ERROR) || 102625979Ssam (yc->yc_status&(CYS_BOT|CYS_EOT))) 102725979Ssam break; 102825979Ssam } 102925979Ssam bp->b_resid = callcount + 1; 103025979Ssam return (geterror(bp)); 103125979Ssam 103225979Ssam case MTIOCGET: 103325979Ssam cycommand(dev, CY_SENSE, 1); 103425979Ssam mtget = (struct mtget *)data; 103525979Ssam mtget->mt_dsreg = yc->yc_status; 103625979Ssam mtget->mt_erreg = yc->yc_control; 103725979Ssam mtget->mt_resid = yc->yc_resid; 103825979Ssam mtget->mt_type = MT_ISCY; 103925675Ssam break; 104025675Ssam 104125675Ssam default: 104225675Ssam return (ENXIO); 104325675Ssam } 104425675Ssam return (0); 104525675Ssam } 104625675Ssam 104725675Ssam /* 104825675Ssam * Poll until the controller is ready. 104925675Ssam */ 105025675Ssam cywait(cp) 105125979Ssam register struct cyccb *cp; 105224000Ssam { 105325675Ssam register int i = 5000; 105424000Ssam 105525979Ssam uncache(&cp->cbgate); 105625979Ssam while (i-- > 0 && cp->cbgate == GATE_CLOSED) { 105724000Ssam DELAY(1000); 105825979Ssam uncache(&cp->cbgate); 105924000Ssam } 106025675Ssam return (i <= 0); 106124000Ssam } 106224000Ssam 106325675Ssam /* 106430371Skarels * Load a 20 bit pointer into a Tapemaster pointer. 106525675Ssam */ 106630371Skarels cyldmba(reg, value) 106734487Skarels register u_char *reg; 106825979Ssam caddr_t value; 106924000Ssam { 107025979Ssam register int v = (int)value; 107125675Ssam 107225979Ssam *reg++ = v; 107325979Ssam *reg++ = v >> 8; 107425979Ssam *reg++ = 0; 107525979Ssam *reg = (v&0xf0000) >> 12; 107624000Ssam } 107724000Ssam 107825675Ssam /* 107925675Ssam * Unconditionally reset all controllers to their initial state. 108025675Ssam */ 108125675Ssam cyreset(vba) 108225675Ssam int vba; 108324000Ssam { 108425675Ssam register caddr_t addr; 108525675Ssam register int ctlr; 108624000Ssam 108725675Ssam for (ctlr = 0; ctlr < NCY; ctlr++) 108825675Ssam if (cyminfo[ctlr] && cyminfo[ctlr]->um_vbanum == vba) { 108925675Ssam addr = cyminfo[ctlr]->um_addr; 109025675Ssam CY_RESET(addr); 109130371Skarels if (!cyinit(ctlr, addr)) { 109225675Ssam printf("cy%d: reset failed\n", ctlr); 109325675Ssam cyminfo[ctlr] = NULL; 109425675Ssam } 109525675Ssam } 109624000Ssam } 109725979Ssam 109825979Ssam cyuncachetpb(cy) 109925979Ssam struct cy_softc *cy; 110025979Ssam { 110125979Ssam register long *lp = (long *)&cy->cy_tpb; 110225979Ssam register int i; 110325979Ssam 110425979Ssam for (i = 0; i < howmany(sizeof (struct cytpb), sizeof (long)); i++) 110525979Ssam uncache(lp++); 110625979Ssam } 110725979Ssam 110825979Ssam /* 110925979Ssam * Dump routine. 111025979Ssam */ 111130869Skarels #define DUMPREC (32*1024) 111225979Ssam cydump(dev) 111325979Ssam dev_t dev; 111425979Ssam { 111525979Ssam register struct cy_softc *cy; 111625979Ssam register int bs, num, start; 111725979Ssam register caddr_t addr; 111830294Ssam int unit = CYUNIT(dev), error; 111925979Ssam 112025979Ssam if (unit >= NCY || cyminfo[unit] == 0 || 112125979Ssam (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) 112225979Ssam return (ENXIO); 112325979Ssam if (cywait(&cy->cy_ccb)) 112425979Ssam return (EFAULT); 112525979Ssam #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) 112630294Ssam addr = phys(cyminfo[unit]->um_addr); 112725979Ssam num = maxfree, start = NBPG*2; 112825979Ssam while (num > 0) { 112930869Skarels bs = num > btoc(DUMPREC) ? btoc(DUMPREC) : num; 113025979Ssam error = cydwrite(cy, start, bs, addr); 113125979Ssam if (error) 113225979Ssam return (error); 113325979Ssam start += bs, num -= bs; 113425979Ssam } 113525979Ssam cyweof(cy, addr); 113625979Ssam cyweof(cy, addr); 113725979Ssam uncache(&cy->cy_tpb); 113825979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 113925979Ssam return (EIO); 114025979Ssam cyrewind(cy, addr); 114125979Ssam return (0); 114225979Ssam } 114325979Ssam 114425979Ssam cydwrite(cy, pf, npf, addr) 114525979Ssam register struct cy_softc *cy; 114625979Ssam int pf, npf; 114725979Ssam caddr_t addr; 114825979Ssam { 114925979Ssam 115025979Ssam cy->cy_tpb.tpcmd = CY_WCOM; 115125979Ssam cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; 115225979Ssam cy->cy_tpb.tpstatus = 0; 115325979Ssam cy->cy_tpb.tpsize = htoms(npf*NBPG); 115425979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 115525979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); 115625979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 115725979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 115825979Ssam CY_GO(addr); 115925979Ssam if (cywait(&cy->cy_ccb)) 116025979Ssam return (EFAULT); 116125979Ssam uncache(&cy->cy_tpb); 116225979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 116325979Ssam return (EIO); 116425979Ssam return (0); 116525979Ssam } 116625979Ssam 116725979Ssam cyweof(cy, addr) 116825979Ssam register struct cy_softc *cy; 116925979Ssam caddr_t addr; 117025979Ssam { 117125979Ssam 117225979Ssam cy->cy_tpb.tpcmd = CY_WEOF; 117325979Ssam cy->cy_tpb.tpcount = htoms(1); 117425979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 117525979Ssam CY_GO(addr); 117625979Ssam (void) cywait(&cy->cy_ccb); 117725979Ssam } 117825979Ssam 117925979Ssam cyrewind(cy, addr) 118025979Ssam register struct cy_softc *cy; 118125979Ssam caddr_t addr; 118225979Ssam { 118325979Ssam 118425979Ssam cy->cy_tpb.tpcmd = CY_REW; 118525979Ssam cy->cy_tpb.tpcount = htoms(1); 118625979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 118725979Ssam CY_GO(addr); 118825979Ssam (void) cywait(&cy->cy_ccb); 118925979Ssam } 119024000Ssam #endif 1191