#
/*
 */

/*
 *  RL disk driver
 */

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

#define NRL01BLK 10240
#define NRL02BLK 20480
#define RLCYLSZ 10240
#define RLSECSZ 256
#define	NDRV 4

#define RESET 013
#define STAT 03
#define GETSTAT 04
#define WCOM 012
#define RCOM 014
#define SEEK 06
#define SEEKHI 5
#define SEEKLO 1
#define RDHDR 010
#define IENABLE 0100
#define CRDY 0200
#define OPI 02000
#define CRCERR 04000
#define TIMOUT 010000
#define NXM 020000
#define DE  040000

struct {
	int rlcs;
	int rlba;
	int rlda;
	int rlmp;
};

#define RLADDR	0174400
#define RL_CNT 1
struct	buf	rrlbuf;
struct	buf	rltab;

struct {
	int	cn[4];		/* location of heads for each drive + 1 */
	int	blks[4];	/* number of blocks */
	int	dn;		/* drive number */
	int	com;		/* read or write command word */
	int	chn;		/* cylinder and head number */
	int	bleft;		/* bytes left to be transferred */
	int	bpart;		/* number of bytes transferred */
	int	sn;		/* sector number */
	int	addr[2];	/* address of memory for transfer */
} rl;

rlstrategy(bp)
struct buf *bp;
{
	register *rp;
	register d;
	register c;

	if (bp->b_flags & B_PHYS)
		mapalloc(bp);
	rp = RLADDR;
	d = bp->b_dev.d_minor;
 	if (NDRV <= d)
		goto err;
	if (rl.blks[d] == 0) {
		for (c = 0; c < 8; c++) {
			rp->rlda = RESET;
			rp->rlcs = (d << 8) | GETSTAT;
			while ((rp->rlcs & CRDY) == 0)
				;
			if ((rp->rlmp & 0157477) == 035)
				break;
		}
		if (c == 8) {
			goto err;
		}
		if (rp->rlmp & 0200) {
			rl.blks[d] = NRL02BLK;
		} else {
			rl.blks[d] = NRL01BLK;
		}
	}
	if (bp->b_blkno >= rl.blks[d]) {
		goto err;
	}
	bp->av_forw = NULL;
	spl5();
	if (rltab.d_actf == NULL)
		rltab.d_actf = bp;
	else
		rltab.d_actl->av_forw = bp;
	rltab.d_actl = bp;
	if (rltab.d_active == NULL)
		rlstart();
	spl0();
	return;
err:
	bp->b_flags =| B_ERROR;
	iodone(bp);
	return;
}

rlstart()
{

	register struct buf *bp;

	if ((bp = rltab.d_actf) == NULL)
		return;
	rltab.d_active++;
	rl.dn = bp->b_dev.d_minor;
	rl.chn = bp->b_blkno / 20;
	rl.sn = (bp->b_blkno % 20) << 1;
	rl.bleft = -bp->b_wcount << 1;
	rl.addr[0] = bp->b_xmem;
	rl.addr[1] = bp->b_addr;
	rl.com = (rl.dn << 8) | IENABLE;
	if (bp->b_flags & B_READ)
		rl.com =| RCOM;
	else
		rl.com =| WCOM;
	rlio();
}

rlintr()
{
	register struct buf *bp;
	register *rp;
	register int status;

	rp = RLADDR;
	if (rltab.d_active == NULL) {
/*
		logstray(rp);
*/
		return;
	}
	bp = rltab.d_actf;
	if (rp->rlcs < 0) {		/* error bit */
		if (rp->rlcs & 036000) {
			if (rltab.d_errcnt > 2)
				deverror(bp, rp->rlcs, rp->rlda);
		}
		if (rp->rlcs & 040000) {
			rp->rlda = STAT;
			rp->rlcs = (rl.dn << 8) | GETSTAT;
			while ((rp->rlcs & CRDY) == 0)
				;
			status = rp->rlmp;
			if (rltab.d_errcnt > 2)
				deverror(bp, status, rp->rlda);
			rp->rlda = RESET;
			rp->rlcs = (rl.dn << 8) | GETSTAT;
			while ((rp->rlcs & CRDY) == 0)
				;
			if(status & 01000) {
				rlstart();
				return;
			}
		}
		if (++rltab.d_errcnt <= 10) {
			rl.cn[rl.dn] = 0;
			rlstart();
			return;
		}
		else {
			bp->b_flags =| B_ERROR;
			rl.bpart = rl.bleft;
		}
	}

	if ((rl.bleft =- rl.bpart) > 0) {
		dpadd(rl.addr, rl.bpart);
		rl.sn=0;
		rl.chn++;
		rlio();
		return;
	}
	rltab.d_active = NULL;
	rltab.d_errcnt = 0;
	rltab.d_actf = bp->av_forw;
	bp->b_resid = 0;
	iodone(bp);
	rlstart();
}

rlio()
{

	register *rp;
	register dif;
	register int head;

	rp = RLADDR;
	head = rl.bpart >> 6;
	if (rl.cn[rl.dn] == 0) {
		rp->rlcs = (rl.dn << 8) | RDHDR;
		while ((rp->rlcs & CRDY) == 0)
			;
		rl.cn[rl.dn] = ((rp->rlmp & 077700) >> 6) + 1;
	}
	dif = ((rl.cn[rl.dn] - 1) >> 1) - (rl.chn >> 1);
	head = (rl.chn & 1) << 4;
	if (dif < 0)
		rp->rlda = (-dif <<7 ) | SEEKHI | head;
	else
		rp->rlda = (dif << 7) | SEEKLO | head;
	rp->rlcs = (rl.dn << 8) | SEEK;
	rl.cn[rl.dn] = rl.chn + 1;
	if (rl.bleft < (rl.bpart = RLCYLSZ - (rl.sn * RLSECSZ)))
		rl.bpart = rl.bleft;
	while ((rp->rlcs & CRDY) == 0)
		;
	rp->rlda = (rl.chn << 6) | rl.sn;
	rp->rlba = rl.addr[1];
	rp->rlmp = -(rl.bpart >> 1);
	rp->rlcs = rl.com | (rl.addr[0] & 3) << 4;
}

rlread(dev)
{

	physio(rlstrategy, &rrlbuf, dev, B_READ);
}

rlwrite(dev)
{

	physio(rlstrategy, &rrlbuf, dev, B_WRITE);
}

