V9/sys/dev.old/uda.c

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

#include "ra.h"
/*
 * UDA50/RAxx disk device driver
 * this should really be two drivers!
 * can only handle unit numbers less than 8 for now
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/vm.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/dk.h"
#include "../h/cpu.h"
#include "../h/cmap.h"
#include "../h/file.h"

/*
 * Parameters for the communications area
 */

/* ring sizes <= 4 for now, as sop to broken emulex controller */

#define	NRSPL2	5
#define	NCMDL2	5
#define	NRSP	(1<<NRSPL2)
#define	NCMD	(1<<NCMDL2)

#include "../h/udareg.h"
#include "../h/udaioc.h"
#include "../h/mscp.h"

#define	SECTOR	512		/* size of an MSCP logical block */
#define	MAXUNIT	8		/* largest unit number we can handle */

struct uda_softc {
	short	sc_state;	/* state of controller */
	short	sc_flags;
	struct udadevice *sc_addr;
	int	sc_ubainfo;	/* Unibus mapping info */
	struct uda *sc_uda;	/* Unibus address of uda struct */
	int	sc_ivec;	/* interrupt vector address */
	short	sc_credits;	/* transfer credits */
	short	sc_lastcmd;	/* pointer into command ring */
	short	sc_lastrsp;	/* pointer into response ring */
	short	sc_ctime;	/* controller timeout interval */
} uda_softc[NUDA];

/*
 * sc_flags
 */

#define	SCCRED	01		/* someone waiting for credits */
#define	SCMAP	02		/* control structure is mapped */
#define	SCFIRST	04		/* allow packets without credit */

/*
 * Controller states
 */
#define	S_IDLE	0		/* hasn't been initialized */
#define	S_STEP1	1		/* doing step 1 init */
#define	S_STEP2	2		/* doing step 2 init */
#define	S_STEP3	3		/* doing step 3 init */
#define	S_SCHAR	4		/* doing "set controller characteristics" */
#define	S_RUN	5		/* running */

/*
 * communication area, per controller
 * mapped into UNIBUS space
 */

struct uda {
	struct udaca	uda_ca;		/* communications area */
	struct mscp	uda_rsp[NRSP];	/* response packets */
	struct mscp	uda_cmd[NCMD];	/* command packets */
} uda[NUDA];

/*
 * a word to the wise:
 * part of the minor device number selects a drive number
 * this is not necessarily the same as the physical unit number
 * in an attempt to clear this up, the former is referred to
 * as the `dev unit,' the latter as the `real unit'
 * most places use the real unit
 */
#define	NPART	8		/* 8 disk partitions */
#define	PART(x)	((x) & 07)	/* minor device -> partition */
#define	DUNIT(x) (((x) >> 3) & 07)	/* minor device -> dev unit */

#define	HUGE	0x7fffffff

struct size {
	daddr_t	nblocks;
	daddr_t	blkoff;
} ra_sizes[NPART] ={
	10240,	0,		/* like emulex */
	20480,	10240,		/* ra81 has 891072 blocks */
	249848,	30720,
	249848,	280568,
	249848,	530416,
	110808,	780264,
	749544,	30720,		/* slices 2,3,4 */
	HUGE,	0,		/* whole drive */
};

/* device flags (ui_flags) */
#define	DUP	1	/* up */
#define	DGTUNIT	2	/* needs GTUNIT */

#define	ui_offl	ui_type
#define	MAXOFFL	10

/* per unit stuff */
struct ud_unit ud_units[MAXUNIT];	/* per real unit */
int ud_open[NRA];			/* per dev unit */

char	ud_rflags;	/* replacement flags.  should be per controller */
#define	RPLOCK	01	/* someone is doing a replacement */
#define	RPWANT	02	/* somebody else wants to */
#define	RPDONE	04	/* replacement finished */

int	ud_rret;	/* status returned from replacement */

int	udprobe(), udslave(), udattach(), udintr();
struct	mscp *udgetcp();
struct	uba_ctlr *udminfo[NUDA];
struct	uba_device *uddinfo[NRA];	/* per dev unit */
struct	uba_device *udip[NUDA][MAXUNIT]; /* (ctlr, realunit) -> device */

u_short	udstd[] = { 0777550, 0 };
struct	uba_driver udadriver =
 { udprobe, udslave, udattach, 0, udstd, "ra", uddinfo, "uda", udminfo, 0 };
struct	buf rudbuf[NRA];		/* raw io buffer header */
struct	buf rctbuf[NRA];		/* buffer for RCT io */
struct	buf udutab[NRA];		/* drive queues */
struct	buf udwtab[NUDA];		/* I/O wait queue, per controller */

#define	b_qsize		b_resid		/* queue size per drive, in udutab */
#define	b_ubinfo	b_resid		/* Unibus mapping info, per buffer */

udprobe(reg, ctlr)
caddr_t reg;
int ctlr;
{	register int br, cvec;
	register struct uda_softc *sc = &uda_softc[ctlr];

#ifdef lint
	br = 0; cvec = br; br = cvec; reg = reg;
	udread(0); udwrite(0); udreset(0); udintr(0);
#endif
	/* SHOULD CHECK THAT IT REALLY IS A UDA */
	br = 0x15;
	cvec = sc->sc_ivec = (uba_hd[numuba].uh_lastiv -= 4);
	sc->sc_addr = (struct udadevice *)reg;
	return(1);
}

udslave(ui, reg)
	struct uba_device *ui;
	caddr_t reg;
{
	/*
	 * TOO HARD TO FIND OUT IF DISK IS THERE UNTIL
	 * INITIALIZED.  WE'LL FIND OUT WHEN WE FIRST
	 * TRY TO ACCESS IT.
	 */
#ifdef lint
	ui = ui; reg = reg;
#endif
	return(1);
}

