#
/*
 * RP disk driver
 * Modified September 1977 - Peter C
 * for use with RP02 drive
 *	UKC System changes which apply to this file:
 *	see changes.h
 *
 *	ERRLOG
 *	ORDER_BLOCKS
 *	RAW_BUFFER_POOL
 *	ACTMON - activity monitor on disc - writes device name and block number
 *	sequences to rk1 in raw mode
 *	if desired define ACTMON as 040 - 
 *
 *	Modified 4/July/1979 - to cope with more than
 *	one physical drive
 *	Define ONEDRIVE if only one drive required
 *
 *	Modified 23-July-1979
 *	To map cylinders so that addressed cylinder 0 for the whole
 *	disc is in the centre of the disc
 *	Mapping is:
 *	Cylinder   0 -> 101 maps to 101 -> 202
 *	Cylinder 102 -> 202 maps to   0 -> 100
 *
 * 	Minor device number interpreted as three octal digits
 *		SDN
 *	S - if 1 then use special mapping otherwise use normal addressing
 *	D - physical device number
 *	N - offset in rpsizes table
 *
 *	Note that Raw interfaces will not work with these 'funny' mapping
 *	because mapping must be done on a block by block basis.
 */

#include "../param.h"
#include "../systm.h"
#include "../buf.h"
#include "../conf.h"
#include "../user.h"
#ifdef ERRLOG
#include "../discerr.h"
#endif
#ifdef UKC1134
#define ONEDRIVE
#endif

struct {
	int	rpds;
	int	rper;
	int	rpcs;
	int	rpwc;
	int	rpba;
	int	rpca;
	int	rpda;
	int	rpm1;
	int	rpm2;
	int	rpm3;
	int	rpsuca;
};

#define	RPADDR	0176710
#define CYLS	203
#define HCYL	CYLS/2
#define NRP	2
#ifdef ARCHIVE_SYSTEM
#define NRP	8
#endif
#ifndef ONEDRIVE
#define NDRVS	2
#endif

struct {
	char	*nblocks;
	int	cyloff;
} rp_sizes[] {
#ifndef ARCHIVE_SYSTEM
	40600,	0,		/* rp0 - cyl 0 thru 202 - entire disc */
	2600,	190,		/* rp01 -cyl 190 thru 202 - last section of disc */
#ifdef OLD_SYSTEM
	19000,	108,		/* rp1 - cyl 108 thru 202 */
	2600,	95,		/* rp2 - cyl 95 thru 107 */
	19000,	0,		/* rp3 - cyl 0 thru 94 */
	9600,	47,		/* rp4 - cyl 47 thru 94 - for compatability with old system */
	9400,	0,		/* rp5 - cyl 0 thru 46 */
#endif
#else
	5600,	0,		/* rp0 - cyl 0 thru 27 */
	5000,	28,		/* rp1 - cyl 28 thru 52 */
	5000,	53,		/* rp2 - cyl 53 thru 77 */
	5000,	78,		/* rp3 - cyl 78 thru 102 */
	5000,	103,		/* rp4 - cyl 103 thru 127 */
	5000,	128,		/* rp5 - cyl 128 thru 152 */
	5000,	153,		/* rp6 - cyl 153 thru 177 */
	5000,	178,		/* rp7 - cyl 178 thru 202 */
#endif
};


#ifndef ONEDRIVE
struct	devtab	rptab[NDRVS];
struct devtab *rp_act;		/* pointer to active drive */
#else
struct devtab rptab;
#endif
#ifndef RAW_BUFFER_POOL
struct	buf	rrpbuf;
#endif

#define	GO	01
#define	RESET	0
#define	HSEEK	014

#define	IENABLE	0100
#define	READY	0200

#define	SUFU	01000
#define	SUSU	02000
#define	SUSI	04000
#define	HNF	010000

/*
 * Use av_back to save track+sector,
 * b_resid for cylinder.
 */

#define	trksec	av_back
#define	cylin	b_resid
#ifndef ONEDRIVE
#define qlen d_active	/* d_active contains no of blocks for each drive */
#endif

/*
 *	ordering routine for rps
 * 	sort on cylinder number 
 */
rpcmp(b1, b2)
struct buf *b1, *b2;
{	register struct buf *a1, *a2;
	a1 = b1;
	a2 = b2;
	if(a1->cylin < a2-> cylin) return(-1);
	return(a1->cylin > a2-> cylin);
}

rpstrategy(abp)
struct buf *abp;
{
	register struct buf *bp;
	register unsigned p1, p2;

