BBN-V6/dmr/ht.c

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

#
/*
 * TU16 driver by Larry Wehr  PY 1A111 x7982
 * Handles one TM02 controller, up to 4 TU16 transports
 * minor device classes:
 * bits 0,1: slave select
 * bit 2 off: rewind on close; on: position after first TM
 * bit 3 off: 800 bpi; on: 1600 bpi
 *
 * Modified by Rob Mathews (Stanford Univ. (415)497-1703 ) to
 * elaborate the error handling somewhat, to make cooked I/O
 * a little more intelligent about TM's, and to allow raw I/O
 * to read TM's as zero-length records.
 *
 */

#include "../h/param.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/user.h"

#define NUNIT 4

struct {
	int	htcs1, htwc, htba, htfc;
	int	htcs2, htds, hter, htas;
	int	htck, htdb, htmr, htdt;
	int	htsn, httc, htbae, htcs3;
};

struct	devtab	httab;
struct	buf	rhtbuf, chtbuf;

int	h_openf[NUNIT];		/*  0 if closed, httc contents if open,
				    RAWTM set iff TM on last raw read,
				    and top bit set if fatal error.  */
char	*h_blkno[NUNIT],
	*h_tmblk[NUNIT];

int htdebug;			/*  nonzero to debug  */


#define	HTADDR	0172440

/*  htcs1  */
#define	NOP	0
#define	GO	01
#define	REW	06
#define	DCLR	010
#define	ERASE	024
#define	WEOF	026
#define	SFORW	030
#define	SREV	032

#define	IENABLE	0100
#define	RDY	0200
#define	TRE	040000

/*  httc  */
#define FATERR	0100000		/*  Software bits  */
#define RAWTM	040000

#define	P800	01300		/* 800 + pdp11 mode */
#define	P1600	02300		/* 1600+pdp11 mode */

/*  htds  */
#define	TM	04
#define	PES	040
#define	DRY	0200
#define	EOT	02000
#define	WRL	04000
#define MOL	010000
#define PIP	020000
#define	ERR	040000

/*  hter  */
#define	HARD	064073  /* UNS|OPI|NEF|FMT|CPAR|DPAR|RMR|ILR|ILF */
#define	FCE	01000
#define	CS	02000
#define	COR	0100000

/*  htcs2  */
#define CHARD	077400			/* Hard controller errors. */

/*  Operation states  */
#define	SSEEK	1
#define	SIO	2
#define	SABORT	3
#define	SRETRY	4
#define	SCOM	5
#define	SATMK	6

htopen(dev, flag)
{
	register unit, ds;

	unit = dev&03;
	if (unit >= NUNIT || h_openf[unit]) {
		u.u_error = ENXIO;
		return;
	}
	h_openf[unit] = (dev&010 ? P1600 : P800)|unit;
	h_blkno[unit] = 0;
	h_tmblk[unit] = -1;
	ds = hcommand(dev, NOP);
	if ((ds&MOL)==0 || (flag && (ds&WRL)))
	  {	u.u_error = ENXIO;
		h_openf[unit] = 0;
	  }
}
htclose(dev, flag)
{	
	register int rdev, unit;

	rdev = dev;
	unit = rdev&03;
	if (flag) {
		hcommand(rdev, WEOF);
		hcommand(rdev, WEOF);
	}

	/*  Rewind if specified.  Otherwise, skip as required.  */
	hcommand(rdev,
		  !(rdev&04)? REW
			    : flag ? SREV
				   :  h_openf[unit]&RAWTM ? NOP
							  : SFORW);
	h_openf[unit] = 0;
}
hcommand(dev, com)
{
	register struct buf *bp;

	bp= &chtbuf;
	spl5();
		while(bp->b_flags&B_BUSY) {
			bp->b_flags =| B_WANTED;
			sleep(bp, PRIBIO);
		}
	spl0();
	bp->b_dev = dev;
	bp->b_resid = com;
	bp->b_blkno = 0;
	bp->b_flags = B_BUSY|B_READ;
	htstrategy(bp);
	iowait(bp);
	if(bp->b_flags&B_WANTED)
		wakeup(bp);
	bp->b_flags = 0;
	return(bp->b_resid);
}
htstrategy(abp)
struct buf *abp;
{	
	register struct buf *bp;
	register char **p;

