Ultrix-3.1/sys/dev/ts.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * ULTRIX-11 TS11 - 1600 BPI tape driver
 *
 * SCCSID: @(#)ts.c	3.0	4/21/86
 *
 * Chung-Wu Lee, Dec-04-85
 *
 *	Start supporting up to 4 controller per system.
 *
 * Chung-Wu Lee, Aug-09-85
 *
 *	add ioctl routine (from 2.9 BSD).
 *
 * Fred Canter, Aug-07-85
 *
 *	changes for 1K block file system.
 *
 * Fred Canter
 *
 * Thanks to Jerry Brenner for most of this driver.
 *
 *******************************************************************
 *								   *
 * If the system has both a TM11 and a TS11, set the TS11 CSR to   *
 * 172550. This allows the TS11 to be booted. Use ms0 for the TS11 *
 * at 172520 or ms6 for the TS11 at 172550. Use the Boot: csr      *
 * command to tell the stand-alone programs the TS11 address, if   *
 * it is not at the standard address (172520).                     *
 *								   *
 *******************************************************************
 */


#include <sys/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/errlog.h>
#include <sys/devmaj.h>
#include <sys/mtio.h>
#include <sys/ts_info.h>

int cmdpkt[];			/* command packet */

struct	device
{
	int	tsdb;		/* TS data buffer and bus address reg */
	int	tssr;		/* TS status register */
};

struct	chrdat	chrbuf[];	/* characteristics buffer */
struct	mespkt	mesbuf[];		/* message buffer */
struct	buf	tstab[];
struct	buf	ctsbuf[];
struct	buf	rtsbuf[];


struct compkt *ts_cbp[];	/* command packet buffer pointer, set in tsopen() */
char	*ts_ubmo;	/* offsets packet addresses if unibus map used */
char	*ts_ubcba[];	/* unibus virtual address of command packet buffer */
			/* set in tsopen() to ts_cbp - ts_ubmo */

char	ts_openf[];
u_short	ts_flags[];
daddr_t ts_blkno[];
daddr_t ts_nxrec[];
u_short	ts_erreg[];
u_short	ts_dsreg[];
short	ts_resid[];

/*
 * Block device error log buffer, holds one
 * error log record until the error retry sequence
 * has been completed.
 */

struct tsebuf ts_ebuf[];

int	ts_csr[];	/* CSR address now in config file (c.c) */
/* int	ts_ivec[];	/* interrupt address now in config file (c.c) */
int	nts;		/* NIY number of drives, see c.c */
int	ts_mcact;	/* active flag of ts controller */

	/* bit definitions for command word in ts command packet */

#define	ACK	0100000		/* acknowledge bit */
#define	CVC	040000		/* clear volume check */
#define	OPP	020000		/* opposite. reverse recovery */
#define	SWB	010000		/* swap bytes. for data xfer */
	/* bit definitions for Command mode field during read command */
#define	RNEXT	0		/* read next (forward) */
#define	RPREV	0400		/* read previous (reverse) */
#define	RRPRV	01000		/* reread previous (space rev, read two) */
#define	RRNXT	01400		/* reread next (space fwd, read rev) */
	/* bit definitions for Command mode field during write command */
#define	WNEXT	0		/* Write data next */
#define	WDRTY	01000		/* write data retry , space rev, erase, write data) */
	/* bit definitions for command mode field during position command */
#define	SPCFWD	0		/* space records forward */
#define	SPCREV	0400		/* space records reverse */
#define	SKTPF	01000		/* skip tape marks forward */
#define	SKTPR	01400		/* skip tape marks reverse */
#define	RWIND	02000		/* rewind */

	/* bit definitions for command mode field during format command */
#define	WEOF	0		/* write tape mark */
#define	ERAS	0400		/* erase */
#define	WEOFE	01000		/* write tape mark entry */
	/* bit definitions for command mode field during control command */
#define	MBREAL	0		/* message buffer release */
#define	REWUNL	0400		/* Rewind and unload */
#define	CLEAN	01000		/* clean */
	/* additional definitions */
#define	IEI	0200		/* interrupt enable bit */

	/* command code definitions */
#define	NOP	0
#define	RCOM	01		/* read command */
#define	WCHAR	04		/* write characteristics */
#define	WCOM	05		/* write */
#define	WSUSM	06		/* write subsystem memory */
#define	POSIT	010		/* position command */
#define	FORMT	011		/* Format command */
#define	CONTRL	012		/* Control command */
#define	INIT	013		/* initialize */
#define	GSTAT	017		/* get status immediate */


	/* definition of tssr bits */
#define	SC	0100000		/* special condition */
#define	UPE	040000		/* unibus parity error */
#define	SPE	020000		/* Serial bus parity error */
#define	RMR	010000		/* register modify refused */
#define	NXM	04000		/* non-existent memory */
#define	NBA	02000		/* Need Buffer address */
#define	SSR	0200		/* Sub-System ready */
#define	OFL	0100		/* off-line */

	/* fatal termination class codes */
#define	FTC	030		/* use this as a mask to get codes */
	/* code = 00	see error code byte in tsx3 */
	/* code = 01	I/O seq Crom or main Crom parity error */
	/* code = 10	u-processor Crom parity error,I/O silo parity */
	/*		serial bus parity, or other fatal */
	/* code = 11	A/C low. drive ac low */

	/* termination class codes */
#define	TCC	016		/* mask for termination class codes */
	/* code = 000	normal termination */
	/* code = 001	Attention condition 
	/* code = 010	Tape status alert 
	/* code = 011	Function reject 
	/* code = 100	Recoverable error - tape pos = 1 record down from
			start of function 
	/* code = 101	Recoverable error - tape has not moved
	/* code = 110	Unrecoverable error - tape position lost
	/* code = 111	Fatal controller error - see fatal class bits */

	/* definition of message buffer header word */
#define	MAKC	0100000		/* acknowledge from controller */
#define	MCCF	07400		/* mask for class code field */
	/* class codes are */
	/*	0 = ATTN, on or ofline
		1 = ATTN, microdiagnostic failure
		0 = FAIL, serial bus parity error
		1 = FAIL, WRT LOCK
		2 = FAIL, interlock or non-executable function
		3 = FAIL, microdiagnostic error
	*/

#define	MMSC	037	/* mask for message code field */
	/* message codes are
		020	= end
		021	= FAIL
		022	= ERROR
		023	= Attention

	/* definition of extended status reg 0 bits */
#define	TMK	0100000		/* Tape mark detected */
#define	RLS	040000		/* Record length short */
#define	LET	020000		/* Logical end of Tape */
#define	RLL	010000		/* Record length long */
#define	WLE	04000		/* Write lock error */
#define	NEF	02000		/* Non-executable function */
#define	ILC	01000		/* Illegal command */
#define	ILA	0400		/* Illegal address */
#define	MOT	0200		/* Capistan is moving */
#define	ONL	0100		/* On Line */
#define	IE	040		/* state of interrupt enable bit */
#define	VCK	020		/* Volume Check */
#define	PED	010		/* Phase encoded drive */
#define	WLK	04		/* Write locked */
#define	BOT	02		/* Tape at bot */
#define	EOT	01		/* Tape at eot */

	/* definitions of xstat1 */
#define	DLT	0100000		/* Data late error */
#define	COR	020000		/* Correctable data error */
#define CRS	010000		/* Crease detected */
#define	TIG	04000		/* Trash in the gap */
#define	DBF	02000		/* Deskew Buffer Fail */
#define	SCK	01000		/* Speed check */
#define	IPR	0200		/* Invalid preamble */
#define	SYN	0100		/* Synch Failure */
#define	IPO	040		/* invalid postamble */
#define	IED	020		/* invalid end data */
#define	POS	010		/* postamble short */
#define	POL	04		/* postamble long */
#define	UNC	02		/* Uncorrectable data error */
#define	MTE	01		/* multi track error */

	/* Definitions of XSTAT2 bits */
#define	OPM	0100000		/* operation in progress (tape moving) */
#define	SIP	040000		/* Silo Parity error */
#define	BPE	020000		/* Serial bus Parity error at drive */
#define	CAF	010000		/* Capstan Acceleration fail */
#define	WCF	02000		/* Write card error */
#define	DTP	0477		/* mask for Dead track bits */

	/*	bit definitions for XSTAT3 */
#define	LMX	0200		/* Limit exceeded (tension arms) */
#define	OPI	0100		/* operation incomplete */
#define	REV	040		/* current operation in reverse */
#define	CRF	020		/* capstan response fail */
#define	DCK	010		/* density check */
#define	NOI	04		/* no tape mark or preamble */
#define	LXS	02		/* limit exceeded manual recovery */
#define	RIB	01		/* Reverse into BOT */


#define	SIO	01
#define SSEEK	02
#define	SINIT	04
#define	SRETRY	010
#define	SCOM	020
#define	SOK	040
#define SERROR	0100
#define SREWND	0200

#define	S_WRITTEN	01	/* last command is write command */
#define	S_EOT		010	/* EOT encountered */
#define	S_CSE		020	/* clear EOT */
#define	S_DEOT		040	/* disable EOT */
#define	S_BUSY		0100	/* ctsbuf is busy, switch for clx/cls */
#define	S_WAIT		0200	/* ctsbuf is waited, switch for clx/cls */
#define	S_BUSYF		01000	/* good termination of busy */
#define	S_WAITF		02000	/* good termination of wait */

tsopen(dev, flag)
{
	register char *cba;
	register int ds;
	register struct device *tsaddr;
	int ctrl;

/* if unit > nts or not configured then flag an error and return */
	ctrl = minor(dev) & 077;
	tsaddr = ts_csr[ctrl];
	if (ctrl >= nts || tsaddr == 0)
	{
		u.u_error = ENXIO;
		return;
	}
/*
 * Set up command and message buffer address offset
 * if unibus map present. Also setup command packet
 * buffer pointer (aligned on mod 4 boundry).
 */
	cba = &cmdpkt[5*ctrl];
	if(ubmaps) {	/* UNIBUS map present (much magic to follow) */
		ts_ubmo = (char *)&cfree;
		if(((int)ts_ubmo & 2) && (((int)cba & 2) == 0))
			cba += 2;
		if((((int)ts_ubmo & 2) == 0) && ((int)cba & 2))
			cba += 2;
	} else {
		ts_ubmo = 0;
		if((int)cba & 2)
			cba += 2;
	}
	ts_cbp[ctrl] = cba;	/* save packet buffer pointer for later use */
	ts_ubcba[ctrl] = (char *)cba - ts_ubmo;	/* set UB addr of cmd pkt buf */

	if (flag&FNDELAY)	/* forced open */
	{
		ts_openf[ctrl] = 1;
		return;
	}

/* if unit already open then flag an error and return */
	if (ts_openf[ctrl] > 0)
	{
		u.u_error = ETO;
		return;
	}

	if(tstab[ctrl].b_active & SREWND)
	{	/* this is a delay loop to allow rewind completion	*/
		/* it allows for 2400 feet of tape plus a fudge factor	*/
		/******** this loop assumes lbolt every 4 seconds *******/

		for(ds = 0; ds < 125; ds++)
		{
			if(tsaddr->tssr & SSR)
			{
				ds = 0; 	/* if ready then break	*/
				break;
			}
			else
				sleep(&lbolt, PZERO +1);
		}
		if(ds)
		{
			u.u_error = ETOL;	/* timeout, off-line */
			return;
		}
		else
			tstab[ctrl].b_active = 0;	/* rewind complete	*/
	}

		/* call init. if init returns 1 then failure.
			flag error and return */
	if(tsinit(ctrl, 0))
	{
		u.u_error = ENXIO;
		return;
	}

		/* some house keeping here */
	ts_blkno[ctrl] = 0;			/* set current block # to 0 */
	ts_nxrec[ctrl] = 1000000;		/* set max accessable block # */
	ts_flags[ctrl] &= S_DEOT;
	tstab[ctrl].b_flags |= B_TAPE;	/* Say we are a tape so no write
						ahead */
	if ((ds = tscommand(dev, NOP, 1)) == -1)     /* Get drive status     */
		u.u_error = ENXIO;

		/* if drive is off-line or open for write(read/write)
			and write locked then fatal error	 */
	if(ds & EOT)
		ts_flags[ctrl] |= S_EOT;
	if(tsaddr->tssr & OFL)
		u.u_error = ETOL;
	else if((flag & FWRITE) && (ds & WLK))
		u.u_error = ETWL;

	if(u.u_error == 0)	/* if no error then say opened	*/
		ts_openf[ctrl] = 1;
	else
		ts_openf[ctrl] = 0;
}

tsclose(dev, flag)
{
	int ctrl;

	ctrl = minor(dev) & 077;
	if (ts_openf[ctrl] <= 0)
		return;
	if(tstab[ctrl].b_active & SREWND)
	{
		ts_openf[ctrl] = 0;		/* say closed	*/
		return;
	}
		/* if opened for write or read/write and was
			written then write two tape marks
			and back up one */
	if(((flag & (FWRITE|FREAD)) == FWRITE) ||
	   ((flag & FWRITE) && (ts_flags[ctrl] & S_WRITTEN)))
	{
		if (tscommand(dev, (FORMT|WEOF), 1) == -1)
			goto badcls;
		if (tscommand(dev, (FORMT|WEOF), 1) == -1)
			goto badcls;
		if (tscommand(dev, (SPCREV|POSIT), 1) == -1)
			goto badcls;
	}

		/* if the no-rewind bit in minor is clear then rewind  */
	if ((minor(dev)&0200) ==0 )
	{
		if (tscommand(dev, (RWIND|POSIT), 0) == -1)
			goto badcls;
		ts_flags[ctrl] &= ~S_EOT;
	}
	ts_openf[ctrl] = 0;		/* say closed	*/
	return;

badcls:
	u.u_error = ENXIO;
	return;
}
tsioctl(dev, cmd, data, flag)
	caddr_t data;
	dev_t dev;
{
	register struct buf *bp;
	struct buf *wbp;
	register callcount;
	int ctrl, fcount;
	struct mtop *mtop;
	struct mtget *mtget;
	/* we depend of the values and order of the MT codes here */
	static tsops[] =
	 {FORMT|WEOF,POSIT|SKTPF,POSIT|SKTPR,POSIT|SPCFWD,POSIT|SPCREV,
	  POSIT|RWIND,CONTRL|REWUNL,NOP,NOP,NOP,NOP,INIT,INIT};

	ctrl = minor(dev) & 077;
	bp = &ctsbuf[ctrl];
	switch (cmd) {

	case MTIOCTOP:	/* tape operation */
		mtop = (struct mtop *)data;
		switch (mtop->mt_op) {

		case MTWEOF:
			callcount = mtop->mt_count;
			fcount = 1;
			break;

		case MTFSF: case MTBSF:
		case MTFSR: case MTBSR:
			callcount = 1;
			fcount = mtop->mt_count;
			break;

		case MTREW: case MTOFFL: case MTNOP:
			callcount = 1;
			fcount = 1;
			break;

		case MTCSE:
			ts_flags[ctrl] |= S_CSE;
			return(0);

		case MTENAEOT:
			ts_flags[ctrl] &= ~S_DEOT;
			return(0);

		case MTDISEOT:
			ts_flags[ctrl] |= S_DEOT;
			return(0);

		case MTCACHE: case MTNOCACHE:
		case MTASYNC: case MTNOASYNC:
			return(0);

		case MTCLX:
		case MTCLS:
			if((wbp = tstab[ctrl].b_actf) != 0) {
				tstab[ctrl].b_actf = wbp->av_forw;
				if (wbp != bp) {
					wbp->b_error = EIO;
					wbp->b_flags |= B_ERROR;
					iodone(wbp);
				}
			}
			if (ts_flags[ctrl]&S_BUSY) {
				bp->b_flags &= ~B_BUSY;
				ts_flags[ctrl] &= ~S_BUSYF;
				iodone(bp);
			}
			if (ts_flags[ctrl]&S_WAIT) {
				bp->b_flags &= ~B_WANTED;
				ts_flags[ctrl] &= ~S_WAITF;
				wakeup((caddr_t)bp);
			}
			if(tsinit(ctrl, 0))
			{
				u.u_error = ENXIO;
				return;
			}
			ts_blkno[ctrl] = 0;
			ts_nxrec[ctrl] = 1000000;
			ts_flags[ctrl] &= S_DEOT;
			tstab[ctrl].b_active = 0;
			ts_openf[ctrl] = 0;
			tsstart(ctrl);
			return(0);

		default:
			return (ENXIO);
		}
		if (callcount <= 0 || fcount <= 0)
			return (EINVAL);
		while (--callcount >= 0) {
			if (tscommand(dev, tsops[mtop->mt_op], fcount) == -1)
			{
				u.u_error = ENXIO;
				return(0);
			}
			if ((mtop->mt_op == MTFSR || mtop->mt_op == MTBSR) &&
			    bp->b_resid)
				return (EIO);
			if ((bp->b_flags&B_ERROR) || ts_erreg[ctrl]&BOT)
				break;
		}
		return (geterror(bp));

	case MTIOCGET:
		mtget = (struct mtget *)data;
		mtget->mt_dsreg = 0;
		mtget->mt_erreg = ts_erreg[ctrl];
		mtget->mt_resid = ts_resid[ctrl];
		mtget->mt_softstat = 0;
		if (ts_flags[ctrl]&S_EOT)
			mtget->mt_softstat |= MT_EOT;
		if (ts_flags[ctrl]&S_DEOT)
			mtget->mt_softstat |= MT_DISEOT;
		mtget->mt_type = MT_ISTS;
		break;

	default:
		return (ENXIO);
	}
	return (0);
}
static
tscommand(dev, com, count)
{
	register struct buf *bp;
	int ctrl;

	ctrl = minor(dev) & 077;
	bp = &ctsbuf[ctrl];		/* set buffer pointer to command table	*/
	spl5(); 		/* grab some priority for this	*/
	while(bp->b_flags&B_BUSY)	/* if command table is busy	*/
	{
		bp->b_flags |= B_WANTED;	/* then say we want it	*/
		ts_flags[ctrl] |= (S_WAIT|S_WAITF);
		sleep((caddr_t)bp, PRIBIO);	/* and sleep around	*/
		if ((ts_flags[ctrl]&S_WAITF) == 0)
			return(-1);
	}
	spl0(); 			/* have the buffer so drop pri	*/
	bp->b_dev = dev;		/* associate with this device	*/
	bp->b_resid = com;		/* load desired command here	*/
	bp->b_bcount = count;
	bp->b_blkno = 0;		/* don't need a block number	*/
	bp->b_flags =  B_BUSY|B_READ;	/* mark command buffer busy	*/
	ts_flags[ctrl] |= (S_BUSY|S_BUSYF);
	tsstrategy(bp); 		/* do it			*/
	iowait(bp);			/* wait until it's done 	*/
	if ((ts_flags[ctrl]&S_BUSYF) == 0)
		return(-1);
	if(bp->b_flags&B_WANTED)	/* if someone (me) wants it	*/
	{
		ts_flags[ctrl] &= ~S_WAIT;
		wakeup((caddr_t)bp);	/* then say he can have it	*/
	}
	bp->b_flags &= B_ERROR;		/* say command buffer avail	*/
	return(bp->b_resid);		/* return drive status		*/
}
tsstrategy(bp)
struct buf *bp;
{
	register daddr_t *p;
	int ctrl;

	mapalloc(bp);
	ctrl = minor(bp->b_dev) & 077;
	if(bp != &ctsbuf[ctrl])		/* if not command buffer	*/
	{
		if(ts_openf[ctrl] < 0)
		{
			bp->b_flags |= B_ERROR; /* mark buffer bad	*/
			iodone(bp);
			return;
		}
		p = &ts_nxrec[ctrl];		/* get pointer to max accessable
						block number	*/

#ifdef	UCB_NKB
		if(dbtofsb(bp->b_blkno) > *p)  /* if requested max access */
#else
		if(bp->b_blkno > *p)	/* if requested > max access	*/
#endif	UCB_NKB
		{
			bp->b_flags |= B_ERROR; /* thats a no-no	*/
			bp->b_error = ENXIO;	/* make sure it's known */
			iodone(bp);		/* return buffer	*/
			return; 		/* bye - bye		*/
		}

#ifdef	UCB_NKB
		if(dbtofsb(bp->b_blkno) == *p && bp->b_flags&B_READ)
#else
		if(bp->b_blkno == *p && bp->b_flags&B_READ)
#endif	UCB_NKB
		{
			/* if requested block = max accessable & read  */

			if((bp->b_flags&B_PHYS) == 0)
				clrbuf(bp);		/* zero the buffer */
			bp->b_resid = bp->b_bcount;	/* didn't read	*/
			iodone(bp);		/* return the buffer	*/
			return; 		/* bye - bye		*/
		}

		ts_flags[ctrl] &= ~S_WRITTEN;
		if((bp->b_flags&B_READ) == 0)	/* if a write command	*/
		{
#ifdef	UCB_NKB
			*p = dbtofsb(bp->b_blkno) + 1; /* set max access to requested + 1 */
#else
			*p = bp->b_blkno +1;	/* set max access to requested
							+ 1	*/
#endif	UCB_NKB
			ts_flags[ctrl] |= S_WRITTEN;	/* say it's been written */
		}
	}
	bp->av_forw = 0;		/* clear the available forward link */
	spl5(); 			/* grab some priority for this	*/

	if (tstab[ctrl].b_actf == NULL)	/* if no forward link on the table */
		tstab[ctrl].b_actf = bp;	/* then set this buffer as next */
	else
		tstab[ctrl].b_actl->av_forw = bp;	/* else link this one to
							the end 	*/

	tstab[ctrl].b_actl = bp;	/* set a reverse link in table	*/
	if (tstab[ctrl].b_active == 0)	/* if not active then		*/
		tsstart(ctrl);		/* start some activity		*/
	spl0();
}
static
tsstart(ctrl)
{
	register struct buf *bp;
	register struct device *tsaddr;
	register struct compkt *combuf;
	daddr_t blkno;
	int com;

	combuf = ts_cbp[ctrl];
    loop:
	if ((bp = tstab[ctrl].b_actf) == NULL)
		return; 	/* no link so return	*/
	tsaddr = ts_csr[ctrl];

	blkno = ts_blkno[ctrl];	/* get current block #	*/
	if(tsaddr->tssr & OFL)
	{				/* driver off-line. bad news	*/
		u.u_error = ETOL;	/* tell user, tape off-line */
/*
		printf("\nMagtape %o offline\n",minor(bp->b_dev)&077);
*/
		ts_openf[ctrl] = -1;		/* say this is fatal		*/
		bp->b_flags |= B_ERROR; /* mark buffer bad		*/
		goto next;		/* start the flush loop 	*/
	}
	if (bp == &ctsbuf[ctrl])	/* if this is command table	*/
	{
		switch(bp->b_resid)
		{
			case POSIT|SKTPF:  case POSIT|SKTPR:
			case POSIT|SPCFWD: case POSIT|SPCREV:
				combuf->tsba = bp->b_bcount;

	 		case FORMT|WEOF: case POSIT|RWIND: case CONTRL|REWUNL:
				break;

			case NOP:
				bp->b_resid = mesbuf[ctrl].mstsx0;
				goto next;

			case INIT:
				tsinit(ctrl,1);
				return;

			default:
				printf("Bad ioctl on ts\n");
				return;
		}
		tstab[ctrl].b_active = SCOM;	/* say just general maint command */
		combuf->tscom = (ACK|IEI|bp->b_resid);	/* load command */
		tsaddr->tsdb = ts_ubcba[ctrl]; /* feed driver packet	*/
		return; 		/* later			*/
	}
	if(ts_openf[ctrl] < 0)
	{
		bp->b_error = ETPL;	/* set fatal flag		*/
		bp->b_flags |= B_ERROR; /* mark buffer bad		*/
		goto next;		/* start the flush loop 	*/
	}
#ifdef	UCB_NKB
	if(dbtofsb(bp->b_blkno) > ts_nxrec[ctrl])
#else
	if(bp->b_blkno > ts_nxrec[ctrl])
#endif	UCB_NKB
	{
		bp->b_flags |= B_ERROR; /* block # out of range 	*/
		goto next;		/* start the flush loop 	*/
	}
#ifdef	UCB_NKB
	if(blkno != dbtofsb(bp->b_blkno))  /* if requested != current  */
#else
	if(blkno != bp->b_blkno)	/* if requested != current	  */
#endif	UCB_NKB
	{
		tstab[ctrl].b_active = SSEEK; /* say this is a seek	*/
#ifdef	UCB_NKB
		if(blkno < dbtofsb(bp->b_blkno))  /*we're not there yet so  */
#else
		if(blkno < bp->b_blkno) /* we're not there yet so	*/
#endif	UCB_NKB
		{			/* space forward to it		*/
			combuf->tscom = (ACK|IEI|SPCFWD|POSIT);
#ifdef	UCB_NKB
			combuf->tsba = dbtofsb(bp->b_blkno) - blkno;  /* load block */
#else
			combuf->tsba = bp->b_blkno - blkno; /* load block */
#endif	UCB_NKB
		}
		else
		{			/* need to backup		*/
			combuf->tscom = (ACK|IEI|SPCREV|POSIT);
#ifdef	UCB_NKB
			combuf->tsba = blkno - dbtofsb(bp->b_blkno);  /*load block */
#else
			combuf->tsba = blkno - bp->b_blkno; /* load block */
#endif	UCB_NKB
		}
		tsaddr->tsdb = ts_ubcba[ctrl]; /* feed driver packet	*/
		return; 			/* bye - bye		*/
	}
	tstab[ctrl].b_active = SIO;	/* say this is read or write	*/
	combuf->tsba = bp->b_un.b_addr; /* get low order address	*/
	combuf->tsbae = bp->b_xmem;	/* set extended bits		*/
	combuf->tswc = bp->b_bcount;	/* grab a positive byte count	*/
	combuf->tscom = (ACK|IEI);	/* set up acknowledge and IEI	*/
	if ((ts_flags[ctrl]&S_DEOT) == 0) {
		if ((mesbuf[ctrl].mstsx0&EOT) && (ts_flags[ctrl]&S_CSE) == 0) {
			bp->b_resid = bp->b_bcount;
			bp->b_error = ENOSPC;
			bp->b_flags |= B_ERROR;
			goto next;
		}
	}
	if(bp->b_flags &B_READ)
		combuf->tscom |= (RNEXT|RCOM);	/* read next record	*/
	else
		combuf->tscom |= (WNEXT|WCOM);	/* write next record	*/
	tsaddr->tsdb = ts_ubcba[ctrl]; /* feed packet to drive 	*/
	ts_mcact |= (1 << ctrl);	/* controller active */
	el_bdact |= (1 << TS_BMAJ);	/* device active */
	return;

	/* this loop is good for flushing buffers on fatal errors	*/
next:
	tstab[ctrl].b_active = 0;
	tstab[ctrl].b_actf = bp->av_forw;	/* grab next avail forward */
	if (bp == &ctsbuf[ctrl])
		ts_flags[ctrl] &= ~S_BUSY;
	iodone(bp);			/* say current buffer done	*/
	goto loop;			/* try some more		*/
}
tsintr(dev)
{
	register struct buf *bp;
	register struct device *tsaddr;
	register struct compkt *combuf;
	int	state, *ebp, ctrl, i;

	ctrl = minor(dev) & 077;
	combuf = ts_cbp[ctrl];
	tsaddr = ts_csr[ctrl];
	ts_erreg[ctrl] = mesbuf[ctrl].mstsx0;
	ts_resid[ctrl] = mesbuf[ctrl].msresid;
	if (mesbuf[ctrl].mstsx0&EOT)
		ts_flags[ctrl] |= S_EOT;
	else
		ts_flags[ctrl] &= ~(S_EOT|S_CSE);
	if((bp = tstab[ctrl].b_actf) == 0)	/* grab a buffer pointer */
	{
		logsi(tsaddr);		/* log stray interrupt */
		return;
	}
	state = tstab[ctrl].b_active; 	/* get a copy of status 	*/

	if(tsaddr->tssr & SC)		/* Special Condition Set ??	*/
	{
		/* If initial error, save reg.'s for error log */
		/* The two real registers are saved by fmtbde(), */
		/* the remaining data is loaded from the buffers. */

		if(tstab[ctrl].b_errcnt == 0)
		{
			fmtbde(bp, &ts_ebuf[ctrl], tsaddr, 2, TSDBOFF);
			ts_ebuf[ctrl].ts_bdh.bd_nreg = NTSREG;
			ebp = (char *)ts_cbp[ctrl];	/* addr of command packet */
			for(i=2; i<6; i++)
				ts_ebuf[ctrl].ts_reg[i] = *ebp++;
			ebp = &chrbuf[ctrl];	/* addr of characteristics buffer */
			for( ; i<10; i++)
				ts_ebuf[ctrl].ts_reg[i] = *ebp++;
			ebp = &mesbuf[ctrl];	/* addr of message buffer */
			for( ; i<17; i++)
				ts_ebuf[ctrl].ts_reg[i] = *ebp++;
		}
			/* look at the Termination Class code	*/
		switch ((tsaddr->tssr & TCC) >> 1)
		{
			case 07:	/* fatal sub-system error	*/
			case 06:	/* non-recoverable error	*/
				state = 0;	/* this means dead	*/
				break;	/* break out			*/

			case 05:	/* recoverable. no tape motion	*/
				++tstab[ctrl].b_errcnt;
				state = SRETRY; /* do a straight retry	*/
				break;

			case 04:	/* recoverable. tape position +1 */
				if (state == SIO) {
					if(++tstab[ctrl].b_errcnt < 10)
					{
						if(bp->b_flags & B_READ)
							/* re-read previous	*/
					  	combuf->tscom =(ACK|IEI|RRPRV|RCOM);
						else
							/* write data retry	*/
					   	combuf->tscom=(ACK|IEI|WDRTY|WCOM);
						tsaddr->tsdb = ts_ubcba[ctrl];
						return;
					}
				} else if (ts_openf[ctrl] > 0 && bp != &rtsbuf[ctrl])
					ts_openf[ctrl] = -1;
				break;

			case 03:	/* function reject		*/
					/* if VCK then someone diddled
						with the drive		*/
					/* this is fatal, but not ioctl	*/
				if (state != SCOM)
					state = 0;
				break;

			case 02:	/* Tape Status Alert		*/
				if (state == SCOM)
					break;
				if(mesbuf[ctrl].mstsx0 & (TMK|EOT))
				{	/* End of file/tape set max to this blk */
#ifdef	UCB_NKB
					ts_nxrec[ctrl] = dbtofsb(bp->b_blkno);
#else
					ts_nxrec[ctrl] = bp->b_blkno;
#endif	UCB_NKB
					state = SOK;
					break;
				}
				else if(mesbuf[ctrl].mstsx0&RLS)
				{	/* record length is short. do not
					have to treat this as fatal because
					the actual byte will be returned */

					state = SIO;	/* update block cnt */
					break;
				}
				else if(mesbuf[ctrl].mstsx0&RLL)
				{
					state = SIO;
					bp->b_flags |= B_ERROR;
					break;
				}
				break;
			case 01:	/* Attention Condition		*/
				if(tsaddr->tssr & OFL)
				{
					printf("\nMagtape %o offline\n", ctrl);
					state = 0;	/* drive offline */
				}
				break;
			default:
				break;
		}
		if(tstab[ctrl].b_errcnt >= 10 || state == 0)
		{
			/* bad news. FATAL ERROR or No recovery */
			bp->b_flags |= B_ERROR; /* mark this buffer bad */
			bp->b_error = ETPL;	/* fatal - tape position lost */
			ts_openf[ctrl] = -1;		/* say no more		*/
			if(state)
				tsinit(ctrl, 0);	/* init the drive. no rewind*/
			else
				tsinit(ctrl, 1);	/* init the drive. rewind */
			state = 0;		/* just to be sure	*/
		}
	}
	if((tstab[ctrl].b_active & SREWND) == 0)
		tstab[ctrl].b_active = 0;	/* say ok to do more	*/
	ts_mcact &= ~(1 << ctrl);		/* controller not active */
	if (ts_mcact == 0)
		el_bdact &= ~(1 << TS_BMAJ);	/* device not active */
	/* If possible log the error, if not print it on the console */

	if(tstab[ctrl].b_errcnt || state == 0)
	{
		ts_ebuf[ctrl].ts_bdh.bd_errcnt = tstab[ctrl].b_errcnt;
		if(!logerr(E_BD, &ts_ebuf[ctrl], sizeof(struct tsebuf)))
		{
			/* tssr, mstsx0 */
			deverror(bp, ts_ebuf[ctrl].ts_reg[1], ts_ebuf[ctrl].ts_reg[13]);
			/* mstsx1, mstsx2 */
			deverror(bp, ts_ebuf[ctrl].ts_reg[14], ts_ebuf[ctrl].ts_reg[15]);
			/* mstsx3, retry count */
			deverror(bp, ts_ebuf[ctrl].ts_reg[16], tstab[ctrl].b_errcnt);
			tstab[ctrl].b_errcnt = 0;	/* clear errcnt here, */
						/* because of state 0 in */	
						/* SCOM below. */
		}
	}
	if(state == 0)
		tstab[ctrl].b_errcnt = 0;
	switch (state)
	{
		case SRETRY:			/* straight retry	*/
			break;
		case SIO:			/* update block # and	*/
		case SOK:			/* fall through SCOM	*/
			ts_blkno[ctrl]++;
		case SCOM:
			tstab[ctrl].b_errcnt = 0;	/* no errors		*/
			tstab[ctrl].b_actf = bp->av_forw;	/* next buffer	*/
			if(combuf->tscom & POSIT)	/* if position	*/
				bp->b_resid = 0;	/* this could be
								garbage */
			else
			       bp->b_resid = mesbuf[ctrl].msresid;   /* get residual
								count	*/
			if (bp == &ctsbuf[ctrl])
				ts_flags[ctrl] &= ~S_BUSY;
			iodone(bp);			/* return buffer */
			break;
		case SSEEK:
#ifdef	UCB_NKB
			ts_blkno[ctrl] = dbtofsb(bp->b_blkno);   /* update blk no */
#else
			ts_blkno[ctrl] = bp->b_blkno; 	/* update blk no */
#endif	UCB_NKB
			break;
		default:
			break;
	}
	tsstart(ctrl);		/* start any pending I/O		*/
}

static
tsinit(ctrl, ini)
int ctrl;
int ini;
{
	register struct device *tsaddr;
	register struct compkt *combuf;
	int ds, cnt;

	combuf = ts_cbp[ctrl];
	tsaddr = ts_csr[ctrl];
	if(ini)
	{
		tsaddr->tssr = 0;		/* init with rewind	*/
		tstab[ctrl].b_active = SREWND;
		return(0);
	}
	else
	{
		combuf->tscom = (ACK|CVC|INIT); /* init no rewind	*/
		tsaddr->tsdb = ts_ubcba[ctrl]; /* feed drive packet	*/
		for(cnt = 0; cnt < 30000; cnt++)
		{				/* wait loop. hopefully */
			if(tsaddr->tssr & SSR)	/* 3 - 5 msecs long	*/
				break;		/* have ready. break out*/
		}
	}
	if(cnt >= 30000)
	{					/* wait loop timeout	*/
ts_iex:
		printf("TS11 init failed\n");
		deverror(&ctsbuf[ctrl], tsaddr->tssr, mesbuf[ctrl].mstsx0);
		return(1);			/* bad stuff. say so	*/
	}
	/* show where message buffer is	*/
	chrbuf[ctrl].msbptr = (char *)&mesbuf[ctrl] - ts_ubmo;
	chrbuf[ctrl].msbae = 0;			/* hope no extended addr */
	chrbuf[ctrl].msbsiz = 016;		/* size of message buf	*/
	chrbuf[ctrl].mschar = 0;		/* set characteristics	*/
	combuf->tscom = (ACK|CVC|WCHAR);	/* say this is write
							characteristics */
	combuf->tsba = (char*)&chrbuf[ctrl] - ts_ubmo;	/* addr of chars buf	*/
	combuf->tsbae = 0;			/* hope no ext addr	*/
	combuf->tswc = 010;			/* size of chars buf	*/
	tsaddr->tsdb = ts_ubcba[ctrl]; 	/* feed packet to drive */
	for(cnt = 0; cnt < 30000; cnt++)
	{
		if(tsaddr->tssr & SSR)
			break;
	}
	if(cnt >= 30000)
		goto ts_iex;
	if(((tsaddr->tssr & TCC) >>1) > 1)
		goto ts_iex;
	else
		return(0);			/* good init		*/
}
tsread(dev)
{
	int ctrl;

	ctrl = minor(dev) & 077;
	tsphys(dev);
	physio(tsstrategy, &rtsbuf[ctrl], dev, B_READ);
}
tswrite(dev)
{
	int ctrl;

	ctrl = minor(dev) & 077;
	tsphys(dev);
	physio(tsstrategy, &rtsbuf[ctrl], dev, B_WRITE);
}
static
tsphys(dev)
{
	daddr_t a;
	int ctrl;

	if((ctrl = (minor(dev) & 077)) < nts)	/* only if valid drive */
	{
#ifdef	UCB_NKB
		a = dbtofsb(u.u_offset >>9);  /* grab the block offset  */
#else
		a = u.u_offset >>9;	/* grab the block offset	*/
#endif	UCB_NKB
		ts_blkno[ctrl] = a;		/* set current block		*/
		ts_nxrec[ctrl] = a+1; 	/* set next max block # 	*/
	}
}