Ultrix-3.1/sys/dev/hp.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * SCCSID: @(#)hp.c	3.0	4/21/86
 */
/*
 * ULTRIX-11 General Massbus Disk Driver (hp.c).
 * Supports up to four RH11/RH70 massbus controllers,
 * (hp, hm, hl, hj). Each controller may have up to 8
 * drives of type: ML11, RP04/5/6, RM02/3/5.
 *
 * This driver contains what on the surface may appear
 * to be strangeness, HOWEVER it is really some very
 * carefully designed and tested code, which allows
 * the driver to handle dual ported drives and to recognize
 * when drives are taken off/on line via removing/replacing the LAP
 * (logical assignment plug). It also handles the case where the LAPs
 * are exchanged between unlike drive types, for example RM03 & RP06.
 * The LAP feature will not function properly for the ML11,
 * which has no LAP anyway !
 * The ML11 cannot be dual ported, GOOD !
 * WARNING !, testing of the dual port code was limited to
 * allowing the driver to work with dual ported drives having
 * the port switch in any of the three positions, A, B, or A/B.
 * Actual dynamic (A/B) operation with two systems will NOT work,
 * because the drive is treated as nonexistent if it is seized
 * by the controller on the other port.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/seg.h>
#include <sys/errlog.h>
#include <sys/devmaj.h>
#include <sys/bads.h>
#include <sys/hpbad.h>
#include <sys/hp_info.h>
#include <sys/uba.h>

char	hp_index[];	/* non-semetrical array index (see conf/rh.c) */
int	io_csr[];	/* (c.c) I/O page CSR address for each cntlr */
			/*       Index is HP_RMAJ + ctrl */
char	io_bae[];	/* (c.c) BAE register offset for each cntlr,	*/
			/*       or 0 if cntlr is an RH11		*/
			/*       Index is HP_BMAJ + ctrl		*/
char	nhp[];		/* (c.c) Number of units configured for each cntlr */
char	*hp_dct[] = {"HP", "HM", "HJ"};	/* cntlr type for error msg */

/*
 * Instrumentation (iostat) structures
 */
struct	ios	hp_ios[];
#define	DK_N	0

/*
 * Disk overlapped seek definitions
 */
#define	SDIST	2
#define	RDIST	6

/*
 * HP disk information structure
 *
 * WARNING!, the order and values of things in this
 * structure are hardwired into the HPX disk exerciser.
 * DO NOT change this table without checking HPX.
 * WARNING!, same deal with /usr/sys/sas/setup.c!
 */
#define	RP456	0
#define	RM23	1
#define	RM5	2
#define	ML	3

struct	hpdi {
	int	nsect;	/* sectors per track */
	int	ntrac;	/* tracks per cylinder */
	int	nbpc;	/* blocks per cylinder */
	int	sti;	/* sizes table index */
} hpdi[] = {
	22,	19,	22*19,	RP456*8,	/* RP04/5/6 */
	32,	5,	32*5,	RM23*8,		/* RM02/3 */
	32,	19,	32*19,	RM5*8,		/* RM05 */
	1,	1,	1,	ML*8,		/* ML11 */
};

/*
 * The size structures determine the starting cylinder
 * number and length in blocks of the disk partitions.
 * For the RM02/3/5 and RP04/5/6 disks, each entry describes
 * a partition, for the ML11 each entry contains the size in
 * blocks of that unit, the ML11 is not partitioned.
 * The size of the ML11 unit is determined by reading
 * the ML maintenance register the first time the drive
 * is accessed.
 * The last two tracks of each are reserved for the bad
 * block (sector) file and can only be accessed by the driver.
 */

/*
 * Sizes table now in /usr/sys/conf/dksizes.c
 */
extern	struct {
	daddr_t	nblocks;
	int	cyloff;
} hp_sizes[];

/*
 * Block device error log buffer, holds one
 * error log record until error retry sequence
 * has been completed.
 * One for each RH11/RH70 controller.
 */
struct	hp_ebuf	hp_ebuf[];

#define	P400	020
#define	M400	0220
#define	P800	040
#define	M800	0240
#define	P1200	060
#define	M1200	0260
int	hp_offset[16] =
{
	P400, M400, P400, M400,
	P800, M800, P800, M800,
	P1200, M1200, P1200, M1200,
	0, 0, 0, 0,
};

struct	buf	hptab[];	/* Head of I/O queue for each cntlr */
struct	buf	rhpbuf[];	/* RAW I/O buffer header for each cntlr */
struct	buf	hputab[];	/* Drive queue (overlap seek) one per unit */

/*
 * Drive types, required by HPX
 * 0	= nonexistent drive
 * otherwise equals the drive type.
 */
char	hp_dt[];	/* see conf/rh.c */

/*
 * Structures for bad block revectoring
 */
struct	hpbad hp_bads[];	/* Buffer for bad block file, one per unit */
struct	dkres hp_res[];		/* Bads revector and continue structure, */
				/* one per controller. */
struct	hp_badr	hp_r[];		/* Flags & base block, one per unit (hp_info.h) */

char	hp_openf[];	/* HP open flag, one per cntlr */

#define	GO	01
#define	PRESET	020
#define	RTC	016
#define	OFFSET	014
#define	SEARCH	030
#define	RECAL	06
#define DCLR	010
#define	DPRLS	012
#define	CCLR	040
#define	WCOM	060
#define	RCOM	070

#define	IE	0100
#define	NED	010000
#define	PIP	020000
#define	DRY	0200
#define	ERR	040000
#define	TRE	040000
#define	DCK	0100000
#define	WLE	04000
#define	ECH	0100
#define VV	0100
#define	DPR	0400
#define	MOL	010000
#define FMT22	010000

#define	b_cylin	b_resid

/*
 * On the first call to hpopen only,
 * determine the existence and type of all possible drives.
 * A dual ported drive that is locked on the opposite port
 * will be treated and nonexistent until it is unlocked.
 * This code assumes that the interrupt enable in HPCS1 is off.
 */

hpopen(dev)
{
	register struct hp_regs *hpaddr;
	register int ctrl, dn;
	int fs, ind, pri;

	ctrl = hpctrl(dev);
	if(hp_openf[ctrl])
		return;
	hp_openf[ctrl]++;
	hpaddr = io_csr[HP_RMAJ+ctrl];	/* Get RH11/RH70 CSR address (HPCS1) */
	for(dn=0; dn<nhp[ctrl]; dn++) {		/* look at all drives */
		ind = hp_index[ctrl] + dn;
		hpaddr->hpcs2.w = dn;	/* select drive */
		hpaddr->hpcs1.w = GO;	/* NOP, attempt seize dual port drive */
		hp_dt[ind] = hpaddr->hpdt & 0377;  /* save drive type, 0 = NED */
		if(hp_dt[ind] == 0)	/* clear the world if NED */
			hpaddr->hpcs2.w = CCLR;
/*
 * Find the size of the ML11 unit by reading the
 * number of array modules from the ML maintenance register.
 * There are 512 blocks per array.
 */
		if(hp_dt[ind] == ML11) {
			hp_sizes[dn+(ML*8)].nblocks = (hpaddr->hpmr>>2)&037000;
/*
 * Check the ML11 transfer rate.
 * 2mb is too fast for any pdp11
 * 1mb allowed on pdp11/70 only
 * .5mb & .25mb ok on any processor.
 */
			fs = hpaddr->hpmr & 01400;
			if((fs == 0) || ((fs == 0400) && (cputype != 70))) {
				printf("\n%s unit %d ML11 too fast for CPU\n",
					hp_dct[ctrl], dn);
				hp_dt[ind] = 0;
			} else
				hp_ios[ind].dk_tr = ((fs >> 8) & 3) + 4;
		}
	}
/*
 * Initialize disk I/O instrumentation pointers
 */
	pri = spl6();
	fs = DK_N + ctrl;
	dk_iop[fs] = &hp_ios[hp_index[ctrl]];
	dk_nd[fs] = nhp[ctrl];
	splx(pri);
}

hpstrategy(bp)
register struct buf *bp;
{
	register struct buf *dp;
	register int	ctrl;
	long sz, bn;
	int dn, dip, fs, pri, unit, ind;

	ctrl = hpctrl(bp->b_dev);
	if(!io_bae[HP_BMAJ+ctrl])
		mapalloc(bp);
	unit = minor(bp->b_dev) & 077;
	dn = (unit >> 3) & 07;	/* get drive number */
	ind = hp_index[ctrl] + dn;
	if(hp_dt[ind] == 0)
		goto bad;	/* NED - can't even touch ! */
/*
 * Set the drive information pointer according to drive type.
 * 0 = RP04/5/6, 1 = RM02/3, 2 = RM05, 3 = ML11
 */
	dip = hp_dipset(ind);
/*
 * Set disk xfer rates for iostat
 */
	switch(dip) {
	case RP456:
	case RM23:
		hp_ios[ind].dk_tr = 1;
		if(hp_dt[ind] == RM03)
			hp_ios[ind].dk_tr++;
		break;
	case RM5:
		hp_ios[ind].dk_tr = 2;
	case ML:	/* ML rate set in hpopen() */
		break;
	default:
		goto bad;
	}
	sz = bp->b_bcount;
	sz = (sz+511) >> 9;
	fs = unit & 07;		/* For RP & RM, fs is partition */
	if(dip == ML) {		/* For ML, fs is unit number */
		fs = dn;
		if(unit & 7)
			goto bad;	/* ML11 is not partitioned */
	}
	if(dn >= nhp[ctrl] ||
	    bp->b_blkno < 0 ||
	    (bn = bp->b_blkno)+sz > hp_sizes[fs+hpdi[dip].sti].nblocks) {
	bad:
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	if(dip == ML)
	    bp->b_cylin = 0;	/* fake out dsort for ML11 */
	else
	    bp->b_cylin = bn/hpdi[dip].nbpc + hp_sizes[fs+hpdi[dip].sti].cyloff;
	dp = &hputab[ind];
	pri = spl5();
	disksort(dp, bp);
	if (dp->b_active == 0) {
		hpustart(ctrl, dn);
		if(hptab[ctrl].b_active == 0)
			hpstart(ctrl);
	}
	splx(pri);
}

static
hpustart(ctrl, unit)
int ctrl;
int unit;
{
	register struct buf *bp, *dp;
	register struct hp_regs *hpaddr;
	daddr_t bn;
	int sn, cn, csn, dip, ind;

	ind = hp_index[ctrl] + unit;
	hpaddr = io_csr[HP_RMAJ+ctrl];
/*
 * Check for nonexistent drive !
 * This could cause a NED error interrupt,
 * which must be handled by hpintr().
 * The drive type reads as zeros on NEDs.
 */
	hpaddr->hpcs2.w = unit;
	hpaddr->hpcs1.c[0] = IE|GO;	/* NOP attempt seize dual port drive */
	hp_dt[ind] = hpaddr->hpdt & 0377;
	if(hp_dt[ind] == 0)
		goto done;	/* NED */
	hpaddr->hpcs1.c[0] = IE;
	hpaddr->hpas = 1<<unit;
	if(unit >= nhp[ctrl])
		return;
	dip = hp_dipset(ind);
	hp_ios[ind].dk_busy = 0;	/* device not busy */
	el_bdact |= (1 << (HP_BMAJ+ctrl));	/* XXX */
	dp = &hputab[ind];
	if((bp=dp->b_actf) == NULL)
		return;
	hpaddr->hpcs2.w = unit;	/* NED interrupt could change CS2 */
	if((hpaddr->hpds & VV) == 0) {
		hpaddr->hpcs1.c[0] = IE|PRESET|GO;
		hp_r[ind].bads &= ~BAD_LOA;
	}
	if(dip != ML)
		hpaddr->hpof = FMT22;
	if(dp->b_active)
		goto done;
	if(dip == ML) {
		sn = bp->b_blkno;
		dp->b_active++;
		goto mlsrch;
		}
	if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL))
		goto done;
	dp->b_active++;
	bn = bp->b_blkno;
	cn = bp->b_cylin;
	sn = bn%hpdi[dip].nbpc;
	sn = (sn+hpdi[dip].nsect-SDIST)%hpdi[dip].nsect;
	if(hpaddr->hpcc != cn)
		goto search;
	csn = (hpaddr->hpla>>6) - sn + SDIST - 1;
	if(csn < 0)
		csn += hpdi[dip].nsect;
	if(csn > hpdi[dip].nsect-RDIST)
		goto done;
search:
	hpaddr->hpdc = cn;
mlsrch:
	hpaddr->hpda = sn;
	hp_ios[ind].dk_busy++;		/* device active */
	el_bdact |= (1 << (HP_BMAJ+ctrl));
	hpaddr->hpcs1.c[0] = IE|SEARCH|GO;
	return;
done:
	dp->b_forw = NULL;
	if(hptab[ctrl].b_actf == NULL)
		hptab[ctrl].b_actf = dp; else
		hptab[ctrl].b_actl->b_forw = dp;
	hptab[ctrl].b_actl = dp;
}

