BSD 4_2 development
[unix-history] / usr / src / sys / vax / tu.c
/* %M 6.2 83/10/12 */
#if defined(VAX750) || defined(VAX730)
/*
* TU58 DECtape II device driver
*
* TU58 console cassette driver (for VAX-11/750 or VAX-11/730).
* The TU58 is treated as a block device (only). Error detection and
* recovery is not extensive, but sufficient for most situations. It is
* assumed that the TU58 will follow the RSP (or MRSP) protocol exactly,
* very few protocol errors are checked for. It is also assumed that
* the 730 uses Modified RSP (MRSP), while the 750 may use either RSP
* or MRSP depending on whether defined(MRSP) is true or not.
* In the case of a 750 without MRSP, the only way for the CPU to
* keep up with the tu58 is to lock out virtually everything else.
*
* NOTE: Reading large amounts of data from the tu58 is likely
* to crash your system if you are running multiuser.
* ******FOR SINGLE USER USE ONLY*****
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/kernel.h"
#include "../vax/cpu.h"
#include "../vax/mtpr.h"
#include "../vax/rsp.h"
#define printd if(tudebug) printf
#ifdef printd
int tudebug; /* printd */
#endif printd
#define NTU ((cpu == VAX_750) ? 1 : 2)
#define DNUM 01 /* mask for drive number (should match NTU) */
#define NTUBLK 512 /* number of blocks on a TU58 cassette */
#define WRV 02 /* bit in minor dev => write w. read verify */
#define NTUQ 2 /* # of blocks which can be queued up */
#define TUIPL ((cpu == VAX_750) ? 0x17 : 0x14)
#ifndef MRSP
#define MRSP (cpu != VAX_750)
#endif
/*
* State information
*/
struct tu {
u_char *tu_rbptr; /* pointer to buffer for read */
int tu_rcnt; /* how much to read */
u_char *tu_wbptr; /* pointer to buffer for write */
int tu_wcnt; /* how much to write */
int tu_state; /* current state of tansfer operation */
int tu_flag; /* read in progress flag */
char *tu_addr; /* real buffer data address */
int tu_count; /* real requested count */
int tu_serrs; /* count of soft errors */
int tu_cerrs; /* count of checksum errors */
int tu_herrs; /* count of hard errors */
char tu_dopen[2]; /* drive is open */
} tu;
/*
* Device register bits
*/
#define READY 0200 /* transmitter ready */
#define DONE 0200 /* receiver done */
#define IE 0100 /* interrupt enable */
#define BREAK 1 /* send break */
struct packet tucmd; /* a command sent to the TU58 */
struct packet tudata; /* a command or data returned from TU58 */
char *tustates[TUS_NSTATES] = {
"INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR",
"SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR"
};
u_char tunull[2] = { 0, 0 }; /* nulls to send for initialization */
u_char tuinit[2] = { TUF_INITF, TUF_INITF }; /* inits to send */
static char tu_pcnt[2]; /* pee/vee counters */
int tutimer = 0;
int tuwake();
struct buf tutab; /* I/O queue header */
/*
* Open the TU58
*/
/*ARGSUSED*/
tuopen(dev, flag)
{
extern int tuwatch();
register s;
#ifdef lint
turintr(); tuwintr();
#endif
if ((minor(dev)&DNUM) >= NTU)
return (ENXIO);
if (tu.tu_dopen[minor(dev)&DNUM])
return (EBUSY);
if (tutimer++ == 0)
timeout(tuwatch, (caddr_t)0, hz);
s = splx(TUIPL);
tu.tu_dopen[minor(dev)&DNUM]++;
/*
* If the cassette's already initialized,
* just enable interrupts and return.
*/
if (tu.tu_state == TUS_IDLE) {
mtpr(CSRS, IE);
goto ok;
}
/*
* Must initialize, reset the cassette
* and wait for things to settle down.
*/
tureset();
sleep((caddr_t)&tu, PZERO+1);
tutab.b_active = NULL;
if (tu.tu_state != TUS_IDLE) {
tu.tu_state = TUS_INIT1;
tu.tu_dopen[minor(dev)&DNUM] = 0;
tu.tu_rcnt = tu.tu_wcnt = 0;
mtpr(CSTS, 0);
mtpr(CSRS, 0);
splx(s);
return (EIO);
}
ok:
splx(s);
return (0);
}
/*
* Close the TU58, but make sure all
* outstanding i/o is complete first..
*/
tuclose(dev, flag)
dev_t dev;
int flag;
{
int s, unit = minor(dev);
struct buf *bp, *last = NULL;
s = splx(TUIPL);
while (tu_pcnt[unit])
sleep(&tu_pcnt[unit], PRIBIO);
/*
* No more writes are pending, scan the
* buffer queue for oustanding reads from
* this unit.
*/
for (bp = tutab.b_actf; bp; bp = bp->b_actf) {
if (bp->b_dev == dev)
last = bp;
}
if (last) {
last->b_flags |= B_CALL;
last->b_iodone = tuwake;
sleep((caddr_t)last, PRIBIO);
}
tu.tu_dopen[unit&DNUM] = 0;
if (!tu.tu_dopen[0] && !tu.tu_dopen[1]) {
tutimer = 0;
mtpr(CSRS, 0);
tu.tu_flag = 0;
}
splx(s);
}
tuwake(bp)
struct buf *bp;
{
wakeup(bp);
}
/*
* Reset the TU58
*/
tureset()
{
mtpr(CSRS, 0);
tu.tu_state = TUS_INIT1;
tu.tu_wbptr = tunull;
tu.tu_wcnt = sizeof (tunull);
tucmd.pk_flag = TUF_CMD;
tucmd.pk_mcount = sizeof (tucmd) - 4;
tucmd.pk_mod = 0;
tucmd.pk_seq = 0;
tucmd.pk_sw = MRSP ? TUSW_MRSP : 0;
tutab.b_active++;
mtpr(CSTS, IE | BREAK);
tuxintr(); /* start output */
}
/*
* Strategy routine for block I/O
*/
tustrategy(bp)
register struct buf *bp;
{
register int s;
if (bp->b_blkno >= NTUBLK) {
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
if ((bp->b_flags&B_READ) == 0)
tu_pee(&tu_pcnt[minor(bp->b_dev)&DNUM]);
bp->av_forw = NULL;
s = splx(TUIPL);
if (tutab.b_actf == NULL)
tutab.b_actf = bp;
else
tutab.b_actl->av_forw = bp;
tutab.b_actl = bp;
if (tutab.b_active == NULL)
tustart();
splx(s);
}
/*
* Start the transfer
*/
tustart()
{
register struct buf *bp;
int s;
if ((bp = tutab.b_actf) == NULL)
return;
s = splx(TUIPL);
if (tu.tu_state != TUS_IDLE) {
tureset();
splx(s);
return;
}
tutab.b_active++;
tutab.b_errcnt = 0;
tucmd.pk_op = bp->b_flags&B_READ ? TUOP_READ : TUOP_WRITE;
tucmd.pk_mod = ((bp->b_flags&B_READ) == 0 && (minor(bp->b_dev)&WRV)) ?
TUMD_WRV : 0;
tucmd.pk_unit = (minor(bp->b_dev)&DNUM);
tucmd.pk_sw = MRSP ? TUSW_MRSP : 0;
tucmd.pk_count = tu.tu_count = bp->b_bcount;
tucmd.pk_block = bp->b_blkno;
tucmd.pk_chksum =
tuchk(*((short *)&tucmd), (u_short *)&tucmd.pk_op,
(int)tucmd.pk_mcount);
tu.tu_state = bp->b_flags&B_READ ? TUS_SENDR : TUS_SENDW;
tu.tu_addr = bp->b_un.b_addr;
tu.tu_wbptr = (u_char *)&tucmd;
tu.tu_wcnt = sizeof (tucmd);
tuxintr();
splx(s);
}
/*
* TU58 receiver interrupt
*/
turintr()
{
register struct buf *bp;
register int c;
c = mfpr(CSRD)&0xff;
if (MRSP) {
while ((mfpr(CSTS)&READY) == 0)
;
mtpr(CSTD, TUF_CONT); /* ACK */
if (tu.tu_rcnt) {
*tu.tu_rbptr++ = c;
if (--tu.tu_rcnt)
return;
}
}
/*
* Switch on the state of the transfer.
*/
switch(tu.tu_state) {
/*
* Probably an overrun error,
* cannot happen if MRSP is used
*/
case TUS_RCVERR:
mtpr(CSRS, 0); /* flush */
printf("overrun error, transfer restarted\n"); /* DEBUG */
tu.tu_serrs++;
tu_restart();
break;
/*
* If we get an unexpected "continue",
* start all over again...
*/
case TUS_INIT2:
tu.tu_state = c == TUF_CONT ? TUS_IDLE : TUS_INIT1;
tu.tu_flag = 0;
wakeup((caddr_t)&tu);
tustart();
break;
/*
* Only transition from this state
* is on a "continue", so if we don't
* get it, reset the world.
*/
case TUS_WAIT: /* waiting for continue */
switch(c) {
case TUF_CONT: /* got the expected continue */
tu.tu_flag = 0;
tudata.pk_flag = TUF_DATA;
tudata.pk_mcount = MIN(128, tu.tu_count);
tudata.pk_chksum =
tuchk(*((short *)&tudata), (caddr_t)tu.tu_addr,
(int)tudata.pk_mcount);
tu.tu_state = TUS_SENDH;
tu.tu_wbptr = (u_char *)&tudata;
tu.tu_wcnt = 2;
tuxintr();
break;
case TUF_CMD: /* sending us an END packet...error */
tu.tu_state = TUS_GET;
tu.tu_rbptr = (u_char *) &tudata;
tu.tu_rcnt = sizeof (tudata) - 1;
tu.tu_flag = 1;
mtpr (CSTS, 0);
*tu.tu_rbptr = c;
break;
case TUF_INITF:
tureset();
break;
default: /* something random...bad news */
tu.tu_state = TUS_INIT1;
break;
}
break;
case TUS_SENDW:
if (c != TUF_CONT && c != TUF_INITF)
goto bad;
tureset();
break;
/*
* Got header, now get data; amount to
* fetch is included in packet.
*/
case TUS_GETH:
if (MRSP && (tudata.pk_flag == TUF_DATA))
tu.tu_rbptr = (u_char *)tu.tu_addr;
tu.tu_rcnt = tudata.pk_mcount;
tu.tu_state = TUS_GETD;
break;
/*
* Got the data, now fetch the checksum.
*/
case TUS_GETD:
tu.tu_rbptr = (u_char *)&tudata.pk_chksum;
tu.tu_rcnt = sizeof (tudata.pk_chksum);
tu.tu_state = TUS_GETC;
break;
case TUS_CHKERR: /* from tudma only */
tu.tu_cerrs++;
goto tus_get;
case TUS_GET:
if (MRSP)
/*
* The checksum has already been calculated and
* verified in the pseudo DMA routine
*/
goto tus_get;
case TUS_GETC:
/* got entire packet */
if (tudata.pk_chksum !=
tuchk(*((short *)&tudata), (u_short *)
(tudata.pk_flag == TUF_DATA ?
(u_short *) tu.tu_addr : (u_short *)&tudata.pk_op),
(int)tudata.pk_mcount))
tu.tu_cerrs++;
tus_get:
if (tudata.pk_flag == TUF_DATA) {
/* data packet, advance to next */
tu.tu_addr += tudata.pk_mcount;
tu.tu_count -= tudata.pk_mcount;
tu.tu_state = TUS_GETH;
tu.tu_rbptr = (u_char *)&tudata; /* next packet */
tu.tu_rcnt = 2;
} else if (tudata.pk_flag==TUF_CMD && tudata.pk_op==TUOP_END) {
/* end packet, idle and reenable transmitter */
tu.tu_state = TUS_IDLE;
tu.tu_flag = 0;
mtpr(CSTS, IE);
printd("ON ");
if ((bp = tutab.b_actf) == NULL) {
printf("tu%d: no bp, active %d\n",
tudata.pk_unit, tutab.b_active);
tustart();
return;
}
if (tudata.pk_mod > 1) { /* hard error */
bp->b_flags |= B_ERROR;
tu.tu_herrs++;
printf("tu%d: hard error bn%d,",
minor(bp->b_dev)&DNUM, bp->b_blkno);
printf(" pk_mod %o\n", tudata.pk_mod&0377);
} else if (tudata.pk_mod != 0) /* soft error */
tu.tu_serrs++;
tutab.b_active = NULL;
tutab.b_actf = bp->av_forw;
bp->b_resid = tu.tu_count;
if ((bp->b_flags&B_READ) == 0)
tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]);
iodone(bp);
tustart();
} else {
/*
* Neither data nor end: data was lost
* somehow, restart the transfer
*/
mtpr(CSRS, 0); /* flush the rest */
tu_restart();
tu.tu_serrs++;
}
break;
case TUS_IDLE:
case TUS_INIT1:
break;
default:
bad:
if (c == TUF_INITF) {
printf("tu%d protocol error, state=",
(int)tudata.pk_unit);
printstate(tu.tu_state);
printf(", op=%x, cnt=%d, block=%d\n",
tucmd.pk_op, tucmd.pk_count, tucmd.pk_block);
tutab.b_active = NULL;
if (bp = tutab.b_actf) {
bp->b_flags |= B_ERROR;
tutab.b_actf = bp->av_forw;
if ((bp->b_flags&B_READ) == 0)
tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]);
iodone(bp);
}
tu.tu_state = TUS_INIT1;
} else {
printf("tu%d: receive state error, state=",
(int)tudata.pk_unit);
printstate(tu.tu_state);
printf(", byte=%x\n", c & 0xff);
if (tutab.b_actf)
tu_restart();
else
wakeup((caddr_t)&tu);
}
}
}
/*
* TU58 transmitter interrupt
*/
tuxintr()
{
top:
if (tu.tu_wcnt) {
/* still stuff to send, send one byte */
while ((mfpr(CSTS) & READY) == 0)
;
mtpr(CSTD, *tu.tu_wbptr++);
tu.tu_wcnt--;
return;
}
/*
* Last message byte was sent out.
* Switch on state of transfer.
*/
if (tudebug) {
printf("tuxintr: state=");
printstate(tu.tu_state);
}
switch(tu.tu_state) {
/*
* Two nulls have been sent, remove break, and send inits
*/
case TUS_INIT1:
mtpr(CSTS, IE);
printd("ON2 ");
tu.tu_state = TUS_INIT2;
tu.tu_wbptr = tuinit;
tu.tu_wcnt = sizeof (tuinit);
goto top;
/*
* Inits have been sent, wait for a continue msg.
*/
case TUS_INIT2:
(void) mfpr(CSRD);
mtpr(CSRS, IE);
tu.tu_flag = 1;
break;
/*
* Read cmd packet sent, get ready for data
*/
case TUS_SENDR:
tu.tu_state = TUS_GETH;
tu.tu_rbptr = (u_char *)&tudata;
tu.tu_rcnt = 2;
tu.tu_flag = 1;
mtpr(CSTS, 0); /* disable transmitter interrupts */
printd("OFF ");
break;
/*
* Write cmd packet sent, wait for continue
*/
case TUS_SENDW:
tu.tu_state = TUS_WAIT;
tu.tu_flag = 1;
if ((mfpr(CSRS)&IE) == 0) {
printf("NO IE\n");
mtpr(CSRS, IE);
}
break;
/*
* Header sent, send data.
*/
case TUS_SENDH:
tu.tu_state = TUS_SENDD;
tu.tu_wbptr = (u_char *)tu.tu_addr;
tu.tu_wcnt = tudata.pk_mcount;
goto top;
/*
* Data sent, follow with checksum.
*/
case TUS_SENDD:
tu.tu_state = TUS_SENDC;
tu.tu_wbptr = (u_char *)&tudata.pk_chksum;
tu.tu_wcnt = sizeof tudata.pk_chksum;
goto top;
/*
* Checksum sent, wait for continue.
*/
case TUS_SENDC:
/*
* Updata buffer address and count.
*/
tu.tu_addr += tudata.pk_mcount;
tu.tu_count -= tudata.pk_mcount;
if (tu.tu_count) {
tu.tu_state = TUS_WAIT;
tu.tu_flag = 1;
break;
}
/*
* End of transmission, get ready for end packet.
*/
tu.tu_state = TUS_GET;
tu.tu_rbptr = (u_char *)&tudata;
tu.tu_rcnt = sizeof (tudata);
tu.tu_flag = 1;
mtpr(CSTS, 0);
printd("OFF2 ");
break;
/*
* Random interrupt, probably from MRSP ACK
*/
case TUS_IDLE:
default:
break;
}
if (tudebug) {
printd(" new tu_state=");
printstate(tu.tu_state);
}
}
/*
* Compute checksum TU58 fashion
*/
#ifdef lint
tuchk(word, cp, n)
register word;
register unsigned short *cp;
int n;
{
register int c = n >> 1;
register long temp;
do {
temp = *cp++; /* temp, only because vax cc won't *r++ */
word += temp;
} while (--c > 0);
if (n & 1)
word += *(unsigned char *)cp;
while (word & 0xffff0000)
word = (word & 0xffff) + ((word >> 16) & 0xffff);
return (word);
}
#else
tuchk(word0, wp, n)
register int word0; /* r11 */
register char *wp; /* r10 */
register int n; /* r9 */
{
asm("loop:");
asm(" addw2 (r10)+,r11"); /* add a word to sum */
asm(" adwc $0,r11"); /* add in carry, end-around */
asm(" acbl $2,$-2,r9,loop"); /* done yet? */
asm(" blbc r9,ok"); /* odd byte count? */
asm(" movzbw (r10),r10"); /* yes, get last byte */
asm(" addw2 r10,r11"); /* add it in */
asm(" adwc $0,r11"); /* and the carry */
asm("ok:");
asm(" movl r11,r0"); /* return sum */
}
#endif
tuwatch()
{
register int s;
register struct buf *bp;
if (tutimer == 0)
return;
if (tu.tu_flag == 0) { /* if no read in progress - skip */
timeout(tuwatch, (caddr_t)0, hz);
return;
}
if (tu.tu_flag++ <= 40) {
timeout(tuwatch, (caddr_t)0, hz);
return;
}
printf("tu%d: read stalled\n", tudata.pk_unit);
#ifdef TUDEBUG
printf("%X %X %X %X %X %X %X %X\n", tu.tu_rbptr, tu.tu_rcnt,
tu.tu_wbptr, tu.tu_wcnt, tu.tu_state, tu.tu_flag,
tu.tu_addr, tu.tu_count);
#endif
s = splx(TUIPL);
tu.tu_flag = 0;
(void) mfpr(CSRD);
mtpr(CSRS, IE); /* in case we were flushing */
mtpr(CSTS, IE);
tu.tu_state = TUS_IDLE;
if (!tutab.b_active) {
wakeup((caddr_t)&tu);
goto retry;
}
if (++tutab.b_errcnt <= 1) {
tustart();
goto retry;
}
if (bp = tutab.b_actf) {
bp->b_flags |= B_ERROR;
if ((bp->b_flags&B_READ) == 0)
tu_vee(&tu_pcnt[minor(bp->b_dev)&DNUM]);
iodone(bp);
}
retry:
splx(s);
timeout(tuwatch, (caddr_t)0, hz);
}
tu_pee(cp)
char *cp;
{
register int s;
s = splx(TUIPL);
if (++(*cp) > NTUQ)
sleep(cp, PRIBIO);
splx(s);
}
tu_vee(cp)
char *cp;
{
register int s;
s = splx(TUIPL);
if (--(*cp) <= NTUQ)
wakeup(cp);
splx(s);
}
tu_restart()
{
tureset();
timeout(tustart, (caddr_t)0, hz * 3);
}
#endif