File:  [MW Coherent from dump] / coherent / d / PS2_KERNEL / io.286 / st.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/* (-lgl
 * 	COHERENT Driver Kit Version 1.1.0
 * 	Copyright (c) 1982, 1990 by Mark Williams Company.
 * 	All rights reserved. May not be copied without permission.
 -lgl) */
/*
 * This is a driver for the
 * Archive SC-400 Series Tape Controller.
 */
#include	<sys/coherent.h>
#include	<sys/buf.h>
#include	<sys/con.h>
#include	<sys/const.h>
#include	<sys/devices.h>
#include	<sys/inode.h>
#include 	<sys/mtioctl.h>
#include	<sys/sched.h>
#include	<sys/seg.h>
#include	<sys/stat.h>
#include	<errno.h>

/*
 * Fixed parameters.
 */
#define	NCMDS	8			/* Max # chained commands */

/*
 * Configurable parameters
 */
int	STIRQ	= 3;			/* IRQ Level 3 */
int	STPORT	= 0x200;		/* I/O Port    */
int	STDMA	= 1;			/* DMA Channel */

#define	BIT(n)		(1 << (n))

/*
 * Forward referenced functions.
 */
void	stcache();
void	stflush();
void	stinvoke();
void	ststart();
void	stintr();
void	strecov();
void	stnext();
void	stdiag();
void	stspin();

/*
 * Driver configuration.
 */
int	stload();
int	stuload();
int	stopen();
int	stclose();
int	stread();
int	stwrite();
int	stioctl();
void	stwatch();
int	nulldev();
int	nonedev();

CON	stcon	= {
	DFCHR,				/* Flags	*/
	ST_MAJOR,				/* Major index	*/
	stopen,				/* Open		*/
	stclose,			/* Close	*/
	nonedev,			/* Block	*/
	stread,				/* Read		*/
	stwrite,			/* Write	*/
	stioctl,			/* Ioctl	*/
	nulldev,			/* Powerfail	*/
	stwatch,			/* Timeout	*/
	stload,				/* Load		*/
	stuload				/* Unload	*/
};

/*
 * I/O Port Addresses
 */
#define	DATA_REG	(STPORT+0)	/* Data register */
#define	CTRL_REG	(STPORT+1)	/* Control/Status register */
#define	DMAGO_REG	(STPORT+2)	/* DMA Go register */
#define	DMARST_REG	(STPORT+3)	/* DMA reset register */

/*
 * Control Register
 */
#define	CR_RSTSAC	BIT(7)		/* 1 -> reset control micro	*/
#define	CR_REQ		BIT(6)		/* 1 -> request to LSI chip	*/
#define	CR_IEN		BIT(5)		/* 1 -> enables interrupts	*/
#define	CR_DNIEN	BIT(4)		/* 1 -> enable DONE interrupts	*/

/*
 * Status Register
 */
#define	SR_IRQF		BIT(7)		/* 1 -> Interrupt Request Flag	*/
#define	SR_NRDY		BIT(6)		/* 0 -> Ready			*/
#define	SR_NEXC		BIT(5)		/* 0 -> Exception		*/
#define	SR_DONE		BIT(4)		/* 1 -> DMA Done		*/
#define	SR_TO_PC	BIT(3)		/* 1 -> Direction is to PC	*/

/*
 * Controller Commands.
 */
#define	CC_SELECT	0x01		/* Select Drive 0 		*/
#define	CC_LOCK		0x11		/* Select Drive 0 and Lock	*/
#define	CC_BOT		0x21		/* Rewind to beginning of tape	*/
#define	CC_ERASE	0x22		/* Completely erase cartridge	*/
#define	CC_TENSION	0x24		/* Wind tape to BOT, EOT, BOT	*/
#define	CC_AUTO		0x25		/* Select auto-initialization	*/
#define	CC_QIC11	0x26		/* Select QIC-11 media format	*/
#define	CC_QIC24	0x27		/* Select QIC-24 media format	*/
#define	CC_WRITE	0x40		/* Write to tape		*/
#define	CC_WFM		0x60		/* Write file mark		*/
#define	CC_READ		0x80		/* Read from tape		*/
#define	CC_RFM		0xA0		/* Skip past next file mark	*/
#define	CC_SENSE	0xC0		/* Read controller status	*/

