static char rcsid[] = "$Header: rf.c,v 820.1 86/12/04 19:56:15 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
 *
 * This is the common controller code for both tape and disk.
 */

#include "rf.h"
#if NRF > 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/kernel.h"
#include "../s32/cpu.h"				/* flushWriteQueue macro */
#include "../s32/dkio.h"
#include "../s32dev/mbvar.h"
#include "../s32dev/rfreg.h"
#include "../s32dev/rfvar.h"

#ifdef DEBUG
char rfdebug = 0;
char rtdebug = 0;
char rftest = 0;
int rferrrate = 80;
union rf_pb *rf_pb0 = rf_ctlr[0].rc_pb;
union rf_pb *rf_pb1 = rf_ctlr[1].rc_pb;
#endif

int rdretry = 20;
int rtretry = 5;
int rtirgretry = 5;		/* retry count for IRG writing on write fail */
int rtrewtime = 60;
char rdautomap = 1;
int rdrps = 60;

u_short rfstd[] = { 0xa0, 0xa2, 0xac, 0xaa, 0 };
struct mb_ctlr *rfinfo[NRF];
struct mb_driver rfdriver =
	{ 0, rfprobe, rfslave, rdattach, rfstd, "rd", rdinfo, "rf", rfinfo };

struct rf_ctlr rf_ctlr[NRF];

struct rf_dr rf_dr[NRF][RD_NSLAVE];

char *rf_errlist[] = {
	"No error",						/* 0x00 */
	"No error after retry",
	"Data CRC error",
	"ID CRC error",
	"Seek error",
	"Seek verify error",
	"Disk not ready",
	"Sector not found",
	"No ID sync mark",
	"Unexpected end of tape",
	"Error on tape read or read after write",
	"Tape FIFO overflow/underflow",
	"Tape command attempted on disk-only controller",
	"Diagnostic command attempted without diagnostic jumper",
	"Checksum error in PROM",
	"Tape time-out during read or write",
	"Tape drive not ready",					/* 0x10 */
	"Drive write protected",
	"No ID sync code",
	"Illegal dump/restor parameters",
	"Attempting to link to block move",
	"Filemark encountered during read",
	"Attempting to access alternate track",
	"End of tape during restore",
	"No tape drive",
	"Illegal parameter",
	"Disk drive error during write",
	0,
	"Disk busy executing command",
	"Disk busy executing command",
	"Illegal disk parameter",
	"Drive down or disconnected",
	"Match found during block move",			/* 0x20 */
	"Data busy",
	"Data busy, formatter busy, or drive not ready",
	"Tape ready not deasserted",
	"Tape ready not asserted",
	"Tape data busy not asserted",
	"Multibus time out",
	"Blank tape",
	"Micro-diagnostic error",
	"Attempting to link block move",
	"Tape offline",
	"Unidentified hardware error",
	"Drive not configured",
	"Unable to write disk ID during format or map",
	"Attempting to map alternate track",
	0,
	"Illegal open-ended sequence",				/* 0x30 */
	"Unexpected exception",
	"Timeout during read status"
};
int rf_nerr = sizeof rf_errlist / sizeof *rf_errlist;

rfprobe(addr, intr, ctlr)
	caddr_t addr;
	int (*intr)();
{
	register struct rf_ctlr *rc = rf_ctlr + ctlr;
	register i;
	int rfintr();

	for (i = 0; i < NRF; i++)
		if (rfinfo[i] && rfinfo[i]->mc_alive &&
		    rfinfo[i]->mc_addr == addr)
			return 0;
	if (intr != rfintr)
		return 0;
	rc->rc_ctlr = ctlr;
	rc->rc_device = (struct rf_device *)addr;
	/*
	 * Reset the controller, or cause a bus error.
	 */
	rc->rc_device->rf_reset = 1;
	/*
	 * Initialize the controller so we can cause an interrupt.
	 */
	RF_SCP->scp_bus = SCPBUS_16;
	rfsetaddr(RF_SCP->scp_addr, vtop((caddr_t)&rc->rc_scb));
	rc->rc_scb.scb_03 = 0x03;
	rfsetaddr(rc->rc_scb.scb_addr, vtop((caddr_t)&rc->rc_ccb));
	rfsetaddr(rc->rc_ccb.ccb_addr, vtop((caddr_t)rc->rc_pb));
	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
	rc->rc_device->rf_atten = 1;
	if (rfwait(rc, "can't initialize controller") < 0)
		return 0;
	/*
	 * Force an interrupt.  CMD_ID is an illegal command before
	 * we configure the controller.
	 */
	rc->rc_pb->pb_d.dp_cmd = CMD_ID;
	rc->rc_pb->pb_d.dp_ctl = CTL_NORMAL | CTL_INTR;
	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
	rc->rc_ccb.ccb_ccw = CCBCCW_NORMAL;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	rc->rc_device->rf_atten = 1;
	if (rfwait(rc, "can't force interrupt") < 0)
		return 0;
	/*
	 * Clear the interrupt.
	 */
	rc->rc_pb->pb_d.dp_cmd = CMD_CLRINT;
	rc->rc_pb->pb_d.dp_ctl = CTL_NORMAL;
	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
	rc->rc_ccb.ccb_ccw = CCBCCW_CLRINT;
	rc->rc_device->rf_atten = 1;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	if (rfwait(rc, "can't clear interrupt") < 0)
		return 0;
	/*
	 * Specify a minimal configuration until
	 * we find out what kind of disks we really have.
	 */
	if (rficonfig(rc) < 0)
		return 0;
	/*
	 * Get the controller id.
	 */
	if (rficmd(rc, (struct rf_info *)0, CMD_ID, CTL_NORMAL,
	    0, 0, 0, 0, 0, "can't read controller id") < 0)
		return 0;
	switch (rc->rc_pb->pb_d.dp_dst) {
	case ID_44:
		printf("rf%d: rimfire 44.\n", ctlr);
		rc->rc_ctape = 1;
		break;
	case ID_45:
		printf("rf%d: rimfire 45.\n", ctlr);
		break;
	default:
		printf("rf%d: unknown board id 0x%x.\n", ctlr,
			rc->rc_pb->pb_d.dp_dst);
	}
	return sizeof (struct rf_device);
}

