Files
Arquivotheca.SunOS-4.1.4/sys/sundev/ar.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

2040 lines
57 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/dir.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/mtio.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <machine/mmu.h>
#include <machine/cpu.h>
#include <sundev/mbvar.h>
#include <sundev/arreg.h>
/*
* 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 <reg>.
*/
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