/*
 * Sense Status Bytes 0 and 1.
 */
#define	SS0_FIL		BIT(0)		/* File Mark Detected		*/
#define	SS0_BNL		BIT(1)		/* Bad Block Not located	*/
#define	SS0_UDA		BIT(2)		/* Unrecoverable data error	*/
#define	SS0_EOM		BIT(3)		/* End of media			*/
#define	SS0_WRP		BIT(4)		/* Write Protected Cartridge	*/
#define	SS0_USL		BIT(5)		/* Unselected Drive		*/
#define	SS0_CNI		BIT(6)		/* Cartridge Not In Place	*/
#define	SS0_ERR		(SS0_BNL+SS0_UDA+SS0_USL+SS0_CNI)

#define	SS1_POR		BIT(0)		/* Power on Reset Occurred	*/
#define	SS1_BOM		BIT(3)		/* Beginning of media		*/
#define	SS1_MBD		BIT(4)		/* Marginal Block Detected	*/
#define	SS1_NDT		BIT(5)		/* No Data Detected		*/
#define	SS1_ILL		BIT(6)		/* Illegal Command		*/
#define	SS1_ERR		(SS1_NDT+SS1_ILL)

/*
 * Device States.
 */
#define	SDEAD	0		/* controller not found    */
#define	SIDLE	1		/* controller idle	   */
#define	SCMD	2		/* initiating command	   */
#define	SRUN	3		/* performing command	   */
#define	SRDWR	4		/* starting read/write	   */
#define	SBLOCK	5		/* performing read/write   */
#define	SBLEND	6		/* concluding block i/o	   */
#define	SSENSE	7		/* reading status bytes	   */
#define	SSDONE	8		/* concluding status sense */

/*
 * Driver State Information.
 */
struct st_s {
	int	st_state;
	int	st_mode;		/* IPR or IPW			*/
	int	st_iocmd;		/* CC_READ or CC_WRITE		*/
	int	st_cmd;			/* last command	executed	*/
	int	st_cmds[NCMDS];		/* list of chained commands	*/
	int	st_ncmds;		/* num of chained commands	*/
	int	st_iswr;
	int	st_wasio;
	int	st_iseof;
	int	st_error;
	paddr_t	st_paddr;
	fsize_t	st_resid;
	fsize_t	st_size;
	saddr_t	st_sel;
	SEG *	st_seg;
	char	st_status[6];
	int	st_nstat;
	int	st_rdys;		/* number of ready watchdogs	*/
	int	st_nlost;		/* number of lost interrupts	*/
} st;

/**
 *
 * void
 * stload()		-- initialize tape device
 *
 *	Action:	Reset tape controller and drive.
 *		Seize tape interrupt vector.
 *
 *	Note:	If the tape controller is present and operational,
 *		a interrupt will occur and set st.st_state to SIDLE.
 */
static
stload()
{
	/*
	 * Paranoia - Turn off DMA.
	 * Should already be turned off.
	 */
	dmaoff( STDMA );

	/*
	 * Reset tape controller and drive
	 */
	outb( CTRL_REG, CR_RSTSAC );

	/*
	 * Wait at least 25 microseconds
	 */
	stspin( 25 );

	/*
	 * Terminate reset condition
	 */
	outb( CTRL_REG, CR_IEN );

	/*
	 * Seize tape interrupt vector.
	 */
	setivec( STIRQ, &stintr );
}

/**
 *
 * stuload( dev )		-- Unload tape device.
 * dev_t dev;
 */