rfslave(md, mc)
	struct mb_device *md;
	struct mb_ctlr *mc;
{
	if (md->md_dk) {
		if (!rdslave(md, mc))
			return 0;
		rfdriver.mdr_dname[1] = 'd';
		rfdriver.mdr_dinfo = rdinfo;
		rfdriver.mdr_attach = rdattach;
	} else {
		if (!rtslave(md, mc))
			return 0;
		rfdriver.mdr_dname[1] = 't';
		rfdriver.mdr_dinfo = rtinfo;
		rfdriver.mdr_attach = rtattach;
	}
	return 1;
}

/*
 * Start controller.
 * We do seeks on disks first, and then disk transfers.
 */
rfstart(rc)
	register struct rf_ctlr *rc;
{
	register struct rf_info *ri;
	register struct buf *bp;
	register union rf_pb *rp;
	register i;
	int n;

	if (rc->rc_ccb.ccb_gate != CCBGATE_OPEN) {
		/* nothing intelligent to do, really */
		printf("rf%d: gate not open, gate 0x%x.\n",
			rc->rc_ctlr, rc->rc_ccb.ccb_gate);
		return;
	}
	rp = rc->rc_pb;
	/*
	 * Check for commands and seeks first.
	 */
	ri = rc->rc_dact;
	for (n = rc->rc_ndisk; !rc->rc_busy && --n >= 0;) {
		if ((ri = ri->ri_link) == 0)
			ri = rc->rc_disk;
		if (ri->ri_active != 0)
			continue;
		if ((bp = ri->rd_actf) == 0)
			continue;
		if (bp == &rfcbuf) {
			if (rfdebug)
				printf("rd%d%x: command.\n",
					ri->ri_unit, rdpart(bp->b_dev));
			*rp = *(union rf_pb *)bp->b_un.b_addr;
			rp->pb_d.dp_ctl |= ri->ri_slave;
			if (rftest)
				rp->pb_d.dp_cst = CST_DONE;
			else
				rp->pb_d.dp_cst = 0;
			rp->pb_d.dp_dst = 0;
			ri->rd_io = bp;
			ri->rd_actf = bp->av_forw;
			bp->av_forw = 0;
			rc->rc_busy = BUSY_DISK;
			rc->rc_dact = ri;
			ri->ri_active = ACTIVE_CMD;
			continue;
		}
		if (ri->rd_cylin == bp->b_resid || rc->rc_ndisk == 1) {
			ri->ri_active = ACTIVE_SEEKDONE;
			continue;
		}
		ri->rd_cylin = bp->b_resid;
		if (rfdebug)
			printf("rd%d%x: seek, cyl %d.\n", ri->ri_unit,
				rdpart(bp->b_dev), ri->rd_cylin);
		rp->pb_d.dp_cmd = DPCMD_SEEK;
		rp->pb_d.dp_ctl = CTL_NORMAL | ri->ri_slave;
		rp->pb_d.dp_sec = 1;
		rp->pb_d.dp_head = 0;
		rp->pb_d.dp_cyl = ri->rd_cylin;
		if (rftest)
			rp->pb_d.dp_cst = CST_DONE;
		else
			rp->pb_d.dp_cst = 0;
		rp->pb_d.dp_dst = 0;
		rc->rc_busy = BUSY_DISK;
		rc->rc_dact = ri;
		ri->ri_active = ACTIVE_SEEK;
		if (ri->ri_dk >= 0) {
			dk_busy |= 1 << ri->ri_dk;
			dk_seek[ri->ri_dk]++;
		}
	}
	/*
	 * Now, look for disk transfers.
	 */
	ri = rc->rc_dact;
	for (n = rc->rc_ndisk; !rc->rc_busy && --n >= 0;) {
		long bcount;
		int xfer;

		if ((ri = ri->ri_link) == 0)
			ri = rc->rc_disk;
		if (ri->ri_active != ACTIVE_SEEKDONE)
			continue;
		if ((bp = ri->rd_io) == 0 && (bp = ri->rd_actf) == 0)
			continue;
		rc->rc_dact = ri;
		if (rfdebug)
			printf("rd%d%x: io, cyl %d,", ri->ri_unit,
				rdpart(bp->b_dev), ri->rd_cylin);
		bcount = 0;
		xfer = 0;
		for (;;) {
			rp->pb_d.dp_cmd = bp->b_flags & B_READ
				? DPCMD_READ : DPCMD_WRITE;
			rp->pb_d.dp_ctl = CTL_NORMAL | ri->ri_slave;
			i = bp->b_blkno;
			rp->pb_d.dp_sec = i % ri->rd_nsec + 1;
			rp->pb_d.dp_head = i / ri->rd_nsec % ri->rd_nhead;
			rp->pb_d.dp_cyl = bp->b_resid;
			i = bp->b_flags & B_PHYS ? (int)bp->b_un.b_addr
					: (int)vtop(bp->b_un.b_addr);
			rp->pb_d.dp_page = rfpage(i);
			rfsetaddr(rp->pb_d.dp_addr, i);
			i = 0x100000 - (i & 0xfffff);	/* XXX */
			if (i < bp->b_bcount)
				printf("rd%d%x: bn %d, bad transfer, bcount %d, max %d.\n",
					ri->ri_unit, rdpart(bp->b_dev),
					bp->b_blkno, bp->b_bcount, i);
			else
				i = bp->b_bcount;
			rp->pb_d.dp_nsec = i >> DEV_BSHIFT;
			rp->pb_d.dp_bcount = i & ~DEV_BMASK;
			if (rftest && time.tv_usec/10000 >= rferrrate)
				rp->pb_d.dp_cst = CST_DONE;
			else
				rp->pb_d.dp_cst = 0;
			rp->pb_d.dp_dst = 0;
			if (rfdebug)
				printf(" (%c %d %d %d)",
					bp->b_flags&B_READ ? 'R' : 'W',
					rp->pb_d.dp_head, rp->pb_d.dp_sec,
					rp->pb_d.dp_nsec);
			bcount += rp->pb_d.dp_bcount;
			xfer++;
			if (rp >= &rc->rc_pb[NCMD - 1] || bp->av_forw == 0
			    || bp->av_forw->b_resid != bp->b_resid)
				break;
			rp->pb_d.dp_ctl |= CTL_LINK;
			i = (int) vtop((caddr_t)&rp[1]);
			rfsetaddr(rp->pb_d.dp_link, i);
			rp++;
			bp = bp->av_forw;
		}
		if (rfdebug)
			printf(".\n");
		if (ri->rd_io == 0) {
			/*
			 * This is the first time we tried these
			 * blocks (not retries), so move them to
			 * the io list.
			 */
			ri->rd_io = ri->rd_actf;
			ri->rd_actf = bp->av_forw;
			bp->av_forw = 0;
		}
		ri->rd_mapcyl = -1;
		ri->rd_maphead = -1;
		rc->rc_busy = BUSY_DISK;
		ri->ri_active = ACTIVE_IO;
		if (ri->ri_dk >= 0) {
			dk_busy |= 1 << ri->ri_dk;
			dk_xfer[ri->ri_dk] += xfer;
			dk_wds[ri->ri_dk] += bcount >> 6;
		}
		rd_profile[xfer]++;
	}
	/*
	 * Check tape operations.
	 */
	ri = rc->rc_tact;
	for (n = rc->rc_ntape; !rc->rc_busy && --n >= 0;) {
		daddr_t blkno;

		if ((ri = ri->ri_link) == 0)
			ri = rc->rc_tape;
again:
		if ((bp = ri->rt_actf) == 0)
			continue;
		blkno = bdbtofsb(bp->b_blkno);
		rp->pb_t.tp_ctl = ri->ri_slave << CTL_TSSHIFT;
		if (!rc->rc_ctape && (minor(bp->b_dev) & RT_1600BPI) == 0)
			rp->pb_t.tp_ctl |= CTL_SD;
		if (rftest)
			rp->pb_d.dp_cst = CST_DONE;
		else
			rp->pb_d.dp_cst = 0;
		rp->pb_t.tp_dst = 0;
		switch (ri->ri_active) {
		case 0:
			/*
			 * This is the only place we need to check
			 * for rt_err, since the other states are never
			 * reached when it's set.
			 */
			if (ri->rt_err)
				goto err;
			if (bp == &rfcbuf) {
#define RP ((union rf_pb *)bp->b_un.b_addr)
				rp->pb_t.tp_cmd = RP->pb_t.tp_cmd;
				rp->pb_t.tp_page = RP->pb_t.tp_page;
				rp->pb_t.tp_ctl |= RP->pb_t.tp_ctl;
				rp->pb_t.tp_retcnt = RP->pb_t.tp_retcnt;
				rp->pb_t.tp_bufsiz = RP->pb_t.tp_bufsiz;
				rp->pb_t.tp_nrec = RP->pb_t.tp_nrec;
				rp->pb_t.tp_addr = RP->pb_t.tp_addr;
				if (rfdebug || rtdebug)
					printf("rt%d: cmd 0x%x.\n",
						ri->ri_unit, rp->pb_t.tp_cmd);
				rc->rc_busy = BUSY_TAPE;
				rc->rc_tact = ri;
				ri->ri_active = ACTIVE_CMD;
				/*
				 * Set the rewind time if necessary.
				 */
				if (RP->pb_t.tp_cmd == TPCMD_REWIND || 
				     RP->pb_t.tp_cmd == TPCMD_OFFLINE)
					ri->rt_rewtime = time.tv_sec;
				break;
#undef RP
			}
			/*
			 * Reading at eof returns 0 bytes.
			 * Reading past eof returns error.
			 * Writing sets eof.
			 */
			if (bp->b_flags & B_READ) {
				if (blkno == ri->rt_nxrec) {
					bp->b_resid = bp->b_bcount;
					goto next;
				} else if (blkno > ri->rt_nxrec) {
					goto err;
				}
			} else
				ri->rt_nxrec = blkno + 1;

			if ((i = blkno - ri->rt_blkno) == 0)
				goto noseek;
			if (rc->rc_ctape) {
				printf("rt%d: can't seek on cartridge tape.\n",
					ri->ri_unit);
				goto err;
			}
			if (rfdebug || rtdebug)
				printf("rt%d: seek %d.\n", ri->ri_unit, i);
			rp->pb_t.tp_cmd = TPCMD_SPACEF;
			if (i < 0) {
				rp->pb_t.tp_ctl |= CTL_REVERSE;
				i = -i;
			}
			rp->pb_t.tp_nrec = i;
			rc->rc_busy = BUSY_TAPE;
			rc->rc_tact = ri;
			ri->ri_active = ACTIVE_SEEK;
			break;
		case ACTIVE_SEEKDONE:
			if (blkno != ri->rt_blkno)
				goto err;
		noseek:
			if (bp->b_flags & B_READ) {
				rp->pb_t.tp_cmd = TPCMD_READ;
				ri->rt_lastiow = 0;
			} else {
				rp->pb_t.tp_cmd = TPCMD_WRITE;
				ri->rt_lastiow = 1;
			}
			if (rc->rc_ctape) {
				i = vtop((caddr_t)&rc->rc_rb);
				rp->pb_t.tp_page = rfpage(i);
				rfsetaddr(rp->pb_t.tp_addr, i);
				rc->rc_rb.rb_gate = RBGATE_NORMAL;
				rfsetaddr(rc->rc_rb.rb_link, i);
			}
			rp->pb_t.tp_nrec = 0;
			i = bp->b_flags & B_PHYS ? (int)bp->b_un.b_addr
					: (int)vtop(bp->b_un.b_addr);
			if (rc->rc_ctape) {
				rc->rc_rb.rb_page = rfpage(i);
				rfsetaddr(rc->rc_rb.rb_addr, i);
			} else {
				rp->pb_t.tp_page = rfpage(i);
				rfsetaddr(rp->pb_t.tp_addr, i);
			}
			i = 0x100000 - (i & 0xfffff);	/* XXX */
			if (i < bp->b_bcount)
				printf("rt%d: bad transfer, bcount %d, max %d.\n",
					ri->ri_unit, bp->b_bcount, i);
			else
				i = bp->b_bcount;
			if (rc->rc_ctape)
				rc->rc_rb.rb_bcount = i;
			else
				rp->pb_t.tp_bufsiz = i;
			if (rftest && time.tv_usec/10000 < rferrrate)
				rp->pb_d.dp_cst = 0;
			if (rfdebug || rtdebug)
				printf("rt%d: %s, blkno %d, bufsiz %d.\n",
					ri->ri_unit,
					bp->b_flags & B_READ ? "read" : "write",
					blkno, i);
			rc->rc_busy = BUSY_TAPE;
			rc->rc_tact = ri;
			ri->ri_active = ACTIVE_IO;
			break;
		err:
			bp->b_flags |= B_ERROR;
		next:
			ri->rt_actf = bp->av_forw;
			iodone(bp);
			goto again;
		}
	}
	if (!rc->rc_busy)
		return;
	rp->pb_d.dp_ctl |= CTL_INTR;
	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
	rc->rc_ccb.ccb_ccw = CCBCCW_CLRINT;
	if (rftest) {
		rc->rc_ccb.ccb_gate = CCBGATE_OPEN;
		timeout(rfintr, rc->rc_ctlr, hz / 10);
	} else {
#ifdef M68020
		flushWriteQueue();
#endif M68020
		rc->rc_device->rf_atten = 1;
	}
}

