1*29952Skarels /* cy.c 1.7 86/11/03 */ 224000Ssam 325979Ssam #include "yc.h" 425675Ssam #if NCY > 0 524000Ssam /* 625675Ssam * Cipher Tapemaster driver. 724000Ssam */ 825675Ssam int cydebug = 0; 925979Ssam #define dlog if (cydebug) log 1024000Ssam 1125675Ssam #include "param.h" 1225675Ssam #include "systm.h" 1325675Ssam #include "vm.h" 1425675Ssam #include "buf.h" 1525675Ssam #include "file.h" 1625675Ssam #include "dir.h" 1725675Ssam #include "user.h" 1825675Ssam #include "proc.h" 1925675Ssam #include "signal.h" 2025675Ssam #include "uio.h" 2125675Ssam #include "ioctl.h" 2225675Ssam #include "mtio.h" 2325675Ssam #include "errno.h" 2425675Ssam #include "cmap.h" 2525979Ssam #include "kernel.h" 2625979Ssam #include "syslog.h" 2724000Ssam 28*29952Skarels #include "../tahoe/cpu.h" 29*29952Skarels #include "../tahoe/mtpr.h" 30*29952Skarels #include "../tahoe/pte.h" 31*29952Skarels 3225675Ssam #include "../tahoevba/vbavar.h" 3325979Ssam #define CYERROR 3425675Ssam #include "../tahoevba/cyreg.h" 3524000Ssam 3625979Ssam /* 3725979Ssam * There is a ccybuf per tape controller. 3825979Ssam * It is used as the token to pass to the internal routines 3925979Ssam * to execute tape ioctls, and also acts as a lock on the slaves 4025979Ssam * on the controller, since there is only one per controller. 4125979Ssam * In particular, when the tape is rewinding on close we release 4225979Ssam * the user process but any further attempts to use the tape drive 4325979Ssam * before the rewind completes will hang waiting for ccybuf. 4425979Ssam */ 4525979Ssam struct buf ccybuf[NCY]; 4624000Ssam 4725979Ssam /* 4825979Ssam * Raw tape operations use rcybuf. The driver notices when 4925979Ssam * rcybuf is being used and allows the user program to contine 5025979Ssam * after errors and read records not of the standard length. 5125979Ssam */ 5225979Ssam struct buf rcybuf[NCY]; 5324000Ssam 5425979Ssam int cyprobe(), cyslave(), cyattach(); 5525979Ssam struct buf ycutab[NYC]; 5625979Ssam short yctocy[NYC]; 5725675Ssam struct vba_ctlr *cyminfo[NCY]; 5825979Ssam struct vba_device *ycdinfo[NYC]; 5925857Ssam long cystd[] = { 0 }; 6025857Ssam struct vba_driver cydriver = 6125979Ssam { cyprobe, cyslave, cyattach, 0, cystd, "yc", ycdinfo, "cy", cyminfo }; 6224000Ssam 6325979Ssam /* bits in minor device */ 6425979Ssam #define YCUNIT(dev) (minor(dev)&03) 6525979Ssam #define CYUNIT(dev) (yctocy[YCUNIT(dev)]) 6625979Ssam #define T_NOREWIND 0x04 6725979Ssam #define T_1600BPI 0x08 6825979Ssam #define T_3200BPI 0x10 6925979Ssam 7025979Ssam #define INF 1000000L /* close to infinity */ 7125979Ssam #define CYMAXIO (32*NBPG) /* max i/o size */ 7225979Ssam 7324000Ssam /* 7425979Ssam * Software state and shared command areas per controller. 7525979Ssam * 7625979Ssam * The i/o buffer must be defined statically to insure 7725979Ssam * it's address will fit in 20-bits (YECH!!!!!!!!!!!!!!) 7824000Ssam */ 7925979Ssam struct cy_softc { 8025979Ssam struct pte *cy_map; /* pte's for mapped buffer i/o */ 8125979Ssam caddr_t cy_utl; /* mapped virtual address */ 8225979Ssam int cy_bs; /* controller's buffer size */ 8325979Ssam char cy_buf[CYMAXIO];/* intermediate buffer */ 8425979Ssam struct cyscp *cy_scp; /* system configuration block address */ 8525979Ssam struct cyccb cy_ccb; /* channel control block */ 8625979Ssam struct cyscb cy_scb; /* system configuration block */ 8725979Ssam struct cytpb cy_tpb; /* tape parameter block */ 8825979Ssam struct cytpb cy_nop; /* nop parameter block for cyintr */ 8925979Ssam } cy_softc[NCY]; 9024000Ssam 9125979Ssam /* 9225979Ssam * Software state per tape transport. 9325979Ssam */ 9425979Ssam struct yc_softc { 9525979Ssam char yc_openf; /* lock against multiple opens */ 9625979Ssam char yc_lastiow; /* last operation was a write */ 9725979Ssam short yc_tact; /* timeout is active */ 9825979Ssam long yc_timo; /* time until timeout expires */ 9925979Ssam u_short yc_control; /* copy of last tpcb.tpcontrol */ 10025979Ssam u_short yc_status; /* copy of last tpcb.tpstatus */ 10125979Ssam u_short yc_resid; /* copy of last bc */ 10225979Ssam u_short yc_dens; /* prototype control word with density info */ 10325979Ssam struct tty *yc_ttyp; /* user's tty for errors */ 10425979Ssam daddr_t yc_blkno; /* block number, for block device tape */ 10525979Ssam daddr_t yc_nxrec; /* position of end of tape, if known */ 10625979Ssam } yc_softc[NYC]; 10724000Ssam 10824000Ssam /* 10925979Ssam * States for vm->um_tab.b_active, the per controller state flag. 11025979Ssam * This is used to sequence control in the driver. 11124000Ssam */ 11225979Ssam #define SSEEK 1 /* seeking */ 11325979Ssam #define SIO 2 /* doing seq i/o */ 11425979Ssam #define SCOM 3 /* sending control command */ 11525979Ssam #define SREW 4 /* sending a rewind */ 11625979Ssam #define SERASE 5 /* erase inter-record gap */ 11725979Ssam #define SERASED 6 /* erased inter-record gap */ 11824000Ssam 11925979Ssam /* there's no way to figure these out dynamically? -- yech */ 12025979Ssam struct cyscp *cyscp[] = 12125979Ssam { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; 12225979Ssam #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) 12325979Ssam 12425857Ssam cyprobe(reg, vm) 12525857Ssam caddr_t reg; 12625857Ssam struct vba_ctlr *vm; 12725675Ssam { 12825857Ssam register br, cvec; /* must be r12, r11 */ 12925979Ssam struct cy_softc *cy; 13025675Ssam 13125857Ssam if (badcyaddr(reg+1)) 13225675Ssam return (0); 13325979Ssam if (vm->um_ctlr > NCYSCP || cyscp[vm->um_ctlr] == 0) /* XXX */ 13425979Ssam return (0); /* XXX */ 13525979Ssam cy_softc[vm->um_ctlr].cy_scp = cyscp[vm->um_ctlr]; /* XXX */ 13625979Ssam /* 13725979Ssam * Tapemaster controller must have interrupt handler 13825979Ssam * disable interrupt, so we'll just kludge things 13925979Ssam * (stupid multibus non-vectored interrupt crud). 14025979Ssam */ 14125979Ssam br = 0x13, cvec = 0x80; /* XXX */ 14225979Ssam return (sizeof (struct cyccb)); 14325675Ssam } 14425675Ssam 14524000Ssam /* 14625857Ssam * Check to see if a drive is attached to a controller. 14725857Ssam * Since we can only tell that a drive is there if a tape is loaded and 14825857Ssam * the drive is placed online, we always indicate the slave is present. 14924000Ssam */ 15025857Ssam cyslave(vi, addr) 15125857Ssam struct vba_device *vi; 15225857Ssam caddr_t addr; 15324000Ssam { 15425857Ssam 15525857Ssam #ifdef lint 15625857Ssam vi = vi; addr = addr; 15725857Ssam #endif 15825857Ssam return (1); 15925857Ssam } 16025857Ssam 16125857Ssam cyattach(vi) 16225857Ssam struct vba_device *vi; 16325857Ssam { 16425979Ssam register struct cy_softc *cy; 16525979Ssam int ctlr = vi->ui_mi->um_ctlr; 16625857Ssam 16725979Ssam yctocy[vi->ui_unit] = ctlr; 16825979Ssam cy = &cy_softc[ctlr]; 16925979Ssam if (cy->cy_bs == 0 && cyinit(ctlr)) { 17025979Ssam uncache(&cy->cy_tpb.tpcount); 17125979Ssam cy->cy_bs = htoms(cy->cy_tpb.tpcount); 17225979Ssam printf("cy%d: %dkb buffer\n", ctlr, cy->cy_bs/1024); 17325979Ssam /* 17425979Ssam * Setup nop parameter block for clearing interrupts. 17525979Ssam */ 17625979Ssam cy->cy_nop.tpcmd = CY_NOP; 17725979Ssam cy->cy_nop.tpcontrol = 0; 17825979Ssam /* 17925979Ssam * Allocate page tables. 18025979Ssam */ 18125979Ssam vbmapalloc(btoc(CYMAXIO)+1, &cy->cy_map, &cy->cy_utl); 18225857Ssam } 18325857Ssam } 18425857Ssam 18525857Ssam /* 18625857Ssam * Initialize the controller after a controller reset or 18725857Ssam * during autoconfigure. All of the system control blocks 18825857Ssam * are initialized and the controller is asked to configure 18925857Ssam * itself for later use. 19025857Ssam */ 19125979Ssam cyinit(ctlr) 19225979Ssam int ctlr; 19325857Ssam { 19425979Ssam register struct cy_softc *cy = &cy_softc[ctlr]; 19525979Ssam register caddr_t addr = cyminfo[ctlr]->um_addr; 19625675Ssam register int *pte; 19724000Ssam 19824000Ssam /* 19925675Ssam * Initialize the system configuration pointer. 20024000Ssam */ 20125675Ssam /* make kernel writable */ 20225979Ssam pte = (int *)vtopte((struct proc *)0, btop(cy->cy_scp)); 20325675Ssam *pte &= ~PG_PROT; *pte |= PG_KW; 20425979Ssam mtpr(TBIS, cy->cy_scp); 20525675Ssam /* load the correct values in the scp */ 20625979Ssam cy->cy_scp->csp_buswidth = CSP_16BITS; 20725979Ssam cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); 20825675Ssam /* put it back to read-only */ 20925675Ssam *pte &= ~PG_PROT; *pte |= PG_KR; 21025979Ssam mtpr(TBIS, cy->cy_scp); 21125675Ssam 21224000Ssam /* 21325675Ssam * Init system configuration block. 21424000Ssam */ 21525979Ssam cy->cy_scb.csb_fixed = 0x3; 21625675Ssam /* set pointer to the channel control block */ 21725979Ssam cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); 21825675Ssam 21924000Ssam /* 22025675Ssam * Initialize the chanel control block. 22124000Ssam */ 22225979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 22325979Ssam cy->cy_ccb.cbgate = GATE_OPEN; 22425675Ssam /* set pointer to the tape parameter block */ 22525979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 22625675Ssam 22724000Ssam /* 22825979Ssam * Issue a nop cmd and get the internal buffer size for buffered i/o. 22924000Ssam */ 23025979Ssam cy->cy_tpb.tpcmd = CY_NOP; 23125979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 23225979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 23325979Ssam CY_GO(addr); 23425979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 23525979Ssam uncache(&cy->cy_tpb.tpstatus); 23625979Ssam printf("cy%d: timeout or err during init, status=%b\n", ctlr, 23725979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 23825675Ssam return (0); 23925675Ssam } 24025979Ssam cy->cy_tpb.tpcmd = CY_CONFIG; 24125979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 24225979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 24325979Ssam CY_GO(addr); 24425979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 24525979Ssam uncache(&cy->cy_tpb.tpstatus); 24625979Ssam printf("cy%d: configuration failure, status=%b\n", ctlr, 24725979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 24825675Ssam return (0); 24925675Ssam } 25025675Ssam return (1); 25124000Ssam } 25224000Ssam 25325979Ssam int cytimer(); 25425979Ssam /* 25525979Ssam * Open the device. Tapes are unique open 25625979Ssam * devices, so we refuse if it is already open. 25725979Ssam * We also check that a tape is available, and 25825979Ssam * don't block waiting here; if you want to wait 25925979Ssam * for a tape you should timeout in user code. 26025979Ssam */ 26125675Ssam cyopen(dev, flag) 26225979Ssam dev_t dev; 26325675Ssam register int flag; 26425675Ssam { 26525979Ssam register int ycunit; 26625979Ssam register struct vba_device *vi; 26725979Ssam register struct yc_softc *yc; 26825979Ssam int s; 26925675Ssam 27025979Ssam ycunit = YCUNIT(dev); 27125979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 27225675Ssam return (ENXIO); 27325979Ssam if ((yc = &yc_softc[ycunit])->yc_openf) 27425979Ssam return (EBUSY); 27525979Ssam #define PACKUNIT(vi) \ 27625979Ssam (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) 27725979Ssam /* no way to select density */ 27825979Ssam yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; 27925979Ssam cycommand(dev, CY_SENSE, 1); 28025979Ssam if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ 28125979Ssam uprintf("yc%d: not online\n", ycunit); 28225675Ssam return (ENXIO); 28325675Ssam } 28425979Ssam if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { 28525979Ssam uprintf("yc%d: no write ring\n", ycunit); 28625675Ssam return (ENXIO); 28725675Ssam } 28825979Ssam yc->yc_openf = 1; 28925979Ssam yc->yc_blkno = (daddr_t)0; 29025979Ssam yc->yc_nxrec = INF; 29125979Ssam yc->yc_lastiow = 0; 29225979Ssam yc->yc_ttyp = u.u_ttyp; 29325979Ssam s = splclock(); 29425979Ssam if (yc->yc_tact == 0) { 29525979Ssam yc->yc_timo = INF; 29625979Ssam yc->yc_tact = 1; 29725979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 29825675Ssam } 29925979Ssam splx(s); 30025675Ssam return (0); 30125675Ssam } 30225675Ssam 30325979Ssam /* 30425979Ssam * Close tape device. 30525979Ssam * 30625979Ssam * If tape was open for writing or last operation was a write, 30725979Ssam * then write two EOF's and backspace over the last one. 30825979Ssam * Unless this is a non-rewinding special file, rewind the tape. 30925979Ssam * Make the tape available to others. 31025979Ssam */ 31125675Ssam cyclose(dev, flag) 31225979Ssam dev_t dev; 31325979Ssam register int flag; 31425675Ssam { 31525979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 31625675Ssam 31725979Ssam if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { 31825979Ssam cycommand(dev, CY_WEOF, 2); 31925979Ssam cycommand(dev, CY_SREV, 1); 32025675Ssam } 32125979Ssam if ((minor(dev)&T_NOREWIND) == 0) 32225979Ssam /* 32325979Ssam * 0 count means don't hang waiting for rewind complete 32425979Ssam * rather ccybuf stays busy until the operation completes 32525979Ssam * preventing further opens from completing by preventing 32625979Ssam * a CY_SENSE from completing. 32725979Ssam */ 32825979Ssam cycommand(dev, CY_REW, 0); 32925979Ssam yc->yc_openf = 0; 33025675Ssam } 33125675Ssam 33224000Ssam /* 33325979Ssam * Execute a command on the tape drive a specified number of times. 33424000Ssam */ 33525979Ssam cycommand(dev, com, count) 33625979Ssam dev_t dev; 33725979Ssam int com, count; 33824000Ssam { 33925675Ssam register int unit = CYUNIT(dev); 34025979Ssam register struct buf *bp; 34125675Ssam int s; 34225675Ssam 34325979Ssam bp = &ccybuf[CYUNIT(dev)]; 34425675Ssam s = spl3(); 34525979Ssam dlog(LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", 34625979Ssam dev, com, count, bp->b_flags); 34725979Ssam while (bp->b_flags&B_BUSY) { 34825979Ssam /* 34925979Ssam * This special check is because B_BUSY never 35025979Ssam * gets cleared in the non-waiting rewind case. 35125979Ssam */ 35225979Ssam if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 35325979Ssam break; 35425979Ssam bp->b_flags |= B_WANTED; 35525979Ssam sleep((caddr_t)bp, PRIBIO); 35625675Ssam } 35725979Ssam bp->b_flags = B_BUSY|B_READ; 35825675Ssam splx(s); 35925979Ssam bp->b_dev = dev; 36025979Ssam bp->b_repcnt = count; 36125979Ssam bp->b_command = com; 36225979Ssam bp->b_blkno = 0; 36325979Ssam cystrategy(bp); 36425979Ssam /* 36525979Ssam * In case of rewind from close; don't wait. 36625979Ssam * This is the only case where count can be 0. 36725979Ssam */ 36825979Ssam if (count == 0) 36925979Ssam return; 37025979Ssam iowait(bp); 37125979Ssam if (bp->b_flags&B_WANTED) 37225979Ssam wakeup((caddr_t)bp); 37325979Ssam bp->b_flags &= B_ERROR; 37424000Ssam } 37524000Ssam 37625675Ssam cystrategy(bp) 37725675Ssam register struct buf *bp; 37825675Ssam { 37925979Ssam int ycunit = YCUNIT(bp->b_dev); 38025979Ssam register struct vba_ctlr *vm; 38125979Ssam register struct buf *dp; 38225675Ssam int s; 38325675Ssam 38425979Ssam /* 38525979Ssam * Put transfer at end of unit queue. 38625979Ssam */ 38725979Ssam dlog(LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command); 38825979Ssam dp = &ycutab[ycunit]; 38925675Ssam bp->av_forw = NULL; 39025979Ssam vm = ycdinfo[ycunit]->ui_mi; 39125979Ssam /* BEGIN GROT */ 39225979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) { 39325979Ssam if (bp->b_bcount > CYMAXIO) { 39425979Ssam uprintf("cy%d: i/o size too large\n", vm->um_ctlr); 39525979Ssam bp->b_error = EIO; 39625979Ssam bp->b_resid = bp->b_bcount; 39725979Ssam bp->b_flags |= B_ERROR; 39825675Ssam iodone(bp); 39925675Ssam return; 40025675Ssam } 40125979Ssam vbasetup(bp, CYMAXIO); 40224000Ssam } 40325979Ssam /* END GROT */ 40425675Ssam s = spl3(); 40525979Ssam if (dp->b_actf == NULL) { 40625979Ssam dp->b_actf = bp; 40725979Ssam /* 40825979Ssam * Transport not already active... 40925979Ssam * put at end of controller queue. 41025979Ssam */ 41125979Ssam dp->b_forw = NULL; 41225979Ssam if (vm->um_tab.b_actf == NULL) 41325979Ssam vm->um_tab.b_actf = dp; 41425979Ssam else 41525979Ssam vm->um_tab.b_actl->b_forw = dp; 41625979Ssam } else 41725979Ssam dp->b_actl->av_forw = bp; 41825979Ssam dp->b_actl = bp; 41925979Ssam /* 42025979Ssam * If the controller is not busy, get it going. 42125979Ssam */ 42225979Ssam if (vm->um_tab.b_active == 0) 42325979Ssam cystart(vm); 42424000Ssam splx(s); 42524000Ssam } 42624000Ssam 42724000Ssam /* 42825979Ssam * Start activity on a cy controller. 42924000Ssam */ 43025979Ssam cystart(vm) 43125979Ssam register struct vba_ctlr *vm; 43224000Ssam { 43325979Ssam register struct buf *bp, *dp; 43425979Ssam register struct yc_softc *yc; 43525979Ssam register struct cy_softc *cy; 43625979Ssam register struct vba_device *vi; 43725979Ssam int ycunit; 43825979Ssam daddr_t blkno; 43924000Ssam 44025979Ssam dlog(LOG_INFO, "cystart()\n"); 44125979Ssam /* 44225979Ssam * Look for an idle transport on the controller. 44325979Ssam */ 44425979Ssam loop: 44525979Ssam if ((dp = vm->um_tab.b_actf) == NULL) 44625675Ssam return; 44725979Ssam if ((bp = dp->b_actf) == NULL) { 44825979Ssam vm->um_tab.b_actf = dp->b_forw; 44925979Ssam goto loop; 45025675Ssam } 45125979Ssam ycunit = YCUNIT(bp->b_dev); 45225979Ssam yc = &yc_softc[ycunit]; 45325979Ssam cy = &cy_softc[CYUNIT(bp->b_dev)]; 45425979Ssam /* 45525979Ssam * Default is that last command was NOT a write command; 45625979Ssam * if we do a write command we will notice this in cyintr(). 45725979Ssam */ 45825979Ssam yc->yc_lastiow = 0; 45925979Ssam if (yc->yc_openf < 0 || 46025979Ssam (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { 46125979Ssam /* 46225979Ssam * Have had a hard error on a non-raw tape 46325979Ssam * or the tape unit is now unavailable (e.g. 46425979Ssam * taken off line). 46525979Ssam */ 46625979Ssam dlog(LOG_INFO, "openf %d command %x status %b\n", 46725979Ssam yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS); 46825979Ssam bp->b_flags |= B_ERROR; 46925979Ssam goto next; 47025675Ssam } 47125979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { 47225979Ssam /* 47325979Ssam * Execute control operation with the specified count. 47425979Ssam * 47525979Ssam * Set next state; give 5 minutes to complete 47625979Ssam * rewind or file mark search, or 10 seconds per 47725979Ssam * iteration (minimum 60 seconds and max 5 minutes) 47825979Ssam * to complete other ops. 47925979Ssam */ 48025979Ssam if (bp->b_command == CY_REW) { 48125979Ssam vm->um_tab.b_active = SREW; 48225979Ssam yc->yc_timo = 5*60; 48325979Ssam } else { 48425979Ssam vm->um_tab.b_active = SCOM; 48525979Ssam yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); 48625979Ssam } 48725979Ssam cy->cy_tpb.tprec = htoms(bp->b_repcnt); 48825979Ssam goto dobpcmd; 48924000Ssam } 49025979Ssam /* 49125979Ssam * The following checks handle boundary cases for operation 49225979Ssam * on no-raw tapes. On raw tapes the initialization of 49325979Ssam * yc->yc_nxrec by cyphys causes them to be skipped normally 49425979Ssam * (except in the case of retries). 49525979Ssam */ 49625979Ssam if (bdbtofsb(bp->b_blkno) > yc->yc_nxrec) { 49725979Ssam /* 49825979Ssam * Can't read past known end-of-file. 49925979Ssam */ 50025979Ssam bp->b_flags |= B_ERROR; 50125979Ssam bp->b_error = ENXIO; 50225979Ssam goto next; 50324000Ssam } 50425979Ssam if (bdbtofsb(bp->b_blkno) == yc->yc_nxrec && bp->b_flags&B_READ) { 50525979Ssam /* 50625979Ssam * Reading at end of file returns 0 bytes. 50725979Ssam */ 50825979Ssam bp->b_resid = bp->b_bcount; 50925979Ssam clrbuf(bp); 51025979Ssam goto next; 51124000Ssam } 51225979Ssam if ((bp->b_flags&B_READ) == 0) 51325979Ssam /* 51425979Ssam * Writing sets EOF. 51525979Ssam */ 51625979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno) + 1; 51725979Ssam if ((blkno = yc->yc_blkno) == bdbtofsb(bp->b_blkno)) { 51825979Ssam caddr_t addr; 51925979Ssam int cmd; 52025675Ssam 52125979Ssam /* 52225979Ssam * Choose the appropriate i/o command based on the 52325979Ssam * transfer size and the controller's internal buffer. 52425979Ssam * If we're retrying a read on a raw device because 52525979Ssam * the original try was a buffer request which failed 52625979Ssam * due to a record length error, then we force the use 52725979Ssam * of the raw controller read (YECH!!!!). 52825979Ssam */ 52925979Ssam if (bp->b_flags&B_READ) { 53025979Ssam if (bp->b_bcount > cy->cy_bs || bp->b_errcnt) 53125979Ssam cmd = CY_RCOM; 53225979Ssam else 53325979Ssam cmd = CY_BRCOM; 53425979Ssam } else { 53525979Ssam /* 53625979Ssam * On write error retries erase the 53725979Ssam * inter-record gap before rewriting. 53825979Ssam */ 53925979Ssam if (vm->um_tab.b_errcnt && 54025979Ssam vm->um_tab.b_active != SERASED) { 54125979Ssam vm->um_tab.b_active = SERASE; 54225979Ssam bp->b_command = CY_ERASE; 54325979Ssam yc->yc_timo = 60; 54425979Ssam goto dobpcmd; 54525675Ssam } 54625979Ssam cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; 54725675Ssam } 54825979Ssam vm->um_tab.b_active = SIO; 54925979Ssam addr = (caddr_t)vbastart(bp, cy->cy_buf, 55025979Ssam (long *)cy->cy_map, cy->cy_utl); 55125979Ssam cy->cy_tpb.tpcmd = cmd; 55225979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 55325979Ssam if (cmd == CY_RCOM || cmd == CY_WCOM) 55425979Ssam cy->cy_tpb.tpcontrol |= CYCW_LOCK; 55525979Ssam cy->cy_tpb.tpstatus = 0; 55625979Ssam cy->cy_tpb.tpcount = 0; 55725979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); 55825979Ssam cy->cy_tpb.tprec = 0; 55925979Ssam cy->cy_tpb.tpsize = htoms(bp->b_bcount); 56025979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 56125979Ssam do 56225979Ssam uncache(&cy->cy_ccb.cbgate); 56325979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 56425979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 56525979Ssam cy->cy_ccb.cbcw = CBCW_IE; 56625979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 56725979Ssam dlog(LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", 56825979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 56925979Ssam htoms(cy->cy_tpb.tpsize)); 57025979Ssam CY_GO(vm->um_addr); 57125979Ssam return; 57224000Ssam } 57325979Ssam /* 57425979Ssam * Tape positioned incorrectly; set to seek forwards 57525979Ssam * or backwards to the correct spot. This happens 57625979Ssam * for raw tapes only on error retries. 57725979Ssam */ 57825979Ssam vm->um_tab.b_active = SSEEK; 57925979Ssam if (blkno < bdbtofsb(bp->b_blkno)) { 58025979Ssam bp->b_command = CY_SFORW; 58125979Ssam cy->cy_tpb.tprec = htoms(bdbtofsb(bp->b_blkno) - blkno); 58225979Ssam } else { 58325979Ssam bp->b_command = CY_SREV; 58425979Ssam cy->cy_tpb.tprec = htoms(blkno - bdbtofsb(bp->b_blkno)); 58524000Ssam } 58625979Ssam yc->yc_timo = imin(imax(10 * htoms(cy->cy_tpb.tprec), 60), 5*60); 58725979Ssam dobpcmd: 58825979Ssam /* 58925979Ssam * Do the command in bp. Reverse direction commands 59025979Ssam * are indicated by having CYCW_REV or'd into their 59125979Ssam * value. For these we must set the appropriate bit 59225979Ssam * in the control field. 59325979Ssam */ 59425979Ssam if (bp->b_command&CYCW_REV) { 59525979Ssam cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; 59625979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; 59725979Ssam } else { 59825979Ssam cy->cy_tpb.tpcmd = bp->b_command; 59925979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 60024000Ssam } 60125979Ssam cy->cy_tpb.tpstatus = 0; 60225979Ssam cy->cy_tpb.tpcount = 0; 60325979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 60425979Ssam do 60525979Ssam uncache(&cy->cy_ccb.cbgate); 60625979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 60725979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 60825979Ssam cy->cy_ccb.cbcw = CBCW_IE; 60925979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 61025979Ssam dlog(LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", 61125979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 61225979Ssam htoms(cy->cy_tpb.tprec)); 61325979Ssam CY_GO(vm->um_addr); 61425979Ssam return; 61525979Ssam next: 61625979Ssam /* 61725979Ssam * Done with this operation due to error or the 61825979Ssam * fact that it doesn't do anything. Release VERSAbus 61925979Ssam * resource (if any), dequeue the transfer and continue 62025979Ssam * processing this slave. 62125979Ssam */ 62225979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) 62325979Ssam vbadone(bp, cy->cy_buf, (long *)cy->cy_map, cy->cy_utl); 62425979Ssam vm->um_tab.b_errcnt = 0; 62525979Ssam dp->b_actf = bp->av_forw; 62625979Ssam iodone(bp); 62725979Ssam goto loop; 62825675Ssam } 62925675Ssam 63025675Ssam /* 63125979Ssam * Cy interrupt routine. 63225675Ssam */ 63325979Ssam cyintr(cipher) 63425979Ssam int cipher; 63525675Ssam { 63625979Ssam struct buf *dp; 63724000Ssam register struct buf *bp; 63825979Ssam register struct vba_ctlr *vm = cyminfo[cipher]; 63925979Ssam register struct cy_softc *cy; 64025979Ssam register struct yc_softc *yc; 64125979Ssam int cyunit, err; 64225979Ssam register state; 64324000Ssam 64425979Ssam dlog(LOG_INFO, "cyintr(%d)\n", cipher); 64525979Ssam /* 64625979Ssam * First, turn off the interrupt from the controller 64725979Ssam * (device uses Multibus non-vectored interrupts...yech). 64825979Ssam */ 64925979Ssam cy = &cy_softc[vm->um_ctlr]; 65025979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 65125979Ssam cyldmba(cy->cy_ccb.cbtpb, &cy->cy_nop); 65225979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 65325979Ssam CY_GO(vm->um_addr); 65425979Ssam if ((dp = vm->um_tab.b_actf) == NULL) { 65525979Ssam dlog(LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr); 65624000Ssam return; 65724000Ssam } 65825979Ssam bp = dp->b_actf; 65925979Ssam cyunit = CYUNIT(bp->b_dev); 66025979Ssam cy = &cy_softc[cyunit]; 66125979Ssam cyuncachetpb(cy); 66225979Ssam /* 66325984Ssam * If last command was a rewind and tape is 66425984Ssam * still moving, wait for the operation to complete. 66525979Ssam */ 66625979Ssam if (vm->um_tab.b_active == SREW) { 66725979Ssam vm->um_tab.b_active = SCOM; 66825979Ssam if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { 66925979Ssam yc->yc_timo = 5*60; /* 5 minutes */ 67025979Ssam return; 67124000Ssam } 67224000Ssam } 67325979Ssam /* 67425979Ssam * An operation completed...record status. 67525979Ssam */ 67625979Ssam yc = &yc_softc[YCUNIT(bp->b_dev)]; 67725979Ssam yc->yc_timo = INF; 67825979Ssam yc->yc_control = cy->cy_tpb.tpcontrol; 67925979Ssam yc->yc_status = cy->cy_tpb.tpstatus; 68025979Ssam yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 68125979Ssam dlog(LOG_INFO, "cmd %x control %b status %b resid %d\n", 68225979Ssam cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, 68325979Ssam yc->yc_status, CYS_BITS, yc->yc_resid); 68425979Ssam if ((bp->b_flags&B_READ) == 0) 68525979Ssam yc->yc_lastiow = 1; 68625979Ssam state = vm->um_tab.b_active; 68725979Ssam vm->um_tab.b_active = 0; 68825979Ssam /* 68925979Ssam * Check for errors. 69025979Ssam */ 69125979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) { 69225979Ssam err = cy->cy_tpb.tpstatus&CYS_ERR; 69325979Ssam dlog(LOG_INFO, "error %d\n", err); 69425979Ssam /* 69525979Ssam * If we hit the end of tape file, update our position. 69625979Ssam */ 69725979Ssam if (err == CYER_FM) { 69825979Ssam yc->yc_status |= CYS_FM; 69925979Ssam state = SCOM; /* force completion */ 70025979Ssam cyseteof(bp); /* set blkno and nxrec */ 70125979Ssam goto opdone; 70225979Ssam } 70325979Ssam /* 70425979Ssam * Fix up errors which occur due to backspacing over 70525979Ssam * the beginning of the tape. 70625979Ssam */ 70725979Ssam if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { 70825979Ssam yc->yc_status |= CYS_BOT; 70925979Ssam goto ignoreerr; 71025979Ssam } 71125979Ssam /* 71225979Ssam * If we were reading raw tape and the only error was that the 71325979Ssam * record was too long, then we don't consider this an error. 71425979Ssam */ 71525979Ssam if (bp == &rcybuf[cyunit] && (bp->b_flags&B_READ) && 71625979Ssam err == CYER_STROBE) { 71725979Ssam /* 71825979Ssam * Retry reads once with the command changed to 71925979Ssam * a raw read (if possible). Setting b_errcnt 72025979Ssam * here causes cystart (above) to force a CY_RCOM. 72125979Ssam */ 72225979Ssam if (bp->b_errcnt++ != 0) 72325979Ssam goto ignoreerr; 72425979Ssam yc->yc_blkno++; 72525979Ssam goto opcont; 72625979Ssam } 72725979Ssam /* 72825979Ssam * If error is not hard, and this was an i/o operation 72925979Ssam * retry up to 8 times. 73025979Ssam */ 73125984Ssam if (((1<<err)&CYER_SOFT) && state == SIO) { 73225979Ssam if (++vm->um_tab.b_errcnt < 7) { 73325979Ssam yc->yc_blkno++; 73425979Ssam goto opcont; 73525979Ssam } 73625979Ssam } else 73725979Ssam /* 73825979Ssam * Hard or non-i/o errors on non-raw tape 73925979Ssam * cause it to close. 74025979Ssam */ 74125979Ssam if (yc->yc_openf>0 && bp != &rcybuf[cyunit]) 74225979Ssam yc->yc_openf = -1; 74325979Ssam /* 74425979Ssam * Couldn't recover from error. 74525979Ssam */ 74625979Ssam tprintf(yc->yc_ttyp, 74725979Ssam "yc%d: hard error bn%d status=%b", YCUNIT(bp->b_dev), 74825979Ssam bp->b_blkno, yc->yc_status, CYS_BITS); 74925979Ssam if (err < NCYERROR) 75025979Ssam tprintf(yc->yc_ttyp, ", %s", cyerror[err]); 75125979Ssam tprintf(yc->yc_ttyp, "\n"); 75225979Ssam bp->b_flags |= B_ERROR; 75325979Ssam goto opdone; 75424000Ssam } 75525979Ssam /* 75625979Ssam * Advance tape control FSM. 75725979Ssam */ 75825979Ssam ignoreerr: 75925979Ssam /* 76025979Ssam * If we hit a tape mark update our position. 76125979Ssam */ 76225979Ssam if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { 76325979Ssam cyseteof(bp); 76425979Ssam goto opdone; 76525675Ssam } 76625979Ssam switch (state) { 76724000Ssam 76825979Ssam case SIO: 76925979Ssam /* 77025979Ssam * Read/write increments tape block number. 77125979Ssam */ 77225979Ssam yc->yc_blkno++; 77325979Ssam goto opdone; 77424000Ssam 77525979Ssam case SCOM: 77625979Ssam /* 77725979Ssam * For forward/backward space record update current position. 77825979Ssam */ 77925979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) switch (bp->b_command) { 78024000Ssam 78125979Ssam case CY_SFORW: 78225979Ssam yc->yc_blkno -= bp->b_repcnt; 78325979Ssam break; 78424000Ssam 78525979Ssam case CY_SREV: 78625979Ssam yc->yc_blkno += bp->b_repcnt; 78725979Ssam break; 78824000Ssam } 78925979Ssam goto opdone; 79025979Ssam 79125979Ssam case SSEEK: 79225979Ssam yc->yc_blkno = bdbtofsb(bp->b_blkno); 79325979Ssam goto opcont; 79424000Ssam 79525979Ssam case SERASE: 79625979Ssam /* 79725979Ssam * Completed erase of the inter-record gap due to a 79825979Ssam * write error; now retry the write operation. 79925979Ssam */ 80025979Ssam vm->um_tab.b_active = SERASED; 80125979Ssam goto opcont; 80224000Ssam } 80325675Ssam 80425979Ssam opdone: 80525979Ssam /* 80625979Ssam * Reset error count and remove from device queue. 80725979Ssam */ 80825979Ssam vm->um_tab.b_errcnt = 0; 80925979Ssam dp->b_actf = bp->av_forw; 81025979Ssam /* 81125979Ssam * Save resid and release resources. 81225979Ssam */ 81325979Ssam bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 81425979Ssam if (bp == &rcybuf[CYUNIT(bp->b_dev)]) 81525979Ssam vbadone(bp, cy->cy_buf, (long *)cy->cy_map, cy->cy_utl); 81625979Ssam iodone(bp); 81725979Ssam /* 81825979Ssam * Circulate slave to end of controller 81925979Ssam * queue to give other slaves a chance. 82025979Ssam */ 82125979Ssam vm->um_tab.b_actf = dp->b_forw; 82225979Ssam if (dp->b_actf) { 82325979Ssam dp->b_forw = NULL; 82425979Ssam if (vm->um_tab.b_actf == NULL) 82525979Ssam vm->um_tab.b_actf = dp; 82625979Ssam else 82725979Ssam vm->um_tab.b_actl->b_forw = dp; 82824000Ssam } 82925979Ssam if (vm->um_tab.b_actf == 0) 83024000Ssam return; 83125979Ssam opcont: 83225979Ssam cystart(vm); 83324000Ssam } 83424000Ssam 83525979Ssam cytimer(dev) 83625979Ssam int dev; 83724000Ssam { 83825979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 83925979Ssam int s; 84024000Ssam 84125979Ssam if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { 84225979Ssam printf("yc%d: lost interrupt\n", YCUNIT(dev)); 84325979Ssam yc->yc_timo = INF; 84425979Ssam s = spl3(); 84525979Ssam cyintr(CYUNIT(dev)); 84625979Ssam splx(s); 84724000Ssam } 84825979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 84924000Ssam } 85024000Ssam 85125979Ssam cyseteof(bp) 85225979Ssam register struct buf *bp; 85324000Ssam { 85425979Ssam register int cyunit = CYUNIT(bp->b_dev); 85525979Ssam register struct cy_softc *cy = &cy_softc[cyunit]; 85625979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; 85724000Ssam 85825979Ssam if (bp == &ccybuf[cyunit]) { 85925979Ssam if (yc->yc_blkno > bdbtofsb(bp->b_blkno)) { 86025979Ssam /* reversing */ 86125979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno) - 86225979Ssam htoms(cy->cy_tpb.tpcount); 86325979Ssam yc->yc_blkno = yc->yc_nxrec; 86425979Ssam } else { 86525979Ssam yc->yc_blkno = bdbtofsb(bp->b_blkno) + 86625979Ssam htoms(cy->cy_tpb.tpcount); 86725979Ssam yc->yc_nxrec = yc->yc_blkno - 1; 86824000Ssam } 86925675Ssam return; 87025675Ssam } 87125979Ssam /* eof on read */ 87225979Ssam yc->yc_nxrec = bdbtofsb(bp->b_blkno); 87324000Ssam } 87424000Ssam 87525979Ssam cyread(dev, uio) 87625979Ssam dev_t dev; 87725979Ssam struct uio *uio; 87825675Ssam { 87925979Ssam int errno; 88025675Ssam 88125979Ssam errno = cyphys(dev, uio); 88225979Ssam if (errno) 88325979Ssam return (errno); 88425979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_READ, minphys, uio)); 88525675Ssam } 88625675Ssam 88725979Ssam cywrite(dev, uio) 88825979Ssam dev_t dev; 88925979Ssam struct uio *uio; 89024000Ssam { 89125979Ssam int errno; 89224000Ssam 89325979Ssam errno = cyphys(dev, uio); 89425979Ssam if (errno) 89525979Ssam return (errno); 89625979Ssam return (physio(cystrategy, &rcybuf[CYUNIT(dev)], dev, B_WRITE, minphys, uio)); 89724000Ssam } 89824000Ssam 89924000Ssam /* 90025979Ssam * Check that a raw device exits. 90125979Ssam * If it does, set up the yc_blkno and yc_nxrec 90225979Ssam * so that the tape will appear positioned correctly. 90325979Ssam */ 90425979Ssam cyphys(dev, uio) 90525675Ssam dev_t dev; 90625675Ssam struct uio *uio; 90725675Ssam { 90825979Ssam register int ycunit = YCUNIT(dev); 90925979Ssam register daddr_t a; 91025979Ssam register struct yc_softc *yc; 91125979Ssam register struct vba_device *vi; 91225675Ssam 91325979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 91425979Ssam return (ENXIO); 91525979Ssam yc = &yc_softc[ycunit]; 91625979Ssam a = bdbtofsb(uio->uio_offset >> DEV_BSHIFT); 91725979Ssam yc->yc_blkno = a; 91825979Ssam yc->yc_nxrec = a + 1; 91925979Ssam return (0); 92025675Ssam } 92125675Ssam 92225675Ssam /*ARGSUSED*/ 92325675Ssam cyioctl(dev, cmd, data, flag) 92425979Ssam caddr_t data; 92525675Ssam dev_t dev; 92625675Ssam { 92725979Ssam int ycunit = YCUNIT(dev); 92825979Ssam register struct yc_softc *yc = &yc_softc[ycunit]; 92925979Ssam register struct buf *bp = &ccybuf[CYUNIT(dev)]; 93025979Ssam register callcount; 93125979Ssam int fcount, op; 93225979Ssam struct mtop *mtop; 93325979Ssam struct mtget *mtget; 93425979Ssam /* we depend of the values and order of the MT codes here */ 93525979Ssam static cyops[] = 93625979Ssam {CY_WEOF,CY_SFORW,CY_SREV,CY_SFORW,CY_SREV,CY_REW,CY_OFFL,CY_SENSE}; 93725675Ssam 93825675Ssam switch (cmd) { 93925675Ssam 94025979Ssam case MTIOCTOP: /* tape operation */ 94125979Ssam mtop = (struct mtop *)data; 94225979Ssam switch (op = mtop->mt_op) { 94325675Ssam 94425979Ssam case MTWEOF: 94525979Ssam case MTFSR: case MTBSR: 94625979Ssam case MTFSF: case MTBSF: 94725979Ssam callcount = mtop->mt_count; 94825979Ssam fcount = 1; 94925979Ssam break; 95025675Ssam 95125979Ssam case MTREW: case MTOFFL: case MTNOP: 95225979Ssam callcount = 1; 95325979Ssam fcount = 1; 95425979Ssam break; 95525675Ssam 95625979Ssam default: 95725979Ssam return (ENXIO); 95825979Ssam } 95925979Ssam if (callcount <= 0 || fcount <= 0) 96025979Ssam return (EINVAL); 96125979Ssam while (--callcount >= 0) { 96225979Ssam /* 96325979Ssam * Gagh, this controller is the pits... 96425979Ssam */ 96525979Ssam if (op == MTFSF || op == MTBSF) { 96625979Ssam do 96725979Ssam cycommand(dev, cyops[op], 1); 96825979Ssam while ((bp->b_flags&B_ERROR) == 0 && 96925979Ssam (yc->yc_status&(CYS_EOT|CYS_BOT|CYS_FM)) == 0); 97025979Ssam } else 97125979Ssam cycommand(dev, cyops[op], fcount); 97225979Ssam if ((bp->b_flags&B_ERROR) || 97325979Ssam (yc->yc_status&(CYS_BOT|CYS_EOT))) 97425979Ssam break; 97525979Ssam } 97625979Ssam bp->b_resid = callcount + 1; 97725979Ssam return (geterror(bp)); 97825979Ssam 97925979Ssam case MTIOCGET: 98025979Ssam cycommand(dev, CY_SENSE, 1); 98125979Ssam mtget = (struct mtget *)data; 98225979Ssam mtget->mt_dsreg = yc->yc_status; 98325979Ssam mtget->mt_erreg = yc->yc_control; 98425979Ssam mtget->mt_resid = yc->yc_resid; 98525979Ssam mtget->mt_type = MT_ISCY; 98625675Ssam break; 98725675Ssam 98825675Ssam default: 98925675Ssam return (ENXIO); 99025675Ssam } 99125675Ssam return (0); 99225675Ssam } 99325675Ssam 99425675Ssam /* 99525675Ssam * Poll until the controller is ready. 99625675Ssam */ 99725675Ssam cywait(cp) 99825979Ssam register struct cyccb *cp; 99924000Ssam { 100025675Ssam register int i = 5000; 100124000Ssam 100225979Ssam uncache(&cp->cbgate); 100325979Ssam while (i-- > 0 && cp->cbgate == GATE_CLOSED) { 100424000Ssam DELAY(1000); 100525979Ssam uncache(&cp->cbgate); 100624000Ssam } 100725675Ssam return (i <= 0); 100824000Ssam } 100924000Ssam 101025675Ssam /* 101125979Ssam * Load a 20 bit pointer into an i/o register. 101225675Ssam */ 101325979Ssam cyldmba(wreg, value) 101425979Ssam short *wreg; 101525979Ssam caddr_t value; 101624000Ssam { 101725979Ssam register int v = (int)value; 101825979Ssam register caddr_t reg = (caddr_t)wreg; 101925675Ssam 102025979Ssam *reg++ = v; 102125979Ssam *reg++ = v >> 8; 102225979Ssam *reg++ = 0; 102325979Ssam *reg = (v&0xf0000) >> 12; 102424000Ssam } 102524000Ssam 102625675Ssam /* 102725675Ssam * Unconditionally reset all controllers to their initial state. 102825675Ssam */ 102925675Ssam cyreset(vba) 103025675Ssam int vba; 103124000Ssam { 103225675Ssam register caddr_t addr; 103325675Ssam register int ctlr; 103424000Ssam 103525675Ssam for (ctlr = 0; ctlr < NCY; ctlr++) 103625675Ssam if (cyminfo[ctlr] && cyminfo[ctlr]->um_vbanum == vba) { 103725675Ssam addr = cyminfo[ctlr]->um_addr; 103825675Ssam CY_RESET(addr); 103925979Ssam if (!cyinit(ctlr)) { 104025675Ssam printf("cy%d: reset failed\n", ctlr); 104125675Ssam cyminfo[ctlr] = NULL; 104225675Ssam } 104325675Ssam } 104424000Ssam } 104525979Ssam 104625979Ssam cyuncachetpb(cy) 104725979Ssam struct cy_softc *cy; 104825979Ssam { 104925979Ssam register long *lp = (long *)&cy->cy_tpb; 105025979Ssam register int i; 105125979Ssam 105225979Ssam for (i = 0; i < howmany(sizeof (struct cytpb), sizeof (long)); i++) 105325979Ssam uncache(lp++); 105425979Ssam } 105525979Ssam 105625979Ssam /* 105725979Ssam * Dump routine. 105825979Ssam */ 105925979Ssam cydump(dev) 106025979Ssam dev_t dev; 106125979Ssam { 106225979Ssam register struct cy_softc *cy; 106325979Ssam register int bs, num, start; 106425979Ssam register caddr_t addr; 106525979Ssam int unit = CYUNIT(dev), ctlr, error; 106625979Ssam 106725979Ssam if (unit >= NCY || cyminfo[unit] == 0 || 106825979Ssam (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) 106925979Ssam return (ENXIO); 107025979Ssam if (cywait(&cy->cy_ccb)) 107125979Ssam return (EFAULT); 107225979Ssam #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) 107325979Ssam addr = phys(cyminfo[ctlr]->um_addr); 107425979Ssam num = maxfree, start = NBPG*2; 107525979Ssam while (num > 0) { 107625979Ssam bs = num > btoc(CYMAXIO) ? btoc(CYMAXIO) : num; 107725979Ssam error = cydwrite(cy, start, bs, addr); 107825979Ssam if (error) 107925979Ssam return (error); 108025979Ssam start += bs, num -= bs; 108125979Ssam } 108225979Ssam cyweof(cy, addr); 108325979Ssam cyweof(cy, addr); 108425979Ssam uncache(&cy->cy_tpb); 108525979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 108625979Ssam return (EIO); 108725979Ssam cyrewind(cy, addr); 108825979Ssam return (0); 108925979Ssam } 109025979Ssam 109125979Ssam cydwrite(cy, pf, npf, addr) 109225979Ssam register struct cy_softc *cy; 109325979Ssam int pf, npf; 109425979Ssam caddr_t addr; 109525979Ssam { 109625979Ssam 109725979Ssam cy->cy_tpb.tpcmd = CY_WCOM; 109825979Ssam cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; 109925979Ssam cy->cy_tpb.tpstatus = 0; 110025979Ssam cy->cy_tpb.tpsize = htoms(npf*NBPG); 110125979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 110225979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); 110325979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 110425979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 110525979Ssam CY_GO(addr); 110625979Ssam if (cywait(&cy->cy_ccb)) 110725979Ssam return (EFAULT); 110825979Ssam uncache(&cy->cy_tpb); 110925979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 111025979Ssam return (EIO); 111125979Ssam return (0); 111225979Ssam } 111325979Ssam 111425979Ssam cyweof(cy, addr) 111525979Ssam register struct cy_softc *cy; 111625979Ssam caddr_t addr; 111725979Ssam { 111825979Ssam 111925979Ssam cy->cy_tpb.tpcmd = CY_WEOF; 112025979Ssam cy->cy_tpb.tpcount = htoms(1); 112125979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 112225979Ssam CY_GO(addr); 112325979Ssam (void) cywait(&cy->cy_ccb); 112425979Ssam } 112525979Ssam 112625979Ssam cyrewind(cy, addr) 112725979Ssam register struct cy_softc *cy; 112825979Ssam caddr_t addr; 112925979Ssam { 113025979Ssam 113125979Ssam cy->cy_tpb.tpcmd = CY_REW; 113225979Ssam cy->cy_tpb.tpcount = htoms(1); 113325979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 113425979Ssam CY_GO(addr); 113525979Ssam (void) cywait(&cy->cy_ccb); 113625979Ssam } 113724000Ssam #endif 1138