V9/sys/dev.old/hp.c

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

/*	hp.c	4.47	82/02/18	*/

#ifdef HPDEBUG
int	hpdebug;
#endif
#ifdef HPBDEBUG
int	hpbdebug;
#endif

#include "hp.h"
/*
 * HP disk driver for RP0x+RMxx+ML11
 *
 * TODO:
 *	check RM80 skip sector handling when ECC's occur later
 *	check offset recovery handling
 *	see if DCLR and/or RELEASE set attention status
 *	print bits of mr && mr2 symbolically
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dk.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/mbareg.h"
#include "../h/mbavar.h"
#include "../h/mtpr.h"
#include "../h/vm.h"
#include "../h/cmap.h"
#include "../h/dkbad.h"
#include "../h/hpio.h"

#include "../h/hpreg.h"

/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
struct	size {
	daddr_t	nblocks;
	int	cyloff;
} hp6_sizes[8] = {
	15884,	0,		/* A=cyl 0 thru 37 */
	33440,	38,		/* B=cyl 38 thru 117 */
	340670,	0,		/* C=cyl 0 thru 814 */
	0,	0,
	0,	0,
	0,	0,
#ifndef NOBADSECT
	291280,	118,		/* G=cyl 118 thru 814 */
#else
	291346,	118,
#endif
	0,	0,
}, rm3_sizes[8] = {
	15884,	0,		/* A=cyl 0 thru 99 */
	33440,	100,		/* B=cyl 100 thru 309 */
	131680,	0,		/* C=cyl 0 thru 822 */
	0,	0,
	0,	0,
	0,	0,
#ifndef NOBADSECT
	81984,	310,		/* G=cyl 310 thru 822 */
#else
	82080,	310,
#endif
	0,	0,
}, rm5_sizes[8] = {
	15884,	0,		/* A=cyl 0 thru 26 */
	33440,	27,		/* B=cyl 27 thru 81 */
	500384,	0,		/* C=cyl 0 thru 822 */
	15884,	562,		/* D=cyl 562 thru 588 */
	55936,	589,		/* E=cyl 589 thru 680 */
#ifndef NOBADSECT
	86240,	681,		/* F=cyl 681 thru 822 */
	158592,	562,		/* G=cyl 562 thru 822 */
#else
	86636,	681,
	158688,	562,
#endif
	291346,	82,		/* H=cyl 82 thru 561 */
}, rm80_sizes[8] = {
	15884,	0,		/* A=cyl 0 thru 36 */
	33440,	37,		/* B=cyl 37 thru 114 */
	242606,	0,		/* C=cyl 0 thru 558 */
	0,	0,
	0,	0,
	192223,	115,		/* F=cyl 115 thru 558 */
	82080,	115,		/* G=cyl 115 thru 304 */
	110143,	305,		/* H=cyl 305 thru 558 */
}, mfj_sizes[8] = {
	10240,	0,		/* A=cyl 0 thru 31 */
	20480,	32,		/* B=cyl 32 thru 95 */
	232640,	96,		/* C=cyl 96 thru 822 */
	0,	0,
	0,	0,
	0,	0,
	0,	0,
	0,	0,
}, eagle_sizes[8] ={
	27520,	0,		/* A=cyl 0 thru 31 */
	27520,	32,		/* B=cyl 32 thru 63 */
	667360,	64,		/* C=cyl 64 thru 839 */
	232640,	64,		/* D=cyl 64 thru 340 (partial) */
	232640,	341,		/* E=cyl 341 thru 617 (partial) */
	0,	0,
	0,	0,
	0,	0,
/* 48 sector Emulex Eagle */
}, eag48_sizes[8] = {	/* cyl 841 used for bad sectors + info */
#ifdef ICARUS
	11*960,   0,	/* A = cyl 0 thru 10		for / */
	22*960,  11,	/* B = cyl 11 thru 32		for swap */
	842*960,  0,	/* C = all cyl 0 thru 841	for testing */
	16*960,  33,	/* D = cyl 33 thru 48		for /usr/guest */
	66*960,  49,	/* E = cyl 49 thru 114		for /usr/src */
	242*960,115,	/* F = cyl 115 thru 356		nearly 1/3 */
	242*960,357,	/* G = cyl 357 thru 598		nearly 1/3 */
	242*960,599,	/* H = cyl 599 thru 840		nearly 1/3 */
#else ICARUS
	45*960,   0,	/* A = cyl 0 thru 44		for /tmp */
	64*960,  45,	/* B = cyl 45 thru 108		for swap */
	842*960,  0,	/* C = all cyl 0 thru 841	for testing */
	244*960,109,	/* D = cyl 109 thru 352		nearly 1/3 */
	244*960,353,	/* E = cyl 353 thru 596		nearly 1/3 */
	244*960,597,	/* F = cyl 597 thru 840		nearly 1/3 */
	0,	0,	/* G = cyl 0 thru 402		nearly 1/2 */
	0, 	0,	/* H = cyl 438 thru 840		nearly 1/2 */
#endif ICARUS
}, hp7_sizes[8] = {
	15884,	0,		/* A=cyl 0 thru 9 */
	64000,	10,		/* B=cyl 10 thru 49 */
	1008000,0,		/* C=cyl 0 thru 629 */
	504000, 0,		/* D=cyl 0 thru 314 */
	504000, 315,		/* E=cyl 315 thru 629 */
	928000,	50,		/* F=cyl 50 thru 629 */
	0,	0,
	0,	0,
};
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */

