AUSAM/sys/dmr/unused/xy.c

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

#
/*
**	Optimised Xylogics Phoenix 211 Disk Controller Driver
**
**	Bugs & comments to:	Piers Lauder
**				Dept. of Computer Science
**				Sydney University
*/


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


#define	NDRV		1		/* Drives on controller */

/* (not	MANY_DRIVES)
#define	MANY_DRIVES			/* if NDRV > 1 (otherwise save some code)

/* (not	DIFFERENT_DRIVES)
#define	DIFFERENT_DRIVES		/* if physically different drives attched to controller */

/* (not	VOLUME_STATS)
#define	VOLUME_STATS			/* record disc usage by volume */

/* (not	DRIVE_STATS)
#define	DRIVE_STATS			/* record disc accesses by cylinder and queue size */

/*
** assignment of minor device bits
**
**	bits 030 are drives,
**	bits 007 are volumes.
*/
#define	NVOL		7		/* max allowable volumes per drive */

#define	INTLV		1		/* interleave factor for rotational optimisation */
#define	XYAGE		10		/* number of times i/o may be preempted */

#define	XYOPENPRI	10		/* sleep priority whilst waiting for drive ready */


#define	XYADDR		0164000		/* controller address */
#define	SPLXY		spl5		/* interrupt priority */

#include	"xy.h"			/* definitions of controller registers */

#include	"ok.h"			/* Okidata 3300 Disc Drive parameters */


/*
**	Structure to describe drive states and conditions
*/

struct	drive
{
	char		dr_state;	/* drive status */
	char		dr_qstate;	/* queue status */
	struct buf	*dr_qp;		/* head of i/o queue */
	int		dr_cyl;		/* current cylinder */
	char		dr_dir;		/* current direction of head sweep */
	char		dr_com;		/* last command to this drive */
	char		dr_errcnt;	/* count of errors on current i/o */
	char		dr_rtzcnt;	/* count of drive resets on current i/o */
	char		dr_id;		/* this drive number */
#	ifdef	DIFFERENT_DRIVES
	int		dr_trksecs;	/* sectors per track */
	int		dr_cylsecs;	/* sectors per cylinder */
#	endif	DIFFERENT_DRIVES
	unsigned	dr_errors[16];	/* drive errors per XYERR */
#	ifdef	DRIVE_STATS
	unsigned	dr_cylref[NOKCYL/8];	/* count of accesses to cylinder areas */
	unsigned	dr_qref[NBUF];	/* count of queue sizes */
	int		dr_qsize;	/* current queue size */
	unsigned	dr_rotopt;	/* count of rotational optimisations */
#	endif	DRIVE_STATS
}
	xydrv[NDRV]
#ifdef	DIFFERENT_DRIVES
{
  {	0,0,0,0,0,0,0,0,0,NOKSEC,NOKSEC*NOKHDS }
}
#endif	DIFFERENT_DRIVES
;


/** drive states **/

#define	DR_RDY		0		/* up and running */
#define	DR_UNR		1		/* unready */
#define	DR_ERR		2		/* hard error */


/** queue (i/o) states **/

#define	Q_EMPTY		0		/* guess */
#define	Q_WAIT		1		/* waiting for i/o */
#ifdef	MANY_DRIVES
#define	Q_SEEK		2		/* seeking */
#define	Q_RDY		3		/* on cylinder */
#else	MANY_DRIVES
#define	Q_RDY		Q_WAIT		/* do i/o immediately */
#endif	MANY_DRIVES
#define	Q_IO		4		/* i/o in progress */


/** sweep direction **/

#define	IN		0		/* increasing cylinder number */
#define	OUT		1		/* decreasing cylinder number */


/*
**	pointer to current drive doing i/o
*/

struct	drive	*xydp;


/*
**	Structure to map logical volumes onto drives
*/

