2.11BSD/sys/OTHERS/ht/ht.c
/*
* TJU77/TWU77/TJE16/TWE16 tape driver
*/
/*
* SCCS id @(#)ht.c 2.2 (2.11BSD GTE) 1/2/93
* Eric Haag's Driver
* I received this from Eric when I first got 2.9 up
* (Oct 84) and have recently sent it to Steve Malone
* both Steve & I have Massbus (RH70) tapes
*/
#include "ht.h"
#if NHT > 0
#include "param.h"
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/htreg.h>
#ifdef HT_IOCTL
#include <sys/mtio.h>
#endif
struct buf httab;
struct buf chtbuf;
struct htdevice *HTADDR;
#define INF 32760
struct softc {
char sc_openf;
char sc_lastiow;
daddr_t sc_blkno;
daddr_t sc_nxrec;
u_short sc_erreg;
u_short sc_fsreg;
#ifdef HT_IOCTL
short sc_resid;
#endif
} tu_softc[NHT];
#define SIO 1
#define SSFOR 2
#define SSREV 3
#define SRETRY 4
#define SCOM 5
#define SOK 6
#define TUUNIT(dev) (minor(dev) & 077)
/* bits in minor device */
#define H_800BPI 0100
#define H_NOREWIND 0200
htattach(addr, unit)
register struct htdevice *addr;
{
if (unit >= NHT)
return(0);
if ((addr != (struct htdevice *) NULL) && (fioword(addr) != -1)) {
HTADDR = addr;
if (fioword(&(addr->htbae)) != -1)
httab.b_flags |= B_RH70;
return(1);
}
HTADDR = (struct hpdevice *) NULL;
return(0);
}
htopen(dev, flag)
dev_t dev;
{
register ds;
register htunit = TUUNIT(dev);
register struct tu_softc *sc = &tu_softc[htunit];
httab.b_flags |= B_TAPE;
if (HTADDR == (struct htdevice *) NULL || htunit >= NHT) {
u.u_error = ENXIO;
return;
}
else
if (sc->sc_openf) {
u.u_error = EBUSY;
return;
}
sc->sc_blkno = (daddr_t) 0;
sc->sc_nxrec = (daddr_t) INF;
sc->sc_lastiow = 0;
ds = htcommand(dev, HT_SENSE, 1);
if ((ds & HTFS_MOL) == 0) {
uprintf("tu%d: not online\n", htunit);
u.u_error = EIO;
return;
}
if ((flag & FWRITE) && (ds & HTFS_WRL)) {
uprintf("tu%d: no write ring\n", htunit);
u.u_error = EIO;
return;
}
if (u.u_error == 0)
sc->sc_openf++;
}
htclose(dev, flag)
dev_t dev;
{
register struct tu_softc *sc = &tu_softc[TUUNIT(dev)];
if (flag == FWRITE || ((flag & FWRITE) && sc->sc_lastiow)) {
htcommand(dev, HT_WEOF, 1);
htcommand(dev, HT_WEOF, 1);
htcommand(dev, HT_SREV, 1);
}
if ((minor(dev) & H_NOREWIND) == 0)
htcommand(dev, HT_REW, 1);
sc->sc_openf = 0;
}
/*ARGSUSED*/
htcommand(dev, com, count)
unsigned count;
dev_t dev;
{
register s;
register struct buf *bp;
bp = &chtbuf;
s = spl5();
while(bp->b_flags & B_BUSY) {
#ifndef SLU70
/*
* 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;
#endif
bp->b_flags |= B_WANTED;
sleep((caddr_t) bp, PRIBIO);
}
bp->b_flags = B_BUSY | B_READ;
splx(s);
bp->b_dev = dev;
#ifdef HT_IOCTL
if (com == HT_SFORW || com == HT_SREV)
bp->b_repcnt = count;
#endif
bp->b_command = com;
bp->b_blkno = (daddr_t) 0;
htstrategy(bp);
#ifndef SLU70
/*
* In case of rewind from close, don't wait.
* This is the only case where count can be 0.
*/
if (count == 0)
return(0);
#endif
iowait(bp);
if(bp->b_flags & B_WANTED)
wakeup((caddr_t)bp);
bp->b_flags &= B_ERROR;
return (bp->b_resid);
}
htstrategy(bp)
register struct buf *bp;
{
int s;
register daddr_t *p;
register struct tu_softc *sc = &tu_softc[TUUNIT(bp->b_dev)];
/* This is almost certainly not in the right place and more work needs
* to be done in htstart(). See /sys/pdpuba/ht.c
*/
if (bp->b_flags & B_PHYS) {
sc->sc_blkno = sc->sc_nxrec = dbtofsb(bp->b_blkno);
sc->sc_nxrec++;
}
if(bp != &chtbuf) {
if ((httab.b_flags & B_RH70) == 0)
mapalloc(bp);
p = &sc->sc_nxrec;
if(dbtofsb(bp->b_blkno) > *p) {
bp->b_flags |= B_ERROR;
bp->b_error = ENXIO;
iodone(bp);
return;
}
if(dbtofsb(bp->b_blkno) == *p && bp->b_flags & B_READ) {
bp->b_resid = bp->b_bcount;
iodone(bp);
return;
}
if ((bp->b_flags & B_READ) == 0) {
*p = dbtofsb(bp->b_blkno) + 1;
sc->sc_lastiow = 1;
}
}
bp->av_forw = NULL;
s = spl5();
if (httab.b_actf == NULL)
httab.b_actf = bp;
else
httab.b_actl->av_forw = bp;
httab.b_actl = bp;
if (httab.b_active == 0)
htstart();
splx(s);
}
htstart()
{
register struct buf *bp;
register den;
int htunit;
daddr_t blkno;
register struct softc *sc;
loop:
if ((bp = httab.b_actf) == NULL)
return;
htunit = minor(bp->b_dev) & 0177;
sc = &tu_softc[TUUNIT(bp->b_dev)];
sc->sc_erreg = HTADDR->hter;
sc->sc_fsreg = HTADDR->htfs;
#ifdef HT_IOCTL
sc->sc_resid = HTADDR->htfc;
#endif
HTADDR->htcs2 = ((htunit >> 3) & 07);
den = HTTC_1600BPI | HTTC_PDP11 | (htunit & 07);
if (htunit & H_800BPI)
den = HTTC_800BPI | HTTC_PDP11 | (htunit & 07);
if ((HTADDR->httc & 03777) != den)
HTADDR->httc = den;
if (HTADDR->htcs2 & HTCS2_NEF || (HTADDR->htfs & HTFS_MOL) == 0)
goto abort;
htunit &= 077;
blkno = sc->sc_blkno;
if (bp == &chtbuf) {
if (bp->b_command == HT_SENSE) {
bp->b_resid = HTADDR->htfs;
goto next;
}
httab.b_active = SCOM;
#ifdef HT_IOCTL
HTADDR->htfc = -bp->b_bcount;
#else
HTADDR->htfc = 0;
#endif
HTADDR->htcs1 = bp->b_command | HT_IE | HT_GO;
return;
}
if (sc->sc_openf < 0 || dbtofsb(bp->b_blkno) > sc->sc_nxrec)
goto abort;
if (blkno == dbtofsb(bp->b_blkno)) {
httab.b_active = SIO;
HTADDR->htba = bp->b_un.b_addr;
if(httab.b_flags & B_RH70)
HTADDR->htbae = bp->b_xmem;
HTADDR->htfc = -bp->b_bcount;
HTADDR->htwc = -(bp->b_bcount >> 1);
den = ((bp->b_xmem & 3) << 8) | HT_IE | HT_GO;
if(bp->b_flags & B_READ)
den |= HT_RCOM;
else {
if(HTADDR->htfs & HTFS_EOT) {
bp->b_resid = bp->b_bcount;
goto next;
}
den |= HT_WCOM;
}
HTADDR->htcs1 = den;
} else {
if (blkno < dbtofsb(bp->b_blkno)) {
httab.b_active = SSFOR;
HTADDR->htfc = blkno - dbtofsb(bp->b_blkno);
HTADDR->htcs1 = HT_SFORW | HT_IE | HT_GO;
} else {
httab.b_active = SSREV;
HTADDR->htfc = dbtofsb(bp->b_blkno) - blkno;
HTADDR->htcs1 = HT_SREV | HT_IE | HT_GO;
}
}
return;
abort:
bp->b_flags |= B_ERROR;
next:
httab.b_actf = bp->av_forw;
iodone(bp);
goto loop;
}
htintr()
{
register struct buf *bp;
register state;
int err, htunit;
register struct softc *sc;
if ((bp = httab.b_actf) == NULL)
return;
htunit = TUUNIT(bp->b_dev);
state = httab.b_active;
httab.b_active = 0;
sc = &tu_softc[htunit];
sc->sc_erreg = HTADDR->hter;
sc->sc_fsreg = HTADDR->htfs;
#ifdef HT_IOCTL
sc->sc_resid = HTADDR->htfc;
#endif
if (HTADDR->htcs1 & HT_TRE) {
err = HTADDR->hter;
if (HTADDR->htcs2 & HTCS2_ERR || (err & HTER_HARD))
state = 0;
if (bp->b_flags & B_PHYS)
err &= ~HTER_FCE;
if ((bp->b_flags & B_READ) && (HTADDR->htfs & HTFS_PES))
err &= ~(HTER_CSITM | HTER_CORCRC);
if ((HTADDR->htfs & HTFS_MOL) == 0) {
if(sc->sc_openf)
sc->sc_openf = -1;
}
else
if (HTADDR->htfs & HTFS_TM) {
HTADDR->htwc = -(bp->b_bcount >> 1);
sc->sc_nxrec = dbtofsb(bp->b_blkno);
state = SOK;
}
else
if (state && err == 0)
state = SOK;
if (httab.b_errcnt > 6)
#ifdef UCB_DEVERR
printf("tu%d: hard error bn %D er=%b ds=%b\n",
htunit, bp->b_blkno,
sc->sc_erreg, HTER_BITS,
sc->sc_fsreg, HTFS_BITS);
#else
deverror(bp, sc->sc_erreg, sc->sc_fsreg);
#endif
htinit();
if (state == SIO && ++httab.b_errcnt < 10) {
httab.b_active = SRETRY;
sc->sc_blkno++;
HTADDR->htfc = -1;
HTADDR->htcs1 = HT_SREV | HT_IE | HT_GO;
return;
}
if (state != SOK) {
bp->b_flags |= B_ERROR;
state = SIO;
}
} else
if (HTADDR->htcs1 & HT_SC)
if(HTADDR->htfs & HTFS_ERR)
htinit();
switch (state) {
case SIO:
case SOK:
sc->sc_blkno++;
case SCOM:
httab.b_errcnt = 0;
httab.b_actf = bp->av_forw;
iodone(bp);
bp->b_resid = -(HTADDR->htwc << 1);
break;
case SRETRY:
if((bp->b_flags & B_READ) == 0) {
httab.b_active = SSFOR;
HTADDR->htcs1 = HT_ERASE | HT_IE | HT_GO;
return;
}
case SSFOR:
case SSREV:
if(HTADDR->htfs & HTFS_TM) {
if(state == SSREV) {
sc->sc_nxrec = dbtofsb(bp->b_blkno) - HTADDR->htfc;
sc->sc_blkno = sc->sc_nxrec;
} else
{
sc->sc_nxrec = dbtofsb(bp->b_blkno) + HTADDR->htfc - 1;
sc->sc_blkno = sc->sc_nxrec + 1;
}
} else
sc->sc_blkno = dbtofsb(bp->b_blkno);
break;
default:
return;
}
htstart();
}
htinit()
{
register ocs2;
register omttc;
omttc = HTADDR->httc & 03777; /* preserve old slave select, dens, format */
ocs2 = HTADDR->htcs2 & 07; /* preserve old unit */
HTADDR->htcs2 = HTCS2_CLR;
HTADDR->htcs2 = ocs2;
HTADDR->httc = omttc;
HTADDR->htcs1 = HT_DCLR | HT_GO;
}
#ifdef HT_IOCTL
/*ARGSUSED*/
htioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
register struct buf *bp = &chtbuf;
register struct softc *sc = &tu_softc[TUUNIT(dev)];
register callcount;
int fcount;
struct mtop mtop;
struct mtget mtget;
/* we depend on the values and order of the MT codes here */
static htops[] = {HT_WEOF, HT_SFORW, HT_SREV, HT_SFORW,
HT_SREV, HT_REW, HT_REWOFFL, HT_SENSE};
switch (cmd) {
case MTIOCTOP:
if (copyin(addr, (caddr_t) &mtop, sizeof(mtop))) {
u.u_error = EFAULT;
return;
}
switch(mtop.mt_op) {
case MTWEOF:
callcount = mtop.mt_count;
fcount = 1;
break;
case MTFSF:
case MTBSF:
callcount = mtop.mt_count;
fcount = INF;
break;
case MTFSR:
case MTBSR:
callcount = 1;
fcount = mtop.mt_count;
break;
case MTREW:
case MTOFFL:
case MTNOP:
callcount = 1;
fcount = 1;
break;
default:
u.u_error = ENXIO;
return;
}
if (callcount <= 0 || fcount <= 0) {
u.u_error = ENXIO;
return;
}
while (--callcount >= 0) {
htcommand(dev, htops[mtop.mt_op], fcount);
if ((mtop.mt_op == MTFSR || mtop.mt_op == MTBSR)
&& bp->b_resid) {
u.u_error = EIO;
break;
}
if ((bp->b_flags & B_ERROR)
|| sc->sc_fsreg & HTFS_BOT)
break;
}
geterror(bp);
return;
case MTIOCGET:
mtget.mt_erreg = sc->sc_erreg;
mtget.mt_dsreg = sc->sc_fsreg;
mtget.mt_resid = sc->sc_resid;
mtget.mt_type = MT_ISHT;
if (copyout((caddr_t) &mtget, addr, sizeof(mtget)))
u.u_error = EFAULT;
return;
default:
u.u_error = ENXIO;
}
}
#endif HT_IOCTL
#endif NHT