static
hpstart(ctrl)
{
	register struct buf *bp, *dp;
	register struct hp_regs *hpaddr;
	int unit, ind;
	daddr_t bn;
	int dn, sn, tn, cn, dt, pri;
	int dip, bae;

	hpaddr = io_csr[HP_RMAJ+ctrl];
	bae = io_bae[HP_BMAJ+ctrl];
loop:
	if ((dp = hptab[ctrl].b_actf) == NULL)
		return;
	if ((bp = dp->b_actf) == NULL) {
		hptab[ctrl].b_actf = dp->b_forw;
		goto loop;
	}
	hptab[ctrl].b_active++;
	unit = minor(bp->b_dev) & 077;
	dn = unit >> 3;
	bn = 0;
/*
 * Do not attempt to access a NED !
 */
	ind = hp_index[ctrl] + dn;
	if(hp_dt[ind] == 0)
		goto bad;	/* NED */
	if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL)) {
	bad:
		hptab[ctrl].b_active = 0;
		bp->b_error = ENXIO;	/* NED */
		hptab[ctrl].b_errcnt = 0;
		dp->b_actf = bp->av_forw;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		goto loop;
	}
	dip = hp_dipset(ind);
	if((hp_r[ind].bads & BAD_LOA) == 0 && dip != ML){
		/* don't have Bad Sector Info. Need to load it. */
		bn = hp_r[ind].vcyl;
		for(pri = 0; pri < 5; pri++){
			cn = bn/hpdi[dip].nbpc;
			sn = bn%hpdi[dip].nbpc;
			tn = sn/hpdi[dip].nsect;
			sn = sn%hpdi[dip].nsect;
			hpaddr->hpcs2.w = dn;
			hpaddr->hpdc = cn;
			hpaddr->hpda = (tn<<8)+ sn;
			if(ubmaps && !bae)
			    hpaddr->hpba = (char *)&hp_bads[ind]-(char *)&cfree;
			else
			    hpaddr->hpba = &hp_bads[ind];
			hpaddr->hpwc = -((sizeof(struct hpbad)-64)>>1);
			if(bae)
				hpaddr->hpbae = 0;
			hpaddr->hpcs1.w = RCOM|GO;
			while((hpaddr->hpcs1.w & DRY) == 0)
				;
			if(hpaddr->hpcs1.w & TRE){
				if((pri==0) && (hptab[ctrl].b_errcnt==0)) {
					if(!bae)
						cn = (NHPREG - 2);
					else
						cn = NHPREG;
					fmtbde(bp, &hp_ebuf[ctrl], hpaddr, cn, HPDBOFF);
					hp_ebuf[ctrl].hp_reg[4] &= ~7;  /* correct unit # */
					hp_ebuf[ctrl].hp_reg[4] |= dn;
				}
				bn += 2;
			}
			else{
				hp_r[ind].bads |= BAD_LOA;
				break;
			}
		}
	}
	if((dip != ML) &&
	   (hptab[ctrl].b_errcnt == 0) &&
	   ((hp_r[ind].bads & BAD_LOA) == 0)) {
		hp_ebuf[ctrl].hp_bdh.bd_errcnt = 0;
		if(!logerr(E_BD,&hp_ebuf[ctrl],sizeof(struct hp_ebuf)))
		deverror(bp,hp_ebuf[ctrl].hp_reg[4],hp_ebuf[ctrl].hp_reg[6]);
	}

	hpaddr->hpcs2.w = dn;
	switch(hp_r[ind].bads & 060){
		case BAD_VEC:
			bn = hp_res[ctrl].r_vbn;
			hpaddr->hpba = hp_res[ctrl].r_vma;
			if(bae)
				hpaddr->hpbae = hp_res[ctrl].r_vxm;
			hpaddr->hpwc = -(hp_res[ctrl].r_vcc>>1);
			pri = ((hp_res[ctrl].r_vxm&03)<<8) | IE | GO;
			break;
		case BAD_CON:
			hp_r[ind].bads &= ~BAD_CON;
			bn = hp_res[ctrl].r_bn;
			hpaddr->hpba = hp_res[ctrl].r_ma;
			if(bae)
				hpaddr->hpbae = hp_res[ctrl].r_xm;
			hpaddr->hpwc = -(hp_res[ctrl].r_cc>>1);
			pri = ((hp_res[ctrl].r_xm&03)<<8) | IE | GO;
			break;
		default:
			bn = bp->b_blkno;
			hpaddr->hpba = bp->b_un.b_addr;
			if(bae)
				hpaddr->hpbae = bp->b_xmem;
			hpaddr->hpwc = -(bp->b_bcount>>1);
			pri = ((bp->b_xmem&3) << 8) | IE | GO;
			break;
	}
	if(dip != ML) {
		cn = bn/hpdi[dip].nbpc;
		if((hp_r[ind].bads & BAD_VEC) == 0)
			cn += hp_sizes[(unit&7)+hpdi[dip].sti].cyloff;
		sn = bn%hpdi[dip].nbpc;
		tn = sn/hpdi[dip].nsect;
		sn = sn%hpdi[dip].nsect;
	}
	if((hptab[ctrl].b_errcnt >= 16) && (dip != ML)) {
		hpaddr->hpof = hp_offset[hptab[ctrl].b_errcnt & 017] | FMT22;
		hpaddr->hpcs1.w = OFFSET|GO;
		while(hpaddr->hpds & PIP)
			;
	}
	if(dip == ML)
		hpaddr->hpda = bn;
	else {
		hpaddr->hpdc = cn;
		hpaddr->hpda = (tn << 8) + sn;
	}
	if(bp->b_flags & B_READ)
		pri |= RCOM; else
		pri |= WCOM;
	hpaddr->hpcs1.w = pri;
	el_bdact |= (1 << (HP_BMAJ+ctrl));
	hp_ios[ind].dk_busy++;		/* Instrumentation - disk active, */
	hp_ios[ind].dk_numb++;		/* count number of transfers, */
	unit = bp->b_bcount >> 6;	/* transfer size in 32 word chunks, */
	hp_ios[ind].dk_wds += unit;	/* count total words transferred */
}