rfintr(ctlr)
{
	register struct rf_ctlr *rc = rf_ctlr + ctlr;
	register struct rf_info *ri;
	register struct buf *bp;
	register union rf_pb *rp;
	register i = 0;

	if (!rc->rc_busy || rc->rc_ccb.ccb_gate != CCBGATE_OPEN)
		return 0;
	rp = rc->rc_pb;
	if (rc->rc_busy == BUSY_DISK) {
		struct buf *errf = 0, *errl;
		struct buf *forw;

#ifdef lint
		errl = 0;
#endif
		ri = rc->rc_dact;
		if ((bp = ri->rd_io) == 0)
			bp = ri->rd_actf;
		i = rp->pb_d.dp_cst & CST_ERROR;
		switch (ri->ri_active) {
		case ACTIVE_CMD:
			if (rp->pb_d.dp_cst & CST_DONE && i == 0) {
				if (rfdebug)
					rdprint(ri, rp, bp, "done");
			} else switch (i) {
			case ERR_SOFT:
				if (rfdebug)
					rdprint(ri, rp, bp, "soft error");
				break;
			default:
				rdprint(ri, rp, bp, "hard error");
				bp->b_flags |= B_ERROR;
			}
			ri->rd_io = 0;
			iodone(bp);
			ri->ri_active = 0;
			break;
		case ACTIVE_SEEK:
			if (rp->pb_d.dp_cst & CST_DONE && i == 0) {
				if (rfdebug)
					rdprint(ri, rp, bp, "seek, done");
			} else switch (i) {
			case ERR_SOFT:
				if (rfdebug)
					rdprint(ri, rp, bp, "seek, soft error");
				break;
			default:
				rdprint(ri, rp, bp, "seek, hard error");
			}
			ri->ri_active = ACTIVE_SEEKDONE;
			break;
		case ACTIVE_IO:
			if (ri->ri_dk >= 0)
				dk_busy &= ~(1 << ri->ri_dk);
			for (;;) {
				/* iodone may clobber av_forw */
				forw = bp->av_forw;
				if (rp->pb_d.dp_cst & CST_DONE && i == 0) {
					if (rfdebug)
						rdprint(ri, rp, bp, "done");
					bp->b_resid = bp->b_bcount -
						rp->pb_d.dp_bcount;
					iodone(bp);
				} else switch (i) {
				case ERR_SOFT:
					if (rfdebug)
						rdprint(ri,rp,bp,"soft error");
					bp->b_resid = bp->b_bcount -
						rp->pb_d.dp_bcount;
					iodone(bp);
					break;
				default:
					if (ri->ri_errcnt < rdretry) {
						if (rfdebug)
							rdprint(ri, rp, bp,
								"retrying");
					} else {
						rdprint(ri, rp, bp,
							"hard error");
						bp->b_flags |= B_ERROR;
						rdrecover(ri, rp, bp);
					}
					if (bp->b_flags & B_ERROR) {
						bp->b_error = 0;
						iodone(bp);
					} else {
						if (errf == 0)
							errf = bp;
						else
							errl->av_forw = bp;
						errl = bp;
					}
				}
				if ((bp = forw) == 0)
					break;
				rp++;
				i = rp->pb_d.dp_cst & CST_ERROR;
			}
			if (errf != 0) {
				ri->ri_errcnt++;
				ri->rd_io = errf;
				errl->av_forw = 0;
				ri->ri_active = ACTIVE_SEEKDONE;
			} else {
				ri->ri_errcnt = 0;
				ri->rd_io = 0;
				ri->ri_active = 0;
			}
			break;
		}
	} else {
		ri = rc->rc_tact;
		bp = ri->rt_actf;
		i = rp->pb_t.tp_cst & CST_ERROR;
		ri->rt_dst = rp->pb_t.tp_dst;
			/* record microstatus for rim44 only */
		ri->rt_mst = rc->rc_ctape ? rp->pb_t.tp_mst : 0;

		if (rtdebug)
		{
			printf("tape intr, ri->ri_active %d\n",ri->ri_active);
			rtprint(ri, rp, bp, "rfintr decoding");
		}

		switch (ri->ri_active) {
		case ACTIVE_CMD:
			if (rp->pb_t.tp_cst & CST_DONE && i == 0) {
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "done");
			} else switch (i) {
			case ERR_SOFT:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "soft error");
				break;
			default:
				rtprint(ri, rp, bp, "hard error");
				bp->b_flags |= B_ERROR;
			}
			ri->rt_actf = bp->av_forw;
			iodone(bp);
			ri->ri_active = 0;
			break;
		case ACTIVE_SEEK:
			/*
			 * Can we handle seek errors?
			 */
			if (rp->pb_t.tp_cst & CST_DONE && i == 0) {
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "done");
			} else switch (i) {
			case ERR_SOFT:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "soft error");
				break;
			default:
				rtprint(ri, rp, bp, "hard error");
				ri->rt_err = 1;
				bp->b_flags |= B_ERROR;
				break;
			}
			if (bp->b_flags & B_ERROR) {
				ri->rt_actf = bp->av_forw;
				iodone(bp);
				ri->ri_active = 0;
			} else {
				i = bdbtofsb(bp->b_blkno);
				if (i < ri->rt_blkno)
					ri->rt_blkno = i + rp->pb_t.tp_nrec;
				else
					ri->rt_blkno = i - rp->pb_t.tp_nrec;
				ri->ri_active = ACTIVE_SEEKDONE;
			}
			break;
		case ACTIVE_IO:
			if (rp->pb_t.tp_cst & CST_DONE && i == 0) {
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "done");
				if (rc->rc_ctape)
					bp->b_resid = 0;
				else
					bp->b_resid = bp->b_bcount -
						rp->pb_t.tp_retcnt;
				ri->ri_errcnt = 0;
			} else switch (i) {
			case ERR_SOFT:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "soft error");
				if (rc->rc_ctape)
					bp->b_resid = 0;
				else
					bp->b_resid = bp->b_bcount -
						rp->pb_t.tp_retcnt;
				ri->ri_errcnt = 0;
				break;
			case ERR_TIMEOUT:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "block short");
				if (rc->rc_ctape)
					bp->b_resid = 0;
				else
					bp->b_resid = bp->b_bcount -
						rp->pb_t.tp_retcnt;
				ri->ri_errcnt = 0;
				break;
			case ERR_FM:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "eof");
				bp->b_resid = bp->b_bcount;
				ri->ri_errcnt = 0;
				ri->rt_nxrec = ri->rt_blkno;
				break;
			case ERR_BLANK:
				rtprint(ri, rp, bp, "blank tape detected");
				bp->b_flags |= B_ERROR;
				ri->ri_errcnt = 0;
				break;
			case ERR_EOT:
				if (rfdebug || rtdebug)
					rtprint(ri, rp, bp, "end of tape");
				bp->b_flags |= B_ERROR;
				ri->ri_errcnt = 0;
				break;
			case ERR_WPROTECT:
				rtprint(ri, rp, bp, "tape is write protected");
				bp->b_flags |= B_ERROR;
				ri->ri_errcnt = 0;
				break;
			case ERR_OFFLINE:
				rtprint(ri, rp, bp, "drive is offline");
				bp->b_flags |= B_ERROR;
				ri->ri_errcnt = 0;
				break;
			case ERR_ILLPARAM:
				/* this should never happen */
				panic("rimfire bad param");
				break;
			default:
				if (ri->ri_errcnt < rtretry && !rc->rc_ctape) {
					if (rfdebug || rtdebug)
						rtprint(ri, rp, bp, "retrying");
					ri->ri_errcnt++;
				} else if (rtwritespace(rc, ri, rp, bp)) {
					ri->ri_errcnt++;
					/*
					 * A little hackery: 
					 * Diddle rt_blkno so it will stay 
					 * the same.
					 * This will prevent a seek backward 
					 * in rfstart() when we retry the
					 * operation.
					 */
					ri->rt_blkno--;
				} else {
					rtprint(ri, rp, bp, "hard error");
					bp->b_flags |= B_ERROR;
					ri->ri_errcnt = 0;
				}
			}
			if (ri->ri_errcnt == 0) {
				ri->rt_actf = bp->av_forw;
				iodone(bp);
			}
			if (rtdebug) 
				printf("rt_blkno = %d, going to incr\n",
					ri->rt_blkno);
			ri->rt_blkno++;
			ri->ri_active = 0;
		}
	}
	rc->rc_busy = 0;
	rfstart(rc);
	if (!rc->rc_busy) {
		rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
		rc->rc_ccb.ccb_ccw = CCBCCW_CLRINT;
		rc->rc_pb->pb_d.dp_cmd = CMD_CLRINT;
		if (rftest)
			rc->rc_ccb.ccb_gate = CCBGATE_OPEN;
		else {
#ifdef M68020
			flushWriteQueue();
#endif M68020
			rc->rc_device->rf_atten = 1;
		}
		if (rfwait(rc, "can't clear interrupt") < 0) {
			/* what do we do now? */
		}
	}
	return 1;
}

