static char rcsid[] = "$Header: rd.c,v 820.1 86/12/04 19:56:12 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * Rimfire 44/45 controller
 *
 * Disk driver
 */

#include "rd.h"
#if NRD > 0
#include "../h/param.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/inode.h"
#include "../h/ioctl.h"
#include "../s32dev/rim_ioctl.h"
#include "../h/user.h"
#include "../h/dk.h"
#include "../h/uio.h"
#include "../h/kernel.h"
#include "../s32/dkio.h"
#include "../s32dev/mbvar.h"
#include "../s32dev/rfreg.h"
#include "../s32dev/rfvar.h"

struct mb_device *rdinfo[NRD];

struct rf_info rf_disk[NRD];

struct rf_disk priam3450_info = {
	0, 23, 5, 515, 10, 3,
	{ 525 * 23*5, 0 },
	{ 512 * 23*5, 3 },
	{ 0, 0 },
	{ 0, 0 },
	{ 1 * 23*5, 0 },
	{ 2 * 23*5, 1 },
	{ 169 * 23*5, 3 },
	{ 0, 0 },
	{ 174 * 23*5, 172 },
	{ 72 * 23*5, 172 },
	{ 271 * 23*5, 244 },
	{ 169 * 23*5, 346 },
	{ 0, 0 },
	{ 0, 0 },
	{ 0, 0 },
	{ 0, 0 }
};

struct rf_disk priam7050_info = {
	0, 23, 5, 1009, 40, 3,
	{ 1049 * 23*5, 0 },		/* all cylinders */
	{ 1006 * 23*5, 3 },		/* all usable cylinders */
	{ 0, 0 },
	{ 0, 0 },
	{ 1 * 23*5, 0 },
	{ 2 * 23*5, 1 },
	{ 169 * 23*5, 3 },		/* root */
	{ 0, 0 },
	{ 213 * 23*5, 172 },		/* large swap */
	{ 72 * 23*5, 172 },		/* small swap */
	{ 765 * 23*5, 244 },		/* a */
	{ 624 * 23*5, 385 },		/* b */
	{ 0, 0 },			/* c */
	{ 488 * 23*5, 521 },		/* d - user if have big swap */
	{ 349 * 23*5, 172 },		/* e - big swap */
	{ 0, 0 }
};

struct rf_disk priam806_info = {
	0, 32, 11, 810, 40, 4,
	{ 850 * 32*11, 0 },		/* all cylinders */
	{ 807 * 32*11, 3 },		/* all usable cylinders */
	{ 0, 0 },
	{ 0, 0 },
	{ 0, 0 },
	{ 0, 0 },
	{ 94 * 32*11, 3 },		/* root */
	{ 0, 0 },
	{ 0, 0 },
	{ 94 * 32*11, 97 },		/* swap */
	{ 373 * 32*11, 191 },		/* a */
	{ 246 * 32*11, 564 },		/* b */
	{ 0, 0 },
	{ 0, 0 },
	{ 0, 0 },
	{ 619 * 32*11, 191 }		/* f */
};

struct rf_disk ken7340_info = {
	0, 23, 5, 391, 20, 3,
	{ 411 * 23*5, 0 },
	{ 388 * 23*5, 3 },
};

struct rf_disk ken7380_info = {
	0, 23, 5, 783, 40, 3,
	{ 823 * 23*5, 0 },		/* all cylinders */
	{ 780 * 23*5, 3 },		/* all usable cylinders */
};

rdslave(md, mc)
	struct mb_device *md;
	struct mb_ctlr *mc;
{
	struct rf_info ri;
	register struct rf_ctlr *rc;
	u_char sense;

