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

1550 lines
34 KiB
C

#ifndef lint
static char sccsid[] = "@(#)xt.c 1.1 94/10/31";
#endif
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
#include "xt.h"
#if NXT > 0
/*
* Driver for Xylogics 472 Tape controller
* Controller names are xtc?
* Device names are xt?
* This driver lifted from the TapeMaster driver
*
* TODO:
* test driver with more than one slave
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <sys/file.h>
#include <sys/map.h>
#include <sys/vm.h>
#include <sys/mtio.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sundev/mbvar.h>
#include <sundev/xycreg.h>
#include <sundev/xtreg.h>
#define SPLXT() spl3()
#define PRIXT (PZERO+1) /* for interruptible sleeps */
/*
* There is a cxtbuf per tape drive.
* It is used as the token to pass to the internal routines
* to execute tape ioctls.
* When the tape is rewinding on close we release
* the user process but any further attempts to use the tape drive
* before the rewind completes will hang waiting for cxtbuf.
*/
struct buf cxtbuf[NXT];
#define b_repcnt b_bcount
#define b_command b_resid
/*
* Raw tape operations use rxtbuf. The driver
* notices when rxtbuf is being used and allows the user
* program to continue after errors and read records
* not of the standard length (BSIZE).
*/
struct buf rxtbuf[NXT];
/*
* Driver Multibus interface routines and variables.
*/
int xtprobe(), xtslave(), xtattach(), xtgo(), xtdone(), xtpoll();
struct mb_ctlr *xtcinfo[NXTC];
struct mb_device *xtdinfo[NXT];
struct mb_driver xtcdriver = {
xtprobe, xtslave, xtattach, xtgo, xtdone, xtpoll,
sizeof (struct xydevice), "xt", xtdinfo, "xtc", xtcinfo, MDR_BIODMA,
};
struct buf xtutab[NXT];
short xttoxtc[NXT];
/* bits in minor device */
#define NUNIT 4
#define XTCTLR(dev) (xttoxtc[MTUNIT(dev)])
#define T_HIDENS MT_DENSITY2 /* select high density */
#define INF (daddr_t)1000000L
/*
* Max # of buffers outstanding per unit
*/
#define MAXMTBUF 3
#define EOM_TWO_EOFS 2
#define EOM_ONE_EOF 1
#define EOM_NO_EOF 0
#define NO_EOM -1
#define FOREVER 0x7fffffff
#define FEET(n) ((n) << 2) /* 3 inch per erase, 4 times per ft */
#define ERASE_AFT_EOT (FEET(1))
#define MX_RETRY 3
#define MINPHYS_BYTES 65534
#define XTE_HOST_TMO 0x40
/* sc_eot status */
#define XT_EOT_OFF 0 /* clear off EOT */
#define XT_EOT_ON 1 /* hit EOT but user does not know */
#define XT_EOT_KNOWN 2 /* hit EOT and user knows about it */
/*
* Software state per tape transport.
*
* 1. A tape drive is a unique-open device; we refuse opens when it is already.
* 2. We keep track of the current position on a block tape and seek
* before operations by forward/back spacing if necessary.
* 3. We remember if the last operation was a write on a tape, so if a tape
* is open read write and the last thing done is a write we can
* write a standard end of tape mark (two eofs).
* 4. We remember the status registers after the last command, using
* then internally and returning them to the SENSE ioctl.
*/
struct xt_softc {
char sc_openf; /* lock against multiple opens */
char sc_lastiow; /* last op was a write */
char sc_stream; /* tape is a streamer */
char sc_bufcnt; /* queued system buffer count */
u_short sc_xtstat; /* status bits from xt_status */
u_short sc_error; /* copy of last erreg */
u_short sc_lastcomm; /* last command executed */
long sc_resid; /* copy of last bytecount */
daddr_t sc_timo; /* time until timeout expires */
short sc_tact; /* timeout is active */
daddr_t sc_fileno; /* file number */
daddr_t sc_recno; /* rec number */
/* NOTE:-1 indicates we're before EOF; */
/* 0 indicates we're after EOF; */
/* 0 if at EOM. */
int sc_open_opt; /* open flags */
off_t sc_lstpos; /* last offset */
int sc_eot; /* eot status */
int sc_firstopen; /* set on attach, cleared on first open */
} xt_softc[NXT];
/*
* Data per controller
*/
struct xtctlr {
struct xt_softc *c_units[NUNIT]; /* units on controller */
struct xydevice *c_io; /* ptr to I/O space data */
struct xtiopb *c_iopb; /* ptr to IOPB */
char c_present; /* controller is present */
char c_wantint; /* expecting interrupt */
} xtctlrs[NXTC];
/*
* States for mc->mc_tab.b_active, the per controller state flag.
* This is used to sequence control in the driver.
*/
#define SIO 2 /* doing seq i/o */
#define SCOM 3 /* sending control command */
#define SREW 4 /* sending a drive rewind */
#define SERR 5 /* doing erase gap (error on write) */
#define SIOBSR 6 /* back record before SIO retry */
#define SWRTEOM 8 /* EOM when WRITE */
#ifndef sun3x
int xtthrot = 4;
#else
int xtthrot = 7;
#endif
/*
* Give a simple command to a controller
* and spin until done.
* Returns the error number or zero
*/
static
simple(c, unit, cmd)
struct xtctlr *c;
{
register struct xydevice *xyio = c->c_io;
register struct xtiopb *xt = c->c_iopb;
int piopb, t;
t = xyio->xy_resupd; /* reset */
if (!xtwait(xyio))
return (XTE_TIMEOUT);
bzero((caddr_t)xt, sizeof *xt);
xt->xt_autoup = 1;
xt->xt_reloc = 1;
xt->xt_cmd = cmd;
xt->xt_throttle = xtthrot;
xt->xt_unit = unit;
piopb = ((char *)xt) - DVMA;
t = XYREL(xyio, piopb);
xyio->xy_iopbrel[0] = t >> 8;
xyio->xy_iopbrel[1] = t;
t = XYOFF(piopb);
xyio->xy_iopboff[0] = t >> 8;
xyio->xy_iopboff[1] = t;
xyio->xy_csr = XY_GO;
while (xyio->xy_csr & XY_BUSY)
DELAY(10);
return (xt->xt_errno);
}
/*
* Determine existence of controller
*/
xtprobe(reg, ctlr)
caddr_t reg;
{
register struct xtctlr *c = &xtctlrs[ctlr];
int err;
c->c_io = (struct xydevice *)reg;
if (peekc((char *)&c->c_io->xy_resupd) == -1)
return (0);
c->c_iopb = (struct xtiopb *)rmalloc(iopbmap,
(long)sizeof (struct xtiopb));
if (c->c_iopb == NULL) {
printf("xtprobe: no iopb space\n");
return (0);
}
c->c_present = 1;
(void) simple(c, 0, XT_NOP);
if (c->c_iopb->xt_ctype != XYC_472) {
printf("xtc%d: unknown controller type\n", ctlr);
return (0);
}
if (err = simple(c, 0, XT_TEST))
printf("xtc%d: self test error %x\n", ctlr, err);
if (err)
return (0);
return (sizeof (struct xydevice));
}
/*
* Always say that a unit is there.
* We can't tell for sure anyway, and this lets
* a guy plug one in without taking down the system
* (These are micros, after all!)
*/
/*ARGSUSED*/
xtslave(md, reg)
struct mb_device *md;
caddr_t reg;
{
return (1);
}
/*
* Record attachment of the unit to the controller.
*/
/*ARGSUSED*/
xtattach(md)
struct mb_device *md;
{
register struct xtctlr *c = &xtctlrs[md->md_ctlr];
int unit = md->md_slave;
/* set up for any vectored interrupts to pass ctlr pointer */
if (md->md_mc->mc_intr) {
if ((c->c_io->xy_csr & XY_ADDR24) == 0)
printf("xtc%d: WARNING: 20 bit addresses\n",
md->md_mc->mc_ctlr);
*(md->md_mc->mc_intr->v_vptr) = (int)c;
}
c->c_units[unit] = &xt_softc[md->md_unit];
/*
* xttoxtc is used in XTCTLR to index the controller
* arrays given a xt unit number.
*/
xttoxtc[md->md_unit] = md->md_mc->mc_ctlr;
/*
* Set first open flag.
*/
xt_softc[md->md_unit].sc_firstopen = 1;
}
int xttimer();
int xtdefdens = 0;
int xtdefspeed = 0;
int recurse_flag = 0;
/*
* Open the device. Tapes are unique open
* devices, so we refuse if it is already open.
* We also check that a tape is available, and
* don't block waiting here; if you want to wait
* for a tape you should timeout in user code.
*/
xtopen(dev, flag)
dev_t dev;
int flag;
{
register int xtunit;
register struct mb_device *md;
register struct xt_softc *sc;
int t;
struct xydevice *xyio;
int result;
xtunit = MTUNIT(dev);
if (xtunit>=NXT || (md = xtdinfo[xtunit]) == 0 || md->md_alive == 0)
return (ENXIO);
if ((sc = &xt_softc[xtunit])->sc_openf)
return (EBUSY);
sc->sc_openf = 1;
if (result = xtcommand(dev, XT_DSTAT, 0, 1)) { /* timed out */
if (result == EINTR) {
sc->sc_openf = 0;
return (EINTR);
}
goto retry;
}
if ((sc->sc_xtstat & (XTS_ONL|XTS_RDY)) != (XTS_ONL|XTS_RDY)) {
uprintf("xt%d: not online\n", xtunit);
sc->sc_openf = 0;
return (EIO);
}
if ((flag&FWRITE) && (sc->sc_xtstat & XTS_FPT)) {
uprintf("xt%d: no write ring\n", xtunit);
sc->sc_openf = 0;
return (EACCES);
}
if (sc->sc_tact == 0) {
sc->sc_timo = INF;
sc->sc_tact = 1;
timeout(xttimer, (caddr_t)dev, 2*hz);
}
if ((sc->sc_xtstat & XTS_BOT) || sc->sc_firstopen) {
/*
* First time through, ensure we're at BOT.
*/
if (sc->sc_firstopen) {
if (!(sc->sc_xtstat & XTS_BOT) &&
xtcommand(dev, XT_SEEK, XT_REW, 1)) {
uprintf("xt%d: could not rewind\n", xtunit);
sc->sc_openf = 0;
return (EIO);
}
sc->sc_firstopen = 0;
}
/*
* suninstall doesn't rewind or offline the tape between
* volumes, so reset accounting variables here.
*/
sc->sc_fileno = 0;
sc->sc_recno = 0;
switch (md->md_flags) {
default:
case 0: /* unknown drive type */
if (xtdefdens) {
if (xtdefdens > 0) {
if (xtcommand(dev, XT_PARAM,
XT_HIDENS, 1))
goto retry;
} else
if (xtcommand(dev, XT_PARAM,
XT_LODENS, 1))
goto retry;
}
if (xtdefspeed) {
if (xtdefspeed > 0) {
if (xtcommand(dev, XT_PARAM,
XT_HIGH, 1))
goto retry;
} else
if (xtcommand(dev, XT_PARAM,
XT_LOW, 1))
goto retry;
}
break;
case 1: /* CDC Keystone III and Telex 9250 */
if (minor(dev) & T_HIDENS) {
if (xtcommand(dev, XT_PARAM, XT_HIDENS, 1))
goto retry;
} else
if (xtcommand(dev, XT_PARAM, XT_LODENS, 1))
goto retry;
break;
case 2: /* Kennedy triple density */
if (minor(dev) & T_HIDENS) {
if (xtcommand(dev, XT_PARAM, XT_HIGH, 1))
goto retry;
} else
if (xtcommand(dev, XT_PARAM, XT_LOW, 1))
goto retry;
break;
}
}
sc->sc_lastiow = 0;
sc->sc_open_opt = flag;
sc->sc_lstpos = 0;
/* invalidate the tape */
if (sc->sc_open_opt == FWRITE)
return (xtwrt_eom(dev, EOM_TWO_EOFS));
else
return (0);
retry: sc->sc_openf = 0;
if (!recurse_flag) {
printf("xt: attempting reset\n");
recurse_flag = 1;
xyio = xtctlrs[md->md_ctlr].c_io;
t = xyio->xy_resupd; /* reset */
(void) xtwait(xyio);
t = xtopen(dev, flag);
recurse_flag = 0;
return (t);
} else
return (EIO);
}
static
xtwrt_eom(dev, n_eof)
register dev_t dev;
int n_eof;
{
register struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
int i, stat = 0;
for (i = 0; i < 2; i++)
if (xtcommand(dev, XT_FMARK, 0, 1)) {
printf("xtwrt_eom:failed in writing EOM %d.\n", i);
return (EIO);
}
if (n_eof != EOM_NO_EOF) {
if (stat = xtcommand(dev, XT_SEEK, XT_FILE+XT_REVERSE, n_eof))
printf("xtwrt_eom: failed in backup %d eof.\n", n_eof);
else
/* we're positioned at EOM so recno = 0 */
sc->sc_recno = 0;
}
return (stat);
}
static
xtflush_eom(dev, n_eof)
register dev_t dev;
int n_eof;
{
register struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
if ((sc->sc_open_opt & FWRITE) && sc->sc_lastiow) {
return (xtwrt_eom(dev, n_eof));
} else
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.
*/
xtclose(dev, flag)
register dev_t dev;
register flag;
{
register struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
#ifdef lint
flag = flag;
#endif lint
if (xtflush_eom(dev, EOM_ONE_EOF)) {
printf("xtclose: flush eom failed\n");
sc->sc_openf = 0;
}
if ((minor(dev)&MT_NOREWIND) == 0 &&
sc->sc_lastcomm != ((XT_SEEK<<8)+XT_UNLOAD))
/*
* 0 count means don't hang waiting for rewind complete
* rather cxtbuf stays busy until the operation completes
* preventing further opens from completing by
* preventing a TM_SENSE from completing.
*/
(void) xtcommand(dev, XT_SEEK, XT_REW, 0);
sc->sc_openf = 0;
}
/*
* Execute a command on the tape drive
* a specified number of times.
*/
xtcommand(dev, com, subfunc, count)
dev_t dev;
int com, subfunc, count;
{
register struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
register struct buf *bp;
register int s;
int error;
if ((subfunc == XT_REW) && (sc->sc_xtstat & XTS_BOT))
/* we're already at BOT */
return (0);
bp = &cxtbuf[MTUNIT(dev)];
s = SPLXT();
while (bp->b_flags&B_BUSY) {
/*
* This special check is because B_BUSY never
* gets cleared in the non-waiting rewind case.
*/
if (bp->b_repcnt == 0 && (bp->b_flags&B_DONE))
break;
bp->b_flags |= B_WANTED;
/*
* Allow user to control-C out of this sleep
* while tape is rewinding from previous process.
*/
if (sleep((caddr_t)bp, PRIXT|PCATCH)) {
(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 << 8) + subfunc;
bp->b_blkno = 0;
xtstrategy(bp);
/*
* In case of rewind from close, don't wait.
* This is the only case where count can be 0.
*/
if (count == 0)
return (0);
s = SPLXT();
while ((bp->b_flags&B_DONE) == 0) {
bp->b_flags |= B_WANTED;
(void) sleep((caddr_t)bp, PRIBIO);
}
(void) splx(s);
error = geterror(bp);
if (com == XT_SEEK && bp->b_resid != 0 && !error) {
switch (subfunc) {
case XT_REW:
case XT_UNLOAD:
break;
default:
error = EIO;
break;
}
}
if (bp->b_flags&B_WANTED)
wakeup((caddr_t)bp);
bp->b_flags &= B_ERROR; /* note: clears B_BUSY */
return (error);
}
/*
* Queue a tape operation.
*/
xtstrategy(bp)
register struct buf *bp;
{
int xtunit = MTUNIT(bp->b_dev);
register struct mb_ctlr *mc;
register struct buf *dp;
register struct xt_softc *sc = &xt_softc[xtunit];
int s;
/*
* Put transfer at end of unit queue
*/
dp = &xtutab[xtunit];
bp->av_forw = NULL;
s = SPLXT();
while (sc->sc_bufcnt >= MAXMTBUF)
(void) sleep((caddr_t)&sc->sc_bufcnt, PRIBIO);
sc->sc_bufcnt++;
mc = xtdinfo[xtunit]->md_mc;
if (dp->b_actf == NULL) {
dp->b_actf = bp;
/*
* Transport not already active...
* put at end of controller queue.
*/
dp->b_forw = NULL;
if (mc->mc_tab.b_actf == NULL)
mc->mc_tab.b_actf = dp;
else
mc->mc_tab.b_actl->b_forw = dp;
mc->mc_tab.b_actl = dp;
} else
dp->b_actl->av_forw = bp;
dp->b_actl = bp;
/*
* If the controller is not busy, get
* it going.
*/
if (mc->mc_tab.b_active == 0)
xtstart(mc);
(void) splx(s);
}
/*
* Start activity on a xt controller.
*/
xtstart(mc)
register struct mb_ctlr *mc;
{
register struct buf *bp, *dp;
register struct xt_softc *sc;
register struct mb_device *md;
register struct xtiopb *xt;
register struct xydevice *xyio;
int xtunit, cmd, subfunc, count, size, fast;
int state;
/*
* Look for an idle transport on the controller.
*/
loop:
if ((dp = mc->mc_tab.b_actf) == NULL)
return;
if ((bp = dp->b_actf) == NULL) {
mc->mc_tab.b_actf = dp->b_forw;
goto loop;
}
xtunit = MTUNIT(bp->b_dev);
md = xtdinfo[xtunit];
sc = &xt_softc[xtunit];
xyio = xtctlrs[md->md_ctlr].c_io;
/*
* Check for command overlap.
*/
if (!xtcsrvalid(xyio) || (xyio->xy_csr & XY_BUSY)) {
printf("xt%d: bad command synchronization\n", xtunit);
sc->sc_openf = -1;
}
/*
* Default is that last command was NOT a write command;
* if we do a write command we will notice this in xtintr().
*/
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).
*/
goto next;
}
fast = 0;
count = 1;
subfunc = 0;
if (bp == &cxtbuf[xtunit]) {
/*
* Execute control operation with the specified count.
*/
/*
* Set next state; give 10 minutes to complete
* rewind, or 10 seconds per iteration (minimum 60
* seconds and max 5 minutes) to complete other ops.
*/
switch (bp->b_command & ~XT_REVERSE) {
case (XT_SEEK<<8)+XT_REW:
case (XT_SEEK<<8)+XT_UNLOAD:
mc->mc_tab.b_active = SREW;
sc->sc_timo = 10 * 60;
fast = 1;
break;
case (XT_SEEK<<8)+XT_FILE:
mc->mc_tab.b_active = SCOM;
sc->sc_timo = 10 * 60;
fast = 1;
break;
case (XT_NOP<<8):
case (XT_DSTAT<<8):
case (XT_PARAM<<8)+XT_LODENS:
case (XT_PARAM<<8)+XT_HIDENS:
case (XT_PARAM<<8)+XT_LOW:
case (XT_PARAM<<8)+XT_HIGH:
mc->mc_tab.b_active = SCOM;
sc->sc_timo = imin(4*(int)bp->b_repcnt, 60);
break;
default:
mc->mc_tab.b_active = SCOM;
sc->sc_timo =
imin(imax(20*(int)bp->b_repcnt, 30), 5*60);
break;
}
count = bp->b_repcnt;
cmd = bp->b_command >> 8;
subfunc = bp->b_command & 0xFF;
size = 0;
goto dobpcmd;
}
/* from here on should be all I/O related stuff, i.e. no ioctl .. */
state = mc->mc_tab.b_active;
/* NOTE: lseek should be done at higher level. */
switch (state) {
case 0:
mc->mc_tab.b_active = SIO;
/* NOTE: fall through SIO state */
case SIO:
size = bp->b_bcount;
if ((bp->b_flags&B_READ) == 0)
cmd = XT_WRITE;
else
cmd = XT_READ;
sc->sc_timo = imin(imax(20*(size/65536)+20, 30), 5 * 60);
break;
case SWRTEOM: /* hit EOM in WRITE, back up one rec, return 0 count */
case SIOBSR: /* READ/WRITE error retry */
cmd = XT_SEEK, subfunc = XT_REC+XT_REVERSE;
count = 1;
sc->sc_timo = imin(imax(20 * count, 30), 5 * 60);
fast = 0;
size = 0;
break;
case SERR:
cmd = XT_FMARK, subfunc = XT_ERASE;
mc->mc_tab.b_active = SERR;
size = 0;
count = 1;
sc->sc_timo = 60;
break;
default:
printf("xtstart: BAD state: %d\n", state);
}
dobpcmd:
/*
* Do the command in bp.
*/
xt = xtctlrs[md->md_ctlr].c_iopb;
bzero((caddr_t)xt, sizeof *xt);
xt->xt_autoup = 1;
xt->xt_reloc = 1;
xt->xt_ie = 1;
xt->xt_cmd = cmd;
xt->xt_throttle = xtthrot;
xt->xt_subfunc = subfunc;
xt->xt_unit = xtdinfo[xtunit]->md_slave;
#ifdef notdef
if (sc->sc_stream) {
tpb->tm_ctl.tmc_speed = fast;
} else {
tpb->tm_ctl.tmc_speed = (minor(bp->b_dev) & T_HIDENS) ? 0 : 1;
}
#else
#ifdef lint
fast = fast;
#endif
#endif
if (size) {
xt->xt_swab = 1;
xt->xt_retry = 1;
xt->xt_cnt = size;
(void) mbgo(mc);
} else {
xt->xt_cnt = count;
xtgo(mc);
}
sc->sc_lastcomm = (cmd << 8) + subfunc;
return;
next:
/*
* Done with this operation due to error or
* the fact that it doesn't do anything.
*/
mc->mc_tab.b_errcnt = 0;
mc->mc_tab.b_active = 0;
sc->sc_timo = INF;
dp->b_actf = bp->av_forw;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
iodone(bp);
if (sc->sc_bufcnt-- >= MAXMTBUF)
wakeup((caddr_t)&sc->sc_bufcnt);
goto loop;
}
/*
* The Multibus resources we needed have been
* allocated to us; start the device.
* We assume the controller is ready because this is checked in xtstart.
*/
xtgo(mc)
struct mb_ctlr *mc;
{
register struct xtctlr *c = &xtctlrs[mc->mc_ctlr];
register struct xydevice *xyio = c->c_io;
register struct xtiopb *xt = c->c_iopb;
register int dmaddr, piopb, t;
dmaddr = MBI_ADDR(mc->mc_mbinfo);
if ((dmaddr + xt->xt_cnt) > 0x100000 && (xyio->xy_csr & XY_ADDR24) == 0)
panic("xt: exceeded 20 bit address");
xt->xt_bufoff = XYOFF(dmaddr);
xt->xt_bufrel = XYREL(xyio, dmaddr);
/* stuff IOPB info into I/O registers */
piopb = ((char *)xt) - DVMA;
t = XYREL(xyio, piopb);
xyio->xy_iopbrel[0] = t >> 8;
xyio->xy_iopbrel[1] = t;
t = XYOFF(piopb);
xyio->xy_iopboff[0] = t >> 8;
xyio->xy_iopboff[1] = t;
xyio->xy_csr = XY_GO;
mc->mc_tab.b_flags &=~ B_DONE;
mc->mc_tab.b_flags |= B_BUSY;
}
/*
* interrupt routine.
*/
xtintr(c)
register struct xtctlr *c;
{
register struct mb_ctlr *mc;
register struct xt_softc *sc;
register struct xtiopb *xt;
register struct buf *dp;
register struct buf *bp;
register int xtunit, state;
register struct xydevice *xyio;
int err;
xyio = c->c_io;
/* wait for controller to settle down */
(void) xtcsrvalid(xyio);
mc = xtcinfo[c - xtctlrs];
xt = c->c_iopb;
if (xt->xt_errno == XTE_HOST_TMO) {
if ((err = simple(c, 0, XT_DRESET)))
printf("xtintr: XTE_TIMEOUT reset failed, err= 0x%x\n",
err);
xt->xt_errno = XTE_HOST_TMO;
} else {
/* clear the interrupt */
if (xyio->xy_csr & (XY_ERROR|XY_DBLERR)) {
xyio->xy_csr = XY_ERROR;
(void) xtcsrvalid(xyio);
}
xyio->xy_csr = XY_INTR;
}
mc->mc_tab.b_flags &= ~B_BUSY;
if ((state = mc->mc_tab.b_active) == 0) {
printf("xt%d: stray interrupt\n", mc->mc_ctlr);
(void) xtcsrvalid(xyio); /* don't return too fast */
return;
}
dp = mc->mc_tab.b_actf;
bp = dp->b_actf;
if (bp == NULL)
panic("xtintr: queuing error");
xtunit = MTUNIT(bp->b_dev);
if (xtunit >= NXT)
panic("xtintr: queueing error 2");
sc = &xt_softc[xtunit];
sc->sc_xtstat = xt->xt_status;
/*
* EOT handling: we let user read/write pass EOT. In READ, EOT is
* transparent to the users. In WRITE, users will be notified at
* the first time WRITE past EOT by 0 byte count return.
*/
if (mc->mc_tab.b_active != SWRTEOM &&
mc->mc_tab.b_active != SIOBSR) {
switch (sc->sc_eot) {
case XT_EOT_OFF:
if (sc->sc_xtstat & XTS_EOT) {
if ((bp->b_flags & B_READ) == B_WRITE) {
sc->sc_eot = XT_EOT_KNOWN;
mc->mc_tab.b_active = SWRTEOM;
goto opcont;
} else
sc->sc_eot = XT_EOT_ON;
}
break;
case XT_EOT_ON:
if ((sc->sc_xtstat & XTS_EOT) == 0) {
sc->sc_eot = XT_EOT_OFF;
} else {
if ((bp->b_flags & B_READ) == B_WRITE) {
sc->sc_eot = XT_EOT_KNOWN;
mc->mc_tab.b_active = SWRTEOM;
goto opcont;
}
}
break;
case XT_EOT_KNOWN:
/*
* NOTE: since we do a SWRTEOM, so the very next
* write may be back off EOT completely, but we
* want to stay in XT_EOT_KNOWN state until other
* command moves the tape off EOT.
*/
if ((sc->sc_xtstat & XTS_EOT) == 0 &&
(bp->b_flags & B_READ) != B_WRITE)
sc->sc_eot = XT_EOT_OFF;
break;
}
/* set residue count */
sc->sc_resid = xt->xt_cnt - xt->xt_acnt;
}
/*
* If last command was a rewind, and tape is still
* rewinding, wait for another interrupt, triggered
* by xttimer
*/
if ((state == SREW) && (sc->sc_xtstat & XTS_ONL) &&
(sc->sc_xtstat & (XTS_RDY|XTS_BOT)) != (XTS_RDY|XTS_BOT)) {
(void) xtcsrvalid(xyio); /* don't return too fast */
return;
}
/*
* An operation completed... record status
*/
err = xt->xt_errno;
if ((bp->b_flags & B_READ) == 0)
sc->sc_lastiow = 1;
if (err == XTE_BOT && (sc->sc_xtstat & XTS_BOT))
err = XTE_NOERROR;
if (err == XTE_SOFTERR) {
printf("xt%d: soft error bn=%d\n", xtunit, bp->b_blkno);
err = XTE_NOERROR;
}
sc->sc_error = err;
/*
* Check for errors.
*/
if (err != XTE_NOERROR && err != XTE_SHORTREC && err != XTE_EOT) {
/*
* If we hit the end of the tape file, update our position.
*/
if (err == XTE_EOF) {
/* just read past EOF */
sc->sc_fileno++;
sc->sc_recno = 0;
goto opdone;
}
if (err == XTE_LONGREC) {
bp->b_flags |= B_ERROR;
bp->b_error = EINVAL;
sc->sc_resid = bp->b_bcount;
sc->sc_recno++; /* this rec has been read */
goto opdone;
}
/*
* If error is not hard, and this was an i/o operation
* retry up to MX_RETRY times.
*/
if (state == SIO && err != XTE_HOST_TMO) {
if (++mc->mc_tab.b_errcnt <= MX_RETRY) {
mc->mc_tab.b_active = SIOBSR;
goto opcont;
}
} else {
/*
* Hard or non-i/o errors on non-raw tape
* cause it to close.
*/
if ((int)sc->sc_openf > 0 && bp != &rxtbuf[xtunit])
sc->sc_openf = -1;
}
/*
* Couldn't recover error
*/
printf("xt%d: hard error bn=%d er=0x%x\n", xtunit,
bp->b_blkno, err);
bp->b_flags |= B_ERROR;
goto opdone;
}
/*
* Advance tape control FSM.
*/
switch (state) {
case SIO:
/*
* Read/write increments tape block number
*/
sc->sc_recno++;
sc->sc_lstpos += xt->xt_acnt;
break;
case SREW:
case SCOM:
/*
* For forward/backward space record update current position.
*/
if (bp == &cxtbuf[xtunit])
switch (bp->b_command) {
/* record skipping */
case (XT_SEEK<<8)+XT_REC:
sc->sc_recno += xt->xt_acnt;
if (err == XTE_SHORTREC) {
sc->sc_fileno++;
sc->sc_recno = 0;
}
break;
case (XT_SEEK<<8)+XT_REVERSE+XT_REC:
sc->sc_recno -= xt->xt_acnt;
if (err == XTE_SHORTREC) {
sc->sc_fileno--;
sc->sc_recno = -1;
}
break;
/* file skipping */
case (XT_SEEK<<8)+XT_FILE:
sc->sc_fileno += xt->xt_acnt;
sc->sc_recno = 0;
break;
case (XT_SEEK<<8)+XT_REVERSE+XT_FILE:
sc->sc_fileno -= xt->xt_acnt;
if (sc->sc_fileno == 0)
sc->sc_recno = 0; /* at BOT */
else
sc->sc_recno = -1; /* before EOF */
break;
case (XT_SEEK<<8)+XT_REW:
case (XT_SEEK<<8)+XT_UNLOAD:
sc->sc_recno = 0;
sc->sc_fileno = 0;
sc->sc_resid = 0;
break;
/* WEOF */
case (XT_FMARK<<8):
/* NOTE: we write one EOF at a time */
sc->sc_fileno++;
break;
}
break;
case SIOBSR:
/* we just back up one record */
if (bp->b_flags & B_READ)
mc->mc_tab.b_active = SIO;
else
mc->mc_tab.b_active = SERR; /* ready for short earse */
goto opcont;
case SWRTEOM:
/* return zero byte count */
sc->sc_resid = bp->b_bcount;
goto opdone;
case SERR:
mc->mc_tab.b_active = SIO; /* ready to write again */
goto opcont;
default:
printf("xtintr: BAD STATE: %d\n", state);
goto opcont;
}
opdone:
mc->mc_tab.b_active = 0;
mc->mc_tab.b_flags |= B_DONE;
opcont:
sc->sc_timo = INF;
if (mc->mc_mbinfo != 0)
mbdone(mc);
else
xtdone(mc);
}
/*
* polling interrupt routine.
*/
xtpoll()
{
register struct xtctlr *c;
for (c = xtctlrs; c < &xtctlrs[NXTC]; c++) {
if (!c->c_present || !xtcsrvalid(c->c_io) ||
(c->c_io->xy_csr & XY_INTR) == 0)
continue;
xtintr(c);
return (1);
}
return (0);
}
xtdone(mc)
register struct mb_ctlr *mc;
{
register struct buf *dp;
register struct buf *bp;
register struct xt_softc *sc;
int xtunit;
dp = mc->mc_tab.b_actf;
bp = dp->b_actf;
xtunit = MTUNIT(bp->b_dev);
sc = &xt_softc[xtunit];
if (mc->mc_tab.b_flags & B_DONE) {
/*
* Reset error count and remove
* from device queue.
*/
mc->mc_tab.b_errcnt = 0;
dp->b_actf = bp->av_forw;
bp->b_resid = sc->sc_resid;
iodone(bp);
if (sc->sc_bufcnt-- >= MAXMTBUF)
wakeup((caddr_t)&sc->sc_bufcnt);
/*
* Advance controller queue and put this
* unit back on the controller queue if
* the unit queue is not empty
*/
mc->mc_tab.b_actf = dp->b_forw;
if (dp->b_actf) {
dp->b_forw = NULL;
if (mc->mc_tab.b_actf == NULL)
mc->mc_tab.b_actf = dp;
else
mc->mc_tab.b_actl->b_forw = dp;
mc->mc_tab.b_actl = dp;
}
} else {
/*
* Circulate slave to end of controller
* queue to give other slaves a chance.
* No need to look at unit queue since operation
* is still in progress
*/
if (dp->b_forw) {
mc->mc_tab.b_actf = dp->b_forw;
dp->b_forw = NULL;
mc->mc_tab.b_actl->b_forw = dp;
mc->mc_tab.b_actl = dp;
}
}
if (mc->mc_tab.b_actf)
xtstart(mc);
}
xttimer(dev)
int dev;
{
register struct mb_ctlr *mc = xtcinfo[XTCTLR(dev)];
register struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
register struct xtctlr *c = &xtctlrs[XTCTLR(dev)];
register struct xtiopb *xt = c->c_iopb;
register struct xydevice *xyio;
register int s;
int piopb, t;
if (sc->sc_timo != INF && (sc->sc_timo -= 2) < 0) {
printf("xt%d: timeout interrupt\n", MTUNIT(dev));
sc->sc_timo = INF;
s = SPLXT();
xt->xt_errno = XTE_HOST_TMO;
xtintr(c);
(void) splx(s);
}
if (mc->mc_tab.b_active == SREW) { /* check rewind status */
s = SPLXT();
if ((mc->mc_tab.b_flags & B_BUSY) == 0 &&
(c->c_io->xy_csr & XY_BUSY) == 0) {
xyio = c->c_io;
bzero((caddr_t)xt, sizeof *xt);
xt->xt_autoup = 1;
xt->xt_reloc = 1;
xt->xt_ie = 1;
xt->xt_cmd = XT_DSTAT;
xt->xt_throttle = xtthrot;
xt->xt_unit = xtdinfo[MTUNIT(dev)]->md_slave;
piopb = ((char *)xt) - DVMA;
t = XYREL(xyio, piopb);
xyio->xy_iopbrel[0] = t >> 8;
xyio->xy_iopbrel[1] = t;
t = XYOFF(piopb);
xyio->xy_iopboff[0] = t >> 8;
xyio->xy_iopboff[1] = t;
xyio->xy_csr = XY_GO;
mc->mc_tab.b_flags |= B_BUSY;
}
(void) splx(s);
}
timeout(xttimer, (caddr_t)dev, 2*hz);
}
/*
* Wait for controller csr to become valid.
* Waits for at most 100 usec. Returns true if wait succeeded.
*/
int
xtcsrvalid(xyio)
register struct xydevice *xyio;
{
register int i;
for (i = 10; i && xyio->xy_csr == (XY_BUSY|XY_DBLERR); i--)
DELAY(10);
return (xyio->xy_csr != (XY_BUSY|XY_DBLERR));
}
/*
* Wait for controller become ready. Used after reset or interrupt.
* Waits for at most 200 usec. Returns true if wait succeeded.
*/
int
xtwait(xyio)
register struct xydevice *xyio;
{
register int i;
for (i = 20; i && (xyio->xy_csr & XY_BUSY); i--)
DELAY(10);
return ((xyio->xy_csr & XY_BUSY) == 0);
}
void
xtminphys(bp)
struct buf *bp;
{
if (bp->b_bcount > MINPHYS_BYTES)
bp->b_bcount = MINPHYS_BYTES;
}
xtread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int xtunit = MTUNIT(dev);
register struct mb_device *md;
if (xtunit >= NXT || (md=xtdinfo[xtunit]) == 0 || md->md_alive == 0)
return (ENXIO);
if (xtflush_eom(dev, EOM_TWO_EOFS))
return (EIO);
return (physio(xtstrategy, &rxtbuf[MTUNIT(dev)], dev, B_READ,
xtminphys, uio));
}
xtwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int xtunit = MTUNIT(dev);
register struct mb_device *md;
if (xtunit >= NXT || (md=xtdinfo[xtunit]) == 0 || md->md_alive == 0)
return (ENXIO);
return (physio(xtstrategy, &rxtbuf[MTUNIT(dev)], dev, B_WRITE,
xtminphys, uio));
}
/*ARGSUSED*/
xtioctl(dev, cmd, data, flag)
dev_t dev;
caddr_t data;
{
int xtunit = MTUNIT(dev);
register struct xt_softc *sc = &xt_softc[xtunit];
register callcount;
int fcount, op, stat;
struct mtop *mtop;
struct mtget *mtget;
int flush = NO_EOM; /* default flush value */
/* we depend on the values and order of the MT codes here */
static tmops[] = {
(XT_FMARK<<8), /* MTWEOF */
(XT_SEEK<<8)+XT_FILE, /* MTFSF */
(XT_SEEK<<8)+XT_REVERSE+XT_FILE, /* MTBSF */
(XT_SEEK<<8)+XT_REC, /* MTFSR */
(XT_SEEK<<8)+XT_REVERSE+XT_REC, /* MTBSR */
(XT_SEEK<<8)+XT_REW, /* MTREW */
(XT_SEEK<<8)+XT_UNLOAD, /* MTOFFL */
(XT_DSTAT<<8), /* MTNOP */
(XT_NOP<<8), /* MTRETEN */
(XT_FMARK<<8)+XT_ERASE, /* MTERASE */
(XT_NOP<<8), /* MTEOM */
(XT_SEEK<<8)+XT_REVERSE+XT_FILE, /* MTNBSF */
};
switch (cmd) {
case MTIOCTOP: /* tape operation */
mtop = (struct mtop *)data;
switch (mtop->mt_op) {
case MTFSF:
case MTBSF:
/*
* For ASF and compatibility with st driver,
* we allow a count of 0 which means we just
* want to go to beginning of current file.
* Equivalent to "nbsf(0)" or "bsf(1) + fsf".
*/
callcount = 1;
if ((fcount = mtop->mt_count) == 0) {
fcount++;
mtop->mt_op = MTNBSF;
}
flush = EOM_TWO_EOFS;
break;
case MTNBSF:
callcount = 1;
/*
* NBSF(n) == BSF(n+1) + FSF
*/
fcount = mtop->mt_count + 1;
flush = EOM_TWO_EOFS;
break;
case MTFSR:
case MTBSR:
callcount = 1;
/*
* For compatibility with st driver,
* 0 count is NOP.
*/
if ((fcount = mtop->mt_count) == 0)
return (0);
flush = EOM_TWO_EOFS;
break;
case MTREW:
case MTOFFL:
case MTNOP:
callcount = 1;
fcount = mtop->mt_count;
flush = EOM_NO_EOF;
break;
case MTERASE:
if ((sc->sc_open_opt & FWRITE) == 0)
return (EACCES);
if (xtcommand(dev, XT_SEEK, XT_REW, 1))
return (EIO);
callcount = FOREVER;
fcount = 1;
break;
case MTWEOF:
if ((sc->sc_open_opt & FWRITE) == 0)
return (EACCES);
callcount = mtop->mt_count;
fcount = 1;
if (callcount == 1) {
/* always write one extra for EOM */
if (xtwrt_eom(dev, EOM_ONE_EOF)) {
return (EIO);
} else
goto opdone;
}
break;
case MTEOM:
if (xtflush_eom(dev, EOM_TWO_EOFS))
return (EIO);
/* search until error or EOM found */
while (1)
if ((stat = xtchk_file(dev)) != 0) {
if (stat == -1) {
/* empty file (EOM) found */
sc->sc_resid = 0;
/* EOM => recno 0 */
sc->sc_recno = 0;
return (0);
} else { /* error */
sc->sc_resid = 1;
return (EIO);
}
}
default:
return (ENOTTY);
}
if (callcount <= 0 || fcount <= 0)
return (ENOTTY);
if (flush != NO_EOM && xtflush_eom(dev, flush))
return (EIO);
while (--callcount >= 0) {
op = tmops[mtop->mt_op];
if (xtcommand(dev, op >> 8, op & 0xFF, fcount))
return (EIO);
/*
* stop erase, otherwise, it would rip the
* tape off the wheel.
*/
if ((sc->sc_xtstat & XTS_EOT) &&
(mtop->mt_op == MTERASE) &&
callcount > ERASE_AFT_EOT) {
callcount = ERASE_AFT_EOT;
}
}
if (mtop->mt_op == MTERASE) {
if (xtcommand(dev, XT_SEEK, XT_REW, 1))
return (EIO);
break;
}
if (mtop->mt_op == MTNBSF) {
/* skip over EOF for NBSF */
op = tmops[MTFSF];
if (xtcommand(dev, op >> 8, op & 0xFF, 1))
return (EIO);
break;
}
opdone:
/* do extra accounting work */
if (mtop->mt_op == MTWEOF)
sc->sc_recno = 0;
break;
case MTIOCGET:
mtget = (struct mtget *)data;
mtget->mt_dsreg = sc->sc_xtstat;
mtget->mt_erreg = sc->sc_error;
mtget->mt_resid = sc->sc_resid;
mtget->mt_type = MT_ISXY;
if (sc->sc_xtstat & XTS_BOT) {
/*
* Foolproofing in case there's manual intervention
* in the middle of a program which is not well-
* behaved. (e.g., suninstall doesn't always issue
* offline/rewind the tape when it should.) This
* really belongs only in xtopen, but as long as
* we're foolproofing, we might as well allow manual
* intervention in a program which does its own ioctls.
*/
sc->sc_fileno = 0;
sc->sc_recno = 0;
}
mtget->mt_fileno = sc->sc_fileno;
mtget->mt_blkno = sc->sc_recno;
/*
* Note that ASF has really only been implemented
* to the extent that it will support suninstall.
*/
mtget->mt_flags = MTF_REEL | MTF_ASF;
mtget->mt_bf = 20;
break;
default:
return (ENOTTY);
}
return (0);
}
static
xtfwd_rec(dev, count)
dev_t dev;
int count;
{
struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
int sv_resid;
/*
* a record length short status is posted when the
* 472 detects a tape mark on space record forward
*/
if (xtcommand(dev, XT_SEEK, XT_REC, count)) {
if (sc->sc_error == XTE_SHORTREC || sc->sc_error == XTE_EOT) {
sv_resid = sc->sc_resid;
/* leave tape positioned between the two EOFs */
(void) xtcommand(dev, XT_SEEK, XT_FILE+XT_REVERSE, 1);
sc->sc_resid = sv_resid;
sc->sc_error = XTE_SHORTREC;
}
return (EIO);
} else
return (0);
}
static
xtchk_file(dev)
dev_t dev;
{
struct xt_softc *sc = &xt_softc[MTUNIT(dev)];
int stat;
/*
* If we're just after EOF (recno == 0) and we just
* encountered another EOF, then we've found the EOM.
*/
if (sc->sc_recno == 0 && xtfwd_rec(dev, 1)) {
if (sc->sc_error == XTE_SHORTREC)
return (-1); /* empty file found!! */
else
return (EIO);
}
/* not an empty file, skip it */
stat = xtcommand(dev, XT_SEEK, XT_FILE, 1);
return (stat);
}
#endif