udattach(ui)
	register struct uba_device *ui;
{

	if (ui->ui_dk > 0)
		dk_mspw[ui->ui_dk] = 1.0 / (60 * 51 * 256);	/* approx */
	ui->ui_flags = 0;
	udip[ui->ui_ctlr][ui->ui_slave] = ui;
	printf("\n");		/* we're expected to babble */
	ud_units[ui->ui_unit].radsize = (daddr_t)HUGE;
}

udopen(dev)
dev_t dev;
{
	register int unit;

	unit = DUNIT(minor(dev));
	if (unit >= NRA) {
		u.u_error = ENXIO;
		return;
	}
	ud_open[unit] |= 1 << PART(minor(dev));
}

udclose(dev)
dev_t dev;
{
	register int unit;
	register struct mscp *mp;
	struct uba_device *ui;
	struct uba_ctlr *um;
	struct uda_softc *sc;

	unit = DUNIT(minor(dev));
	ud_open[unit] &=~ (1 << PART(minor(dev)));
	if (ud_open[unit])
		return;
	/*
	 * last close on this drive
	 * put it offline
	 */
	ui = uddinfo[unit];
	if (ui->ui_flags == 0)
		return;
	um = ui->ui_mi;
	sc = &uda_softc[um->um_ctlr];
	if (udcredit(sc, 1) == 0)
		return;
	if ((mp = udgetcp(um)) == NULL)
		return;
	mp->mscp_opcode = M_OP_AVAIL;
	mp->mscp_unit = ui->ui_slave;
	mp->mscp_modifier = 0;
	mp->mscp_cmdref = (long)mp;	/* any old number */
	udsend(mp, sc);
	ui->ui_flags = 0;	/* drive is offline now */
}
	
/*
 * Initialize a UDA.  Set up UBA mapping registers,
 * initialize data structures, and perform hardware
 * initialization sequence.
 */

udinit(d)
{
	register struct uda_softc *sc;
	register struct uba_ctlr *um;
	register struct buf *bp;
	register struct mscp *mp;
	register int i;
	int s;

	sc = &uda_softc[d];
	um = udminfo[d];
	um->um_tab.b_active++;
	if ((sc->sc_flags & SCMAP) == 0) {
		sc->sc_ubainfo = uballoc(um->um_ubanum, (caddr_t)&uda[d],
		    sizeof (struct uda), 0);
		sc->sc_uda = (struct uda *)(sc->sc_ubainfo & 0x3ffff);
		sc->sc_flags |= SCMAP;
	}
	for (i = 0; i < NRA; i++)
		if (uddinfo[i] && uddinfo[i]->ui_mi == um)
			uddinfo[i]->ui_flags = 0;
	if (udhwinit(d) == 0)
		return;
	sc->sc_state = S_SCHAR;
	bp = &udwtab[d];
	bp->av_forw = bp->av_back = bp;
	sc->sc_lastcmd = 0;
	sc->sc_lastrsp = 0;
	s = spl5();
	if ((mp = udgetcp(um)) == NULL)
		panic("uda: no packets");
	mp->mscp_opcode = M_OP_STCON;
	mp->mscp_cntflgs = M_CF_ATTN|M_CF_MISC|M_CF_THIS;
	sc->sc_flags |= SCFIRST;
	udsend(mp, sc);
	splx(s);
	return;
}

/*
 * hardware initialization handshake
 * for simplicity, don't bother with init interrupts
 * returns nonzero if ok
 */

#define	UDA_STEPS	(UDA_ERR|UDA_STEP4|UDA_STEP3|UDA_STEP2|UDA_STEP1)