struct	volume
{
	unsigned	v_blocks;	/* max. blocks this volume */
	int		v_cyloff;	/* 1st. cylinder address */
#	ifdef	VOLUME_STATS
	long		v_ref;		/* count of accesses to this volume */
#	endif	VOLUME_STATS
}
	xymap[NDRV][NVOL]
{
  {	/** Drive 1 -- Okidata 3300 **/
	 {	32256,   0	}	/* 0: 84 cylinders	*/
	,{	13824,  84	}	/* 1: 36		*/
	,{	 3840, 120	}	/* 2: 10		*/
	,{	14592, 130	}	/* 3: 38		*/
	,{	33408, 168	}	/* 4: 87		*/
	,{	32256, 255	}	/* 5: 84		*/
	/*     130176, 339		   1-1 mapping		*/
	,{      65536, 168	}	/* 6: 171 (max. volume)	*/
  }
};


/*
**	Error Recovery Strategy for read errors
*/

int	xyretrycm[]
{
	0			/* 3 in-place retrys */
	,02000			/* data late,  servo normal */
	,04000			/* data early, servo normal */
	,(SEEKINH|0400)		/* data normal, servo minus */	/* single sector only */
	,(SEEKINH|01000)	/* data normal, servo plus  */	/* single sector only */
	,(SEEKINH|02400)	/* data late,  servo minus  */	/* single sector only */
	,(SEEKINH|03000)	/* data late,  servo plus   */	/* single sector only */
	,(SEEKINH|04400)	/* data early, servo minus  */	/* single sector only */
	,(SEEKINH|05000)	/* data early, servo plus   */	/* single sector only */
};

#define	RECALTRYS	3	/* number of recalibrates to be attempted after RECAL error */


/*
**	Use b_scratch in the buffer header to hold cylinder address,
**	b_resid to hold i/o skip count,
**	av_forw to hold pointer to next buffer in i/o queue,
**	and av_back to hold unit_sector_head address.
*/

#define	b_next		av_forw		/* must be "struct buf *" */
#define	b_ush		av_back		/* 16 bits */
#define	b_age		b_resid		/* must be int */
#define	b_cyl		b_scratch	/* 16 bits */


/*
**	Raw buffer for physical i/o
*/

#ifdef	RAW_BUFFER_POOL
#define	XYRAWBUF	0
#else	RAW_BUFFER_POOL
struct	buf		xyrawbuf;
#define	XYRAWBUF	&xyrawbuf
#endif	RAW_BUFFER_POOL


/*
**	Device Table
*/

struct	devtab		xytab;




/*
**	Strategy routine
**
**	-- sort buffer into optimised i/o queue
*/