rtwritespace(rc, ri, rp, bp)
	register struct rf_ctlr *rc;
	register struct rf_info *ri;
	register union rf_pb *rp;
	register struct buf *bp;
{
	int gapsize = 1 << (ri->ri_errcnt - rtretry);

	if (!rc->rc_ctape && !(bp->b_flags & B_READ) ) {

		/* back up one record */

		printf("rt%d: bad spot on tape\n", ri->ri_unit);
		if (rficmd(rc, 0, TPCMD_SPACE, CTL_NORMAL|CTL_REVERSE,
			0, 0, bp->b_bcount, 1, 0, "can't back up") < 0)
		       return(0);

		/* write a big IRG */
		printf("rt%d: writing a %d x 3.5 inch IRG gap\n",
			ri->ri_unit, gapsize);

		if (rficmd(rc, 0, TPCMD_ERASE, CTL_NORMAL, 
			0, 0, 0, gapsize, 0, "can't write IRG") < 0)
		       return(0);

		return(1);
	} else {
		/* can't recover */
		return(0);
	}
}

/*
 * Busy wait for controller.
 * The delay should be generous.  Commands like defect mapping
 * can be slow (several seconds).
 */
rfwait(rc, mesg)
	register struct rf_ctlr *rc;
	char *mesg;
{
	register i;

	for (i = 5000000; --i >= 0 && rc->rc_ccb.ccb_gate != CCBGATE_OPEN;)
		;
	if (rc->rc_ccb.ccb_gate != CCBGATE_OPEN) {
		if (mesg != 0)
			printf("rf%d: %s, gate 0x%x.\n",
				rc->rc_ctlr, mesg, rc->rc_ccb.ccb_gate);
		return -1;
	}
	return 0;
}