#ifndef EAG48
#define	_hpSDIST	2
#define	_hpRDIST	3
#else EAG48
/*
	these constants were originally 2 and 3 respectively.  they
	have been changed to improve the performance of the eagle
	drives with the emulex controller.  to date, 8,9 is better than
	2,3.  12,13 did not do any better than 8,9.  changing these
	constants changes the lead for all drives using the hp driver
	including the rm80.  that's a mistake, but empirically,
	changing these to 8,9 did not measurably change rm80
	performance.
*/
#define	_hpSDIST	8
#define	_hpRDIST	9
#endif EAG48

int	hpSDIST = _hpSDIST;
int	hpRDIST = _hpRDIST;

short	hptypes[] =
	{ MBDT_RM03, MBDT_RM05, MBDT_RP06, MBDT_RM80, MBDT_RP05, MBDT_RP07,
	  MBDT_FUJI, MBDT_FUJI, MBDT_FUJI, MBDT_ML11A, MBDT_ML11B, 0 };
struct	mba_device *hpinfo[NHP];
int	hpattach(),hpustart(),hpstart(),hpdtint();
struct	mba_driver hpdriver =
	{ hpattach, 0, hpustart, hpstart, hpdtint, 0,
	  hptypes, "hp", 0, hpinfo };

struct hpst {
	short	nsect;
	short	ntrak;
	short	nspc;
	short	ncyl;
	struct	size *sizes;
} hpst[] = {
	32,	5,	32*5,	823,	rm3_sizes,	/* RM03 */
	32,	19,	32*19,	823,	rm5_sizes,	/* RM05 */
	22,	19,	22*19,	815,	hp6_sizes,	/* RP06 */
	31,	14, 	31*14,	559,	rm80_sizes,	/* RM80 */
	22,	19,	22*19,	411,	hp6_sizes,	/* RP05 */
	50,	32,	50*32,	630,	hp7_sizes,	/* RP07 */
	/* the following three entries must be contiguous */
	32,	10,	32*10,	823,	mfj_sizes,	/* little fujitsu */
	43,	20,	43*20,	842,	eagle_sizes,	/* eagle */
	48,	20,	48*20,	842,	eag48_sizes,	/* eagle 48 sectors */
	1,	1,	1,	1,	0,		/* ML11A */
	1,	1,	1,	1,	0,		/* ML11B */
};

u_char	hp_offset[16] = {
    HPOF_P400, HPOF_M400, HPOF_P400, HPOF_M400,
    HPOF_P800, HPOF_M800, HPOF_P800, HPOF_M800,
    HPOF_P1200, HPOF_M1200, HPOF_P1200, HPOF_M1200,
    0, 0, 0, 0,
};

struct	buf	rhpbuf[NHP];
#ifndef NOBADSECT
struct	buf	bhpbuf[NHP];
struct	dkbad	hpbad[NHP];
#endif
char	hpinit[NHP];
char	hprecal[NHP];
char	hphdr[NHP];
daddr_t	mlsize[NHP];

#define	b_cylin b_resid
 