xystrategy( bp )
  register struct buf *bp;
{
	int			unit;
  {
	register unsigned	b;
	register struct volume	*vp;

	b = bp->b_dev.d_minor;
	unit = (b>>3)&7;
	b =& 7;
#	ifdef	MANY_DRIVES
	vp = &xymap[unit][b];
#	else	MANY_DRIVES
	vp = &xymap[0][b];
#	endif	MANY_DRIVES

	/*
	**	Check for validity of this request
	*/

	if ( unit >= NDRV || b >= NVOL || (b = bp->b_blkno) >= vp->v_blocks )
	{
bad:
		bp->b_flags =| B_ERROR;
		iodone( bp );
		return;
	}

	/*
	**	if raw i/o, check within limits
	*/

	if ( bp->b_flags & B_PHYS )
	{
		if ( (b+(-bp->b_wcount)/256) > vp->v_blocks )
			goto bad;
#		ifdef	UNIBUS_MAP
		mapalloc( bp );
#		endif	UNIBUS_MAP
	}

#	ifdef	VOLUME_STATS
	vp->v_ref++;
#	endif	VOLUME_STATS

	/*
	**	Calculate sector, cylinder, and disk address
	*/

#	ifdef	DIFFERENT_DRIVES
#	define	TRKSECS	trksecs
#	define	CYLSECS	cylsecs
#	else	DIFFERENT_DRIVES
#	define	TRKSECS	NOKSEC
#	define	CYLSECS	(NOKSEC*NOKHDS)
#	endif	DIFFERENT_DRIVES
	{
#		ifdef	DIFFERENT_DRIVES
		register	trksecs = xydrv[unit].dr_trksecs;
		register	cylsecs = xydrv[unit].dr_cylsecs;

#		endif	DIFFERENT_DRIVES
		bp->b_cyl = b / CYLSECS + vp->v_cyloff;
#		ifdef	MANY_DRIVES
		bp->b_ush = (unit << 12) | ((b % CYLSECS)/TRKSECS << 7) | (bp->b_sector = b % TRKSECS);
#		else	MANY_DRIVES
		bp->b_ush = ((b % CYLSECS)/TRKSECS << 7) | (bp->b_sector = b % TRKSECS);
#		endif	MANY_DRIVES
		bp->b_age = XYAGE;
	}
  }

  SPLXY();

  {
	register struct buf	*p1, *p2;
	int			d;

#	ifdef	MANY_DRIVES
	p2 = &xydrv[unit];
#	else	MANY_DRIVES
	p2 = xydrv;
#	endif	MANY_DRIVES

#	ifdef	DRIVE_STATS
	p2->dr_qref[p2->dr_qsize++]++;
	p2->dr_cylref[bp->b_cyl/8]++;
#	endif	DRIVE_STATS

	if ( (p1 = p2->dr_qp) == NULL )
	{
		/** Start up empty queue **/

		p2->dr_qp = bp;
		p2->dr_qstate = Q_WAIT;

		xystart();
	}
	else
	{
		/*
		**	Non-empty queue - determine where to place this request
		**	in the queue, taking into account current direction of
		**	head movement, and minimisation of head movement.
		*/

		d = p2->dr_dir;

		if ( p2 = p1->b_next )
			do
				if ( p2->b_age == 0 )
					p1 = p2;	/* skip this block overtaken too often */
			while
				( p2 = p2->b_next );

		for ( ; p2 = p1->b_next ; p1 = p2 )
		{
			if (	p1->b_cyl <= bp->b_cyl
			     && bp->b_cyl <= p2->b_cyl
			  ||	p1->b_cyl >= bp->b_cyl
			     && bp->b_cyl >= p2->b_cyl
			   )
			{
				for ( p1 = p2 ; (p2 = p1->b_next) && ( bp->b_cyl == p2->b_cyl ) ; p1 = p2 )
				{
					/** Cylinder match -- do rotational optimisation **/

#					ifdef	DRIVE_STATS
					xydrv[unit].dr_rotopt++;
#					endif	DRIVE_STATS

					if ( p2->b_sector > p1->b_sector )
					{
						if ( bp->b_sector > p1->b_sector + INTLV
						  && bp->b_sector < p2->b_sector - INTLV
						   )
							break;
					}
					else
						if ( bp->b_sector > p1->b_sector + INTLV
						  || bp->b_sector < p2->b_sector - INTLV
						   )
							break;
				}
				break;
			}
			else
				if ( d == IN )
				{
					if ( p2->b_cyl < p1->b_cyl )
						if ( bp->b_cyl > p1->b_cyl )
							break;
						else
							d = OUT;
				}
				else
					if ( p2->b_cyl > p1->b_cyl )
						if ( bp->b_cyl < p1->b_cyl )
							break;
						else
							d = IN;
		}

		p1->b_next = bp;

		if ( bp->b_next = p2 )
			do
				/** following blocks overtaken **/

				p2->b_age--;
			while
				( p2 = p2->b_next );
	}
  }

  spl0();
}




/*
#ifdef	MANY_DRIVES
**	Start seeks on all drives waiting that are not on cylinder
#endif	MANY_DRIVES
**	Start i/o on first drive ready on cylinder
*/