/*
 * For tape, the arguments are:
 *	ri, cmd, ctl, retcnt, unused, bufsiz, nrec, addr.
 */
rfcmd(ri, cmd, ctl, cyl, head, sec, nsec, addr)
	register struct rf_info *ri;
{
	register struct buf *bp = &rfcbuf;

	spl5();
while (bp->b_flags & B_BUSY) {
		bp->b_flags |= B_WANTED;
		sleep((caddr_t)bp, PRIBIO+1);	/* let iowait() go first */
	}
	bp->b_flags = B_BUSY;
	spl0();
	bp->b_un.b_addr = (caddr_t)&rfcpb;
	bp->b_dev = ri->ri_istape ? rtmkdev(ri->ri_unit) :
		rdmkdev(ri->ri_unit, 0);
	rfcpb.pb_d.dp_cmd = cmd;
	rfcpb.pb_d.dp_page = rfpage(addr);
	rfcpb.pb_d.dp_head = head;
	rfcpb.pb_d.dp_ctl = ctl;
	rfcpb.pb_d.dp_cyl = cyl;
	rfcpb.pb_d.dp_sec = sec;
	rfcpb.pb_d.dp_nsec = nsec;
	rfsetaddr(rfcpb.pb_d.dp_addr, addr);
	if (ri->ri_istape)
		rtstrategy(bp);
	else
		rdstrategy(bp);
	iowait(bp);
	spl5();
	if (bp->b_flags & B_WANTED)
		wakeup((caddr_t)bp);
	bp->b_flags = 0;
	spl0();
}