stuload( dev )
dev_t dev;
{
	/*
	 * Turn off DMA.
	 */
	dmaoff( STDMA );

	/*
	 * Release tape interrupt vector.
	 */
	clrivec( STIRQ );

	/*
	 * Disable tape interrupts.
	 */
	outb( CTRL_REG, 0 );
}

/**
 *
 * stopen( dev, mode )		-- open tape device
 * dev_t dev;
 * int mode;
 *
 *	Input:	dev  = tape device to be opened.
 *		mode = desired access mode.
 *
 *	Action:	Refuse access if tape drive does not exist or is in use.
 *		Refuse simultaneous read and write access.
 *		Refuse access if cartridge is not inserted in tape drive.
 *		Refuse write access to a write protected cartridge.
 *		Allocate tape cache.
 *		Initialize device state.
 *		Lock tape cartridge.
 */
static
stopen( dev, mode )
register dev_t	dev;
register int	mode;
{
	int s;

	/*
	 * Refuse access if no tape drive.
	 */
	if ( st.st_state == SDEAD ) {
		u.u_error = ENXIO;
		return;
	}

	/*
	 * Refuse access if tape drive is already open.
	 */
	if ( st.st_mode != 0 ) {
		u.u_error = EDBUSY;
		return;
	}

	/*
	 * Access must be read-only or write-only.
	 */
	if ( (mode != IPR) && (mode != IPW) ) {
		u.u_error = EINVAL;
		return;
	}

	/*
	 * Wait for tape drive to become idle.
	 */
	if ( stwait() < 0 ) {
		u.u_error = EINTR;
		return;
	}

	/*
	 * Initialize tape interface.
	 */
	s = sphi();
	outb( DMARST_REG, 0 );
	outb( CTRL_REG, CR_IEN );
	spl( s );

	/*
	 * Obtain tape status.
	 */
	stinvoke( CC_SENSE );

	/*
	 * Wait for tape status.
	 */
	if ( stwait() < 0 ) {
		u.u_error = EINTR;
		return;
	}

	/*
	 * Refuse access if no cartridge.
	 */
	if ( st.st_status[0] & (SS0_CNI|SS0_USL) ) {
		u.u_error = EDATTN;
		return;
	}

	/*
	 * Refuse write access to a write protected cartridge.
	 */
	if ( (mode == IPW) && (st.st_status[0] & SS0_WRP) ) {
		u.u_error = EROFS;
		return;
	}

	/*
	 * Calculate desired cache size in Kbytes.
	 */
	st.st_size = minor(dev) & ~0x80;
	if ( st.st_size == 0 )
		st.st_size = 256;

	/*
	 * Allocate cache
	 */
	for ( st.st_size *= 1024; st.st_size != 0; st.st_size -= 1024 )
		if ( st.st_seg = salloc( st.st_size, SFSYST|SFNSWP|SFNCLR ) )
			break;

	/*
	 * Refuse access if couldn't allocate cache.
	 */
	if ( st.st_seg == 0 ) {
		u.u_error = ENOMEM;
		return;
	};

	/*
	 * Initialize device state.
	 */
	st.st_sel   = FP_SEL(st.st_seg->s_faddr);
	st.st_iswr  = (mode == IPW);
	st.st_paddr = st.st_seg->s_paddr;
	st.st_resid = (mode == IPW) ? st.st_size : 0 ;
	st.st_iocmd = (mode == IPW) ? CC_WRITE : CC_READ ;
	st.st_mode  = mode;
	st.st_iseof = 0;
	st.st_wasio = 0;
	st.st_error = 0;
	st.st_rdys  = 0;
	st.st_nlost = 0;

	/*
	 * Lock cartridge if at beginning of media.
	 */
	if ( st.st_status[1] & SS1_BOM )
		stinvoke( CC_LOCK );
}

/**
 *
 * stclose( dev, mode )		-- close tape device
 * dev_t dev;
 * int mode;
 *
 *	Input:	dev  = tape device to be closed.
 *		mode = access mode.
 *
 *	Action:	If access mode was for writing, flush the tape cache.
 *		If data was written to tape, write a file mark.
 *		If data was read from tape on the non rewinding device,
 *		read until end of file or an error is encountered.
 *		Rewind the tape if the rewinding device is open.
 *		Unlock the tape cartridge.
 *		Clear tape state and release tape cache memory.
 */