xystart()
{
	register struct drive	*dp = xydrv;
# ifdef	MANY_DRIVES
	register		x, i;
	static struct drive	*lastdrive xydrv;

  /*
  **	Start seeks on waiting queues
  */

  for ( ; dp < &xydrv[NDRV] ; dp++ )
	if ( (dp->dr_state == DR_RDY) && (dp->dr_qstate == Q_WAIT) )
		if ( dp->dr_cyl != dp->dr_qp->b_cyl )
		{
			/** Start this drive seeking **/

			xycommand( dp, XY_SEEK );
			dp->dr_qstate = Q_SEEK;
		}
		else
			dp->dr_qstate = Q_RDY;

  /*
  **	Attend to "seek done" status for other drives
  */

  if ( (x = XYSTAT) & ANYSKDN )
	for ( dp = xydrv, i = FSTSKDN ; dp < &xydrv[NDRV] ; i =<< 1, dp++ )
		if ( x & i )
		{
			dp->dr_qstate = Q_RDY;
			break;
		}

  /*
  **	"Round robin" poll of ready drives
  */

  for ( dp = lastdrive ; ((dp == &xydrv[NDRV-1]) ? (dp = xydrv) : ++dp) && (dp != lastdrive) ; )
# endif	MANY_DRIVES

	if ( (xydp == NULL) && (dp->dr_state == DR_RDY) && (dp->dr_qstate == Q_RDY) )
	{
		/** Start i/o on this drive **/

		xycommand( dp, (dp->dr_qp->b_flags & B_READ ? XY_READ : XY_WRITE) );
		dp->dr_qstate = Q_IO;
#		ifdef	MANY_DRIVES
		lastdrive = dp;
		break;
#		endif	MANY_DRIVES
	}
}




/*
**	Set up command in controller registers
*/

xycommand( dp, com )
  register struct drive *dp;
{
	register struct buf	*bp = dp->dr_qp;
  {
	register		x;

	if ( (x = com & XY_COM) == XY_READ || x == XY_WRITE )
		xydp = dp;	/** indicate controller busy on this i/o **/
	else
		xydp = NULL;

	dp->dr_com = x;
  }
  {
	register		*rp = &XYCYL;

	*rp = bp->b_cyl;				/* XYCYL  */
#	ifdef	MANY_DRIVES
	if ( xydp )
	{
#	endif	MANY_DRIVES
		*--rp = bp->b_wcount;			/* XYWCNT */
		*--rp = bp->b_addr;			/* XYCAR  */
#	ifdef	MANY_DRIVES
	}
	else
		--rp--;
#	endif	MANY_DRIVES
	*--rp = bp->b_ush;				/* XYUSH  */
	*--rp = (bp->b_xmem<<12)|com|INTEB|GO;		/* XYCSR  */
  }

	if ( bp->b_cyl > dp->dr_cyl )
		dp->dr_dir = IN;
	else
		if ( dp->dr_cyl > bp->b_cyl )
			dp->dr_dir = OUT;

	dp->dr_cyl = bp->b_cyl;
}




/*
**	Command completion interrupt handler
*/