/*
 * Run command without interrupts.
 * The controller has better be ready and the gate open.
 */
rficmd(rc, ri, cmd, ctl, cyl, head, sec, nsec, addr, mesg)
	register struct rf_ctlr *rc;
	register struct rf_info *ri;
	char *mesg;
{
	register retval = 0;

	if (rc == 0)
		rc = rf_ctlr + ri->ri_ctlr;
	rc->rc_pb->pb_d.dp_cmd = cmd;
	rc->rc_pb->pb_d.dp_page = rfpage(addr);
	rc->rc_pb->pb_d.dp_head = head;
	if (ri)
		ctl |= ri->ri_istape ? ri->ri_slave << CTL_TSSHIFT :
			ri->ri_slave;
	rc->rc_pb->pb_d.dp_ctl = ctl;
	rc->rc_pb->pb_d.dp_cyl = cyl;
	rc->rc_pb->pb_d.dp_sec = sec;
	rc->rc_pb->pb_d.dp_nsec = nsec;
	rfsetaddr(rc->rc_pb->pb_d.dp_addr, addr);
	if (rftest)
		rc->rc_pb->pb_d.dp_cst = CST_DONE;
	else
		rc->rc_pb->pb_d.dp_cst = 0;
	rc->rc_pb->pb_d.dp_dst = 0;

	if (ri && ri->ri_istape && (cmd == TPCMD_REWIND||cmd == TPCMD_OFFLINE))
		/*
		 * Set the rewind time if necessary.
		 */
		ri->rt_rewtime = time.tv_sec;

	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
	rc->rc_ccb.ccb_ccw = CCBCCW_NORMAL;
	if (rftest)
		rc->rc_ccb.ccb_gate = CCBGATE_OPEN;
	else {
#ifdef M68020
		flushWriteQueue();
#endif M68020
		rc->rc_device->rf_atten = 1;
	}
	if (rfwait(rc, "rficmd timeout") < 0) {
		if (mesg != 0)
			printf("r%c%d: %s.\n",
				ri == 0 ? 'f' : (ri->ri_istape ? 't' : 'd'),
				ri ? ri->ri_unit : rc->rc_ctlr, mesg);
		retval = -1;
		goto done;
	}
	if ((rc->rc_pb->pb_d.dp_cst & CST_DONE) == 0
	    || (rc->rc_pb->pb_d.dp_cst & CST_ERROR) != 0
	    && (rc->rc_pb->pb_d.dp_cst & CST_ERROR) != ERR_SOFT) {
		if (mesg != 0) {
			if (ri) {
				if (ri->ri_istape)
					rtprint(ri, rc->rc_pb,
						(struct buf *)0, mesg);
				else
					rdprint(ri, rc->rc_pb,
						(struct buf *)0, mesg);
			} else {
				rtdebugprint(rc->rc_pb,
						(struct buf *)0, mesg);
			}
		}
		retval = -1;
		goto done;
	}
done:
	if (ri->ri_istape) {
		/* 
		 * Record status field(s):
		 * dst always, mst if rim44.
		 */
		ri->rt_dst = rc->rc_pb->pb_t.tp_dst;
		ri->rt_mst = rc->rc_ctape ? rc->rc_pb->pb_t.tp_mst : 0;
	}
	return retval;
}