	bp = abp;
	p = &h_tmblk[bp->b_dev&03];
	if (*p <= bp->b_blkno) {
		if (*p < bp->b_blkno) {
			bp->b_flags =| B_ERROR;
			iodone(bp);
			return;
		}
		if (bp->b_flags&B_READ) {	
			clrbuf(bp);
			iodone(bp);
			return;
		}
	}
	if ((bp->b_flags&B_READ)==0)
		*p = bp->b_blkno + 1;
	bp->av_forw = 0;
	spl5();
		if (httab.d_actf==0)
			httab.d_actf = bp;
		else
			httab.d_actl->av_forw = bp;
		httab.d_actl = bp;
		if (httab.d_active==0)
			htstart();
	spl0();
}
htstart()
{
	register struct buf *bp;
	register int unit;
	register char *blkno;

    loop:
	if ((bp = httab.d_actf) == 0)
		return;
	unit = bp->b_dev&03;
	HTADDR->htcs2 = 0;
	HTADDR->httc = h_openf[unit] =& ~RAWTM;

	/*  If rewinding, must await completion.  */
	if (HTADDR->htds&PIP)
	  {	HTADDR->htas = 1<<unit;
		HTADDR->htcs1 = TRE|IENABLE;
		return;
	  }
	HTADDR->htcs1 = TRE|DCLR|GO;
 	blkno = h_blkno[unit];
	if (bp == &chtbuf) {
		if (bp->b_resid==NOP) {
			bp->b_resid = HTADDR->htds;
			goto next;
		}
		httab.d_active = SCOM;
		HTADDR->htfc = 0;
		HTADDR->htcs1 = bp->b_resid|IENABLE|GO;
		return;
	}
	if(h_openf[unit]&FATERR)
	  goto abort;
	if (blkno == bp->b_blkno) {
		httab.d_active = SIO;
		rhstart(bp, &HTADDR->htfc, bp->b_wcount<<1, &HTADDR->htbae);
	} else {
		httab.d_active = SSEEK;
		if (blkno < bp->b_blkno) {
			HTADDR->htfc = blkno - bp->b_blkno;
			HTADDR->htcs1 = SFORW|IENABLE|GO;
		} else {
			HTADDR->htfc = bp->b_blkno - blkno;
			HTADDR->htcs1 = SREV|IENABLE|GO;
		}
	}
	return;
    abort:
	bp->b_flags =| B_ERROR;
    next:
	httab.d_active = 0;
	httab.d_actf = bp->av_forw;
	iodone(bp);
	goto loop;
}