xyintr()
{
	register struct drive	*dp;
	register struct buf	*bp;
	register unsigned	x;
	extern unsigned		xyerrors();

  if ( XYCSR > 0 )
  {
	/** Last command completed ok **/

	if ( dp = xydp )
	{
		/** I/O command completion **/

		bp = dp->dr_qp;

done:
		xydp = NULL;
		dp->dr_errcnt = 0;
		dp->dr_rtzcnt = 0;
		dp->dr_qstate = ( (dp->dr_qp = bp->b_next) != 0 );	/* Q_EMPTY or Q_WAIT */
#		ifdef	DRIVE_STATS
		dp->dr_qsize--;
#		endif	DRIVE_STATS

		xystart();

		bp->b_resid = 0;
		iodone( bp );
	}
	else
	{
		if ( (x = XYSTAT) & SEEKDON )
		{
			/** Seek complete **/

#			ifdef	MANY_DRIVES
			dp = &xydrv[x & SEEKID];
#			else	MANY_DRIVES
			dp = xydrv;
#			endif	MANY_DRIVES

			if ( dp->dr_state == DR_RDY )

				/** Normal seek complete **/

				dp->dr_qstate = Q_RDY;
			else
			{
				/** Drive error fixed or Drive come on line **/

				dp->dr_state = DR_RDY;
				if ( bp = dp->dr_qp )
					bp->b_flags =& ~B_ERROR;
			}
		}

		xystart();
	}
  }
  else
  {
	/*
	**	ERROR
	*/

#	ifdef	MANY_DRIVES
	dp = &xydrv[(XYUSH>>12)&3];
#	else	MANY_DRIVES
	dp = xydrv;
#	endif	MANY_DRIVES
	bp = dp->dr_qp;

	/*
	**	log errors and check for non-recoverable errors
	*/

	if ( (x = xyerrlog( dp->dr_errors )) == 0 )
	{
		/** Soft error recoverable **/

		/** CRC Error Correction ? **/

		if ( (x = dp->dr_errcnt++) < (((sizeof xyretrycm)/2)*3) )
		{
			/** In place retry from Error Recovery Strategy table **/

			if ( ((x = xyretrycm[x/3]) & SEEKINH)
			   && ((dp->dr_qstate != Q_IO) || ((-bp->b_wcount) > 256))
			   )
				goto error;	/* unsuitable strategy */

			xycommand( dp, dp->dr_com|x );
		}
		else
		{
			/** Retry Strategy exhausted -- recalibrate ? **/
error:
			if ( dp->dr_rtzcnt++ == 0 )
			{
				/** Recalibrate drive and try again **/

				dp->dr_errcnt = 0;
rtz:
				xycommand( dp, XY_RTZ );
				dp->dr_cyl = 0;
				dp->dr_qstate = Q_WAIT;
			}
			else
			{
				/** Too many errors, clear controller and give up on this i/o **/

				xyerrprt();	/* print out controller registers */

				XYCSR = XY_CLEAR|GO;

				bp->b_flags =| B_ERROR;
				goto done;
			}
		}
	}
	else
	{
		/*
		**	HARD error
		*/

		xyerrprt();	/* Print controller registers */

		bp->b_flags =| B_ERROR;

		if ( x & PROGERR )
			goto done;	/* Can't fix this one ! */

		if ( (dp->dr_com == XY_WRITE) && (x & REWRITE) )
		{
			/** Almost certainly a BUSERR **/
#			ifdef	RE_WRITE_FIX
			/** Must do a rewrite to avoid an unreadable sector **/

			register unsigned	y;

			x = bp->b_addr;	bp->b_addr = 0;
			y = bp->b_xmem;	bp->b_xmem = 0;

			xycommand( dp, XY_WRITE );

			bp->b_addr = x;
			bp->b_xmem = y;
#			else	RE_WRITE_FIX
			goto done;
#			endif	RE_WRITE_FIX
		}
		else
		{
			dp->dr_state = DR_ERR;

			if ( x & RECAL )
				if ( dp->dr_rtzcnt++ < RECALTRYS )
					/*
					** Attempt recalibration
					*/
					goto rtz;
				else
				{
					/** Drive has unsolvable problem **/
faultclear:
					xycommand( dp, XY_FLTCL );
					goto done;
				}
			else
				if ( x & DRVUNR )
				{
					/** Drive off-line **/

					extern char	*panicstring;

					dp->dr_state = DR_UNR;
					panicstring++;
					printf( "\nXY drive %d off line!\n", dp->dr_id );
					panicstring = 0;
				}
				else
					/*
					** Hard drive error
					*/
					if ( x & DRVFLT )
						goto faultclear;
					else
					{
						/** Clear controller **/

						XYCSR = XY_CLEAR|GO;
						goto done;
					}
		}
	}
  }
}




/*
**	Log errors and return any hard errors
*/

unsigned xyerrlog( ep )
  register unsigned *ep;
{
	register unsigned	e = XYERR;
	register unsigned	i = 1;

  do
	if ( e & i )
		if ( ++*ep == 0 )
			--*ep;
  while
	( ep++, i =<< 1 );

  return( e & ~RETRYER );
}




/*
**	Print out controller registers
*/

xyerrprt()
{
	register unsigned	*ep = XYADDR;

	printf( "\nXYERR=%o,ST=%o,CL=%o,WC=%o,BA=%o,DA=%o,CS=%o\n"
			 ,*ep,*ep++,*ep++,*ep++,*ep++,*ep++,*ep++	/* (stacked in reverse) */
	      );
}




/*
**	Physical I/O
*/

xyread( dev )
{
  physio( xystrategy, XYRAWBUF, dev, B_READ );
}

xywrite( dev )
{
  physio( xystrategy, XYRAWBUF, dev, B_WRITE );
}