/*
 * Configure the controller based on the information in rc_dinfo[].
 */
rfconfig(rc)
	register struct rf_ctlr *rc;
{
	rfsetdr(rc, rf_dr[rc->rc_ctlr]);
	rfcmd(rc->rc_disk, DPCMD_CONFIG, CTL_NORMAL, 0, 0, 0, 0,
		vtop((caddr_t)rf_dr[rc->rc_ctlr]));
}

rficonfig(rc)
	register struct rf_ctlr *rc;
{
	rfsetdr(rc, rf_dr[rc->rc_ctlr]);
	return rficmd(rc, (struct rf_info *)0, DPCMD_CONFIG, CTL_NORMAL,
		0, 0, 0, 0, vtop((caddr_t)rf_dr[rc->rc_ctlr]),
		"can't config controller");
}

rfsetdr(rc, dr)
	register struct rf_ctlr *rc;
	register struct rf_dr *dr;
{
	register struct rf_info *ri;
	register i;

	for (i = 0; i < RD_NSLAVE; i++) {
		dr[i].dr_type = 0;
		dr[i].dr_ncyl = 0;
		dr[i].dr_nhead = 0;
		dr[i].dr_nsec = 1;
		dr[i].dr_secsize = DEV_BSIZE;
	}
	for (ri = rc->rc_disk; ri; ri = ri->ri_link) {
		i = ri->ri_slave;
		dr[i].dr_type = ri->rd_type;
		dr[i].dr_ncyl = ri->rd_ncyl + ri->rd_acyl - 1;
		dr[i].dr_nhead = ri->rd_nhead - 1;
		dr[i].dr_nsec = ri->rd_nsec;
		dr[i].dr_secsize = DEV_BSIZE;
	}
}