hpintr(dev)
{
	register struct buf *bp, *dp;
	register struct hp_regs *hpaddr;
	int unit, ctrl, ind;
	int seg, uba;
	int as;
	int tpuis[2], tpbad[2], tppos, tpofst;
	int bsp;
	int pri, ctr, dn;

	ctrl = dev & 3;
	ind = hp_index[ctrl];
	hpaddr = io_csr[HP_RMAJ+ctrl];
/*
 * Must handle the NED error interrupt which could
 * be generated by the NED check in hpustart above.
 */
	if(hptab[ctrl].b_active == 0) {
		ctr = hpaddr->hpcs2.w & 7;
		if(hpaddr->hpcs2.w & NED) {
			printf("\n%s unit %d nonexistent\n", hp_dct[ctrl], ctr);
/* XXX */		hpaddr->hpcs2.w = 0; /* assumes drive 0 exists !!! */
			hpaddr->hpcs1.w = TRE|IE;
			hp_dt[ind+ctr] = 0;		/* NED */
			return;
		}
		as = hpaddr->hpas & 0377;
		if(as == 0) {
			logsi(hpaddr);
			return;
		}
	} else
		as = hpaddr->hpas & 0377;
	if(hptab[ctrl].b_active) {
		dp = hptab[ctrl].b_actf;
		bp = dp->b_actf;
		unit = (bp->b_dev >> 3) & 7;
		dn = unit;
		if(hpaddr->hpcs2.w & NED) {
			ctr = hpaddr->hpcs2.w & 7;
			if(ctr != unit) {
				hp_dt[ind+ctr] = 0;	/* NED */
				dn = ctr;
			}
		}
		ind += unit;
		hpaddr->hpcs2.c[0] = unit;
		hp_ios[ind].dk_busy = 0;	/* device no longer active */
		if (hpaddr->hpcs1.w & TRE) {		/* error bit */
			while((hpaddr->hpds & DRY) == 0)
				;
			if(hptab[ctrl].b_errcnt == 0) {
				if(!io_bae[HP_BMAJ+ctrl])
					ctr = (NHPREG - 2);
				else
					ctr = NHPREG;
				fmtbde(bp, &hp_ebuf[ctrl], hpaddr, ctr, HPDBOFF);
				hp_ebuf[ctrl].hp_reg[4] &= ~7;  /* correct unit # */
				hp_ebuf[ctrl].hp_reg[4] |= dn;
				}
			if((hp_dt[ind] == ML11) && (hptab[ctrl].b_errcnt == 0))
				hptab[ctrl].b_errcnt = 18;
			if(++hptab[ctrl].b_errcnt > 28 || hpaddr->hper1&WLE) {
				if(hpaddr->hper1 & WLE)
					hptab[ctrl].b_errcnt = 0;
				bp->b_flags |= B_ERROR;
			} else
				hptab[ctrl].b_active = 0;
/* Start of ECC code, please excuse the improper indentation */
	if(((hpaddr->hper1&(DCK|ECH)) == DCK) && (hp_dt[ind] != ML11))
	{
		fixbad(&hp_res[ctrl], bp, hpaddr->hpwc, 0);
		pri = spl7();
		tpuis[0] = UISA->r[0];	/* save User I space PAR */
		tpuis[1] = UISD->r[0];	/* save User I space PDR */
		if(io_bae[HP_BMAJ+ctrl])
			uba = (hpaddr->hpbae << 10) & 0176000;
		else
			uba = (hpaddr->hpcs1.w & 01400) << 2;
		uba += ((((int)hpaddr->hpba >> 1) & ~0100000) >> 5);
		uba -= 8;
		if(bp->b_flags & B_MAP) {
			ctr = ((uba >> 7) & 037);
			seg = (UBMAP + ctr)->ub_hi << 10;
			seg |= ((UBMAP + ctr)->ub_lo >> 6) & 01777;
			UISA->r[0] = seg + (uba & 0177);
/* ECC FIX */		bsp = (UBMAP + ctr)->ub_lo & 077;
		} else
			UISA->r[0] = uba;
		UISD->r[0] = 077406;	/* Set User I space PDR */
		tppos = (hpaddr->hpec1 - 1)/16;	/* word addr of error burst */
		tpofst = (hpaddr->hpec1 - 1)%16;	/* bit in word */
/* ECC FIX */	if((bp->b_flags&B_MAP) == 0)
			bsp = ((int)hpaddr->hpba & 077);
		bsp += (tppos*2);
		tpbad[0] = fuiword(bsp);
		tpbad[1] = fuiword(bsp+2);
		tpbad[0] ^= (hpaddr->hpec2 << tpofst); /* xor first word */
		if(tpofst > 5) {	/* must fix second word */
			ctr = (hpaddr->hpec2 >> 1) & ~0100000;
			if(tpofst < 15)
				ctr = ctr >> (15 - tpofst);
			tpbad[1] ^= ctr;	/* xor second word */
			}
		suiword(bsp,tpbad[0]);
		suiword((bsp+2),tpbad[1]);
		UISD->r[0] = tpuis[1];	/* restore User PDR */
		UISA->r[0] = tpuis[0];	/* restore User PAR */
		splx(pri);
		/* now see if transfer finished. */
		/* If not then reset;track,sector,etc. */
		if(hpaddr->hpwc != 0)
			{
			hpaddr->hpcs1.w = TRE|DCLR|GO;
			hp_r[ind].bads |= BAD_CON;
			hpstart(ctrl);	/* Continue transfer */
			return;
			}
		hptab[ctrl].b_active++;
	}
/* End of ECC code					*/

			switch(hp_dt[ind]){
				case RP04:
				case RP05:
				case RP06:
					if(hpaddr->hper1 & 020)/* Fmt Error */
						if(hpvector(bp, ctrl, ind, hpaddr->hpwc)){
						    hp_r[ind].bads |= BAD_VEC;
						    hptab[ctrl].b_errcnt--;
						    hpaddr->hper1 = 0;
						    hpstart(ctrl);
						    return;
						}
						break;
				case RM02:
				case RM03:
				case RM05:
					if(hpaddr->hper3 & DCK)	/* BSE Error */
						if(hpvector(bp, ctrl, ind, hpaddr->hpwc)){
						    hp_r[ind].bads |= BAD_VEC;
						    hptab[ctrl].b_errcnt--;
						    hpaddr->hper3 = 0;
						    hpstart(ctrl);
						    return;
						}
						break;
				default:
					break;
			}
			hpaddr->hpcs1.w = TRE|IE|DCLR|GO;
			if(((hptab[ctrl].b_errcnt&07) == 4)&& (hp_dt[ind] != ML11)) {
				hpaddr->hpcs1.w = RECAL|IE|GO;
				while(hpaddr->hpds & PIP)
					;
			}
		}
		if(hptab[ctrl].b_active) {
			if((hptab[ctrl].b_errcnt) && (hp_dt[ind] != ML11)) {
				hpaddr->hpcs1.w = RTC|GO;
				while(hpaddr->hpds & PIP)
					;
			}
			if(hptab[ctrl].b_errcnt || (bp->b_flags & B_ERROR))
			{
			  if(hp_ebuf[ctrl].hp_reg[6] & WLE)
				printf("\n%s unit %d Write Locked\n",
					hp_dct[ctrl], unit);
			  else {
			    hp_ebuf[ctrl].hp_bdh.bd_errcnt = hptab[ctrl].b_errcnt;
			    if(!logerr(E_BD, &hp_ebuf[ctrl], sizeof(struct hp_ebuf)))
		     deverror(bp, hp_ebuf[ctrl].hp_reg[4], hp_ebuf[ctrl].hp_reg[6]);
			  }
			}
			if(hp_r[ind].bads&BAD_VEC &&(bp->b_flags&B_ERROR)==0){
				hp_r[ind].bads &= ~BAD_VEC;
				if(hp_res[ctrl].r_cc > 0){
					hp_r[ind].bads |= BAD_CON;
					hpstart(ctrl);
					return;
				}
			}
			el_bdact &= ~(1 << (HP_BMAJ+ctrl));
			hp_r[ind].bads &= ~(BAD_VEC|BAD_CON);
			hptab[ctrl].b_active = 0;
			hptab[ctrl].b_errcnt = 0;
			hptab[ctrl].b_actf = dp->b_forw;
			dp->b_active = 0;
			dp->b_errcnt = 0;
			dp->b_actf = bp->av_forw;
			bp->b_resid = -(hpaddr->hpwc<<1);
			iodone(bp);
			hpaddr->hpcs1.w = DPRLS|GO; /* usseize dual port drive*/
			hpaddr->hpcs1.w = IE;
			if(dp->b_actf)
				hpustart(ctrl, unit);
		}
		as &= ~(1<<unit);
	} else {
		if(as == 0)
			hpaddr->hpcs1.w = IE;
		hpaddr->hpcs1.c[1] = TRE>>8;
	}
	for(unit=0; unit<8; unit++)
		if(as & (1<<unit)) {
			hpaddr->hpcs2.w = unit;
			hpaddr->hpcs1.w = (DCLR|GO);
/*
 * In case the attention came from
 * a drive that is seized by the other
 * port, clear the attention summary bit.
 */
			hpaddr->hpas = (1<<unit);	/* clear attn bit */
			if(unit >= nhp[ctrl])
				hpaddr->hpcs1.c[0] = IE;
			else
				hpustart(ctrl, unit);
			}
	hpstart(ctrl);
}