/* #define ML11 0  to remove ML11 support */
#define ML11	0
/*#define	ML11	(hptypes[mi->mi_type] == MBDT_ML11A)*/
#define	RP06	(hptypes[mi->mi_type] <= MBDT_RP06)
#define	RM80	(hptypes[mi->mi_type] == MBDT_RM80)

#ifdef INTRLVE
daddr_t dkblock();
#endif
 
int	hpseek;

/*ARGSUSED*/
hpattach(mi, slave)
	struct mba_device *mi;
{
	register struct hpst *st = &hpst[mi->mi_type];

	if (hptypes[mi->mi_type] == MBDT_ML11B)
		mi->mi_type--;	/* A CHEAT - ML11B D.T. SHOULD == ML11A */
	if (ML11) {
		register struct hpdevice *hpaddr =
			(struct hpdevice *)mi->mi_drv;
		register int trt, sz;

		sz = hpaddr->hpmr & HPMR_SZ;
		if ((hpaddr->hpmr & HPMR_ARRTYP) == 0)
			sz >>= 2;
		mlsize[mi->mi_unit] = sz;
		if (mi->mi_dk >= 0) {
			trt = (hpaddr->hpmr & HPMR_TRT) >> 8;
			dk_mspw[mi->mi_dk] = 1.0 / (1<<(20-trt));
		}
	} else if (mi->mi_dk >= 0) {
		register struct hpdevice *hp;
		register int type;

		hp = (struct hpdevice *) mi->mi_drv;
		type = hp->hpdt & MBDT_TYPE;
		if (type == MBDT_FUJI) {
			short maxtrk, maxsect;
			/*
			 *	we have some kind of disk on an Emulex SC750.
			 *	We assume it's a little Fuji, and must now check
			 *	if it's an Eagle.
			 */
			hp->hphr = HPHR_MAXTRK;
			maxtrk = hp->hphr;
			if (maxtrk >= hpst[mi->mi_type].ntrak) {
				mi->mi_type++;
				/* it's an Eagle. now check # of sectors */
				hp->hphr = HPHR_MAXSECT;
				maxsect = hp->hphr;
				if (maxsect > hpst[mi->mi_type].nsect) {
					printf("%d sectors\n", maxsect);
					mi->mi_type++;
				}
			}
		}
		st = &hpst[mi->mi_type];
		dk_mspw[mi->mi_dk] = 1.0 / 60 / (st->nsect * 256);
	}
}

hpstrategy(bp)
	register struct buf *bp;
{
	register struct mba_device *mi;
	register struct hpst *st;
	register int unit;
	long sz, bn;
	int xunit = minor(bp->b_dev) & 07;
	int s;

	sz = bp->b_bcount;
	sz = (sz+511) >> 9;
	unit = dkunit(bp);
	if (unit >= NHP)
		goto bad;
	mi = hpinfo[unit];
	if (mi == 0 || mi->mi_alive == 0)
		goto bad;
	st = &hpst[mi->mi_type];
	if (ML11) {
		if (bp->b_blkno < 0 || dkblock(bp)+sz > mlsize[mi->mi_unit])
			goto bad;
		bp->b_cylin = 0;
	} else {
		if (bp->b_blkno < 0 ||
			(bn = dkblock(bp))+sz > st->sizes[xunit].nblocks)
			goto bad;
		bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff;
	}
	s = spl5();
	disksort(&mi->mi_tab, bp);
	if (mi->mi_tab.b_active == 0)
		mbustart(mi);
	splx(s);
	return;

bad:
	bp->b_flags |= B_ERROR;
	iodone(bp);
	return;
}

