134487Skarels /* 234487Skarels * Copyright (c) 1988 Regents of the University of California. 334487Skarels * All rights reserved. 434487Skarels * 534487Skarels * This code is derived from software contributed to Berkeley by 634487Skarels * Computer Consoles Inc. 734487Skarels * 8*44533Sbostic * %sccs.include.redist.c% 934487Skarels * 1044398Smarc * @(#)cy.c 7.4 (Berkeley) 5/5/89 1134487Skarels */ 1224000Ssam 1325979Ssam #include "yc.h" 1425675Ssam #if NCY > 0 1524000Ssam /* 1625675Ssam * Cipher Tapemaster driver. 1724000Ssam */ 1830371Skarels #define CYDEBUG 1930371Skarels #ifdef CYDEBUG 2025675Ssam int cydebug = 0; 2130371Skarels #define dlog(params) if (cydebug) log params 2230371Skarels #else 2330371Skarels #define dlog(params) /* */ 2430371Skarels #endif 2524000Ssam 2625675Ssam #include "param.h" 2725675Ssam #include "systm.h" 2825675Ssam #include "vm.h" 2925675Ssam #include "buf.h" 3025675Ssam #include "file.h" 3125675Ssam #include "signal.h" 3225675Ssam #include "ioctl.h" 3325675Ssam #include "mtio.h" 3425675Ssam #include "errno.h" 3525675Ssam #include "cmap.h" 3644398Smarc #include "time.h" 3725979Ssam #include "kernel.h" 3825979Ssam #include "syslog.h" 3944398Smarc #include "tprintf.h" 4024000Ssam 4129952Skarels #include "../tahoe/cpu.h" 4229952Skarels #include "../tahoe/mtpr.h" 4329952Skarels #include "../tahoe/pte.h" 4429952Skarels 4525675Ssam #include "../tahoevba/vbavar.h" 4625979Ssam #define CYERROR 4725675Ssam #include "../tahoevba/cyreg.h" 4824000Ssam 4925979Ssam /* 5025979Ssam * There is a ccybuf per tape controller. 5125979Ssam * It is used as the token to pass to the internal routines 5225979Ssam * to execute tape ioctls, and also acts as a lock on the slaves 5325979Ssam * on the controller, since there is only one per controller. 5425979Ssam * In particular, when the tape is rewinding on close we release 5525979Ssam * the user process but any further attempts to use the tape drive 5625979Ssam * before the rewind completes will hang waiting for ccybuf. 5725979Ssam */ 5825979Ssam struct buf ccybuf[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 7330371Skarels #define T_1600BPI 0x00 /* pseudo */ 7430371Skarels #define T_3200BPI 0x08 /* unused */ 7525979Ssam 7625979Ssam #define INF 1000000L /* close to infinity */ 7725979Ssam 7824000Ssam /* 7925979Ssam * Software state and shared command areas per controller. 8025979Ssam * 8130719Skarels * The i/o intermediate buffer must be allocated in startup() 8230719Skarels * so its address will fit in 20-bits (YECH!!!!!!!!!!!!!!). 8324000Ssam */ 8425979Ssam struct cy_softc { 8525979Ssam int cy_bs; /* controller's buffer size */ 8625979Ssam struct cyscp *cy_scp; /* system configuration block address */ 8725979Ssam struct cyccb cy_ccb; /* channel control block */ 8825979Ssam struct cyscb cy_scb; /* system configuration block */ 8925979Ssam struct cytpb cy_tpb; /* tape parameter block */ 9025979Ssam struct cytpb cy_nop; /* nop parameter block for cyintr */ 9130719Skarels struct vb_buf cy_rbuf; /* vba resources */ 9225979Ssam } cy_softc[NCY]; 9324000Ssam 9425979Ssam /* 9525979Ssam * Software state per tape transport. 9625979Ssam */ 9725979Ssam struct yc_softc { 9825979Ssam char yc_openf; /* lock against multiple opens */ 9925979Ssam char yc_lastiow; /* last operation was a write */ 10025979Ssam short yc_tact; /* timeout is active */ 10125979Ssam long yc_timo; /* time until timeout expires */ 10225979Ssam u_short yc_control; /* copy of last tpcb.tpcontrol */ 10325979Ssam u_short yc_status; /* copy of last tpcb.tpstatus */ 10425979Ssam u_short yc_resid; /* copy of last bc */ 10525979Ssam u_short yc_dens; /* prototype control word with density info */ 10644398Smarc tpr_t yc_tpr; /* handle for tprintf */ 10725979Ssam daddr_t yc_blkno; /* block number, for block device tape */ 10825979Ssam daddr_t yc_nxrec; /* position of end of tape, if known */ 10930371Skarels int yc_blksize; /* current tape blocksize estimate */ 11030371Skarels int yc_blks; /* number of I/O operations since open */ 11130371Skarels int yc_softerrs; /* number of soft I/O errors since open */ 11225979Ssam } yc_softc[NYC]; 11324000Ssam 11424000Ssam /* 11525979Ssam * States for vm->um_tab.b_active, the per controller state flag. 11625979Ssam * This is used to sequence control in the driver. 11724000Ssam */ 11825979Ssam #define SSEEK 1 /* seeking */ 11925979Ssam #define SIO 2 /* doing seq i/o */ 12025979Ssam #define SCOM 3 /* sending control command */ 12125979Ssam #define SREW 4 /* sending a rewind */ 12225979Ssam #define SERASE 5 /* erase inter-record gap */ 12325979Ssam #define SERASED 6 /* erased inter-record gap */ 12424000Ssam 12525979Ssam /* there's no way to figure these out dynamically? -- yech */ 12625979Ssam struct cyscp *cyscp[] = 12725979Ssam { (struct cyscp *)0xc0000c06, (struct cyscp *)0xc0000c16 }; 12825979Ssam #define NCYSCP (sizeof (cyscp) / sizeof (cyscp[0])) 12925979Ssam 13025857Ssam cyprobe(reg, vm) 13125857Ssam caddr_t reg; 13225857Ssam struct vba_ctlr *vm; 13325675Ssam { 13425857Ssam register br, cvec; /* must be r12, r11 */ 13530371Skarels register struct cy_softc *cy; 13630371Skarels int ctlr = vm->um_ctlr; 13725675Ssam 13830294Ssam #ifdef lint 13930294Ssam br = 0; cvec = br; br = cvec; 14030294Ssam cyintr(0); 14130294Ssam #endif 14225857Ssam if (badcyaddr(reg+1)) 14325675Ssam return (0); 14430371Skarels if (ctlr > NCYSCP || cyscp[ctlr] == 0) /* XXX */ 14530371Skarels return (0); 14630371Skarels cy = &cy_softc[ctlr]; 14730371Skarels cy->cy_scp = cyscp[ctlr]; /* XXX */ 14825979Ssam /* 14925979Ssam * Tapemaster controller must have interrupt handler 15025979Ssam * disable interrupt, so we'll just kludge things 15125979Ssam * (stupid multibus non-vectored interrupt crud). 15225979Ssam */ 15330371Skarels if (cyinit(ctlr, reg)) { 15430371Skarels uncache(&cy->cy_tpb.tpcount); 15530371Skarels cy->cy_bs = htoms(cy->cy_tpb.tpcount); 15630371Skarels /* 15730371Skarels * Setup nop parameter block for clearing interrupts. 15830371Skarels */ 15930371Skarels cy->cy_nop.tpcmd = CY_NOP; 16030371Skarels cy->cy_nop.tpcontrol = 0; 16130371Skarels /* 16230371Skarels * Allocate page tables. 16330371Skarels */ 16430719Skarels if (cybuf == 0) { 16530719Skarels printf("no cy buffer!!!\n"); 16630719Skarels return (0); 16730719Skarels } 16830719Skarels cy->cy_rbuf.vb_rawbuf = cybuf + ctlr * CYMAXIO; 16931737Skarels if (vbainit(&cy->cy_rbuf, CYMAXIO, VB_20BIT) == 0) { 17031737Skarels printf("cy%d: vbainit failed\n", ctlr); 17131737Skarels return (0); 17231737Skarels } 17330371Skarels 17430371Skarels br = 0x13, cvec = 0x80; /* XXX */ 17530371Skarels return (sizeof (struct cyccb)); 17630371Skarels } else 17730371Skarels return (0); 17825675Ssam } 17925675Ssam 18024000Ssam /* 18125857Ssam * Check to see if a drive is attached to a controller. 18225857Ssam * Since we can only tell that a drive is there if a tape is loaded and 18325857Ssam * the drive is placed online, we always indicate the slave is present. 18424000Ssam */ 18525857Ssam cyslave(vi, addr) 18625857Ssam struct vba_device *vi; 18725857Ssam caddr_t addr; 18824000Ssam { 18925857Ssam 19025857Ssam #ifdef lint 19125857Ssam vi = vi; addr = addr; 19225857Ssam #endif 19325857Ssam return (1); 19425857Ssam } 19525857Ssam 19625857Ssam cyattach(vi) 19725857Ssam struct vba_device *vi; 19825857Ssam { 19925979Ssam register struct cy_softc *cy; 20025979Ssam int ctlr = vi->ui_mi->um_ctlr; 20125857Ssam 20225979Ssam yctocy[vi->ui_unit] = ctlr; 20325979Ssam cy = &cy_softc[ctlr]; 20430371Skarels if (vi->ui_slave == 0 && cy->cy_bs) 20530371Skarels printf("; %dkb buffer", cy->cy_bs/1024); 20625857Ssam } 20725857Ssam 20825857Ssam /* 20925857Ssam * Initialize the controller after a controller reset or 21025857Ssam * during autoconfigure. All of the system control blocks 21125857Ssam * are initialized and the controller is asked to configure 21225857Ssam * itself for later use. 21325857Ssam */ 21430371Skarels cyinit(ctlr, addr) 21525979Ssam int ctlr; 21630371Skarels register caddr_t addr; 21725857Ssam { 21825979Ssam register struct cy_softc *cy = &cy_softc[ctlr]; 21925675Ssam register int *pte; 22024000Ssam 22124000Ssam /* 22225675Ssam * Initialize the system configuration pointer. 22324000Ssam */ 22425675Ssam /* make kernel writable */ 22530719Skarels pte = (int *)&Sysmap[btop((int)cy->cy_scp &~ KERNBASE)]; 22625675Ssam *pte &= ~PG_PROT; *pte |= PG_KW; 22725979Ssam mtpr(TBIS, cy->cy_scp); 22825675Ssam /* load the correct values in the scp */ 22925979Ssam cy->cy_scp->csp_buswidth = CSP_16BITS; 23025979Ssam cyldmba(cy->cy_scp->csp_scb, (caddr_t)&cy->cy_scb); 23125675Ssam /* put it back to read-only */ 23225675Ssam *pte &= ~PG_PROT; *pte |= PG_KR; 23325979Ssam mtpr(TBIS, cy->cy_scp); 23425675Ssam 23524000Ssam /* 23625675Ssam * Init system configuration block. 23724000Ssam */ 23830371Skarels cy->cy_scb.csb_fixed = CSB_FIXED; 23925675Ssam /* set pointer to the channel control block */ 24025979Ssam cyldmba(cy->cy_scb.csb_ccb, (caddr_t)&cy->cy_ccb); 24125675Ssam 24224000Ssam /* 24325675Ssam * Initialize the chanel control block. 24424000Ssam */ 24525979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 24625979Ssam cy->cy_ccb.cbgate = GATE_OPEN; 24725675Ssam /* set pointer to the tape parameter block */ 24825979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 24925675Ssam 25024000Ssam /* 25125979Ssam * Issue a nop cmd and get the internal buffer size for buffered i/o. 25224000Ssam */ 25325979Ssam cy->cy_tpb.tpcmd = CY_NOP; 25425979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 25525979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 25625979Ssam CY_GO(addr); 25725979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 25825979Ssam uncache(&cy->cy_tpb.tpstatus); 25925979Ssam printf("cy%d: timeout or err during init, status=%b\n", ctlr, 26025979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 26125675Ssam return (0); 26225675Ssam } 26325979Ssam cy->cy_tpb.tpcmd = CY_CONFIG; 26425979Ssam cy->cy_tpb.tpcontrol = CYCW_16BITS; 26525979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 26625979Ssam CY_GO(addr); 26725979Ssam if (cywait(&cy->cy_ccb) || (cy->cy_tpb.tpstatus&CYS_ERR)) { 26825979Ssam uncache(&cy->cy_tpb.tpstatus); 26925979Ssam printf("cy%d: configuration failure, status=%b\n", ctlr, 27025979Ssam cy->cy_tpb.tpstatus, CYS_BITS); 27125675Ssam return (0); 27225675Ssam } 27325675Ssam return (1); 27424000Ssam } 27524000Ssam 27625979Ssam int cytimer(); 27725979Ssam /* 27825979Ssam * Open the device. Tapes are unique open 27925979Ssam * devices, so we refuse if it is already open. 28025979Ssam * We also check that a tape is available, and 28125979Ssam * don't block waiting here; if you want to wait 28225979Ssam * for a tape you should timeout in user code. 28325979Ssam */ 28425675Ssam cyopen(dev, flag) 28525979Ssam dev_t dev; 28625675Ssam register int flag; 28725675Ssam { 28825979Ssam register int ycunit; 28925979Ssam register struct vba_device *vi; 29025979Ssam register struct yc_softc *yc; 29125675Ssam 29225979Ssam ycunit = YCUNIT(dev); 29325979Ssam if (ycunit >= NYC || (vi = ycdinfo[ycunit]) == 0 || vi->ui_alive == 0) 29425675Ssam return (ENXIO); 29525979Ssam if ((yc = &yc_softc[ycunit])->yc_openf) 29625979Ssam return (EBUSY); 29730371Skarels yc->yc_openf = 1; 29825979Ssam #define PACKUNIT(vi) \ 29925979Ssam (((vi->ui_slave&1)<<11)|((vi->ui_slave&2)<<9)|((vi->ui_slave&4)>>2)) 30025979Ssam /* no way to select density */ 30125979Ssam yc->yc_dens = PACKUNIT(vi)|CYCW_IE|CYCW_16BITS; 30230371Skarels if (yc->yc_tact == 0) { 30330371Skarels yc->yc_timo = INF; 30430371Skarels yc->yc_tact = 1; 30530371Skarels timeout(cytimer, (caddr_t)dev, 5*hz); 30630371Skarels } 30725979Ssam cycommand(dev, CY_SENSE, 1); 30825979Ssam if ((yc->yc_status&CYS_OL) == 0) { /* not on-line */ 30934285Skarels uprintf("cy%d: not online\n", ycunit); 31030439Skarels yc->yc_openf = 0; 31130872Skarels return (EIO); 31225675Ssam } 31325979Ssam if ((flag&FWRITE) && (yc->yc_status&CYS_WP)) { 31434285Skarels uprintf("cy%d: no write ring\n", ycunit); 31530439Skarels yc->yc_openf = 0; 31630872Skarels return (EIO); 31725675Ssam } 31825979Ssam yc->yc_blkno = (daddr_t)0; 31925979Ssam yc->yc_nxrec = INF; 32025979Ssam yc->yc_lastiow = 0; 32130869Skarels yc->yc_blksize = CYMAXIO; /* guess > 0 */ 32230371Skarels yc->yc_blks = 0; 32330371Skarels yc->yc_softerrs = 0; 32444398Smarc yc->yc_tpr = tprintf_open(); 32525675Ssam return (0); 32625675Ssam } 32725675Ssam 32825979Ssam /* 32925979Ssam * Close tape device. 33025979Ssam * 33125979Ssam * If tape was open for writing or last operation was a write, 33225979Ssam * then write two EOF's and backspace over the last one. 33325979Ssam * Unless this is a non-rewinding special file, rewind the tape. 33425979Ssam * Make the tape available to others. 33525979Ssam */ 33625675Ssam cyclose(dev, flag) 33725979Ssam dev_t dev; 33830371Skarels int flag; 33925675Ssam { 34030371Skarels struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 34125675Ssam 34225979Ssam if (flag == FWRITE || (flag&FWRITE) && yc->yc_lastiow) { 34334285Skarels cycommand(dev, CY_WEOF, 1); /* can't use count with WEOF */ 34434285Skarels cycommand(dev, CY_WEOF, 1); 34525979Ssam cycommand(dev, CY_SREV, 1); 34625675Ssam } 34725979Ssam if ((minor(dev)&T_NOREWIND) == 0) 34825979Ssam /* 34925979Ssam * 0 count means don't hang waiting for rewind complete 35025979Ssam * rather ccybuf stays busy until the operation completes 35125979Ssam * preventing further opens from completing by preventing 35225979Ssam * a CY_SENSE from completing. 35325979Ssam */ 35425979Ssam cycommand(dev, CY_REW, 0); 35530371Skarels if (yc->yc_blks > 10 && yc->yc_softerrs > yc->yc_blks / 10) 35630371Skarels log(LOG_INFO, "yc%d: %d soft errors in %d blocks\n", 35730371Skarels YCUNIT(dev), yc->yc_softerrs, yc->yc_blks); 35830371Skarels dlog((LOG_INFO, "%d soft errors in %d blocks\n", 35930371Skarels yc->yc_softerrs, yc->yc_blks)); 36044398Smarc tprintf_close(yc->yc_tpr); 36125979Ssam yc->yc_openf = 0; 36230719Skarels return (0); 36325675Ssam } 36425675Ssam 36524000Ssam /* 36625979Ssam * Execute a command on the tape drive a specified number of times. 36724000Ssam */ 36825979Ssam cycommand(dev, com, count) 36925979Ssam dev_t dev; 37025979Ssam int com, count; 37124000Ssam { 37225979Ssam register struct buf *bp; 37325675Ssam int s; 37425675Ssam 37525979Ssam bp = &ccybuf[CYUNIT(dev)]; 37625675Ssam s = spl3(); 37730371Skarels dlog((LOG_INFO, "cycommand(%o, %x, %d), b_flags %x\n", 37830371Skarels dev, com, count, bp->b_flags)); 37925979Ssam while (bp->b_flags&B_BUSY) { 38025979Ssam /* 38125979Ssam * This special check is because B_BUSY never 38225979Ssam * gets cleared in the non-waiting rewind case. 38325979Ssam */ 38425979Ssam if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 38525979Ssam break; 38625979Ssam bp->b_flags |= B_WANTED; 38725979Ssam sleep((caddr_t)bp, PRIBIO); 38825675Ssam } 38925979Ssam bp->b_flags = B_BUSY|B_READ; 39025675Ssam splx(s); 39125979Ssam bp->b_dev = dev; 39225979Ssam bp->b_repcnt = count; 39325979Ssam bp->b_command = com; 39425979Ssam bp->b_blkno = 0; 39525979Ssam cystrategy(bp); 39625979Ssam /* 39725979Ssam * In case of rewind from close; don't wait. 39825979Ssam * This is the only case where count can be 0. 39925979Ssam */ 40025979Ssam if (count == 0) 40125979Ssam return; 40230371Skarels biowait(bp); 40325979Ssam if (bp->b_flags&B_WANTED) 40425979Ssam wakeup((caddr_t)bp); 40525979Ssam bp->b_flags &= B_ERROR; 40624000Ssam } 40724000Ssam 40825675Ssam cystrategy(bp) 40925675Ssam register struct buf *bp; 41025675Ssam { 41125979Ssam int ycunit = YCUNIT(bp->b_dev); 41225979Ssam register struct vba_ctlr *vm; 41325979Ssam register struct buf *dp; 41425675Ssam int s; 41525675Ssam 41625979Ssam /* 41725979Ssam * Put transfer at end of unit queue. 41825979Ssam */ 41930371Skarels dlog((LOG_INFO, "cystrategy(%o, %x)\n", bp->b_dev, bp->b_command)); 42025979Ssam dp = &ycutab[ycunit]; 42125675Ssam bp->av_forw = NULL; 42225979Ssam vm = ycdinfo[ycunit]->ui_mi; 42325979Ssam /* BEGIN GROT */ 42434507Skarels if (bp->b_flags & B_RAW) { 42530869Skarels if (bp->b_bcount >= CYMAXIO) { 42625979Ssam uprintf("cy%d: i/o size too large\n", vm->um_ctlr); 42730869Skarels bp->b_error = EINVAL; 42825979Ssam bp->b_resid = bp->b_bcount; 42925979Ssam bp->b_flags |= B_ERROR; 43030371Skarels biodone(bp); 43125675Ssam return; 43225675Ssam } 43324000Ssam } 43425979Ssam /* END GROT */ 43525675Ssam s = spl3(); 43625979Ssam if (dp->b_actf == NULL) { 43725979Ssam dp->b_actf = bp; 43825979Ssam /* 43925979Ssam * Transport not already active... 44025979Ssam * put at end of controller queue. 44125979Ssam */ 44225979Ssam dp->b_forw = NULL; 44325979Ssam if (vm->um_tab.b_actf == NULL) 44425979Ssam vm->um_tab.b_actf = dp; 44525979Ssam else 44625979Ssam vm->um_tab.b_actl->b_forw = dp; 44725979Ssam } else 44825979Ssam dp->b_actl->av_forw = bp; 44925979Ssam dp->b_actl = bp; 45025979Ssam /* 45125979Ssam * If the controller is not busy, get it going. 45225979Ssam */ 45325979Ssam if (vm->um_tab.b_active == 0) 45425979Ssam cystart(vm); 45524000Ssam splx(s); 45624000Ssam } 45724000Ssam 45824000Ssam /* 45925979Ssam * Start activity on a cy controller. 46024000Ssam */ 46125979Ssam cystart(vm) 46225979Ssam register struct vba_ctlr *vm; 46324000Ssam { 46425979Ssam register struct buf *bp, *dp; 46525979Ssam register struct yc_softc *yc; 46625979Ssam register struct cy_softc *cy; 46725979Ssam int ycunit; 46825979Ssam daddr_t blkno; 46924000Ssam 47030371Skarels dlog((LOG_INFO, "cystart()\n")); 47125979Ssam /* 47225979Ssam * Look for an idle transport on the controller. 47325979Ssam */ 47425979Ssam loop: 47525979Ssam if ((dp = vm->um_tab.b_actf) == NULL) 47625675Ssam return; 47725979Ssam if ((bp = dp->b_actf) == NULL) { 47825979Ssam vm->um_tab.b_actf = dp->b_forw; 47925979Ssam goto loop; 48025675Ssam } 48125979Ssam ycunit = YCUNIT(bp->b_dev); 48225979Ssam yc = &yc_softc[ycunit]; 48325979Ssam cy = &cy_softc[CYUNIT(bp->b_dev)]; 48425979Ssam /* 48525979Ssam * Default is that last command was NOT a write command; 48625979Ssam * if we do a write command we will notice this in cyintr(). 48725979Ssam */ 48825979Ssam yc->yc_lastiow = 0; 48925979Ssam if (yc->yc_openf < 0 || 49025979Ssam (bp->b_command != CY_SENSE && (cy->cy_tpb.tpstatus&CYS_OL) == 0)) { 49125979Ssam /* 49225979Ssam * Have had a hard error on a non-raw tape 49325979Ssam * or the tape unit is now unavailable (e.g. 49425979Ssam * taken off line). 49525979Ssam */ 49630371Skarels dlog((LOG_INFO, "openf %d command %x status %b\n", 49730371Skarels yc->yc_openf, bp->b_command, cy->cy_tpb.tpstatus, CYS_BITS)); 49825979Ssam bp->b_flags |= B_ERROR; 49925979Ssam goto next; 50025675Ssam } 50125979Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) { 50225979Ssam /* 50325979Ssam * Execute control operation with the specified count. 50425979Ssam * 50525979Ssam * Set next state; give 5 minutes to complete 50625979Ssam * rewind or file mark search, or 10 seconds per 50725979Ssam * iteration (minimum 60 seconds and max 5 minutes) 50825979Ssam * to complete other ops. 50925979Ssam */ 51025979Ssam if (bp->b_command == CY_REW) { 51125979Ssam vm->um_tab.b_active = SREW; 51225979Ssam yc->yc_timo = 5*60; 51330869Skarels } else if (bp->b_command == CY_FSF || 51430869Skarels bp->b_command == CY_BSF) { 51530869Skarels vm->um_tab.b_active = SCOM; 51630869Skarels yc->yc_timo = 5*60; 51725979Ssam } else { 51825979Ssam vm->um_tab.b_active = SCOM; 51925979Ssam yc->yc_timo = imin(imax(10*(int)bp->b_repcnt,60),5*60); 52025979Ssam } 52125979Ssam cy->cy_tpb.tprec = htoms(bp->b_repcnt); 52230719Skarels dlog((LOG_INFO, "bpcmd ")); 52325979Ssam goto dobpcmd; 52424000Ssam } 52525979Ssam /* 52634507Skarels * For raw I/O, save the current block 52734507Skarels * number in case we have to retry. 52825979Ssam */ 52934507Skarels if (bp->b_flags & B_RAW) { 53034507Skarels if (vm->um_tab.b_errcnt == 0) { 53134507Skarels yc->yc_blkno = bp->b_blkno; 53234507Skarels yc->yc_nxrec = yc->yc_blkno + 1; 53334507Skarels } 53434507Skarels } else { 53525979Ssam /* 53634507Skarels * Handle boundary cases for operation 53734507Skarels * on non-raw tapes. 53825979Ssam */ 53934507Skarels if (bp->b_blkno > yc->yc_nxrec) { 54034507Skarels /* 54134507Skarels * Can't read past known end-of-file. 54234507Skarels */ 54334507Skarels bp->b_flags |= B_ERROR; 54434507Skarels bp->b_error = ENXIO; 54534507Skarels goto next; 54634507Skarels } 54734507Skarels if (bp->b_blkno == yc->yc_nxrec && bp->b_flags&B_READ) { 54834507Skarels /* 54934507Skarels * Reading at end of file returns 0 bytes. 55034507Skarels */ 55134507Skarels bp->b_resid = bp->b_bcount; 55234507Skarels clrbuf(bp); 55334507Skarels goto next; 55434507Skarels } 55534507Skarels if ((bp->b_flags&B_READ) == 0) 55634507Skarels /* 55734507Skarels * Writing sets EOF. 55834507Skarels */ 55934507Skarels yc->yc_nxrec = bp->b_blkno + 1; 56024000Ssam } 56130719Skarels if ((blkno = yc->yc_blkno) == bp->b_blkno) { 56225979Ssam caddr_t addr; 56325979Ssam int cmd; 56425675Ssam 56525979Ssam /* 56625979Ssam * Choose the appropriate i/o command based on the 56730371Skarels * transfer size, the estimated block size, 56830371Skarels * and the controller's internal buffer size. 56930869Skarels * If the request length is longer than the tape 57030869Skarels * block length, a buffered read will fail, 57130869Skarels * thus, we request at most the size that we expect. 57230869Skarels * We then check for larger records when the read completes. 57325979Ssam * If we're retrying a read on a raw device because 57425979Ssam * the original try was a buffer request which failed 57525979Ssam * due to a record length error, then we force the use 57625979Ssam * of the raw controller read (YECH!!!!). 57725979Ssam */ 57825979Ssam if (bp->b_flags&B_READ) { 57930869Skarels if (yc->yc_blksize <= cy->cy_bs && 58030869Skarels vm->um_tab.b_errcnt == 0) 58130869Skarels cmd = CY_BRCOM; 58230869Skarels else 58325979Ssam cmd = CY_RCOM; 58425979Ssam } else { 58525979Ssam /* 58625979Ssam * On write error retries erase the 58725979Ssam * inter-record gap before rewriting. 58825979Ssam */ 58925979Ssam if (vm->um_tab.b_errcnt && 59025979Ssam vm->um_tab.b_active != SERASED) { 59125979Ssam vm->um_tab.b_active = SERASE; 59225979Ssam bp->b_command = CY_ERASE; 59325979Ssam yc->yc_timo = 60; 59425979Ssam goto dobpcmd; 59525675Ssam } 59625979Ssam cmd = (bp->b_bcount > cy->cy_bs) ? CY_WCOM : CY_BWCOM; 59725675Ssam } 59825979Ssam vm->um_tab.b_active = SIO; 59930719Skarels addr = (caddr_t)vbasetup(bp, &cy->cy_rbuf, 1); 60025979Ssam cy->cy_tpb.tpcmd = cmd; 60125979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 60225979Ssam if (cmd == CY_RCOM || cmd == CY_WCOM) 60325979Ssam cy->cy_tpb.tpcontrol |= CYCW_LOCK; 60425979Ssam cy->cy_tpb.tpstatus = 0; 60525979Ssam cy->cy_tpb.tpcount = 0; 60625979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)addr); 60725979Ssam cy->cy_tpb.tprec = 0; 60830869Skarels if (cmd == CY_BRCOM) 60934487Skarels cy->cy_tpb.tpsize = htoms(imin(yc->yc_blksize, 61034487Skarels (int)bp->b_bcount)); 61130371Skarels else 61230371Skarels cy->cy_tpb.tpsize = htoms(bp->b_bcount); 61325979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 61425979Ssam do 61525979Ssam uncache(&cy->cy_ccb.cbgate); 61625979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 61725979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 61825979Ssam cy->cy_ccb.cbcw = CBCW_IE; 61925979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 62030371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x size %d\n", 62125979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 62230371Skarels htoms(cy->cy_tpb.tpsize))); 62325979Ssam CY_GO(vm->um_addr); 62425979Ssam return; 62524000Ssam } 62625979Ssam /* 62725979Ssam * Tape positioned incorrectly; set to seek forwards 62825979Ssam * or backwards to the correct spot. This happens 62925979Ssam * for raw tapes only on error retries. 63025979Ssam */ 63125979Ssam vm->um_tab.b_active = SSEEK; 63230719Skarels if (blkno < bp->b_blkno) { 63325979Ssam bp->b_command = CY_SFORW; 63430719Skarels cy->cy_tpb.tprec = htoms(bp->b_blkno - blkno); 63525979Ssam } else { 63625979Ssam bp->b_command = CY_SREV; 63730719Skarels cy->cy_tpb.tprec = htoms(blkno - bp->b_blkno); 63824000Ssam } 63934487Skarels yc->yc_timo = imin(imax((int)(10 * htoms(cy->cy_tpb.tprec)), 60), 5*60); 64025979Ssam dobpcmd: 64125979Ssam /* 64225979Ssam * Do the command in bp. Reverse direction commands 64325979Ssam * are indicated by having CYCW_REV or'd into their 64425979Ssam * value. For these we must set the appropriate bit 64525979Ssam * in the control field. 64625979Ssam */ 64725979Ssam if (bp->b_command&CYCW_REV) { 64825979Ssam cy->cy_tpb.tpcmd = bp->b_command &~ CYCW_REV; 64925979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens | CYCW_REV; 65030719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 65125979Ssam } else { 65225979Ssam cy->cy_tpb.tpcmd = bp->b_command; 65325979Ssam cy->cy_tpb.tpcontrol = yc->yc_dens; 65430719Skarels dlog((LOG_INFO, "cmd %x control %x\n", cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol)); 65524000Ssam } 65625979Ssam cy->cy_tpb.tpstatus = 0; 65725979Ssam cy->cy_tpb.tpcount = 0; 65825979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 65925979Ssam do 66025979Ssam uncache(&cy->cy_ccb.cbgate); 66125979Ssam while (cy->cy_ccb.cbgate == GATE_CLOSED); 66225979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 66325979Ssam cy->cy_ccb.cbcw = CBCW_IE; 66425979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 66530371Skarels dlog((LOG_INFO, "CY_GO(%x) cmd %x control %x rec %d\n", 66625979Ssam vm->um_addr, cy->cy_tpb.tpcmd, cy->cy_tpb.tpcontrol, 66730371Skarels htoms(cy->cy_tpb.tprec))); 66825979Ssam CY_GO(vm->um_addr); 66925979Ssam return; 67025979Ssam next: 67125979Ssam /* 67225979Ssam * Done with this operation due to error or the 67330719Skarels * fact that it doesn't do anything. 67430719Skarels * Dequeue the transfer and continue 67525979Ssam * processing this slave. 67625979Ssam */ 67725979Ssam vm->um_tab.b_errcnt = 0; 67825979Ssam dp->b_actf = bp->av_forw; 67930371Skarels biodone(bp); 68025979Ssam goto loop; 68125675Ssam } 68225675Ssam 68325675Ssam /* 68425979Ssam * Cy interrupt routine. 68525675Ssam */ 68630719Skarels cyintr(cyunit) 68730719Skarels int cyunit; 68825675Ssam { 68925979Ssam struct buf *dp; 69024000Ssam register struct buf *bp; 69130719Skarels register struct vba_ctlr *vm = cyminfo[cyunit]; 69225979Ssam register struct cy_softc *cy; 69325979Ssam register struct yc_softc *yc; 69430719Skarels int err; 69525979Ssam register state; 69624000Ssam 69730719Skarels dlog((LOG_INFO, "cyintr(%d)\n", cyunit)); 69825979Ssam /* 69925979Ssam * First, turn off the interrupt from the controller 70025979Ssam * (device uses Multibus non-vectored interrupts...yech). 70125979Ssam */ 70225979Ssam cy = &cy_softc[vm->um_ctlr]; 70325979Ssam cy->cy_ccb.cbcw = CBCW_CLRINT; 70430294Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_nop); 70525979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 70625979Ssam CY_GO(vm->um_addr); 70725979Ssam if ((dp = vm->um_tab.b_actf) == NULL) { 70830371Skarels dlog((LOG_ERR, "cy%d: stray interrupt", vm->um_ctlr)); 70924000Ssam return; 71024000Ssam } 71125979Ssam bp = dp->b_actf; 71225979Ssam cy = &cy_softc[cyunit]; 71325979Ssam cyuncachetpb(cy); 71430294Ssam yc = &yc_softc[YCUNIT(bp->b_dev)]; 71525979Ssam /* 71625984Ssam * If last command was a rewind and tape is 71725984Ssam * still moving, wait for the operation to complete. 71825979Ssam */ 71925979Ssam if (vm->um_tab.b_active == SREW) { 72025979Ssam vm->um_tab.b_active = SCOM; 72125979Ssam if ((cy->cy_tpb.tpstatus&CYS_RDY) == 0) { 72225979Ssam yc->yc_timo = 5*60; /* 5 minutes */ 72325979Ssam return; 72424000Ssam } 72524000Ssam } 72625979Ssam /* 72725979Ssam * An operation completed...record status. 72825979Ssam */ 72925979Ssam yc->yc_timo = INF; 73025979Ssam yc->yc_control = cy->cy_tpb.tpcontrol; 73125979Ssam yc->yc_status = cy->cy_tpb.tpstatus; 73225979Ssam yc->yc_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 73330371Skarels dlog((LOG_INFO, "cmd %x control %b status %b resid %d\n", 73425979Ssam cy->cy_tpb.tpcmd, yc->yc_control, CYCW_BITS, 73530371Skarels yc->yc_status, CYS_BITS, yc->yc_resid)); 73625979Ssam if ((bp->b_flags&B_READ) == 0) 73725979Ssam yc->yc_lastiow = 1; 73825979Ssam state = vm->um_tab.b_active; 73925979Ssam vm->um_tab.b_active = 0; 74025979Ssam /* 74125979Ssam * Check for errors. 74225979Ssam */ 74325979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) { 74425979Ssam err = cy->cy_tpb.tpstatus&CYS_ERR; 74530371Skarels dlog((LOG_INFO, "error %d\n", err)); 74625979Ssam /* 74725979Ssam * If we hit the end of tape file, update our position. 74825979Ssam */ 74925979Ssam if (err == CYER_FM) { 75025979Ssam yc->yc_status |= CYS_FM; 75125979Ssam state = SCOM; /* force completion */ 75225979Ssam cyseteof(bp); /* set blkno and nxrec */ 75325979Ssam goto opdone; 75425979Ssam } 75525979Ssam /* 75625979Ssam * Fix up errors which occur due to backspacing over 75725979Ssam * the beginning of the tape. 75825979Ssam */ 75925979Ssam if (err == CYER_BOT && cy->cy_tpb.tpcontrol&CYCW_REV) { 76025979Ssam yc->yc_status |= CYS_BOT; 76125979Ssam goto ignoreerr; 76225979Ssam } 76325979Ssam /* 76425979Ssam * If we were reading raw tape and the only error was that the 76525979Ssam * record was too long, then we don't consider this an error. 76625979Ssam */ 76734507Skarels if ((bp->b_flags & (B_READ|B_RAW)) == (B_READ|B_RAW) && 76825979Ssam err == CYER_STROBE) { 76925979Ssam /* 77030371Skarels * Retry reads with the command changed to 77130371Skarels * a raw read if necessary. Setting b_errcnt 77225979Ssam * here causes cystart (above) to force a CY_RCOM. 77325979Ssam */ 77430869Skarels if (cy->cy_tpb.tpcmd == CY_BRCOM && 77530719Skarels vm->um_tab.b_errcnt++ == 0) { 77630371Skarels yc->yc_blkno++; 77730371Skarels goto opcont; 77830371Skarels } else 77925979Ssam goto ignoreerr; 78025979Ssam } 78125979Ssam /* 78225979Ssam * If error is not hard, and this was an i/o operation 78325979Ssam * retry up to 8 times. 78425979Ssam */ 78534285Skarels if (state == SIO && (CYMASK(err) & 78634285Skarels ((bp->b_flags&B_READ) ? CYER_RSOFT : CYER_WSOFT))) { 78725979Ssam if (++vm->um_tab.b_errcnt < 7) { 78825979Ssam yc->yc_blkno++; 78925979Ssam goto opcont; 79025979Ssam } 79125979Ssam } else 79225979Ssam /* 79325979Ssam * Hard or non-i/o errors on non-raw tape 79425979Ssam * cause it to close. 79525979Ssam */ 79634507Skarels if ((bp->b_flags&B_RAW) == 0 && 79734507Skarels yc->yc_openf > 0) 79825979Ssam yc->yc_openf = -1; 79925979Ssam /* 80025979Ssam * Couldn't recover from error. 80125979Ssam */ 80244398Smarc tprintf(yc->yc_tpr, 80330371Skarels "yc%d: hard error bn%d status=%b, %s\n", YCUNIT(bp->b_dev), 80430371Skarels bp->b_blkno, yc->yc_status, CYS_BITS, 80530371Skarels (err < NCYERROR) ? cyerror[err] : ""); 80625979Ssam bp->b_flags |= B_ERROR; 80725979Ssam goto opdone; 80830869Skarels } else if (cy->cy_tpb.tpcmd == CY_BRCOM) { 80930869Skarels int reclen = htoms(cy->cy_tpb.tprec); 81030869Skarels 81130869Skarels /* 81230869Skarels * If we did a buffered read, check whether the read 81330869Skarels * was long enough. If we asked the controller for less 81430869Skarels * than the user asked for because the previous record 81530869Skarels * was shorter, update our notion of record size 81630869Skarels * and retry. If the record is longer than the buffer, 81730869Skarels * bump the errcnt so the retry will use direct read. 81830869Skarels */ 81930869Skarels if (reclen > yc->yc_blksize && bp->b_bcount > yc->yc_blksize) { 82030869Skarels yc->yc_blksize = reclen; 82130869Skarels if (reclen > cy->cy_bs) 82230869Skarels vm->um_tab.b_errcnt++; 82330869Skarels yc->yc_blkno++; 82430869Skarels goto opcont; 82530869Skarels } 82624000Ssam } 82725979Ssam /* 82825979Ssam * Advance tape control FSM. 82925979Ssam */ 83025979Ssam ignoreerr: 83125979Ssam /* 83225979Ssam * If we hit a tape mark update our position. 83325979Ssam */ 83425979Ssam if (yc->yc_status&CYS_FM && bp->b_flags&B_READ) { 83525979Ssam cyseteof(bp); 83625979Ssam goto opdone; 83725675Ssam } 83825979Ssam switch (state) { 83924000Ssam 84025979Ssam case SIO: 84125979Ssam /* 84225979Ssam * Read/write increments tape block number. 84325979Ssam */ 84425979Ssam yc->yc_blkno++; 84530371Skarels yc->yc_blks++; 84630371Skarels if (vm->um_tab.b_errcnt || yc->yc_status & CYS_CR) 84730371Skarels yc->yc_softerrs++; 84830371Skarels yc->yc_blksize = htoms(cy->cy_tpb.tpcount); 84930371Skarels dlog((LOG_ERR, "blocksize %d", yc->yc_blksize)); 85025979Ssam goto opdone; 85124000Ssam 85225979Ssam case SCOM: 85325979Ssam /* 85425979Ssam * For forward/backward space record update current position. 85525979Ssam */ 85630294Ssam if (bp == &ccybuf[CYUNIT(bp->b_dev)]) 85730294Ssam switch ((int)bp->b_command) { 85824000Ssam 85930294Ssam case CY_SFORW: 86030294Ssam yc->yc_blkno -= bp->b_repcnt; 86130294Ssam break; 86224000Ssam 86330294Ssam case CY_SREV: 86430294Ssam yc->yc_blkno += bp->b_repcnt; 86530294Ssam break; 86630294Ssam } 86725979Ssam goto opdone; 86825979Ssam 86925979Ssam case SSEEK: 87030719Skarels yc->yc_blkno = bp->b_blkno; 87125979Ssam goto opcont; 87224000Ssam 87325979Ssam case SERASE: 87425979Ssam /* 87525979Ssam * Completed erase of the inter-record gap due to a 87625979Ssam * write error; now retry the write operation. 87725979Ssam */ 87825979Ssam vm->um_tab.b_active = SERASED; 87925979Ssam goto opcont; 88024000Ssam } 88125675Ssam 88225979Ssam opdone: 88325979Ssam /* 88425979Ssam * Reset error count and remove from device queue. 88525979Ssam */ 88625979Ssam vm->um_tab.b_errcnt = 0; 88725979Ssam dp->b_actf = bp->av_forw; 88825979Ssam /* 88925979Ssam * Save resid and release resources. 89025979Ssam */ 89125979Ssam bp->b_resid = bp->b_bcount - htoms(cy->cy_tpb.tpcount); 89230719Skarels if (bp != &ccybuf[cyunit]) 89330719Skarels vbadone(bp, &cy->cy_rbuf); 89430371Skarels biodone(bp); 89525979Ssam /* 89625979Ssam * Circulate slave to end of controller 89725979Ssam * queue to give other slaves a chance. 89825979Ssam */ 89925979Ssam vm->um_tab.b_actf = dp->b_forw; 90025979Ssam if (dp->b_actf) { 90125979Ssam dp->b_forw = NULL; 90225979Ssam if (vm->um_tab.b_actf == NULL) 90325979Ssam vm->um_tab.b_actf = dp; 90425979Ssam else 90525979Ssam vm->um_tab.b_actl->b_forw = dp; 90624000Ssam } 90725979Ssam if (vm->um_tab.b_actf == 0) 90824000Ssam return; 90925979Ssam opcont: 91025979Ssam cystart(vm); 91124000Ssam } 91224000Ssam 91325979Ssam cytimer(dev) 91425979Ssam int dev; 91524000Ssam { 91625979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(dev)]; 91725979Ssam int s; 91824000Ssam 91930371Skarels if (yc->yc_openf == 0 && yc->yc_timo == INF) { 92030371Skarels yc->yc_tact = 0; 92130371Skarels return; 92230371Skarels } 92325979Ssam if (yc->yc_timo != INF && (yc->yc_timo -= 5) < 0) { 92425979Ssam printf("yc%d: lost interrupt\n", YCUNIT(dev)); 92525979Ssam yc->yc_timo = INF; 92625979Ssam s = spl3(); 92725979Ssam cyintr(CYUNIT(dev)); 92825979Ssam splx(s); 92924000Ssam } 93025979Ssam timeout(cytimer, (caddr_t)dev, 5*hz); 93124000Ssam } 93224000Ssam 93325979Ssam cyseteof(bp) 93425979Ssam register struct buf *bp; 93524000Ssam { 93625979Ssam register int cyunit = CYUNIT(bp->b_dev); 93725979Ssam register struct cy_softc *cy = &cy_softc[cyunit]; 93825979Ssam register struct yc_softc *yc = &yc_softc[YCUNIT(bp->b_dev)]; 93924000Ssam 94025979Ssam if (bp == &ccybuf[cyunit]) { 94130719Skarels if (yc->yc_blkno > bp->b_blkno) { 94225979Ssam /* reversing */ 94330719Skarels yc->yc_nxrec = bp->b_blkno - htoms(cy->cy_tpb.tpcount); 94425979Ssam yc->yc_blkno = yc->yc_nxrec; 94525979Ssam } else { 94630719Skarels yc->yc_blkno = bp->b_blkno + htoms(cy->cy_tpb.tpcount); 94725979Ssam yc->yc_nxrec = yc->yc_blkno - 1; 94824000Ssam } 94925675Ssam return; 95025675Ssam } 95125979Ssam /* eof on read */ 95230719Skarels yc->yc_nxrec = bp->b_blkno; 95324000Ssam } 95424000Ssam 95525675Ssam /*ARGSUSED*/ 95625675Ssam cyioctl(dev, cmd, data, flag) 95725979Ssam caddr_t data; 95825675Ssam dev_t dev; 95925675Ssam { 96025979Ssam int ycunit = YCUNIT(dev); 96125979Ssam register struct yc_softc *yc = &yc_softc[ycunit]; 96225979Ssam register struct buf *bp = &ccybuf[CYUNIT(dev)]; 96325979Ssam register callcount; 96425979Ssam int fcount, op; 96525979Ssam struct mtop *mtop; 96625979Ssam struct mtget *mtget; 96725979Ssam /* we depend of the values and order of the MT codes here */ 96825979Ssam static cyops[] = 96930371Skarels {CY_WEOF,CY_FSF,CY_BSF,CY_SFORW,CY_SREV,CY_REW,CY_OFFL,CY_SENSE}; 97025675Ssam 97125675Ssam switch (cmd) { 97225675Ssam 97325979Ssam case MTIOCTOP: /* tape operation */ 97425979Ssam mtop = (struct mtop *)data; 97525979Ssam switch (op = mtop->mt_op) { 97625675Ssam 97725979Ssam case MTWEOF: 97830371Skarels callcount = mtop->mt_count; 97930371Skarels fcount = 1; 98030371Skarels break; 98130371Skarels 98225979Ssam case MTFSR: case MTBSR: 98330371Skarels callcount = 1; 98430371Skarels fcount = mtop->mt_count; 98530371Skarels break; 98630371Skarels 98725979Ssam case MTFSF: case MTBSF: 98825979Ssam callcount = mtop->mt_count; 98925979Ssam fcount = 1; 99025979Ssam break; 99125675Ssam 99225979Ssam case MTREW: case MTOFFL: case MTNOP: 99325979Ssam callcount = 1; 99425979Ssam fcount = 1; 99525979Ssam break; 99625675Ssam 99725979Ssam default: 99825979Ssam return (ENXIO); 99925979Ssam } 100025979Ssam if (callcount <= 0 || fcount <= 0) 100125979Ssam return (EINVAL); 100225979Ssam while (--callcount >= 0) { 100330371Skarels #ifdef notdef 100425979Ssam /* 100525979Ssam * Gagh, this controller is the pits... 100625979Ssam */ 100725979Ssam if (op == MTFSF || op == MTBSF) { 100825979Ssam do 100925979Ssam cycommand(dev, cyops[op], 1); 101025979Ssam while ((bp->b_flags&B_ERROR) == 0 && 101125979Ssam (yc->yc_status&(CYS_EOT|CYS_BOT|CYS_FM)) == 0); 101225979Ssam } else 101330371Skarels #endif 101425979Ssam cycommand(dev, cyops[op], fcount); 101530371Skarels dlog((LOG_INFO, 101630371Skarels "cyioctl: status %x, b_flags %x, resid %d\n", 101730371Skarels yc->yc_status, bp->b_flags, bp->b_resid)); 101825979Ssam if ((bp->b_flags&B_ERROR) || 101925979Ssam (yc->yc_status&(CYS_BOT|CYS_EOT))) 102025979Ssam break; 102125979Ssam } 102225979Ssam bp->b_resid = callcount + 1; 102337638Smckusick /* 102437638Smckusick * Pick up the device's error number and pass it 102537638Smckusick * to the user; if there is an error but the number 102637638Smckusick * is 0 set a generalized code. 102737638Smckusick */ 102837638Smckusick if ((bp->b_flags & B_ERROR) == 0) 102937638Smckusick return (0); 103037638Smckusick if (bp->b_error) 103137638Smckusick return (bp->b_error); 103237638Smckusick return (EIO); 103325979Ssam 103425979Ssam case MTIOCGET: 103525979Ssam cycommand(dev, CY_SENSE, 1); 103625979Ssam mtget = (struct mtget *)data; 103725979Ssam mtget->mt_dsreg = yc->yc_status; 103825979Ssam mtget->mt_erreg = yc->yc_control; 103925979Ssam mtget->mt_resid = yc->yc_resid; 104025979Ssam mtget->mt_type = MT_ISCY; 104125675Ssam break; 104225675Ssam 104325675Ssam default: 104425675Ssam return (ENXIO); 104525675Ssam } 104625675Ssam return (0); 104725675Ssam } 104825675Ssam 104925675Ssam /* 105025675Ssam * Poll until the controller is ready. 105125675Ssam */ 105225675Ssam cywait(cp) 105325979Ssam register struct cyccb *cp; 105424000Ssam { 105525675Ssam register int i = 5000; 105624000Ssam 105725979Ssam uncache(&cp->cbgate); 105825979Ssam while (i-- > 0 && cp->cbgate == GATE_CLOSED) { 105924000Ssam DELAY(1000); 106025979Ssam uncache(&cp->cbgate); 106124000Ssam } 106225675Ssam return (i <= 0); 106324000Ssam } 106424000Ssam 106525675Ssam /* 106630371Skarels * Load a 20 bit pointer into a Tapemaster pointer. 106725675Ssam */ 106830371Skarels cyldmba(reg, value) 106934487Skarels register u_char *reg; 107025979Ssam caddr_t value; 107124000Ssam { 107225979Ssam register int v = (int)value; 107325675Ssam 107425979Ssam *reg++ = v; 107525979Ssam *reg++ = v >> 8; 107625979Ssam *reg++ = 0; 107725979Ssam *reg = (v&0xf0000) >> 12; 107824000Ssam } 107924000Ssam 108025675Ssam /* 108125675Ssam * Unconditionally reset all controllers to their initial state. 108225675Ssam */ 108325675Ssam cyreset(vba) 108425675Ssam int vba; 108524000Ssam { 108625675Ssam register caddr_t addr; 108725675Ssam register int ctlr; 108824000Ssam 108925675Ssam for (ctlr = 0; ctlr < NCY; ctlr++) 109025675Ssam if (cyminfo[ctlr] && cyminfo[ctlr]->um_vbanum == vba) { 109125675Ssam addr = cyminfo[ctlr]->um_addr; 109225675Ssam CY_RESET(addr); 109330371Skarels if (!cyinit(ctlr, addr)) { 109425675Ssam printf("cy%d: reset failed\n", ctlr); 109525675Ssam cyminfo[ctlr] = NULL; 109625675Ssam } 109725675Ssam } 109824000Ssam } 109925979Ssam 110025979Ssam cyuncachetpb(cy) 110125979Ssam struct cy_softc *cy; 110225979Ssam { 110325979Ssam register long *lp = (long *)&cy->cy_tpb; 110425979Ssam register int i; 110525979Ssam 110625979Ssam for (i = 0; i < howmany(sizeof (struct cytpb), sizeof (long)); i++) 110725979Ssam uncache(lp++); 110825979Ssam } 110925979Ssam 111025979Ssam /* 111125979Ssam * Dump routine. 111225979Ssam */ 111330869Skarels #define DUMPREC (32*1024) 111425979Ssam cydump(dev) 111525979Ssam dev_t dev; 111625979Ssam { 111725979Ssam register struct cy_softc *cy; 111825979Ssam register int bs, num, start; 111925979Ssam register caddr_t addr; 112030294Ssam int unit = CYUNIT(dev), error; 112125979Ssam 112225979Ssam if (unit >= NCY || cyminfo[unit] == 0 || 112325979Ssam (cy = &cy_softc[unit])->cy_bs == 0 || YCUNIT(dev) >= NYC) 112425979Ssam return (ENXIO); 112525979Ssam if (cywait(&cy->cy_ccb)) 112625979Ssam return (EFAULT); 112725979Ssam #define phys(a) ((caddr_t)((int)(a)&~0xc0000000)) 112830294Ssam addr = phys(cyminfo[unit]->um_addr); 112925979Ssam num = maxfree, start = NBPG*2; 113025979Ssam while (num > 0) { 113130869Skarels bs = num > btoc(DUMPREC) ? btoc(DUMPREC) : num; 113225979Ssam error = cydwrite(cy, start, bs, addr); 113325979Ssam if (error) 113425979Ssam return (error); 113525979Ssam start += bs, num -= bs; 113625979Ssam } 113725979Ssam cyweof(cy, addr); 113825979Ssam cyweof(cy, addr); 113925979Ssam uncache(&cy->cy_tpb); 114025979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 114125979Ssam return (EIO); 114225979Ssam cyrewind(cy, addr); 114325979Ssam return (0); 114425979Ssam } 114525979Ssam 114625979Ssam cydwrite(cy, pf, npf, addr) 114725979Ssam register struct cy_softc *cy; 114825979Ssam int pf, npf; 114925979Ssam caddr_t addr; 115025979Ssam { 115125979Ssam 115225979Ssam cy->cy_tpb.tpcmd = CY_WCOM; 115325979Ssam cy->cy_tpb.tpcontrol = CYCW_LOCK|CYCW_25IPS|CYCW_16BITS; 115425979Ssam cy->cy_tpb.tpstatus = 0; 115525979Ssam cy->cy_tpb.tpsize = htoms(npf*NBPG); 115625979Ssam cyldmba(cy->cy_tpb.tplink, (caddr_t)0); 115725979Ssam cyldmba(cy->cy_tpb.tpdata, (caddr_t)(pf*NBPG)); 115825979Ssam cyldmba(cy->cy_ccb.cbtpb, (caddr_t)&cy->cy_tpb); 115925979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 116025979Ssam CY_GO(addr); 116125979Ssam if (cywait(&cy->cy_ccb)) 116225979Ssam return (EFAULT); 116325979Ssam uncache(&cy->cy_tpb); 116425979Ssam if (cy->cy_tpb.tpstatus&CYS_ERR) 116525979Ssam return (EIO); 116625979Ssam return (0); 116725979Ssam } 116825979Ssam 116925979Ssam cyweof(cy, addr) 117025979Ssam register struct cy_softc *cy; 117125979Ssam caddr_t addr; 117225979Ssam { 117325979Ssam 117425979Ssam cy->cy_tpb.tpcmd = CY_WEOF; 117525979Ssam cy->cy_tpb.tpcount = htoms(1); 117625979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 117725979Ssam CY_GO(addr); 117825979Ssam (void) cywait(&cy->cy_ccb); 117925979Ssam } 118025979Ssam 118125979Ssam cyrewind(cy, addr) 118225979Ssam register struct cy_softc *cy; 118325979Ssam caddr_t addr; 118425979Ssam { 118525979Ssam 118625979Ssam cy->cy_tpb.tpcmd = CY_REW; 118725979Ssam cy->cy_tpb.tpcount = htoms(1); 118825979Ssam cy->cy_ccb.cbgate = GATE_CLOSED; 118925979Ssam CY_GO(addr); 119025979Ssam (void) cywait(&cy->cy_ccb); 119125979Ssam } 119224000Ssam #endif 1193