V8/usr/sys/dev/tu.c

Compare this file to the similar file:
Show the results in this format:

/*	tu.c	81/11/11	4.1	*/

#if defined(VAX750) || defined(VAX7ZZ)
/*
 * TU58 DECtape II driver
 *
 * This driver controls the console TU58s on a VAX-11/750 or VAX-11/7ZZ.
 * It could be easily modified for a Unibus TU58.  The TU58
 * is treated as a block device (only).  Error detection and
 * recovery is almost non-existant.  It is assumed that the
 * TU58 will follow the RSP protocol exactly, very few protocol
 * errors are checked for.  It is assumed that the 750 uses standard
 * RSP while the 7ZZ uses Modified RSP (MRSP).  At the time when 750's
 * are converted to MRSP (by replacing EPROMS in the TU58), the tests
 * based on MRSP can be removed.
 */
#define	NTU	((cpu == VAX_750) ? 1 : 2)

#define	MRSP	(cpu != VAX_750)

#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/mtpr.h"
#include "../h/cpu.h"

#define	printd	if(tudebug) printf
#ifdef	printd
int	tudebug;	/* printd */
#endif	printd

#define	NTUBLK	512		/* number of blocks on a TU58 cassette */

#define	TUIPL	((cpu == VAX_750) ? 0x17 : 0x14)

/*
 * Device register bits
 */
#define	READY	0200		/* transmitter ready */
#define	DONE	0200		/* receiver done */
#define	IE	0100		/* interrupt enable */
#define	BREAK	1		/* send break */

/*
 * Structure of a command packet
 */

struct packet {
	u_char	pk_flag;	/* indicates packet type (cmd, data, etc.) */
	u_char	pk_mcount;	/* length of packet (bytes) */
	u_char	pk_op;		/* operation to perform (read, write, etc.) */
	u_char	pk_mod;		/* modifier for op or returned status */
	u_char	pk_unit;	/* unit number */
	u_char	pk_sw;		/* switches */
	u_short	pk_seq;		/* sequence number, always zero */
	u_short	pk_count;	/* requested byte count for read or write */
	u_short	pk_block;	/* block number for read, write, or seek */
	u_short	pk_chksum;	/* checksum, by words with end around carry */
};

struct packet tucmd;		/* a command sent to the TU58 */
struct packet tudata;		/* a command or data returned from TU58 */

/*
 * State information
 */

struct tu {
	u_char	*rbptr;		/* pointer to buffer for read */
	int	rcnt;		/* how much to read */
	u_char	*wbptr;		/* pointer to buffer for write */
	int	wcnt;		/* how much to write */
	int	state;		/* current state of tansfer operation */
	int	flag;		/* read in progress flag */
	char	*addr;		/* real buffer data address */
	int	count;		/* real requested count */
	int	serrs;		/* count of soft errors */
	int	cerrs;		/* count of checksum errors */
	int	herrs;		/* count of hard errors */
} tu;

/*
 * States
 */
#define	INIT1	0		/* sending nulls */
#define	INIT2	1		/* sending inits */
#define	IDLE	2		/* initialized, no transfer in progress */
#define	SENDH	3		/* sending header */
#define	SENDD	4		/* sending data */
#define	SENDC	5		/* sending checksum */
#define	SENDR	6		/* sending read command packet */
#define	SENDW	7		/* sending write command packet */
#define	GETH	8		/* reading header */
#define	GETD	9		/* reading data */
#define	GETC	10		/* reading checksum */
#define	GET	11		/* reading an entire packet */
#define	WAIT	12		/* waiting for continue */

/*
 * Packet Flags
 */
#define	TUF_DATA	1	/* data packet */
#define	TUF_CMD		2	/* command packet */
#define	TUF_INITF	4	/* initialize */
#define	TUF_CONT	020	/* continue */
#define	TUF_XOFF	023	/* flow control */

/*
 * Op Codes
 */