hpustart(mi)
	register struct mba_device *mi;
{
	register struct hpdevice *hpaddr = (struct hpdevice *)mi->mi_drv;
	register struct buf *bp = mi->mi_tab.b_actf;
	register struct hpst *st = &hpst[mi->mi_type];
	daddr_t bn;
	int sn, dist;

	hpaddr->hpcs1 = 0;
	if ((hpaddr->hpcs1&HP_DVA) == 0)
		return (MBU_BUSY);
	if ((hpaddr->hpds & HPDS_VV) == 0 || hpinit[mi->mi_unit] == 0) {
#ifndef NOBADSECT
		struct buf *bbp = &bhpbuf[mi->mi_unit];
#endif

		hpinit[mi->mi_unit] = 1;
		hpaddr->hpcs1 = HP_DCLR|HP_GO;
		if (mi->mi_mba->mba_drv[0].mbd_as & (1<<mi->mi_drive))
			printf("DCLR attn\n");
		hpaddr->hpcs1 = HP_PRESET|HP_GO;
		if (!ML11)
			hpaddr->hpof = HPOF_FMT22;
		mbclrattn(mi);
#ifndef NOBADSECT
		if (!ML11) {
			bbp->b_flags = B_READ|B_BUSY;
			bbp->b_dev = bp->b_dev;
			bbp->b_bcount = 512;
			bbp->b_un.b_addr = (caddr_t)&hpbad[mi->mi_unit];
			bbp->b_blkno = st->ncyl*st->nspc - st->nsect;
			bbp->b_cylin = st->ncyl - 1;
			mi->mi_tab.b_actf = bbp;
			bbp->av_forw = bp;
			bp = bbp;
		}
#endif
	}
	if (mi->mi_tab.b_active || mi->mi_hd->mh_ndrive == 1)
		return (MBU_DODATA);
	if (ML11)
		return (MBU_DODATA);
	if ((hpaddr->hpds & HPDS_DREADY) != HPDS_DREADY)
		return (MBU_DODATA);
	bn = dkblock(bp);
	sn = bn%st->nspc;
	sn = (sn+st->nsect-hpSDIST)%st->nsect;
	if (bp->b_cylin == (hpaddr->hpdc & 0xffff)) {
		if (hpseek)
			return (MBU_DODATA);
		dist = ((hpaddr->hpla & 0xffff)>>6) - st->nsect + 1;
		if (dist < 0)
			dist += st->nsect;
		if (dist > st->nsect - hpRDIST)
			return (MBU_DODATA);
	} else
		hpaddr->hpdc = bp->b_cylin;
	if (hpseek)
		hpaddr->hpcs1 = HP_SEEK|HP_GO;
	else {
		hpaddr->hpda = sn;
		hpaddr->hpcs1 = HP_SEARCH|HP_GO;
	}
	return (MBU_STARTED);
}

hpstart(mi)
	register struct mba_device *mi;
{
	register struct hpdevice *hpaddr = (struct hpdevice *)mi->mi_drv;
	register struct buf *bp = mi->mi_tab.b_actf;
	register struct hpst *st = &hpst[mi->mi_type];
	daddr_t bn;
	int sn, tn;

	bn = dkblock(bp);
	if (ML11)
		hpaddr->hpda = bn;
	else {
		sn = bn%st->nspc;
		tn = sn/st->nsect;
		sn %= st->nsect;
		hpaddr->hpdc = bp->b_cylin;
		hpaddr->hpda = (tn << 8) + sn;
	}
	if (hphdr[mi->mi_unit]) {
		if (bp->b_flags & B_READ)
			return (HP_RHDR|HP_GO);
		else
			return (HP_WHDR|HP_GO);
	}
	return (0);
}