	ri.ri_istape = 0;
	ri.ri_md = md;
	if (rficmd(rc = rf_ctlr + mc->mc_ctlr, &ri, DPCMD_STATUS, CTL_NORMAL,
	    0, 0, 0, 0, 0, (char *)0) < 0) {
		return 0;
	} else {
		/* 
		 * We have determined empirically that the disk is there
		 * if the low bit of the first sense byte is on.
		 * Don't ask me to explain this.
		 */
		return(rc->rc_pb->pb_d.dp_sense[0] & 1);
	}
		
}

rdattach(md)
	register struct mb_device *md;
{
	struct rf_attr attr;
	register struct rf_ctlr *rc = rf_ctlr + md->md_ctlr;
	register struct rf_info *ri = rf_disk + md->md_unit;

	ri->ri_md = md;
	ri->ri_link = rc->rc_disk;
	rc->rc_disk = ri;
	rc->rc_dact = ri;
	rc->rc_ndisk++;
	ri->rd_type = 0;
	ri->rd_nsec = 1;
	ri->rd_nhead = 1;
	ri->rd_ncyl = 1;
	ri->rd_acyl = 1;
	ri->rd_intrlv = 1;
	ri->rd_sizes->sz_cyloff = 0;
	ri->rd_sizes->sz_nblk = 1;
	if (rficmd(rc, ri, DPCMD_INTERR, CTL_NORMAL, 0, 0, 0, 0x300,
	    vtop((caddr_t)&attr), "can't interrogate drive") < 0)
		goto out;
	if (rftest)
		attr.attr_model0 = PRIAM7050;
	switch (attr.attr_model0) {
	case PRIAM3450:
		printf("rd%d: priam 3450.\n", md->md_unit);
		ri->ri_d = priam3450_info;
		break;
	case PRIAM7050:
		printf("rd%d: priam 7050.\n", md->md_unit);
		ri->ri_d = priam7050_info;
		break;
	case PRIAM806:
		printf("rd%d: priam 806.\n", md->md_unit);
		ri->ri_d = priam806_info;
		break;
	case KEN7340:
		printf("rd%d: kennedy 7340.\n", md->md_unit);
		ri->ri_d = ken7340_info;
		break;
	case KEN7380:
		printf("rd%d: kennedy 7380.\n", md->md_unit);
		ri->ri_d = ken7380_info;
		break;
	default:
		printf("rd%d: unknown drive type %d.\n",
			md->md_unit, attr.attr_model0);
		goto out;
	}
out:
	ri->rd_nsecpercyl = ri->rd_nsec * ri->rd_nhead;
	(void) rficonfig(rc);
	if (rdspinup(rc, ri) < 0)
		printf ("rd%d: Spinup failure", md->md_unit);
	if (md->md_dk >= 0)
		dk_mspw[md->md_dk] = (float)ri->rd_intrlv /
			(rdrps * (ri->rd_nsec ? ri->rd_nsec : 1) *
			(DEV_BSIZE/2));
}

/*ARGSUSED*/
rdopen(dev, flag)
	dev_t dev;
{
	int unit = rdunit(dev);
	struct mb_device *md;

	if (unit >= NRD || (md = rdinfo[unit]) == 0 || !md->md_alive)
		return ENXIO;
	return 0;
}

rdstrategy(bp)
	register struct buf *bp;
{
	register struct rf_info *ri = rf_disk + rdunit(bp->b_dev);
	struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;
	int nblk;
	int s;

	if (bp == &rfcbuf) {
		bp->b_resid = CMDCYL;
		bp->b_error = 0;
	} else {
		register struct dk_sizes *sz = ri->rd_sizes + rdpart(bp->b_dev);

		nblk = bp->b_bcount >> DEV_BSHIFT;
		if (nblk == 0) {
			if (rfdebug)
				printf("rd%d%x: bcount < DEV_BSIZE.\n",
					ri->ri_unit, rdpart(bp->b_dev));
			bp->b_resid = bp->b_bcount;
			iodone(bp);
			return;
		}
		if (bp->b_blkno < 0 || bp->b_blkno + nblk > sz->sz_nblk) {
			if (rfdebug)
				printf("rd%d%x: bad blkno, bn %d, bcount %d.\n",
					ri->ri_unit, rdpart(bp->b_dev),
					bp->b_blkno, bp->b_bcount);
			bp->b_flags |= B_ERROR;
			iodone(bp);
			return;
		}
		bp->b_resid = bp->b_blkno / ri->rd_nsecpercyl + sz->sz_cyloff;
		bp->b_error = bp->b_blkno % ri->rd_nsecpercyl;
	}
	s = spl5();
	rdsort(ri, bp);
	if (!rc->rc_busy)
		rfstart(rc);
	splx(s);
}

