/* cy.c 1.1 85/07/21 */ /* cy.c Tahoe version Mar 1983. */ #include "cy.h" #if NCY > 0 /* number of CYPHER tapes in system */ /* * Cypher tape driver * */ #include "../h/param.h" #include "../h/systm.h" #include "../h/vm.h" #include "../h/buf.h" #include "../h/dir.h" #include "../h/conf.h" #include "../h/user.h" #include "../h/file.h" #include "../machine/pte.h" #include "../vba/vbavar.h" #include "../h/mtio.h" #include "../machine/mtpr.h" #include "../h/ioctl.h" #include "../h/cmap.h" #include "../h/uio.h" #include "../vba/cyvar.h" #define NTM 1 /* number of TAPEMASTER controllers */ /* * There is a ccybuf per tape controller. * It is used as the token to pass to the control routines * and also acts as a lock on the slaves on the * controller, since there is only one per controller. * In particular, when the tape is rewinding on close we release * the user process but any further attempts to use the tape drive * before the rewind completes will hang waiting for ccybuf. */ struct buf ccybuf[NTM]; /* * Raw tape operations use rcybuf. The driver * notices when rcybuf is being used and allows the user * program to continue after errors and read records * not of the standard length (BSIZE). */ struct buf rcybuf[NTM]; long cybufused = 0; /* * Driver interface routines and variables. */ int cyprobe(), cyslave(), cyattach(), cydgo(), cyintr(); int cywait(), cyrewind(); unsigned tminphys(); struct vba_ctlr *cyminfo[NTM]; struct vba_device *cydinfo[NCY]; struct buf cyutab[NCY]; short cytotm[NCY]; extern char cyutl[]; long cystd[] = { 0x400000, 0 }; struct vba_driver cydriver = { cyprobe, cyslave, cyattach, cydgo, cystd, "yc", cydinfo, "cy", cyminfo, 0 }; /* bits in minor device */ #define CYUNIT(dev) (minor(dev)&07) /* tape unit number */ #define TMUNIT(dev) (cytotm[CYUNIT(dev)]) /* tape controller number */ #define T_NOREWIND 0x08 /* no rewind bit */ #define T_100IPS 0x10 /* high speed flag */ int pflag; /* probe flag, set every interrupt by cyintr */ #define INF (daddr_t)1000000L extern int hz; struct scp /* SYSTEM CONFIGUREATION POINTER */ { char sysbus ; /* width of system buss 0=8;1=16 */ char nu1 ; char pt_scb[4] ; /* pointer to ->SYSTEM CONFIGUREATION BLOCK */ }; /* absolute address - jumpered on the controller */ #define SCP ((struct scp *)0xc0000c06) struct Scb /* SYSTEM CONFIGUREATION BLOCK */ { char sysblk[1] ; /* 0x03 fixed value code */ char nu2[1] ; char pt_ccb[4] ; /* pointer to ->CHANNEL CONTROL BLOCK */ }Scb; struct ccb /* CHANNEL CONTROL BLOCK */ { char ccw[1] ; /* 0x11 normal; 0x09 clear non_vect interrupt */ char gate[1] ; /* This is "the" GATE */ char pt_tpb[4] ; /* pointer to ->TAPE OPERATION BLOCK or MOVE BLOCK */ }ccb; struct tpb /* TAPE OPERATIONS PARAMETER BLOCK */ { long cmd ; /* COMMAND (input) */ char control[2] ; /* CONTROL (input) */ short count ; /* RETURN COUNT (output) */ short size ; /* BUFFER SIZE (input/output) */ short rec_over ; /* RECORDS/OVERRUN (input/output) */ char pt_data[4] ; /* pointer to ->SOURCE/DEST (input) */ char status[2] ; /* STATUS (output) */ char pt_link[4] ; /* pointer to ->INTERRUPT/PARAMETER BLOCK (input) */ } tpb[NTM]; struct tpb cycool /* tape parameter block to clear interrupts */ = { 0L, /* command */ 0, 0, /* control */ 0, /* count */ 0, /* size */ 0, /* rec_over */ 0, 0, 0, 0, /* pt_data */ 0, 0, /* status */ 0, 0, 0, 0 /* pt_link */ } ; /* * Software state per tape transport. * * 1. A tape drive is a unique-open device; we refuse opens when it is already. * 2. We keep track of the current position on a block tape and seek * before operations by forward/back spacing if necessary. * 3. We remember if the last operation was a write on a tape, so if a tape * is open read write and the last thing done is a write we can * write a standard end of tape mark (two eofs). */ struct cy_softc { char cy_openf; /* lock against multiple opens */ char cy_lastiow; /* last op was a write */ daddr_t cy_blkno; /* block number, for block device tape */ daddr_t cy_nxrec; /* position of end of tape, if known */ daddr_t cy_timo; /* time until timeout expires */ short cy_tact; /* timeout is active */ short cy_count; /* return count of last operation */ char cy_status[2]; /* return status of last operation */ } cy_softc[NTM]; /* * I/O buffer for raw devices. */ char cybuf[TBUFSIZ*NBPG]; /* 10k buffer */ /* * States for um->um_tab.b_active, the per controller state flag. * This is used to sequence control in the driver. */ #define SSEEK 1 /* seeking */ #define SIO 2 /* doing seq i/o */ #define SCOM 3 /* sending control command */ #define SREW 4 /* sending a drive rewind */ /* * Determine if there is a controller for * a cypher at address ctlr_vaddr. * Reset the controller. * Our goal is to make the device interrupt. */ cyprobe(ctlr_vaddr) caddr_t ctlr_vaddr; { int *ip; pflag = 0; /* clear interrupt flag */ if (badcyaddr(ctlr_vaddr + 1)) /* check for versabuss timeout */ return (0); /* * Initialize the system configuration pointer */ ip = (int *)vtopte(0, btop(SCP)); *ip &= ~PG_PROT; *ip |= PG_KW; mtpr(SCP, TBIS); SCP->sysbus = 1; /* system width = 16 bits. */ /* initialize the pointer to the system configuration block */ set_pointer((int)&Scb.sysblk[0], (char *)SCP->pt_scb); /* * Initialize the system configuration block. */ Scb.sysblk[0] = 0x3; /* fixed value */ /* initialize the pointer to the channel control block */ set_pointer((int)&ccb.ccw[0], (char *)Scb.pt_ccb); /* * Initialize the channel control block. */ ccb.ccw[0] = 0x11; /* normal interrupts */ /* initialize the pointer to the tape parameter block */ set_pointer((int)&tpb[0], (char *)ccb.pt_tpb); /* * set the command to be CONFIGURE. */ tpb[0].cmd = CONFIG; tpb[0].control[0] = CW_I; /* interrupt on completion */ tpb[0].control[1] = CW_16bits; ccb.gate[0] = GATE_CLOSED; *ip &= ~PG_PROT; *ip |= PG_KR; mtpr(SCP, TBIS); TM_ATTENTION(ctlr_vaddr, 0xff); /* execute! */ if (cywait()) return(0); else return(1); } /* * Due to a design flaw, we cannot ascertain if the tape * exists or not unless it is on line - ie: unless a tape is * mounted. This is too severe a restriction to bear, * so all units are assumed to exist. */ /*ARGSUSED*/ cyslave(ui, ctlr_vaddr) struct vba_device *ui; caddr_t ctlr_vaddr; { return (1); } /* * Record attachment of the unit to the controller. */ /*ARGSUSED*/ cyattach(ui) struct vba_device *ui; { /* * Cytotm is used in TMUNIT to index the ccybuf and rcybuf * arrays given a cy unit number. */ cytotm[ui->ui_unit] = ui->ui_mi->um_ctlr; } int cytimer(); /* * Open the device. Tapes are unique open * devices, so we refuse if it is already open. * We also check that a tape is available, and * don't block waiting here; if you want to wait * for a tape you should timeout in user code. */ cyopen(dev, flag) dev_t dev; int flag; { register int cyunit, s; register struct vba_device *ui; register struct cy_softc *cy; cyunit = CYUNIT(dev); if (cyunit>=NCY || (cy = &cy_softc[cyunit])->cy_openf || (ui = cydinfo[cyunit]) == 0 || ui->ui_alive == 0) return ENXIO; cycommand(dev, (int)DRIVE_S, 1); /* drive status */ uncache(&tpb[cyunit].status[0]); if ((tpb[cyunit].status[0]&(CS_DR|CS_OL)) != (CS_DR|CS_OL)) { uprintf("cy%d: not online\n", cyunit); return EIO; } if ((flag&FWRITE) && (tpb[cyunit].status[0]&CS_P)) { uprintf("cy%d: no write ring\n", cyunit); return EIO; } cy->cy_openf = 1; cy->cy_blkno = (daddr_t)0; cy->cy_nxrec = INF; cy->cy_lastiow = 0; s = spl8(); if (cy->cy_tact == 0) { cy->cy_timo = INF; cy->cy_tact = 1; timeout(cytimer, (caddr_t)dev, 5*hz); } splx(s); return 0; } /* * Close tape device. * * If tape was open for writing or last operation was * a write, then write two EOF's and backspace over the last one. * Unless this is a non-rewinding special file, rewind the tape. * Make the tape available to others. */ cyclose(dev, flag) register dev_t dev; register flag; { register struct cy_softc *cy = &cy_softc[CYUNIT(dev)]; if (flag == FWRITE || (flag&FWRITE) && cy->cy_lastiow) { cycommand(dev, (int)WRIT_FM, 1); /* write file mark */ cycommand(dev, (int)WRIT_FM, 1); cycommand(dev, (int)SP_BACK, 1); /* space back */ } if ((minor(dev)&T_NOREWIND) == 0) /* * 0 count means don't hang waiting for rewind complete * rather ccybuf stays busy until the operation completes * preventing further opens from completing by * preventing a SENSE operation from completing. */ cycommand(dev, (int)REWD_TA, 0); cy->cy_openf = 0; } int commflag; /* signal cystrategy that it is called from cycommand */ /* * Execute a command on the tape drive * a specified number of times. */ cycommand(dev, com, count) dev_t dev; int com, count; { register struct buf *bp; int s; bp = &ccybuf[TMUNIT(dev)]; s = spl8(); while (bp->b_flags&B_BUSY) { /* * This special check is because B_BUSY never * gets cleared in the non-waiting rewind case. */ if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) break; bp->b_flags |= B_WANTED; sleep((caddr_t)bp, PRIBIO); } bp->b_flags = B_BUSY|B_READ; splx(s); bp->b_dev = dev; bp->b_repcnt = count; bp->b_command = com; bp->b_blkno = 0; commflag = 1; cystrategy(bp); commflag = 0; /* * In case of rewind from close, don't wait. * This is the only case where count can be 0. */ if (count == 0) return; iowait(bp); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags &= B_ERROR; } /* * Queue a tape operation. */ cystrategy(bp) register struct buf *bp; { int cyunit = CYUNIT(bp->b_dev); int s; register struct vba_ctlr *um; register struct buf *dp; /* * Put transfer at end of unit queue */ dp = &cyutab[cyunit]; bp->av_forw = NULL; s = spl8(); /* * Next piece of logic takes care of unusual cases when more than * a full block is required. * The driver reads the tape to a temporary buffer and * then moves the amount needed back to the process. * In this case, the flag NOT1K is set. */ if (commflag == 0) buf_setup(bp, 1); um = cydinfo[cyunit]->ui_mi; if (dp->b_actf == NULL) { dp->b_actf = bp; /* * Transport not already active... * put at end of controller queue. */ dp->b_forw = NULL; if (um->um_tab.b_actf == NULL) um->um_tab.b_actf = dp; else um->um_tab.b_actl->b_forw = dp; um->um_tab.b_actl = dp; } else dp->b_actl->av_forw = bp; dp->b_actl = bp; /* * If the controller is not busy, get * it going. */ if (um->um_tab.b_active == 0) cystart(um); splx(s); } /* * Start activity on a cypher controller. */ cystart(um) register struct vba_ctlr *um; { register struct buf *bp, *dp; register struct tpb *tp; register struct cy_softc *cy; register int phadr; int cyunit, timer; daddr_t blkno; caddr_t ctlr_vaddr; ctlr_vaddr = um->um_addr; /* * Look for an idle transport on the controller. */ loop: if ((dp = um->um_tab.b_actf) == NULL) return; if ((bp = dp->b_actf) == NULL) { um->um_tab.b_actf = dp->b_forw; goto loop; } cyunit = CYUNIT(bp->b_dev); cy = &cy_softc[cyunit]; tp = &tpb[cyunit]; /* * Default is that last command was NOT a write command; * if we do a write command we will notice this in cyintr(). */ cy->cy_lastiow = 0; uncache(&tp->status[0]); uncache(&tp->count); cy->cy_count = TM_SHORT(tp->count); cy->cy_status[0] = tp->status[0]; cy->cy_status[1] = tp->status[1]; if (cy->cy_openf < 0 || (bp->b_command != DRIVE_S) && ((tp->status[0]&CS_OL) != CS_OL)) { /* * Have had a hard error on a non-raw tape * or the tape unit is now unavailable * (e.g. taken off line). */ bp->b_flags |= B_ERROR; goto next; } if (bp == &ccybuf[TMUNIT(bp->b_dev)]) { /* * Execute control operation with the specified count. * Set next state; give 5 minutes to complete * rewind, or 10 seconds per iteration (minimum 60 * seconds and max 5 minutes) to complete other ops. */ if (bp->b_command == REWD_TA) { um->um_tab.b_active = SREW; cy->cy_timo = 5 * 60; } else { um->um_tab.b_active = SCOM; cy->cy_timo = imin(imax(10*(int)bp->b_repcnt, 60), 5*60); } /* * Prepare parameter block for controller */ tp->cmd = bp->b_command; tp->control[0] = (CW_I | (cyunit<b_dev)&T_100IPS) tp->control[1] = (CW_100ips | CW_16bits); else tp->control[1] = (CW_25ips | CW_16bits); if (bp->b_command == SP_BACK) { tp->control[1] |= CW_R; tp->cmd = SPACE; tp->rec_over = TM_SHORT((short)bp->b_repcnt); } if (bp->b_command == SP_FORW) tp->rec_over = TM_SHORT((short)bp->b_repcnt); if (bp->b_command == SRFM_BK) { tp->control[1] |= CW_R; tp->cmd = SERH_FM; tp->rec_over = TM_SHORT((short)bp->b_repcnt); } if (bp->b_command == SRFM_FD) tp->rec_over = TM_SHORT((short)bp->b_repcnt); tp->status[0] = tp->status[1] = 0; tp->count = 0; set_pointer((int)&tpb[cyunit], (char *)ccb.pt_tpb); goto dobpcmd; } /* * The following checks handle boundary cases for operation * on non-raw tapes. On raw tapes the initialization of * cy->cy_nxrec by cyphys causes them to be skipped normally */ if (bdbtofsb(bp->b_blkno) > cy->cy_nxrec) { /* * Can't read past known end-of-file. */ bp->b_flags |= B_ERROR; bp->b_error = ENXIO; goto next; } if (bdbtofsb(bp->b_blkno) == cy->cy_nxrec && bp->b_flags&B_READ) { /* * Reading at end of file returns 0 bytes. */ bp->b_resid = bp->b_bcount; clrbuf(bp); goto next; } if ((bp->b_flags&B_READ) == 0) /* * Writing sets EOF */ cy->cy_nxrec = bdbtofsb(bp->b_blkno) + 1; /* * If the data transfer command is in the correct place, * set up the tape parameter block, and start the i/o. */ if ((blkno = cy->cy_blkno) == bdbtofsb(bp->b_blkno)) { um->um_tab.b_active = SIO; cy->cy_timo = 60; /* premature, but should serve */ phadr = get_ioadr(bp, cybuf, CYmap, cyutl); if ( (bp->b_flags & B_READ) == 0) tp->cmd = WRIT_BU; else tp->cmd = READ_BU; tp->control[0] = (CW_I | (cyunit<b_dev)&T_100IPS) tp->control[1] = (CW_100ips | CW_16bits); else tp->control[1] = (CW_25ips | CW_16bits); tp->status[0] = tp->status[1] = 0; tp->count = 0; tp->size = TM_SHORT(bp->b_bcount); set_pointer(phadr, (char *)tp->pt_data); set_pointer((int)&tpb[cyunit], (char *)ccb.pt_tpb); goto dobpcmd; } /* * Tape positioned incorrectly; * set to seek forwards or backwards to the correct spot. */ um->um_tab.b_active = SSEEK; tp->cmd = SPACE; tp->control[0] = (CW_I | (cyunit<b_dev)&T_100IPS) tp->control[1] = (CW_100ips | CW_16bits); else tp->control[1] = (CW_25ips | CW_16bits); tp->status[0] = tp->status[1] = 0; set_pointer((int)&tpb[cyunit], (char *)ccb.pt_tpb); if (blkno < bdbtofsb(bp->b_blkno)) tp->rec_over = TM_SHORT((short)(blkno - bdbtofsb(bp->b_blkno))); else { tp->rec_over = TM_SHORT((short)(bdbtofsb(bp->b_blkno) - blkno)); tp->control[1] |= CW_R; } cy->cy_timo = imin(imax(10 * (int)TM_SHORT(tp->rec_over), 60), 5 * 60); dobpcmd: /* * Do the command in bp. */ timer = 8000; /* software tolerance for gate open */ uncache(&ccb.gate[0]); while (ccb.gate[0] != GATE_OPEN) { if (--timer == 0) { ccb.ccw[0] = 0x9; /* forget it...... */ TM_RESET(ctlr_vaddr, 0xff); bp->b_flags |= B_ERROR; goto next; } uncache(&ccb.gate[0]); } ccb.ccw[0] = 0x11; /* normal mode */ ccb.gate[0] = GATE_CLOSED; TM_ATTENTION(ctlr_vaddr, 0xff); /* execute! */ return; next: /* * Done with this operation due to error or * the fact that it doesn't do anything. * dequeue the transfer and continue processing this slave. */ um->um_tab.b_errcnt = 0; dp->b_actf = bp->av_forw; iodone(bp); goto loop; } /* * Kept for historical reasons. Probably not neccessary. */ cydgo(um) struct vba_ctlr *um; { } /* * Cy interrupt routine. */ /*ARGSUSED*/ cyintr(ctlr) int ctlr; { struct buf *dp; register struct buf *bp; register struct tpb *tp; register struct vba_ctlr *um = cyminfo[ctlr]; register struct cy_softc *cy; caddr_t ctlr_vaddr; int cyunit; register state; /* * First we clear the interrupt and close the gate. */ ctlr_vaddr = um->um_addr; ccb.ccw[0] = 0x9; /* clear the interrupt */ ccb.gate[0] = GATE_CLOSED; set_pointer((int)&cycool, (char *)ccb.pt_tpb); cycool.cmd = NO_OP; /* no operation */ cycool.control[0] = 0; /* No INTERRUPTS */ cycool.control[1] = 0; TM_ATTENTION(ctlr_vaddr, 0xff); /* cool it ! */ cywait(); /* * Now we can start handling the interrupt. */ pflag = 1; /* set for the probe routine */ if (intenable == 0) return; /* ignore all interrupts */ if ((dp = um->um_tab.b_actf) == NULL) return; bp = dp->b_actf; cyunit = CYUNIT(bp->b_dev); tp = &tpb[cyunit]; cy = &cy_softc[cyunit]; /* * If last command was a rewind, and tape is still * rewinding, wait for the rewind complete interrupt. */ if (um->um_tab.b_active == SREW) { um->um_tab.b_active = SCOM; /* uncache(&tp->status[1]); */ /* if (tp->status[1]&CS_CC != CS_CC) { */ /* not completed */ /* cy->cy_timo = 5*60; */ /* 5 minutes */ /* return; */ /* } */ } /* * An operation completed... update status */ cy->cy_timo = INF; uncache(&tp->count); uncache(&tp->status[0]); cy->cy_count = TM_SHORT(tp->count); cy->cy_status[0] = tp->status[0]; cy->cy_status[1] = tp->status[1]; if ((bp->b_flags & B_READ) == 0) cy->cy_lastiow = 1; state = um->um_tab.b_active; um->um_tab.b_active = 0; /* * Check for errors. */ if (tp->status[1] & CS_ERm) { /* * If we hit the end of the tape file, update our position. */ if (tp->status[0] & CS_FM) { cyseteof(bp); /* set blkno and nxrec */ state = SCOM; goto opdone; } /* If reading raw device and block was too short ignore the * error and let the user program decide what to do. */ if ((tp->status[0] & ER_TOF) && /* (bp->b_flags & B_PHYS) && */ (bp->b_flags & B_READ)) goto cont; cy->cy_openf = -1; /* cause to close */ printf("cy%d: hard error bn %d er=%x\n", cyunit, bp->b_blkno, tp->status[1]&CS_ERm); bp->b_flags |= B_ERROR; goto opdone; } /* * If we were reading block tape and the record * was too long, we consider this an error. */ cont: uncache(&tp->count); uncache(&tp->cmd); if (bp != &rcybuf[TMUNIT(bp->b_dev)] && (tp->cmd == READ_BU) && bp->b_bcount < TM_SHORT(tp->count)) { cy->cy_openf = -1; /* cause to close */ printf("cy%d: error - tape block too long \n", cyunit); bp->b_flags |= B_ERROR; goto opdone; } /* * No errors. * Advance tape control FSM. */ switch (state) { case SIO: /* * Read/write increments tape block number */ cy->cy_blkno++; end_transfer(bp, cybuf, CYmap, cyutl); goto opdone; case SCOM: /* * For forward/backward space record update current position. */ if (bp == &ccybuf[TMUNIT(bp->b_dev)]) switch (bp->b_command) { case SP_FORW: cy->cy_blkno += bp->b_repcnt; break; case SP_BACK: cy->cy_blkno -= bp->b_repcnt; break; } goto opdone; case SSEEK: cy->cy_blkno = bdbtofsb(bp->b_blkno); goto opcont; default: panic("cyintr"); } opdone: /* * Reset error count and remove * from device queue. */ um->um_tab.b_errcnt = 0; dp->b_actf = bp->av_forw; uncache(&tp->count); bp->b_resid = bp->b_bcount - TM_SHORT(tp->count); iodone(bp); /* * Circulate slave to end of controller * queue to give other slaves a chance. */ um->um_tab.b_actf = dp->b_forw; if (dp->b_actf) { dp->b_forw = NULL; if (um->um_tab.b_actf == NULL) um->um_tab.b_actf = dp; else um->um_tab.b_actl->b_forw = dp; um->um_tab.b_actl = dp; } if (um->um_tab.b_actf == 0) return; opcont: cystart(um); } cytimer(dev) int dev; { register struct cy_softc *cy = &cy_softc[CYUNIT(dev)]; int s; if (cy->cy_timo != INF && (cy->cy_timo -= 5) < 0) { printf("cy%d: lost interrupt\n", CYUNIT(dev)); cy->cy_timo = INF; s = spl8(); cyintr(TMUNIT(dev)); splx(s); return; } if (cy->cy_timo != INF ) timeout(cytimer, (caddr_t)dev, 5*hz); } cyseteof(bp) register struct buf *bp; { register int cyunit = CYUNIT(bp->b_dev); register struct cy_softc *cy = &cy_softc[cyunit]; register struct tpb *tp; tp = &tpb[cyunit]; uncache(&tp->rec_over); if (bp == &ccybuf[TMUNIT(bp->b_dev)]) { if (cy->cy_blkno > bdbtofsb(bp->b_blkno)) { /* reversing */ cy->cy_nxrec = bdbtofsb(bp->b_blkno) - (int)TM_SHORT(tp->rec_over); cy->cy_blkno = cy->cy_nxrec; } else { /* spacing forward */ cy->cy_blkno = bdbtofsb(bp->b_blkno) + (int)TM_SHORT(tp->rec_over); cy->cy_nxrec = cy->cy_blkno - 1; } return; } /* eof on read */ cy->cy_nxrec = bdbtofsb(bp->b_blkno); } cyread(dev, uio) dev_t dev; struct uio *uio; { register error; error = cyphys(dev, uio); if (error) return error; while (cybufused) sleep (&cybufused, PRIBIO+1); cybufused = 1; error = physio(cystrategy, &rcybuf[TMUNIT(dev)], dev, B_READ, tminphys, uio); cybufused = 0; wakeup (&cybufused); return error; } cywrite(dev, uio) dev_t dev; struct uio *uio; { register error; error = cyphys(dev, uio); if (error) return error; while (cybufused) sleep (&cybufused, PRIBIO+1); cybufused = 1; error = physio(cystrategy, &rcybuf[TMUNIT(dev)], dev, B_WRITE, tminphys, uio); cybufused = 0; wakeup (&cybufused); return error; } cyreset(uban) int uban; { register struct vba_ctlr *um; register cy0f, cyunit; register struct vba_device *ui; register struct buf *dp; for (cy0f = 0; cy0f < NTM; cy0f++) { if ((um = cyminfo[cy0f]) == 0 || um->um_alive == 0 || um->um_vbanum != uban) continue; printf(" cy%d", cy0f); um->um_tab.b_active = 0; um->um_tab.b_actf = um->um_tab.b_actl = 0; for (cyunit = 0; cyunit < NCY; cyunit++) { if ((ui = cydinfo[cyunit]) == 0 || ui->ui_mi != um || ui->ui_alive == 0) continue; dp = &cyutab[cyunit]; dp->b_active = 0; dp->b_forw = 0; dp->b_command = DRIVE_R; if (um->um_tab.b_actf == NULL) um->um_tab.b_actf = dp; else um->um_tab.b_actl->b_forw = dp; um->um_tab.b_actl = dp; if (cy_softc[cyunit].cy_openf > 0) cy_softc[cyunit].cy_openf = -1; } cystart(um); } } cyioctl(dev, cmd, data, flag) caddr_t data; dev_t dev; { int cyunit = CYUNIT(dev); register struct cy_softc *cy = &cy_softc[cyunit]; register struct buf *bp = &ccybuf[TMUNIT(dev)]; register callcount; int fcount; struct mtop *mtop; struct mtget *mtget; /* we depend of the values and order of the MT codes here */ static cyops[] = {WRIT_FM, SRFM_FD, SRFM_BK, SP_FORW, SP_BACK, REWD_TA, OFF_UNL, NO_OP}; switch (cmd) { case MTIOCTOP: /* tape operation */ mtop = (struct mtop *)data; switch(mtop->mt_op) { case MTWEOF: callcount = mtop->mt_count; fcount = 1; break; case MTFSF: case MTBSF: callcount = mtop->mt_count; fcount = INF; break; case MTFSR: case MTBSR: callcount = 1; fcount = mtop->mt_count; break; case MTREW: case MTOFFL: case MTNOP: callcount = 1; fcount = 1; break; default: return ENXIO; } if (callcount <= 0 || fcount <= 0) return EINVAL; while (--callcount >= 0) { cycommand(dev, cyops[mtop->mt_op], fcount); if ((bp->b_flags&B_ERROR) || cy->cy_status[1]&CS_ERm) break; } return geterror(bp); case MTIOCGET: mtget = (struct mtget *)data; mtget->mt_dsreg = cy->cy_status[0]; mtget->mt_erreg = cy->cy_status[1]; mtget->mt_resid = cy->cy_count; mtget->mt_type = MT_ISCY; break; default: return ENXIO; } return 0; } /* * Check that a raw device exists. * If it does, set up cy_blkno and cy_nxrec * so that the tape will appear positioned correctly. */ cyphys(dev, uio) dev_t dev; struct uio *uio; { register int cyunit = CYUNIT(dev); register daddr_t a; register struct cy_softc *cy; register struct vba_device *ui; if (cyunit >= NCY || (ui=cydinfo[cyunit]) == 0 || ui->ui_alive == 0) return ENXIO; cy = &cy_softc[cyunit]; a = bdbtofsb(uio->uio_offset >> PGSHIFT); cy->cy_blkno = a; cy->cy_nxrec = a + 1; return 0; } /* * Set a TAPEMASTER pointer (first parameter), into the * 4 bytes array pointed by the second parameter. */ set_pointer(pointer, dest) int pointer; char * dest; { *dest++ = pointer & 0xff; /* low byte - offset */ *dest++ = (pointer >> 8) & 0xff; /* high byte - offset */ *dest++ = 0; *dest = (pointer & 0xf0000) >> 12; /* base */ } cydump(dev) dev_t dev; { register struct vba_device *ui; register struct tpb *tp; int cyunit = CYUNIT(dev); int blk, num; int start; start = 0x800; num = maxfree; tp = &tpb[cyunit]; if (cyunit >= NCY || (ui=cydinfo[cyunit]) == 0 || ui->ui_alive == 0) return(ENXIO); if (cywait) return(EFAULT); while (num > 0) { blk = num > TBUFSIZ ? TBUFSIZ : num; bcopy(start*NBPG, cybuf, blk*NBPG); tp->cmd = WRIT_BU; tp->control[0] = cyunit<control[1] = (CW_100ips | CW_16bits); tp->status[0] = tp->status[1] = 0; tp->size = TM_SHORT(blk*NBPG); set_pointer((int)cybuf, (char *)tp->pt_data); set_pointer((int)&tpb[cyunit], (char *)ccb.pt_tpb); ccb.gate[0] = GATE_CLOSED; TM_ATTENTION(cyaddr, 0xff); /* execute! */ start += blk; num -= blk; if (cywait) return(EFAULT); uncache(&tp->status[1]); if (tp->status[1]&CS_ERm) /* error */ return (EIO); } cyeof(tp, cyunit); if (cywait) return(EFAULT); cyeof(tp, cyunit); if (cywait) return(EFAULT); uncache(&tp->status[1]); if (tp->status[1]&CS_ERm) /* error */ return (EIO); cyrewind(tp, cyunit); return (0); } cywait() { register cnt; cnt = 5000; /* 5 seconds timeout */ do { --cnt; DELAY(1000); uncache(&ccb.gate[0]); } while (cnt>0 && ccb.gate[0] == GATE_CLOSED); if (cnt == 0) return(1); /* timeout */ else return(0); } cyeof(tp, unit) register struct tpb *tp; int unit; { tp->cmd = WRIT_FM; tp->control[0] = unit<control[1] = (CW_100ips | CW_16bits); tp->status[0] = tp->status[1] = 0; tp->rec_over = TM_SHORT(1); set_pointer((int)&tpb[unit], (char *)ccb.pt_tpb); ccb.gate[0] = GATE_CLOSED; TM_ATTENTION(cyaddr, 0xff); /* execute! */ } cyrewind(tp, unit) register struct tpb *tp; int unit; { tp->cmd = REWD_TA; tp->control[0] = unit<control[1] = (CW_100ips | CW_16bits); tp->status[0] = tp->status[1] = 0; set_pointer((int)&tpb[unit], (char *)ccb.pt_tpb); ccb.gate[0] = GATE_CLOSED; TM_ATTENTION(cyaddr, 0xff); /* execute! */ } unsigned tminphys(bp) register struct buf *bp; { if (bp->b_bcount > sizeof cybuf) bp->b_bcount = sizeof cybuf; } #endif