V7M/sys/dev/ts.c

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

/*
 * UNIX/v7m TS11 - 1600 BPI tape driver
 *
 * Fred Canter 5/5/81
 *
 * Thanks to Jerry Brenner for most of this driver.
 *
 *******************************************************************
 *								   *
 * The TM11 and TS11 share the standard address (0172520) and      *
 * the standard vector (0224). This driver allows for the          *
 * coexistence of the TM11 and TS11 on the same system, by using   *
 * the minor device number to modify the TS11 CSR address.         *
 * The minor device number is added to the base address of 0172520 *
 * to form the actual TS11 address. If the TS11 is the only tape   *
 * on the system, it's hardware address is set to 0172520 and the  *
 * minor device number is zero. If the system also has a TM11 tape *
 * tape, the TS11 hardware address is set to 0172550 and the minor *
 * device number is set to six. The minor device number is set by  *
 * the makefile in /dev, i.e., "make ts0" for the TS11 at 0172520  *
 * or "make ts6" for the TS11 at 0172550.                          *
 * The same technigue is usde to boot the TS11 using the M9312     *
 * hardware bootstrap, i.e., "MS0" for TS11 at 0172520 and "MS6"   *
 * for TS11 at 0172550.      					   *
 *								   *
 *******************************************************************
 */


#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/user.h"

int cmdpkt[5];	      /* command packet. extra location is mod 4 alignment */

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

struct	mespkt
{
	int	mshdr;		/* message packet header word */
	int	mssiz;		/* size of message returned */
	int	msresid;	/* remaining count register */
	int	mstsx0;		/* extended status reg 0 */
	int	mstsx1;		/* extended status reg 1 */
	int	mstsx2;		/* extneded status reg 2 */
	int	mstsx3;		/* extended status reg 3 */
};

struct compkt
{
	int	tscom;		/* command packet command word */
	int	tsba;		/* memory address */
	int	tsbae;		/* extended address */
	int	tswc;		/* byte count, record count, etc. */
};

struct	chrdat
{
	int	msbptr;		/* pointer to message buffer */
	int	msbae;		/* hi address (bits 16 & 17) */
	int	msbsiz;		/* size of message buffer */
	int	mschar;		/* characteristics word */
};

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


char	ts_flags;
int	ts_openf;
int	ts_stray;
daddr_t ts_blkno;
daddr_t ts_nxrec;

/*
 * DO NOT !!!!, change the following TS11 address assignment
 * without first reading the comments at the head of this driver.
 * If the TS11 is at address 0172520 or 0172550, then the
 * TSADDR definition should NOT be changed.
 * If the TS11 is at some other address, then set the TSADDR
 * to that address and make sure that the minor device
 * number is set to zero (/dev/makefile "make ts0").
 */
#define	TSADDR	((struct device *)0172520)

/*	The following 3 lines are necessary to align com buffer on mod 4 */
#define	t_temp	(&cmdpkt[1])	/* get address of cmdpkt +2 */
#define	t_temp1	(t_temp & 0177776)
#define	combuf	((struct compkt *)t_temp1)


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