rdread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return physio(rdstrategy, &rf_disk[rdunit(dev)].ri_rbuf,
		dev, B_READ, minphys, uio);
}

rdwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return physio(rdstrategy, &rf_disk[rdunit(dev)].ri_rbuf,
		dev, B_WRITE, minphys, uio);
}

/*ARGSUSED*/
rdioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register struct rf_info *ri = rf_disk + rdunit(dev);
	register struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;

	switch (cmd) {
	case DKIOCSETP:
#define P ((struct dk_param *)addr)
		spl5();
		ri->rd_type = P->dk_type;
		ri->rd_nsec = P->dk_nsec;
		ri->rd_nhead = P->dk_nhead;
		ri->rd_ncyl = P->dk_ncyl;
		ri->rd_acyl = P->dk_acyl;
		ri->rd_intrlv = P->dk_intrlv;
		ri->rd_nsecpercyl = ri->rd_nsec * ri->rd_nhead;
		if (ri->ri_dk >= 0)
			dk_mspw[ri->ri_dk] = (float)ri->rd_intrlv /
				(rdrps * (ri->rd_nsec ? ri->rd_nsec : 1) *
				(DEV_BSIZE/2));
		spl0();
		rfconfig(rc);
		break;
	case DKIOCGETP:
		P->dk_type = ri->rd_type;
		P->dk_nsec = ri->rd_nsec;
		P->dk_nhead = ri->rd_nhead;
		P->dk_ncyl = ri->rd_ncyl;
		P->dk_acyl = ri->rd_acyl;
		P->dk_intrlv = ri->rd_intrlv;
		break;
#undef P
	case DKIOCSETS:
		bcopy(addr, (caddr_t)ri->rd_sizes, sizeof ri->rd_sizes);
		break;
	case DKIOCGETS:
		bcopy((caddr_t)ri->rd_sizes, addr, sizeof ri->rd_sizes);
		break;
	case DKIOCFORMAT:
		rdformat(ri, rdpart(dev), *(int *)addr);
		break;
	case DKIOCMAP:
		rdmap(ri, rdpart(dev), *(int *)addr);
		break;
	case DKIOCRESET:
		if (rfreset(rc) < 0)
			u.u_error = EIO;	/* as if it would do any good */
		break;
	case DKIOCSETA:
		rdautomap = *(int *)addr ? 1 : 0;
		break;
	case DKIOCGETA:
		*(int *)addr = rdautomap;
		break;
	case _IOW(_IO_DK, 10, int [8]):
#define P ((int *)addr)
		rfcmd(P[0] ? rf_tape + P[1] : rf_disk + P[1], P[2],
			CTL_NORMAL, P[3], P[4], P[5], P[6], P[7]);
		break;
#undef P
	default:
		u.u_error = ENXIO;
		break;
	}
	return u.u_error;
}

rdsize(dev)
	dev_t dev;
{
	int unit = rdunit(dev);
	struct mb_device *md;

	if (unit >= NRD || (md = rdinfo[unit]) == 0 || !md->md_alive)
		return -1;
	return rf_disk[unit].rd_sizes[rdpart(dev)].sz_nblk;
}