static
stclose( dev, mode )
register dev_t dev;
{
	/*
	 * Check if tape was opened for writing.
	 */
	if ( st.st_iswr ) {

		/*
		 * Flush the tape cache.
		 */
		stflush();

		/*
		 * Write a file mark if data was written to tape.
		 */
		if ( st.st_wasio )
			stinvoke( CC_WFM );
	}

	/*
	 * Check if non-rewinding device was opened for reading.
	 */
	else if ( st.st_wasio && (dev & 0x80 ) ) {

		/*
		 * Read file mark if not just past one.
		 */
		if ( (st.st_status[0] & SS0_FIL) == 0 )
			stinvoke( CC_RFM );
	}

	/*
	 * Rewinding device.
	 */
	if ( (dev & 0x80) == 0 ) {

		/*
		 * Wait for controller to idle.
		 */
		while ( stwait() < 0 )
			;

		/*
		 * Initiate rewind.
		 */
		stinvoke( CC_BOT   );

		/*
		 * Unlock the drive [turn off the light].
		 */
		stinvoke( CC_SELECT );
	}

	/*
	 * Clear tape state, releasing tape cache.
	 */
	sfree( st.st_seg );
	st.st_seg  = 0;
	st.st_mode = 0;
}

/**
 *
 * stread( dev, iop )	-- tape device read
 * dev_t dev;
 * IO * iop;
 *
 *	Input:	dev = tape device to be read from.
 *		iop = pointer to IO structure.
 *
 *	Action:	Transfer data from tape cache to user memory,
 *		filling the cache as required by initiating reads from tape.
 */

static
stread( dev, iop )
dev_t	dev;
register IO * iop;
{
	register int n;
	register int ioc;

	ioc = iop->io_ioc;
	
	while ( iop->io_ioc > 0 ) {

		/*
		 * Check for empty cache.
		 */
		while ( st.st_resid == 0 ) {

			/*
			 * Special handling if end of file was encountered.
			 */
			if ( st.st_iseof ) {

				/*
				 * Clear EOF if no data was transferred yet.
				 */
				if ( ioc == iop->io_ioc )
					st.st_iseof = 0;

				return;
			}

			/*
			 * Abort on I/O error.
			 */
			if ( u.u_error = st.st_error ) {
				stdiag();
				return;
			}

			/*
			 * Fill the cache from tape.
			 */
			stcache();
		}

		/*
		 * Determine max data transferable in one chunk.
		 */
		n = iop->io_ioc;
		if ( n > st.st_resid )
			n = st.st_resid;

		/*
		 * Transfer some data from cache to user memory.
		 */
		if ( pucopy( st.st_paddr, iop->io_base, n ) != n )
			return;

		/*
		 * Update addresses and counts.
		 */
		iop->io_base += n;
		iop->io_ioc  -= n;
		st.st_resid  -= n;
		st.st_paddr  += n;
	}
}

/**
 *
 * stwrite( dev, iop )	-- write to tape device
 * dev_t dev;
 * IO * iop;
 *
 *	Input:	dev = tape device to be written to.
 *		iop = pointer to IO structure.
 *
 *	Action:	Transfer data from user memory to tape cache,
 *		flushing the cache as required by initiating writes to tape.
 */

static
stwrite( dev, iop )
dev_t	dev;
register IO *iop;
{
	register int n;

	while ( iop->io_ioc > 0 ) {

		/*
		 * Determine max data transferable in one chunk.
		 */
		n = iop->io_ioc;
		if ( n > st.st_resid )
			n = st.st_resid;

		/*
		 * Transfer some data from user memory to cache.
		 */
		if ( upcopy( iop->io_base, st.st_paddr, n ) != n )
			break;

		/*
		 * Update addresses and counts.
		 */
		iop->io_base += n;
		iop->io_ioc  -= n;
		st.st_paddr  += n;
		st.st_resid  -= n;

		/*
		 * Flush the cache to tape if full.
		 */
		if ( st.st_resid == 0 )
			stflush();

		/*
		 * Abort on I/O error.
		 */
		if ( u.u_error = st.st_error ) {
			stdiag();
			return;
		}
	}
}