tsopen(dev, flag)
{
	register int ds;
	register struct device *tsadr;

		/* if unit > 7 or if already open
			then flag an error and return */
	tsadr = TSADDR + (dev&07);
	if (((minor(dev) & 0177) > 7)  || ts_openf > 0)
	{
		u.u_error = ENXIO;
		return;
	}

	if(tstab.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(tsadr->tssr & SSR)
			{
				ds = 0; 	/* if ready then break	*/
				break;
			}
			else
				sleep(&lbolt, PZERO +1);
		}
		if(ds)
		{
			u.u_error = ENXIO;	/* timeout. byebye	*/
			return;
		}
		else
			tstab.b_active = 0;	/* rewind complete	*/
	}

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

		/* some house keeping here */
	ts_blkno = 0;			/* set current block # to 0 */
	ts_nxrec = 1000000;		/* set max accessable block # */
	ts_flags = 0;			/* clear status flags	*/
	tstab.b_flags |= B_TAPE;	/* Say we are a tape so no write
						ahead */
	ds = tscommand(dev, NOP);	     /* Get drive status     */

		/* if drive is off-line or open for write(read/write)
			and write locked then fatal error	 */
	if(tsadr->tssr & OFL || flag && (ds&WLK))
	{
	       printf("Magtape %o offline or write locked\n",minor(dev)&0177);
		u.u_error = ENXIO;
	}
	if(u.u_error == 0)	/* if no error then say opened	*/
		ts_openf = 1;
	else
		ts_openf = 0;
}
tsclose(dev, flag)
{
	if(tstab.b_active & SREWND)
	{
		ts_openf = 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 || ((flag & FWRITE) && (ts_flags & S_WRITTEN)))
	{
		tscommand(dev, (FORMT|WEOF));
		tscommand(dev, (FORMT|WEOF));
		tscommand(dev, (SPCREV|POSIT));
	}

		/* if the no-rewind bit in minor is clear then rewind  */
	if ((minor(dev)&0200) ==0 )
	{
		tscommand(dev, (RWIND|POSIT));
	}
	ts_openf = 0;		/* say closed	*/
}
tscommand(dev, com)
{
	register struct buf *bp;

	bp = &ctsbuf;		/* 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	*/
		sleep((caddr_t)bp, PRIBIO);	/* and sleep around	*/
	}
	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_blkno = 0;		/* don't need a block number	*/
	bp->b_flags =  B_BUSY|B_READ;	/* mark command buffer busy	*/
	tsstrategy(bp); 		/* do it			*/
	iowait(bp);			/* wait until it's done 	*/
	if(bp->b_flags&B_WANTED)	/* if someone (me) wants it	*/
		wakeup((caddr_t)bp);	/* then say he can have it	*/
	bp->b_flags = 0;		/* say command buffer avail	*/
	return(bp->b_resid);		/* return drive status		*/
}
tsstrategy(bp)
{
	register daddr_t *p;

	if(bp->b_flags & B_PHYS)  /* if RAW I/O call unibus map allocate */
		mapalloc(bp);
	if(bp != &ctsbuf)		/* if not command buffer	*/
	{
		if(ts_openf < 0)
		{
			bp->b_flags |= B_ERROR; /* mark buffer bad	*/
			iodone(bp);
			return;
		}
		p = &ts_nxrec;		/* get pointer to max accessable
						block number	*/

		if(bp->b_blkno > *p)	/* if requested > max access	*/
		{
			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		*/
		}

		if(bp->b_blkno == *p && bp->b_flags&B_READ)
		{
			/* if requested block = max accessable & read  */

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

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

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

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

    loop:
	if ((bp = tstab.b_actf) == NULL)
		return; 	/* no link so return	*/
	tsadr = TSADDR + (bp->b_dev & 07);

	blkno = ts_blkno;	/* get current block #	*/
	if(tsadr->tssr & OFL)
	{				/* driver off-line. bad news	*/
		printf("\nMagtape %o offline\n",minor(bp->b_dev)&0177);
		ts_openf = -1;		/* say this is fatal		*/
		bp->b_flags |= B_ERROR; /* mark buffer bad		*/
		goto next;		/* start the flush loop 	*/
	}
	if (bp == &ctsbuf)		/* if this is command table	*/
	{
		if(bp->b_resid == NOP)	/* No-op means get status	*/
		{
			bp->b_resid = mesbuf.mstsx0;	/* hope this is the latest */
			goto next;	/* try the next one		*/
		}
		tstab.b_active = SCOM;	/* say just general maint command */
		combuf->tswc = 1;	/* we only do one		*/
		combuf->tscom = (ACK|IEI|bp->b_resid);	/* load command */
		tsadr->tsdb = &combuf->tscom;	/* feed driver packet	*/
		return; 		/* later			*/
	}
	if(ts_openf < 0)
	{
		bp->b_error = ENXIO;	/* set fatal flag		*/
		bp->b_flags |= B_ERROR; /* mark buffer bad		*/
		goto next;		/* start the flush loop 	*/
	}
	if(bp->b_blkno > ts_nxrec)
	{
		bp->b_flags |= B_ERROR; /* block # out of range 	*/
		goto next;		/* start the flush loop 	*/
	}
	if(blkno != bp->b_blkno)	/* if requested != current	  */
	{
		tstab.b_active = SSEEK; /* say this is a seek		*/
		if(blkno < bp->b_blkno) /* we're not there yet so	*/
		{			/* space forward to it		*/
			combuf->tscom = (ACK|IEI|SPCFWD|POSIT);
			combuf->tsba = bp->b_blkno - blkno; /* load block */
		}
		else
		{			/* need to backup		*/
			combuf->tscom = (ACK|IEI|SPCREV|POSIT);
			combuf->tsba = blkno - bp->b_blkno; /* load block */
		}
		tsadr->tsdb = &combuf->tscom;	/* feed driver packet	*/
		return; 			/* bye - bye		*/
	}
	tstab.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(bp->b_flags &B_READ)
		combuf->tscom |= (RNEXT|RCOM);	/* read next record	*/
	else
		combuf->tscom |= (WNEXT|WCOM);	/* write next record	*/
	tsadr->tsdb = &combuf->tscom;	/* feed packet to drive 	*/
	return;

	/* this loop is good for flushing buffers on fatal errors	*/
next:
	tstab.b_actf = bp->av_forw;	/* grab next avail forward	*/
	iodone(bp);			/* say current buffer done	*/
	goto loop;			/* try some more		*/
}
tsintr()
{
	register struct buf *bp;
	register int state;
	register struct device *tsadr;

	if((bp = tstab.b_actf) == 0)	/* grab a buffer pointer	*/
	{
		ts_stray++;		/* no pointer. stray interrupt	*/
		return;
	}
	tsadr = TSADDR + (bp->b_dev & 07);
	state = tstab.b_active; 	/* get a copy of status 	*/

	if(tsadr->tssr & SC)		/* Special Condition Set ??	*/
	{
			/* look at the Termination Class code	*/
		switch ((tsadr->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.b_errcnt;
				state = SRETRY; /* do a straight retry	*/
				break;

			case 04:	/* recoverable. tape position +1 */
				if(++tstab.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);
					tsadr->tsdb = &combuf->tscom;
					return;
				}
				else
					break;

			case 03:	/* function reject		*/
					/* if VCK then someone diddled
						with the drive		*/
					/* this is fatal		*/
				state = 0;
				break;

			case 02:	/* Tape Status Alert		*/
				if(mesbuf.mstsx0 & TMK)
				{	/* End of file set max to this blk */
					ts_nxrec = bp->b_blkno;
					state = SOK;
					break;
				}
				else if(mesbuf.mstsx0 & EOT)
				{	/* End of tape set max to this blk */
					state = 0;	/* this is fatal */
					break;
				}
				else if(mesbuf.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.mstsx0&RLL)
				{
					state = SIO;
					bp->b_flags |= B_ERROR;
					break;
				}
				break;
			case 01:	/* Attention Condition		*/
				if(tsadr->tssr & OFL)
				{
					printf("\nMagtape %o offline\n",
						   minor(bp->b_dev)&0177);
					state = 0;	/* drive offline */
				}
				break;
			default:
				break;
		}
		if(tstab.b_errcnt > 4 || state == 0)
		{
			deverror(bp, tsadr->tssr, mesbuf.mstsx0);
			deverror(bp, mesbuf.mstsx1, mesbuf.mstsx2);
			deverror(bp, mesbuf.mstsx3, tstab.b_errcnt);
		}
		if(tstab.b_errcnt >= 10 || state == 0)
		{
			/* bad news. FATAL ERROR or No recovery */
			tstab.b_errcnt = 0;	/* clear error count	*/
			bp->b_flags |= B_ERROR; /* mark this buffer bad */
			ts_openf = -1;		/* say no more		*/
			if(state)
				tsinit(tsadr, 0);	/* init the drive. no rewind*/
			else
				tsinit(tsadr, 1);	/* init the drive. rewind */
			state = 0;		/* just to be sure	*/
		}
	}
	if((tstab.b_active & SREWND) == 0)
		tstab.b_active = 0;		/* say ok to do more	*/
	switch (state)
	{
		case SRETRY:			/* straight retry	*/
			break;
		case SIO:			/* update block # and	*/
		case SOK:			/* fall through SCOM	*/
			ts_blkno++;
		case SCOM:
			tstab.b_errcnt = 0;	/* no errors		*/
			tstab.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.msresid;   /* get residual
								count	*/
			iodone(bp);			/* return buffer */
			break;
		case SSEEK:
			ts_blkno = bp->b_blkno; 	/* update blk no */
			break;
		default:
			break;
	}
	tsstart();		/* start any pending I/O		*/
}

tsinit(addr, ini)
struct device *addr;
int ini;
{
	int ds, cnt;
	register struct device *tsadr;

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

	if((minor(dev) & 0177) < 8)	/* only if drive 0,		*/
					/* minor device 0 thru 7	*/
	{
		a = u.u_offset >>9;	/* grab the block offset	*/
		ts_blkno = a;		/* set current block		*/
		ts_nxrec = a+1; 	/* set next max block # 	*/
	}
}