AUSAM/sys106/dmr/tm.c

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

#
/*
 *	tm11 driver
 *
 *	minor devices 0-7  get rewound on close
 *	minor devices 8-15 don't
 *
 *	indutry standard 2 tape marks for EOF
 *
 *	stty functions for skip forward/reverse file/block
 *			   rewind
 *			   write tape mark
 *			   put drive off line.
 */


/*#define	ERRORS	01		/* set debug bit 0 to enable error reports */
/*#define	TRACE	02		/* set debug bit 1 to enable trace */
#ifdef	ERRORS | TRACE
int	tmdebug;			/* non-zero to debug */
#endif

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

/*
 * Structure of device registers
 */

#define	TMADDR	0172520		/* register base	*/
struct	{
	int	tmer;
	int	tmcs;
	int	tmbc;
	int	tmba;
	int	tmdb;
	int	tmrd;
};
#define	TMER	TMADDR->tmer
#define	TMCS	TMADDR->tmcs
#define	TMBC	TMADDR->tmbc
#define	TMBA	TMADDR->tmba
#define	TMDB	TMADDR->tmdb
#define	TMRD	TMADDR->tmrd

struct	devtab	tmtab;
#ifndef	RAW_BUFFER_POOL
struct	buf	rtmbuf;
#endif

#define	NTM11	3		/* drives on controller	*/
#define MANY_DRIVES		/* if NTM11 > 1		*/

/* tmcs */
#define	GO	01		/* hardware go bit 	*/
#define	RCOM	03		/* read command 	*/
#define	WCOM	05		/* write command	*/
#define	WIRG	015		/* write extended gap	*/
#define	IENABLE	0100		/* interrupt enable	*/
#define	CRDY	0200		/* controller ready	*/
#define	UNIT	03400		/* unit select		*/
#define	PCLR	010000		/* power clear		*/
#define	DENS	060000		/* 9-track 800 bpi	*/

/* tmrd */
#define	GAPSD	010000		/* gap slow down	*/

/* tmer */
#define	TUR	01		/* tape driver ready	*/
#define	HARD	0102200		/* hard error		*/
#define	EOF	0040000		/* end-of-file		*/
#define	RWS	02		/* rewind started	*/
#define	WRL	04		/* write locked		*/
#define	TAPSD	010		/* tape settle down	*/
#define	SELR	0100		/* tape unit on line	*/

/* driver states */
#define	SSEEK	1		/* positioning		*/
#define	SIO	2		/* read/write		*/
#define	SBSP	3		/* error recovery BS	*/
#define	SPHYS	4		/* stty functions	*/

/* special commands */
#define	SSFB	011		/* skip forward block	*/
#define	SSRB	013		/* skip reverse block	*/
#define	SSFF	001		/* skip forward file	*/
#define	SSRF	003		/* skip reverse file	*/
#define	SSREW	017		/* rewind on-line	*/
#define	SSWEOF	007		/* write eof		*/
#define	SSOFFL	020000		/* put drive off-line	*/
#define	SSWRL	040000		/* check tape write lock */
#define	SSONLN	0100000		/* check tape on-line	*/

/* drive states */
#define	OPEN	01		/* drive open		*/
#define	HARDE	02		/* drive hard error	*/
#define	LASTW	04		/* driver last command was write */


struct	{
	char	t_status[NTM11];
	unsigned t_blkno[NTM11];
	unsigned t_nxrec[NTM11];
} tm11;


/*
 * Open: check for legal drive number, and
 * already open drive.
 */

tmopen(dev, flag)
{
	register dminor;

	dminor = dev.d_minor & 07;
	if(dminor >= NTM11 || tm11.t_status[dminor]&OPEN)
		u.u_error = ENXIO;
	else 
	{
		tm11.t_status[dminor] = OPEN;
		tmcommand(dev, flag ? SSWRL|SSONLN : SSONLN);
		tm11.t_blkno[dminor] = 0;
		tm11.t_nxrec[dminor] = 0177777;	/* last block written set at 64k */
		if(u.u_error)
			tm11.t_status[dminor] = 0;
	}
}