/*
 * Format one track.  The track number is relative to the partition.
 * We have to fool the controller into thinking this is the
 * last track on the disk.
 * Race conditions abound, but you're in trouble already if you
 * try to format an active disk.
 */
rdformat(ri, part, trk)
	register struct rf_info *ri;
{
	struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;
	int head = trk % ri->rd_nhead;
	int cyl = trk / ri->rd_nhead + ri->rd_sizes[part].sz_cyloff;
	int ncyl = ri->rd_ncyl;
	int acyl = ri->rd_acyl;
	int nhead = ri->rd_nhead;

	spl5();
	ri->rd_ncyl = cyl + 1;
	ri->rd_acyl = 0;
	ri->rd_nhead = head + 1;
	spl0();
	rfconfig(rc);
	if (u.u_error)
		return;
	rfcmd(ri, DPCMD_FORMAT, CTL_NORMAL|CTL_INTRLV, cyl, head, 0,
		ri->rd_intrlv, 0);
	if (u.u_error)
		return;
	spl5();
	ri->rd_ncyl = ncyl;
	ri->rd_acyl = acyl;
	ri->rd_nhead = nhead;
	spl0();
	rfconfig(rc);
}

rdmap(ri, part, trk)
	register struct rf_info *ri;
{
	char b[DEV_BSIZE];
	struct rf_ctlr *rc = rf_ctlr + ri->ri_ctlr;
	int head = trk % ri->rd_nhead;
	int cyl = trk / ri->rd_nhead + ri->rd_sizes[part].sz_cyloff;
	int s = spl5();

	if (rc->rc_busy) {
		printf("rd%d: controller busy.\n", ri->ri_unit);
		u.u_error = EIO;
		goto out;
	}
	if (rdmap1(ri, cyl, head, b) < 0)
		u.u_error = EIO;
out:
	splx(s);
}

/*
 * Our very own disk sort routine.  Similar to the standard
 * disksort() and the old Rimfile dsort45().  See the comments
 * in ufs_dsort.c for an explanation of the algorithm.
 * We sort by sector number within a cylinder.  Also,
 * the buffers with io already started are assumed to be removed
 * from the list, so insertion at the beginning is allowed.
 * Rd->rd_cylin contains the current cylinder number.
 */
#define b_cylin b_resid
#define b_sector b_error

rdsort(ri, bp)
	register struct rf_info *ri;
	register struct buf *bp;
{
	register struct buf **pp;

	if (*(pp = &ri->rd_actf) == 0) {
		ri->rd_actf = bp;
		bp->av_forw = 0;
		return;
	}
	if (bp->b_cylin == ri->rd_cylin) {
		/*
		 * We're sitting on this cylinder, just insert it at
		 * the beginning.  Nothing to do;
		 */
	} else if (bp->b_cylin > ri->rd_cylin) {
		/*
		 * On the first request list.
		 * Insert before the first request not less than this one.
		 */
		while (*pp != 0 && (*pp)->b_cylin < bp->b_cylin)
			pp = &(*pp)->av_forw;
	} else {
		register cylin;

		/*
		 * Not on the first request list.
		 * Find inversion, which is the beginning of second list.
		 * Then insert as before.
		 */
		do {
			cylin = (*pp)->b_cylin;
			pp = &(*pp)->av_forw;
		} while (*pp != 0 && cylin <= (*pp)->b_cylin);
		while (*pp != 0 && (*pp)->b_cylin < bp->b_cylin)
			pp = &(*pp)->av_forw;
	}
	while (*pp != 0 && (*pp)->b_cylin == bp->b_cylin &&
	       (*pp)->b_sector <= bp->b_sector)
		pp = &(*pp)->av_forw;
	bp->av_forw = *pp;
	*pp = bp;
}

/*
 * Pretty print disk error
 *
 * The idea is to give several levels of information.
 */