/**
 *
 * stioctl( dev, cmd, arg )	-- service tape I/O control requests
 * int dev;
 * int cmd;
 * int arg;
 *
 *	Input:	dev = tape device to be serviced
 *		cmd = ioctl command
 *		arg = argument to ioctl command
 *
 *	Action:	Service tape I/O control request.
 */

static
stioctl( dev, cmd, arg )
{
	if ( st.st_iswr )
		stflush();

	st.st_error = EINVAL;

	switch ( cmd ) {

	case MTERASE:
		stinvoke( CC_ERASE );
		break;

	case MTTENSE:
		stinvoke( CC_TENSION );
		break;

	case MTREWIND:
		if ( st.st_iswr && st.st_wasio ) {
			stinvoke( CC_WFM );
			st.st_wasio = 0;
		}
		stinvoke( CC_BOT );
		break;

	case MTWEOF:
		if ( st.st_iswr ) {
			stinvoke( CC_WFM );
			st.st_wasio = 0;
		}
		break;

	case MTFSKIP:
		if ( ! st.st_iswr ) {
			if ( ! st.st_iseof )
				stinvoke( CC_RFM );
			st.st_iseof = 0;
			st.st_resid = 0;
		}
		break;
	}

	/*
	 * Record tape error code.
	 */
	u.u_error = st.st_error;
}

/**
 *
 * void
 * stcache()	-- read from tape into cache
 *
 *	Action:	Read as much data as possible into the tape cache.
 *		Set st.st_paddr to the cache address.
 *		Set st.st_resid to the number of data bytes in the cache.
 */
static void
stcache()
{
	/*
	 * Try to fill cache from tape.
	 */
	st.st_paddr = st.st_seg->s_paddr;
	st.st_resid = st.st_size;
	ststart();

	/*
	 * Update cache information.
	 */
	st.st_paddr = st.st_seg->s_paddr;
	st.st_resid = st.st_size - st.st_resid;

	/*
	 * Clear the cache on I/O error.
	 */
	if ( st.st_error )
		st.st_resid = 0;
}

/**
 *
 * void
 * stflush()	-- flush cache to tape
 *
 *	Action:	Ensure tape cache is block aligned.
 *		Write cache to the tape.
 *		Set st.st_paddr to the cache address.
 *		Set st.st_resid to the number of cache bytes available.
 */
static void
stflush()
{
	static char zc;

	/*
	 * Check for empty cache.
	 */
	if ( st.st_resid == st.st_size )
		return;

	/*
	 * Block align the cache.
	 */
	while ( st.st_resid % BSIZE ) {
		kpcopy( &zc, st.st_paddr, 1 );
		st.st_paddr++;
		st.st_resid--;
	}

	/*
	 * Flush the cache to tape.
	 */
	st.st_paddr = st.st_seg->s_paddr;
	st.st_resid = st.st_size - st.st_resid;
	ststart();

	/*
	 * Update cache information.
	 */
	st.st_paddr = st.st_seg->s_paddr;
	st.st_resid = st.st_size;
}

/**
 *
 * void
 * stinvoke()	-- start tape control operation
 *
 *	Action:	Initiate tape control operation.
 */