htintr()
  {
	register struct buf *bp;
	register int unit, state;

	if ((bp = httab.d_actf)==0) return;
	unit = bp->b_dev&03;
	state = httab.d_active;

/*  A very useful tool for debugging........................  */
	if( htdebug != 0 )
	  printf("\nbn%d cb%d\nds%o er%o fc%o s%o\n",
		  bp->b_blkno, h_blkno[unit],
		  HTADDR->htds, HTADDR->hter, HTADDR->htfc, state);
/*  ........................................................  */

	/*  Handle errors and TM's whenever not backing
	    over one (cooked TM recovery).		*/
	if ((HTADDR->htcs1&TRE) ||
	    (HTADDR->htds&EOT)  ||	/*  for now...  */
	    (HTADDR->htds&TM && state!=SATMK))

	  /*  Screen out fatal errors (tape position lost).  */
	  if (HTADDR->htcs2&CHARD ||
	      HTADDR->hter&HARD   ||
	      HTADDR->htds&EOT    ||	/* for now... */
	      state==SSEEK && (HTADDR->hter&~FCE)!=0 ||
	      state!=SIO && state!=SCOM && state!=SSEEK)

	    {	/*  Fatal.  Abort all operations and chain ahead.  */
		h_openf[unit] =| FATERR;
		state = SABORT;			/* for below */

		deverror(bp, HTADDR->hter, HTADDR->htcs2);
		bp->b_flags =| B_ERROR;
	    }

	  /*  Nonfatal.  Deal with it.  */
	   else switch(state)
	    {
		case SIO:
			/*  Screen out actual errors:  anything if
			    writing; any remaining controller errors;
			    if reading, things not corrected; if
			    reading raw, not FCE's; and in any event
			    no TM-caused FCE's.			*/
			if ((bp->b_flags&B_READ)==0 ||
			    HTADDR->htcs2.hibyte    ||
			    HTADDR->hter&~(HTADDR->htds&PES ? CS|COR : 0)
				&~(bp== &rhtbuf || HTADDR->htds&TM ? FCE : 0))

			  /*  It's an error.  Retry if not too many.  */
			  if (httab.d_errcnt++ < 10)
			    {	httab.d_active = SRETRY;
				HTADDR->htcs1 = TRE|DCLR|GO;
				HTADDR->htfc = -1;
				HTADDR->htcs1 = SREV|IENABLE|GO;
				return;
			    }

			   /*  Too many errors.  */
			   else
			    {	deverror(bp, HTADDR->hter, HTADDR->htcs2);
				bp->b_flags =| B_ERROR;
				break;
			    }

			/*  Not actually an error.  Get out of here
			    unless cooked TM, which is handled as 
			    for a seek.  Note: NRZ TM causes fc
			    to be set to 2!!!			*/

			/*  If raw read, all is okay as is.  */
			if (!(HTADDR->htds&TM)) break;

			/*  If raw TM, zero fc (to be sure).  Otherwise,
			    set it to 1 for the seek logic.	*/
			if ((HTADDR->htfc = bp== &rhtbuf ? 0 : 1)==0)
			  {	h_openf[unit] =| RAWTM;
				break;
			  }

		case SSEEK:

			/*  Must be cooked (no SSEEK on raw) TM on a
			    forward skip (FCE above).  Confine tape
			    position to before the TM.		*/
			h_blkno[unit] = bp->b_blkno + HTADDR->htfc - 1;
			bp->b_flags =| B_ERROR;

			httab.d_active = SATMK;
			HTADDR->htcs1 = TRE|DCLR|GO;
			HTADDR->htfc = -1;
			HTADDR->htcs1 = SREV|IENABLE|GO;
			return;

		case SCOM:
			/*  Ignore everything.  */
			;
	    };

	/*  Either no error or completely processed error.  */
	switch(state)
	  {
	    case SRETRY:
		if ((bp->b_flags&B_READ)==0)
		  {	httab.d_active = SSEEK;
			HTADDR->htcs1 = ERASE|IENABLE|GO;
			return;
		  }

	    case SSEEK:
		h_blkno[unit] = bp->b_blkno;
		break;


	    case SIO:
		h_blkno[unit]++;

	    case SCOM:
	    case SABORT:
	    case SATMK:
		httab.d_active = httab.d_errcnt = 0;
		httab.d_actf = bp->av_forw;
		bp->b_resid = HTADDR->htfc;
		iodone(bp);
	  };

	/*  Start an operation (perhaps the old same one).  */
	htstart();
  }

htread(dev)
{
	htphys(dev);
	physio(htstrategy, &rhtbuf, dev, B_READ);
	u.u_count = u.u_arg[1] - rhtbuf.b_resid;
}

htwrite(dev)
{
	htphys(dev);
	physio(htstrategy, &rhtbuf, dev, B_WRITE);
	u.u_count = 0;
}

htphys(dev)
{
	register unit, a;

	unit = dev&03;
	a = lshift(u.u_offset, -9);
	h_blkno[unit] = a;
	h_tmblk[unit] = ++a;
}