1*30371Skarels /* cy.c 1.9 87/01/11 */ 224000Ssam 325979Ssam #include "yc.h" 425675Ssam #if NCY > 0 524000Ssam /* 625675Ssam * Cipher Tapemaster driver. 724000Ssam */ 8*30371Skarels #define CYDEBUG 9*30371Skarels #ifdef CYDEBUG 1025675Ssam int cydebug = 0; 11*30371Skarels #define dlog(params) if (cydebug) log params 12*30371Skarels #else 13*30371Skarels #define dlog(params) /* */ 14*30371Skarels #endif 1524000Ssam 1625675Ssam #include "param.h" 1725675Ssam #include "systm.h" 1825675Ssam #include "vm.h" 1925675Ssam #include "buf.h" 2025675Ssam #include "file.h" 2125675Ssam #include "dir.h" 2225675Ssam #include "user.h" 2325675Ssam #include "proc.h" 2425675Ssam #include "signal.h" 2525675Ssam #include "uio.h" 2625675Ssam #include "ioctl.h" 2725675Ssam #include "mtio.h" 2825675Ssam #include "errno.h" 2925675Ssam #include "cmap.h" 3025979Ssam #include "kernel.h" 3125979Ssam #include "syslog.h" 3230294Ssam #include "tty.h" 3324000Ssam 3429952Skarels #include "../tahoe/cpu.h" 3529952Skarels #include "../tahoe/mtpr.h" 3629952Skarels #include "../tahoe/pte.h" 3729952Skarels 3825675Ssam #include "../tahoevba/vbavar.h" 3925979Ssam #define CYERROR 4025675Ssam #include "../tahoevba/cyreg.h" 4124000Ssam 4225979Ssam /* 4325979Ssam * There is a ccybuf per tape controller. 4425979Ssam * It is used as the token to pass to the internal routines 4525979Ssam * to execute tape ioctls, and also acts as a lock on the slaves 4625979Ssam * on the controller, since there is only one per controller. 4725979Ssam * In particular, when the tape is rewinding on close we release 4825979Ssam * the user process but any further attempts to use the tape drive 4925979Ssam * before the rewind completes will hang waiting for ccybuf. 5025979Ssam */ 5125979Ssam struct buf ccybuf[NCY]; 5224000Ssam 5325979Ssam /* 5425979Ssam * Raw tape operations use rcybuf. The driver notices when 5525979Ssam * rcybuf is being used and allows the user program to contine 5625979Ssam * after errors and read records not of the standard length. 5725979Ssam */ 5825979Ssam struct buf rcybuf[NCY]; 5924000Ssam 6025979Ssam int cyprobe(), cyslave(), cyattach(); 6125979Ssam struct buf ycutab[NYC]; 6225979Ssam short yctocy[NYC]; 6325675Ssam struct vba_ctlr *cyminfo[NCY]; 6425979Ssam struct vba_device *ycdinfo[NYC]; 6525857Ssam long cystd[] = { 0 }; 6625857Ssam struct vba_driver cydriver = 6725979Ssam { cyprobe, cyslave, cyattach, 0, cystd, "yc", ycdinfo, "cy", cyminfo }; 6824000Ssam 6925979Ssam /* bits in minor device */ 7025979Ssam #define YCUNIT(dev) (minor(dev)&03) 7125979Ssam #define CYUNIT(dev) (yctocy[YCUNIT(dev)]) 7225979Ssam #define T_NOREWIND 0x04 73*30371Skarels #define T_1600BPI 0x00 /* pseudo */ 74*30371Skarels #define T_3200BPI 0x08 /* unused */ 7525979Ssam 7625979Ssam #define INF 1000000L /* close to infinity */ 7725979Ssam #define CYMAXIO (32*NBPG) /* max i/o size */ 7825979Ssam 7924000Ssam /* 8025979Ssam * Software state and shared command areas per controller. 8125979Ssam * 8225979Ssam * The i/o buffer must be defined statically to insure 8325979Ssam * it's address will fit in 20-bits (YECH!!!!!!!!!!!!!!) 8424000Ssam */ 8525979Ssam struct cy_softc { 8625979Ssam struct pte *cy_map; /* pte's for mapped buffer i/o */ 8725979Ssam caddr_t cy_utl; /* mapped virtual address */ 8825979Ssam int cy_bs; /* controller's buffer size */ 8925979Ssam struct cyscp *cy_scp; /* system configuration block address */ 9025979Ssam struct cyccb cy_ccb; /* channel control block */ 9125979Ssam struct cyscb cy_scb; /* system configuration block */ 9225979Ssam struct cytpb cy_tpb; /* tape parameter block */ 9325979Ssam struct cytpb cy_nop; /* nop parameter block for cyintr */ 94*30371Skarels char cy_buf[CYMAXIO];/* intermediate buffer */ 9525979Ssam } cy_softc[NCY]; 9624000Ssam 9725979Ssam /* 9825979Ssam * Software state per tape transport. 9925979Ssam */ 10025979Ssam struct yc_softc { 10125979Ssam char yc_openf; /* lock against multiple opens */ 10225979Ssam char yc_lastiow; /* last operation was a write */ 10325979Ssam short yc_tact; /* timeout is active */ 10425979Ssam long yc_timo; /* time until timeout expires */ 10525979Ssam u_short yc_control; /* copy of last tpcb.tpcontrol */ 10625979Ssam u_short yc_status; /* copy of last tpcb.tpstatus */ 10725979Ssam u_short yc_resid; /* copy of last bc */ 10825979Ssam u_short yc_dens; /* prototype control word with density info */ 10925979Ssam struct tty *yc_ttyp; /* user's tty for errors */ 11025979Ssam daddr_t yc_blkno; /* block number, for block device tape */ 11125979Ssam daddr_t yc_nxrec; /* position of end of tape, if known */ 112*30371Skarels int yc_blksize; /* current tape blocksize estimate */ 113*30371Skarels int yc_blks; /* number of I/O operations since open */ 114*30371Skarels int yc_softerrs; /* number of soft I/O errors since open */ 11525979Ssam } yc_softc[NYC]; 11624000Ssam 11724000Ssam /* 11825979Ssam * States for vm->um_tab.b_active, the per controller state flag. 11925979Ssam * This is used to sequence control in the driver. 12024000Ssam */ 12125979Ssam #define SSEEK 1 /* seeking */ 12225979Ssam #define SIO 2 /* doing seq i/o */ 12325979Ssam #define SCOM 3 /* sending control command */ 12425979Ssam #define SREW 4 /* sending a rewind */ 12525979Ssam #define SERASE 5 /* erase inter-record gap */ 12625979Ssam #define SERASED 6 /* erased inter-record gap */ 12724000Ssam 12825979Ssam /* there's no way to figure these out dynamically? -- yech */ 12925979Ssam struct cyscp *cyscp[] = 13025979Ssam { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; 13125979Ssam #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) 13225979Ssam 13325857Ssam cyprobe(reg, vm) 13425857Ssam caddr_t reg; 13525857Ssam struct vba_ctlr *vm; 13625675Ssam { 13725857Ssam register br, cvec; /* must be r12, r11 */ 138*30371Skarels register struct cy_softc *cy; 139*30371Skarels int ctlr = vm->um_ctlr; 14025675Ssam 14130294Ssam #ifdef lint 14230294Ssam br = 0; cvec = br; br = cvec; 14330294Ssam cyintr(0); 14430294Ssam #endif 14525857Ssam if (badcyaddr(reg+1)) 14625675Ssam return (0); 147*30371Skarels if (ctlr > NCYSCP || cyscp[ctlr] == 0) /* XXX */ 148*30371Skarels return (0); 149*30371Skarels cy = &cy_softc[ctlr]; 150*30371Skarels cy->cy_scp = cyscp[ctlr]; /* XXX */ 15125979Ssam /* 15225979Ssam * Tapemaster controller must have interrupt handler 15325979Ssam * disable interrupt, so we'll just kludge things 15425979Ssam * (stupid multibus non-vectored interrupt crud). 15525979Ssam */ 156*30371Skarels if (cyinit(ctlr, reg)) { 157*30371Skarels uncache(&cy->cy_tpb.tpcount); 158*30371Skarels cy->cy_bs = htoms(cy->cy_tpb.tpcount); 159*30371Skarels /* 160*30371Skarels * Setup nop parameter block for clearing interrupts. 161*30371Skarels */ 162*30371Skarels cy->cy_nop.tpcmd = CY_NOP; 163*30371Skarels cy->cy_nop.tpcontrol = 0; 164*30371Skarels /* 165*30371Skarels * Allocate page tables. 166*30371Skarels */ 167*30371Skarels vbmapalloc(btoc(CYMAXIO)+1, &cy->cy_map, &cy->cy_utl); 168*30371Skarels 169*30371Skarels br = 0x13, cvec = 0x80; /* XXX */ 170*30371Skarels return (sizeof (struct cyccb)); 171*30371Skarels } else 172*30371Skarels return (0); 17325675Ssam } 17425675Ssam 17524000Ssam /* 17625857Ssam * Check to see if a drive is attached to a controller. 17725857Ssam * Since we can only tell that a drive is there if a tape is loaded and 17825857Ssam * the drive is placed online, we always indicate the slave is present. 17924000Ssam */ 18025857Ssam cyslave(vi, addr) 18125857Ssam struct vba_device *vi; 18225857Ssam caddr_t addr; 18324000Ssam { 18425857Ssam 18525857Ssam #ifdef lint 18625857Ssam vi = vi; addr = addr; 18725857Ssam #endif 18825857Ssam return (1); 18925857Ssam } 19025857Ssam 19125857Ssam cyattach(vi) 19225857Ssam struct vba_device *vi; 19325857Ssam { 19425979Ssam register struct cy_softc *cy; 19525979Ssam int ctlr = vi->ui_mi->um_ctlr; 19625857Ssam 19725979Ssam yctocy[vi->ui_unit] = ctlr; 19825979Ssam cy = &cy_softc[ctlr]; 199*30371Skarels if (vi->ui_slave == 0 && cy->cy_bs) 200*30371Skarels printf("; %dkb buffer", cy->cy_bs/1024); 20125857Ssam } 20225857Ssam 20325857Ssam /* 20425857Ssam * Initialize the controller after a controller reset or 20525857Ssam * during autoconfigure. All of the system control blocks 20625857Ssam * are initialized and the controller is asked to configure 20725857Ssam * itself for later use. 20825857Ssam */ 209*30371Skarels cyinit(ctlr, addr) 21025979Ssam int ctlr; 211*30371Skarels register caddr_t addr; 21225857Ssam { 21325979Ssam register struct cy_softc *cy = &cy_softc[ctlr]; 21425675Ssam register int *pte; 21524000Ssam 21624000Ssam /* 21725675Ssam * Initialize the system configuration pointer. 21824000Ssam */ 21925675Ssam /* make kernel writable */ 22025979Ssam pte = (int *)vtopte((struct proc *)0, btop(cy->cy_scp)); 22125675Ssam *pte &= ~PG_PROT; *pte |= PG_KW; 22225979Ssam mtpr(TBIS, cy->cy_scp); 22325675Ssam /* load the correct values in the scp */ 22425979Ssam cy->cy_scp->csp_buswidth = CSP_16BITS; 22525979Ssam cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); 22625675Ssam /* put it back to read-only */ 22725675Ssam *pte &= ~PG_PROT; *pte |= PG_KR; 22825979Ssam mtpr(TBIS, cy->cy_scp); 22925675Ssam 23024000Ssam /* 23125675Ssam * Init system configuration block. 23224000Ssam */ 233*30371Skarels cy->cy_scb.csb_fixed = CSB_FIXED; 23425675Ssam /* set pointer to the channel control block */ 23525979Ssam cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); 23625675Ssam 23724000Ssam /* 23825675Ssam * Initialize the chanel control block. 23924000Ssam */ 24025979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 24125979Ssam cy->cy_ccb.cbgate = GATE_OPEN; 24225675Ssam /* set pointer to the tape parameter block */ 24325979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 24425675Ssam 24524000Ssam /* 24625979Ssam * Issue a nop cmd and get the internal buffer size for buffered i/o. 24724000Ssam */ 24825979Ssam cy->cy_tpb.tpcmd = CY_NOP; 24925979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 25025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 25125979Ssam CY_GO(addr); 25225979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 25325979Ssam uncache(&cy->cy_tpb.tpstatus); 25425979Ssam printf("cy%d: timeout or err during init, status=%b\n", ctlr, 25525979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 25625675Ssam return (0); 25725675Ssam } 25825979Ssam cy->cy_tpb.tpcmd = CY_CONFIG; 25925979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 26025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 26125979Ssam CY_GO(addr); 26225979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 26325979Ssam uncache(&cy->cy_tpb.tpstatus); 26425979Ssam printf("cy%d: configuration failure, status=%b\n", ctlr, 26525979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 26625675Ssam return (0); 26725675Ssam } 26825675Ssam return (1); 26924000Ssam } 27024000Ssam 27125979Ssam int cytimer(); 27225979Ssam /* 27325979Ssam * Open the device. Tapes are unique open 27425979Ssam * devices, so we refuse if it is already open. 27525979Ssam * We also check that a tape is available, and 27625979Ssam * don't block waiting here; if you want to wait 27725979Ssam * for a tape you should timeout in user code. 27825979Ssam */ 27925675Ssam cyopen(dev, flag) 28025979Ssam dev_t dev; 28125675Ssam register int flag; 28225675Ssam { 28325979Ssam register int ycunit; 28425979Ssam register struct vba_device *vi; 28525979Ssam register struct yc_softc *yc; 28625979Ssam int s; 28725675Ssam 28825979Ssam ycunit = YCUNIT(dev); 28925979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 29025675Ssam return (ENXIO); 29125979Ssam if ((yc = &yc_softc[ycunit])->yc_openf) 29225979Ssam return (EBUSY); 293*30371Skarels yc->yc_openf = 1; 29425979Ssam #define PACKUNIT(vi) \ 29525979Ssam (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) 29625979Ssam /* no way to select density */ 29725979Ssam yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; 298*30371Skarels if (yc->yc_tact == 0) { 299*30371Skarels yc->yc_timo = INF; 300*30371Skarels yc->yc_tact = 1; 301*30371Skarels timeout(cytimer, (caddr_t)dev, 5*hz); 302*30371Skarels } 30325979Ssam cycommand(dev, CY_SENSE, 1); 30425979Ssam if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ 30525979Ssam uprintf("yc%d: not online\n", ycunit); 30625675Ssam return (ENXIO); 30725675Ssam } 30825979Ssam if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { 30925979Ssam uprintf("yc%d: no write ring\n", ycunit); 31025675Ssam return (ENXIO); 31125675Ssam } 31225979Ssam yc->yc_blkno = (daddr_t)0; 31325979Ssam yc->yc_nxrec = INF; 31425979Ssam yc->yc_lastiow = 0; 315*30371Skarels yc->yc_blksize = 1024; /* guess > 0 */ 316*30371Skarels yc->yc_blks = 0; 317*30371Skarels yc->yc_softerrs = 0; 31825979Ssam yc->yc_ttyp = u.u_ttyp; 31925675Ssam return (0); 32025675Ssam } 32125675Ssam 32225979Ssam /* 32325979Ssam * Close tape device. 32425979Ssam * 32525979Ssam * If tape was open for writing or last operation was a write, 32625979Ssam * then write two EOF's and backspace over the last one. 32725979Ssam * Unless this is a non-rewinding special file, rewind the tape. 32825979Ssam * Make the tape available to others. 32925979Ssam */ 33025675Ssam cyclose(dev, flag) 33125979Ssam dev_t dev; 332*30371Skarels int flag; 33325675Ssam { 334*30371Skarels struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 33525675Ssam 33625979Ssam if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { 33725979Ssam cycommand(dev, CY_WEOF, 2); 33825979Ssam cycommand(dev, CY_SREV, 1); 33925675Ssam } 34025979Ssam if ((minor(dev)&T_NOREWIND) == 0) 34125979Ssam /* 34225979Ssam * 0 count means don't hang waiting for rewind complete 34325979Ssam * rather ccybuf stays busy until the operation completes 34425979Ssam * preventing further opens from completing by preventing 34525979Ssam * a CY_SENSE from completing. 34625979Ssam */ 34725979Ssam cycommand(dev, CY_REW, 0); 348*30371Skarels if (yc->yc_blks > 10 && yc->yc_softerrs > yc->yc_blks / 10) 349*30371Skarels log(LOG_INFO, "yc%d: %d soft errors in %d blocks\n", 350*30371Skarels YCUNIT(dev), yc->yc_softerrs, yc->yc_blks); 351*30371Skarels dlog((LOG_INFO, "%d soft errors in %d blocks\n", 352*30371Skarels yc->yc_softerrs, yc->yc_blks)); 35325979Ssam yc->yc_openf = 0; 35425675Ssam } 35525675Ssam 35624000Ssam /* 35725979Ssam * Execute a command on the tape drive a specified number of times. 35824000Ssam */ 35925979Ssam cycommand(dev, com, count) 36025979Ssam dev_t dev; 36125979Ssam int com, count; 36224000Ssam { 36325979Ssam register struct buf *bp; 36425675Ssam int s; 36525675Ssam 36625979Ssam bp = &ccybuf[CYUNIT(dev)]; 36725675Ssam s = spl3(); 368*30371Skarels dlog((LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", 369*30371Skarels dev, com, count, bp->b_flags)); 37025979Ssam while (bp->b_flags&B_BUSY) { 37125979Ssam /* 37225979Ssam * This special check is because B_BUSY never 37325979Ssam * gets cleared in the non-waiting rewind case. 37425979Ssam */ 37525979Ssam if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 37625979Ssam break; 37725979Ssam bp->b_flags |= B_WANTED; 37825979Ssam sleep((caddr_t)bp, PRIBIO); 37925675Ssam } 38025979Ssam bp->b_flags = B_BUSY|B_READ; 38125675Ssam splx(s); 38225979Ssam bp->b_dev = dev; 38325979Ssam bp->b_repcnt = count; 38425979Ssam bp->b_command = com; 38525979Ssam bp->b_blkno = 0; 38625979Ssam cystrategy(bp); 38725979Ssam /* 38825979Ssam * In case of rewind from close; don't wait. 38925979Ssam * This is the only case where count can be 0. 39025979Ssam */ 39125979Ssam if (count == 0) 39225979Ssam return; 393*30371Skarels biowait(bp); 39425979Ssam if (bp->b_flags&B_WANTED) 39525979Ssam wakeup((caddr_t)bp); 39625979Ssam bp->b_flags &= B_ERROR; 39724000Ssam } 39824000Ssam 39925675Ssam cystrategy(bp) 40025675Ssam register struct buf *bp; 40125675Ssam { 40225979Ssam int ycunit = YCUNIT(bp->b_dev); 40325979Ssam register struct vba_ctlr *vm; 40425979Ssam register struct buf *dp; 40525675Ssam int s; 40625675Ssam 40725979Ssam /* 40825979Ssam * Put transfer at end of unit queue. 40925979Ssam */ 410*30371Skarels dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); 41125979Ssam dp = &ycutab[ycunit]; 41225675Ssam bp->av_forw = NULL; 413*30371Skarels bp->b_errcnt = 0; 41425979Ssam vm = ycdinfo[ycunit]->ui_mi; 41525979Ssam /* BEGIN GROT */ 41625979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) { 41725979Ssam if (bp->b_bcount > CYMAXIO) { 41825979Ssam uprintf("cy%d: i/o size too large\n", vm->um_ctlr); 41925979Ssam bp->b_error = EIO; 42025979Ssam bp->b_resid = bp->b_bcount; 42125979Ssam bp->b_flags |= B_ERROR; 422*30371Skarels biodone(bp); 42325675Ssam return; 42425675Ssam } 42525979Ssam vbasetup(bp, CYMAXIO); 42624000Ssam } 42725979Ssam /* END GROT */ 42825675Ssam s = spl3(); 42925979Ssam if (dp->b_actf == NULL) { 43025979Ssam dp->b_actf = bp; 43125979Ssam /* 43225979Ssam * Transport not already active... 43325979Ssam * put at end of controller queue. 43425979Ssam */ 43525979Ssam dp->b_forw = NULL; 43625979Ssam if (vm->um_tab.b_actf == NULL) 43725979Ssam vm->um_tab.b_actf = dp; 43825979Ssam else 43925979Ssam vm->um_tab.b_actl->b_forw = dp; 44025979Ssam } else 44125979Ssam dp->b_actl->av_forw = bp; 44225979Ssam dp->b_actl = bp; 44325979Ssam /* 44425979Ssam * If the controller is not busy, get it going. 44525979Ssam */ 44625979Ssam if (vm->um_tab.b_active == 0) 44725979Ssam cystart(vm); 44824000Ssam splx(s); 44924000Ssam } 45024000Ssam 45124000Ssam /* 45225979Ssam * Start activity on a cy controller. 45324000Ssam */ 45425979Ssam cystart(vm) 45525979Ssam register struct vba_ctlr *vm; 45624000Ssam { 45725979Ssam register struct buf *bp, *dp; 45825979Ssam register struct yc_softc *yc; 45925979Ssam register struct cy_softc *cy; 46025979Ssam int ycunit; 46125979Ssam daddr_t blkno; 46224000Ssam 463*30371Skarels dlog((LOG_INFO, "cystart()\n")); 46425979Ssam /* 46525979Ssam * Look for an idle transport on the controller. 46625979Ssam */ 46725979Ssam loop: 46825979Ssam if ((dp = vm->um_tab.b_actf) == NULL) 46925675Ssam return; 47025979Ssam if ((bp = dp->b_actf) == NULL) { 47125979Ssam vm->um_tab.b_actf = dp->b_forw; 47225979Ssam goto loop; 47325675Ssam } 47425979Ssam ycunit = YCUNIT(bp->b_dev); 47525979Ssam yc = &yc_softc[ycunit]; 47625979Ssam cy = &cy_softc[CYUNIT(bp->b_dev)]; 47725979Ssam /* 47825979Ssam * Default is that last command was NOT a write command; 47925979Ssam * if we do a write command we will notice this in cyintr(). 48025979Ssam */ 48125979Ssam yc->yc_lastiow = 0; 48225979Ssam if (yc->yc_openf < 0 || 48325979Ssam (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { 48425979Ssam /* 48525979Ssam * Have had a hard error on a non-raw tape 48625979Ssam * or the tape unit is now unavailable (e.g. 48725979Ssam * taken off line). 48825979Ssam */ 489*30371Skarels dlog((LOG_INFO, "openf %d command %x status %b\n", 490*30371Skarels yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); 49125979Ssam bp->b_flags |= B_ERROR; 49225979Ssam goto next; 49325675Ssam } 49425979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { 49525979Ssam /* 49625979Ssam * Execute control operation with the specified count. 49725979Ssam * 49825979Ssam * Set next state; give 5 minutes to complete 49925979Ssam * rewind or file mark search, or 10 seconds per 50025979Ssam * iteration (minimum 60 seconds and max 5 minutes) 50125979Ssam * to complete other ops. 50225979Ssam */ 50325979Ssam if (bp->b_command == CY_REW) { 50425979Ssam vm->um_tab.b_active = SREW; 50525979Ssam yc->yc_timo = 5*60; 50625979Ssam } else { 50725979Ssam vm->um_tab.b_active = SCOM; 50825979Ssam yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); 50925979Ssam } 51025979Ssam cy->cy_tpb.tprec = htoms(bp->b_repcnt); 51125979Ssam goto dobpcmd; 51224000Ssam } 51325979Ssam /* 51425979Ssam * The following checks handle boundary cases for operation 51525979Ssam * on no-raw tapes. On raw tapes the initialization of 51625979Ssam * yc->yc_nxrec by cyphys causes them to be skipped normally 51725979Ssam * (except in the case of retries). 51825979Ssam */ 51925979Ssam if (bdbtofsb(bp->b_blkno) > yc->yc_nxrec) { 52025979Ssam /* 52125979Ssam * Can't read past known end-of-file. 52225979Ssam */ 52325979Ssam bp->b_flags |= B_ERROR; 52425979Ssam bp->b_error = ENXIO; 52525979Ssam goto next; 52624000Ssam } 52725979Ssam if (bdbtofsb(bp->b_blkno) == yc->yc_nxrec && bp->b_flags&B_READ) { 52825979Ssam /* 52925979Ssam * Reading at end of file returns 0 bytes. 53025979Ssam */ 53125979Ssam bp->b_resid = bp->b_bcount; 53225979Ssam clrbuf(bp); 53325979Ssam goto next; 53424000Ssam } 53525979Ssam if ((bp->b_flags&B_READ) == 0) 53625979Ssam /* 53725979Ssam * Writing sets EOF. 53825979Ssam */ 53925979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno) + 1; 54025979Ssam if ((blkno = yc->yc_blkno) == bdbtofsb(bp->b_blkno)) { 54125979Ssam caddr_t addr; 54225979Ssam int cmd; 54325675Ssam 54425979Ssam /* 54525979Ssam * Choose the appropriate i/o command based on the 546*30371Skarels * transfer size, the estimated block size, 547*30371Skarels * and the controller's internal buffer size. 54825979Ssam * If we're retrying a read on a raw device because 54925979Ssam * the original try was a buffer request which failed 55025979Ssam * due to a record length error, then we force the use 55125979Ssam * of the raw controller read (YECH!!!!). 55225979Ssam */ 55325979Ssam if (bp->b_flags&B_READ) { 554*30371Skarels if ((bp->b_bcount > cy->cy_bs && 555*30371Skarels yc->yc_blksize > cy->cy_bs) || bp->b_errcnt) 55625979Ssam cmd = CY_RCOM; 55725979Ssam else 55825979Ssam cmd = CY_BRCOM; 55925979Ssam } else { 56025979Ssam /* 56125979Ssam * On write error retries erase the 56225979Ssam * inter-record gap before rewriting. 56325979Ssam */ 56425979Ssam if (vm->um_tab.b_errcnt && 56525979Ssam vm->um_tab.b_active != SERASED) { 56625979Ssam vm->um_tab.b_active = SERASE; 56725979Ssam bp->b_command = CY_ERASE; 56825979Ssam yc->yc_timo = 60; 56925979Ssam goto dobpcmd; 57025675Ssam } 57125979Ssam cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; 57225675Ssam } 57325979Ssam vm->um_tab.b_active = SIO; 57425979Ssam addr = (caddr_t)vbastart(bp, cy->cy_buf, 57525979Ssam (long *)cy->cy_map, cy->cy_utl); 57625979Ssam cy->cy_tpb.tpcmd = cmd; 57725979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 57825979Ssam if (cmd == CY_RCOM || cmd == CY_WCOM) 57925979Ssam cy->cy_tpb.tpcontrol |= CYCW_LOCK; 58025979Ssam cy->cy_tpb.tpstatus = 0; 58125979Ssam cy->cy_tpb.tpcount = 0; 58225979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); 58325979Ssam cy->cy_tpb.tprec = 0; 584*30371Skarels if (cmd == CY_BRCOM && bp->b_bcount > cy->cy_bs) 585*30371Skarels cy->cy_tpb.tpsize = htoms(cy->cy_bs); 586*30371Skarels else 587*30371Skarels cy->cy_tpb.tpsize = htoms(bp->b_bcount); 58825979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 58925979Ssam do 59025979Ssam uncache(&cy->cy_ccb.cbgate); 59125979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 59225979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 59325979Ssam cy->cy_ccb.cbcw = CBCW_IE; 59425979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 595*30371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", 59625979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 597*30371Skarels htoms(cy->cy_tpb.tpsize))); 59825979Ssam CY_GO(vm->um_addr); 59925979Ssam return; 60024000Ssam } 60125979Ssam /* 60225979Ssam * Tape positioned incorrectly; set to seek forwards 60325979Ssam * or backwards to the correct spot. This happens 60425979Ssam * for raw tapes only on error retries. 60525979Ssam */ 60625979Ssam vm->um_tab.b_active = SSEEK; 60725979Ssam if (blkno < bdbtofsb(bp->b_blkno)) { 60825979Ssam bp->b_command = CY_SFORW; 60925979Ssam cy->cy_tpb.tprec = htoms(bdbtofsb(bp->b_blkno) - blkno); 61025979Ssam } else { 61125979Ssam bp->b_command = CY_SREV; 61225979Ssam cy->cy_tpb.tprec = htoms(blkno - bdbtofsb(bp->b_blkno)); 61324000Ssam } 61425979Ssam yc->yc_timo = imin(imax(10 * htoms(cy->cy_tpb.tprec), 60), 5*60); 61525979Ssam dobpcmd: 61625979Ssam /* 61725979Ssam * Do the command in bp. Reverse direction commands 61825979Ssam * are indicated by having CYCW_REV or'd into their 61925979Ssam * value. For these we must set the appropriate bit 62025979Ssam * in the control field. 62125979Ssam */ 62225979Ssam if (bp->b_command&CYCW_REV) { 62325979Ssam cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; 62425979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; 62525979Ssam } else { 62625979Ssam cy->cy_tpb.tpcmd = bp->b_command; 62725979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 62824000Ssam } 62925979Ssam cy->cy_tpb.tpstatus = 0; 63025979Ssam cy->cy_tpb.tpcount = 0; 63125979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 63225979Ssam do 63325979Ssam uncache(&cy->cy_ccb.cbgate); 63425979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 63525979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 63625979Ssam cy->cy_ccb.cbcw = CBCW_IE; 63725979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 638*30371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", 63925979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 640*30371Skarels htoms(cy->cy_tpb.tprec))); 64125979Ssam CY_GO(vm->um_addr); 64225979Ssam return; 64325979Ssam next: 64425979Ssam /* 64525979Ssam * Done with this operation due to error or the 64625979Ssam * fact that it doesn't do anything. Release VERSAbus 64725979Ssam * resource (if any), dequeue the transfer and continue 64825979Ssam * processing this slave. 64925979Ssam */ 65025979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) 65125979Ssam vbadone(bp, cy->cy_buf, (long *)cy->cy_map, cy->cy_utl); 65225979Ssam vm->um_tab.b_errcnt = 0; 65325979Ssam dp->b_actf = bp->av_forw; 654*30371Skarels biodone(bp); 65525979Ssam goto loop; 65625675Ssam } 65725675Ssam 65825675Ssam /* 65925979Ssam * Cy interrupt routine. 66025675Ssam */ 66125979Ssam cyintr(cipher) 66225979Ssam int cipher; 66325675Ssam { 66425979Ssam struct buf *dp; 66524000Ssam register struct buf *bp; 66625979Ssam register struct vba_ctlr *vm = cyminfo[cipher]; 66725979Ssam register struct cy_softc *cy; 66825979Ssam register struct yc_softc *yc; 66925979Ssam int cyunit, err; 67025979Ssam register state; 67124000Ssam 672*30371Skarels dlog((LOG_INFO, "cyintr(%d)\n", cipher)); 67325979Ssam /* 67425979Ssam * First, turn off the interrupt from the controller 67525979Ssam * (device uses Multibus non-vectored interrupts...yech). 67625979Ssam */ 67725979Ssam cy = &cy_softc[vm->um_ctlr]; 67825979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 67930294Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); 68025979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 68125979Ssam CY_GO(vm->um_addr); 68225979Ssam if ((dp = vm->um_tab.b_actf) == NULL) { 683*30371Skarels dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); 68424000Ssam return; 68524000Ssam } 68625979Ssam bp = dp->b_actf; 68725979Ssam cyunit = CYUNIT(bp->b_dev); 68825979Ssam cy = &cy_softc[cyunit]; 68925979Ssam cyuncachetpb(cy); 69030294Ssam yc = &yc_softc[YCUNIT(bp->b_dev)]; 69125979Ssam /* 69225984Ssam * If last command was a rewind and tape is 69325984Ssam * still moving, wait for the operation to complete. 69425979Ssam */ 69525979Ssam if (vm->um_tab.b_active == SREW) { 69625979Ssam vm->um_tab.b_active = SCOM; 69725979Ssam if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { 69825979Ssam yc->yc_timo = 5*60; /* 5 minutes */ 69925979Ssam return; 70024000Ssam } 70124000Ssam } 70225979Ssam /* 70325979Ssam * An operation completed...record status. 70425979Ssam */ 70525979Ssam yc->yc_timo = INF; 70625979Ssam yc->yc_control = cy->cy_tpb.tpcontrol; 70725979Ssam yc->yc_status = cy->cy_tpb.tpstatus; 70825979Ssam yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 709*30371Skarels dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", 71025979Ssam cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, 711*30371Skarels yc->yc_status, CYS_BITS, yc->yc_resid)); 71225979Ssam if ((bp->b_flags&B_READ) == 0) 71325979Ssam yc->yc_lastiow = 1; 71425979Ssam state = vm->um_tab.b_active; 71525979Ssam vm->um_tab.b_active = 0; 71625979Ssam /* 71725979Ssam * Check for errors. 71825979Ssam */ 71925979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) { 72025979Ssam err = cy->cy_tpb.tpstatus&CYS_ERR; 721*30371Skarels dlog((LOG_INFO, "error %d\n", err)); 72225979Ssam /* 72325979Ssam * If we hit the end of tape file, update our position. 72425979Ssam */ 72525979Ssam if (err == CYER_FM) { 72625979Ssam yc->yc_status |= CYS_FM; 72725979Ssam state = SCOM; /* force completion */ 72825979Ssam cyseteof(bp); /* set blkno and nxrec */ 72925979Ssam goto opdone; 73025979Ssam } 73125979Ssam /* 73225979Ssam * Fix up errors which occur due to backspacing over 73325979Ssam * the beginning of the tape. 73425979Ssam */ 73525979Ssam if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { 73625979Ssam yc->yc_status |= CYS_BOT; 73725979Ssam goto ignoreerr; 73825979Ssam } 73925979Ssam /* 74025979Ssam * If we were reading raw tape and the only error was that the 74125979Ssam * record was too long, then we don't consider this an error. 74225979Ssam */ 74325979Ssam if (bp == &rcybuf[cyunit] && (bp->b_flags&B_READ) && 74425979Ssam err == CYER_STROBE) { 74525979Ssam /* 746*30371Skarels * Retry reads with the command changed to 747*30371Skarels * a raw read if necessary. Setting b_errcnt 74825979Ssam * here causes cystart (above) to force a CY_RCOM. 74925979Ssam */ 750*30371Skarels if (htoms(cy->cy_tpb.tprec) > cy->cy_bs && 751*30371Skarels bp->b_bcount > cy->cy_bs && 752*30371Skarels yc->yc_blksize <= cy->cy_bs && 753*30371Skarels bp->b_errcnt++ == 0) { 754*30371Skarels yc->yc_blkno++; 755*30371Skarels goto opcont; 756*30371Skarels } else 75725979Ssam goto ignoreerr; 75825979Ssam } 75925979Ssam /* 76025979Ssam * If error is not hard, and this was an i/o operation 76125979Ssam * retry up to 8 times. 76225979Ssam */ 76325984Ssam if (((1<<err)&CYER_SOFT) && state == SIO) { 76425979Ssam if (++vm->um_tab.b_errcnt < 7) { 76525979Ssam yc->yc_blkno++; 76625979Ssam goto opcont; 76725979Ssam } 76825979Ssam } else 76925979Ssam /* 77025979Ssam * Hard or non-i/o errors on non-raw tape 77125979Ssam * cause it to close. 77225979Ssam */ 773*30371Skarels if (yc->yc_openf > 0 && bp != &rcybuf[cyunit]) 77425979Ssam yc->yc_openf = -1; 77525979Ssam /* 77625979Ssam * Couldn't recover from error. 77725979Ssam */ 77825979Ssam tprintf(yc->yc_ttyp, 779*30371Skarels "yc%d: hard error bn%d status=%b, %s\n", YCUNIT(bp->b_dev), 780*30371Skarels bp->b_blkno, yc->yc_status, CYS_BITS, 781*30371Skarels (err < NCYERROR) ? cyerror[err] : ""); 78225979Ssam bp->b_flags |= B_ERROR; 78325979Ssam goto opdone; 78424000Ssam } 78525979Ssam /* 78625979Ssam * Advance tape control FSM. 78725979Ssam */ 78825979Ssam ignoreerr: 78925979Ssam /* 79025979Ssam * If we hit a tape mark update our position. 79125979Ssam */ 79225979Ssam if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { 79325979Ssam cyseteof(bp); 79425979Ssam goto opdone; 79525675Ssam } 79625979Ssam switch (state) { 79724000Ssam 79825979Ssam case SIO: 79925979Ssam /* 80025979Ssam * Read/write increments tape block number. 80125979Ssam */ 80225979Ssam yc->yc_blkno++; 803*30371Skarels yc->yc_blks++; 804*30371Skarels if (vm->um_tab.b_errcnt || yc->yc_status & CYS_CR) 805*30371Skarels yc->yc_softerrs++; 806*30371Skarels yc->yc_blksize = htoms(cy->cy_tpb.tpcount); 807*30371Skarels dlog((LOG_ERR, "blocksize %d", yc->yc_blksize)); 80825979Ssam goto opdone; 80924000Ssam 81025979Ssam case SCOM: 81125979Ssam /* 81225979Ssam * For forward/backward space record update current position. 81325979Ssam */ 81430294Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) 81530294Ssam switch ((int)bp->b_command) { 81624000Ssam 81730294Ssam case CY_SFORW: 81830294Ssam yc->yc_blkno -= bp->b_repcnt; 81930294Ssam break; 82024000Ssam 82130294Ssam case CY_SREV: 82230294Ssam yc->yc_blkno += bp->b_repcnt; 82330294Ssam break; 82430294Ssam } 82525979Ssam goto opdone; 82625979Ssam 82725979Ssam case SSEEK: 82825979Ssam yc->yc_blkno = bdbtofsb(bp->b_blkno); 82925979Ssam goto opcont; 83024000Ssam 83125979Ssam case SERASE: 83225979Ssam /* 83325979Ssam * Completed erase of the inter-record gap due to a 83425979Ssam * write error; now retry the write operation. 83525979Ssam */ 83625979Ssam vm->um_tab.b_active = SERASED; 83725979Ssam goto opcont; 83824000Ssam } 83925675Ssam 84025979Ssam opdone: 84125979Ssam /* 84225979Ssam * Reset error count and remove from device queue. 84325979Ssam */ 84425979Ssam vm->um_tab.b_errcnt = 0; 84525979Ssam dp->b_actf = bp->av_forw; 84625979Ssam /* 84725979Ssam * Save resid and release resources. 84825979Ssam */ 84925979Ssam bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 85025979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) 85125979Ssam vbadone(bp, cy->cy_buf, (long *)cy->cy_map, cy->cy_utl); 852*30371Skarels biodone(bp); 85325979Ssam /* 85425979Ssam * Circulate slave to end of controller 85525979Ssam * queue to give other slaves a chance. 85625979Ssam */ 85725979Ssam vm->um_tab.b_actf = dp->b_forw; 85825979Ssam if (dp->b_actf) { 85925979Ssam dp->b_forw = NULL; 86025979Ssam if (vm->um_tab.b_actf == NULL) 86125979Ssam vm->um_tab.b_actf = dp; 86225979Ssam else 86325979Ssam vm->um_tab.b_actl->b_forw = dp; 86424000Ssam } 86525979Ssam if (vm->um_tab.b_actf == 0) 86624000Ssam return; 86725979Ssam opcont: 86825979Ssam cystart(vm); 86924000Ssam } 87024000Ssam 87125979Ssam cytimer(dev) 87225979Ssam int dev; 87324000Ssam { 87425979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 87525979Ssam int s; 87624000Ssam 877*30371Skarels if (yc->yc_openf == 0 && yc->yc_timo == INF) { 878*30371Skarels yc->yc_tact = 0; 879*30371Skarels return; 880*30371Skarels } 88125979Ssam if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { 88225979Ssam printf("yc%d: lost interrupt\n", YCUNIT(dev)); 88325979Ssam yc->yc_timo = INF; 88425979Ssam s = spl3(); 88525979Ssam cyintr(CYUNIT(dev)); 88625979Ssam splx(s); 88724000Ssam } 88825979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 88924000Ssam } 89024000Ssam 89125979Ssam cyseteof(bp) 89225979Ssam register struct buf *bp; 89324000Ssam { 89425979Ssam register int cyunit = CYUNIT(bp->b_dev); 89525979Ssam register struct cy_softc *cy = &cy_softc[cyunit]; 89625979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; 89724000Ssam 89825979Ssam if (bp == &ccybuf[cyunit]) { 89925979Ssam if (yc->yc_blkno > bdbtofsb(bp->b_blkno)) { 90025979Ssam /* reversing */ 90125979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno) - 90225979Ssam htoms(cy->cy_tpb.tpcount); 90325979Ssam yc->yc_blkno = yc->yc_nxrec; 90425979Ssam } else { 90525979Ssam yc->yc_blkno = bdbtofsb(bp->b_blkno) + 90625979Ssam htoms(cy->cy_tpb.tpcount); 90725979Ssam yc->yc_nxrec = yc->yc_blkno - 1; 90824000Ssam } 90925675Ssam return; 91025675Ssam } 91125979Ssam /* eof on read */ 91225979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno); 91324000Ssam } 91424000Ssam 91525979Ssam cyread(dev, uio) 91625979Ssam dev_t dev; 91725979Ssam struct uio *uio; 91825675Ssam { 91925979Ssam int errno; 92025675Ssam 92125979Ssam errno = cyphys(dev, uio); 92225979Ssam if (errno) 92325979Ssam return (errno); 92425979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_READ, minphys, uio)); 92525675Ssam } 92625675Ssam 92725979Ssam cywrite(dev, uio) 92825979Ssam dev_t dev; 92925979Ssam struct uio *uio; 93024000Ssam { 93125979Ssam int errno; 93224000Ssam 93325979Ssam errno = cyphys(dev, uio); 93425979Ssam if (errno) 93525979Ssam return (errno); 93625979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_WRITE, minphys, uio)); 93724000Ssam } 93824000Ssam 93924000Ssam /* 94025979Ssam * Check that a raw device exits. 94125979Ssam * If it does, set up the yc_blkno and yc_nxrec 94225979Ssam * so that the tape will appear positioned correctly. 94325979Ssam */ 94425979Ssam cyphys(dev, uio) 94525675Ssam dev_t dev; 94625675Ssam struct uio *uio; 94725675Ssam { 94825979Ssam register int ycunit = YCUNIT(dev); 94925979Ssam register daddr_t a; 95025979Ssam register struct yc_softc *yc; 95125979Ssam register struct vba_device *vi; 95225675Ssam 95325979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 95425979Ssam return (ENXIO); 95525979Ssam yc = &yc_softc[ycunit]; 95625979Ssam a = bdbtofsb(uio->uio_offset >> DEV_BSHIFT); 95725979Ssam yc->yc_blkno = a; 95825979Ssam yc->yc_nxrec = a + 1; 95925979Ssam return (0); 96025675Ssam } 96125675Ssam 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[] = 976*30371Skarels {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: 985*30371Skarels callcount = mtop->mt_count; 986*30371Skarels fcount = 1; 987*30371Skarels break; 988*30371Skarels 98925979Ssam case MTFSR: case MTBSR: 990*30371Skarels callcount = 1; 991*30371Skarels fcount = mtop->mt_count; 992*30371Skarels break; 993*30371Skarels 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) { 1010*30371Skarels #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 1020*30371Skarels #endif 102125979Ssam cycommand(dev, cyops[op], fcount); 1022*30371Skarels dlog((LOG_INFO, 1023*30371Skarels "cyioctl: status %x, b_flags %x, resid %d\n", 1024*30371Skarels 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 /* 1064*30371Skarels * Load a 20 bit pointer into a Tapemaster pointer. 106525675Ssam */ 1066*30371Skarels cyldmba(reg, value) 1067*30371Skarels register caddr_t 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); 1091*30371Skarels 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 */ 111125979Ssam cydump(dev) 111225979Ssam dev_t dev; 111325979Ssam { 111425979Ssam register struct cy_softc *cy; 111525979Ssam register int bs, num, start; 111625979Ssam register caddr_t addr; 111730294Ssam int unit = CYUNIT(dev), error; 111825979Ssam 111925979Ssam if (unit >= NCY || cyminfo[unit] == 0 || 112025979Ssam (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) 112125979Ssam return (ENXIO); 112225979Ssam if (cywait(&cy->cy_ccb)) 112325979Ssam return (EFAULT); 112425979Ssam #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) 112530294Ssam addr = phys(cyminfo[unit]->um_addr); 112625979Ssam num = maxfree, start = NBPG*2; 112725979Ssam while (num > 0) { 112825979Ssam bs = num > btoc(CYMAXIO) ? btoc(CYMAXIO) : num; 112925979Ssam error = cydwrite(cy, start, bs, addr); 113025979Ssam if (error) 113125979Ssam return (error); 113225979Ssam start += bs, num -= bs; 113325979Ssam } 113425979Ssam cyweof(cy, addr); 113525979Ssam cyweof(cy, addr); 113625979Ssam uncache(&cy->cy_tpb); 113725979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 113825979Ssam return (EIO); 113925979Ssam cyrewind(cy, addr); 114025979Ssam return (0); 114125979Ssam } 114225979Ssam 114325979Ssam cydwrite(cy, pf, npf, addr) 114425979Ssam register struct cy_softc *cy; 114525979Ssam int pf, npf; 114625979Ssam caddr_t addr; 114725979Ssam { 114825979Ssam 114925979Ssam cy->cy_tpb.tpcmd = CY_WCOM; 115025979Ssam cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; 115125979Ssam cy->cy_tpb.tpstatus = 0; 115225979Ssam cy->cy_tpb.tpsize = htoms(npf*NBPG); 115325979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 115425979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); 115525979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 115625979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 115725979Ssam CY_GO(addr); 115825979Ssam if (cywait(&cy->cy_ccb)) 115925979Ssam return (EFAULT); 116025979Ssam uncache(&cy->cy_tpb); 116125979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 116225979Ssam return (EIO); 116325979Ssam return (0); 116425979Ssam } 116525979Ssam 116625979Ssam cyweof(cy, addr) 116725979Ssam register struct cy_softc *cy; 116825979Ssam caddr_t addr; 116925979Ssam { 117025979Ssam 117125979Ssam cy->cy_tpb.tpcmd = CY_WEOF; 117225979Ssam cy->cy_tpb.tpcount = htoms(1); 117325979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 117425979Ssam CY_GO(addr); 117525979Ssam (void) cywait(&cy->cy_ccb); 117625979Ssam } 117725979Ssam 117825979Ssam cyrewind(cy, addr) 117925979Ssam register struct cy_softc *cy; 118025979Ssam caddr_t addr; 118125979Ssam { 118225979Ssam 118325979Ssam cy->cy_tpb.tpcmd = CY_REW; 118425979Ssam cy->cy_tpb.tpcount = htoms(1); 118525979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 118625979Ssam CY_GO(addr); 118725979Ssam (void) cywait(&cy->cy_ccb); 118825979Ssam } 118924000Ssam #endif 1190