static void
stinvoke( cmd )
int cmd;
{
	register int s;

	/*
	 * Disable interrupts.
	 */
	s = sphi();

	/*
	 * Wait for controller to become idle.
	 */
	while ( st.st_state != SIDLE ) {

		/*
		 * Create chained command if possible.
		 */
		if ( st.st_ncmds < NCMDS ) {
			st.st_cmds[ st.st_ncmds++ ] = cmd;
			spl( s );
			return;
		}

		sleep( &st, CVTTOUT, IVTTOUT, SVTTOUT );
	}

	/*
	 * Setup for tape operation.
	 */
	drvl[ST_MAJOR].d_time = 1;
	st.st_state = SCMD;
	st.st_error = 0;
	st.st_rdys  = 0;
	stspin( 100 );

	/*
	 * Request tape operation.
	 * Do NOT wait for results.
	 */
	outb( DATA_REG, st.st_cmd = cmd );
	outb( CTRL_REG, CR_IEN+CR_REQ );

	/*
	 * Enable interrupts.
	 */
	spl( s );
}

/**
 *
 * void
 * ststart()	-- start tape read/write operation
 *
 *	Action:	Initiate tape read/write operation.
 *		Wait for tape operation to complete.
 */
static void
ststart()
{
	register int s;

	/*
	 * Disable interrupts.
	 */
	s = sphi();

	/*
	 * Wait for controller to become idle.
	 */
	while ( st.st_state != SIDLE )
		sleep( &st, CVTTOUT, IVTTOUT, SVTTOUT );

	/*
	 * Setup for tape read/write.
	 */
	drvl[ST_MAJOR].d_time = 1;
	st.st_state = SRDWR;
	st.st_error = 0;
	st.st_rdys  = 0;
	stspin( 100 );

	/*
	 * Tape read/write was last command executed.
	 */
	if ( st.st_cmd == st.st_iocmd ) {
		/*
		 * Resume tape i/o operation.
		 * Simulate RDY interrupt.
		 */
		stintr();
	}
	else {
		/*
		 * Request tape operation.
		 */
		outb( DATA_REG, st.st_cmd = st.st_iocmd );
		outb( CTRL_REG, CR_IEN+CR_REQ );
	}

	/*
	 * Wait for tape operation to complete.
	 */
	while ( st.st_state != SIDLE )
		sleep( &st, CVTTOUT, IVTTOUT, SVTTOUT );

	/*
	 * Enable interrupts.
	 */
	spl( s );
}

/**
 *
 * void
 * stintr()	-- tape interrupt handler
 *
 *	Action:	Service tape interrupts.
 *		Perform transitions to new tape states.
 *		Wake sleeping processes if appropriate.
 */