hpdtint(mi, mbsr)
	register struct mba_device *mi;
	int mbsr;
{
	register struct hpdevice *hpaddr = (struct hpdevice *)mi->mi_drv;
	register struct buf *bp = mi->mi_tab.b_actf;
	register int er1, er2;
	int retry = 0;

#ifndef NOBADSECT
	if (bp->b_flags&B_BAD) {
		if (hpecc(mi, CONT))
			return(MBD_RESTARTED);
	}
#endif
	if (hpaddr->hpds&HPDS_ERR || mbsr&MBSR_EBITS) {
#ifdef HPDEBUG
		if (hpdebug) {
			int dc = hpaddr->hpdc, da = hpaddr->hpda;
			struct hpst *st = &hpst[mi->mi_type];

			printf("hperr: bp %x cyl %d blk %d as %o ",
				bp, bp->b_cylin, bp->b_blkno,
				hpaddr->hpas&0xff);
			printf("dc %x da %x\n",dc&0xffff, da&0xffff);
			printf("errcnt %d ", mi->mi_tab.b_errcnt);
			printf("mbsr=%b ", mbsr, mbsr_bits);
			printf("er1=%b er2=%b\n",
			    hpaddr->hper1, HPER1_BITS,
			    hpaddr->hper2, HPER2_BITS);
			DELAY(1000000);
		}
#endif
		er1 = hpaddr->hper1;
		er2 = hpaddr->hper2;
		if (er1 & HPER1_HCRC) {
			er1 &= ~(HPER1_HCE|HPER1_FER);
			er2 &= ~HPER2_BSE;
		}
		if (er1&HPER1_WLE) {
			printf("hp%d: write locked\n", dkunit(bp));
			bp->b_flags |= B_ERROR;
		} else if ((er1&0xffff) == HPER1_FER && RP06 &&
		    hphdr[mi->mi_unit] == 0) {
#ifndef NOBADSECT
			if (hpecc(mi, BSE))
			{
#ifdef HPBDEBUG
				if (hpdebug)
					printf("MBD_RESTARTED\n");
#endif
				return(MBD_RESTARTED);
			}
			else
#endif
				goto hard;
		} else if (++mi->mi_tab.b_errcnt > 27 ||
		    mbsr & MBSR_HARD ||
		    er1 & HPER1_HARD ||
		    hphdr[mi->mi_unit] ||
		    (!ML11 && (er2 & HPER2_HARD))) {
hard:
			harderr(bp, "hp");
			if (mbsr & (MBSR_EBITS &~ (MBSR_DTABT|MBSR_MBEXC)))
				printf("mbsr=%b ", mbsr, mbsr_bits);
			printf("er1=%b er2=%b",
			    hpaddr->hper1, HPER1_BITS,
			    hpaddr->hper2, HPER2_BITS);
			if (hpaddr->hpmr)
				printf(" mr=%o", hpaddr->hpmr&0xffff);
			if (hpaddr->hpmr2)
				printf(" mr2=%o", hpaddr->hpmr2&0xffff);
			printf("\n");
			bp->b_flags |= B_ERROR;
			hprecal[mi->mi_unit] = 0;
		} else if ((er2 & HPER2_BSE) && !ML11) {
#ifndef NOBADSECT
			if (hpecc(mi, BSE))
				return(MBD_RESTARTED);
			else
#endif
				goto hard;
		} else if (RM80 && er2&HPER2_SSE) {
			(void) hpecc(mi, SSE);
			return (MBD_RESTARTED);
		} else if ((er1&(HPER1_DCK|HPER1_ECH))==HPER1_DCK) {
			if (hpecc(mi, ECC))
				return (MBD_RESTARTED);
			/* else done */
		} else
			retry = 1;
		hpaddr->hpcs1 = HP_DCLR|HP_GO;
		if (ML11) {
			if (mi->mi_tab.b_errcnt >= 16)
				goto hard;
		} else if ((mi->mi_tab.b_errcnt&07) == 4) {
			hpaddr->hpcs1 = HP_RECAL|HP_GO;
			hprecal[mi->mi_unit] = 1;
			return(MBD_RESTARTED);
		}
		if (retry)
			return (MBD_RETRY);
	}
#ifdef HPDEBUG
	else
		if (hpdebug && hprecal[mi->mi_unit]) {
			printf("recal %d ", hprecal[mi->mi_unit]);
			printf("errcnt %d\n", mi->mi_tab.b_errcnt);
			printf("mbsr=%b ", mbsr, mbsr_bits);
			printf("er1=%b er2=%b\n",
			    hpaddr->hper1, HPER1_BITS,
			    hpaddr->hper2, HPER2_BITS);
		}
#endif
	switch (hprecal[mi->mi_unit]) {

	case 1:
		hpaddr->hpdc = bp->b_cylin;
		hpaddr->hpcs1 = HP_SEEK|HP_GO;
		hprecal[mi->mi_unit]++;
		return (MBD_RESTARTED);
	case 2:
		if (mi->mi_tab.b_errcnt < 16 ||
		    (bp->b_flags & B_READ) == 0)
			goto donerecal;
		hpaddr->hpof = hp_offset[mi->mi_tab.b_errcnt & 017]|HPOF_FMT22;
		hpaddr->hpcs1 = HP_OFFSET|HP_GO;
		hprecal[mi->mi_unit]++;
		return (MBD_RESTARTED);
	donerecal:
	case 3:
		hprecal[mi->mi_unit] = 0;
		return (MBD_RETRY);
	}
	hphdr[mi->mi_unit] = 0;
	bp->b_resid = -(mi->mi_mba->mba_bcr) & 0xffff;
	if (mi->mi_tab.b_errcnt >= 16) {
		/*
		 * This is fast and occurs rarely; we don't
		 * bother with interrupts.
		 */
		hpaddr->hpcs1 = HP_RTC|HP_GO;
		while (hpaddr->hpds & HPDS_PIP)
			;
		mbclrattn(mi);
	}
	if (!ML11) {
		hpaddr->hpof = HPOF_FMT22;
		hpaddr->hpcs1 = HP_RELEASE|HP_GO;
	}
	return (MBD_DONE);
}