rfreset(rc)
	register struct rf_ctlr *rc;
{
	register struct rf_info *ri;
	int s;

	s = spl5();
	rc->rc_device->rf_reset = 1;		/* reset */
	RF_SCP->scp_bus = SCPBUS_16;
	rfsetaddr(RF_SCP->scp_addr, vtop((caddr_t)&rc->rc_scb));
	rc->rc_scb.scb_03 = 0x03;
	rfsetaddr(rc->rc_scb.scb_addr, vtop((caddr_t)&rc->rc_ccb));
	rfsetaddr(rc->rc_ccb.ccb_addr, vtop((caddr_t)rc->rc_pb));
	rc->rc_ccb.ccb_gate = CCBGATE_CLOSED;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	rc->rc_device->rf_atten = 1;		/* initialize */
	if (rfwait(rc, "cat't initialize controller") < 0)
		goto bad;
	if (rficonfig(rc) < 0)
		goto bad;
	/*
	 * Let the disk operations restart.
	 */
	for (ri = rc->rc_disk; ri; ri = ri->ri_link) {
		rficmd(rc, ri, DPCMD_RESET, CTL_NORMAL, 0, 0, 0, 0, 0,
			"can't reset drive");
		ri->rd_cylin = 0;
		switch (ri->ri_active) {
		case ACTIVE_SEEK:
		case ACTIVE_CMD:
			ri->ri_active = 0;
			break;
		case ACTIVE_IO:
			ri->ri_active = ACTIVE_SEEKDONE;
			break;
		}
	}
	/*
	 * But throw away all pending tape requests.
	 * And set the hard error flag to abort all future operations
	 * until we reopen.
	 */
	for (ri = rc->rc_tape; ri; ri = ri->ri_link) {
		register struct buf *bp;

		rficmd(rc, ri, TPCMD_RESET, CTL_NORMAL, 0, 0, 0, 0, 0,
			"can't reset drive");
		while ((bp = ri->rt_actf) != 0) {
			ri->rt_actf = bp->av_forw;
			bp->b_flags |= B_ERROR;
			iodone(bp);
		}
		if (ri->rt_open)
			ri->rt_err = 1;
		ri->ri_active = 0;
	}
	/*
	 * If we were doing something, then restart it.
	 */
	if (rc->rc_busy) {
		rc->rc_busy = 0;
		rfstart(rc);
	}
	splx(s);
	return 0;
bad:
	splx(s);
	return -1;
}
#endif
