all nonexistent slaves aren't on formatter 0!
[unix-history] / usr / src / sys / vax / mba / mt.c
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)mt.c 7.1 (Berkeley) %G%
*/
#include "mu.h"
#if NMT > 0
/*
* TM78/TU78 tape driver
*
* Original author - ?
* Most error recovery bug fixes - ggs (ulysses!ggs)
*
* OPTIONS:
* MTLERRM - Long error message text - twd, Brown University
* MTRDREV - `read reverse' error recovery - ggs (ulysses!ggs)
*
* TODO:
* Add odd byte count kludge from VMS driver (?)
* Write dump routine
*/
#include "../machine/pte.h"
#include "param.h"
#include "systm.h"
#include "buf.h"
#include "conf.h"
#include "dir.h"
#include "file.h"
#include "user.h"
#include "map.h"
#include "ioctl.h"
#include "mtio.h"
#include "cmap.h"
#include "uio.h"
#include "tty.h"
#include "../vax/cpu.h"
#include "mbareg.h"
#include "mbavar.h"
#include "mtreg.h"
#define MTTIMEOUT 10000 /* loop limit for controller test */
#define INF 1000000L /* a block number that won't exist */
#define MASKREG(r) ((r) & 0xffff) /* the control registers have 16 bits */
/* Bits for sc_flags */
#define H_WRITTEN 01 /* last operation was a write */
#define H_EOT 02 /* end of tape encountered */
#define H_IEOT 04 /* ignore EOT condition */
/* Bits in minor device */
#define MUUNIT(dev) (minor(dev)&03)
#define H_NOREWIND 04
#define H_6250BPI 010
#define MTUNIT(dev) (mutomt[MUUNIT(dev)])
#ifdef MTRDREV
int mt_do_readrev = 1;
#else
int mt_do_readrev = 0;
#endif
/* Per unit status information */
struct mu_softc {
char sc_openf; /* unit is open if != 0 */
char sc_flags; /* state flags */
daddr_t sc_blkno; /* current physical block number */
daddr_t sc_nxrec; /* firewall input block number */
u_short sc_erreg; /* copy of mter or mtner */
u_short sc_dsreg; /* copy of mtds */
short sc_resid; /* residual function count for ioctl */
short sc_dens; /* density code - MT_GCR or zero */
struct mba_device *sc_mi; /* massbus structure for unit */
int sc_slave; /* slave number for unit */
int sc_i_mtas; /* mtas at slave attach time */
int sc_i_mtner; /* mtner at slave attach time */
int sc_i_mtds; /* mtds at slave attach time */
#ifdef MTLERRM
char *sc_mesg; /* text for interrupt type code */
char *sc_fmesg; /* text for tape error code */
#endif
struct tty *sc_ttyp; /* record user's tty for errors */
} mu_softc[NMU];
struct buf rmtbuf[NMT]; /* data transfer buffer structures */
struct buf cmtbuf[NMT]; /* tape command buffer structures */
struct mba_device *mtinfo[NMT]; /* unit massbus structure pointers */
short mutomt[NMU]; /* tape unit to controller number map */
char mtds_bits[] = MTDS_BITS; /* mtds bit names for error messages */
short mttypes[] = { MBDT_TU78, 0 };
int mtattach(), mtslave(), mtustart(), mtstart(), mtndtint(), mtdtint();
struct mba_driver mtdriver =
{ mtattach, mtslave, mtustart, mtstart, mtdtint, mtndtint,
mttypes, "mt", "mu", mtinfo };
void mtcreset();
/*ARGSUSED*/
mtattach(mi)
struct mba_device *mi;
{
}
mtslave(mi, ms, sn)
struct mba_device *mi;
struct mba_slave *ms;
int sn;
{
register struct mu_softc *sc = &mu_softc[ms->ms_unit];
register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
int s = spl5(), rtn = 0, i;
/* Just in case the controller is ill, reset it. Then issue */
/* a sense operation and wait about a second for it to respond. */
mtcreset(mtaddr);
mtaddr->mtas = -1;
mtaddr->mtncs[sn] = MT_SENSE|MT_GO;
for (i = MTTIMEOUT; i> 0; i--) {
DELAY(50);
if (MASKREG(mtaddr->mtas) != 0)
break;
}
sc->sc_i_mtas = mtaddr->mtas;
sc->sc_i_mtner = mtaddr->mtner;
sc->sc_i_mtds = mtaddr->mtds;
/* If no response, whimper. If wrong response, call it an */
/* unsolicited interrupt and use mtndtint to log and correct. */
/* Otherwise, note whether this slave exists. */
if (i <= 0) {
printf("mt: controller hung\n");
} else if ((mtaddr->mtner & MTER_INTCODE) != MTER_DONE) {
(void) mtndtint(mi);
} else if (mtaddr->mtds & MTDS_PRES) {
sc->sc_mi = mi;
sc->sc_slave = sn;
mutomt[ms->ms_unit] = mi->mi_unit;
rtn = 1;
}
/* Cancel the interrupt, then wait a little while for it to go away. */
mtaddr->mtas = mtaddr->mtas;
DELAY(10);
splx(s);
return (rtn);
}
mtopen(dev, flag)
dev_t dev;
int flag;
{
register int muunit;
register struct mba_device *mi;
register struct mu_softc *sc;
muunit = MUUNIT(dev);
if ( (muunit >= NMU)
|| ((mi = mtinfo[MTUNIT(dev)]) == 0)
|| (mi->mi_alive == 0) )
return (ENXIO);
if ((sc = &mu_softc[muunit])->sc_openf)
return (EBUSY);
sc->sc_dens = (minor(dev) & H_6250BPI) ? MT_GCR : 0;
mtcommand(dev, MT_SENSE, 1);
if ((sc->sc_dsreg & MTDS_ONL) == 0) {
uprintf("mu%d: not online\n", muunit);
return (EIO);
}
if ((sc->sc_dsreg & MTDS_AVAIL) == 0) {
uprintf("mu%d: not online (port selector)\n", muunit);
return (EIO);
}
if ((flag & FWRITE) && (sc->sc_dsreg & MTDS_FPT)) {
uprintf("mu%d: no write ring\n", muunit);
return (EIO);
}
if ( ((sc->sc_dsreg & MTDS_BOT) == 0)
&& (flag & FWRITE)
&& ( ( (sc->sc_dens == MT_GCR)
&& (sc->sc_dsreg & MTDS_PE) )
|| ( (sc->sc_dens != MT_GCR)
&& ((sc->sc_dsreg & MTDS_PE) == 0)))) {
uprintf("mu%d: can't change density in mid-tape\n", muunit);
return (EIO);
}
sc->sc_openf = 1;
sc->sc_blkno = (daddr_t)0;
/* Since cooked I/O may do a read-ahead before a write, trash */
/* on a tape can make the first write fail. Suppress the first */
/* read-ahead unless definitely doing read-write */
sc->sc_nxrec = ((flag & (FTRUNC | FWRITE)) == (FTRUNC | FWRITE))
? (daddr_t)0
: (daddr_t)INF;
sc->sc_flags = 0;
sc->sc_ttyp = u.u_ttyp;
return (0);
}
mtclose(dev, flag)
register dev_t dev;
register int flag;
{
register struct mu_softc *sc = &mu_softc[MUUNIT(dev)];
if ( ((flag & (FREAD | FWRITE)) == FWRITE)
|| ( (flag & FWRITE)
&& (sc->sc_flags & H_WRITTEN) ))
mtcommand(dev, MT_CLS|sc->sc_dens, 1);
if ((minor(dev) & H_NOREWIND) == 0)
mtcommand(dev, MT_REW, 0);
sc->sc_openf = 0;
}
mtcommand(dev, com, count)
dev_t dev;
int com, count;
{
register struct buf *bp;
register int s;
bp = &cmtbuf[MTUNIT(dev)];
s = spl5();
while (bp->b_flags & B_BUSY) {
if((bp->b_repcnt == 0) && (bp->b_flags & B_DONE))
break;
bp->b_flags |= B_WANTED;
sleep((caddr_t)bp, PRIBIO);
}
bp->b_flags = B_BUSY|B_READ;
splx(s);
bp->b_dev = dev;
bp->b_command = com;
bp->b_repcnt = count;
bp->b_blkno = 0;
bp->b_error = 0;
mtstrategy(bp);
if (count == 0)
return;
iowait(bp);
if (bp->b_flags & B_WANTED)
wakeup((caddr_t)bp);
bp->b_flags &= B_ERROR;
}
mtstrategy(bp)
register struct buf *bp;
{
register struct mba_device *mi = mtinfo[MTUNIT(bp->b_dev)];
register struct buf *dp;
register int s;
/* If this is a data transfer operation, set the resid to a */
/* default value (EOF) to simplify getting it right during */
/* error recovery or bail out. */
if (bp != &cmtbuf[MTUNIT(bp->b_dev)])
bp->b_resid = bp->b_bcount;
/* Link this request onto the end of the queue for this */
/* controller, then start I/O if not already active. */
bp->av_forw = NULL;
dp = &mi->mi_tab;
s = spl5();
if (dp->b_actf == NULL)
dp->b_actf = bp;
else
dp->b_actl->av_forw = bp;
dp->b_actl = bp;
if (dp->b_active == 0)
mbustart(mi);
splx(s);
}
mtustart(mi)
register struct mba_device *mi;
{
register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
register struct buf *bp = mi->mi_tab.b_actf;
register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
daddr_t blkno;
int count;
if (sc->sc_openf < 0) {
bp->b_flags |= B_ERROR;
return (MBU_NEXT);
}
if (bp != &cmtbuf[MTUNIT(bp->b_dev)]) {
/* Signal "no space" if out of tape unless suppressed */
/* by MTIOCIEOT. */
if ( ((sc->sc_flags & (H_EOT | H_IEOT)) == H_EOT)
&& ((bp->b_flags & B_READ) == 0) ) {
bp->b_flags |= B_ERROR;
bp->b_error = ENOSPC;
return (MBU_NEXT);
}
/* special case tests for cooked mode */
if (bp != &rmtbuf[MTUNIT(bp->b_dev)]) {
/* seek beyond end of file */
if (bdbtofsb(bp->b_blkno) > sc->sc_nxrec) {
bp->b_flags |= B_ERROR;
bp->b_error = ENXIO;
return (MBU_NEXT);
}
/* This should be end of file, but the buffer */
/* system wants a one-block look-ahead. Humor it. */
if ( (bdbtofsb(bp->b_blkno) == sc->sc_nxrec)
&& (bp->b_flags & B_READ) ) {
clrbuf(bp);
return (MBU_NEXT);
}
/* If writing, mark the next block invalid. */
if ((bp->b_flags & B_READ) == 0)
sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
}
} else {
/* It's a command, do it now. */
mtaddr->mtncs[MUUNIT(bp->b_dev)] =
(bp->b_repcnt<<8)|bp->b_command|MT_GO;
return (MBU_STARTED);
}
/* If raw I/O, or if the tape is positioned correctly for */
/* cooked I/O, set the byte count, unit number and repeat count */
/* then tell the MASSBUS to proceed. Note that a negative */
/* bcount tells mbstart to map the buffer for "read backwards". */
if ( (bp == &rmtbuf[MTUNIT(bp->b_dev)])
|| ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) ) {
if (mi->mi_tab.b_errcnt == 2) {
mtaddr->mtbc = -(bp->b_bcount);
mtaddr->mtca = MUUNIT(bp->b_dev);
} else {
mtaddr->mtbc = bp->b_bcount;
mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);
}
return (MBU_DODATA);
}
/* Issue skip operations to position the next block for cooked I/O. */
if (blkno < bdbtofsb(bp->b_blkno))
count = (unsigned)(bdbtofsb(bp->b_blkno) - blkno);
else
count = (unsigned)(blkno - bdbtofsb(bp->b_blkno));
if (count > 0377)
count = 0377;
mtaddr->mtncs[MUUNIT(bp->b_dev)] = count | MT_SFORW|MT_GO;
return (MBU_STARTED);
}
mtstart(mi)
register struct mba_device *mi;
{
register struct buf *bp = mi->mi_tab.b_actf;
register struct mu_softc *sc = &mu_softc[MUUNIT(bp->b_dev)];
if (bp->b_flags & B_READ)
if (mi->mi_tab.b_errcnt == 2)
return(MT_READREV|MT_GO);
else
return(MT_READ|MT_GO);
else
return(MT_WRITE|sc->sc_dens|MT_GO);
}
mtdtint(mi, mbsr)
register struct mba_device *mi;
int mbsr;
{
register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
register struct buf *bp = mi->mi_tab.b_actf;
register struct mu_softc *sc;
register int er;
/* I'M still NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
if ((mtaddr->mtca & 3) != MUUNIT(bp->b_dev)) {
printf("mt: wrong unit!\n");
mtaddr->mtca = MUUNIT(bp->b_dev);
}
er = MASKREG(mtaddr->mter);
sc = &mu_softc[MUUNIT(bp->b_dev)];
sc->sc_erreg = er;
if (bp->b_flags & B_READ)
sc->sc_flags &= ~H_WRITTEN;
else
sc->sc_flags |= H_WRITTEN;
switch (er & MTER_INTCODE) {
case MTER_EOT:
sc->sc_flags |= H_EOT;
/* fall into MTER_DONE */
case MTER_DONE:
sc->sc_blkno++;
if (mi->mi_tab.b_errcnt == 2) {
bp->b_bcount = bp->b_resid;
bp->b_resid -= MASKREG(mtaddr->mtbc);
if ( (bp->b_resid > 0)
&& (bp != &rmtbuf[MTUNIT(bp->b_dev)]) )
bp->b_flags |= B_ERROR;
} else {
bp->b_resid = 0;
}
break;
case MTER_SHRTREC:
sc->sc_blkno++;
bp->b_bcount = bp->b_resid;
bp->b_resid -= MASKREG(mtaddr->mtbc);
if (bp != &rmtbuf[MTUNIT(bp->b_dev)])
bp->b_flags |= B_ERROR;
break;
case MTER_RETRY:
/* Simple re-try. Since resid is always a copy of the */
/* original byte count, use it to restore the count. */
mi->mi_tab.b_errcnt = 1;
bp->b_bcount = bp->b_resid;
return(MBD_RETRY);
case MTER_RDOPP:
/* The controller just decided to read it backwards. */
/* If the controller returns a byte count of zero, */
/* change it to 1, since zero encodes 65536, which */
/* isn't quite what we had in mind. The byte count */
/* may be larger than the size of the input buffer, so */
/* limit the count to the buffer size. After */
/* making the byte count reasonable, set bcount to the */
/* negative of the controller's version of the byte */
/* count so that the start address for the transfer is */
/* set up correctly. */
if (mt_do_readrev) {
mi->mi_tab.b_errcnt = 2;
if ((bp->b_bcount = MASKREG(mtaddr->mtbc)) == 0)
bp->b_bcount = 1;
if (bp->b_bcount > bp->b_resid)
bp->b_bcount = bp->b_resid;
bp->b_bcount = -(bp->b_bcount);
return(MBD_RETRY);
} else if (MASKREG(mtaddr->mtbc) <= bp->b_resid) {
sc->sc_blkno++;
bp->b_bcount = bp->b_resid;
bp->b_resid -= MASKREG(mtaddr->mtbc);
bp->b_flags |= B_ERROR;
break;
}
bp->b_flags |= B_ERROR;
/* fall into MTER_LONGREC */
case MTER_LONGREC:
sc->sc_blkno++;
bp->b_bcount = bp->b_resid;
bp->b_resid = 0;
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
break;
case MTER_NOTCAP:
printf("mu%d: blank tape\n", MUUNIT(bp->b_dev));
goto err;
case MTER_TM:
/* End of file. Since the default byte count has */
/* already been set, just count the block and proceed. */
sc->sc_blkno++;
err:
if (bp != &rmtbuf[MTUNIT(bp->b_dev)])
sc->sc_nxrec = bdbtofsb(bp->b_blkno);
break;
case MTER_OFFLINE:
if (sc->sc_openf > 0) {
sc->sc_openf = -1;
tprintf(sc->sc_ttyp, "mu%d: offline\n", MUUNIT(bp->b_dev));
}
bp->b_flags |= B_ERROR;
break;
case MTER_NOTAVL:
if (sc->sc_openf > 0) {
sc->sc_openf = -1;
tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n",
MUUNIT(bp->b_dev));
}
bp->b_flags |= B_ERROR;
break;
case MTER_FPT:
tprintf(sc->sc_ttyp, "mu%d: no write ring\n", MUUNIT(bp->b_dev));
bp->b_flags |= B_ERROR;
break;
case MTER_UNREAD:
sc->sc_blkno++;
bp->b_bcount = bp->b_resid;
bp->b_resid -= MIN(MASKREG(mtaddr->mtbc), bp->b_bcount);
/* Code 010 means a garbage record, nothing serious. */
if (((er & MTER_FAILCODE) >> 10) == 010) {
tprintf(sc->sc_ttyp, "mu%d: rn=%d bn=%d unreadable record\n",
MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno);
bp->b_flags |= B_ERROR;
break;
}
/* Anything else might be a hardware problem, */
/* fall into the error report. */
default:
/* The bits in sc->sc_dsreg are from the last sense */
/* command. To get the most recent copy, you have to */
/* do a sense at interrupt level, which requires nested */
/* error processing. This is a bit messy, so leave */
/* well enough alone. */
tprintf(sc->sc_ttyp, "mu%d: hard error (data transfer) rn=%d bn=%d mbsr=%b er=%o (octal) ds=%b\n",
MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
mbsr, mbsr_bits, er,
MASKREG(sc->sc_dsreg), mtds_bits);
#ifdef MTLERRM
mtintfail(sc);
printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n",
er & MTER_INTCODE, sc->sc_mesg,
(er & MTER_FAILCODE) >> 10, sc->sc_fmesg);
#endif
bp->b_flags |= B_ERROR;
/* The TM78 manual says to reset the controller after */
/* TM fault B or MASSBUS fault. */
if ( ((er & MTER_INTCODE) == MTER_TMFLTB)
|| ((er & MTER_INTCODE) == MTER_MBFLT) ) {
mtcreset(mtaddr);
}
}
/* Just in case some strange error slipped through, (drive off */
/* line during read-reverse error recovery comes to mind) make */
/* sure the byte count is reasonable. */
if (bp->b_bcount < 0)
bp->b_bcount = bp->b_resid;
return (MBD_DONE);
}
mtndtint(mi)
register struct mba_device *mi;
{
register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
register struct buf *bp = mi->mi_tab.b_actf;
register struct mu_softc *sc;
register int er, fc;
int unit;
unit = (mtaddr->mtner >> 8) & 3;
er = MASKREG(mtaddr->mtner);
sc = &mu_softc[unit];
sc->sc_erreg = er;
/* Check for unsolicited interrupts. */
if (bp == 0 || unit != MUUNIT(bp->b_dev)) { /* consistency check */
if ((er & MTER_INTCODE) != MTER_ONLINE) {
printf("mt: unit %d unexpected interrupt (non data transfer) er=%o (octal) ds=%b\n",
unit, er, MASKREG(sc->sc_dsreg), mtds_bits);
#ifdef MTLERRM
mtintfail(sc);
printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n",
er & MTER_INTCODE, sc->sc_mesg,
(er & MTER_FAILCODE) >> 10, sc->sc_fmesg);
#endif
if ( ((er & MTER_INTCODE) == MTER_TMFLTB)
|| ((er & MTER_INTCODE) == MTER_MBFLT) ) {
/* Reset the controller, then set error */
/* status if there was anything active */
/* when the fault occurred. This may */
/* shoot an innocent bystander, but */
/* it's better than letting an error */
/* slip through. */
mtcreset(mtaddr);
if (bp != 0) {
bp->b_flags |= B_ERROR;
return (MBN_DONE);
}
}
}
return (MBN_SKIP);
}
if (bp == 0)
return (MBN_SKIP);
fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
sc->sc_resid = fc;
/* Clear the "written" flag after any operation that changes */
/* the position of the tape. */
if ( (bp != &cmtbuf[MTUNIT(bp->b_dev)])
|| (bp->b_command != MT_SENSE) )
sc->sc_flags &= ~H_WRITTEN;
switch (er & MTER_INTCODE) {
case MTER_EOT:
sc->sc_flags |= H_EOT;
/* fall into MTER_DONE */
case MTER_DONE:
/* If this is a command buffer, just update the status. */
if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
done:
if (bp->b_command == MT_SENSE)
sc->sc_dsreg = MASKREG(mtaddr->mtds);
return (MBN_DONE);
}
/* It's not a command buffer, must be a cooked I/O */
/* skip operation (perhaps a shaky assumption, but it */
/* wasn't my idea). */
if ((fc = bdbtofsb(bp->b_blkno) - sc->sc_blkno) < 0)
sc->sc_blkno -= MIN(0377, -fc);
else
sc->sc_blkno += MIN(0377, fc);
return (MBN_RETRY);
case MTER_ONLINE: /* ddj -- shouldn't happen but did */
case MTER_RWDING:
return (MBN_SKIP); /* ignore "rewind started" interrupt */
case MTER_NOTCAP:
tprintf(sc->sc_ttyp, "mu%d: blank tape\n", MUUNIT(bp->b_dev));
bp->b_flags |= B_ERROR;
return (MBN_DONE);
case MTER_TM:
case MTER_LEOT:
/* For an ioctl skip operation, count a tape mark as */
/* a record. If there's anything left to do, update */
/* the repeat count and re-start the command. */
if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
if ((sc->sc_resid = bp->b_repcnt = fc - 1) == 0)
return (MBN_DONE);
else
return (MBN_RETRY);
/* Cooked I/O again. Just update the books and wait */
/* for someone else to return end of file or complain */
/* about a bad seek. */
} else if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc - 1;
sc->sc_blkno = sc->sc_nxrec;
} else {
sc->sc_nxrec = bdbtofsb(bp->b_blkno) - fc;
sc->sc_blkno = sc->sc_nxrec + 1;
}
return (MBN_RETRY);
case MTER_FPT:
tprintf(sc->sc_ttyp, "mu%d: no write ring\n", MUUNIT(bp->b_dev));
bp->b_flags |= B_ERROR;
return (MBN_DONE);
case MTER_OFFLINE:
/* If `off line' was intentional, don't complain. */
if ( (bp == &cmtbuf[MTUNIT(bp->b_dev)])
&& (bp->b_command == MT_UNLOAD) )
return(MBN_DONE);
if (sc->sc_openf > 0) {
sc->sc_openf = -1;
tprintf(sc->sc_ttyp, "mu%d: offline\n", MUUNIT(bp->b_dev));
}
bp->b_flags |= B_ERROR;
return (MBN_DONE);
case MTER_NOTAVL:
if (sc->sc_openf > 0) {
sc->sc_openf = -1;
tprintf(sc->sc_ttyp, "mu%d: offline (port selector)\n", MUUNIT(bp->b_dev));
}
bp->b_flags |= B_ERROR;
return (MBN_DONE);
case MTER_BOT:
if (bp == &cmtbuf[MTUNIT(bp->b_dev)])
goto done;
/* fall through */
default:
tprintf(sc->sc_ttyp, "mu%d: hard error (non data transfer) rn=%d bn=%d er=%o (octal) ds=%b\n",
MUUNIT(bp->b_dev), sc->sc_blkno, bp->b_blkno,
er, MASKREG(sc->sc_dsreg), mtds_bits);
#ifdef MTLERRM
mtintfail(sc);
printf(" interrupt code = %o (octal) <%s>\n failure code = %o (octal) <%s>\n",
(er & MTER_INTCODE), sc->sc_mesg,
(er & MTER_FAILCODE) >> 10, sc->sc_fmesg);
#endif
if ( ((er & MTER_INTCODE) == MTER_TMFLTB)
|| ((er & MTER_INTCODE) == MTER_MBFLT) ) {
mtcreset(mtaddr); /* reset the controller */
}
bp->b_flags |= B_ERROR;
return (MBN_DONE);
}
/* NOTREACHED */
}
void mtcreset(mtaddr)
register struct mtdevice *mtaddr;
{
register int i;
mtaddr->mtid = MTID_CLR; /* reset the TM78 */
DELAY(200);
for (i = MTTIMEOUT; i > 0; i--) {
DELAY(50); /* don't nag */
if ((mtaddr->mtid & MTID_RDY) != 0)
return; /* exit when ready */
}
printf("mt: controller hung\n");
}
mtread(dev, uio)
dev_t dev;
struct uio *uio;
{
int errno;
errno = mtphys(dev, uio);
if (errno)
return (errno);
return (physio(mtstrategy, &rmtbuf[MTUNIT(dev)], dev, B_READ, minphys, uio));
}
mtwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
int errno;
errno = mtphys(dev, uio);
if (errno)
return (errno);
return (physio(mtstrategy, &rmtbuf[MTUNIT(dev)], dev, B_WRITE, minphys, uio));
}
mtphys(dev, uio)
dev_t dev;
struct uio *uio;
{
register int mtunit;
struct mba_device *mi;
register int bsize = uio->uio_iov->iov_len;
mtunit = MTUNIT(dev);
if ( (mtunit >= NMT)
|| ((mi = mtinfo[mtunit]) == 0)
|| (mi->mi_alive == 0) )
return (ENXIO);
if ( (bsize > 0xffff) /* controller limit */
|| (bsize <= 0) ) /* ambiguous */
return (EINVAL);
return (0);
}
/*ARGSUSED*/
mtioctl(dev, cmd, data, flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
register struct mu_softc *sc = &mu_softc[MUUNIT(dev)];
register struct buf *bp = &cmtbuf[MTUNIT(dev)];
register struct mtop *mtop;
register struct mtget *mtget;
int callcount, fcount;
int op;
/* We depend on the values and order of the MT codes here. */
static mtops[] =
{MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE};
switch (cmd) {
/* tape operation */
case MTIOCTOP:
mtop = (struct mtop *)data;
switch (mtop->mt_op) {
case MTWEOF:
callcount = mtop->mt_count;
fcount = 1;
break;
case MTFSF: case MTBSF:
callcount = mtop->mt_count;
fcount = 1;
break;
case MTFSR: case MTBSR:
callcount = 1;
fcount = mtop->mt_count;
break;
case MTREW: case MTOFFL:
callcount = 1;
fcount = 1;
break;
default:
return (ENXIO);
}
if ((callcount <= 0) || (fcount <= 0))
return (EINVAL);
op = mtops[mtop->mt_op];
if (op == MT_WTM)
op |= sc->sc_dens;
while (--callcount >= 0) {
register int n, fc = fcount;
do {
n = MIN(fc, 0xff);
mtcommand(dev, op, n);
n -= sc->sc_resid;
fc -= n;
switch (mtop->mt_op) {
case MTWEOF:
sc->sc_blkno += (daddr_t)n;
sc->sc_nxrec = sc->sc_blkno - 1;
break;
case MTOFFL:
case MTREW:
case MTFSF:
sc->sc_blkno = (daddr_t)0;
sc->sc_nxrec = (daddr_t)INF;
break;
case MTBSF:
if (sc->sc_resid) {
sc->sc_blkno = (daddr_t)0;
sc->sc_nxrec = (daddr_t)INF;
} else {
sc->sc_blkno = (daddr_t)(-1);
sc->sc_nxrec = (daddr_t)(-1);
}
break;
case MTFSR:
sc->sc_blkno += (daddr_t)n;
break;
case MTBSR:
sc->sc_blkno -= (daddr_t)n;
break;
}
if (sc->sc_resid)
break;
} while (fc);
if (fc) {
sc->sc_resid = callcount + fc;
if ( (mtop->mt_op == MTFSR)
|| (mtop->mt_op == MTBSR) )
return (EIO);
else
break;
}
if (bp->b_flags & B_ERROR)
break;
}
return (geterror(bp));
/* tape status */
case MTIOCGET:
mtget = (struct mtget *)data;
mtget->mt_erreg = sc->sc_erreg;
mtget->mt_resid = sc->sc_resid;
mtcommand(dev, MT_SENSE, 1); /* update drive status */
mtget->mt_dsreg = sc->sc_dsreg;
mtget->mt_type = MT_ISMT;
break;
/* ignore EOT condition */
case MTIOCIEOT:
sc->sc_flags |= H_IEOT;
break;
/* enable EOT condition */
case MTIOCEEOT:
sc->sc_flags &= ~H_IEOT;
break;
default:
return (ENXIO);
}
return (0);
}
#define DBSIZE 20
mtdump()
{
register struct mba_device *mi;
register struct mba_regs *mp;
int blk, num;
int start;
start = 0;
num = maxfree;
#define phys(a,b) ((b)((int)(a)&0x7fffffff))
if (mtinfo[0] == 0)
return (ENXIO);
mi = phys(mtinfo[0], struct mba_device *);
mp = phys(mi->mi_hd, struct mba_hd *)->mh_physmba;
mp->mba_cr = MBCR_IE;
#if lint
blk = 0; num = blk; start = num; blk = start;
return (0);
#endif
#ifdef notyet
mtaddr = (struct mtdevice *)&mp->mba_drv[mi->mi_drive];
mtaddr->mttc = MTTC_PDP11|MTTC_1600BPI;
mtaddr->mtcs1 = MT_DCLR|MT_GO;
while (num > 0) {
blk = num > DBSIZE ? DBSIZE : num;
mtdwrite(start, blk, mtaddr, mp);
start += blk;
num -= blk;
}
mteof(mtaddr);
mteof(mtaddr);
mtwait(mtaddr);
if (mtaddr->mtds&MTDS_ERR)
return (EIO);
mtaddr->mtcs1 = MT_REW|MT_GO;
return (0);
}
mtdwrite(dbuf, num, mtaddr, mp)
register dbuf, num;
register struct mtdevice *mtaddr;
struct mba_regs *mp;
{
register struct pte *io;
register int i;
mtwait(mtaddr);
io = mp->mba_map;
for (i = 0; i < num; i++)
*(int *)io++ = dbuf++ | PG_V;
mtaddr->mtfc = -(num*NBPG);
mp->mba_sr = -1;
mp->mba_bcr = -(num*NBPG);
mp->mba_var = 0;
mtaddr->mtcs1 = MT_WCOM|MT_GO;
}
mtwait(mtaddr)
struct mtdevice *mtaddr;
{
register s;
do
s = mtaddr->mtds;
while ((s & MTDS_DRY) == 0);
}
mteof(mtaddr)
struct mtdevice *mtaddr;
{
mtwait(mtaddr);
mtaddr->mtcs1 = MT_WEOF|MT_GO;
#endif notyet
}
#ifdef MTLERRM
mtintfail(sc)
register struct mu_softc *sc;
{
switch (sc->sc_erreg & MTER_INTCODE) {
/* unexpected BOT detected */
case MTER_BOT:
sc->sc_mesg = "unexpected BOT";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "tape was at BOT";
break;
case 02:
sc->sc_fmesg = "BOT seen after tape started";
break;
case 03:
sc->sc_fmesg = "ARA ID detected";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* unexpected LEOT detected */
case MTER_LEOT:
sc->sc_mesg = "unexpected LEOT";
sc->sc_fmesg = "";
break;
/* rewinding */
case MTER_RWDING:
sc->sc_mesg = "tape rewinding";
sc->sc_fmesg = "";
break;
/* not ready */
case MTER_NOTRDY:
sc->sc_mesg = "drive not ready";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "TU on-line but not ready";
break;
case 02:
sc->sc_fmesg = "fatal error has occurred";
break;
case 03:
sc->sc_fmesg = "access allowed but not really";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* not available */
case MTER_NOTAVL:
sc->sc_mesg = "drive not available";
sc->sc_fmesg = "";
break;
/* unit does not exist */
case MTER_NONEX:
sc->sc_mesg = "unit does not exist";
sc->sc_fmesg = "";
break;
/* not capable */
case MTER_NOTCAP:
sc->sc_mesg = "not capable";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "no record found within 25 feet";
break;
case 02:
sc->sc_fmesg = "ID burst neither PE nor GCR";
break;
case 03:
sc->sc_fmesg = "ARA ID not found";
break;
case 04:
sc->sc_fmesg = "no gap found after ID burst";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* long tape record */
case MTER_LONGREC:
sc->sc_mesg = "long record";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 00:
sc->sc_fmesg = "extended sense data not found";
break;
case 01:
sc->sc_fmesg = "extended sense data updated";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* unreadable */
case MTER_UNREAD:
sc->sc_mesg = "unreadable record";
goto code22;
/* error */
case MTER_ERROR:
sc->sc_mesg = "error";
goto code22;
/* EOT error */
case MTER_EOTERR:
sc->sc_mesg = "EOT error";
goto code22;
/* tape position lost */
case MTER_BADTAPE:
sc->sc_mesg = "bad tape";
code22:
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "GCR write error";
break;
case 02:
sc->sc_fmesg = "GCR read error";
break;
case 03:
sc->sc_fmesg = "PE read error";
break;
case 04:
sc->sc_fmesg = "PE write error";
break;
case 05:
sc->sc_fmesg = "at least 1 bit set in ECCSTA";
break;
case 06:
sc->sc_fmesg = "PE write error";
break;
case 07:
sc->sc_fmesg = "GCR write error";
break;
case 010:
sc->sc_fmesg = "RSTAT contains bad code";
break;
case 011:
sc->sc_fmesg = "PE write error";
break;
case 012:
sc->sc_fmesg = "MASSBUS parity error";
break;
case 013:
sc->sc_fmesg = "invalid data transferred";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* TM fault A */
case MTER_TMFLTA:
sc->sc_mesg = "TM fault A";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "illegal command code";
break;
case 02:
sc->sc_fmesg = "DT command issued when NDT command active";
break;
case 03:
sc->sc_fmesg = "WMC error";
break;
case 04:
sc->sc_fmesg = "RUN not received from MASSBUS controller";
break;
case 05:
sc->sc_fmesg = "mismatch in command read - function routine";
break;
case 06:
sc->sc_fmesg = "ECC ROM parity error";
break;
case 07:
sc->sc_fmesg = "XMC ROM parity error";
break;
case 010:
sc->sc_fmesg = "mismatch in command read - ID burst command";
break;
case 011:
sc->sc_fmesg = "mismatch in command read - verify ARA burst command";
break;
case 012:
sc->sc_fmesg = "mismatch in command read - verify ARA ID command";
break;
case 013:
sc->sc_fmesg = "mismatch in command read - verify gap command";
break;
case 014:
sc->sc_fmesg = "mismatch in command read - read id burst command";
break;
case 015:
sc->sc_fmesg = "mismatch in command read - verify ARA ID command";
break;
case 016:
sc->sc_fmesg = "mismatch in command read - verify gap command";
break;
case 017:
sc->sc_fmesg = "mismatch in command read - find gap command";
break;
case 020:
sc->sc_fmesg = "WMC LEFT failed to set";
break;
case 021:
sc->sc_fmesg = "XL PE set in INTSTA register";
break;
case 022:
sc->sc_fmesg = "XMC DONE did not set";
break;
case 023:
sc->sc_fmesg = "WMC ROM PE or RD PE set in WMCERR register";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* TU fault A */
case MTER_TUFLTA:
sc->sc_mesg = "TU fault A";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "TU status parity error";
break;
case 02:
sc->sc_fmesg = "TU command parity error";
break;
case 03:
sc->sc_fmesg = "rewinding tape went offline";
break;
case 04:
sc->sc_fmesg = "tape went not ready during DSE";
break;
case 05:
sc->sc_fmesg = "TU CMD status changed during DSE";
break;
case 06:
sc->sc_fmesg = "TU never came up to speed";
break;
case 07:
sc->sc_fmesg = "TU velocity changed";
break;
case 010:
sc->sc_fmesg = "TU CMD did not load correctly to start tape motion";
break;
case 011:
sc->sc_fmesg = "TU CMD did not load correctly to set drive density";
break;
case 012:
sc->sc_fmesg = "TU CMD did not load correctly to start tape motion to write BOT ID";
break;
case 013:
sc->sc_fmesg = "TU CMD did not load correctly to backup tape to BOT after failing to write BOT ID";
break;
case 014:
sc->sc_fmesg = "failed to write density ID burst";
break;
case 015:
sc->sc_fmesg = "failed to write ARA burst";
break;
case 016:
sc->sc_fmesg = "failed to write ARA ID";
break;
case 017:
sc->sc_fmesg = "ARA error bit set in MTA status B register";
break;
case 021:
sc->sc_fmesg = "could not find a gap after ID code was written correctly";
break;
case 022:
sc->sc_fmesg = "TU CMD did not load correctly to start tape motion to read ID burst";
break;
case 023:
sc->sc_fmesg = "timeout looking for BOT after detecting ARA ID burst";
break;
case 024:
sc->sc_fmesg = "failed to write tape mark";
break;
case 025:
sc->sc_fmesg = "tape never came up to speed while trying to reposition for retry of writing tape mark";
break;
case 026:
sc->sc_fmesg = "TU CMD did not load correctly to start tape motion in erase gap routine";
break;
case 027:
sc->sc_fmesg = "could not detect a gap in in erase gap routine";
break;
case 030:
sc->sc_fmesg = "could not detect a gap after writing record";
break;
case 031:
sc->sc_fmesg = "read path terminated before entire record was written";
break;
case 032:
sc->sc_fmesg = "could not find a gap after writing record and read path terminated early";
break;
case 033:
sc->sc_fmesg = "TU CMD did not load correctly to backup for retry of write tape mark";
break;
case 034:
sc->sc_fmesg = "TU velocity changed after up to speed while trying to reposition for retry of writing tape mark";
break;
case 035:
sc->sc_fmesg = "TU CMD did not load correctly to backup to retry a load of BOT ID";
break;
case 036:
sc->sc_fmesg = "timeout looking for BOT after failing to write BOT ID";
break;
case 037:
sc->sc_fmesg = "TU velocity changed while writing PE gap before starting to write record";
break;
case 040:
sc->sc_fmesg = "TU CMD did not load correctly to set PE tape density at start of write BOT ID burst";
break;
case 041:
sc->sc_fmesg = "TU CMD did not load correctly to set GCR tape density after writing Density ID";
break;
case 042:
sc->sc_fmesg = "TU CMD did not load correctly to set PE tape density at start of read from BOT";
break;
case 043:
sc->sc_fmesg = "TU CMD did not load correctly to set GCR tape density after reading a GCR Density ID burst";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* TM fault B */
case MTER_TMFLTB:
sc->sc_mesg = "TM fault B";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 00:
sc->sc_fmesg = "RST0 interrupt occurred with TM RDY set";
break;
case 01:
sc->sc_fmesg = "power failed to interrupt";
break;
case 02:
sc->sc_fmesg = "unknown interrupt on channel 5.5";
break;
case 03:
sc->sc_fmesg = "unknown interrupt on channel 6.5";
break;
case 04:
sc->sc_fmesg = "unknown interrupt on channel 7";
break;
case 05:
sc->sc_fmesg = "unknown interrupt on channel 7.5";
break;
case 06:
sc->sc_fmesg = "CAS contention retry count expired";
break;
case 07:
sc->sc_fmesg = "CAS contention error not retryable";
break;
case 010:
sc->sc_fmesg = "queue error, could not find queue entry";
break;
case 011:
sc->sc_fmesg = "queue entry already full";
break;
case 012:
sc->sc_fmesg = "8085 ROM parity error";
break;
case 013:
case 014:
case 015:
case 016:
case 017:
case 020:
case 021:
case 022:
case 023:
case 024:
case 025:
case 026:
case 027:
case 030:
case 031:
case 032:
case 033:
case 034:
case 035:
case 036:
case 037:
case 040:
case 041:
case 042:
case 043:
case 044:
case 045:
case 046:
case 047:
case 050:
case 051:
case 052:
case 053:
case 054:
case 055:
case 056:
case 057:
sc->sc_fmesg = "inline test failed";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* MASSBUS fault */
case MTER_MBFLT:
sc->sc_mesg = "MB fault";
switch ((sc->sc_erreg & MTER_FAILCODE) >> 10) {
case 01:
sc->sc_fmesg = "control bus parity error";
break;
case 02:
sc->sc_fmesg = "illegal register referenced";
break;
default:
sc->sc_fmesg = "unclassified failure code";
}
break;
/* keypad entry error */
case MTER_KEYFAIL:
sc->sc_mesg = "keypad entry error";
sc->sc_fmesg = "";
break;
default:
sc->sc_mesg = "unclassified error";
sc->sc_fmesg = "";
break;
}
}
#endif MTLERRM
#endif