#define	TUOP_INIT	1	/* initialize */
#define	TUOP_READ	2	/* read block */
#define	TUOP_WRITE	3	/* write block */
#define	TUOP_SEEK	5	/* seek to block */
#define TUOP_DIAGNOSE	7	/* run micro-diagnostics */
#define	TUOP_END	0100	/* end packet */

/*
 * Switches
 */
#define	TUSW_MRSP	010	/* use Modified RSP */

u_char	tunull[2] = { 0, 0 };	/* nulls to send for initialization */
u_char	tuinit[2] = { TUF_INITF, TUF_INITF };	/* inits to send */

int	tutimer = 0;

struct buf tutab;		/* I/O queue header */

/*
 * Open the TU58
 */

tuopen(dev, flag)
{
	extern int tuwatch();

	if (minor(dev) >= NTU) {
		u.u_error = ENXIO;
		return;
	}
	if (tutimer == 0) {
		tutimer++;
		timeout(tuwatch, (caddr_t)0, hz);
	}
	splx(TUIPL);
	if (tu.state != IDLE) {
		tureset();
		sleep((caddr_t)&tu, PZERO);
		tutab.b_active = NULL;
		if (tu.state != IDLE) {	/* couldn't initialize */
			u.u_error = ENXIO;
			tu.state = INIT1;
			tu.rcnt = tu.wcnt = 0;
			mtpr(CSTS, 0);
			mtpr(CSRS, 0);
		}
	} else
		mtpr(CSRS, IE);
	spl0();
}

/*
 * Close the TU58
 */

tuclose(dev)
{
	if (tutab.b_active == 0) {
		mtpr(CSRS, 0);
		tutimer = 0;
	}
	if (tu.serrs + tu.cerrs + tu.herrs != 0) {	/* any errors ? */
		uprintf("tu%d: %d soft errors, %d chksum errors, %d hard errors\n",
			minor(dev), tu.serrs, tu.cerrs, tu.herrs);
		tu.serrs = tu.cerrs = tu.herrs = 0;
	}
}

/*
 * Reset the TU58
 */

tureset()
{
	tu.state = INIT1;
	tu.wbptr = tunull;
	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(CSRS, 0);
	mtpr(CSTS, IE|BREAK);
	tuxintr();		/* start output */
	return;
}

/*
 * Strategy routine for block I/O
 */

