1*34487Skarels /* 2*34487Skarels * Copyright (c) 1988 Regents of the University of California. 3*34487Skarels * All rights reserved. 4*34487Skarels * 5*34487Skarels * This code is derived from software contributed to Berkeley by 6*34487Skarels * Computer Consoles Inc. 7*34487Skarels * 8*34487Skarels * Redistribution and use in source and binary forms are permitted 9*34487Skarels * provided that this notice is preserved and that due credit is given 10*34487Skarels * to the University of California at Berkeley. The name of the University 11*34487Skarels * may not be used to endorse or promote products derived from this 12*34487Skarels * software without specific prior written permission. This software 13*34487Skarels * is provided ``as is'' without express or implied warranty. 14*34487Skarels * 15*34487Skarels * @(#)cy.c 1.16 (Berkeley) 05/25/88 16*34487Skarels */ 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 /* 6925979Ssam * Raw tape operations use rcybuf. The driver notices when 7025979Ssam * rcybuf is being used and allows the user program to contine 7125979Ssam * after errors and read records not of the standard length. 7225979Ssam */ 7325979Ssam struct buf rcybuf[NCY]; 7424000Ssam 7525979Ssam int cyprobe(), cyslave(), cyattach(); 7625979Ssam struct buf ycutab[NYC]; 7725979Ssam short yctocy[NYC]; 7825675Ssam struct vba_ctlr *cyminfo[NCY]; 7925979Ssam struct vba_device *ycdinfo[NYC]; 8025857Ssam long cystd[] = { 0 }; 8125857Ssam struct vba_driver cydriver = 8225979Ssam { cyprobe, cyslave, cyattach, 0, cystd, "yc", ycdinfo, "cy", cyminfo }; 8324000Ssam 8425979Ssam /* bits in minor device */ 8525979Ssam #define YCUNIT(dev) (minor(dev)&03) 8625979Ssam #define CYUNIT(dev) (yctocy[YCUNIT(dev)]) 8725979Ssam #define T_NOREWIND 0x04 8830371Skarels #define T_1600BPI 0x00 /* pseudo */ 8930371Skarels #define T_3200BPI 0x08 /* unused */ 9025979Ssam 9125979Ssam #define INF 1000000L /* close to infinity */ 9225979Ssam 9324000Ssam /* 9425979Ssam * Software state and shared command areas per controller. 9525979Ssam * 9630719Skarels * The i/o intermediate buffer must be allocated in startup() 9730719Skarels * so its address will fit in 20-bits (YECH!!!!!!!!!!!!!!). 9824000Ssam */ 9925979Ssam struct cy_softc { 10025979Ssam int cy_bs; /* controller's buffer size */ 10125979Ssam struct cyscp *cy_scp; /* system configuration block address */ 10225979Ssam struct cyccb cy_ccb; /* channel control block */ 10325979Ssam struct cyscb cy_scb; /* system configuration block */ 10425979Ssam struct cytpb cy_tpb; /* tape parameter block */ 10525979Ssam struct cytpb cy_nop; /* nop parameter block for cyintr */ 10630719Skarels struct vb_buf cy_rbuf; /* vba resources */ 10725979Ssam } cy_softc[NCY]; 10824000Ssam 10925979Ssam /* 11025979Ssam * Software state per tape transport. 11125979Ssam */ 11225979Ssam struct yc_softc { 11325979Ssam char yc_openf; /* lock against multiple opens */ 11425979Ssam char yc_lastiow; /* last operation was a write */ 11525979Ssam short yc_tact; /* timeout is active */ 11625979Ssam long yc_timo; /* time until timeout expires */ 11725979Ssam u_short yc_control; /* copy of last tpcb.tpcontrol */ 11825979Ssam u_short yc_status; /* copy of last tpcb.tpstatus */ 11925979Ssam u_short yc_resid; /* copy of last bc */ 12025979Ssam u_short yc_dens; /* prototype control word with density info */ 12125979Ssam struct tty *yc_ttyp; /* user's tty for errors */ 12225979Ssam daddr_t yc_blkno; /* block number, for block device tape */ 12325979Ssam daddr_t yc_nxrec; /* position of end of tape, if known */ 12430371Skarels int yc_blksize; /* current tape blocksize estimate */ 12530371Skarels int yc_blks; /* number of I/O operations since open */ 12630371Skarels int yc_softerrs; /* number of soft I/O errors since open */ 12725979Ssam } yc_softc[NYC]; 12824000Ssam 12924000Ssam /* 13025979Ssam * States for vm->um_tab.b_active, the per controller state flag. 13125979Ssam * This is used to sequence control in the driver. 13224000Ssam */ 13325979Ssam #define SSEEK 1 /* seeking */ 13425979Ssam #define SIO 2 /* doing seq i/o */ 13525979Ssam #define SCOM 3 /* sending control command */ 13625979Ssam #define SREW 4 /* sending a rewind */ 13725979Ssam #define SERASE 5 /* erase inter-record gap */ 13825979Ssam #define SERASED 6 /* erased inter-record gap */ 13924000Ssam 14025979Ssam /* there's no way to figure these out dynamically? -- yech */ 14125979Ssam struct cyscp *cyscp[] = 14225979Ssam { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; 14325979Ssam #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) 14425979Ssam 14525857Ssam cyprobe(reg, vm) 14625857Ssam caddr_t reg; 14725857Ssam struct vba_ctlr *vm; 14825675Ssam { 14925857Ssam register br, cvec; /* must be r12, r11 */ 15030371Skarels register struct cy_softc *cy; 15130371Skarels int ctlr = vm->um_ctlr; 15225675Ssam 15330294Ssam #ifdef lint 15430294Ssam br = 0; cvec = br; br = cvec; 15530294Ssam cyintr(0); 15630294Ssam #endif 15725857Ssam if (badcyaddr(reg+1)) 15825675Ssam return (0); 15930371Skarels if (ctlr > NCYSCP || cyscp[ctlr] == 0) /* XXX */ 16030371Skarels return (0); 16130371Skarels cy = &cy_softc[ctlr]; 16230371Skarels cy->cy_scp = cyscp[ctlr]; /* XXX */ 16325979Ssam /* 16425979Ssam * Tapemaster controller must have interrupt handler 16525979Ssam * disable interrupt, so we'll just kludge things 16625979Ssam * (stupid multibus non-vectored interrupt crud). 16725979Ssam */ 16830371Skarels if (cyinit(ctlr, reg)) { 16930371Skarels uncache(&cy->cy_tpb.tpcount); 17030371Skarels cy->cy_bs = htoms(cy->cy_tpb.tpcount); 17130371Skarels /* 17230371Skarels * Setup nop parameter block for clearing interrupts. 17330371Skarels */ 17430371Skarels cy->cy_nop.tpcmd = CY_NOP; 17530371Skarels cy->cy_nop.tpcontrol = 0; 17630371Skarels /* 17730371Skarels * Allocate page tables. 17830371Skarels */ 17930719Skarels if (cybuf == 0) { 18030719Skarels printf("no cy buffer!!!\n"); 18130719Skarels return (0); 18230719Skarels } 18330719Skarels cy->cy_rbuf.vb_rawbuf = cybuf + ctlr * CYMAXIO; 18431737Skarels if (vbainit(&cy->cy_rbuf, CYMAXIO, VB_20BIT) == 0) { 18531737Skarels printf("cy%d: vbainit failed\n", ctlr); 18631737Skarels return (0); 18731737Skarels } 18830371Skarels 18930371Skarels br = 0x13, cvec = 0x80; /* XXX */ 19030371Skarels return (sizeof (struct cyccb)); 19130371Skarels } else 19230371Skarels return (0); 19325675Ssam } 19425675Ssam 19524000Ssam /* 19625857Ssam * Check to see if a drive is attached to a controller. 19725857Ssam * Since we can only tell that a drive is there if a tape is loaded and 19825857Ssam * the drive is placed online, we always indicate the slave is present. 19924000Ssam */ 20025857Ssam cyslave(vi, addr) 20125857Ssam struct vba_device *vi; 20225857Ssam caddr_t addr; 20324000Ssam { 20425857Ssam 20525857Ssam #ifdef lint 20625857Ssam vi = vi; addr = addr; 20725857Ssam #endif 20825857Ssam return (1); 20925857Ssam } 21025857Ssam 21125857Ssam cyattach(vi) 21225857Ssam struct vba_device *vi; 21325857Ssam { 21425979Ssam register struct cy_softc *cy; 21525979Ssam int ctlr = vi->ui_mi->um_ctlr; 21625857Ssam 21725979Ssam yctocy[vi->ui_unit] = ctlr; 21825979Ssam cy = &cy_softc[ctlr]; 21930371Skarels if (vi->ui_slave == 0 && cy->cy_bs) 22030371Skarels printf("; %dkb buffer", cy->cy_bs/1024); 22125857Ssam } 22225857Ssam 22325857Ssam /* 22425857Ssam * Initialize the controller after a controller reset or 22525857Ssam * during autoconfigure. All of the system control blocks 22625857Ssam * are initialized and the controller is asked to configure 22725857Ssam * itself for later use. 22825857Ssam */ 22930371Skarels cyinit(ctlr, addr) 23025979Ssam int ctlr; 23130371Skarels register caddr_t addr; 23225857Ssam { 23325979Ssam register struct cy_softc *cy = &cy_softc[ctlr]; 23425675Ssam register int *pte; 23524000Ssam 23624000Ssam /* 23725675Ssam * Initialize the system configuration pointer. 23824000Ssam */ 23925675Ssam /* make kernel writable */ 24030719Skarels pte = (int *)&Sysmap[btop((int)cy->cy_scp &~ KERNBASE)]; 24125675Ssam *pte &= ~PG_PROT; *pte |= PG_KW; 24225979Ssam mtpr(TBIS, cy->cy_scp); 24325675Ssam /* load the correct values in the scp */ 24425979Ssam cy->cy_scp->csp_buswidth = CSP_16BITS; 24525979Ssam cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); 24625675Ssam /* put it back to read-only */ 24725675Ssam *pte &= ~PG_PROT; *pte |= PG_KR; 24825979Ssam mtpr(TBIS, cy->cy_scp); 24925675Ssam 25024000Ssam /* 25125675Ssam * Init system configuration block. 25224000Ssam */ 25330371Skarels cy->cy_scb.csb_fixed = CSB_FIXED; 25425675Ssam /* set pointer to the channel control block */ 25525979Ssam cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); 25625675Ssam 25724000Ssam /* 25825675Ssam * Initialize the chanel control block. 25924000Ssam */ 26025979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 26125979Ssam cy->cy_ccb.cbgate = GATE_OPEN; 26225675Ssam /* set pointer to the tape parameter block */ 26325979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 26425675Ssam 26524000Ssam /* 26625979Ssam * Issue a nop cmd and get the internal buffer size for buffered i/o. 26724000Ssam */ 26825979Ssam cy->cy_tpb.tpcmd = CY_NOP; 26925979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 27025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 27125979Ssam CY_GO(addr); 27225979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 27325979Ssam uncache(&cy->cy_tpb.tpstatus); 27425979Ssam printf("cy%d: timeout or err during init, status=%b\n", ctlr, 27525979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 27625675Ssam return (0); 27725675Ssam } 27825979Ssam cy->cy_tpb.tpcmd = CY_CONFIG; 27925979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 28025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 28125979Ssam CY_GO(addr); 28225979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 28325979Ssam uncache(&cy->cy_tpb.tpstatus); 28425979Ssam printf("cy%d: configuration failure, status=%b\n", ctlr, 28525979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 28625675Ssam return (0); 28725675Ssam } 28825675Ssam return (1); 28924000Ssam } 29024000Ssam 29125979Ssam int cytimer(); 29225979Ssam /* 29325979Ssam * Open the device. Tapes are unique open 29425979Ssam * devices, so we refuse if it is already open. 29525979Ssam * We also check that a tape is available, and 29625979Ssam * don't block waiting here; if you want to wait 29725979Ssam * for a tape you should timeout in user code. 29825979Ssam */ 29925675Ssam cyopen(dev, flag) 30025979Ssam dev_t dev; 30125675Ssam register int flag; 30225675Ssam { 30325979Ssam register int ycunit; 30425979Ssam register struct vba_device *vi; 30525979Ssam register struct yc_softc *yc; 30625675Ssam 30725979Ssam ycunit = YCUNIT(dev); 30825979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 30925675Ssam return (ENXIO); 31025979Ssam if ((yc = &yc_softc[ycunit])->yc_openf) 31125979Ssam return (EBUSY); 31230371Skarels yc->yc_openf = 1; 31325979Ssam #define PACKUNIT(vi) \ 31425979Ssam (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) 31525979Ssam /* no way to select density */ 31625979Ssam yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; 31730371Skarels if (yc->yc_tact == 0) { 31830371Skarels yc->yc_timo = INF; 31930371Skarels yc->yc_tact = 1; 32030371Skarels timeout(cytimer, (caddr_t)dev, 5*hz); 32130371Skarels } 32225979Ssam cycommand(dev, CY_SENSE, 1); 32325979Ssam if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ 32434285Skarels uprintf("cy%d: not online\n", ycunit); 32530439Skarels yc->yc_openf = 0; 32630872Skarels return (EIO); 32725675Ssam } 32825979Ssam if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { 32934285Skarels uprintf("cy%d: no write ring\n", ycunit); 33030439Skarels yc->yc_openf = 0; 33130872Skarels return (EIO); 33225675Ssam } 33325979Ssam yc->yc_blkno = (daddr_t)0; 33425979Ssam yc->yc_nxrec = INF; 33525979Ssam yc->yc_lastiow = 0; 33630869Skarels yc->yc_blksize = CYMAXIO; /* guess > 0 */ 33730371Skarels yc->yc_blks = 0; 33830371Skarels yc->yc_softerrs = 0; 33925979Ssam yc->yc_ttyp = u.u_ttyp; 34025675Ssam return (0); 34125675Ssam } 34225675Ssam 34325979Ssam /* 34425979Ssam * Close tape device. 34525979Ssam * 34625979Ssam * If tape was open for writing or last operation was a write, 34725979Ssam * then write two EOF's and backspace over the last one. 34825979Ssam * Unless this is a non-rewinding special file, rewind the tape. 34925979Ssam * Make the tape available to others. 35025979Ssam */ 35125675Ssam cyclose(dev, flag) 35225979Ssam dev_t dev; 35330371Skarels int flag; 35425675Ssam { 35530371Skarels struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 35625675Ssam 35725979Ssam if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { 35834285Skarels cycommand(dev, CY_WEOF, 1); /* can't use count with WEOF */ 35934285Skarels cycommand(dev, CY_WEOF, 1); 36025979Ssam cycommand(dev, CY_SREV, 1); 36125675Ssam } 36225979Ssam if ((minor(dev)&T_NOREWIND) == 0) 36325979Ssam /* 36425979Ssam * 0 count means don't hang waiting for rewind complete 36525979Ssam * rather ccybuf stays busy until the operation completes 36625979Ssam * preventing further opens from completing by preventing 36725979Ssam * a CY_SENSE from completing. 36825979Ssam */ 36925979Ssam cycommand(dev, CY_REW, 0); 37030371Skarels if (yc->yc_blks > 10 && yc->yc_softerrs > yc->yc_blks / 10) 37130371Skarels log(LOG_INFO, "yc%d: %d soft errors in %d blocks\n", 37230371Skarels YCUNIT(dev), yc->yc_softerrs, yc->yc_blks); 37330371Skarels dlog((LOG_INFO, "%d soft errors in %d blocks\n", 37430371Skarels yc->yc_softerrs, yc->yc_blks)); 37525979Ssam yc->yc_openf = 0; 37630719Skarels return (0); 37725675Ssam } 37825675Ssam 37924000Ssam /* 38025979Ssam * Execute a command on the tape drive a specified number of times. 38124000Ssam */ 38225979Ssam cycommand(dev, com, count) 38325979Ssam dev_t dev; 38425979Ssam int com, count; 38524000Ssam { 38625979Ssam register struct buf *bp; 38725675Ssam int s; 38825675Ssam 38925979Ssam bp = &ccybuf[CYUNIT(dev)]; 39025675Ssam s = spl3(); 39130371Skarels dlog((LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", 39230371Skarels dev, com, count, bp->b_flags)); 39325979Ssam while (bp->b_flags&B_BUSY) { 39425979Ssam /* 39525979Ssam * This special check is because B_BUSY never 39625979Ssam * gets cleared in the non-waiting rewind case. 39725979Ssam */ 39825979Ssam if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 39925979Ssam break; 40025979Ssam bp->b_flags |= B_WANTED; 40125979Ssam sleep((caddr_t)bp, PRIBIO); 40225675Ssam } 40325979Ssam bp->b_flags = B_BUSY|B_READ; 40425675Ssam splx(s); 40525979Ssam bp->b_dev = dev; 40625979Ssam bp->b_repcnt = count; 40725979Ssam bp->b_command = com; 40825979Ssam bp->b_blkno = 0; 40925979Ssam cystrategy(bp); 41025979Ssam /* 41125979Ssam * In case of rewind from close; don't wait. 41225979Ssam * This is the only case where count can be 0. 41325979Ssam */ 41425979Ssam if (count == 0) 41525979Ssam return; 41630371Skarels biowait(bp); 41725979Ssam if (bp->b_flags&B_WANTED) 41825979Ssam wakeup((caddr_t)bp); 41925979Ssam bp->b_flags &= B_ERROR; 42024000Ssam } 42124000Ssam 42225675Ssam cystrategy(bp) 42325675Ssam register struct buf *bp; 42425675Ssam { 42525979Ssam int ycunit = YCUNIT(bp->b_dev); 42625979Ssam register struct vba_ctlr *vm; 42725979Ssam register struct buf *dp; 42825675Ssam int s; 42925675Ssam 43025979Ssam /* 43125979Ssam * Put transfer at end of unit queue. 43225979Ssam */ 43330371Skarels dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); 43425979Ssam dp = &ycutab[ycunit]; 43525675Ssam bp->av_forw = NULL; 43625979Ssam vm = ycdinfo[ycunit]->ui_mi; 43725979Ssam /* BEGIN GROT */ 43825979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) { 43930869Skarels if (bp->b_bcount >= CYMAXIO) { 44025979Ssam uprintf("cy%d: i/o size too large\n", vm->um_ctlr); 44130869Skarels bp->b_error = EINVAL; 44225979Ssam bp->b_resid = bp->b_bcount; 44325979Ssam bp->b_flags |= B_ERROR; 44430371Skarels biodone(bp); 44525675Ssam return; 44625675Ssam } 44724000Ssam } 44825979Ssam /* END GROT */ 44925675Ssam s = spl3(); 45025979Ssam if (dp->b_actf == NULL) { 45125979Ssam dp->b_actf = bp; 45225979Ssam /* 45325979Ssam * Transport not already active... 45425979Ssam * put at end of controller queue. 45525979Ssam */ 45625979Ssam dp->b_forw = NULL; 45725979Ssam if (vm->um_tab.b_actf == NULL) 45825979Ssam vm->um_tab.b_actf = dp; 45925979Ssam else 46025979Ssam vm->um_tab.b_actl->b_forw = dp; 46125979Ssam } else 46225979Ssam dp->b_actl->av_forw = bp; 46325979Ssam dp->b_actl = bp; 46425979Ssam /* 46525979Ssam * If the controller is not busy, get it going. 46625979Ssam */ 46725979Ssam if (vm->um_tab.b_active == 0) 46825979Ssam cystart(vm); 46924000Ssam splx(s); 47024000Ssam } 47124000Ssam 47224000Ssam /* 47325979Ssam * Start activity on a cy controller. 47424000Ssam */ 47525979Ssam cystart(vm) 47625979Ssam register struct vba_ctlr *vm; 47724000Ssam { 47825979Ssam register struct buf *bp, *dp; 47925979Ssam register struct yc_softc *yc; 48025979Ssam register struct cy_softc *cy; 48125979Ssam int ycunit; 48225979Ssam daddr_t blkno; 48324000Ssam 48430371Skarels dlog((LOG_INFO, "cystart()\n")); 48525979Ssam /* 48625979Ssam * Look for an idle transport on the controller. 48725979Ssam */ 48825979Ssam loop: 48925979Ssam if ((dp = vm->um_tab.b_actf) == NULL) 49025675Ssam return; 49125979Ssam if ((bp = dp->b_actf) == NULL) { 49225979Ssam vm->um_tab.b_actf = dp->b_forw; 49325979Ssam goto loop; 49425675Ssam } 49525979Ssam ycunit = YCUNIT(bp->b_dev); 49625979Ssam yc = &yc_softc[ycunit]; 49725979Ssam cy = &cy_softc[CYUNIT(bp->b_dev)]; 49825979Ssam /* 49925979Ssam * Default is that last command was NOT a write command; 50025979Ssam * if we do a write command we will notice this in cyintr(). 50125979Ssam */ 50225979Ssam yc->yc_lastiow = 0; 50325979Ssam if (yc->yc_openf < 0 || 50425979Ssam (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { 50525979Ssam /* 50625979Ssam * Have had a hard error on a non-raw tape 50725979Ssam * or the tape unit is now unavailable (e.g. 50825979Ssam * taken off line). 50925979Ssam */ 51030371Skarels dlog((LOG_INFO, "openf %d command %x status %b\n", 51130371Skarels yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); 51225979Ssam bp->b_flags |= B_ERROR; 51325979Ssam goto next; 51425675Ssam } 51525979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { 51625979Ssam /* 51725979Ssam * Execute control operation with the specified count. 51825979Ssam * 51925979Ssam * Set next state; give 5 minutes to complete 52025979Ssam * rewind or file mark search, or 10 seconds per 52125979Ssam * iteration (minimum 60 seconds and max 5 minutes) 52225979Ssam * to complete other ops. 52325979Ssam */ 52425979Ssam if (bp->b_command == CY_REW) { 52525979Ssam vm->um_tab.b_active = SREW; 52625979Ssam yc->yc_timo = 5*60; 52730869Skarels } else if (bp->b_command == CY_FSF || 52830869Skarels bp->b_command == CY_BSF) { 52930869Skarels vm->um_tab.b_active = SCOM; 53030869Skarels yc->yc_timo = 5*60; 53125979Ssam } else { 53225979Ssam vm->um_tab.b_active = SCOM; 53325979Ssam yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); 53425979Ssam } 53525979Ssam cy->cy_tpb.tprec = htoms(bp->b_repcnt); 53630719Skarels dlog((LOG_INFO, "bpcmd ")); 53725979Ssam goto dobpcmd; 53824000Ssam } 53925979Ssam /* 54025979Ssam * The following checks handle boundary cases for operation 54125979Ssam * on no-raw tapes. On raw tapes the initialization of 54225979Ssam * yc->yc_nxrec by cyphys causes them to be skipped normally 54325979Ssam * (except in the case of retries). 54425979Ssam */ 54530719Skarels if (bp->b_blkno > yc->yc_nxrec) { 54625979Ssam /* 54725979Ssam * Can't read past known end-of-file. 54825979Ssam */ 54925979Ssam bp->b_flags |= B_ERROR; 55025979Ssam bp->b_error = ENXIO; 55125979Ssam goto next; 55224000Ssam } 55330719Skarels if (bp->b_blkno == yc->yc_nxrec && bp->b_flags&B_READ) { 55425979Ssam /* 55525979Ssam * Reading at end of file returns 0 bytes. 55625979Ssam */ 55725979Ssam bp->b_resid = bp->b_bcount; 55825979Ssam clrbuf(bp); 55925979Ssam goto next; 56024000Ssam } 56125979Ssam if ((bp->b_flags&B_READ) == 0) 56225979Ssam /* 56325979Ssam * Writing sets EOF. 56425979Ssam */ 56530719Skarels yc->yc_nxrec = bp->b_blkno + 1; 56630719Skarels if ((blkno = yc->yc_blkno) == bp->b_blkno) { 56725979Ssam caddr_t addr; 56825979Ssam int cmd; 56925675Ssam 57025979Ssam /* 57125979Ssam * Choose the appropriate i/o command based on the 57230371Skarels * transfer size, the estimated block size, 57330371Skarels * and the controller's internal buffer size. 57430869Skarels * If the request length is longer than the tape 57530869Skarels * block length, a buffered read will fail, 57630869Skarels * thus, we request at most the size that we expect. 57730869Skarels * We then check for larger records when the read completes. 57825979Ssam * If we're retrying a read on a raw device because 57925979Ssam * the original try was a buffer request which failed 58025979Ssam * due to a record length error, then we force the use 58125979Ssam * of the raw controller read (YECH!!!!). 58225979Ssam */ 58325979Ssam if (bp->b_flags&B_READ) { 58430869Skarels if (yc->yc_blksize <= cy->cy_bs && 58530869Skarels vm->um_tab.b_errcnt == 0) 58630869Skarels cmd = CY_BRCOM; 58730869Skarels else 58825979Ssam cmd = CY_RCOM; 58925979Ssam } else { 59025979Ssam /* 59125979Ssam * On write error retries erase the 59225979Ssam * inter-record gap before rewriting. 59325979Ssam */ 59425979Ssam if (vm->um_tab.b_errcnt && 59525979Ssam vm->um_tab.b_active != SERASED) { 59625979Ssam vm->um_tab.b_active = SERASE; 59725979Ssam bp->b_command = CY_ERASE; 59825979Ssam yc->yc_timo = 60; 59925979Ssam goto dobpcmd; 60025675Ssam } 60125979Ssam cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; 60225675Ssam } 60325979Ssam vm->um_tab.b_active = SIO; 60430719Skarels addr = (caddr_t)vbasetup(bp, &cy->cy_rbuf, 1); 60525979Ssam cy->cy_tpb.tpcmd = cmd; 60625979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 60725979Ssam if (cmd == CY_RCOM || cmd == CY_WCOM) 60825979Ssam cy->cy_tpb.tpcontrol |= CYCW_LOCK; 60925979Ssam cy->cy_tpb.tpstatus = 0; 61025979Ssam cy->cy_tpb.tpcount = 0; 61125979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); 61225979Ssam cy->cy_tpb.tprec = 0; 61330869Skarels if (cmd == CY_BRCOM) 614*34487Skarels cy->cy_tpb.tpsize = htoms(imin(yc->yc_blksize, 615*34487Skarels (int)bp->b_bcount)); 61630371Skarels else 61730371Skarels cy->cy_tpb.tpsize = htoms(bp->b_bcount); 61825979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 61925979Ssam do 62025979Ssam uncache(&cy->cy_ccb.cbgate); 62125979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 62225979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 62325979Ssam cy->cy_ccb.cbcw = CBCW_IE; 62425979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 62530371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", 62625979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 62730371Skarels htoms(cy->cy_tpb.tpsize))); 62825979Ssam CY_GO(vm->um_addr); 62925979Ssam return; 63024000Ssam } 63125979Ssam /* 63225979Ssam * Tape positioned incorrectly; set to seek forwards 63325979Ssam * or backwards to the correct spot. This happens 63425979Ssam * for raw tapes only on error retries. 63525979Ssam */ 63625979Ssam vm->um_tab.b_active = SSEEK; 63730719Skarels if (blkno < bp->b_blkno) { 63825979Ssam bp->b_command = CY_SFORW; 63930719Skarels cy->cy_tpb.tprec = htoms(bp->b_blkno - blkno); 64025979Ssam } else { 64125979Ssam bp->b_command = CY_SREV; 64230719Skarels cy->cy_tpb.tprec = htoms(blkno - bp->b_blkno); 64324000Ssam } 644*34487Skarels yc->yc_timo = imin(imax((int)(10 * htoms(cy->cy_tpb.tprec)), 60), 5*60); 64525979Ssam dobpcmd: 64625979Ssam /* 64725979Ssam * Do the command in bp. Reverse direction commands 64825979Ssam * are indicated by having CYCW_REV or'd into their 64925979Ssam * value. For these we must set the appropriate bit 65025979Ssam * in the control field. 65125979Ssam */ 65225979Ssam if (bp->b_command&CYCW_REV) { 65325979Ssam cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; 65425979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; 65530719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 65625979Ssam } else { 65725979Ssam cy->cy_tpb.tpcmd = bp->b_command; 65825979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 65930719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 66024000Ssam } 66125979Ssam cy->cy_tpb.tpstatus = 0; 66225979Ssam cy->cy_tpb.tpcount = 0; 66325979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 66425979Ssam do 66525979Ssam uncache(&cy->cy_ccb.cbgate); 66625979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 66725979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 66825979Ssam cy->cy_ccb.cbcw = CBCW_IE; 66925979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 67030371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", 67125979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 67230371Skarels htoms(cy->cy_tpb.tprec))); 67325979Ssam CY_GO(vm->um_addr); 67425979Ssam return; 67525979Ssam next: 67625979Ssam /* 67725979Ssam * Done with this operation due to error or the 67830719Skarels * fact that it doesn't do anything. 67930719Skarels * Dequeue the transfer and continue 68025979Ssam * processing this slave. 68125979Ssam */ 68225979Ssam vm->um_tab.b_errcnt = 0; 68325979Ssam dp->b_actf = bp->av_forw; 68430371Skarels biodone(bp); 68525979Ssam goto loop; 68625675Ssam } 68725675Ssam 68825675Ssam /* 68925979Ssam * Cy interrupt routine. 69025675Ssam */ 69130719Skarels cyintr(cyunit) 69230719Skarels int cyunit; 69325675Ssam { 69425979Ssam struct buf *dp; 69524000Ssam register struct buf *bp; 69630719Skarels register struct vba_ctlr *vm = cyminfo[cyunit]; 69725979Ssam register struct cy_softc *cy; 69825979Ssam register struct yc_softc *yc; 69930719Skarels int err; 70025979Ssam register state; 70124000Ssam 70230719Skarels dlog((LOG_INFO, "cyintr(%d)\n", cyunit)); 70325979Ssam /* 70425979Ssam * First, turn off the interrupt from the controller 70525979Ssam * (device uses Multibus non-vectored interrupts...yech). 70625979Ssam */ 70725979Ssam cy = &cy_softc[vm->um_ctlr]; 70825979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 70930294Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); 71025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 71125979Ssam CY_GO(vm->um_addr); 71225979Ssam if ((dp = vm->um_tab.b_actf) == NULL) { 71330371Skarels dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); 71424000Ssam return; 71524000Ssam } 71625979Ssam bp = dp->b_actf; 71725979Ssam cy = &cy_softc[cyunit]; 71825979Ssam cyuncachetpb(cy); 71930294Ssam yc = &yc_softc[YCUNIT(bp->b_dev)]; 72025979Ssam /* 72125984Ssam * If last command was a rewind and tape is 72225984Ssam * still moving, wait for the operation to complete. 72325979Ssam */ 72425979Ssam if (vm->um_tab.b_active == SREW) { 72525979Ssam vm->um_tab.b_active = SCOM; 72625979Ssam if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { 72725979Ssam yc->yc_timo = 5*60; /* 5 minutes */ 72825979Ssam return; 72924000Ssam } 73024000Ssam } 73125979Ssam /* 73225979Ssam * An operation completed...record status. 73325979Ssam */ 73425979Ssam yc->yc_timo = INF; 73525979Ssam yc->yc_control = cy->cy_tpb.tpcontrol; 73625979Ssam yc->yc_status = cy->cy_tpb.tpstatus; 73725979Ssam yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 73830371Skarels dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", 73925979Ssam cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, 74030371Skarels yc->yc_status, CYS_BITS, yc->yc_resid)); 74125979Ssam if ((bp->b_flags&B_READ) == 0) 74225979Ssam yc->yc_lastiow = 1; 74325979Ssam state = vm->um_tab.b_active; 74425979Ssam vm->um_tab.b_active = 0; 74525979Ssam /* 74625979Ssam * Check for errors. 74725979Ssam */ 74825979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) { 74925979Ssam err = cy->cy_tpb.tpstatus&CYS_ERR; 75030371Skarels dlog((LOG_INFO, "error %d\n", err)); 75125979Ssam /* 75225979Ssam * If we hit the end of tape file, update our position. 75325979Ssam */ 75425979Ssam if (err == CYER_FM) { 75525979Ssam yc->yc_status |= CYS_FM; 75625979Ssam state = SCOM; /* force completion */ 75725979Ssam cyseteof(bp); /* set blkno and nxrec */ 75825979Ssam goto opdone; 75925979Ssam } 76025979Ssam /* 76125979Ssam * Fix up errors which occur due to backspacing over 76225979Ssam * the beginning of the tape. 76325979Ssam */ 76425979Ssam if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { 76525979Ssam yc->yc_status |= CYS_BOT; 76625979Ssam goto ignoreerr; 76725979Ssam } 76825979Ssam /* 76925979Ssam * If we were reading raw tape and the only error was that the 77025979Ssam * record was too long, then we don't consider this an error. 77125979Ssam */ 77225979Ssam if (bp == &rcybuf[cyunit] && (bp->b_flags&B_READ) && 77325979Ssam err == CYER_STROBE) { 77425979Ssam /* 77530371Skarels * Retry reads with the command changed to 77630371Skarels * a raw read if necessary. Setting b_errcnt 77725979Ssam * here causes cystart (above) to force a CY_RCOM. 77825979Ssam */ 77930869Skarels if (cy->cy_tpb.tpcmd == CY_BRCOM && 78030719Skarels vm->um_tab.b_errcnt++ == 0) { 78130371Skarels yc->yc_blkno++; 78230371Skarels goto opcont; 78330371Skarels } else 78425979Ssam goto ignoreerr; 78525979Ssam } 78625979Ssam /* 78725979Ssam * If error is not hard, and this was an i/o operation 78825979Ssam * retry up to 8 times. 78925979Ssam */ 79034285Skarels if (state == SIO && (CYMASK(err) & 79134285Skarels ((bp->b_flags&B_READ) ? CYER_RSOFT : CYER_WSOFT))) { 79225979Ssam if (++vm->um_tab.b_errcnt < 7) { 79325979Ssam yc->yc_blkno++; 79425979Ssam goto opcont; 79525979Ssam } 79625979Ssam } else 79725979Ssam /* 79825979Ssam * Hard or non-i/o errors on non-raw tape 79925979Ssam * cause it to close. 80025979Ssam */ 80130371Skarels if (yc->yc_openf > 0 && bp != &rcybuf[cyunit]) 80225979Ssam yc->yc_openf = -1; 80325979Ssam /* 80425979Ssam * Couldn't recover from error. 80525979Ssam */ 80625979Ssam tprintf(yc->yc_ttyp, 80730371Skarels "yc%d: hard error bn%d status=%b, %s\n", YCUNIT(bp->b_dev), 80830371Skarels bp->b_blkno, yc->yc_status, CYS_BITS, 80930371Skarels (err < NCYERROR) ? cyerror[err] : ""); 81025979Ssam bp->b_flags |= B_ERROR; 81125979Ssam goto opdone; 81230869Skarels } else if (cy->cy_tpb.tpcmd == CY_BRCOM) { 81330869Skarels int reclen = htoms(cy->cy_tpb.tprec); 81430869Skarels 81530869Skarels /* 81630869Skarels * If we did a buffered read, check whether the read 81730869Skarels * was long enough. If we asked the controller for less 81830869Skarels * than the user asked for because the previous record 81930869Skarels * was shorter, update our notion of record size 82030869Skarels * and retry. If the record is longer than the buffer, 82130869Skarels * bump the errcnt so the retry will use direct read. 82230869Skarels */ 82330869Skarels if (reclen > yc->yc_blksize && bp->b_bcount > yc->yc_blksize) { 82430869Skarels yc->yc_blksize = reclen; 82530869Skarels if (reclen > cy->cy_bs) 82630869Skarels vm->um_tab.b_errcnt++; 82730869Skarels yc->yc_blkno++; 82830869Skarels goto opcont; 82930869Skarels } 83024000Ssam } 83125979Ssam /* 83225979Ssam * Advance tape control FSM. 83325979Ssam */ 83425979Ssam ignoreerr: 83525979Ssam /* 83625979Ssam * If we hit a tape mark update our position. 83725979Ssam */ 83825979Ssam if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { 83925979Ssam cyseteof(bp); 84025979Ssam goto opdone; 84125675Ssam } 84225979Ssam switch (state) { 84324000Ssam 84425979Ssam case SIO: 84525979Ssam /* 84625979Ssam * Read/write increments tape block number. 84725979Ssam */ 84825979Ssam yc->yc_blkno++; 84930371Skarels yc->yc_blks++; 85030371Skarels if (vm->um_tab.b_errcnt || yc->yc_status & CYS_CR) 85130371Skarels yc->yc_softerrs++; 85230371Skarels yc->yc_blksize = htoms(cy->cy_tpb.tpcount); 85330371Skarels dlog((LOG_ERR, "blocksize %d", yc->yc_blksize)); 85425979Ssam goto opdone; 85524000Ssam 85625979Ssam case SCOM: 85725979Ssam /* 85825979Ssam * For forward/backward space record update current position. 85925979Ssam */ 86030294Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) 86130294Ssam switch ((int)bp->b_command) { 86224000Ssam 86330294Ssam case CY_SFORW: 86430294Ssam yc->yc_blkno -= bp->b_repcnt; 86530294Ssam break; 86624000Ssam 86730294Ssam case CY_SREV: 86830294Ssam yc->yc_blkno += bp->b_repcnt; 86930294Ssam break; 87030294Ssam } 87125979Ssam goto opdone; 87225979Ssam 87325979Ssam case SSEEK: 87430719Skarels yc->yc_blkno = bp->b_blkno; 87525979Ssam goto opcont; 87624000Ssam 87725979Ssam case SERASE: 87825979Ssam /* 87925979Ssam * Completed erase of the inter-record gap due to a 88025979Ssam * write error; now retry the write operation. 88125979Ssam */ 88225979Ssam vm->um_tab.b_active = SERASED; 88325979Ssam goto opcont; 88424000Ssam } 88525675Ssam 88625979Ssam opdone: 88725979Ssam /* 88825979Ssam * Reset error count and remove from device queue. 88925979Ssam */ 89025979Ssam vm->um_tab.b_errcnt = 0; 89125979Ssam dp->b_actf = bp->av_forw; 89225979Ssam /* 89325979Ssam * Save resid and release resources. 89425979Ssam */ 89525979Ssam bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 89630719Skarels if (bp != &ccybuf[cyunit]) 89730719Skarels vbadone(bp, &cy->cy_rbuf); 89830371Skarels biodone(bp); 89925979Ssam /* 90025979Ssam * Circulate slave to end of controller 90125979Ssam * queue to give other slaves a chance. 90225979Ssam */ 90325979Ssam vm->um_tab.b_actf = dp->b_forw; 90425979Ssam if (dp->b_actf) { 90525979Ssam dp->b_forw = NULL; 90625979Ssam if (vm->um_tab.b_actf == NULL) 90725979Ssam vm->um_tab.b_actf = dp; 90825979Ssam else 90925979Ssam vm->um_tab.b_actl->b_forw = dp; 91024000Ssam } 91125979Ssam if (vm->um_tab.b_actf == 0) 91224000Ssam return; 91325979Ssam opcont: 91425979Ssam cystart(vm); 91524000Ssam } 91624000Ssam 91725979Ssam cytimer(dev) 91825979Ssam int dev; 91924000Ssam { 92025979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 92125979Ssam int s; 92224000Ssam 92330371Skarels if (yc->yc_openf == 0 && yc->yc_timo == INF) { 92430371Skarels yc->yc_tact = 0; 92530371Skarels return; 92630371Skarels } 92725979Ssam if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { 92825979Ssam printf("yc%d: lost interrupt\n", YCUNIT(dev)); 92925979Ssam yc->yc_timo = INF; 93025979Ssam s = spl3(); 93125979Ssam cyintr(CYUNIT(dev)); 93225979Ssam splx(s); 93324000Ssam } 93425979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 93524000Ssam } 93624000Ssam 93725979Ssam cyseteof(bp) 93825979Ssam register struct buf *bp; 93924000Ssam { 94025979Ssam register int cyunit = CYUNIT(bp->b_dev); 94125979Ssam register struct cy_softc *cy = &cy_softc[cyunit]; 94225979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; 94324000Ssam 94425979Ssam if (bp == &ccybuf[cyunit]) { 94530719Skarels if (yc->yc_blkno > bp->b_blkno) { 94625979Ssam /* reversing */ 94730719Skarels yc->yc_nxrec = bp->b_blkno - htoms(cy->cy_tpb.tpcount); 94825979Ssam yc->yc_blkno = yc->yc_nxrec; 94925979Ssam } else { 95030719Skarels yc->yc_blkno = bp->b_blkno + htoms(cy->cy_tpb.tpcount); 95125979Ssam yc->yc_nxrec = yc->yc_blkno - 1; 95224000Ssam } 95325675Ssam return; 95425675Ssam } 95525979Ssam /* eof on read */ 95630719Skarels yc->yc_nxrec = bp->b_blkno; 95724000Ssam } 95824000Ssam 95925979Ssam cyread(dev, uio) 96025979Ssam dev_t dev; 96125979Ssam struct uio *uio; 96225675Ssam { 96325979Ssam int errno; 96425675Ssam 96525979Ssam errno = cyphys(dev, uio); 96625979Ssam if (errno) 96725979Ssam return (errno); 96825979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_READ, minphys, uio)); 96925675Ssam } 97025675Ssam 97125979Ssam cywrite(dev, uio) 97225979Ssam dev_t dev; 97325979Ssam struct uio *uio; 97424000Ssam { 97525979Ssam int errno; 97624000Ssam 97725979Ssam errno = cyphys(dev, uio); 97825979Ssam if (errno) 97925979Ssam return (errno); 98025979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_WRITE, minphys, uio)); 98124000Ssam } 98224000Ssam 98324000Ssam /* 98425979Ssam * Check that a raw device exits. 98525979Ssam * If it does, set up the yc_blkno and yc_nxrec 98625979Ssam * so that the tape will appear positioned correctly. 98725979Ssam */ 98825979Ssam cyphys(dev, uio) 98925675Ssam dev_t dev; 99025675Ssam struct uio *uio; 99125675Ssam { 99225979Ssam register int ycunit = YCUNIT(dev); 99325979Ssam register daddr_t a; 99425979Ssam register struct yc_softc *yc; 99525979Ssam register struct vba_device *vi; 99625675Ssam 99725979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 99825979Ssam return (ENXIO); 99925979Ssam yc = &yc_softc[ycunit]; 100030719Skarels a = uio->uio_offset >> DEV_BSHIFT; 100125979Ssam yc->yc_blkno = a; 100225979Ssam yc->yc_nxrec = a + 1; 100325979Ssam return (0); 100425675Ssam } 100525675Ssam 100625675Ssam /*ARGSUSED*/ 100725675Ssam cyioctl(dev, cmd, data, flag) 100825979Ssam caddr_t data; 100925675Ssam dev_t dev; 101025675Ssam { 101125979Ssam int ycunit = YCUNIT(dev); 101225979Ssam register struct yc_softc *yc = &yc_softc[ycunit]; 101325979Ssam register struct buf *bp = &ccybuf[CYUNIT(dev)]; 101425979Ssam register callcount; 101525979Ssam int fcount, op; 101625979Ssam struct mtop *mtop; 101725979Ssam struct mtget *mtget; 101825979Ssam /* we depend of the values and order of the MT codes here */ 101925979Ssam static cyops[] = 102030371Skarels {CY_WEOF,CY_FSF,CY_BSF,CY_SFORW,CY_SREV,CY_REW,CY_OFFL,CY_SENSE}; 102125675Ssam 102225675Ssam switch (cmd) { 102325675Ssam 102425979Ssam case MTIOCTOP: /* tape operation */ 102525979Ssam mtop = (struct mtop *)data; 102625979Ssam switch (op = mtop->mt_op) { 102725675Ssam 102825979Ssam case MTWEOF: 102930371Skarels callcount = mtop->mt_count; 103030371Skarels fcount = 1; 103130371Skarels break; 103230371Skarels 103325979Ssam case MTFSR: case MTBSR: 103430371Skarels callcount = 1; 103530371Skarels fcount = mtop->mt_count; 103630371Skarels break; 103730371Skarels 103825979Ssam case MTFSF: case MTBSF: 103925979Ssam callcount = mtop->mt_count; 104025979Ssam fcount = 1; 104125979Ssam break; 104225675Ssam 104325979Ssam case MTREW: case MTOFFL: case MTNOP: 104425979Ssam callcount = 1; 104525979Ssam fcount = 1; 104625979Ssam break; 104725675Ssam 104825979Ssam default: 104925979Ssam return (ENXIO); 105025979Ssam } 105125979Ssam if (callcount <= 0 || fcount <= 0) 105225979Ssam return (EINVAL); 105325979Ssam while (--callcount >= 0) { 105430371Skarels #ifdef notdef 105525979Ssam /* 105625979Ssam * Gagh, this controller is the pits... 105725979Ssam */ 105825979Ssam if (op == MTFSF || op == MTBSF) { 105925979Ssam do 106025979Ssam cycommand(dev, cyops[op], 1); 106125979Ssam while ((bp->b_flags&B_ERROR) == 0 && 106225979Ssam (yc->yc_status&(CYS_EOT|CYS_BOT|CYS_FM)) == 0); 106325979Ssam } else 106430371Skarels #endif 106525979Ssam cycommand(dev, cyops[op], fcount); 106630371Skarels dlog((LOG_INFO, 106730371Skarels "cyioctl: status %x, b_flags %x, resid %d\n", 106830371Skarels yc->yc_status, bp->b_flags, bp->b_resid)); 106925979Ssam if ((bp->b_flags&B_ERROR) || 107025979Ssam (yc->yc_status&(CYS_BOT|CYS_EOT))) 107125979Ssam break; 107225979Ssam } 107325979Ssam bp->b_resid = callcount + 1; 107425979Ssam return (geterror(bp)); 107525979Ssam 107625979Ssam case MTIOCGET: 107725979Ssam cycommand(dev, CY_SENSE, 1); 107825979Ssam mtget = (struct mtget *)data; 107925979Ssam mtget->mt_dsreg = yc->yc_status; 108025979Ssam mtget->mt_erreg = yc->yc_control; 108125979Ssam mtget->mt_resid = yc->yc_resid; 108225979Ssam mtget->mt_type = MT_ISCY; 108325675Ssam break; 108425675Ssam 108525675Ssam default: 108625675Ssam return (ENXIO); 108725675Ssam } 108825675Ssam return (0); 108925675Ssam } 109025675Ssam 109125675Ssam /* 109225675Ssam * Poll until the controller is ready. 109325675Ssam */ 109425675Ssam cywait(cp) 109525979Ssam register struct cyccb *cp; 109624000Ssam { 109725675Ssam register int i = 5000; 109824000Ssam 109925979Ssam uncache(&cp->cbgate); 110025979Ssam while (i-- > 0 && cp->cbgate == GATE_CLOSED) { 110124000Ssam DELAY(1000); 110225979Ssam uncache(&cp->cbgate); 110324000Ssam } 110425675Ssam return (i <= 0); 110524000Ssam } 110624000Ssam 110725675Ssam /* 110830371Skarels * Load a 20 bit pointer into a Tapemaster pointer. 110925675Ssam */ 111030371Skarels cyldmba(reg, value) 1111*34487Skarels register u_char *reg; 111225979Ssam caddr_t value; 111324000Ssam { 111425979Ssam register int v = (int)value; 111525675Ssam 111625979Ssam *reg++ = v; 111725979Ssam *reg++ = v >> 8; 111825979Ssam *reg++ = 0; 111925979Ssam *reg = (v&0xf0000) >> 12; 112024000Ssam } 112124000Ssam 112225675Ssam /* 112325675Ssam * Unconditionally reset all controllers to their initial state. 112425675Ssam */ 112525675Ssam cyreset(vba) 112625675Ssam int vba; 112724000Ssam { 112825675Ssam register caddr_t addr; 112925675Ssam register int ctlr; 113024000Ssam 113125675Ssam for (ctlr = 0; ctlr < NCY; ctlr++) 113225675Ssam if (cyminfo[ctlr] && cyminfo[ctlr]->um_vbanum == vba) { 113325675Ssam addr = cyminfo[ctlr]->um_addr; 113425675Ssam CY_RESET(addr); 113530371Skarels if (!cyinit(ctlr, addr)) { 113625675Ssam printf("cy%d: reset failed\n", ctlr); 113725675Ssam cyminfo[ctlr] = NULL; 113825675Ssam } 113925675Ssam } 114024000Ssam } 114125979Ssam 114225979Ssam cyuncachetpb(cy) 114325979Ssam struct cy_softc *cy; 114425979Ssam { 114525979Ssam register long *lp = (long *)&cy->cy_tpb; 114625979Ssam register int i; 114725979Ssam 114825979Ssam for (i = 0; i < howmany(sizeof (struct cytpb), sizeof (long)); i++) 114925979Ssam uncache(lp++); 115025979Ssam } 115125979Ssam 115225979Ssam /* 115325979Ssam * Dump routine. 115425979Ssam */ 115530869Skarels #define DUMPREC (32*1024) 115625979Ssam cydump(dev) 115725979Ssam dev_t dev; 115825979Ssam { 115925979Ssam register struct cy_softc *cy; 116025979Ssam register int bs, num, start; 116125979Ssam register caddr_t addr; 116230294Ssam int unit = CYUNIT(dev), error; 116325979Ssam 116425979Ssam if (unit >= NCY || cyminfo[unit] == 0 || 116525979Ssam (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) 116625979Ssam return (ENXIO); 116725979Ssam if (cywait(&cy->cy_ccb)) 116825979Ssam return (EFAULT); 116925979Ssam #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) 117030294Ssam addr = phys(cyminfo[unit]->um_addr); 117125979Ssam num = maxfree, start = NBPG*2; 117225979Ssam while (num > 0) { 117330869Skarels bs = num > btoc(DUMPREC) ? btoc(DUMPREC) : num; 117425979Ssam error = cydwrite(cy, start, bs, addr); 117525979Ssam if (error) 117625979Ssam return (error); 117725979Ssam start += bs, num -= bs; 117825979Ssam } 117925979Ssam cyweof(cy, addr); 118025979Ssam cyweof(cy, addr); 118125979Ssam uncache(&cy->cy_tpb); 118225979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 118325979Ssam return (EIO); 118425979Ssam cyrewind(cy, addr); 118525979Ssam return (0); 118625979Ssam } 118725979Ssam 118825979Ssam cydwrite(cy, pf, npf, addr) 118925979Ssam register struct cy_softc *cy; 119025979Ssam int pf, npf; 119125979Ssam caddr_t addr; 119225979Ssam { 119325979Ssam 119425979Ssam cy->cy_tpb.tpcmd = CY_WCOM; 119525979Ssam cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; 119625979Ssam cy->cy_tpb.tpstatus = 0; 119725979Ssam cy->cy_tpb.tpsize = htoms(npf*NBPG); 119825979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 119925979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); 120025979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 120125979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 120225979Ssam CY_GO(addr); 120325979Ssam if (cywait(&cy->cy_ccb)) 120425979Ssam return (EFAULT); 120525979Ssam uncache(&cy->cy_tpb); 120625979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 120725979Ssam return (EIO); 120825979Ssam return (0); 120925979Ssam } 121025979Ssam 121125979Ssam cyweof(cy, addr) 121225979Ssam register struct cy_softc *cy; 121325979Ssam caddr_t addr; 121425979Ssam { 121525979Ssam 121625979Ssam cy->cy_tpb.tpcmd = CY_WEOF; 121725979Ssam cy->cy_tpb.tpcount = htoms(1); 121825979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 121925979Ssam CY_GO(addr); 122025979Ssam (void) cywait(&cy->cy_ccb); 122125979Ssam } 122225979Ssam 122325979Ssam cyrewind(cy, addr) 122425979Ssam register struct cy_softc *cy; 122525979Ssam caddr_t addr; 122625979Ssam { 122725979Ssam 122825979Ssam cy->cy_tpb.tpcmd = CY_REW; 122925979Ssam cy->cy_tpb.tpcount = htoms(1); 123025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 123125979Ssam CY_GO(addr); 123225979Ssam (void) cywait(&cy->cy_ccb); 123325979Ssam } 123424000Ssam #endif 1235