	bp = abp;
#ifdef RH70
	if(bp->b_flags&B_PHYS)
		mapalloc(bp);
#endif
	p1 = &rp_sizes[bp->b_dev.d_minor&07];
	if ((bp->b_dev.d_minor&07) >= NRP ||
	    bp->b_blkno >= p1->nblocks) {
		bp->b_flags =| B_ERROR;
		iodone(bp);
		return;
	}
	bp->cylin = p1->cyloff;
	p1 = bp->b_blkno;
	p2 = lrem(p1, 10);
	p1 = ldiv(p1, 10);
	bp->trksec = (p1%20)<<8 | p2;
	bp->cylin =+ p1/20;
	if(bp->b_dev.d_minor&0100)
	{	/* "Special" cylinder mapping */
		bp->cylin = ((p2 = bp->cylin) <= HCYL) ? p2 + HCYL: p2 - HCYL -1;
	}
	spl5();
#ifndef ONEDRIVE
	p1 = &rptab[(bp->b_dev.d_minor>>3)&07];
	p1->qlen++;
	bporder(bp, p1, rpcmp);
	if(rp_act == 0)
		rpstart();
#else
	bporder(bp, &rptab, rpcmp);
	if(rptab.d_active == 0)
		rpstart();
#endif
	spl0();
}

/*
 *	rpstart selects the next block to be transferred
 *	picks block on longest queue
 */
rpstart()
{
#ifndef ONEDRIVE
	register struct devtab *dv;
	register i, qmax=0;
	static lastdrv;	/* contains index of last drive used */

	for(i = 0; i < NDRVS; i++)
	{	if((++lastdrv) >= NDRVS) lastdrv = 0;
		dv = &rptab[lastdrv];
		if(dv->qlen > qmax)
		{	rp_act = dv;
			qmax = dv->qlen;
		}
	}
	if(qmax)
#else
	if(rptab.d_actf)
#endif
		rpio();
}

/*
 *	rpio does the I/O to the selected drive
 */
rpio()
{	register struct buf *bp;

#ifndef ONEDRIVE
	bp = rp_act->d_actf;
#else
	bp = rptab.d_actf;
	rptab.d_active++;
#endif
	bp->b_error = 0;
	RPADDR->rpda = bp->trksec;
	devstart(bp, &RPADDR->rpca, bp->cylin, (bp->b_dev.d_minor>>3)&07);
}

rpintr()
{
	register struct devtab *rpa;
	register struct buf *bp;
	register ctr;
#ifdef ERRLOG
	struct disc_err *de;
#endif

#ifndef ONEDRIVE
	if((rpa = rp_act) == 0)
		return;
#else
	if((rpa = &rptab)->d_active == 0)
		return;
	rpa->d_active = 0;
#endif
	bp = rpa->d_actf;
	if (RPADDR->rpcs < 0) {		/* error bit */
		deverror(bp, RPADDR->rper, RPADDR->rpds);
#ifdef ERRLOG
		if(rpa->d_errcnt == 0)
		{	de = superblk;
			de->rp_time = time;
			bcopy(&RPADDR->rpds, de->rp_stat, 7);
			de->rp_suca = RPADDR->rpsuca;
			de->rp_retry[((RPADDR->rpcs>>8)&07)]++;
			de->ds_mod++;
		}
#endif
		if(RPADDR->rpds & (SUFU|SUSI|HNF)) {
			RPADDR->rpcs.lobyte = HSEEK|GO;
			ctr = 0;
			while ((RPADDR->rpds&SUSU) && --ctr);
		}
		RPADDR->rpcs = RESET|GO;
		ctr = 0;
		while ((RPADDR->rpcs&READY) == 0 && --ctr);
		if (++rpa->d_errcnt <= 10) {
			rpio();
			return;
		}
		bp->b_flags =| B_ERROR;
	}
	rpa->d_errcnt = 0;
	rpa->d_actf = bp->av_forw;
#ifndef ONEDRIVE
	rpa->qlen--;
	rp_act = 0;
#endif
	bp->b_resid = RPADDR->rpwc;
	rpstart();
	iodone(bp);
}

rpread(dev)
{

	if(rpphys(dev))
#ifndef RAW_BUFFER_POOL
	physio(rpstrategy, &rrpbuf, dev, B_READ);
#endif
#ifdef RAW_BUFFER_POOL
	physio(rpstrategy, 0, dev, B_READ);
#endif
}

rpwrite(dev)
{

	if(rpphys(dev))
#ifndef RAW_BUFFER_POOL
	physio(rpstrategy, &rrpbuf, dev, B_WRITE);
#endif
#ifdef RAW_BUFFER_POOL
	physio(rpstrategy, 0, dev, B_WRITE);
#endif
}

rpphys(dev)
{
	register c;

	c = lshift(u.u_offset, -9);
	c =+ ldiv(u.u_count+511, 512);
	if(c > rp_sizes[dev.d_minor & 07].nblocks) {
		u.u_error = ENXIO;
		return(0);
	}
	return(1);
}