rdprint(ri, rp, bp, mesg)
	register struct rf_info *ri;
	register union rf_pb *rp;
	register struct buf *bp;
	char *mesg;
{
	register err = rp->pb_d.dp_cst & CST_ERROR;
	char *indent = "      ";

	if (bp == 0 || bp == &rfcbuf) {
		printf("rd%d: %s, cmd 0x%x, ",
			ri->ri_unit, mesg, rp->pb_d.dp_cmd);
		indent++;
	} else
		printf("rd%d%x: %s, bn %d, %s, ",
			ri->ri_unit, rdpart(bp->b_dev), mesg, bp->b_blkno,
			bp->b_flags & B_READ ? "read" : "write");
	if (err != 0 && err != ERR_SOFT)
		printf("error 0x%x \"%s,\"\n%s", err, rferror(err), indent);
	printf("cst 0x%b, dst 0x%b",
		rp->pb_d.dp_cst, CST_BITS, rp->pb_d.dp_dst, DPDST_BITS);
	if (rp->pb_d.dp_dst & (DPDST_SENSE1|DPDST_SENSE2)) {
		printf(",\n%s", indent);
		if (rp->pb_d.dp_dst & DPDST_SENSE1)
			printf("sense1 0x%b",
				rp->pb_d.dp_sense[0], DPSN1_BITS);
		if (rp->pb_d.dp_dst * DPDST_SENSE2)
			printf("%ssense2 0x%b",
				rp->pb_d.dp_dst & DPDST_SENSE1 ? ", " : "",
				rp->pb_d.dp_sense[1], DPSN2_BITS);
	}
	if (rfdebug)
		printf(",\n%scyl %d, head %d, sec %d, nsec %d", indent,
			rp->pb_d.dp_cyl, rp->pb_d.dp_head, rp->pb_d.dp_sec,
			rp->pb_d.dp_nsec);
	printf(".\n");
}

/*
 * Below are routines that run at spl5 or higher.
 * They are called at either interrupt or startup time.
 */

/*
 * Try to recover from a hard IO error
 * This is all run at spl5().
 *
 * If the operation is a read, then we first try to write into the
 * block to clear it up.
 * Otherwise, the track is mapped to an alternate track, and the data
 * copied to the new track as much as possible.
 * When this is done, a write operation is restarted, but a read
 * fails (as it should).
 *
 * Some data is inevitably lost in the transaction, but we don't
 * necessary report an error.  The best thing is probably to panic
 * so fsck will be run after reboot.  But on the other hand,
 * the user might want to do his own cleaning up.
 *
 * We do the best we can.
 */
