#ifndef lint static char sccsid[] = "@(#)ar.c 1.1 94/10/31"; #endif /* * Copyright (c) 1987 by Sun Microsystems, Inc. */ #include "ar.h" #if NAR > 0 /* * Driver for Archive "Intelligent" (hah!) Streaming Tape * * Device name is ar? * * TODO: * test driver with more than one slave * test driver with more than one controller * test reset code * Make this LINT without errors. * Conditionally compile debug printfs so they don't take space. * Conditionally compile multi-drive-per-controller code, ditto. * Put bus error catch around RD/WR loops? * What if tape is yanked in mid-operation? * What if cable is yanked? Kernel loops, or Timeout & kernel crash * Make random addressing of block tapes work. * Test with a file system on the tape. * Check that all SPL()'s are correct and that all routines are * entered at the right SPL(). * Change to Bill Joy Normal Form. * Ensure that no infinite kernel hangs remain. * Try to break it with invalid protocol, see if it hangs things. * Try to break it with bad tapes (eg broken tape), ditto. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Debugging and performance monitoring variables */ int ardebug = 0; /* Print huge amounts of junk */ int arprintf = 0; /* Print messages about hardware hangs and recovery */ #define Dprintf if (ardebug) printf #define PRF /* Histogram flag gives distribution of queue sizes for tape buffers */ /* #define HISTOGRAM */ #define SPL() spl3() #define PRI (PZERO+5) /* We'll go this many times looking for Ready or Exception before quitting */ /* Quitting won't fix the problem but will prevent infinite kernel hangs. */ #define BREAKOUTCOUNT 100000 /* Glitchcount is the same thing, but for loops containing a printf, which takes lots more time, and clutters the console if you do it 1e5 times. */ #define GLITCHCOUNT 7 /* Define assorted debugging strings to save repeating them N times */ char ar_stat_bits[] = ARCH_BITS; /* * Driver Multibus interface routines and variables. */ int arprobe(), arattach(), arintr(); /* * Device information pointers, filled in by auto-config. */ struct mb_device *ardinfo[NAR]; /* * The mb_driver structure tells auto-config who we are and * what we want. */ struct mb_driver ardriver = { arprobe, 0, arattach, 0, 0, arintr, sizeof (struct ardevice), "ar", ardinfo, 0, 0, 0, }; /* bits in minor device */ /* The (short) is to avoid long multiplies when using this to subscript */ #define ARUNIT(dev) (short)(minor(dev)&03) #define ARCTLR(dev) (short)((minor(dev)>>2)&0x03) #define ARIDENT(dev) (minor(dev)&0x0F) #define ARUNWEDGE(dev) ((minor(dev)&0x20) != 0) #define T_NOREWIND 0x10 #define INF (daddr_t)10000000L #ifdef HISTOGRAM /* * Histogram buckets for queue length when an operation is queued. * * The first bucket arenqhist[0] is for overflow (queue len > 50). * If the queue was empty before queueing, arenqhist[1] is incremented. */ short arenqhist[50] = 0; #endif HISTOGRAM /* * Maximum # of buffers which writes to the tape are allowed to consume. * If a user attempts to write more than this number, we will sleep until * we free up a buffer. * * This is initialized to 1/2 the buffer pool in our probe routine. * It is decremented when we queue a buffer, and incremented when we * release one. We will sleep, rather than enqueue, until it is positive. * We will wakeup after dequeueing if it is negative. */ int armaxbufs; /* * States into which the tape drive can get. */ enum ARstates { FINstate = 0x00, IDLEstate, CMDstate, /* Finished, Idle, Command */ WFMinit, /* Write File Mark */ RFMinit, /* Read to File Mark */ REWinit, /* Rewind tape */ TENSEinit, /* Retension tape */ ERASEinit, /* Erase tape */ SELinit, /* Select a drive */ DESELinit, /* Deselect all drives */ RDSTinit, /* Read status */ CLOSEinit, CLOSEtwo, CLOSEthree, /* Deassert aronline */ READinit, READcmd, READburst, READfin, READidle, /* Read */ WRinit, WRcmd, WRburst, WRfin, WRidle, /* Write */ CMDOKinit, /* OK to issue commands? */ }; #ifdef PRF char *arstatename[] = { "Fin", "Idle", "Cmd", "Wfm", "Rfm", "Rew", "Tense", "Erase", "Sel", "Desel", "RdSt", "CloseInit", "CloseTwo", "CloseThree", "ReadInit", "ReadCmd", "ReadBurst", "ReadFin", "ReadIdle", "WriteInit", "WriteCmd", "WriteBurst", "WriteFin", "WriteIdle", "CmdOk", }; #endif PRF /* * Interesting tape commands */ #define AR_CLOSE 0 /* Close tape: WTM-if-writing, rewind */ #define AR_REWIND 1 /* Rewind (overlapped) */ #define AR_STATUS 2 /* Drive Status */ #define AR_READ 3 /* Read to MB memory */ #define AR_WRITE 4 /* Write to MB memory */ #define AR_WEOF 5 /* Write file mark (EOF) */ #define AR_ERASE 6 /* Erase entire tape */ #define AR_SELECT 7 /* Select drive of interest */ #define AR_DESELECT 8 /* Select no interesting drive */ #define AR_TENSE 9 /* Retension tape */ #define AR_SKIPFILE 10 /* Skip one file forward */ #define AR_CMDOK 11 /* See if ok to do cmd */ /* * Translation between generic tape commands and initial states of * the Archive state machine. */ enum ARstates ar_cmds[] = { /* CLOSE */ CLOSEinit, /* REWIND */ REWinit, /* STATUS */ RDSTinit, /* READ */ READinit, /* WRITE */ WRinit, /* WEOF */ WFMinit, /* ERASE */ ERASEinit, /* SELECT */ SELinit, /* DELSELECT */ DESELinit, /* TENSE */ TENSEinit, /* SKIPFILE */ RFMinit, /* CMD OK? */ CMDOKinit, }; /* * Software state per tape controller. * * 1. A tape controller is a unique-open device; we refuse 2nd opens. * 2. We keep track of the current position on a block tape. If a request * is made for the wrong (non-sequential) block, we give an error. * This avoids problems with readaheads and such, I think. -- JCGnu * 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 an end of tape mark. * 4. We remember the status registers after the last command, using * then internally and returning them to the SENSE ioctl. */ struct ar_softc { char sc_initted; /* Is controller initialized yet? */ char sc_openf; /* lock against multiple opens */ char sc_attached; /* Is controller attached to system? */ char sc_lastiow; /* last op was a write */ char sc_eoflag; /* raw eof flag */ u_char sc_cmdok; /* 0 => can only issue read/RFM or write/WFM */ /* The protocol for this is: sc->sc_cmdok = 0; (void) arcommand(dev, AR_CMDOK, 1); if (sc->sc_cmdok) ... */ char sc_selecteddev; /* currently selected drive */ char sc_selectok; /* OK for aropen() to issue select command */ /* We remember this because tape drive won't tell us if it's OK. It's OK if at BOT, or if tape inserted but not yet positioned to BOT. Thanks assholes at Archive. */ char sc_quietly; /* =1 if errors should not printf; they will be reported by more intelligent high level software. Normally 0. */ char sc_writeable; /* =1 if opened for write */ daddr_t sc_blkno; /* block number, for block device tape */ daddr_t sc_nxrec; /* position of end of tape, if known, or INF */ enum ARstates sc_state; /* Current state of hard/software */ enum ARstates sc_oldstate; /* Previous state of sc_state */ enum ARstates sc_hangstate; /* Cur state when Rdy hang occurs */ struct arstatus sc_status; /* Status at last "Read status" cmd */ int sc_size; /* Size of buffer to read/write */ char *sc_bufptr; /* Pointer to buffer to read/write */ int sc_count; /* # times to repeat high-level op */ u_char sc_drive; /* Drive # to select/deselect */ u_char sc_histate; /* Higher level state than sc_state */ struct ardevice *sc_addr;/* Address of I/O registers */ u_char sc_qidle; /* =0 if buf in progress, =1 if not. */ short sc_stops; /* number of tape stops since last MTIOCGET */ short sc_softerrs; /* number of soft errors since last MTIOCGET */ int sc_ident; /* Identifying number of this drive/ctlr */ struct buf sc_pend_chain; /* head of queue of buffers awaiting I/O */ struct buf cbuf; /* fake buf for ioctl's and other commands */ struct buf rbuf; /* for raw tape ops; tape errs OK in an rbuf */ /* When adding new fields to ar_softc, also initialize them in arinit(). */ } ar_softc[NAR]; /* * Redefined fields in the buf header for cbufs (control commands) */ #define b_repcnt b_bcount #define b_command b_resid /* * States for sc->sc_histate, the per controller state flag. * This is used to sequence control in the driver. */ #ifdef SEEK #define SSEEK 1 /* seeking for correct posn (unused) FIXME */ #endif SEEK #define SIO 2 /* doing seq i/o */ #define SCOM 3 /* sending control command */ #define SSWAIT 4 /* Previous block completed, nothing new started. If an interrupt occurs, look in sc_pend_chain for new bufs to start. */ /* * Determine if there exist a device at address . */ arprobe(reg) register caddr_t reg; { extern int nbuf; /* # buffers in the system */ armaxbufs = nbuf >> 1; /* Set max # bufs we'll allow ourself */ if (pokec((char *)(&((struct ardevice *)reg)->arunwedge), 0)) { return (0); /* Bus error trying to touch it */ } return (sizeof (struct ardevice)); } /* * Record attachment of the unit to the system. */ /*ARGSUSED*/ arattach(md) register struct mb_device *md; { register struct ar_softc *sc; sc = &ar_softc[md->md_unit]; sc->sc_addr = (struct ardevice *)md->md_addr; sc->sc_attached++; } /* * Initialize a controller */ #define SPININIT 1000000 arinit(sc, dev) register struct ar_softc *sc; dev_t dev; { register int count; register struct ardevice *araddr = sc->sc_addr; sc->sc_initted = 0; /* until later */ sc->sc_openf = 0; sc->sc_lastiow = 0; sc->sc_selecteddev = -1; sc->sc_selectok = 1; sc->sc_stops = 0; sc->sc_softerrs = 0; sc->sc_quietly = 0; sc->sc_writeable = 0; sc->sc_blkno = 0; sc->sc_nxrec = INF; sc->sc_state = IDLEstate; sc->sc_oldstate = IDLEstate; sc->sc_hangstate = IDLEstate; /* sc->sc_status = 0; Doesn't work, so leave it alone. */ sc->sc_size = 0; sc->sc_bufptr = (char *) 0x1ff001; /* very funny buf addr */ /* sc->sc_attached had better already be 1. */ sc->sc_drive = 0; sc->sc_histate = 0; /* sc->sc_addr had better already be initialized. */ sc->sc_qidle = 1; sc->sc_stops = 0; sc->sc_eoflag = 0; sc->sc_cmdok = 0; sc->sc_ident = ARIDENT(dev); /* When adding new fields to softc, be sure to initialize them here. */ /* FIXME, should initialize buffer headers here too */ araddr->arunwedge = 1; /* Take it out of burst mode wedge */ araddr->arburst = 0; araddr->arreq = 0; araddr->arxfer = 0; araddr->arcatch = 0; araddr->arexcie = 0; araddr->arrdyie = 0; /* * If tape is up from previous system operation, * take it down gently. */ if (araddr->aronline) { Dprintf("ar*init tape online\n"); araddr->aronline = 0; /* Writes TM (if writing) & rewinds */ } count = SPININIT; while (!araddr->arrdy && !araddr->arexc && count) count--; if (count == 0) Dprintf("ar: Timeout waiting for Ready at %x\n", araddr); if (araddr->arrdy) Dprintf("ar*init arrdy on before reset\n"); if (araddr->arexc) Dprintf("ar*init arexc on before reset\n"); /* Tape is ready or exceptional. Reset it for good measure. */ araddr->arreset = 1; DELAY(25); /* at least 13 usec */ araddr->arreset = 0; count = SPININIT; while (!araddr->arexc && count) count--; if (count == 0) { /* This happens if no tape ctlr is there, so be silent. */ Dprintf("ar: Timeout waiting for Exception after reset\n"); return (0); } /* Now read back status from the reset. */ sc->sc_initted = 1; /* Must do first so interrupt OK */ araddr->aronline = 1; /* Must do first so RDST microcode doesn't play games with arrdy line. See comments in aropen(). */ if (arcommand(dev, AR_STATUS, 1)) { araddr->aronline = 0; sc->sc_initted = 0; /* Try again on next open */ return (0); } sc->sc_openf = 0; /* In case arcommand() set it to -1 */ return (1); } /* * Open the device. * * Archive controllers can control up to 4 drives, but only one of them * can be off BOT at once. So we only allow one drive per controller to * be open at once, and just do a "select" command when the drive is * opened, to point the controller to that drive. * * 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. */ aropen(dev, flag) dev_t dev; int flag; { register int unit = ARCTLR(dev); register struct ar_softc *sc = &ar_softc[unit]; register struct ardevice *araddr; int foo; Dprintf("ar*open(%d, %d)\n", dev, flag); if (unit >= NAR || !sc->sc_attached) { uprintf("ar%d: no such controller\n", ARIDENT(dev)); return (ENXIO); } /* * Initialize the controller, if this is the first open. * * If arinit() claims the device doesn't exist, believe it. * (On the next open we will call init again.) */ if (!sc->sc_initted || ARUNWEDGE(dev)) { if (!arinit(sc, dev)) { uprintf("ar%d: would not initialize\n", ARIDENT(dev)); return (ENXIO); } } if (sc->sc_openf) { uprintf("ar%d: already open\n", sc->sc_ident); return (EBUSY); } sc->sc_openf = 1; /* * There is a problem in the Archive in that after each command, * it goes thru a cleanup routine. If aronline is not asserted, * this cleanup routine drops arrdy while it rewinds the tape * to BOT. It deasserts arrdy for 90 us even if the tape is * already at BOT. This causes us problems because we get a arrdy * interrupt and then discover that arrdy is gone. * * The problem has been circumvented with a two-pronged approach. * The first prong is to always have ONLINE turned on. There is * one place (CLOSEinit/CLOSEtwo) where we can't do that, so * we added a kludge test to arintproc() too. * * This info was obtained from Lou Domshy, Mgr. of Product Mgmt and * Applications Engineering(?) at the Archive factory, 714-641-0279, * on 1 December 1982, by John Gilmore of Sun Microsystems. */ araddr = sc->sc_addr; araddr->aronline = 1; /* Let ctrlr know we are doing a series */ /* * First select the drive we're interested in. * * Since the select command doesn't work when we aren't at BOT, * we have to reject the open if we aren't opening the same drive as * last time. */ sc->sc_quietly = 1; /* Let us do the error reporting */ if (sc->sc_selecteddev != ARUNIT(dev)) { if (!sc->sc_selectok) { uprintf("ar%d: can't switch drives in mid-tape\n", sc->sc_ident); sc->sc_openf = 0; return (EIO); } foo = arcommand(dev, AR_SELECT, 1); if (foo == EINTR) { /* FIXME: May want to expand this to a variety of errors */ Dprintf("ar*open SELECT gave EINTR\n"); err: foo = sc->sc_openf; sc->sc_openf = 1; /* In case cmd set to -1 */ (void) arcommand(dev, AR_DESELECT, 1); /* Unlight LED */ sc->sc_selecteddev = -1; /* We dunno who */ sc->sc_selectok = 1; /* But it's OK to chg */ sc->sc_quietly = 0; /* Error reports on */ araddr->aronline = 0; /* After hard error, force re-init. */ if (foo < 0 || (int)sc->sc_openf < 0) sc->sc_initted = 0; sc->sc_openf = 0; return (EIO); } sc->sc_selecteddev = ARUNIT(dev); } /* * Now get its status and check on a few things. */ sc->sc_cmdok = 0; (void) arcommand(dev, AR_CMDOK, 1); /* See if OK to issue cmds */ if (sc->sc_cmdok) { if (arcommand(dev, AR_STATUS, 1)) { /* interrupted */ goto err; } if (sc->sc_status.NoDrive) { uprintf("ar%d: no such drive\n", sc->sc_ident); goto err; } if (sc->sc_status.NoCart) { uprintf("ar%d: no cartridge in drive\n", sc->sc_ident); goto err; } } if ((flag&FWRITE) && sc->sc_status.WriteProt) { uprintf("ar%d: cartridge is write protected\n", sc->sc_ident); goto err; } sc->sc_quietly = 0; /* Turn error reports to console back on */ sc->sc_writeable = 0 != (flag&FWRITE); sc->sc_blkno = (daddr_t)0; sc->sc_nxrec = INF; sc->sc_lastiow = 0; Dprintf("ar*open exiting\n"); 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. */ arclose(dev, flag) register dev_t dev; register flag; { register struct ar_softc *sc = &ar_softc[ARCTLR(dev)]; Dprintf("ar*close(%d, %d)\n", dev, flag); if ((int)sc->sc_openf <= 0) goto seeyalater; else if (minor(dev)&T_NOREWIND) { sc->sc_selectok = 0; /* Probably not OK to select drive */ if (sc->sc_lastiow) { /* * No Rewind, and last op was write. * Write a file mark, and continue. Leave aronline on. * Read status to get tape stops info. */ (void) arcommand(dev, AR_WEOF, 1); (void) arcommand(dev, AR_STATUS, 1); } else { /* * No rewind, and last op was read/position. * If in mid-file (not at tapemark) we can't do * anything at all. * See if tape is at BOT. If so, de-select it. * Reading status also tells us tape stops * (breaks in streaming) info. */ sc->sc_cmdok = 0; (void) arcommand(dev, AR_CMDOK, 1); if (sc->sc_cmdok) { (void) arcommand(dev, AR_STATUS, 1); if (sc->sc_status.BOT) { (void) arcommand(dev, AR_DESELECT, 1); sc->sc_selecteddev = -1; sc->sc_selectok = 1; /* OK to sel */ } } } } else { /* * Write file mark and rewind, by dropping aronline. * FIXME. These 3 commands should be moved into AR_CLOSE * in order that the user program can continue while the * tape is rewinding. */ (void) arcommand(dev, AR_CLOSE, 1); /* Shut down things */ (void) arcommand(dev, AR_STATUS, 1); /* Read block counts */ (void) arcommand(dev, AR_DESELECT, 1); /* Turn LED off */ sc->sc_selecteddev = -1; sc->sc_selectok = 1; /* OK for aropen() to sel drive */ sc->sc_eoflag = 0; /* Not at eof after rewind */ } seeyalater: /* After hard error, force re-init. */ if ((int)sc->sc_openf < 0) { sc->sc_initted = 0; sc->sc_addr->aronline = 0; /* Terminate tape op if any */ /* This might give an interrupt but with initted=0 we will ignore and disable anyway. */ } sc->sc_openf = 0; Dprintf("ar*close exiting\n"); } /* * Execute a command on the tape drive * a specified number of times. * Allow signals to occur (with arsleep) so the * poor user can use his terminal if something goes wrong * * FIXME: Should a low-level death wakeup bp and armaxbufs? */ arcommand(dev, com, count) dev_t dev; int com, count; { register struct buf *bp; register struct ar_softc *sc = &ar_softc[ARCTLR(dev)]; register int error, s; Dprintf("ar*command(%x, %d, %d)\n", dev, com, count); if ((int)sc->sc_openf < 0) return (EIO); bp = &sc->cbuf; s = SPL(); while (bp->b_flags&B_BUSY) { bp->b_flags |= B_WANTED; if (arsleep((caddr_t)bp, PRI)) { (void) splx(s); return (EINTR); } } bp->b_flags = B_BUSY|B_READ; (void) splx(s); bp->b_dev = dev; bp->b_repcnt = count; bp->b_command = com; bp->b_blkno = 0; #ifdef PRF2 Dprintf("ar*command before strategy %x\n", bp); #endif PRF arstrategy(bp); #ifdef PRF2 Dprintf("ar*command after strategy %x\n", bp); #endif PRF s = SPL(); while ((bp->b_flags&B_DONE)==0) { bp->b_flags |= B_WANTED; if (arsleep((caddr_t)bp, PRI)) return (1); } (void) splx(s); #ifdef PRF2 Dprintf("ar*command before geterror\n"); #endif PRF error = geterror(bp); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags = 0; /* note: clears B_BUSY */ return (error); } /* * Queue a tape operation. */ arstrategy(bp) register struct buf *bp; { int unit = ARCTLR(bp->b_dev); struct ar_softc *sc = &ar_softc[unit]; register struct buf *dp = &sc->sc_pend_chain; int s; bp_mapin (bp); Dprintf ("ar*strategy bp=%x mapped in; addr=%x\n", bp, bp->b_un.b_addr); bp->av_forw = NULL; s = SPL(); /* * See if we have used up our quota of write buffers; if so, sleep. */ if ((bp->b_flags & B_READ) == 0 && bp != &sc->cbuf && bp != &sc->rbuf) { armaxbufs--; while (armaxbufs < 0) { Dprintf("ar*strategy sleeping for maxbufs\n"); /* FIXME: This code has never been tested, since the block device is "unsupported". Test it and fix it. -- JCG 2Jun83 */ if (arsleep((caddr_t)&armaxbufs, PRI)) { /* Sleep interrupted, cancel out */ armaxbufs++; bp->b_flags |= B_ERROR; bp->b_error = EINTR; sc->sc_histate = SSWAIT; sc->sc_pend_chain.b_actf = bp->av_forw; biodone(bp); goto goaway; } /* FIXME: End of untested code. */ } } /* * Put transfer at end of unit queue */ if (dp->b_actf == NULL) { dp->b_actf = bp; dp->b_forw = NULL; } else dp->b_actl->av_forw = bp; dp->b_actl = bp; #ifdef HISTOGRAM /* Count how big the queue now is, and histogram that. */ count = 0; bp = dp; /* Start at head of queue */ while ((bp = bp->av_forw) != NULL) count++; if (count >((sizeof arenqhist)/(sizeof arenqhist[0]))) count = 0; arenqhist[count]++; /* Histogram the queue size. */ Dprintf("ar*strategy queueing %x, qlen %d, maxbufs %d\n", bp, count, armaxbufs); #else HISTOGRAM Dprintf("ar*strategy queueing %x, maxbufs %d\n", bp, armaxbufs); #endif HISTOGRAM /* * If the controller is not busy, get * it going. */ if (sc->sc_qidle) arstart(unit); goaway: (void) splx(s); } /* * Start activity on a device. * * Each command to be sent to a device is in a buffer. (If it's a * control command, the buffer is the cbuf in softc for this controller. * If it's a block read or write, the buffer comes out of the system buffer * cache. If it's a raw read or write, the buffer is the rbuf in softc.) * The commands are queued in order in a singly linked list which comes off * sc_pend_chain. (We keep track of both the first and last things on the * list; it's otherwise singly linked.) Buffers are always added on the * end of the chain and taken off the front. This routine starts the I/O * requested by the buffer on the front of the chain. It stays on the front * of the chain until it is completed, then it is dequeued and we might * start the next op, if there is one. * * When entered, sc_qidle must be true (the queue must be idle). * * Must be entered at SPL() or higher. * * This is SOFTWARE oriented software. It doesn't know or care about the * state of the hardware; what it knows about is the software (buffers, * queued commands, etc). It calls arstart_cmd() to kick the hardware. * * FIXME! This routine diddles internal sc_ variables of the interrupt * low-level routine. IT MUST NOT, until it knows that it's safe to * initiate the I/O -- that is, the low-level stuff is IDLE or FIN. Then * it will be safe to do serious things in the low-level while no buffer * is atop the queue (eg, rewind tape after releasing buffer). */ arstart(unit) register int unit; { register struct ar_softc *sc = &ar_softc[unit]; register struct buf *bp; int cmd; #ifdef PRF2 Dprintf("ar*start(%d)\n", unit); #endif if (!sc->sc_qidle) panic("arstart qidle not set"); loop: /* * See if this controller has any buffers queued for it. */ if ((bp = sc->sc_pend_chain.b_actf) == NULL) { Dprintf("ar*start unit %d found empty\n", unit); return; } #ifdef PRF Dprintf("ar*start unit %d sc %x bp %x\n", unit, sc, bp); #endif bp_mapin (bp); Dprintf ("ar*start bp=%x mapped in; addr=%x\n", bp, bp->b_un.b_addr); /* * Default is that last command was NOT a write command; * if we do a write command we will notice this below. */ sc->sc_lastiow = 0; if ((int)sc->sc_openf < 0) { /* * Have had a hard error on a non-raw tape * or the tape unit is now unavailable * (e.g. taken off line, or broken). */ bp->b_flags |= B_ERROR; bp->b_error = EIO; goto next; } if (bp == &sc->cbuf) { /* * Execute control operation with the specified count. */ sc->sc_histate = SCOM; /* State for interrupt response */ cmd = bp->b_command; sc->sc_size = 0; if (cmd == AR_SKIPFILE) { /* * When we get the EXCEPTION/FileMark at the end of * this SKIPFILE, set end-of-file pointer to blkno, * which happens to be infinity; this lets us read * the next file if we care to. */ sc->sc_blkno = INF; } } else { /* * Did we hit file mark before dispatching the last buffer? * If so, the next read (this one) should return 0 bytes. * This flag is cleared by ioctl * commands that move the tape, and by RFM command. */ if ((bp == &sc->rbuf) && sc->sc_eoflag) { sc->sc_eoflag = 0; bp->b_resid = bp->b_bcount; goto next; } /* * Do the data transfer specified by bp (read or write). * This could be a block transfer or a raw transfer, we * really don't care. */ /* * The following checks handle boundary cases for operation * on block tapes. On raw tapes the initialization of * sc->sc_nxrec by arphys causes them to be skipped normally * (except in the case of retries). * * These are important because block tapes do read-ahead. * When we detect an EOF, we must derail the readahead requests * which arrive (or are already queued) after the EOF. (Block * tapes only allow you to read one file.) */ if (bp->b_blkno > sc->sc_nxrec) { /* * Can't read past known end-of-file. */ bp->b_flags |= B_ERROR; bp->b_error = EIO; goto next; } if (bp->b_blkno == sc->sc_nxrec && bp->b_flags&B_READ) { /* * Reading at end of file returns 0 bytes. */ clrbuf(bp); bp->b_resid = bp->b_bcount; goto next; } if ((bp->b_flags&B_READ) == 0) /* * Writing sets EOF */ sc->sc_nxrec = bp->b_blkno+(bp->b_bcount >> AR_BSHIFT); /* * If the data transfer command is in the correct place, * set up, and start the I/O. */ if (sc->sc_blkno == bp->b_blkno) { sc->sc_size = bp->b_bcount; sc->sc_histate = SIO; if ((bp->b_flags&B_READ) == 0) { cmd = AR_WRITE; sc->sc_lastiow = 1; } else { cmd = AR_READ; } } else { /* * Tape positioned incorrectly; * set to seek forwards or backwards to the correct spot. * This happens for raw tapes only on error retries. */ /* We eventually want to rewind and/or forwardspace. */ Dprintf("ar: block %d requested at tape posn %d, bp=%x\n", bp->b_blkno, sc->sc_blkno, bp); bp->b_flags |= B_ERROR; bp->b_error = ENOTBLK; /* This ain't a block dev */ goto next; #ifdef SEEK /* sc->sc_histate = SSEEK; not implem */ /* sc->sc_size = 0; we don't care since not implem. */ #endif SEEK } } /* * Start the physical device (by invoking the state machine). * If the operation is not complete, just return. If it is, * unchain the request and call biodone() to free up the user * process or whoever. */ sc->sc_drive = ARUNIT(bp->b_dev); sc->sc_bufptr = bp->b_un.b_addr; /* sc->sc_size is already set. */ sc->sc_qidle = arstart_cmd(sc, ar_cmds[cmd]); /* Start it. */ if (sc->sc_qidle == 2) { /* error in starting the command */ sc->sc_qidle = 1; bp->b_flags |= B_ERROR; bp->b_error = EIO; goto next; } if (sc->sc_qidle) goto loop; /* This buf done */ return; next: /* * Done with this operation due to error or * the fact that it doesn't do anything. */ Dprintf("ar*start op completed bp %x addr %x size %x blkno %d flags %x err %d\n" , bp, bp->b_un.b_addr, bp->b_bcount, bp->b_blkno, bp->b_flags, bp->b_error); sc->sc_histate = SSWAIT; sc->sc_pend_chain.b_actf = bp->av_forw; /* * We're done with this buffer; allow another one to be queued. */ if ((bp->b_flags & B_READ) == 0 && bp != &sc->cbuf && bp != &sc->rbuf) { if (armaxbufs <= 0) wakeup((caddr_t)&armaxbufs); armaxbufs++; Dprintf("ar*start buffer done, maxbufs=%d\n", armaxbufs); } biodone(bp); goto loop; } /* * Begin execution of a device command for the device pointed to by * sc. The command begins execution in state newstate. * * This is HARDWARE oriented software. It doesn't know or care of * state of buffers, etc. Its result reflects what the hardware is * doing, not what the software is doing. * * The device is assumed to be in one of the FIN or IDLE states: * IDLEstate, FINstate, READfin, READidle, WRfin, or WRidle. * This is a requirement, since various fields in sc have already * been set up for us, and the use of those fields would conflict * with their use by the interrupt routine if we weren't idle or fin. * * Our result is: * 0 if the operation has been started, but is not yet * complete; * 1 if the operation is complete. * 2 if the operation is invalid and was not started. * * Must be entered at SPL() or higher. * * FIXME, this is too big a snake pit to leave this way. * (Actually much less of a pit nowadays. Think about it.) */ arstart_cmd(sc, newstate) register struct ar_softc *sc; register enum ARstates newstate; { register enum ARstates oldstate = sc->sc_state; /* * Continuous Reads and Writes need not go back to READinit * or WRinit; they can just tail-end into the Burst state. */ if ((oldstate == WRidle || oldstate == WRfin)) { if (newstate == WRinit) newstate = WRburst; else if (newstate != CLOSEinit && newstate != WFMinit) return (2); } else if ((oldstate == READidle || oldstate == READfin)) { if (newstate == READinit) newstate = READburst; else if (newstate == REWinit) newstate = CLOSEinit; else if (newstate != CLOSEinit && newstate != RFMinit) return (2); /* * Bitch if we are not in a reasonable state now */ } else if (oldstate != IDLEstate && oldstate != FINstate) { printf("ar: start_cmd bad state %s=%x for newstate %s=%x\n", arstatename[(int)oldstate], oldstate, arstatename[(int)newstate], newstate); sc->sc_openf = -1; /* Force close & re-initialize */ return (2); /* Cancel it, we know it'll just screw h/w */ } sc->sc_oldstate = sc->sc_state; sc->sc_state = newstate; #ifdef FIXME /* * These assignments should happen here, once we know the * low level is in an idle state. They currently happen in * arstart() instead, which is a bug. */ sc->sc_drive = ARUNIT(bp->b_dev); sc->sc_bufptr = bp->b_un.b_addr; /* sc->sc_size is already set. */ #endif FIXME Dprintf("ar*starting old state %s=%x, new %s=%x, addr %x, size %x\n", arstatename[(int)oldstate], oldstate, arstatename[(int)newstate], newstate, sc->sc_bufptr, sc->sc_size); /* * If we were in an IDLE state, call arintproc() to begin * the operation. If we were in a FIN state, waiting for a * completion interrupt, just keep waiting; the interrupt * will cause us to kick off the machine in its new state. * If we do run it now, and it returns 1, the op is done. */ if (oldstate == IDLEstate || oldstate == READidle || oldstate == WRidle) { return (arintproc(sc)); } return (0); /* Command not finished */ } /* * Our interrupt routine. * * Must be entered at SPL() or higher. * * Result is 0 if this is not our interrupt, 1 if it is. */ arintr() { register struct ar_softc *sc; register struct ardevice *araddr; register int unit; for (unit = 0, sc = ar_softc; unit < NAR; unit++, sc++) { if (sc->sc_attached) { araddr = sc->sc_addr; #ifdef PRF2 Dprintf("ar*intr araddr %x unit %d sc %x\n", araddr, unit, sc); #endif if (araddr->arintr) goto found; } } return (0); /* Not any of our devices, boss... */ /* * We found a device that is interrupting us. * * unit, sc, and araddr are all set up. */ found: if (!sc->sc_initted) { /* * This interrupt is for a device which we have not opened * yet. Since we don't really want to deal with it yet, * just shut off the interrupt and tell Mother we're done. * When somebody opens it, we'll go thru the whole arinit() * rigamarole. */ printf("ar: interrupt from uninitialized controller %x\n", araddr); araddr->arrdyie = 0; araddr->arexcie = 0; return (1); } /* * Process the interrupt. * * If this interrupt causes the current operation (chained via * sc_pend_chain) to complete, arintproc() returns 1, and we * see if we can start the next one. * * In either case, we return to ioint indicating that the interrupt * has been serviced. */ sc->sc_qidle = arintproc(sc); if (sc->sc_qidle) { /* There is no buffer currently active. Start I/O on the new current buffer, if there is one. */ if (sc->sc_pend_chain.b_actf != NULL) arstart(unit); } return (1); /* Interrupt was serviced. */ } /* * Process an interrupt, or an initial action on the device. * * We must be entered at interrupt level SPL(). * * We negate then assert arcatch, so it will catch the next * transition of arrdy, which might occur while we are running. * If armachine() exits with arrdyie, the transition also causes * an interrupt. (It normally does, unless we are idle.) * * The result of arintproc() is: * 0 if an operation is still in progress. * 1 if no operation is in progress. Another one can be started. * * We depend on armachine() to never return a zero once it has returned * a one, until somebody (arstart_cmd, or our SIO case stmt) twitches its state. */ arintproc(sc) register struct ar_softc *sc; { register struct ardevice *araddr = sc->sc_addr; register struct buf *bp = sc->sc_pend_chain.b_actf; int breakout = GLITCHCOUNT; /* * WARNING! bp may not reflect the buffer we are processing. * If sc_histate == SSWAIT, we dispatched that buffer already * (to let the user run earlier), so the buffer in bp is * the next buffer on the chain of requests. TAKE CARE to * not reference bp UNTIL you have checked histate. */ /* * If an exception exists, do something. * * Since the Archive signals some exceptions before we are * ready for them (eg, FileMark before we try to read that * block, unlike a 9-track tape) we must hold these in * software for dealing-with next time we get a command. */ again: if (araddr->arexc) { /* * This interrupt is from the true level of EXC. * * An error has occurred. Deal with it somehow. * If we are reading status, just do it; else do our own * RDST to find out the problem, and cancel the current * operation. */ if (sc->sc_state == RDSTinit) goto doit; Dprintf("ar*intr EXC set, old state %s=%x\n", arstatename[(int)sc->sc_state], sc->sc_state); sc->sc_oldstate = sc->sc_state; sc->sc_state = RDSTinit; /* * Clear EdgeReady and enable it so the next arrdy * edge will be caught (possibly inside armachine()). */ araddr->arcatch = 0; araddr->arcatch = 1; while (!armachine(sc)) { /* Shouldn't happen! */ printf("ar: RDST did not complete\n"); } exc: if (sc->sc_status.FileMark) { /* Eof signaled now means that NEXT block is an EOF. */ sc->sc_eoflag = 1; sc->sc_nxrec = sc->sc_blkno + 1; goto advance; } /* FIXME: GettingFlakey does NOT cause an EXC, it's just an info bit. */ if (sc->sc_status.GettingFlakey && !sc->sc_status.HardErr) { uprintf( "ar%d: many retries, consider retiring this tape\n", sc->sc_ident); /* FIXME, what do we do now? */ goto opdone; /* Keep reading, or whatever */ } if (sc->sc_histate == SSWAIT) { /* FIXME, use sc_qidle? */ /* * This arexc is not for the current buffer, * it's for the previous one, which we have already * dispatched as "done". Punt - ignore it for now. * If this causes problems, FIXME later. */ if (!sc->sc_quietly) printf("ar%d: %b error at block # %d punted\n", sc->sc_ident, *(unsigned short*)&sc->sc_status, ar_stat_bits, sc->sc_blkno); goto opcont; /* See if another buf waiting */ } #ifdef FIXME /* I don't know where this line came from. It is in error here. -- JCG */ if (sc->sc_status.EndOfMedium || sc->sc_status.FileMark) #endif FIXME /* * Errors on block tape cause it to close. * This is because people doing I/O on block devices * don't expect errors or know how to deal with them. * This info from wnj, June '83, transcribed by gnu. */ if (bp != &sc->rbuf && bp != &sc->cbuf) { sc->sc_openf = -1; } /* * On the Archive, all errors are hard; the controller has * retried them enough times already. */ if (!sc->sc_quietly) { printf("ar%d: %b error at block # %d\n", sc->sc_ident, *(unsigned short*)&sc->sc_status, ar_stat_bits, sc->sc_blkno); } bp->b_flags |= B_ERROR; bp->b_error = EIO; goto opdone; } else { /* * The interrupt was not from EXCEPTION. If READY is not * on, we have an Archive glitch. * * Problem is that it provides arrdy and then for * no reason rescinds it. It eventually comes back * again on its own. This happens after most or all * commands, if ONLINE is off. Eg, on RDST, 310 us after * the 6th byte is xferred, arrdy comes on; 180 us * later it drops for no reason; 90 us later it comes * back on, permanently. (Tracked down with logic * analyzer, triggering on (NOT arrdy) & arrdyedge). * * We "solve" the problem by making real damn sure that * we always turn on ONLINE before we do anything. * There is one place where that doesn't work -- when * dropping ONLINE to rewind the tape -- and we make an * explicit sc_state test below for that. * * Eventually it is hoped that Archive might fix this * microcode bug, but I'm not holding my breath. */ if (!araddr->arrdy) { /* * See if this is happening after a CLOSE, * where we Must have ONLINE turned off. * If so, just exit, another int will occur * when it turns READY on again. */ if (sc->sc_state == CLOSEtwo) { sc->sc_oldstate = sc->sc_state; sc->sc_state = CLOSEthree; araddr->arcatch = 0; araddr->arcatch = 1; Dprintf("ar*intproc badint after CLOSE\n"); if (araddr->arrdy) goto doit; else return (0); } /* * Otherwise, print a nasty message and go back * and look again. Eventually break out. Note * that we're at SPL() here. */ if (arprintf) printf("ar: interrupt without Rdy or Exc, state %s=%x, old %s=%x, bits=%b\n", arstatename[(int)sc->sc_state], sc->sc_state, arstatename[(int)sc->sc_oldstate], sc->sc_oldstate, *(long *)(2+(char *)sc->sc_addr), ARCH_LONG_CTRL_BITS); /* Only go around a finite number of times. */ if (breakout--) goto again; else { /* * OK, here's the scoop: * * WE know that the Archive should be ready * at this point -- either it interrupted us * with the edge of Ready, or it was in a * known IDLE state and we are initiating * an operation. Nevertheless, it claims * it is not ready. We have waited around * awhile for it to come to grips with * reality, but the flip-flop that controls * the READY line to us, cannot be read back * by the 8048 on the Archive controller board, * so if that flop has flipped by accident, * their controller will never set it right. * * We believe our version of the truth, and * to validate our reality we give it a * command. Since probably it knows itself * to be ready, it will accept the command * and we will be back in sync. If not, * our carefully-written command (RDSTinit) * will time out and return us a HardErr. * Then we will go thru the Exception routine * and hopefully deal with this by killing * the drive until re-opened and re-initted. * * We only do this if we are in a known good * state -- if we're in the middle of something, * we chicken out and give the user an error * and force them to re-close and re-init. * * This approach was suggested by Lou Domshy * at Archive, in December 1982. Implemented * by John Gilmore at Sun, June 1983. Tech * support by Kavee Phongprateep. Wardrobe by * Calvin Kline. */ if (arprintf) printf("ar: trying to re-sync with Archive\n"); switch (sc->sc_state) { case WFMinit: case RFMinit: case REWinit: case TENSEinit: case ERASEinit: case SELinit: case DESELinit: case RDSTinit: case CLOSEinit: case READinit: case WRinit: case CMDOKinit: sc->sc_hangstate = sc->sc_state; sc->sc_oldstate = sc->sc_state; sc->sc_state = RDSTinit; /* DEPENDS ON RDST BEING ATOMIC */ (void) armachine(sc); if (sc->sc_status.AnyEx0 || sc->sc_status.InvalidCmd || sc->sc_status.NoData || sc->sc_status.GotReset) { printf("ar: Archive hardware hang caused an error -- check your tape\n"); goto exc; } /* DEPENDS ON RDST EXIT STATE == IDLE */ sc->sc_oldstate = sc->sc_state; sc->sc_state = sc->sc_hangstate; /* * Go on to do whatever * we were trying to do before this * little "problem" arose... * Ready should be on but to be safe, * let's check again... */ goto again; default: printf("ar: Archive hardware hang -- restart entire tape job\n"); /* Close up shop, things are hung. */ sc->sc_openf = -1; bp->b_flags |= B_ERROR; bp->b_error = EINTR; goto opdone; } } } doit: /* * This interrupt is from the leading edge of arrdy. * * Run the low-level state machine. Since a single op can * take several interrupts, it returns us 0 if it is not * finished, and 1 if it is. If *it* is finished, then * we must look to see if the completion of its low-level * op means that our high-level op (buf) is done. * * Clear EdgeReady and enable it so the next arrdy * edge will be caught (possibly inside armachine()). */ araddr->arcatch = 0; araddr->arcatch = 1; if (!armachine(sc)) return (0); } /* * An operation completed... record status */ /* * Advance tape control FSM. */ advance: switch (sc->sc_histate) { case SSWAIT: /* * We have finished off our previous buffer, but are waiting * for the final interrupt to come in (to tell us it's ok * to do something else). Here it is. We should look to * see if another request has come in (on sc_pend_queue) * or just exit until sometime one does. */ goto opcont; case SIO: /* * Read/write increments tape block number * and possibly does another block if count not exhausted. */ sc->sc_blkno++; sc->sc_bufptr += AR_BSIZE; sc->sc_size -= AR_BSIZE; if (sc->sc_size > 0 && !sc->sc_eoflag) { /* * If we haven't hit EOF, and need more data to * satisfy the read, go back and do it again. * This involves setting the state for the next * interrupt so it will read/write the next block. * Then return, indicating that this buffer is not * yet finished. */ Dprintf("ar*arintproc restarting\n"); if (bp->b_flags & B_READ) { if (sc->sc_state != READidle) { printf("ar: OOPS READ %x\n", sc->sc_state); } sc->sc_oldstate = sc->sc_state; sc->sc_state = READburst; goto doit; } else { if (sc->sc_state != WRfin) { printf("ar: OOPS WR %x\n", sc->sc_state); } sc->sc_oldstate = sc->sc_state; sc->sc_state = WRburst; } return (0); /* Not done this buffer yet */ } else { sc->sc_histate = SSWAIT; goto opdone; } case SCOM: /* * For forward/backward space record update current position. */ if (bp == &sc->cbuf) { switch (bp->b_command) { case AR_SKIPFILE: sc->sc_blkno++; break; case AR_CLOSE: case AR_REWIND: case AR_TENSE: case AR_ERASE: sc->sc_blkno = 0; } } sc->sc_histate = SSWAIT; goto opdone; #ifdef SEEK case SSEEK: /* This seems to assume that we get an interrupt when these ops start, then another when they end? FIXME */ /* No, this seek came about because we were positioned wrong; now we can really do the I/O op. True? */ sc->sc_blkno = bp->b_blkno; sc->sc_histate = SSWAIT; goto opcont; #endif SEEK default: printf("ar: histate bad = %d\n", sc->sc_histate); sc->sc_histate = 0; /* Repeat til somebody sets it */ bp->b_flags |= B_ERROR; bp->b_error = EIO; goto opcont; } opdone: /* * Reset error count and remove * from device queue. */ sc->sc_pend_chain.b_actf = bp->av_forw; bp->b_resid = sc->sc_size; /* * We're done with this buffer; allow another one to be queued. */ if ((bp->b_flags & B_READ) == 0 && bp != &sc->cbuf && bp != &sc->rbuf) { if (armaxbufs <= 0) wakeup((caddr_t)&armaxbufs); armaxbufs++; Dprintf("ar*arintproc buffer done, maxbufs=%d\n", armaxbufs); } biodone(bp); sc->sc_histate = SSWAIT; /* Til next buf started, just wait */ /* * Begin I/O on the next buffer in the pend_chain. This is * used when the current buffer has been finished during this * interrupt (fall-thru from opdone), or when the previous buffer * was finished before its final interrupt (leaving us in histate * SSWAIT, waiting for the final interrupt). This is the final * interrupt; we come here to see if we can start a new request. */ opcont: return (1); /* OK to start next request */ } /* * Raw I/O routines */ arread(dev, uio) dev_t dev; struct uio *uio; { register int error; error = arphys(dev, uio); if (error) return (error); return (physio(arstrategy, &ar_softc[ARCTLR(dev)].rbuf, dev, B_READ, minphys, uio)); } arwrite(dev, uio) dev_t dev; struct uio *uio; { register int error; error = arphys(dev, uio); if (error) return (error); return (physio(arstrategy, &ar_softc[ARCTLR(dev)].rbuf, dev, B_WRITE, minphys, uio)); } /* * Check that a raw device exists. * If it does, set up sc_blkno and sc_nxrec * so that the tape will appear positioned correctly. */ arphys(dev, uio) dev_t dev; struct uio *uio; { register int unit = ARCTLR(dev); register daddr_t a; register struct ar_softc *sc; register struct mb_device *md; if (unit >= NAR ||(md=ardinfo[unit]) == 0 || md->md_alive == 0) return (ENXIO); sc = &ar_softc[unit]; a = uio->uio_offset / AR_BSIZE; sc->sc_blkno = a; sc->sc_nxrec = INF; return (0); } /* * Process IOCTL's used on raw tape. */ /*ARGSUSED*/ arioctl(dev, cmd, data, flag) caddr_t data; dev_t dev; { int unit = ARCTLR(dev); register struct ar_softc *sc = &ar_softc[unit]; register callcount; int error; int fcount, ioctlcmd; struct mtop *mtop; struct mtget *mtget; /* we depend on the values and order of the MT codes here */ static ops[] = { AR_WEOF, /* Write EOF (tapemark) */ AR_SKIPFILE, /* Forward space file */ 0, /* Backspace file -- can't do it */ 0, /* Forward space record -- too dumb FIXME */ 0, /* Backspace record -- can't do it */ AR_REWIND, /* Rewind tape */ AR_REWIND, /* Unload - we just rewind */ AR_STATUS, /* Read status back */ AR_TENSE, /* Retension tape */ AR_ERASE, /* Erase entire tape */ }; #ifdef PRF2 Dprintf("ar*ioctl(%d, %d, %x, %d)\n", dev, cmd, data, flag); #endif switch (cmd) { case MTIOCTOP: /* tape operation */ mtop = (struct mtop *)data; Dprintf("ar*ioctl MTIOCTOP %d count %d\n", mtop->mt_op, mtop->mt_count); switch (mtop->mt_op) { case MTWEOF: if (!sc->sc_writeable) goto NoWrite; /* FALL THRU */ case MTFSF: callcount = mtop->mt_count; fcount = 1; break; case MTERASE: if (!sc->sc_writeable) goto NoWrite; /* FALL THRU */ case MTRETEN: error = arcommand(dev, AR_REWIND, 1); if (error) return (error); /* FALL THRU */ case MTREW: case MTOFFL: case MTNOP: callcount = 1; fcount = mtop->mt_count; break; default: NoWrite: return (ENXIO); } if (callcount <= 0 || fcount <= 0) return (ENXIO); ioctlcmd = ops[mtop->mt_op]; /* * If eoflag then we already hit tape mark but * didn't send notification back to the user (by sending * zero length record). Allow writes after this * command by assigning to blkno. */ if (ioctlcmd == AR_SKIPFILE && sc->sc_eoflag) { sc->sc_eoflag = 0; sc->sc_blkno = INF; callcount--; } while (--callcount >= 0) { error = arcommand(dev, ioctlcmd, fcount); if (error) return (error); } if (ioctlcmd != AR_STATUS) sc->sc_eoflag = 0; return (0); case MTIOCGET: mtget = (struct mtget *)data; mtget->mt_type = MT_ISAR; error = arcommand(dev, AR_STATUS, 1); if (error) return (error); *( (char *)&mtget->mt_dsreg) = *(3+(char *)sc->sc_addr); *(1+(char *)&mtget->mt_dsreg) = *(5+(char *)sc->sc_addr); mtget->mt_erreg = *(short *)&sc->sc_status; /* FIXME. Move this from resid to a real place. */ mtget->mt_resid = sc->sc_stops; /* # tape stops since asked */ sc->sc_stops = 0; /* Clear it for next time */ sc->sc_softerrs = 0; /* FIXME. Add read errors info */ return (0); default: return (ENXIO); } } /* * State machine for archive tape drive controller. * This actually accomplishes things w.r.t. the tape drive. * Returns 0 if operation still in progress, 1 if finished. * Note that we may take further interrupts after claiming that an * operation is "finished". For example, we say a write is done when * we have transferred the last byte of the block; but there will be * an interrupt 5.5ms later to tell us it's ok to send the next block. * Eventually, we will rewind the tape asynchronously after the file is * closed, letting the user go free while it spins. FIXME: THIS CANNOT * BE DONE until we clean up the high level code so it doesn't clobber * our variables as it is setting up to call arstart_cmd(). * * Must be entered at SPL(). */ armachine(sc) register struct ar_softc *sc; { register struct ardevice *araddr = sc->sc_addr; register int count; register char *byteptr; Dprintf("ar*machine(%x, %x) state %s=%x\n", sc, araddr, arstatename[(int)sc->sc_state], sc->sc_state); switch (sc->sc_state) { case CMDOKinit: /* * If we got here the command state is ok, * i.e., not in the middle of a read or write. */ sc->sc_cmdok = 1; goto IdleState; case CLOSEinit: /* * FIXME, this is time dependent and not documented in * the Archive manual. However, it looks like in the * schematics (and in actual performance), if you drop * ONLINE, hardware gates will drop READY. This appears * to depend on whether you are "at position" or not; * If not, nothing happens; if so, READY drops and we move * the tape. */ araddr->aronline = 0; /* Drop online; we're done. */ if (araddr->arrdy) goto IdleState; /* No interrupt will occur. */ else goto next; /* Rewinding; wait for interrupt. */ case CLOSEtwo: case CLOSEthree: /* * Note that there is special code in arintproc() which * deals with READY line glitches for state CLOSEtwo. */ araddr->aronline = 1; /* Reassert ONLINE to avoid glitch in READY line */ goto IdleState; case WFMinit: sc->sc_status.BOT = 0; araddr->ardata = ARCMD_WREOF; araddr->arreq = 1; goto CmdState; case RFMinit: araddr->ardata = ARCMD_RDEOF; araddr->arreq = 1; goto CmdState; case REWinit: araddr->ardata = ARCMD_REWIND; araddr->arreq = 1; goto CmdState; case TENSEinit: araddr->ardata = ARCMD_TENSION; araddr->arreq = 1; goto CmdState; case ERASEinit: araddr->ardata = ARCMD_ERASE; araddr->arreq = 1; goto CmdState; case SELinit: araddr->ardata = ARCMD_LED | (1 << sc->sc_drive); araddr->arreq = 1; goto CmdState; case DESELinit: araddr->ardata = 1 << sc->sc_drive; araddr->arreq = 1; goto CmdState; RDSTagain: printf("ar: RDST gave Exception, retrying\n"); /* Fall thru... */ case RDSTinit: byteptr = (char *) &sc->sc_status; count = BREAKOUTCOUNT; if (araddr->arrdy) { araddr->ardata = ARCMD_RDSTAT; araddr->arreq = 1; } else { araddr->ardata = ARCMD_RDSTAT; araddr->arreq = 1; while (araddr->arexc) if (!count--) goto RDSTfail; } /* * Now wait for arrdy indicating command accepted. * Check for Exception too; * (It's not legal to do RDST all the time(!).) */ while (!araddr->arrdy) { if (araddr->arexc) goto RDSTagain; if (!count--) { RDSTfail: printf("ar: RDST failure\n"); sc->sc_openf = -1; sc->sc_status.HardErr = 1; sc->sc_status.AnyEx0 = 1; goto DisAble; } } /* Negate arreq, wait for arrdy to drop. */ araddr->arcatch = 0; /* Clear arrdyedge */ araddr->arcatch = 1; /* Catch edge */ araddr->arreq = 0; /* Now xfer a byte or six. */ do { /* Wait for edge of arrdy */ while (!araddr->arrdyedge) { if (araddr->arexc) goto RDSTagain; if (!count--) goto RDSTfail; } *byteptr++ = araddr->ardata; araddr->arcatch = 0; /* Clear edge indicator */ araddr->arcatch = 1; /* Catch next one */ araddr->arreq = 1; /* Tell controller we have it */ /* Ready will fall within 250ns of our arreq, but we're supposed to keep it high for 20us */ DELAY(30); /* at least 20 usec */ araddr->arreq = 0; } while (byteptr < (char *)(&sc->sc_status) + sizeof (sc->sc_status)); /* * On exit from this loop, arcatch has been negated and * asserted, so it will correctly reflect the leading edge * of arrdy for the next command. */ sc->sc_stops += sc->sc_status.TapeStops; /* Accum total */ sc->sc_softerrs += sc->sc_status.SoftErrs; /* For debugging, dump status bytes after a command AR_STATUS */ Dprintf("ar*RDST %b %d %d\n", *(unsigned short*)&sc->sc_status, ar_stat_bits, sc->sc_status.SoftErrs, sc->sc_status.TapeStops); /* * Wait for Ready to appear again, so we can exit in IDLE * state and be atomic (not require any interrupts to * complete). Several places in higher-level code depend * on this. We leave the edge of Ready caught in EdgeReady, * but IdleState will disable interrupts on it. */ while (!araddr->arrdy) { if (araddr->arexc) /* Safety test */ goto RDSTagain; if (!count--) goto RDSTfail; /* Safety #2 */ } goto IdleState; case READcmd: case WRcmd: araddr->arreq = 0; goto next; case READinit: araddr->ardata = ARCMD_RDDATA; araddr->arreq = 1; goto next; case READburst: /* Read a block of data from the tape drive. */ Dprintf("ar*READ addr %x count %x\n", sc->sc_bufptr, sc->sc_size); sc->sc_status.BOT = 0; /* FIXME, this could hang kernel, but at least it printf's. */ while (!araddr->arrdy) { if (araddr->arexc) return (0); /* Not yet done */ printf("ar: Read no READY\n"); } araddr->arburst = 1; /* Begin block xfer */ count = AR_BSIZE; byteptr = sc->sc_bufptr; while (count--) *byteptr++ = araddr->ardata; /* read data */ araddr->arburst = 0; sc->sc_oldstate = sc->sc_state; sc->sc_state = READfin; /* Like FINstate sorta */ break; case WRinit: araddr->ardata = ARCMD_WRDATA; araddr->arreq = 1; goto next; case WRburst: /* Write a block of data to the tape drive. */ Dprintf("ar*WRITE addr %x count %x\n", sc->sc_bufptr, sc->sc_size); /* FIXME, this could hang kernel, but at least it printf's. */ while (!araddr->arrdy) { if (araddr->arexc) return (0); /* Not done yet */ printf("ar: Write no READY\n"); } araddr->arburst = 1; /* Begin block xfer */ count = AR_BSIZE; byteptr = sc->sc_bufptr; while (count--) araddr->ardata = *byteptr++; araddr->arburst = 0; sc->sc_oldstate = sc->sc_state; sc->sc_state = WRfin; /* Like FINstate sorta */ Dprintf("ar*machine exiting done in state %s=%x\n", arstatename[(int)sc->sc_state], sc->sc_state); /* * This code is a copy of the code at the end of this * switch statement, except that it returns 1 (operation * completed) instead of 0 (more needs doing) */ araddr->arrdyie = 1; araddr->arexcie = 1; return (1); case CMDstate: /* All commands that stop interacting once you say "do it" */ araddr->arreq = 0; /* Done with command */ goto FinState; /* Final interaction for this cmd */ IdleState: /* Drive is idle; set IDLEstate and disable */ sc->sc_oldstate = sc->sc_state; sc->sc_state = IDLEstate; goto DisAble; case WRfin: /* Entry after writing a block */ case READfin: /* Entry after reading a block */ case FINstate: /* Entry after any other command */ /* * Go to next sequential state - WRidle, READidle, IDLEstate. * Disable interrupts, and return. arstart_cmd() will later * put us into READ/WRburst or some commandinit state. */ sc->sc_oldstate = sc->sc_state; sc->sc_state = (enum ARstates)(1 +(int)sc->sc_state); DisAble: Dprintf("ar*machine idling\n"); araddr->arrdyie = 0; /* Negate arrdy interrupt */ return (1); /* Tell caller op is done */ case WRidle: /* Writing blocks, but none to write now */ case READidle: /* Reading blocks, but don't need one now */ case IDLEstate: /* Issuing commands, but don't have one now */ /* This can only happen if software triggers us. */ printf("ar: triggerred at idle %x\n", sc->sc_state); goto DisAble; /* Turn off interrupt enable again */ default: printf("ar: invalid state 0x%x\n", sc->sc_state); sc->sc_openf = -1; /* Force closure & re-initialization */ goto DisAble; /* Is this reasonable? */ next: /* Go to next sequential state */ sc->sc_oldstate = sc->sc_state; sc->sc_state = (enum ARstates)(1 +(int)sc->sc_state); break; FinState: sc->sc_oldstate = sc->sc_state; sc->sc_state = FINstate; break; CmdState: sc->sc_oldstate = sc->sc_state; sc->sc_state = CMDstate; break; } Dprintf("ar*machine exiting in state %s=%x\n", arstatename[(int)sc->sc_state], sc->sc_state); /* Go to next state on the next leading edge of arrdy. */ araddr->arrdyie = 1; /* Interrupt on arrdy leading edge */ araddr->arexcie = 1; /* Interrupt on arexc too */ /* FIXME. Figure out where to set and unset, leave alone otherwise. */ return (0); } /* * Sort of like tsleep for the Archive driver. * XXX - this can be replaced by new PCATCH option to sleep. */ arsleep(cp, pri) caddr_t cp; int pri; { label_t q; int rc = 0; q = u.u_qsave; if (setjmp(&u.u_qsave)) rc = 1; else (void) sleep(cp, pri); u.u_qsave = q; return (rc); } #endif