hpread(dev)
	dev_t dev;
{
	register int unit = minor(dev) >> 3;

	if (unit >= NHP)
		u.u_error = ENXIO;
	else
		physio(hpstrategy, &rhpbuf[unit], dev, B_READ, minphys);
}

hpwrite(dev)
	dev_t dev;
{
	register int unit = minor(dev) >> 3;

	if (unit >= NHP)
		u.u_error = ENXIO;
	else
		physio(hpstrategy, &rhpbuf[unit], dev, B_WRITE, minphys);
}

/*ARGSUSED*/
hpioctl(dev, cmd, addr, flag)
	dev_t dev;
	int cmd;
	caddr_t addr;
	int flag;
{

	switch (cmd) {
	case DKIOCHDR:	/* do header read/write */
		if (suser())
			hphdr[minor(dev)>>3] = 1;
		return;

	default:
		u.u_error = ENXIO;
	}
}

hpecc(mi, flag)
	register struct mba_device *mi;
	int flag;
{
	register struct mba_regs *mbp = mi->mi_mba;
	register struct hpdevice *rp = (struct hpdevice *)mi->mi_drv;
	register struct buf *bp = mi->mi_tab.b_actf;
	register struct hpst *st = &hpst[mi->mi_type];
	int npf, o;
	int bn, cn, tn, sn;
	int bcr;
	static nerr;

	bcr = mbp->mba_bcr & 0xffff;
	if (bcr)
		bcr |= 0xffff0000;		/* sxt */
#ifndef NOBADSECT
	if (flag == CONT)
		npf = bp->b_error;
	else
#endif
		npf = btop(bcr + bp->b_bcount);
	o = (int)bp->b_un.b_addr & PGOFSET;
	bn = dkblock(bp);
#ifdef HPBDEBUG
	if (hpbdebug)	
	printf("hpecc: mbp->mba_bcr %x bcr %x bp->b_bcount %x bp->b_error %x bn %x npf %x\n",
		mbp->mba_bcr, bcr, bp->b_bcount, bp->b_error, bn, npf);
	if (hpbdebug)
	printf("ber npf: %x\n", ((rp->hpdc & 0xffff) * (st->nspc))
					  + ((((rp->hpda) >> 8) & 0xff) * st->nsect)
				      + ((rp->hpda) & 0xff)
					  - (((st->sizes[minor(bp->b_dev)&07].cyloff) * st->nspc)
					   + 1 + bn));
#endif
	cn = bp->b_cylin;
	sn = bn%(st->nspc) + npf;
	tn = sn/st->nsect;
	sn %= st->nsect;
	cn += tn/st->ntrak;
	tn %= st->ntrak;
	switch (flag) {
	case ECC:
		{
		register int i;
		caddr_t addr;
		struct pte mpte;
		int bit, byte, mask;

		npf--;		/* because block in error is previous block */
		printf("hp%d%c: soft ecc sn%d\n", dkunit(bp),
		    'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf);
		nerr++;
		if ((nerr%100)==0)
			printf("%d soft errors\n", nerr);
		mask = rp->hpec2&0xffff;
		i = (rp->hpec1&0xffff) - 1;		/* -1 makes 0 origin */
		bit = i&07;
		i = (i&~07)>>3;
		byte = i + o;
		while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
			mpte = mbp->mba_map[npf+btop(byte)];
			addr = ptob(mpte.pg_pfnum) + (byte & PGOFSET);
			putmemc(addr, getmemc(addr)^(mask<<bit));
			byte++;
			i++;
			bit -= 8;
		}
		if (bcr == 0)
			return (0);
		npf++;
		break;
		}

	case SSE:
#ifdef HPBDEBUG
		if (hpbdebug)
		printf("hpeccSSE\n");
#endif
		rp->hpof |= HPOF_SSEI;
		mbp->mba_bcr = -(bp->b_bcount - (int)ptob(npf));
		break;

#ifndef NOBADSECT
	case BSE:
#ifdef HPBDEBUG
		if (hpbdebug)
		printf("hpeccBSE: bn %d cn %d tn %d sn %d\n", bn, cn, tn, sn);
#endif
		if ((bn = isbad(&hpbad[mi->mi_unit], cn, tn, sn)) < 0)
			return(0);
		bp->b_flags |= B_BAD;
		bp->b_error = npf + 1;
		bn = st->ncyl*st->nspc - st->nsect - 1 - bn;
		cn = bn/st->nspc;
		sn = bn%st->nspc;
		tn = sn/st->nsect;
		sn %= st->nsect;
		mbp->mba_bcr = -512;
#ifdef HPBDEBUG
		if (hpbdebug)
		printf("revector to cn %d tn %d sn %d\n", cn, tn, sn);
#endif
		break;

	case CONT:
#ifdef HPBDEBUG
		if (hpbdebug)
		printf("hpecc, CONT: bn %d cn %d tn %d sn %d\n", bn,cn,tn,sn);
#endif
		npf = bp->b_error;
		bp->b_flags &= ~B_BAD;
		mbp->mba_bcr = -(bp->b_bcount - (int)ptob(npf));
		if ((mbp->mba_bcr & 0xffff) == 0)
			return(0);
		break;
#endif
	}
#ifdef HPBDEBUG
	if (hpbdebug)
	printf("hpecc, endcase: bn %d\n",bn);
#endif
	rp->hpcs1 = HP_DCLR|HP_GO;
	if (rp->hpof&HPOF_SSEI)
		sn++;
	rp->hpdc = cn;
	rp->hpda = (tn<<8) + sn;
	mbp->mba_sr = -1;
	mbp->mba_var = (int)ptob(npf) + o;
	rp->hpcs1 = bp->b_flags&B_READ ? HP_RCOM|HP_GO : HP_WCOM|HP_GO;
	mi->mi_tab.b_errcnt = 0;	/* error has been corrected */
	return (1);
}