rdrecover(ri, rp, bp)
	register struct rf_info *ri;
	register union rf_pb *rp;
	register struct buf *bp;
{
	char b[DEV_BSIZE];	/* can we guarantee space for this? */
	register i;
	int cyl, head, sec;
	char erred = 0;

	/*
	 * Decide whether we should try to recover at all
	 */
	if (bp == &rfcbuf || bp->b_flags & B_PHYS || !rdautomap)
		return;
	switch (rp->pb_d.dp_cst & CST_ERROR) {
	case ERR_DATACRC:
	case ERR_IDCRC:
		break;
	case 0:
		if (rftest)
			break;
	default:
		return;
	}
	/*
	 * Save some information, *rp may be clobbered by rficmd().
	 */
	cyl = rp->pb_d.dp_cyl;
	head = rp->pb_d.dp_head;
	sec = rp->pb_d.dp_sec;
	/*
	 * If two buffers on the same track and in the same set
	 * of linked operations are passed here, we don't want
	 * to map out the same track twice.  So we remember
	 * the last cylinder and head mapped and skip it if we
	 * see it again.
	 * We know the head number is strictly non-decreasing
	 * within one set of linked operations.
	 * Rd_mapcyl and rd_maphead are forgotten in rfstart(),
	 * so we start afresh with the next set of linked requests
	 * (or with the next retry).
	 */
	if (cyl == ri->rd_mapcyl && head == ri->rd_maphead)
		goto out;
	printf("rd%d: bad block found on %s, cyl %d, head %d, sec %d.\n",
		ri->ri_unit, bp->b_flags & B_READ ? "read" : "write",
		cyl, head, sec);
	/*
	 * If a read, then first try writing into that block.
	 * The data is lost already.
	 */
	if ((bp->b_flags & B_READ) == 0)
		goto map;
	printf("rd%d: attempting to overwrite block.\n", ri->ri_unit);
	for (i = 0; i < sizeof b; i++)
		b[i] = i & 0xff;
	if (rficmd((struct rf_ctlr *)0, ri, DPCMD_WRITE, CTL_NORMAL,
	    cyl, head, sec, 1, vtop(b), "write failed") < 0)
		goto map;
	for (i = 0; i < sizeof b; i++)
		b[i] = 0;
	printf("rd%d: reading back.\n", ri->ri_unit);
	if (rficmd((struct rf_ctlr *)0, ri, DPCMD_READ, CTL_NORMAL,
	    cyl, head, sec, 1, vtop(b), "read failed") < 0)
		goto map;
	for (i = 0; i < sizeof b && (u_char)b[i] != (u_char)i; i++)
		;
	if (i >= sizeof b) {
		printf("rd%d: bad block salvaged by overwriting.\n",
			ri->ri_unit);
		goto out;
	}
	printf("rd%d: verify failed.\n", ri->ri_unit);
map:
	ri->rd_mapcyl = cyl;
	ri->rd_maphead = head;
	if (rdmap1(ri, cyl, head, b) < 0)
		erred = 1;
out:
	/*
	 * We can now try to write again.
	 * Some data was probably lost in the track copy, but that can't
	 * be helped, and it shouldn't reflect on this operation.
	 */
	if (!erred && (bp->b_flags & B_READ) == 0) {
		/*
		 * This causes the operation to start over
		 */
		bp->b_flags &= ~B_ERROR;
		ri->ri_errcnt = 0;
	}
	/* XXX call reboot directly? */
	printf("rd%d: REBOOT AS SOON AS POSSIBLE.\n", ri->ri_unit);
}

rdmap1(ri, cyl, head, b)
	register struct rf_info *ri;
	char *b;
{
	struct rf_id id;
	int acyl, ahead;
	char erred = 0;

	/*
	 * First find out whether we have any alternates left.
	 * We must have at least two, one into which to map the bad track,
	 * and one as a scratch for copying the track.
	 * We always use the first of the alternates as scratch.
	 */
	printf("rd%d: attempting to map bad track, cyl %d, head %d.\n",
		ri->ri_unit, cyl, head);
	id.id_type = IDTYPE_ALT;
	for (acyl = ri->rd_ncyl + ri->rd_acyl; id.id_type != IDTYPE_NORMAL &&
	     --acyl >= ri->rd_ncyl;) {
		for (ahead = ri->rd_nhead; id.id_type != IDTYPE_NORMAL &&
		    --ahead >= 0;)
			if (rdreadid(ri, acyl, ahead, &id) < 0)
				goto bad;
	}
	if (acyl < ri->rd_ncyl || acyl == ri->rd_ncyl && ahead == 0) {
		/* one or fewer track left */
		printf("rd%d: no more alternate tracks.\n", ri->ri_unit);
		goto bad;
	}
	/*
	 * Copy as much of the bad track as possible to the scratch
	 * track, map it, and copy the data back.
	 */
	if (rdcptrack(ri, cyl, head, ri->rd_ncyl, 0, b) < 0)
		erred = 1;
	printf("rd%d: mapping track to cyl %d, head %d.\n",
		ri->ri_unit, acyl, ahead);
	if (rficmd((struct rf_ctlr *)0, ri, DPCMD_MAP, CTL_NORMAL|CTL_INTRLV,
	    cyl, head, 0, ri->rd_intrlv, 0, "map failed") < 0)
		goto bad;
	if (rdcptrack(ri, ri->rd_ncyl, 0, cyl, head, b) < 0)
		erred = 1;	/* shouldn't get an error here */
	if (erred)
		printf("rd%d: some data is lost.\n", ri->ri_unit);
	printf("rd%d: track cyl %d, head %d mapped to cyl %d, head %d.\n",
		ri->ri_unit, cyl, head, acyl, ahead);
	return 0;
bad:
	printf("rd%d: can't map bad track.\n", ri->ri_unit);
	return -1;
}