static void
stintr()
{
	register int csr;
	register int s;

	s   = sphi();
	csr = inb( CTRL_REG );

	/*
	 * Initiate exception recovery.
	 */
	if ( (csr & SR_NEXC) == 0 ) {
		strecov();
		spl( s );
		return;
	}

	/*
	 * Clear ready watchdog count.
	 */
	st.st_rdys = 0;

	/*
	 * Process normal operations.
	 */
	switch ( st.st_state ) {

	case SCMD:
		/*
		 * Command has been acknowledged.
		 * Wait for command completion.
		 */
		outb( CTRL_REG, CR_IEN );
 		st.st_state = (st.st_cmd == CC_SENSE) ? SSENSE : SRUN;
 		st.st_nstat = 0;
		break;

	case SRUN:
		/*
		 * Command has completed.
		 * Chain a sense status command if no other chained commands.
		 */
		if ( st.st_ncmds == 0 )
			st.st_cmds[ st.st_ncmds++ ] = CC_SENSE;

		/*
		 * Initiate next chained command.
		 */
		stnext();
		break;

	case SRDWR:
		/*
		 * Read/Write command had been acknowledged.
		 * Clear tape request, enable done interrupt.
		 */
		outb( CTRL_REG, CR_IEN+CR_DNIEN );

		/*
		 * Define direct memory access parameters.
		 */
		dmaon( STDMA, st.st_paddr, BSIZE, st.st_iswr );

		/*
		 * If tape read command, wait for interface to switch direction
		 */
		if ( st.st_iocmd == CC_READ )
			while ( (inb(CTRL_REG) & SR_TO_PC) != SR_TO_PC )
				;

		/*
		 * Enable DMA transfer on tape interface and at DMA controller chip.
		 */
		st.st_state = SBLOCK;
		outb( DMAGO_REG, 0 );
		dmago( STDMA );
		break;

	case SBLOCK:
		/*
		 * Perform Block I/O.
		 * Ignore RDY interrupt, wait for [DMA] DONE interrupt.
		 */
		if ( (csr & SR_DONE) == 0 )
			break;

		/*
		 * Turn off DMA.
		 */
		dmaoff( STDMA );

		/*
		 * If more data remains to be transferred, reenable DMA.
		 * NOTE: do -= BEFORE if() to avoid potential compiler bug.
		 */
		st.st_resid -= BSIZE;
		if ( st.st_resid > 0 ) {
			st.st_paddr += BSIZE;
			dmaon( STDMA, st.st_paddr, BSIZE, st.st_iswr );
			outb( DMAGO_REG, 0 );
			dmago( STDMA );
			break;
		}

		/*
		 * Disable done interrupt.
		 * Wait for I/O completion.
		 */
		outb( CTRL_REG, CR_IEN );
		st.st_state = SBLEND;
		break;

	case SBLEND:
		/*
		 * Completion of Block I/O.
		 * Clear the file mark and beginning of media indicators.
		 * Record the fact that data has been transferred.
		 */
		st.st_status[0] &= ~SS0_FIL;
		st.st_status[1] &= ~SS1_BOM;
		st.st_wasio = 1;
		stnext();
		break;

	case SSENSE:
		/*
		 * Sense Status Byte.
		 * Wait for availability.
		 */
		do {
			csr = inb(CTRL_REG) & (SR_NRDY|SR_TO_PC);
		} while ( csr != SR_TO_PC );

		/*
		 * Save status byte.
		 */
		st.st_status[st.st_nstat] = inb(DATA_REG);

		/*
		 * Acknowledge reception.
		 * CR_REQ must be present for at least 20 microseconds.
		 */
		outb( CTRL_REG, CR_IEN+CR_REQ );
		stspin( 20 );
		outb( CTRL_REG, CR_IEN );

		/*
		 * Change state to status completion if all bytes saved.
		 */
		if ( ++(st.st_nstat) == 6 )
			st.st_state = SSDONE;
		break;

	case SSDONE:
		/*
		 * Completion of Sense Status Command.
		 * Check for file mark.
		 */
		if ( st.st_status[0] & SS0_FIL ) {
			outb( DMARST_REG, 0 );
			st.st_iseof = 1;
		}

		/*
		 * Check for I/O error.
		 */
		else if ( (st.st_status[0] & SS0_ERR) ||
			  (st.st_status[1] & SS1_ERR) ) {
			st.st_error = EIO;
		}

		/*
		 * Check for write protected cartridge.
		 */
		else if ( (st.st_iocmd == CC_WRITE) &&
			  (st.st_status[0] & SS0_WRP) ) {
			st.st_error = EROFS;
		}

		stnext();
		break;
	}

	spl( s );
}

/**
 *
 * void
 * strecov()	-- initiate recovery from exception conditions
 *
 *	Action:	Invoked when the tape controller asserts EXCEPTION.
 *		A sense status command is initiated to clear the exception.
 */
static void
strecov()
{
	/*
	 * Ensure tape interface is idle.
	 */
	outb( CTRL_REG, CR_IEN );
	stspin( 100 );

	/*
	 * Turn off DMA on read/write exception.
	 */
	if ( st.st_cmd == st.st_iocmd )
		dmaoff( STDMA );

	/*
	 * Initiate sense status command.
	 */
	outb( DATA_REG, st.st_cmd = CC_SENSE );
	outb( CTRL_REG, CR_IEN+CR_REQ );
	drvl[ST_MAJOR].d_time = 1;
	st.st_state = SCMD;
	st.st_error = 0;
	st.st_rdys  = 0;
}

/**
 *
 * static void
 * stnext()	-- initiate next chained command.
 */