/*
 * Close: if the last command on the drive was
 * a write, then close off with a WEOF
 */

tmclose(dev, flag)
{
	register dminor;


	dminor = dev.d_minor&07;

	if(tm11.t_status[dminor]&LASTW)  
	{
		tmcommand(dev, SSWEOF, -2);
		if ( dev.d_minor & 010 )
			tmcommand(dev, SSRB, -1);
	}

	if ( (dev.d_minor & 010) == 0 )
		tmcommand(dev, SSREW);

	tm11.t_status[dminor] = 0;
}

/*
 * Strategy: the tm supports three basically distinct types
 * of I/O. As a UNIX style block device, it behaves just like
 * a random access device, with the exception that writes
 * must be sequential, and invalidate what lies beyond them.
 * As a raw device, it can read and write records of any
 * blocksize. It also supports tape positioning functions:
 * skip [forward/reverse] [blocks/files], rewind, and weof.
 */

tmstrategy(bp)
register struct buf *bp;
{
	register unsigned *p;

	if((bp->b_flags&(B_NOTIO|B_PHYS))==0) 	/* i.e. block device */
	{
		p = &tm11.t_nxrec[bp->b_dev.d_minor&07];
		if(*p <= bp->b_blkno) 
		{
			if(*p < bp->b_blkno) 	/* attempt to read/write non-existent block */
			{
				bp->b_flags =| B_ERROR;
				iodone(bp);
				return;
			}

			if(bp->b_flags&B_READ) /* attempt to read block immediately beyond last block written */
			{
				clrbuf(bp);
				iodone(bp);
				return;
			}
		}

		if((bp->b_flags&B_READ)==0)
			*p = bp->b_blkno + 1;
	}

#	ifdef	_1170
	if(bp->b_flags&B_PHYS)
		mapalloc(bp);
#	endif

	bp->av_forw = NULL;

	spl5();

	if(tmtab.d_actf == NULL) 
	{
		tmtab.d_actf = bp;
		tmstart();
	}
	else 
	{
		tmtab.d_actl->av_forw = bp;
	}

	tmtab.d_actl = bp;

	spl0();
}


/*
 * Start: If the requested function is on a drive with
 * a hard error, shoot it down.  turn skip-files into
 * the skip-blocks that the hardware supports. Do positioning
 * for block-type i/o.  Write with extended IRG for
 * crudy tapes.
 */