#define	DBSIZE	20

hpdump(dev)
	dev_t dev;
{
	register struct mba_device *mi;
	register struct mba_regs *mba;
	struct hpdevice *hpaddr;
	char *start;
	int num, unit;
	register struct hpst *st;

	num = physmem;
	start = 0;
	unit = minor(dev) >> 3;
	if (unit >= NHP)
		return (ENXIO);
#define	phys(a,b)	((b)((int)(a)&0x7fffffff))
	mi = phys(hpinfo[unit],struct mba_device *);
	if (mi == 0 || mi->mi_alive == 0)
		return (ENXIO);
	mba = phys(mi->mi_hd, struct mba_hd *)->mh_physmba;
	mba->mba_cr = MBCR_INIT;
	hpaddr = (struct hpdevice *)&mba->mba_drv[mi->mi_drive];
	if ((hpaddr->hpds & HPDS_VV) == 0) {
		hpaddr->hpcs1 = HP_DCLR|HP_GO;
		hpaddr->hpcs1 = HP_PRESET|HP_GO;
		hpaddr->hpof = HPOF_FMT22;
	}
	st = &hpst[mi->mi_type];
	if (dumplo < 0 || dumplo + num >= st->sizes[minor(dev)&07].nblocks)
		return (EINVAL);
	while (num > 0) {
		register struct pte *hpte = mba->mba_map;
		register int i;
		int blk, cn, sn, tn;
		daddr_t bn;

		blk = num > DBSIZE ? DBSIZE : num;
		bn = dumplo + btop(start);
		cn = bn/st->nspc + st->sizes[minor(dev)&07].cyloff;
		sn = bn%st->nspc;
		tn = sn/st->nsect;
		sn = sn%st->nsect;
		hpaddr->hpdc = cn;
		hpaddr->hpda = (tn << 8) + sn;
		for (i = 0; i < blk; i++)
			*(int *)hpte++ = (btop(start)+i) | PG_V;
		mba->mba_sr = -1;
		mba->mba_bcr = -(blk*NBPG);
		mba->mba_var = 0;
		hpaddr->hpcs1 = HP_WCOM | HP_GO;
		while ((hpaddr->hpds & HPDS_DRY) == 0)
			;
		if (hpaddr->hpds&HPDS_ERR)
			return (EIO);
		start += blk*NBPG;
		num -= blk;
	}
	return (0);
}