static
hpvector(bp, ctrl, ind, wc)
struct buf *bp;
{
	register int dt, cn, tn;
	int sn, j;
	long bn;

	fixbad(&hp_res[ctrl], bp, wc, 1);
	dt = hp_dipset(ind);
	bn = bp->b_blkno;
	bn += ((bp->b_bcount - hp_res[ctrl].r_cc)/512)-1;
	cn = bn/hpdi[dt].nbpc
		+ hp_sizes[(minor(bp->b_dev)&7)+hpdi[dt].sti].cyloff;
	sn = bn%hpdi[dt].nbpc;
	tn = (sn/hpdi[dt].nsect)<<8;
	tn |= sn%hpdi[dt].nsect;
	if((j = isbad(&hp_bads[ind], cn, tn, hpdi[dt].nsect)) >= 0){
		hp_res[ctrl].r_vbn = (hp_r[ind].vcyl - 1) - j;
#ifdef HPBAD
		hp_bads[ind].pbt_vec[j]++;
#endif
		return(1);
	}
	return(0);
}

hpread(dev)
{

	physio(hpstrategy, &rhpbuf[hpctrl(dev)], dev, B_READ);
}

hpwrite(dev)
{

	physio(hpstrategy, &rhpbuf[hpctrl(dev)], dev, B_WRITE);
}

/*
 * Set the drive information pointer
 * according to the drive type.
 */

static
hp_dipset(ind)
{
	switch(hp_dt[ind]) {
	case RP04:
	case RP05:
		hp_r[ind].vcyl = 171798L - 22;
		return(0);
	case RP06:
		hp_r[ind].vcyl = 340670L - 22;
		return(0);
	case RM02:
	case RM03:
		hp_r[ind].vcyl = 131680L - 32;
		return(1);
	case RM05:
		hp_r[ind].vcyl = 500384L - 32;
		return(2);
	case ML11:
		return(3);
	}
}

/*
 * Determine the RH11/RH70 controller number
 * based on the major device.
 */
static
hpctrl(dev)
{
	register int maj;

	maj = (dev>>8) & 077;
	if(maj < nblkdev)
		return(maj - HP_BMAJ);
	else
		return(maj - HP_RMAJ);
}

hpclose(dev,flag)
dev_t dev;
{

	bflclose(dev);
}