tustrategy(bp)
register struct buf *bp;
{
	if (bp->b_blkno >= NTUBLK) {	/* block number out of range? */
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	bp->av_forw = NULL;
	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();
	spl0();
}

/*
 * Start the transfer
 */

tustart()
{
	register struct buf *bp;

    top:
	if ((bp = tutab.b_actf) == NULL)
		return;
	if (tu.state != IDLE) {
		tureset();
		return;
	}
	tutab.b_active++;
	tutab.b_errcnt = 0;
	tucmd.pk_op = bp->b_flags&B_READ ? TUOP_READ : TUOP_WRITE;
	tucmd.pk_unit = minor(bp->b_dev);
	tucmd.pk_count = tu.count = bp->b_bcount;
	tucmd.pk_block = bp->b_blkno;
	tucmd.pk_chksum = tuchk(*((short *)&tucmd), &tucmd.pk_op,
			tucmd.pk_mcount);
	tu.state = bp->b_flags&B_READ ? SENDR : SENDW;
	tu.addr = bp->b_un.b_addr;
	tu.count = bp->b_bcount;
	tu.wbptr = (u_char *)&tucmd;
	tu.wcnt = sizeof tucmd;
	tuxintr();
}

/*
 * TU58 receiver interrupt
 */

turintr()
{
	register struct buf *bp;
	register int c;

	c = mfpr(CSRD)&0xff;		/* get the char, clear the interrupt */
	if (MRSP) {
		while ((mfpr(CSTS)&READY) == 0)
			;
		mtpr(CSTD, TUF_CONT);	/* ACK */
	}
	if (tu.rcnt) {		/* still waiting for data? */
		*tu.rbptr++ = c;	/* yup, put it there */
		if (--tu.rcnt)	/* decrement count, any left? */
			return;	/* get some more */
	}

	/*
	 * We got all the data we were expecting for now,
	 * switch on the state of the transfer.
	 */

	switch(tu.state) {
	case INIT2:
		if (c == TUF_CONT)	/* did we get the expected continue? */
			tu.state = IDLE;
		else
			tu.state = INIT1;	/* bad news... */
		tu.flag = 0;
		wakeup((caddr_t)&tu);
		tustart();
		break;

	case WAIT:			/* waiting for continue */
		if (c != TUF_CONT) {
			tu.state = INIT1;	/* bad news... */
			break;
		}
		tu.flag = 0;
		tudata.pk_flag = TUF_DATA;
		tudata.pk_mcount = min(128, tu.count);
		tudata.pk_chksum = tuchk(*((short *)&tudata), tu.addr,
					tudata.pk_mcount);
		tu.state = SENDH;
		tu.wbptr = (u_char *)&tudata;
		tu.wcnt = 2;
		tuxintr();
		break;

	case GETH:		/* got header, get data */
		if (tudata.pk_flag == TUF_DATA)	/* is it a data message? */
			tu.rbptr = (u_char *)tu.addr;	/* yes, put it in buffer */
		tu.rcnt = tudata.pk_mcount;	/* amount to get */
		tu.state = GETD;
		break;

	case GETD:		/* got data, get checksum */
		tu.rbptr = (u_char *)&tudata.pk_chksum;
		tu.rcnt = sizeof tudata.pk_chksum;
		tu.state = GETC;
		break;

	case GET:
	case GETC:		/* got entire packet */
#ifdef notdef
		if (tuchk(*((short *)&tudata), tudata.pk_flag == TUF_DATA ? tu.addr 
		    : &tudata.pk_op, tudata.pk_mcount) != tudata.pk_chksum)
			tu.cerrs++;
#endif
		if (tudata.pk_flag == TUF_DATA) {	/* was it a data packet? */
			tu.addr += tudata.pk_mcount;	/* update buf addr */
			tu.count -= tudata.pk_mcount;	/* and byte count */
			tu.state = GETH;
			tu.rbptr = (u_char *)&tudata;	/* next packet */
			tu.rcnt = 2;
		} else				/* was it an end packet? */
		if (tudata.pk_flag == TUF_CMD && tudata.pk_op == TUOP_END) {
			tu.state = IDLE;		/* all done reading */
			tu.flag = 0;
			mtpr(CSTS, IE);		/* reenable transmitter */
			printd("ON ");
			if ((bp = tutab.b_actf) == NULL) {
				printf("tu: no bp!\n");
				printf("active %d\n", tutab.b_active);
				tustart();
				return;
			}
			if (tudata.pk_mod < 0) {	/* hard error */
				bp->b_flags |= B_ERROR;
				tu.herrs++;
				harderr(bp, "tu");
				printf("  pk_mod %d\n", -tudata.pk_mod);
			} else if (tudata.pk_mod > 0)	/* soft error */
				tu.serrs++;
			tutab.b_active = NULL;
			tutab.b_actf = bp->av_forw;
			bp->b_resid = tu.count;
			iodone(bp);
			tustart();
		} else {
			printf("neither data nor end: %o %o\n",
				tudata.pk_flag&0xff, tudata.pk_op&0xff);
			mtpr(CSRS, 0);		/* flush the rest */
			tu.state = INIT1;
		}
		break;

	case IDLE:
	case INIT1:
		break;

	default:		/* what are we doing here??? */
		if (c == TUF_INITF) {
			printf("TU protocol error, state %d\n", tu.state);
			printf("%o %d %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;
				iodone(bp);
			}
			tu.state = INIT1;
		} else {
			printf("TU receive state error %d %o\n", tu.state, c);
		/*	tu.state = INIT1; */
			wakeup((caddr_t)&tu);
		}
	}
}

/*
 * TU58 transmitter interrupt
 */

tuxintr()
{
    top:
	if (tu.wcnt) {			/* still stuff to send out? */
		while ((mfpr(CSTS) & READY) == 0)
			;
		mtpr(CSTD, *tu.wbptr++);	/* yup, send another byte */
		tu.wcnt--;		/* decrement count */
		return;
	}

	/*
	 * Last message byte was sent out.
	 * Switch on state of transfer.
	 */

	printd("tuxintr: state %d\n", tu.state);
	switch(tu.state) {
	case INIT1:		/* two nulls sent, remove break, send inits */
		mtpr(CSTS, IE);
		printd("ON2 ");
		tu.state = INIT2;
		tu.wbptr = tuinit;
		tu.wcnt = sizeof tuinit;
		goto top;

	case INIT2:		/* inits sent, wait for continue */
		mfpr(CSRD);
		mtpr(CSRS, IE);
		tu.flag = 1;
		break;

	case IDLE:		/* stray interrupt? */
		break;

	case SENDR:		/* read cmd packet sent, get ready for data */
		tu.state = GETH;
		tu.rbptr = (u_char *)&tudata;
		tu.rcnt = 2;
		tu.flag = 1;
		mtpr(CSTS, 0);	/* disable transmitter interrupts */
		printd("OFF ");
		break;

	case SENDW:		/* write cmd packet sent, wait for continue */
		tu.state = WAIT;
		tu.flag = 1;
		if ((mfpr(CSRS)&IE) == 0) {
			printf("NO IE\n");
			mtpr(CSRS, IE);
		}
		break;

	case SENDH:		/* header sent, send data */
		tu.state = SENDD;
		tu.wbptr = (u_char *)tu.addr;
		tu.wcnt = tudata.pk_mcount;
		goto top;

	case SENDD:		/* data sent, send checksum */
		tu.state = SENDC;
		tu.wbptr = (u_char *)&tudata.pk_chksum;
		tu.wcnt = sizeof tudata.pk_chksum;
		goto top;

	case SENDC:		/* checksum sent, wait for continue */
		tu.addr += tudata.pk_mcount;	/* update buffer address */
		tu.count -= tudata.pk_mcount;	/* and count */
		if (tu.count == 0) {		/* all done? */
			tu.state = GET;		/* set up to get end packet */
			tu.rbptr = (u_char *)&tudata;
			tu.rcnt = sizeof tudata;
			tu.flag = 1;
			mtpr(CSTS, 0);
			printd("OFF2 ");
		} else {
			tu.state = WAIT;	/* wait for continue */
			tu.flag = 1;
		}
		break;

	default:	/* random interrupt, probably from MRSP ACK */
		break;
	}
	printd("  new state %d\n", tu.state);
}

/*
 * Compute checksum TU58 fashion
 *
 * *** WARNING ***
 * This procedure is not in C because
 * it has to be fast and it is hard to
 * do add-carry in C.  Sorry.
 */

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 */
}

tuwatch()
{
	register int s;
	register struct buf *bp;

	if (tutimer == 0) {
		tu.flag = 0;
		return;
	}
	if (tu.flag)
		tu.flag++;
	if (tu.flag > 40) {
		printf("tu: read stalled\n");
		printf("%X %X %X %X %X %X %X %X\n", tu.rbptr, tu.rcnt,
		tu.wbptr, tu.wcnt, tu.state, tu.flag, tu.addr, tu.count);
		tu.flag = 0;
		s = splx(TUIPL);
		mfpr(CSRD);
		mtpr(CSRS, IE);		/* in case we were flushing */
		mtpr(CSTS, IE);
		tu.state = IDLE;
		if (tutab.b_active) {
			if (++tutab.b_errcnt > 1) {
				if (bp = tutab.b_actf) {
					bp->b_flags |= B_ERROR;
					iodone(bp);
				}
			} else
				tustart();
		} else
			wakeup((caddr_t)&tu);
		splx(s);
	}
	timeout(tuwatch, (caddr_t)0, hz);
}
#endif