udhwinit(d)
int d;
{
	register struct uda_softc *sc;
	register struct udadevice *udaddr;
	register int i;
	register struct uda *ud;
	register struct uda *uud;
	time_t out;
	int s;

	sc = &uda_softc[d];
	udaddr = sc->sc_addr;
	out = time + 11;
	udaddr->udaip = 0;		/* start initialization */
	s = spl0();
	while((udaddr->udasa & (UDA_ERR|UDA_STEP1)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP1) {
		porterror(udaddr->udasa, d);
		splx(s);
		return (0);
	}
	sc->sc_state = S_STEP1;
	sc->sc_credits = 0;
	udaddr->udasa = UDA_ERR|(NCMDL2<<11)|(NRSPL2<<8)|(sc->sc_ivec/4);
	/* no diagnostic wrap, no interrupts during initialization */
	out = time + 11;
	while((udaddr->udasa & (UDA_ERR|UDA_STEP2)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP2) {
		porterror(udaddr->udasa, d);
		splx(s);
		return (0);
	}
	sc->sc_state = S_STEP2;
	/* assert that &ringbase & 03 == 0 */
	udaddr->udasa = (short)&sc->sc_uda->uda_ca.ca_ringbase | UDA_PI;
	out = time + 11;
	while((udaddr->udasa & (UDA_ERR|UDA_STEP3)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP3) {
		porterror(udaddr->udasa, d);
		splx(s);
		return (0);
	}
	sc->sc_state = S_STEP3;
	udaddr->udasa = ((long)&sc->sc_uda->uda_ca.ca_ringbase) >> 16;
	out = time + 11;
	while((udaddr->udasa & (UDA_ERR|UDA_STEP4)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP4) {
		porterror(udaddr->udasa, d);
		splx(s);
		return (0);
	}
	uud = sc->sc_uda;
	ud = &uda[d];
	for (i = 0; i < NRSP; i++) {
		ud->uda_ca.ca_rspdsc[i] = UDA_OWN|UDA_INT|
			(long)&uud->uda_rsp[i].mscp_cmdref;
		ud->uda_rsp[i].mscp_dscptr = &ud->uda_ca.ca_rspdsc[i];
		ud->uda_rsp[i].mscp_header.uda_msglen =
			sizeof(struct mscp);
	}
	for (i = 0; i < NCMD; i++) {
		ud->uda_ca.ca_cmddsc[i] = UDA_INT|
			(long)&uud->uda_cmd[i].mscp_cmdref;
		ud->uda_cmd[i].mscp_dscptr = &ud->uda_ca.ca_cmddsc[i];
		ud->uda_cmd[i].mscp_header.uda_msglen =
			sizeof(struct mscp);
	}
	if (udaddr->udasa & UDA_ERR) {
		porterror(udaddr->udasa, d);
		splx(s);
		return (0);
	}
	udaddr->udasa = UDA_GO;		/* finish init */
	splx(s);
	return (1);
}

static char *pmesg[] = {
	"(error 0?)",
	"data read",
	"data write",
	"parity",
	"ram parity",
	"rom parity",
	"ring read",
	"ring write",
	"interrupt master",
	"host timeout",
	"credit limit",
	"unibus master",
	"diag controller",
	"instruction loop timeout",
	"invalid connection id",
	"interrupt write",
	"maint region",
	"maint load",
	"ram",
	"init sequence",
	"protocol incompat",
	"purge/poll",
};

porterror(sa, d)
short sa;
int d;
{
	register struct uda_softc *sc;
	int n;

	sc = &uda_softc[d];
	printf("uda%d port error, sa x%x ", d, sa);
	if(!(sa & UDA_ERR)) {
		printf("(time out?), state %d\n", sc->sc_state);
		sc->sc_state = S_IDLE;
		return;
	}
	n = (sa & 0x3ff);
	if(n > sizeof(pmesg)/sizeof(pmesg[0]))
		printf("(?)\n");
	else
		printf("%s\n", pmesg[n]);
	sc->sc_state = S_IDLE;
}

udstrategy(bp)
	register struct buf *bp;
{
	register struct uba_device *ui;
	register struct uba_ctlr *um;
	register struct buf *dp;
	register int unit;
	int part = PART(minor(bp->b_dev));
	daddr_t sz, maxsz;
	int s;

	sz = (bp->b_bcount+(SECTOR-1)) / SECTOR;
	unit = dkunit(bp);
	if (unit >= NRA) {
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	ui = uddinfo[unit];
	um = ui->ui_mi;
	if (ui == 0 || ui->ui_alive == 0) {
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	maxsz = ud_units[unit].radsize - ra_sizes[part].blkoff;
	if (maxsz > ra_sizes[part].nblocks)
		maxsz = ra_sizes[part].nblocks;
	if (bp != &rctbuf[unit]) {
		if (bp->b_blkno == maxsz) {
			bp->b_resid = bp->b_bcount;
			iodone(bp);
			return;
		}
		if (bp->b_blkno < 0 || bp->b_blkno+sz > maxsz) {
			bp->b_flags |= B_ERROR;
			iodone(bp);
			return;
		}
	}
	s = spl5();
	/*
	 * Link the buffer onto the drive queue
	 */
	dp = &udutab[ui->ui_unit];
	if (dp->b_actf == 0)
		dp->b_actf = bp;
	else
		dp->b_actl->av_forw = bp;
	dp->b_actl = bp;
	bp->av_forw = NULL;
	/*
	 * Link the drive onto the controller queue (why bother?)
	 */
	if (dp->b_active == 0) {
		dp->b_forw = NULL;
		if (um->um_tab.b_actf == NULL)
			um->um_tab.b_actf = dp;
		else
			um->um_tab.b_actl->b_forw = dp;
		um->um_tab.b_actl = dp;
		dp->b_active = 1;
	}
	if (um->um_tab.b_active == 0)
		(void) udstart(um);
	splx(s);
}

udstart(um)
	register struct uba_ctlr *um;
{
	register struct buf *bp, *dp;
	register struct mscp *mp;
	register struct udadevice *udaddr;
	register struct uda_softc *sc;
	register struct uba_device *ui;
	int i;

	sc = &uda_softc[um->um_ctlr];
	
loop:
	if ((dp = um->um_tab.b_actf) == NULL) {
		/*
		 * Release uneeded UBA resources and return
		 */
		um->um_tab.b_active = 0;
		return (0);
	}
	if ((bp = dp->b_actf) == NULL) {
		/*
		 * No more requests for this drive, remove
		 * from controller queue and look at next drive.
		 * We know we're at the head of the controller queue.
		 */
		dp->b_active = 0;
		um->um_tab.b_actf = dp->b_forw;
		goto loop;
	}
	um->um_tab.b_active++;
	udaddr = sc->sc_addr;
	if ((udaddr->udasa&UDA_ERR) || sc->sc_state != S_RUN) {
		if(sc->sc_state != S_IDLE) {
			harderr(bp, "ra");
			printf("uda%d udasa x%x, state %d\n",
				um->um_ctlr, udaddr->udasa&0xffff, sc->sc_state);
		}
		udinit(um->um_ctlr);
		/* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE UDRESET */
		return (0);
	}
	ui = uddinfo[dkunit(bp)];
	/*
	 * get a packet, fill it in, and send it
	 */
	if (udcredit(sc, 0) == 0)
		return (0);
	if ((mp = udgetcp(um)) == NULL)
		return (0);
	if (ui->ui_flags == 0) {	/* not online */
		if (++ui->ui_offl > MAXOFFL) {
			ui->ui_offl = 0;
			printf("uda%d ra%d offline\n", um->um_ctlr, ui->ui_slave);
			bp->b_flags |= B_ERROR;
			iodone(bp);
			dp->b_actf = bp->av_forw;
			if (dp->b_forw) {
				um->um_tab.b_actf = dp->b_forw;
				um->um_tab.b_actl->b_forw = dp;
				um->um_tab.b_actl = dp;
				dp->b_forw = NULL;
			}
			goto loop;
		}
		mp->mscp_opcode = M_OP_ONLIN;
		mp->mscp_unit = ui->ui_slave;
		dp->b_active = 2;	/* MAGIC */
		um->um_tab.b_actf = dp->b_forw;	/* remove from controller q */
		udsend(mp, sc);
		goto loop;
	}
	ui->ui_offl = 0;
	if(ui->ui_flags & DGTUNIT) {
		mp->mscp_opcode = M_OP_GTUNT;
		mp->mscp_unit = ui->ui_slave;
		udsend(mp, sc);
		ui->ui_flags &= ~DGTUNIT;
		goto loop;
	}
	if ((i = ubasetup(um->um_ubanum, bp, UBA_WANTBDP|UBA_CANTWAIT)) == 0) {
		/* this is issued just to get an interrupt */
		/* to try ubasetup again, a doubtful procedure */
		mp->mscp_opcode = M_OP_GTUNT;
		mp->mscp_unit = ui->ui_slave;
		udsend(mp, sc);
		return(1);		/* wait for interrupt */
	}
	mp->mscp_cmdref = (long)bp;	/* pointer to get back */
	mp->mscp_opcode = bp->b_flags&B_READ ? M_OP_READ : M_OP_WRITE;
	mp->mscp_unit = ui->ui_slave;
	mp->mscp_lbn = bp->b_blkno + ra_sizes[PART(minor(bp->b_dev))].blkoff;
	mp->mscp_bytecnt = bp->b_bcount;
	/* address; datapath number in high byte */
	mp->mscp_buffer = (i & 0x3ffff) | (((i>>28)&0xf)<<24);
	bp->b_ubinfo = i;		/* save mapping info */
	udsend(mp, sc);
	if (ui->ui_dk >= 0) {
		dk_busy |= 1<<ui->ui_dk;
		dp->b_qsize++;
		dk_xfer[ui->ui_dk]++;
		dk_wds[ui->ui_dk] += bp->b_bcount>>6;
		/* the way initialization is done now, this is useless */
	}

	/*
	 * Move drive to the end of the controller queue
	 */
	if (dp->b_forw != NULL) {
		um->um_tab.b_actf = dp->b_forw;
		um->um_tab.b_actl->b_forw = dp;
		um->um_tab.b_actl = dp;
		dp->b_forw = NULL;
	}
	/*
	 * Move buffer to I/O wait queue
	 */
	dp->b_actf = bp->av_forw;
	dp = &udwtab[um->um_ctlr];
	bp->av_forw = dp;
	bp->av_back = dp->av_back;
	dp->av_back->av_forw = bp;
	dp->av_back = bp;
	goto loop;
}

/*
 * UDA interrupt routine.
 */

long ud_stray;

udintr(d)
	int d;
{
	register struct uba_ctlr *um;
	register struct udadevice *udaddr;
	register int i;
	register struct uda_softc *sc;
	register struct uda *ud;

	if (d < 0 || d >= NUDA)
		panic("udintr");
	um = udminfo[d];
	sc = &uda_softc[d];
	ud = &uda[d];
	udaddr = sc->sc_addr;
	switch (sc->sc_state) {
	case S_IDLE:
		printf("uda%d: stray intr, sa x%x\n", d, udaddr->udasa);
		return;

	case S_SCHAR:
	case S_RUN:
		break;

	default:
		printf("uda%d: intr in unknown state %d ignored\n",
			d, sc->sc_state);
		porterror(udaddr->udasa, d);
		return;
	}
	if (udaddr->udasa&UDA_ERR) {
		printf("uda%d: fatal error, sa x%x\n",
			d, udaddr->udasa&0xffff);
		udaddr->udaip = 0;
		sc->sc_state = S_IDLE;
	}
	if (ud->uda_ca.ca_bdp == 0
	&&  ud->uda_ca.ca_rspint == 0
	&&  ud->uda_ca.ca_cmdint == 0)
		ud_stray++;		/* shouldn't happen */
	/*
	 * buffer purge request
	 */
	if (ud->uda_ca.ca_bdp) {
		/*
		 * THIS IS A KLUDGE.
		 * Maybe we should change the entire
		 * UBA interface structure.
		 */
		int s = spl7();

		i = um->um_ubinfo;
		um->um_ubinfo = ud->uda_ca.ca_bdp<<28;
		ubapurge(um);
		um->um_ubinfo = i;
		(void) splx(s);
		ud->uda_ca.ca_bdp = 0;
		udaddr->udasa = 0;	/* signal purge complete */
	}
	/*
	 * response ring transition
	 */
	if (ud->uda_ca.ca_rspint) {
		ud->uda_ca.ca_rspint = 0;
		for (i = sc->sc_lastrsp;; i++) {
			i %= NRSP;
			if (ud->uda_ca.ca_rspdsc[i]&UDA_OWN)
				break;
			udrsp(um, ud, sc, i);
			ud->uda_ca.ca_rspdsc[i] |= UDA_OWN;
		}
		sc->sc_lastrsp = i;
	}
	/*
	 * command ring transition
	 */
	if (ud->uda_ca.ca_cmdint)
		ud->uda_ca.ca_cmdint = 0;
	(void) udstart(um);
}

/*
 * send a packet to the uda
 */

udsend(mp, sc)
struct mscp *mp;
struct uda_softc *sc;
{
	register short junk;

	if ((sc->sc_flags & SCFIRST) == 0) {
		if (sc->sc_credits < 2)
			panic("udsend no credits");
		sc->sc_credits--;
	}
	*((long *)mp->mscp_dscptr) |= UDA_OWN | UDA_INT;
	junk = sc->sc_addr->udaip;		/* trigger polling */
}

/*
 * check whether a packet may be sent
 * wait until possible if desired
 * call at spl6, and stay there until you've sent the packet
 * else your credit may be stolen
 */

udcredit(sc, wait)
register struct uda_softc *sc;
int wait;
{

	if (wait == 0 && sc->sc_credits < 2)
		return (0);
	while (sc->sc_credits < 2) {
		sc->sc_flags |= SCCRED;
		sleep((caddr_t)&sc->sc_credits, PRIBIO);
	}
	return (1);
}
	
/*
 * Process a response packet
 */
udrsp(um, ud, sc, i)
	register struct uba_ctlr *um;
	register struct uda *ud;
	register struct uda_softc *sc;
	int i;
{
	register struct mscp *mp;
	register struct uba_device *ui;
	register struct buf *dp, *bp;
	register int st;
	struct ud_unit *up;

	mp = &ud->uda_rsp[i];
	st = mp->mscp_status;
	mp->mscp_header.uda_msglen = sizeof (struct mscp);
	sc->sc_credits += mp->mscp_header.uda_credits & 0xf;
	sc->sc_flags &=~ SCFIRST;
	if (sc->sc_flags & SCCRED) {
		sc->sc_flags &=~ SCCRED;
		wakeup((caddr_t)&sc->sc_credits);
	}
	if((mp->mscp_header.uda_credits & 0xf0) > 0x10)	/* credits only */
		return;
	/*
	 * If it's an error log message (datagram),
	 * pass it on for more extensive processing.
	 */
	if ((mp->mscp_header.uda_credits & 0xf0) == 0x10) {
		uderror(um, (struct mslg *)mp);
		return;
	}
	if (mp->mscp_unit >= MAXUNIT)
		return;
	ui = udip[um->um_ctlr][mp->mscp_unit];
	switch (mp->mscp_opcode) {
	case M_OP_STCON|M_OP_END:
		if ((st & M_ST_MASK) == M_ST_SUCC) {
			sc->sc_state = S_RUN;
			printf("uda%d run\n", um->um_ctlr);
		}
		else {
			sc->sc_state = S_IDLE;
			printf("uda%d: can't set ctlr char, status x%x\n",
				um->um_ctlr, st);
		}
		sc->sc_ctime = mp->mscp_un.mscp_setcntchar.Mscp_hsttmo;
		um->um_tab.b_active = 0;
		break;

	case M_OP_ONLIN|M_OP_END:
		/*
		 * Link the drive onto the controller queue
		 */
		if(ui == 0) {
bad:
			printf("uda%d ra%d op x%x st x%x unexpected\n",
				um->um_ctlr, mp->mscp_unit, mp->mscp_opcode, st);
			return;
		}
		dp = &udutab[ui->ui_unit];
		dp->b_forw = NULL;
		if (um->um_tab.b_actf == NULL)
			um->um_tab.b_actf = dp;
		else
			um->um_tab.b_actl->b_forw = dp;
		um->um_tab.b_actl = dp;
		if ((st & M_ST_MASK) == M_ST_SUCC) {
			ui->ui_flags = DUP;	/* mark it online */
			ud_units[ui->ui_unit].radsize = (daddr_t)mp->mscp_untsize;
		} else {
			printf("uda%d ra%d online failed; status x%x\n",
				um->um_ctlr, mp->mscp_unit, st);
			while (bp = dp->b_actf) {
				dp->b_actf = bp->av_forw;
				bp->b_flags |= B_ERROR;
				iodone(bp);
			}
		}
		ui->ui_flags |= DGTUNIT;
		dp->b_active = 1;
		break;

	case M_OP_AVATN:	/* is this the only kind of attn? */
		if(ui == 0)
			goto bad;
		ui->ui_flags = 0;	/* it went offline, get status */
		break;

	case M_OP_READ|M_OP_END:
	case M_OP_WRITE|M_OP_END:
		if(ui == 0)
			goto bad;
		for (bp = udwtab[um->um_ctlr].b_actf; bp; bp = bp->av_forw)
			if (bp == (struct buf *)mp->mscp_cmdref)
				break;
		if (bp == NULL) {
			printf("uda%d ra%d: unknown cmdref x%x\n",
				um->um_ctlr, mp->mscp_unit, mp->mscp_cmdref);
			return;
		}
		ubarelse(um->um_ubanum, (int *)&bp->b_resid);
		/*
		 * Unlink buffer from I/O wait queue.
		 */
		bp->av_back->av_forw = bp->av_forw;
		bp->av_forw->av_back = bp->av_back;
		dp = &udutab[ui->ui_unit];
		/* do we have drive queues just to gather statistics? */
		if (ui->ui_dk >= 0)
			if (--dp->b_qsize == 0)
				dk_busy &= ~(1<<ui->ui_dk);
		if (st == M_ST_OFFLN || st == M_ST_AVLBL) {
			ui->ui_flags = DGTUNIT;	/* mark unit offline, GTUNT */
			/*
			 * Link the buffer onto the front of the drive queue
			 */
			if ((bp->av_forw = dp->b_actf) == 0)
				dp->b_actl = bp;
			dp->b_actf = bp;
			/*
			 * Link the drive onto the controller queue
			 */
			if (dp->b_active == 0) {
				dp->b_forw = NULL;
				if (um->um_tab.b_actf == NULL)
					um->um_tab.b_actf = dp;
				else
					um->um_tab.b_actl->b_forw = dp;
				um->um_tab.b_actl = dp;
				dp->b_active = 1;
			}
			return;
		}
		if ((st & M_ST_MASK) != M_ST_SUCC) {
			harderr(bp, "ra");
			printf("uda%d; status x%x\n", um->um_ctlr, st);
			bp->b_flags |= B_ERROR;
			if (st == 0x68)	/* for wretched optical disk */
				bp->b_error = ENXIO;
		}
		bp->b_resid = bp->b_bcount - mp->mscp_bytecnt;
		iodone(bp);
		break;

	case M_OP_GTUNT|M_OP_END:
		if(ui == 0)
			goto bad;
		up = ud_units + ui->ui_unit;
		ui->ui_flags &= ~DGTUNIT;
		up->rctsize = mp->mscp_rctsize;
		up->medium = mp->mscp_mediaid;
		up->tracksz = mp->mscp_track;
		up->groupsz = mp->mscp_group;
		up->cylsz = mp->mscp_cylinder;
		up->rbns = mp->mscp_rbns;
		up->copies = mp->mscp_rctcpys;
		break;

	case M_OP_REPLC|M_OP_END:
		if (ui == 0)
			goto bad;
		ud_rret = 0;
		if ((st & M_ST_MASK) != M_ST_SUCC) {
			printf("uda%d ra%d bad replace, status x%x\n",
				um->um_ctlr, mp->mscp_unit, st);
			ud_rret = EIO;
		}
		ud_rflags |= RPDONE;
		wakeup((caddr_t)&ud_rret);
		break;

	case M_OP_AVAIL|M_OP_END:
		break;

	default:
		printf("uda%d ra%d unknown packet type x%x\n",
			um->um_ctlr, mp->mscp_unit, mp->mscp_opcode);
	}
}


/*
 * Process an error log message
 *
 * For now, just log the error on the console.
 * Only minimal decoding is done, only "useful"
 * information is printed.  Eventually should
 * send message to an error logger.
 */

static char *events[] = {
	"ok",
	"inv cmd",
	"op aborted",
	"offline",
	"available",
	"med fmt",
	"write prot",
	"comp err",
	"data err",
	"host buf access err",
	"cntl err",
	"drive err",
};
#define	MAXEVT	0xb

uderror(um, mp)
	register struct uba_ctlr *um;
	register struct mslg *mp;
{
	register u_short *sp;		/* for sdi crap */
	register int i;			/* for useless design crap */
	register unsigned char *cp;	/* for useless design crap */

	printf("uda%d ra%d seq %d: %s err; fmt x%x ev x%x fl x%x\n",
		um->um_ctlr, mp->mslg_unit, mp->mslg_seqnum,
		mp->mslg_flags&(M_LF_SUCC|M_LF_CONT) ? "soft" : "hard",
		mp->mslg_format, mp->mslg_event, mp->mslg_flags);
	if ((mp->mslg_event & M_ST_MASK) <= MAXEVT)
		printf("%s; ", events[mp->mslg_event & M_ST_MASK]);
	switch (mp->mslg_format) {
	case M_FM_CNTERR:
		/* now the thing should be marked disastrously bad */
		printf("oops\n");
		break;

	case M_FM_BUSADDR:
		printf("host mem access; addr x%x\n",
			*((long *)&mp->mslg_busaddr[0]));
		break;

	case M_FM_DISKTRN:
		printf("%sbn %d; lev x%x, retry x%x\n",
			(mp->mslg_hdr & 0xf0000000) == 0 ? "l" : "r",
			mp->mslg_hdr & 0x0fffffff,
			mp->mslg_level, mp->mslg_retry);
		break;

	case M_FM_SDI:
		printf("%sbn %d;",
			(mp->mslg_hdr & 0xf0000000) == 0 ? "l" : "r",
			mp->mslg_hdr & 0x0fffffff);
		/*
		 * print the bytes in the same order used
		 * by the dec diagnostics
		 */
		sp = (u_short *)&mp->mslg_sdi[3];
		while (sp > (u_short *)mp->mslg_sdi)
			printf(" %x", *--sp);
		printf(" xx\n"); 
		break;

	case M_FM_SMLDSK:
		printf("cyl %d\n", mp->mslg_sdecyl);
		break;

	case 0x40:		/* hack for useless design */
		printf("scsi:");
		cp = (unsigned char *)mp->mslg_sdi;
		for(i = 0; i < 10; i++)
			printf(" %x", *cp++);
		printf(" [%x %x]\n", cp[0], cp[1]); 
	}
}

/*
 * Find an unused command packet
 * more exactly, return the next command packet if possible,
 * return NULL if we can't
 * the device insists that entries in the ring be used in sequence
 */
struct mscp *
udgetcp(um)
	struct uba_ctlr *um;
{
	register struct mscp *mp;
	register struct udaca *cp;
	register struct uda_softc *sc;
	register int i;

	cp = &uda[um->um_ctlr].uda_ca;
	sc = &uda_softc[um->um_ctlr];
	i = sc->sc_lastcmd;
	if ((cp->ca_cmddsc[i] & (UDA_OWN|UDA_INT)) == UDA_INT) {
		cp->ca_cmddsc[i] &= ~UDA_INT;
		mp = &uda[um->um_ctlr].uda_cmd[i];
		mp->mscp_unit = mp->mscp_modifier = 0;
		mp->mscp_opcode = mp->mscp_flags = 0;
		mp->mscp_bytecnt = mp->mscp_buffer = 0;
		mp->mscp_errlgfl = mp->mscp_copyspd = 0;
		sc->sc_lastcmd = (i + 1) % NCMD;
		return(mp);
	}
	return(NULL);
}

udaphys(bp)
struct buf *bp;
{
	if(bp->b_bcount > 65536)
		bp->b_bcount = 65536;
}

udread(dev)
	dev_t dev;
{
	int unit = DUNIT(minor(dev));

	if (unit >= NRA)
		u.u_error = ENXIO;
	else
		physio(udstrategy, &rudbuf[unit], dev, B_READ, udaphys);
}

udwrite(dev)
	dev_t dev;
{
	int unit = DUNIT(minor(dev));

	if (unit >= NRA)
		u.u_error = ENXIO;
	else
		physio(udstrategy, &rudbuf[unit], dev, B_WRITE, udaphys);
}

udioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	register int unit;
	register union arg {
		struct ud_rctbuf r;
		struct ud_repl b;
	} *uap;
	register struct ud_unit *uu;
	register int i;

	unit = DUNIT(minor(dev));
	if (unit >= NRA) {
		u.u_error = ENXIO;
		return;
	}
	if ((uddinfo[unit]->ui_flags & (DGTUNIT | DUP)) != DUP) {
		/* should bring it online here */
		u.u_error = EIO;
		return;
	}
	uu = &ud_units[unit];
	uap = (union arg *)addr;
	switch(cmd) {
	default:
		u.u_error = ENOTTY;
		return;

	case UIOCHAR:
		if (copyout((caddr_t)uu, addr, sizeof(struct ud_unit)))
			u.u_error = EFAULT;
		return;

	case UIORRCT:
		if(uap->r.lbn < 0 || uap->r.lbn > uu->rctsize) {
			u.u_error = EIO;
			return;
		}
		/*
		 * try different copies until one works
		 */
		for (i = 0; i < uu->copies; i++) {
			u.u_count = SECTOR;	/* block size on disk */
			u.u_offset = (uap->r.lbn + uu->radsize) * (daddr_t)SECTOR;
			u.u_offset += i * uu->rctsize * (daddr_t)SECTOR;
			u.u_base = uap->r.buf;
			u.u_segflg = 0;
			u.u_error = 0;
			physio(udstrategy, &rctbuf[unit], dev, B_READ, udaphys);
			if (u.u_error == 0)
				break;
		}
		return;

	case UIOWRCT:
		if ((flag & FWRITE) == 0) {
			u.u_error = EBADF;
			return;
		}
		if(uap->r.lbn < 0 || uap->r.lbn > uu->rctsize) {
			u.u_error = EIO;
			return;
		}
		/*
		 * write every copy we can
		 * should do read-after-write crap
		 */
		for (i = 0; i < uu->copies; i++) {
			u.u_count = SECTOR;	/* block size on disk */
			u.u_offset = (uap->r.lbn + uu->radsize) * (daddr_t)SECTOR;
			u.u_offset += i * uu->rctsize * (daddr_t)SECTOR;
			u.u_base = uap->r.buf;
			u.u_segflg = 0;
			physio(udstrategy, &rctbuf[unit], dev, B_WRITE, udaphys);
			u.u_error = 0;
		}
		return;

	case UIOREPL:
		if ((flag & FWRITE) == 0) {
			u.u_error = EBADF;
			return;
		}
		udreplace(dev, uap->b.lbn, uap->b.replbn, uap->b.prim);
		return;
	}
}

udreplace(dev, badlbn, replbn, prim)
int dev;
daddr_t badlbn;
daddr_t replbn;
int prim;
{
	register struct mscp *mp;
	register struct uba_ctlr *um;
	register int unit;
	struct uda_softc *sc;

	unit = DUNIT(minor(dev));
	um = uddinfo[unit]->ui_mi;
	sc = &uda_softc[um->um_ctlr];
	spl6();
	while (ud_rflags & RPLOCK) {
		ud_rflags |= RPWANT;
		sleep((caddr_t)&ud_rflags, PZERO + 1);
	}
	ud_rflags = RPLOCK;
	printf("uda%d ra%d replace %D with %D\n",
		um->um_ctlr, unit, badlbn, replbn);
	udcredit(sc, 1);
	if ((mp = udgetcp(um)) == NULL) {
		u.u_error = EIO;
		goto out;
	}
	mp->mscp_cmdref = 42;	/* anything you like */
	mp->mscp_unit = unit;
	mp->mscp_opcode = M_OP_REPLC;
	mp->mscp_rbn = replbn;
	mp->mscp_lbn = badlbn;
	mp->mscp_modifier = prim ? M_MD_PRIMR : 0;
	udsend(mp, sc);
	while ((ud_rflags & RPDONE) == 0)
		sleep((caddr_t)&ud_rret, PZERO);
	u.u_error = ud_rret;
out:
	if (ud_rflags & RPWANT)
		wakeup((caddr_t)&ud_rflags);
	ud_rflags = 0;
	spl0();
}

udreset(uban)
	int uban;
{
	register struct uba_ctlr *um;
	register struct uba_device *ui;
	register struct buf *bp, *dp;
	register int unit;
	struct buf *nbp;
	int d;

	for (d = 0; d < NUDA; d++) {
		if ((um = udminfo[d]) == 0 || um->um_ubanum != uban ||
		    um->um_alive == 0)
			continue;
		printf(" uda%d", d);
		um->um_tab.b_active = 0;
		um->um_tab.b_actf = um->um_tab.b_actl = 0;
		uda_softc[d].sc_state = S_IDLE;
		for (unit = 0; unit < NRA; unit++) {
			if ((ui = uddinfo[unit]) == 0)
				continue;
			if (ui->ui_alive == 0 || ui->ui_mi != um)
				continue;
			udutab[unit].b_active = 0;
			udutab[unit].b_qsize = 0;
		}
		for (bp = udwtab[d].av_forw; bp != &udwtab[d]; bp = nbp) {
			nbp = bp->av_forw;
			ubarelse(uban, (int *)&bp->b_ubinfo);
			/*
			 * Link the buffer onto the drive queue
			 */
			dp = &udutab[dkunit(bp)];
			if (dp->b_actf == 0)
				dp->b_actf = bp;
			else
				dp->b_actl->av_forw = bp;
			dp->b_actl = bp;
			bp->av_forw = 0;
			/*
			 * Link the drive onto the controller queue
			 */
			if (dp->b_active == 0) {
				dp->b_forw = NULL;
				if (um->um_tab.b_actf == NULL)
					um->um_tab.b_actf = dp;
				else
					um->um_tab.b_actl->b_forw = dp;
				um->um_tab.b_actl = dp;
				dp->b_active = 1;
			}
		}
		udinit(d);
	}
}

/*
 * crash dumps
 */

#define DBSIZE 64

#define ca_Rspdsc	ca_rspdsc[0]
#define ca_Cmddsc	ca_rspdsc[1]
#define uda_Rsp		uda_rsp[0]
#define uda_Cmd		uda_cmd[0]

uddump(dev)
dev_t dev;
{
	struct udadevice *udaddr;
	struct uda *ud_ubaddr;
	char *start;
	int num, blk, unit;
	int maxsz;
	int blkoff;
	register struct uba_regs *uba;
	register struct uba_device *ui;
	register struct uda *udp;
	register struct pte *io;
	register int i;

	unit = DUNIT(minor(dev));
	if (unit >= NRA)
		return (ENXIO);
#define	phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
	ui = phys(struct uba_device *, uddinfo[unit]);
	if (ui->ui_alive == 0)
		return (ENXIO);
	uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
	ubainit(uba);
	udaddr = (struct udadevice *)ui->ui_physaddr;
	DELAY(2000000);
	udp = phys(struct uda *, &uda[ui->ui_ctlr]);

	num = btoc(sizeof(struct uda)) + 1;
	io = &uba->uba_map[NUBMREG-num];
	for(i = 0; i<num; i++)
		*(int *)io++ = UBAMR_MRV|(btop(udp)+i);
	ud_ubaddr = (struct uda *)(((int)udp & PGOFSET)|((NUBMREG-num)<<9));

	udaddr->udaip = 0;
	while ((udaddr->udasa & UDA_STEP1) == 0)
		if(udaddr->udasa & UDA_ERR) return(EFAULT);
	udaddr->udasa = UDA_ERR;
	while ((udaddr->udasa & UDA_STEP2) == 0)
		if(udaddr->udasa & UDA_ERR) return(EFAULT);
	udaddr->udasa = (short)&ud_ubaddr->uda_ca.ca_ringbase;
	while ((udaddr->udasa & UDA_STEP3) == 0)
		if(udaddr->udasa & UDA_ERR) return(EFAULT);
	udaddr->udasa = (short)(((int)&ud_ubaddr->uda_ca.ca_ringbase) >> 16);
	while ((udaddr->udasa & UDA_STEP4) == 0)
		if(udaddr->udasa & UDA_ERR) return(EFAULT);
	udaddr->udasa = UDA_GO;
	udp->uda_ca.ca_Rspdsc = (long)&ud_ubaddr->uda_Rsp.mscp_cmdref;
	udp->uda_ca.ca_Cmddsc = (long)&ud_ubaddr->uda_Cmd.mscp_cmdref;
	udp->uda_Cmd.mscp_cntflgs = 0;
	udp->uda_Cmd.mscp_version = 0;
	if (dudcmd(M_OP_STCON, udp, udaddr) == 0)
		return(EFAULT);
	udp->uda_Cmd.mscp_unit = ui->ui_slave;
	if (dudcmd(M_OP_ONLIN, udp, udaddr) == 0)
		return(EFAULT);
	num = physmem;
	start = 0;
	maxsz = ra_sizes[PART(minor(dev))].nblocks;
	blkoff = ra_sizes[PART(minor(dev))].blkoff;
	if(maxsz < 0)
		maxsz = ud_units[unit].radsize-blkoff;
	if (dumplo < 0 || dumplo + num > maxsz)
		return (EINVAL);
	blkoff += dumplo;
	while (num > 0) {
		blk = num > DBSIZE ? DBSIZE : num;
		io = uba->uba_map;
		for (i = 0; i < blk; i++)
			*(int *)io++ = (btop(start)+i) | UBAMR_MRV;
		*(int *)io = 0;
		udp->uda_Cmd.mscp_lbn = btop(start) + blkoff;
		udp->uda_Cmd.mscp_unit = ui->ui_slave;
		udp->uda_Cmd.mscp_bytecnt = blk*NBPG;
		udp->uda_Cmd.mscp_buffer = 0;
		if (dudcmd(M_OP_WRITE, udp, udaddr) == 0) {
			return(EIO);
		}
		start += blk*NBPG;
		num -= blk;
	}
	return (0);
}

static
dudcmd(op, udp, udaddr)
int op;
register struct uda *udp;
struct udadevice *udaddr;
{
	int i;

	udp->uda_Cmd.mscp_opcode = op;
	udp->uda_Rsp.mscp_header.uda_msglen = sizeof (struct mscp);
	udp->uda_Cmd.mscp_header.uda_msglen = sizeof (struct mscp);
	udp->uda_ca.ca_Rspdsc |= UDA_OWN|UDA_INT;
	udp->uda_ca.ca_Cmddsc |= UDA_OWN|UDA_INT;
	i = udaddr->udaip;
	for (;;) {
		if (udp->uda_ca.ca_cmdint)
			udp->uda_ca.ca_cmdint = 0;
		if (udp->uda_ca.ca_rspint)
			break;
	}
	udp->uda_ca.ca_rspint = 0;
	if (udp->uda_Rsp.mscp_opcode != (op|M_OP_END) ||
	    (udp->uda_Rsp.mscp_status&M_ST_MASK) != M_ST_SUCC) {
		if(udaddr->udasa&UDA_ERR)
			printf("error: udasa 0x%x\ndump ", udaddr->udasa);
		printf("error: com %d opc 0x%x stat 0x%x\ndump ",
			op,
			udp->uda_Rsp.mscp_opcode,
			udp->uda_Rsp.mscp_status);
		return(0);
	}
	return(1);
}