tmstart()
{
	register struct buf *bp;
	register com;
	register blkno;
	unsigned unit;

loop:
	if((bp = tmtab.d_actf) == NULL)
		return;

	unit = bp->b_dev.d_minor & 07;

	if(tm11.t_status[unit]&HARDE) 
	{
		bp->b_flags =| B_ERROR;
deq:
		tmtab.d_actf = bp->av_forw;
		iodone(bp);
		goto loop;
	}

	tm11.t_status[unit] =& ~LASTW;

	com = (unit << 8) | ((bp->b_xmem&03) << 4) | IENABLE | DENS;

	if(bp->b_flags&B_NOTIO) 
	{
		tmtab.d_active = SPHYS;
		if ((blkno = bp->b_blkno) & (SSWRL|SSONLN)) 
		{
#			ifdef	MANY_DRIVES
			TMCS = (com & (UNIT|IENABLE));
			com = 100;
			do; while(--com);
#			endif	MANY_DRIVES

			if ( (com = TMER) & RWS )
				return;		/* await rewind completed */

			if ( (com&(TUR|SELR))!=(TUR|SELR) || ((com&WRL)&&(blkno&SSWRL)) )
				bp->b_flags =| B_ERROR;
			goto deq;
		}
		if ( blkno & SSOFFL )  
		{
			TMCS = com|GO;
			goto deq;
		}
		if(blkno == SSFF || blkno ==SSRF) 
		{
			com =| 010;
			TMBC = 0;
		}
		else
			TMBC = bp->b_wcount;

		if ( blkno == SSRF || blkno == SSRB )  
		{
			TMCS = (com&03400);
			while ( TMER & TAPSD );
		}

		TMCS = com | blkno;
	}
	else  
	{
		if((bp->b_flags&B_PHYS)==0 && (blkno = tm11.t_blkno[unit] - bp->b_blkno)) 
		{
			tmtab.d_active = SSEEK;

			if(blkno < 0) 
			{
				com =| SSFB;
			}
			else 
			{
				blkno = -blkno;
				if(bp->b_blkno == 0)
					com =| SSREW;
				else 
				{
					com =| SSRB;
				}
				while ( TMER & TAPSD );
			}

			TMBC = blkno;
		}
		else 
		{
			tmtab.d_active = SIO;

			TMBC = (bp->b_wcount << 1);
			TMBA = bp->b_addr;

			if ( bp->b_flags & B_READ )
				com =| RCOM;
			else  
			{
				tm11.t_status[unit] =| LASTW;
				if ( tmtab.d_errcnt )
					com =| WIRG;
				else
					com =| WCOM;
			}
		}

		TMCS = com;
	}
}


/* Interrupt: If there is nothing to do, just return.
 * Fix up soft errors on read and write, continue
 * processing of Skip files.
 * Note that physical limitations mean that a mag-tape
 * could not have more than about 50000 records .
 *
 *	rewritten - Piers Lauder	Jan '78
 */

#define	iotyp	error	

tmintr()
{
	register struct buf *bp;
	register unsigned unit, error;

#	ifdef	TRACE
	if ( tmdebug & TRACE )
		printf( "\nTMER %o CS %o BC %o BA %o DB %o RD %o IOTYP %d\n"
			      ,TMER, TMCS, TMBC, TMBA, TMDB, TMRD, tmtab.d_active
		      );
#	endif

  if ( (bp = tmtab.d_actf) == NULL )	/* nothing doing */
	return;

  unit = bp->b_dev.d_minor & 07;

  bp->b_resid = TMBC;

  if ( TMCS < 0 )	/* error */
  {
	error = TMER;
#	ifdef	ERRORS
	if ( tmdebug & ERRORS )
		printf( "\nTMER %o CS %o BC %o BA %o DB %o RD %o IOTYP %d\n"
			      ,error, TMCS, TMBC, TMBA, TMDB, TMRD, tmtab.d_active
		      );
#	endif

	/*while ( TMRD & GAPSD );*/

	if ( error & HARD )
		tm11.t_status[unit] =| HARDE;
	else
		if ( error & EOF )  
		{
			if ( tmtab.d_active == SPHYS )  
			{
				if (((error = bp->b_blkno) == SSFF) || (error == SSRF) || (error == SSWEOF))
					if ( ++bp->b_wcount < 0 )
					{
						TMBC = 0;
						TMCS =| GO;
						return;
					}
			}
			else
				if ( bp->b_flags & B_PHYS )
					bp->b_resid = bp->b_wcount;	/* indicate EOF by 0 bytes read */
				else
				{
					bp->b_flags =| B_ERROR;		/* indicate EOF by error */
					goto backsp;
				}

			goto done;
		}
		else
			if ((tmtab.d_active == SIO) && (++tmtab.d_errcnt < 10))  
			{
backsp:
				tmtab.d_active = SBSP;
				while ( TMER & TAPSD );
				TMBC = -1;
				TMCS = (unit << 8)|IENABLE|DENS|SSRB;
				if ( bp->b_flags & B_ERROR )
				{
					tmtab.d_actf = bp->av_forw;
					iodone( bp );
				}
				return;
			}

	bp->b_flags =| B_ERROR;
	tmtab.d_active = SIO;
  }

  if ((iotyp = tmtab.d_active) == SSEEK)
	tm11.t_blkno[unit] = bp->b_blkno;
  else
	if ( iotyp != SBSP )  
	{
		tm11.t_blkno[unit]++;
		if ( iotyp == SIO )
			bp->b_resid =>> 1;
done:
		tmtab.d_actf = bp->av_forw;

		iodone( bp );

		tmtab.d_errcnt = 0;
	}

  tmstart();
}