static void
stnext()
{
	/*
	 * Ensure tape interface is idle.
	 */
	outb( CTRL_REG, CR_IEN );
	drvl[ST_MAJOR].d_time = 0;
	st.st_state = SIDLE;
	stspin( 100 );

	/*
	 * Initiate a chained command.
	 */
	if ( st.st_ncmds ) {
		outb( DATA_REG, st.st_cmd = st.st_cmds[ --st.st_ncmds ] );
		outb( CTRL_REG, CR_IEN+CR_REQ );
		drvl[ST_MAJOR].d_time = 1;
		st.st_state = SCMD;
		st.st_error = 0;
		st.st_rdys  = 0;
		return;
	}

	/*
	 * Wake waiting processes.
	 */
	wakeup( &st );
}

/**
 *
 * void
 * stwatch()	-- periodic [1 sec] watchdog
 *
 *	Action:	If an exception condition exists, initate recovery actions.
 *		If ready condition exists for 1-2 seconds, simulate interrupt.
 *
 *	Notes:	If an exception condition occurs after a ready interrupt has
 *		been serviced, but before the ready condition is cleared,
 *		the exception interrupt will not occur, and is simulated here.
 */
static void
stwatch()
{
	register int csr;
	register int s;

	/*
	 * Disable interrupts, preventing critical race with stintr().
	 */
	s   = sphi();
	csr = inb(CTRL_REG);

	/*
	 * Initiate recovery from exception conditions.
	 */
	if ( (csr & SR_NEXC) == 0 )
		strecov();

	/*
	 * Reset ready watchdog if not ready.
	 */
	else if ( csr & SR_NRDY ) 
		st.st_rdys = 0;

	/*
	 * Simulate lost ready interrupts after 2 seconds.
	 */
	else if ( ++st.st_rdys >= 2 )
		stintr();

	/*
	 * Enable interrupts.
	 */
	spl( s );
}

/**
 * 
 * void
 * stdiag()	- Report tape status.
 *
 *	Action:	Identify and report the highest priority tape error.
 *		There will normally only be one valid error present.
 *		The USL error can invalidate most remaining flags.
 *		The CNI error can invalidate cartridge related flags.
 *
 *	Notes:	Never called from interrupt level, but always from background.
 */
static void
stdiag()
{
	if ( st.st_status[0] & SS0_USL )
		printf( "st: Unselected Drive\n" );

	else if ( st.st_status[0] & SS0_CNI )
		printf( "st: Cartridge missing\n" );

	else if ( st.st_status[1] & SS1_NDT )
		printf( "st: No data detected\n" );

	else if ( st.st_status[0] & SS0_BNL )
		printf( "st: Bad block not located\n" );

	else if ( st.st_status[0] & SS0_UDA )
		printf( "st: Unrecoverable data error\n" );

	else if ( st.st_status[1] & SS1_ILL )
		printf( "st: Illegal command\n" );

	else
		printf( "st: %x\n", (st.st_status[1] << 8) + st.st_status[0] );
}

/**
 *
 * int
 * stwait()	-- wait for tape controller to idle.
 *
 *	Return:	0  = tape controller idle.
 *		-1 = signal received.
 */
static int
stwait()
{
	int s;

	s = sphi();
	while ( st.st_state != SIDLE ) {

		sleep( &st, CVTTOUT, IVTTOUT, SVTTOUT );

		if ( SELF->p_ssig ) {
			spl( s );
			return -1;
		}
	}
	spl( s );

	return 0;
}

/**
 *
 * void
 * stspin( usec )	-- delay execution
 * int usec;
 *
 *	Input:	usec = number of micro-seconds to delay.
 *
 *	Action:	Wait at least 'usec' micro-seconds.
 *
 *	Notes:	Provides minimum delay required at times by tape controller.
 *		Should function properly up to at least 16 Mhz system clock.
 */

static void
stspin( usec )
register int usec;
{
	while ( --usec >= 0 )
		;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.