rdreadid(ri, cyl, head, id)
	register struct rf_info *ri;
	struct rf_id *id;
{
	if (rficmd((struct rf_ctlr *)0, ri, DPCMD_SEEK, CTL_NORMAL,
	    cyl, head, 0, 0, 0, "read id, seek failed") < 0)
		return -1;
	if (rficmd((struct rf_ctlr *)0, ri, DPCMD_READID, CTL_NORMAL,
	    0, 0, 0, 0, vtop((caddr_t)id), "can't read sector id") < 0) {
		printf("     cyl %d, head %d.\n", ri->ri_unit, cyl, head);
		return -1;
	}
	if (rftest) {
		id->id_head = head;
		id->id_cyl0 = cyl;
		id->id_cyl1 = cyl >> NBBY;
		id->id_type = IDTYPE_NORMAL;
	}
	return 0;
}

/*
 * Try very hard to copy a track.
 */
rdcptrack(ri, fromcyl, fromhead, tocyl, tohead, b)
	register struct rf_info *ri;
	char *b;
{
	register sec;
	int i;
	char erred = 0;

	printf("rd%d: copying cyl %d, head %d to cyl %d, head %d.\n",
		ri->ri_unit, fromcyl, fromhead, tocyl, tohead);
	for (sec = 1; sec <= ri->rd_nsec; sec++) {
		if (rfdebug)
			printf("rd%d: copying sector %d.\n", ri->ri_unit, sec);
		for (i = 20; --i >= 0;)
			if (rficmd((struct rf_ctlr *)0, ri, DPCMD_READ,
			    CTL_NORMAL, fromcyl, fromhead, sec, 1, vtop(b),
			    rfdebug ? "read failed" : 0) >= 0)
				break;
		if (i < 0) {
			printf("rd%d: can't read sector %d.\n",
				ri->ri_unit, sec);
			erred = 1;
			continue;
		}
		for (i = 20; --i >= 0;)
			if (rficmd((struct rf_ctlr *)0, ri, DPCMD_WRITE,
			    CTL_NORMAL, tocyl, tohead, sec, 1, vtop(b),
			    rfdebug ? "write failed" : 0) >= 0)
				break;
		if (i < 0) {
			printf("rd%d: can't write sector %d.\n",
				ri->ri_unit, sec);
			erred = 1;
		}
	}
	return erred ? -1 : 0;
}

rdspinup(rc, ri)
register struct rf_ctlr *rc;
register struct rf_info *ri;
{

	if (rficmd(rc, ri, DPCMD_STATUS, CTL_NORMAL, 0,0,0,0,0,0) < 0)
		return -1;
	if (rc->rc_pb->pb_d.dp_cst & CST_DONE && 
	    rc->rc_pb->pb_d.dp_dst & DPDST_NOTRDY) {
		if (rficmd (rc, ri, DPCMD_LOAD, CTL_NORMAL,
		    0,0,0,0,0,"Spinup command rejected") < 0)
			return -1;
		while (rc->rc_pb->pb_d.dp_cst & CST_DONE &&
		    rc->rc_pb->pb_d.dp_dst & DPDST_NOTRDY) 
			if (rficmd(rc, ri, DPCMD_STATUS, CTL_NORMAL,
			    0,0,0,0,0,0) < 0)
				return -1;
	}
	return 0;
}
#endif