/*
 * TM stty: fetch code and count from user, and pass then
 * into raw buffer header. Obey buffer protocols.
 * Codes:
 *	0	Skip forward file
 *	1	Skip reverse file
 *	2	Clear ( super-users only )
 *	3	Write end-of-file
 *	4	Skip forward block
 *	5	Skip reverse block
 *	6	Drive off line
 *	7	Rewind
 */

tmsgtty(dev, v)
{
	register cnt, com;

	if ( v || (u.u_arg[0] & ~07) )
	{
		u.u_error = ENXIO;
		return;
	}
	com = ((u.u_arg[0] & 07) << 1) | GO;
	if(com == 05) 
	{
		if ( suser() )
			TMCS = PCLR;
		return;
	}
	if (com == 015)
		com = SSOFFL;
	if ((cnt = -u.u_arg[1]) >= 0)
		cnt = -1;
	u.u_ar0[R0] = tmcommand(dev, com, cnt);
}

/*
 *	queue a non-io type request to the tapedrive
 */

tmcommand(dev, com, cnt)
  register com, cnt;
{
	register struct buf *bp;

	if ( u.u_error )
		goto out;

#	ifndef	RAW_BUFFER_POOL
	bp = &rtmbuf;
	while(bp->b_flags&B_BUSY) 
	{
		bp->b_flags =| B_WANTED;
		sleep(bp, PRIBIO);
	}
#	else	RAW_BUFFER_POOL
	bp = getrb(dev , B_NOTIO);
#	endif	RAW_BUFFER_POOL

	bp->b_flags = B_BUSY | B_NOTIO;
	bp->b_dev = dev;
	bp->b_blkno = com;
	bp->b_wcount = cnt;

	tmstrategy(bp);

#	ifndef	RAW_BUFFER_POOL
	spl5();
#	else	RAW_BUFFER_POOL
	spl6();
#	endif	RAW_BUFFER_POOL

	while((bp->b_flags&B_DONE)==0)
		sleep(bp, PRIBIO);

#	ifndef	RAW_BUFFER_POOL
	if(bp->b_flags&B_WANTED)
		wakeup(bp);
	spl0();
	bp->b_flags =& ~(B_BUSY|B_WANTED);
#	else	RAW_BUFFER_POOL
	freerb(bp);
#	endif	RAW_BUFFER_POOL

	geterror(bp);

out:
	if(com == SSFB || com == SSRB)
		return(bp->b_resid);
	return(bp->b_wcount);
}

#ifndef	RAW_BUFFER_POOL
tmread(dev)
{
	physio(tmstrategy, &rtmbuf, dev, B_READ);
}

tmwrite(dev)
{
	physio(tmstrategy, &rtmbuf, dev, B_WRITE);
}
#else	RAW_BUFFER_POOL

tmread(dev)
{
	physio(tmstrategy, 0, dev, B_READ);
}

tmwrite(dev)
{
	physio(tmstrategy, 0, dev, B_WRITE);
}
#endif	RAW_BUFFER_POOL


#ifdef	POWER_FAIL
/*
 *	restart controller after a power restore
 */
tmpowerf()
{
	register char *cp;

  for ( cp=tm11.t_status ; cp < &tm11.t_status[NTM11] ; cp++ )
	*cp =| HARDE;

  tmstart();
}
#endif	POWER_FAIL