code:
/*	$OpenBSD: sii.c,v 1.15 2013/01/06 22:06:54 martynas Exp $	*/
/*	$NetBSD: sii.c,v 1.42 2000/06/02 20:20:29 mhitch Exp $	*/
/*
 * Copyright (c) 2008 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell and Rick Macklem.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)sii.c	8.2 (Berkeley) 11/30/93
 *
 * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c,
 *	v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)";
 */

/*
 * SCSI interface driver
 */
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_message.h>
#ifdef DEBUG
#include <scsi/scsi_disk.h>
#endif

#include <vax/dec/siireg.h>		/* device registers */
#include <vax/dec/siivar.h>		/* softc and prototypes */

/* XXX not in scsi/scsi_message.h */
#define	MSG_EXT_MODIFY_DATA_PTR		0x00

struct cfdriver sii_cd = {
	NULL, "sii", DV_DULL
};

/*
 * MACROS for timing out spin loops.
 *
 * Wait until expression is true.
 *
 * Control register bits can change at any time so when the CPU
 * reads a register, the bits might change and
 * invalidate the setup and hold times for the CPU.
 * This macro reads the register twice to be sure the value is stable.
 *
 *	args:	var 		- variable to save control register contents
 *		reg		- control register to read
 *		expr 		- expression to spin on
 *		spincount 	- maximum number of times through the loop
 *		cntr		- variable for number of tries
 */
#define	SII_WAIT_UNTIL(var, reg, expr, spincount, cntr)			\
	do {								\
		u_int tmp = reg;					\
		for (cntr = 0; cntr < spincount; cntr++) {		\
			while (tmp != (var = reg))			\
				tmp = var;				\
			if (expr)					\
				break;					\
			if (cntr >= 100)				\
				DELAY(100);				\
		}							\
	} while (0)

#ifdef DEBUG
u_int	sii_debug = 1;
int	sii_debug_cmd;
int	sii_debug_bn;
int	sii_debug_sz;
#define NLOG 16
struct sii_log {
	u_short	cstat;
	u_short	dstat;
	u_short	comm;
	u_short	msg;
	int	rlen;
	int	dlen;
	int	target;
} sii_log[NLOG], *sii_logp = sii_log;
#endif

#define NORESET	0
#define RESET	1
#define NOWAIT	0
#define WAIT	1

#if 0
/*
 * XXX That hardcoded 7 below implies SII_NCMD <= 7, which is not the case...
 * XXX Thankfully all uses of SII_BUF_ADDR are in #if 0 sections (and this
 * XXX should be reasonably safe as long as we use 7 as the host id).
 */
/*
 * Define a safe address in the SCSI buffer for doing status & message DMA
 * XXX why not add another field to softc?
 */
#define SII_BUF_ADDR(sc)	(SII_MAX_DMA_XFER_LENGTH * 7 * 2)
#endif

/*
 * Forward references
 */

void	sii_Reset(struct sii_softc *sc, int resetbus);
void	sii_StartCmd(struct sii_softc *sc, int target);
void	sii_CmdDone(struct sii_softc *sc, int target, int error);
void	sii_DoIntr(struct sii_softc *sc, u_int dstat);
void	sii_StateChg(struct sii_softc *sc, u_int cstat);
int	sii_GetByte(SIIRegs *regs, int phase, int ack);
void	sii_DoSync(struct sii_softc *, State *);
void	sii_StartDMA(SIIRegs *regs, int phase, u_int dmaAddr, int size);
u_int	sii_msgout(SIIRegs *, u_int, u_int8_t);
void	sii_scsi_cmd(struct scsi_xfer *);
void	sii_schedule(struct sii_softc *);
#ifdef DEBUG
void	sii_DumpLog(void);
#endif

struct scsi_adapter sii_scsiswitch = {
	sii_scsi_cmd,
	scsi_minphys,
	NULL,
	NULL,
	NULL
};

void
sii_attach(sc)
	struct sii_softc *sc;
{
	struct scsibus_attach_args saa;
	int i;

	sc->sc_target = -1;	/* no command active */

	/*
	 * Give each target its own DMA buffer region.
	 * Make it big enough for 2 max transfers so we can ping pong buffers
	 * while we copy the data.
	 */
	for (i = 0; i < SII_NCMD; i++) {
		sc->sc_st[i].dmaAddr[0] =
			SII_MAX_DMA_XFER_LENGTH * 2 * i;
		sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
			SII_MAX_DMA_XFER_LENGTH;
	}

	sii_Reset(sc, RESET);
	printf("\n");

	/*
	 * fill in the prototype scsi_link.
	 */
	sc->sc_link.adapter = &sii_scsiswitch;
	sc->sc_link.adapter_buswidth = 8;
	sc->sc_link.adapter_softc = sc;
	sc->sc_link.adapter_target = sc->sc_regs->id & SII_IDMSK;
	sc->sc_link.openings = 1;	/* driver can't queue requests yet */

	/*
	 * Now try to attach all the sub-devices
	 */
	bzero(&saa, sizeof(saa));
	saa.saa_sc_link = &sc->sc_link;
	config_found(&sc->sc_dev, &saa, scsiprint);
}

/*
 * Start activity on a SCSI device.
 * We maintain information on each device separately since devices can
 * connect/disconnect during an operation.
 */

void
sii_scsi_cmd(xs)
	struct scsi_xfer *xs;
{
	int target = xs->sc_link->target;
	struct sii_softc *sc = xs->sc_link->adapter_softc;
	int s;
	int count;

	s = splbio();
	if (sc->sc_xs[target] != NULL) {
#ifdef DEBUG
		printf("[busy at start]\n");
#endif
		xs->error = XS_NO_CCB;
		scsi_done(xs);
		splx(s);
		return;
	}
	/*
	 * Build a ScsiCmd for this command and start it.
	 */
	sc->sc_xs[target] = xs;
	sii_StartCmd(sc, target);
	splx(s);

	if ((xs->flags & ITSDONE) != 0)
		return;
	if ((xs->flags & SCSI_POLL) == 0)
		return;
	count = xs->timeout;
	while (count) {
		s = splbio();
		(void)sii_intr(sc);
		splx(s);
		if ((xs->flags & ITSDONE) != 0)
			break;
		/* XXX schedule another command? */
		DELAY(1000);
		--count;
	}
	if ((xs->flags & ITSDONE) != 0)
		return;
	xs->error = XS_TIMEOUT;
	scsi_done(xs);
	return;
}

/*
 * Check to see if any SII chips have pending interrupts
 * and process as appropriate.
 */
int
sii_intr(xxxsc)
	void *xxxsc;
{
	struct sii_softc *sc = xxxsc;
	u_int dstat;

	/*
	 * Find which controller caused the interrupt.
	 */
	dstat = sc->sc_regs->dstat;
	if (dstat & (SII_CI | SII_DI)) {
		sii_DoIntr(sc, dstat);
		return (1);
	}

	return (0);
}

/*
 * Reset the SII chip and do a SCSI reset if 'reset' is true.
 * NOTE: if !cold && reset, should probably probe for devices
 * since a SCSI bus reset will set UNIT_ATTENTION.
 */
void
sii_Reset(sc, reset)
	struct sii_softc* sc;
	int reset;				/* TRUE => reset SCSI bus */
{
	SIIRegs *regs = sc->sc_regs;

#ifdef DEBUG
	if (sii_debug > 1)
		printf("sii: RESET\n");
#endif
	/*
	 * Reset the SII chip.
	 */
	regs->comm = SII_CHRESET;
	/*
	 * Set arbitrated bus mode.
	 */
	regs->csr = SII_HPM;
	/*
	 * Set host adapter ID.
	 */
	regs->id = SII_ID_IO | sc->sc_hostid;
	/*
	 * Enable SII to drive the SCSI bus.
	 */
	regs->dictrl = SII_PRE;
	regs->dmctrl = 0;

	if (reset) {
		int i;

		/*
		 * Assert SCSI bus reset for at least 25 Usec to clear the
		 * world. SII_DO_RST is self clearing.
		 * Delay 250 ms before doing any commands.
		 */
		regs->comm = SII_DO_RST;
		DELAY(250000);

		/* rearbitrate synchronous offset */
		for (i = 0; i < SII_NCMD; i++)
			sc->sc_st[i].dmaReqAck = 0;
	}

	/*
	 * Clear any pending interrupts from the reset.
	 */
	regs->cstat = regs->cstat;
	regs->dstat = regs->dstat;
	/*
	 * Set up SII for arbitrated bus mode, SCSI parity checking,
	 * Reselect Enable, and Interrupt Enable.
	 */
	regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE;
}

/*
 * Start a SCSI command by sending the cmd data
 * to a SCSI controller via the SII.
 * Call the device done procedure if it can't be started.
 * NOTE: we should be called with interrupts disabled.
 */
void
sii_StartCmd(sc, target)
	struct sii_softc *sc;	/* which SII to use */
	int target;		/* which command to start */
{
	SIIRegs *regs;
	State *state;
	struct scsi_xfer *xs;
	u_int status;
	int error, retval;

	splassert(IPL_BIO);

	/* if another command is currently in progress, just wait */
	if (sc->sc_target >= 0)
		return;

	/* initialize state information for this command */
	xs = sc->sc_xs[target];
	state = &sc->sc_st[target];
	state->flags = FIRST_DMA;
	state->prevComm = 0;
	state->dmalen = 0;
	state->dmaCurPhase = -1;
	state->dmaPrevPhase = -1;
	state->dmaBufIndex = 0;
	state->cmd = (u_char *)xs->cmd;
	state->cmdlen = xs->cmdlen;
	if ((state->buflen = xs->datalen) == 0) {
		state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */
		state->buf = (char *)NULL;
	} else {
		state->buf = xs->data;
	}

#ifdef DEBUG
	if (sii_debug > 1) {
		printf("sii_StartCmd: %s target %d cmd 0x%x addr %p size %d DMA %d\n",
			sc->sc_dev.dv_xname,
			target, xs->cmd->opcode, xs->data, xs->datalen,
			state->dmaDataPhase);
	}
	sii_debug_cmd = xs->cmd->opcode;
	if (sii_debug_cmd == READ_BIG || sii_debug_cmd == WRITE_BIG) {
		sii_debug_bn = (state->cmd[2] << 24) | (state->cmd[3] << 16) |
		    (state->cmd[4] << 8) | state->cmd[5];
		sii_debug_sz = (state->cmd[7] << 8) | state->cmd[8];
	} else {
		sii_debug_bn = 0;
		sii_debug_sz = 0;
	}
#endif

	/* try to select the target */
	regs = sc->sc_regs;

	/*
	 * Another device may have selected us; in which case,
	 * this command will be restarted later.
	 */
	if ((status = regs->dstat) & (SII_CI | SII_DI)) {
		sii_DoIntr(sc, status);
		return;
	}

	sc->sc_target = target;
	/* do a chained, select with ATN and programmed I/O command */
	regs->data = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG |
	    xs->sc_link->lun;
	regs->slcsr = target;
	regs->dmctrl = state->dmaReqAck;
	regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |
		SII_MSG_OUT_PHASE;

	/*
	 * Wait for something to happen
	 * (should happen soon or we would use interrupts).
	 */
	SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI),
		SII_WAIT_COUNT/4, retval);

	/* check to see if we are connected OK */
	if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) ==
	    (SII_SCH | SII_CON)) {
		regs->cstat = status;

#ifdef DEBUG
		sii_logp->target = target;
		sii_logp->cstat = status;
		sii_logp->dstat = 0;
		sii_logp->comm = regs->comm;
		sii_logp->msg = -1;
		sii_logp->rlen = state->buflen;
		sii_logp->dlen = state->dmalen;
		if (++sii_logp >= &sii_log[NLOG])
			sii_logp = sii_log;
#endif

		/* wait a short time for command phase */
		SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS,
			SII_WAIT_COUNT, retval);
#ifdef DEBUG
		if (sii_debug > 2)
			printf("sii_StartCmd: ds %x cnt %d\n", status, retval);
#endif
		if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) !=
		    (SII_MIS | SII_CMD_PHASE)) {
			printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n",
				regs->cstat, status, retval); /* XXX */
			/* process interrupt or continue until it happens */
			if (status & (SII_CI | SII_DI))
				sii_DoIntr(sc, status);
			return;
		}
		regs->dstat = SII_DNE;	/* clear Msg Out DMA done */

		/* send command data */
		sc->sii_copytobuf(sc, state->cmd, state->dmaAddr[0],
		    state->cmdlen);
		sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE,
			state->dmaAddr[0], state->dmalen = xs->cmdlen);

		/* wait a little while for DMA to finish */
		SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI),
			SII_WAIT_COUNT, retval);
#ifdef DEBUG
		if (sii_debug > 2)
			printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);
#endif
		if (status & (SII_CI | SII_DI))
			sii_DoIntr(sc, status);
#ifdef DEBUG
		if (sii_debug > 2)
			printf("sii_StartCmd: DONE ds %x\n", regs->dstat);
#endif
		return;
	}

	/*
	 * Another device may have selected us; in which case,
	 * this command will be restarted later.
	 */
	if (status & (SII_CI | SII_DI)) {
		sii_DoIntr(sc, regs->dstat);
		return;
	}

	/*
	 * Disconnect if selection command still in progress.
	 */
	if (status & SII_SIP) {
		error = XS_SELTIMEOUT;	/* device didn't respond */
		regs->comm = SII_DISCON;
		SII_WAIT_UNTIL(status, regs->cstat,
			!(status & (SII_CON | SII_SIP)),
			SII_WAIT_COUNT, retval);
	} else
		error = XS_BUSY;	/* couldn't get the bus */
#ifdef DEBUG
	if (sii_debug > 1)
		printf("sii_StartCmd: Couldn't select target %d error %d\n",
			target, error);
#endif
	sc->sc_target = -1;
	regs->cstat = 0xffff;
	regs->dstat = 0xffff;
	regs->comm = 0;
	sii_CmdDone(sc, target, error);
}

/*
 * Process interrupt conditions.
 */
void
sii_DoIntr(sc, dstat)
	struct sii_softc *sc;
	u_int dstat;
{
	SIIRegs *regs = sc->sc_regs;
	State *state;
	u_int cstat;
	int i, msg;
	u_int comm;

again:
	comm = regs->comm;

#ifdef DEBUG
	if (sii_debug > 3)
		printf("sii_DoIntr: cs %x, ds %x cm %x ",
			regs->cstat, dstat, comm);
	sii_logp->target = sc->sc_target;
	sii_logp->cstat = regs->cstat;
	sii_logp->dstat = dstat;
	sii_logp->comm = comm;
	sii_logp->msg = -1;
	if (sc->sc_target >= 0) {
		sii_logp->rlen = sc->sc_st[sc->sc_target].buflen;
		sii_logp->dlen = sc->sc_st[sc->sc_target].dmalen;
	} else {
		sii_logp->rlen = 0;
		sii_logp->dlen = 0;
	}
	if (++sii_logp >= &sii_log[NLOG])
		sii_logp = sii_log;
#endif

	regs->dstat = dstat;	/* acknowledge everything */

	if (dstat & SII_CI) {
		/* deglitch cstat register */
		msg = regs->cstat;
		while (msg != (cstat = regs->cstat))
			msg = cstat;
		regs->cstat = cstat;	/* acknowledge everything */
#ifdef DEBUG
		if (sii_logp > sii_log)
			sii_logp[-1].cstat = cstat;
		else
			sii_log[NLOG - 1].cstat = cstat;
#endif

		/* check for a BUS RESET */
		if (cstat & SII_RST) {
			printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname);
			/* need to flush disconnected commands */
			for (i = 0; i < SII_NCMD; i++) {
				if (sc->sc_xs[i] == NULL)
					continue;
				sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */);
			}
			/* rearbitrate synchronous offset */
			for (i = 0; i < SII_NCMD; i++)
				sc->sc_st[i].dmaReqAck = 0;
			sc->sc_target = -1;
			return;
		}

#ifdef notdef
		/*
		 * Check for a BUS ERROR.
		 * According to DEC, this feature doesn't really work
		 * and to just clear the bit if it's set.
		 */
		if (cstat & SII_BER) {
			regs->cstat = SII_BER;
		}
#endif

		/* check for state change */
		if (cstat & SII_SCH) {
			sii_StateChg(sc, cstat);
			comm = regs->comm;
		}
	}

	/* check for DMA completion */
	if (dstat & SII_DNE) {
		u_int dma;
		char *buf;

		/*
		 * There is a race condition with SII_SCH. There is a short
		 * window between the time a SII_SCH is seen after a disconnect
		 * and when the SII_SCH is cleared. A reselect can happen
		 * in this window and we will clear the SII_SCH without
		 * processing the reconnect.
		 */
		if (sc->sc_target < 0) {
			cstat = regs->cstat;
			printf("%s: DNE?? dev %d,%d cs %x\n",
				sc->sc_dev.dv_xname,
				regs->slcsr, regs->destat,
				cstat); /* XXX */
			if (cstat & SII_DST) {
				sc->sc_target = regs->destat;
				state = &sc->sc_st[sc->sc_target];
				state->prevComm = 0;
			} else
				panic("sc_target 1");
		}
		state = &sc->sc_st[sc->sc_target];
		/* check for a PARITY ERROR */
		if (dstat & SII_IPE) {
			state->flags |= PENDING_CMD;
			state->nextCmd = MSG_PARITY_ERROR;
			printf("%s: Parity error\n", sc->sc_dev.dv_xname);
			goto abort;
		}
		/* dmalen = amount left to transfer, i = amount transferred */
		i = state->dmalen;
		state->dmalen = 0;
		state->dmaCurPhase = -1;
#ifdef DEBUG
		if (sii_debug > 4) {
			printf("DNE: amt %d ", i);
			if (!(dstat & SII_TCZ))
				printf("no TCZ?? (%d) ", regs->dmlotc);
		} else if (!(dstat & SII_TCZ)) {
			printf("%s: device %d: no TCZ?? (%d)\n",
				sc->sc_dev.dv_xname, sc->sc_target, regs->dmlotc);
			sii_DumpLog(); /* XXX */
		}
#endif
		switch (comm & SII_PHASE_MSK) {
		case SII_CMD_PHASE:
			state->cmdlen -= i;
			break;

		case SII_DATA_IN_PHASE:
			/* check for more data for the same phase */
			dma = state->dmaAddr[state->dmaBufIndex];
			buf = state->buf;
			state->buf += i;
			state->buflen -= i;
			if (state->buflen > 0 && !(dstat & SII_MIS)) {
				int len;

				/* start reading next chunk */
				len = state->buflen;
				if (len > SII_MAX_DMA_XFER_LENGTH)
					len = SII_MAX_DMA_XFER_LENGTH;
				state->dmaBufIndex = !state->dmaBufIndex;
				sii_StartDMA(regs,
					state->dmaCurPhase = SII_DATA_IN_PHASE,
					state->dmaAddr[state->dmaBufIndex],
					state->dmaCnt = state->dmalen = len);
				dstat &= ~(SII_IBF | SII_TBE);
			}
			/* copy in the data */
			sc->sii_copyfrombuf(sc, dma, buf, i);
			break;

		case SII_DATA_OUT_PHASE:
			state->dmaBufIndex = !state->dmaBufIndex;
			state->buf += i;
			state->buflen -= i;

			/* check for more data for the same phase */
			if (state->buflen <= 0 || (dstat & SII_MIS))
				break;

			/* start next chunk */
			i = state->buflen;
			if (i > SII_MAX_DMA_XFER_LENGTH) {
				sii_StartDMA(regs, state->dmaCurPhase =
					SII_DATA_OUT_PHASE,
					state->dmaAddr[state->dmaBufIndex],
					state->dmaCnt = state->dmalen =
					SII_MAX_DMA_XFER_LENGTH);
				/* prepare for next chunk */
				i -= SII_MAX_DMA_XFER_LENGTH;
				if (i > SII_MAX_DMA_XFER_LENGTH)
					i = SII_MAX_DMA_XFER_LENGTH;
				sc->sii_copytobuf(sc, (u_char *)(state->buf +
				    SII_MAX_DMA_XFER_LENGTH),
				    state->dmaAddr[!state->dmaBufIndex], i);
			} else {
				sii_StartDMA(regs, state->dmaCurPhase =
					SII_DATA_OUT_PHASE,
					state->dmaAddr[state->dmaBufIndex],
					state->dmaCnt = state->dmalen = i);
			}
			dstat &= ~(SII_IBF | SII_TBE);
		}
	}

	/* check for phase change or another MsgIn/Out */
	if (dstat & (SII_MIS | SII_IBF | SII_TBE)) {
		/*
		 * There is a race condition with SII_SCH. There is a short
		 * window between the time a SII_SCH is seen after a disconnect
		 * and when the SII_SCH is cleared. A reselect can happen
		 * in this window and we will clear the SII_SCH without
		 * processing the reconnect.
		 */
		if (sc->sc_target < 0) {
			cstat = regs->cstat;
			printf("%s: target %d MIS?? dev %d,%d cs %x ds %x\n",
				sc->sc_dev.dv_xname, sc->sc_target,
				regs->slcsr, regs->destat,
				cstat, dstat); /* XXX */
			if (cstat & SII_DST) {
				sc->sc_target = regs->destat;
				state = &sc->sc_st[sc->sc_target];
				state->prevComm = 0;
			} else {
#ifdef DEBUG
				sii_DumpLog();
#endif
				panic("sc_target 2");
			}
		}
		state = &sc->sc_st[sc->sc_target];
		switch (dstat & SII_PHASE_MSK) {
		case SII_CMD_PHASE:
			if (state->dmaPrevPhase >= 0) {
				/* restart DMA after disconnect/reconnect */
				if (state->dmaPrevPhase != SII_CMD_PHASE) {
					printf("%s: device %d: DMA reselect phase doesn't match\n",
						sc->sc_dev.dv_xname, sc->sc_target);
					goto abort;
				}
				state->dmaCurPhase = SII_CMD_PHASE;
				state->dmaPrevPhase = -1;
				regs->dmaddrl = state->dmaAddrL;
				regs->dmaddrh = state->dmaAddrH;
				regs->dmlotc = state->dmaCnt;
				if (state->dmaCnt & 1)
					regs->dmabyte = state->dmaByte;
				regs->comm = SII_DMA | SII_INXFER |
					(comm & SII_STATE_MSK) | SII_CMD_PHASE;
#ifdef DEBUG
				if (sii_debug > 4)
					printf("Cmd dcnt %d dadr %x ",
						state->dmaCnt,
						(state->dmaAddrH << 16) |
							state->dmaAddrL);
#endif
			} else {
				/* send command data */
				i = state->cmdlen;
				if (i == 0) {
					printf("%s: device %d: cmd count exceeded\n",
						sc->sc_dev.dv_xname, sc->sc_target);
					goto abort;
				}
				sc->sii_copytobuf(sc, state->cmd,
				    state->dmaAddr[0], i);
				sii_StartDMA(regs, state->dmaCurPhase =
					SII_CMD_PHASE, state->dmaAddr[0],
					state->dmaCnt = state->dmalen = i);
			}
			/* wait a short time for XFER complete */
			SII_WAIT_UNTIL(dstat, regs->dstat,
				dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
			if (dstat & (SII_CI | SII_DI)) {
#ifdef DEBUG
				if (sii_debug > 4)
					printf("cnt %d\n", i);
				else if (sii_debug > 0)
					printf("sii_DoIntr: cmd wait ds %x cnt %d\n",
						dstat, i);
#endif
				goto again;
			}
			break;

		case SII_DATA_IN_PHASE:
		case SII_DATA_OUT_PHASE:
			if (state->cmdlen > 0) {
				printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 1\n",
					sc->sc_dev.dv_xname, sc->sc_target,
					sc->sc_xs[sc->sc_target]->cmd->opcode,
					state->cmdlen);
				state->cmdlen = 0;
#ifdef DEBUG
				sii_DumpLog();
#endif
			}
			if (state->dmaPrevPhase >= 0) {
				/* restart DMA after disconnect/reconnect */
				if (state->dmaPrevPhase !=
				    (dstat & SII_PHASE_MSK)) {
					printf("%s: device %d: DMA reselect phase doesn't match\n",
						sc->sc_dev.dv_xname, sc->sc_target);
					goto abort;
				}
				state->dmaCurPhase = state->dmaPrevPhase;
				state->dmaPrevPhase = -1;
				regs->dmaddrl = state->dmaAddrL;
				regs->dmaddrh = state->dmaAddrH;
				regs->dmlotc = state->dmaCnt;
				if (state->dmaCnt & 1)
					regs->dmabyte = state->dmaByte;
				regs->comm = SII_DMA | SII_INXFER |
					(comm & SII_STATE_MSK) |
					state->dmaCurPhase;
#ifdef DEBUG
				if (sii_debug > 4)
					printf("Data %d dcnt %d dadr %x ",
						state->dmaDataPhase,
						state->dmaCnt,
						(state->dmaAddrH << 16) |
							state->dmaAddrL);
#endif
				break;
			}
#ifdef DEBUG
			if (sii_debug > 4) {
				printf("Data %d ", state->dmaDataPhase);
				if (sii_debug > 5)
					printf("\n");
			}
#endif
			i = state->buflen;
			if (i == 0) {
				printf("%s: device %d: data count exceeded\n",
					sc->sc_dev.dv_xname, sc->sc_target);
				goto abort;
			}
			if (i > SII_MAX_DMA_XFER_LENGTH)
				i = SII_MAX_DMA_XFER_LENGTH;
			if ((dstat & SII_PHASE_MSK) == SII_DATA_IN_PHASE) {
				sii_StartDMA(regs,
					state->dmaCurPhase = SII_DATA_IN_PHASE,
					state->dmaAddr[state->dmaBufIndex],
					state->dmaCnt = state->dmalen = i);
				break;
			}
			/* start first chunk */
			if (state->flags & FIRST_DMA) {
				state->flags &= ~FIRST_DMA;
				sc->sii_copytobuf(sc, (u_char *)state->buf,
				    state->dmaAddr[state->dmaBufIndex], i);
			}
			sii_StartDMA(regs,
				state->dmaCurPhase = SII_DATA_OUT_PHASE,
				state->dmaAddr[state->dmaBufIndex],
				state->dmaCnt = state->dmalen = i);
			i = state->buflen - SII_MAX_DMA_XFER_LENGTH;
			if (i > 0) {
				/* prepare for next chunk */
				if (i > SII_MAX_DMA_XFER_LENGTH)
					i = SII_MAX_DMA_XFER_LENGTH;
				sc->sii_copytobuf(sc, (u_char *)(state->buf +
				    SII_MAX_DMA_XFER_LENGTH),
				    state->dmaAddr[!state->dmaBufIndex], i);
			}
			break;

		case SII_STATUS_PHASE:
			if (state->cmdlen > 0) {
				printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 2\n",
					sc->sc_dev.dv_xname, sc->sc_target,
					sc->sc_xs[sc->sc_target]->cmd->opcode,
					state->cmdlen);
				state->cmdlen = 0;
#ifdef DEBUG
				sii_DumpLog();
#endif
			}

			/* read amount transferred if DMA didn't finish */
			if (state->dmalen > 0) {
				i = state->dmalen - regs->dmlotc;
				state->dmalen = 0;
				state->dmaCurPhase = -1;
				regs->dmlotc = 0;
				regs->comm = comm &
					(SII_STATE_MSK | SII_PHASE_MSK);
				regs->dstat = SII_DNE;
#ifdef DEBUG
				if (sii_debug > 4)
					printf("DMA amt %d ", i);
#endif
				switch (comm & SII_PHASE_MSK) {
				case SII_DATA_IN_PHASE:
					/* copy in the data */
					sc->sii_copyfrombuf(sc,
					    state->dmaAddr[state->dmaBufIndex],
					    state->buf, i);

				case SII_CMD_PHASE:
				case SII_DATA_OUT_PHASE:
					state->buflen -= i;
				}
			}

			/* read a one byte status message */
			state->statusByte = msg =
				sii_GetByte(regs, SII_STATUS_PHASE, 1);
			if (msg < 0) {
				dstat = regs->dstat;
				goto again;
			}
#ifdef DEBUG
			if (sii_debug > 4)
				printf("Status %x ", msg);
			if (sii_logp > sii_log)
				sii_logp[-1].msg = msg;
			else
				sii_log[NLOG - 1].msg = msg;
#endif

			/* do a quick wait for COMMAND_COMPLETE */
			SII_WAIT_UNTIL(dstat, regs->dstat,
				dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
			if (dstat & (SII_CI | SII_DI)) {
#ifdef DEBUG
				if (sii_debug > 4)
					printf("cnt2 %d\n", i);
#endif
				goto again;
			}
			break;

		case SII_MSG_IN_PHASE:
			/*
			 * Save DMA state if DMA didn't finish.
			 * Be careful not to save state again after reconnect
			 * and see RESTORE_POINTER message.
			 * Note that the SII DMA address is not incremented
			 * as DMA proceeds.
			 */
			if (state->dmaCurPhase >= 0) {
				/* save DMA registers */
				state->dmaPrevPhase = state->dmaCurPhase;
				state->dmaCurPhase = -1;
				if (dstat & SII_OBB)
					state->dmaByte = regs->dmabyte;
				i = regs->dmlotc;
				if (i != 0)
					i = state->dmaCnt - i;
				/* note: no carry from dmaddrl to dmaddrh */
				state->dmaAddrL = regs->dmaddrl + i;
				state->dmaAddrH = regs->dmaddrh;
				state->dmaCnt = regs->dmlotc;
				if (state->dmaCnt == 0)
					state->dmaCnt = SII_MAX_DMA_XFER_LENGTH;
				regs->comm = comm &
					(SII_STATE_MSK | SII_PHASE_MSK);
				regs->dstat = SII_DNE;
#ifdef DEBUG
				if (sii_debug > 4) {
					printf("SavP dcnt %d dadr %x ",
						state->dmaCnt,
						(state->dmaAddrH << 16) |
						state->dmaAddrL);
					if (((dstat & SII_OBB) != 0) ^
					    (state->dmaCnt & 1))
						printf("OBB??? ");
				} else if (sii_debug > 0) {
					if (((dstat & SII_OBB) != 0) ^
					    (state->dmaCnt & 1)) {
						printf("sii_DoIntr: OBB??? ds %x cnt %d\n",
							dstat, state->dmaCnt);
						sii_DumpLog();
					}
				}
#endif
			}

			/* read a one byte message */
			msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 0);
			if (msg < 0) {
				dstat = regs->dstat;
				goto again;
			}
#ifdef DEBUG
			if (sii_debug > 4)
				printf("MsgIn %x ", msg);
			if (sii_logp > sii_log)
				sii_logp[-1].msg = msg;
			else
				sii_log[NLOG - 1].msg = msg;
#endif

			/* process message */
			switch (msg) {
			case MSG_CMDCOMPLETE:
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				msg = sc->sc_target;
				sc->sc_target = -1;
				/*
				 * Wait a short time for disconnect.
				 * Don't be fooled if SII_BER happens first.
				 * Note: a reselect may happen here.
				 */
				SII_WAIT_UNTIL(cstat, regs->cstat,
					cstat & (SII_RST | SII_SCH),
					SII_WAIT_COUNT, i);
				if ((cstat & (SII_RST | SII_SCH |
				    SII_STATE_MSK)) == SII_SCH) {
					regs->cstat = SII_SCH | SII_BER;
					regs->comm = 0;
					/*
					 * Double check that we didn't miss a
					 * state change between seeing it and
					 * clearing the SII_SCH bit.
					 */
					i = regs->cstat;
					if (!(i & SII_SCH) &&
					    (i & SII_STATE_MSK) !=
					    (cstat & SII_STATE_MSK))
						sii_StateChg(sc, i);
				}
#ifdef DEBUG
				if (sii_debug > 4)
					printf("cs %x\n", cstat);
#endif
				sii_CmdDone(sc, msg, XS_NOERROR);
				break;

			case MSG_EXTENDED:
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				/* read the message length */
				msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1);
				if (msg < 0) {
					dstat = regs->dstat;
					goto again;
				}
				sc->sc_buf[1] = msg;	/* message length */
				if (msg == 0)
					msg = 256;
				/*
				 * We read and acknowledge all the bytes
				 * except the last so we can assert ATN
				 * if needed before acknowledging the last.
				 */
				for (i = 0; i < msg; i++) {
					dstat = sii_GetByte(regs,
						SII_MSG_IN_PHASE, i < msg - 1);
					if ((int)dstat < 0) {
						dstat = regs->dstat;
						goto again;
					}
					sc->sc_buf[i + 2] = dstat;
				}

				switch (sc->sc_buf[2]) {
				case MSG_EXT_MODIFY_DATA_PTR:
					/* acknowledge last byte */
					regs->comm = SII_INXFER |
						SII_MSG_IN_PHASE |
						(comm & SII_STATE_MSK);
					SII_WAIT_UNTIL(dstat, regs->dstat,
						dstat & SII_DNE,
						SII_WAIT_COUNT, i);
					regs->dstat = SII_DNE;
					i = (sc->sc_buf[3] << 24) |
						(sc->sc_buf[4] << 16) |
						(sc->sc_buf[5] << 8) |
						sc->sc_buf[6];
					if (state->dmaPrevPhase >= 0) {
						state->dmaAddrL += i;
						state->dmaCnt -= i;
					}
					break;

				case MSG_EXT_SDTR_LEN:
					/*
					 * Acknowledge last byte and
					 * signal a request for MSG_OUT.
					 */
					regs->comm = SII_INXFER | SII_ATN |
						SII_MSG_IN_PHASE |
						(comm & SII_STATE_MSK);
					SII_WAIT_UNTIL(dstat, regs->dstat,
						dstat & SII_DNE,
						SII_WAIT_COUNT, i);
					regs->dstat = SII_DNE;
					sii_DoSync(sc, state);
					break;

				default:
				reject:
					/*
					 * Acknowledge last byte and
					 * signal a request for MSG_OUT.
					 */
					regs->comm = SII_INXFER | SII_ATN |
						SII_MSG_IN_PHASE |
						(comm & SII_STATE_MSK);
					SII_WAIT_UNTIL(dstat, regs->dstat,
						dstat & SII_DNE,
						SII_WAIT_COUNT, i);
					regs->dstat = SII_DNE;

					/* wait for MSG_OUT phase */
					SII_WAIT_UNTIL(dstat, regs->dstat,
						dstat & SII_TBE,
						SII_WAIT_COUNT, i);

					/* send a reject message */
					sii_msgout(regs, regs->cstat,
					    MSG_MESSAGE_REJECT);
					regs->dstat = SII_DNE;
				}
				break;

			case MSG_SAVEDATAPOINTER:
			case MSG_RESTOREPOINTERS:
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				/* wait a short time for another msg */
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & (SII_CI | SII_DI),
					SII_WAIT_COUNT, i);
				if (dstat & (SII_CI | SII_DI)) {
#ifdef DEBUG
					if (sii_debug > 4)
						printf("cnt %d\n", i);
#endif
					goto again;
				}
				break;

			case MSG_DISCONNECT:
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				state->prevComm = comm;
#ifdef DEBUG
				if (sii_debug > 4)
					printf("disconn %d ", sc->sc_target);
#endif
				/*
				 * Wait a short time for disconnect.
				 * Don't be fooled if SII_BER happens first.
				 * Note: a reselect may happen here.
				 */
				SII_WAIT_UNTIL(cstat, regs->cstat,
					cstat & (SII_RST | SII_SCH),
					SII_WAIT_COUNT, i);
				if ((cstat & (SII_RST | SII_SCH |
				    SII_STATE_MSK)) != SII_SCH) {
#ifdef DEBUG
					if (sii_debug > 4)
						printf("cnt %d\n", i);
#endif
					dstat = regs->dstat;
					goto again;
				}
				regs->cstat = SII_SCH | SII_BER;
				regs->comm = 0;
				sc->sc_target = -1;
				/*
				 * Double check that we didn't miss a state
				 * change between seeing it and clearing
				 * the SII_SCH bit.
				 */
				i = regs->cstat;
				if (!(i & SII_SCH) && (i & SII_STATE_MSK) !=
				    (cstat & SII_STATE_MSK))
					sii_StateChg(sc, i);
				break;

			case MSG_MESSAGE_REJECT:
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				printf("%s: device %d: message reject.\n",
					sc->sc_dev.dv_xname, sc->sc_target);
				break;

			default:
				if (!(msg & MSG_IDENTIFYFLAG)) {
					printf("%s: device %d: couldn't handle "
					    "message 0x%x... rejecting.\n",
					    sc->sc_dev.dv_xname, sc->sc_target,
					    msg);
#ifdef DEBUG
					sii_DumpLog();
#endif
					goto reject;
				}
				/* acknowledge last byte */
				regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
					(comm & SII_STATE_MSK);
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & SII_DNE, SII_WAIT_COUNT, i);
				regs->dstat = SII_DNE;
				/* may want to check LUN some day */
				/* wait a short time for another msg */
				SII_WAIT_UNTIL(dstat, regs->dstat,
					dstat & (SII_CI | SII_DI),
					SII_WAIT_COUNT, i);
				if (dstat & (SII_CI | SII_DI)) {
#ifdef DEBUG
					if (sii_debug > 4)
						printf("cnt %d\n", i);
#endif
					goto again;
				}
			}
			break;

		case SII_MSG_OUT_PHASE:
#ifdef DEBUG
			if (sii_debug > 4)
				printf("MsgOut %x\n", state->flags); /* XXX */
#endif

			/*
			 * Check for parity error.
			 * Hardware will automatically set ATN
			 * to request the device for a MSG_OUT phase.
			 */
			if (state->flags & PENDING_CMD) {
				state->flags &= ~PENDING_CMD;
				dstat = sii_msgout(regs, comm, state->nextCmd);
			} else
				dstat = sii_msgout(regs, comm, MSG_NOOP);

#ifdef DEBUG
			if (sii_debug > 4)
				printf("ds %x i %d\n", dstat, i);
#endif
			/* just clear the DNE bit and check errors later */
			if (dstat & SII_DNE) {
				regs->dstat = SII_DNE;
			}
			break;

		default:
			printf("%s: Couldn't handle phase %d... ignoring.\n",
				   sc->sc_dev.dv_xname, dstat & SII_PHASE_MSK);
		}
	}

#ifdef DEBUG
	if (sii_debug > 3)
		printf("\n");
#endif
	/*
	 * Check to make sure we won't be interrupted again.
	 * Deglitch dstat register.
	 */
	msg = regs->dstat;
	while (msg != (dstat = regs->dstat))
		msg = dstat;
	if (dstat & (SII_CI | SII_DI))
		goto again;

	if (sc->sc_target < 0)
		sii_schedule(sc);
	return;

abort:
#ifdef DEBUG
	/* jump here to abort the current command */
	printf("%s: device %d: current command terminated\n",
		sc->sc_dev.dv_xname, sc->sc_target);

	sii_DumpLog();
#endif

	if ((cstat = regs->cstat) & SII_CON) {
		/* try to send an abort msg for awhile */
		regs->dstat = SII_DNE;
		regs->data = MSG_ABORT;
		regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) |
			SII_MSG_OUT_PHASE;
		SII_WAIT_UNTIL(dstat, regs->dstat,
		    (dstat & (SII_DNE | SII_PHASE_MSK)) ==
			(SII_DNE | SII_MSG_OUT_PHASE), 2 * SII_WAIT_COUNT, i);
#ifdef DEBUG
		if (sii_debug > 0)
			printf("Abort: cs %x ds %x i %d\n", cstat, dstat, i);
#endif
		if ((dstat & (SII_DNE | SII_PHASE_MSK)) ==
		    (SII_DNE | SII_MSG_OUT_PHASE)) {
			/* disconnect if command in progress */
			regs->comm = SII_DISCON;
			SII_WAIT_UNTIL(cstat, regs->cstat,
				!(cstat & SII_CON), SII_WAIT_COUNT, i);
		}
	} else {
#ifdef DEBUG
		if (sii_debug > 0)
			printf("Abort: cs %x\n", cstat);
#endif
	}
	regs->cstat = 0xffff;
	regs->dstat = 0xffff;
	regs->comm = 0;

	i = sc->sc_target;
	sc->sc_target = -1;

	/* XXX xs might have been get taken care of already */
	if (sc->sc_xs[i] != NULL)
		sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */);
	else
		sii_schedule(sc);

#ifdef DEBUG
	if (sii_debug > 4)
		printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
#endif
}

void
sii_StateChg(sc, cstat)
	struct sii_softc *sc;
	u_int cstat;
{
	SIIRegs *regs = sc->sc_regs;
	State *state;
	int i;

#ifdef DEBUG
	if (sii_debug > 4)
		printf("SCH: ");
#endif

	switch (cstat & SII_STATE_MSK) {
	case 0:
		/* disconnect */
		i = sc->sc_target;
		sc->sc_target = -1;
#ifdef DEBUG
		if (sii_debug > 4)
			printf("disconn %d ", i);
#endif
		if (i >= 0 && !sc->sc_st[i].prevComm) {
			printf("%s: device %d: spurrious disconnect (%d)\n",
				sc->sc_dev.dv_xname, i, regs->slcsr);
			sc->sc_st[i].prevComm = 0;
		}
		break;

	case SII_CON:
		/* connected as initiator */
		i = regs->slcsr;
		if (sc->sc_target == i)
			break;
		printf("%s: device %d: connect to device %d??\n",
			sc->sc_dev.dv_xname, sc->sc_target, i);
		sc->sc_target = i;
		break;

	case SII_DST:
		/*
		 * Wait for CON to become valid,
		 * chip is slow sometimes.
		 */
		SII_WAIT_UNTIL(cstat, regs->cstat,
			cstat & SII_CON, SII_WAIT_COUNT, i);
		if (!(cstat & SII_CON))
			panic("sii resel");
		/* FALLTHROUGH */

	case SII_CON | SII_DST:
		/*
		 * Its a reselection. Save the ID and wait for
		 * interrupts to tell us what to do next
		 * (should be MSG_IN of IDENTIFY).
		 * NOTE: sc_target may be >= 0 if we were in
		 * the process of trying to start a command
		 * and were reselected before the select
		 * command finished.
		 */
		sc->sc_target = i = regs->destat;
		state = &sc->sc_st[i];
		regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE;
		regs->dmctrl = state->dmaReqAck;
		if (!state->prevComm) {
			printf("%s: device %d: spurious reselection\n",
				sc->sc_dev.dv_xname, i);
			break;
		}
		state->prevComm = 0;
#ifdef DEBUG
		if (sii_debug > 4)
			printf("resel %d ", sc->sc_target);
#endif
		break;

#ifdef notyet
	case SII_DST | SII_TGT:
	case SII_CON | SII_DST | SII_TGT:
		/* connected as target */
		printf("%s: Selected by device %d as target!!\n",
			sc->sc_dev.dv_xname, regs->destat);
		regs->comm = SII_DISCON;
		SII_WAIT_UNTIL(!(regs->cstat & SII_CON),
			SII_WAIT_COUNT, i);
		regs->cstat = 0xffff;
		regs->dstat = 0xffff;
		regs->comm = 0;
		break;
#endif

	default:
		printf("%s: Unknown state change (cs %x)!!\n",
			sc->sc_dev.dv_xname, cstat);
#ifdef DEBUG
		sii_DumpLog();
#endif
	}
}

/*
 * Read one byte of data.
 * If 'ack' is true, acknowledge the byte.
 */
int
sii_GetByte(regs, phase, ack)
	SIIRegs *regs;
	int phase, ack;
{
	u_int dstat;
	u_int state;
	int i;
	int data;

	dstat = regs->dstat;
	state = regs->cstat & SII_STATE_MSK;
	i = -1;
	if (!(dstat & SII_IBF) || (dstat & SII_MIS)) {
		regs->comm = state | phase;
		/* wait a short time for IBF */
		SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF,
			SII_WAIT_COUNT, i);
#ifdef DEBUG
		if (!(dstat & SII_IBF))
			printf("status no IBF\n");
#endif
	}
	if (dstat & SII_DNE) { /* XXX */
		printf("sii_GetByte: DNE set 5\n");
#ifdef DEBUG
		sii_DumpLog();
#endif
		regs->dstat = SII_DNE;
	}
	data = regs->data;
	/* check for parity error */
	if (dstat & SII_IPE) {
#ifdef DEBUG
		if (sii_debug > 4)
			printf("cnt0 %d\n", i);
#endif
		printf("sii_GetByte: data %x ?? ds %x cm %x i %d\n",
			data, dstat, regs->comm, i); /* XXX */
		data = -1;
		ack = 1;
	}

	if (ack) {
		regs->comm = SII_INXFER | state | phase;

		/* wait a short time for XFER complete */
		SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
			SII_WAIT_COUNT, i);

		/* clear the DNE */
		if (dstat & SII_DNE) {
			regs->dstat = SII_DNE;
		}
	}

	return (data);
}

/*
 * Exchange messages to initiate synchronous data transfers.
 */
void
sii_DoSync(sc, state)
	struct sii_softc *sc;
	State *state;
{
	SIIRegs *regs = sc->sc_regs;
	u_int dstat, comm;
	int i, j;
	u_int len;

#ifdef DEBUG
	if (sii_debug > 0)
		printf("sii_DoSync: len %d per %d req/ack %d\n",
			sc->sc_buf[1], sc->sc_buf[3], sc->sc_buf[4]);
#endif

	/* SII chip can only handle a minimum transfer period of ??? */
	if (sc->sc_buf[3] < 64)
		sc->sc_buf[3] = 64;
	/* SII chip can only handle a maximum REQ/ACK offset of 3 */
	len = sc->sc_buf[4];
	if (len > 3)
		len = 3;

	sc->sc_buf[0] = MSG_EXTENDED;
	sc->sc_buf[1] = MSG_EXT_SDTR_LEN;
	sc->sc_buf[2] = MSG_EXT_SDTR;
	sc->sc_buf[4] = len;
#if 1
	comm = SII_INXFER | SII_ATN | SII_MSG_OUT_PHASE |
		(regs->cstat & SII_STATE_MSK);
	regs->comm = comm & ~SII_INXFER;
	for (j = 0; j < 5; j++) {
		/* wait for target to request the next byte */
		SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_TBE,
			SII_WAIT_COUNT, i);
		if (!(dstat & SII_TBE) ||
		    (dstat & SII_PHASE_MSK) != SII_MSG_OUT_PHASE) {
			printf("sii_DoSync: TBE? ds %x cm %x i %d\n",
				dstat, comm, i); /* XXX */
			return;
		}

		/* the last message byte should have ATN off */
		if (j == 4)
			comm &= ~SII_ATN;

		regs->data = sc->sc_buf[j];
		regs->comm = comm;

		/* wait a short time for XFER complete */
		SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
			SII_WAIT_COUNT, i);

		if (!(dstat & SII_DNE)) {
			printf("sii_DoSync: DNE? ds %x cm %x i %d\n",
				dstat, comm, i); /* XXX */
			return;
		}

		/* clear the DNE, other errors handled later */
		regs->dstat = SII_DNE;
	}
#else	/* 0 */
	sc->sii_copytobuf(sc, sc->sc_buf, SII_BUF_ADDR(sc), 5);
	printf("sii_DoSync: %x %x %x ds %x\n",
		((u_short *)sc->sc_buf)[0], ((u_short *)sc->sc_buf)[1],
		((u_short *)sc->sc_buf)[2], regs->dstat); /* XXX */
	regs->dmaddrl = SII_BUF_ADDR(sc);
	regs->dmaddrh = SII_BUF_ADDR(sc) >> 16;
	regs->dmlotc = 5;
	regs->comm = SII_DMA | SII_INXFER | SII_ATN |
		(regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE;

	/* wait a short time for XFER complete */
	SII_WAIT_UNTIL(dstat, regs->dstat,
		(dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ),
		SII_WAIT_COUNT, i);

	if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) {
		printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
			dstat, regs->comm, i, regs->dmlotc); /* XXX */
		sii_DumpLog(); /* XXX */
		return;
	}
	/* clear the DNE, other errors handled later */
	regs->dstat = SII_DNE;
#endif	/* 0 */

#if 0
	SII_WAIT_UNTIL(dstat, regs->dstat, dstat & (SII_CI | SII_DI),
		SII_WAIT_COUNT, i);
	printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
		dstat, regs->comm, i, regs->dmlotc); /* XXX */
#endif

	state->dmaReqAck = len;
}

/*
 * Issue the sequence of commands to the controller to start DMA.
 * NOTE: the data buffer should be word-aligned for DMA out.
 */
void
sii_StartDMA(regs, phase, dmaAddr, size)
	SIIRegs *regs;	/* which SII to use */
	int phase;	/* phase to send/receive data */
	u_int dmaAddr;	/* DMA buffer offset */
	int size;	/* # of bytes to transfer */
{

	if (regs->dstat & SII_DNE) { /* XXX */
		regs->dstat = SII_DNE;
		printf("sii_StartDMA: DNE set\n");
#ifdef DEBUG
		sii_DumpLog();
#endif
	}
	regs->dmaddrl = dmaAddr;
	regs->dmaddrh = dmaAddr >> 16;
	regs->dmlotc = size;
	regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) |
		phase;

#ifdef DEBUG
	if (sii_debug > 5) {
		printf("sii_StartDMA: cs 0x%x, ds 0x%x, cm 0x%x, size %d\n",
			regs->cstat, regs->dstat, regs->comm, size);
	}
#endif
}

/*
 * Call the device driver's 'done' routine to let it know the command is done.
 * The 'done' routine may try to start another command.
 * To be fair, we should start pending commands for other devices
 * before allowing the same device to start another command.
 */
void
sii_CmdDone(sc, target, error)
	struct sii_softc *sc;	/* which SII to use */
	int target;			/* which device is done */
	int error;			/* error code if any errors */
{
	struct scsi_xfer *xs;

	splassert(IPL_BIO);

	xs = sc->sc_xs[target];
#ifdef DIAGNOSTIC
	if (target < 0 || xs == NULL)
		panic("sii_CmdDone");
#endif
	sc->sc_xs[target] = NULL;
#ifdef DEBUG
	if (sii_debug > 1) {
		printf("sii_CmdDone: %s target %d cmd 0x%x err %d resid %d\n",
			sc->sc_dev.dv_xname,
			target, xs->cmd->opcode, error,
			sc->sc_st[target].buflen);
	}
#endif

	sii_schedule(sc);

	xs->status = sc->sc_st[target].statusByte;
	xs->error = error;
	xs->resid = sc->sc_st[target].buflen;
	scsi_done(xs);
}

/* Send a particular message */
u_int
sii_msgout(SIIRegs *regs, u_int state, u_int8_t cmd)
{
	int i;
	u_int dstat;

	regs->data = cmd;
	regs->comm = SII_INXFER | (state & SII_STATE_MSK) | SII_MSG_OUT_PHASE;

	SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, SII_WAIT_COUNT, i);

	return dstat;
}

void
sii_schedule(struct sii_softc *sc)
{
	unsigned int i;

	/* look for another device that is ready */
	for (i = 0; i < SII_NCMD; i++) {
		/* don't restart a disconnected command */
		if (sc->sc_xs[i] == NULL || sc->sc_st[i].prevComm)
			continue;
		sii_StartCmd(sc, i);
		break;
	}
}

#ifdef DEBUG
void
sii_DumpLog()
{
	struct sii_log *lp;

	printf("sii: cmd 0x%x bn %d cnt %d\n", sii_debug_cmd, sii_debug_bn,
		sii_debug_sz);
	lp = sii_logp;
	do {
		printf("target %d cs %x ds %x cm %x msg %x rlen %x dlen %x\n",
			lp->target, lp->cstat, lp->dstat, lp->comm, lp->msg,
			lp->rlen, lp->dlen);
		if (++lp >= &sii_log[NLOG])
			lp = sii_log;
	} while (lp != sii_logp);
}
#endif


code:
/*	$OpenBSD: siireg.h,v 1.1 2008/08/18 23:19:22 miod Exp $	*/
/*	$NetBSD: siireg.h,v 1.2 2006/07/29 19:10:57 ad Exp $	*/

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)siireg.h	8.1 (Berkeley) 6/10/93
 *
 * sii.h --
 *
 * 	SII registers.
 *
 *	Copyright (C) 1989 Digital Equipment Corporation.
 *	Permission to use, copy, modify, and distribute this software and
 *	its documentation for any purpose and without fee is hereby granted,
 *	provided that the above copyright notice appears in all copies.
 *	Digital Equipment Corporation makes no representations about the
 *	suitability of this software for any purpose.  It is provided "as is"
 *	without express or implied warranty.
 *
 * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/sii.h,
 *	v 1.2 89/08/15 19:53:04 rab Exp  SPRITE (DECWRL)
 */

#ifndef _SII
#define _SII

/*
 * SII hardware registers
 */
typedef volatile struct {
	u_short	sdb;		/* SCSI Data Bus and Parity */
	u_short	pad0;
	u_short	sc1;		/* SCSI Control Signals One */
	u_short	pad1;
	u_short	sc2;		/* SCSI Control Signals Two */
	u_short	pad2;
	u_short	csr;		/* Control/Status register */
	u_short	pad3;
	u_short	id;		/* Bus ID register */
	u_short	pad4;
	u_short	slcsr;		/* Select Control and Status Register */
	u_short	pad5;
	u_short	destat;		/* Selection Detector Status Register */
	u_short	pad6;
	u_short	dstmo;		/* DSSI Timeout Register */
	u_short	pad7;
	u_short	data;		/* Data Register */
	u_short	pad8;
	u_short	dmctrl;		/* DMA Control Register */
	u_short	pad9;
	u_short	dmlotc;		/* DMA Length of Transfer Counter */
	u_short	pad10;
	u_short	dmaddrl;	/* DMA Address Register Low */
	u_short	pad11;
	u_short	dmaddrh;	/* DMA Address Register High */
	u_short	pad12;
	u_short	dmabyte;	/* DMA Initial Byte Register */
	u_short	pad13;
	u_short	stlp;		/* DSSI Short Target List Pointer */
	u_short	pad14;
	u_short	ltlp;		/* DSSI Long Target List Pointer */
	u_short	pad15;
	u_short	ilp;		/* DSSI Initiator List Pointer */
	u_short	pad16;
	u_short	dsctrl;		/* DSSI Control Register */
	u_short	pad17;
	u_short	cstat;		/* Connection Status Register */
	u_short	pad18;
	u_short	dstat;		/* Data Transfer Status Register */
	u_short	pad19;
	u_short	comm;		/* Command Register */
	u_short	pad20;
	u_short	dictrl;		/* Diagnostic Control Register */
	u_short	pad21;
	u_short	clock;		/* Diagnostic Clock Register */
	u_short	pad22;
	u_short	bhdiag;		/* Bus Handler Diagnostic Register */
	u_short	pad23;
	u_short	sidiag;		/* SCSI IO Diagnostic Register */
	u_short	pad24;
	u_short	dmdiag;		/* Data Mover Diagnostic Register */
	u_short	pad25;
	u_short	mcdiag;		/* Main Control Diagnostic Register */
	u_short	pad26;
} SIIRegs;

/*
 * SC1 - SCSI Control Signals One
 */
#define SII_SC1_MSK	0x1ff		/* All possible signals on the bus */
#define SII_SC1_SEL	0x80		/* SCSI SEL signal active on bus */
#define SII_SC1_ATN	0x08		/* SCSI ATN signal active on bus */

/*
 * SC2 - SCSI Control Signals Two
 */
#define SII_SC2_IGS	0x8		/* SCSI drivers for initiator mode */

/*
 * CSR - Control/Status Register
 */
#define SII_HPM	0x10			/* SII in on an arbitrated SCSI bus */
#define	SII_RSE	0x08			/* 1 = respond to reselections */
#define SII_SLE	0x04			/* 1 = respond to selections */
#define SII_PCE	0x02			/* 1 = report parity errors */
#define SII_IE	0x01			/* 1 = enable interrupts */

/*
 * ID - Bus ID Register
 */
#define SII_ID_IO	0x8000		/* I/O */

/*
 * DESTAT - Selection Detector Status Register
 */
#define SII_IDMSK	0x7		/* ID of target reselected the SII */

/*
 * DMCTRL - DMA Control Register
 */
#define SII_ASYNC	0x00		/* REQ/ACK Offset for async mode */
#define SII_SYNC	0x03		/* REQ/ACK Offset for sync mode */

/*
 * DMLOTC - DMA Length Of Transfer Counter
 */
#define SII_TCMSK	0x1fff		/* transfer count mask */

/*
 * CSTAT - Connection Status Register
 */
#define	SII_CI		0x8000	/* composite interrupt bit for CSTAT */
#define SII_DI		0x4000	/* composite interrupt bit for DSTAT */
#define SII_RST		0x2000	/* 1 if reset is asserted on SCSI bus */
#define	SII_BER		0x1000	/* Bus error */
#define	SII_OBC		0x0800	/* Out_en Bit Cleared (DSSI mode) */
#define SII_TZ		0x0400	/* Target pointer Zero (STLP or LTLP is zero) */
#define	SII_BUF		0x0200	/* Buffer service - outbound pkt to non-DSSI */
#define SII_LDN		0x0100	/* List element Done */
#define SII_SCH		0x0080	/* State Change */
#define SII_CON		0x0040	/* SII is Connected to another device */
#define SII_DST		0x0020	/* SII was Destination of current transfer */
#define SII_TGT		0x0010	/* SII is operating as a Target */
#define SII_STATE_MSK	0x0070	/* State Mask */
#define SII_SWA		0x0008	/* Selected With Attention */
#define SII_SIP		0x0004	/* Selection In Progress */
#define SII_LST		0x0002	/* Lost arbitration */

/*
 * DSTAT - Data Transfer Status Register
 */
#define SII_DNE		0x2000	/* DMA transfer Done */
#define SII_TCZ		0x1000	/* Transfer Count register is Zero */
#define SII_TBE		0x0800	/* Transmit Buffer Empty */
#define SII_IBF		0x0400	/* Input Buffer Full */
#define SII_IPE		0x0200	/* Incoming Parity Error */
#define SII_OBB		0x0100	/* Odd Byte Boundry */
#define SII_MIS		0x0010	/* Phase Mismatch */
#define SII_ATN		0x0008	/* ATN set by initiator if in Target mode */
#define SII_MSG		0x0004	/* current bus state of MSG */
#define SII_CD		0x0002	/* current bus state of C/D */
#define SII_IO		0x0001	/* current bus state of I/O */
#define SII_PHASE_MSK	0x0007	/* Phase Mask */

/*
 * The different phases.
 */
#define SII_MSG_IN_PHASE	0x7
#define SII_MSG_OUT_PHASE	0x6
#define SII_STATUS_PHASE	0x3
#define SII_CMD_PHASE		0x2
#define SII_DATA_IN_PHASE	0x1
#define SII_DATA_OUT_PHASE	0x0

/*
 * COMM - Command Register
 */
#define	SII_DMA		0x8000	/* DMA mode */
#define SII_DO_RST	0x4000	/* Assert reset on SCSI bus for 25 usecs */
#define SII_RSL		0x1000	/* 0 = select, 1 = reselect desired device */

/* Commands: I - Initiator, T - Target, D - Disconnected */
#define SII_INXFER	0x0800	/* Information Transfer command	(I,T) */
#define SII_SELECT	0x0400	/* Select command		(D) */
#define SII_REQDATA	0x0200	/* Request Data command		(T) */
#define	SII_DISCON	0x0100	/* Disconnect command		(I,T,D) */
#define SII_CHRESET	0x0080	/* Chip Reset command		(I,T,D) */

/* Command state bits same as connection status register */
/* Command phase bits same as data transfer status register */

/*
 * DICTRL - Diagnostic Control Register
 */
#define SII_PRE		0x4	/* Enable the SII to drive the SCSI bus */

#define SII_WAIT_COUNT		10000	/* Delay count used for the SII chip */
/*
 * Max DMA transfer length for SII
 * The SII chip only has a 13 bit counter. If 8192 is used as the max count,
 * you can't tell the difference between a count of zero and 8192.
 * 8190 is used instead of 8191 so the count is even.
 */
#define SII_MAX_DMA_XFER_LENGTH	8192

#endif /* _SII */

license: unknown


code:
/*	$OpenBSD: siivar.h,v 1.3 2008/08/30 20:13:03 miod Exp $	*/
/*	$NetBSD: siivar.h,v 1.6 2000/06/02 20:16:51 mhitch Exp $	*/

#ifndef _SIIVAR_H
#define _SIIVAR_H

typedef struct scsi_state {
	int	statusByte;	/* status byte returned during STATUS_PHASE */
	int	dmaDataPhase;	/* which data phase to expect */
	int	dmaCurPhase;	/* SCSI phase if DMA is in progress */
	int	dmaPrevPhase;	/* SCSI phase of DMA suspended by disconnect */
	u_int	dmaAddr[2];	/* DMA buffer memory offsets */
	int	dmaBufIndex;	/* which of the above is currently in use */
	int	dmalen;		/* amount to transfer in this chunk */
	int	cmdlen;		/* total remaining amount of cmd to transfer */
	u_char	*cmd;		/* current pointer within scsicmd->cmd */
	int	buflen;		/* total remaining amount of data to transfer */
	char	*buf;		/* current pointer within scsicmd->buf */
	u_short	flags;		/* see below */
	u_int8_t nextCmd;	/* next command to send if PENDING_CMD set */
	u_short	prevComm;	/* command reg before disconnect */
	u_short	dmaCtrl;	/* DMA control register if disconnect */
	u_short	dmaAddrL;	/* DMA address register if disconnect */
	u_short	dmaAddrH;	/* DMA address register if disconnect */
	u_short	dmaCnt;		/* DMA count if disconnect */
	u_short	dmaByte;	/* DMA byte if disconnect on odd boundary */
	u_short	dmaReqAck;	/* DMA synchronous xfer offset or 0 if async */
} State;

/* state flags */
#define FIRST_DMA	0x01	/* true if no data DMA started yet */
#define	PENDING_CMD	0x02	/* need to send a particular command */

#define SII_NCMD	8
struct sii_softc {
	struct device sc_dev;		/* us as a device */
	struct scsi_link sc_link;	/* scsi link struct */
	SIIRegs	*sc_regs;		/* HW address of SII controller chip */
	int	sc_flags;
	int	sc_target;		/* target SCSI ID if connected */
	int	sc_hostid;
	void	(*sii_copytobuf)(void *, u_char *, u_int, int);
 	void	(*sii_copyfrombuf)(void *, u_int, u_char *, int);

	struct scsi_xfer *sc_xs[SII_NCMD]; /* currently executing requests */
	State	sc_st[SII_NCMD];	/* state info for each active command */

	u_char	sc_buf[258];		/* used for extended messages */
};

/* Machine-independent back-end attach entry point */
void	sii_attach(struct sii_softc *sc);
int	sii_intr(void *sc);

#endif	/* _SIIVAR_H */


code:
/*	$OpenBSD: vsms_ws.c,v 1.4 2008/08/26 19:46:23 miod Exp $	*/
/*	$NetBSD: dzms.c,v 1.1 2000/12/02 17:03:55 ragge Exp $	*/

/*
 * Copyright (c) 2008 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)ms.c	8.1 (Berkeley) 6/11/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/tty.h>

#include <machine/bus.h>

#include <vax/qbus/dzreg.h>
#include <vax/qbus/dzvar.h>

#include <vax/dec/dzkbdvar.h>
#include <vax/dec/vsmsvar.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>

int	lkms_handle_error(struct lkms_softc *, int);
void	lkms_input_disabled(struct lkms_softc *, int);
void	lkms_input_mouse(struct lkms_softc *, int);
void	lkms_input_tablet(struct lkms_softc *, int);

#define WSMS_BUTTON(x)	(1 << ((x) - 1))

struct	cfdriver lkms_cd = {
	NULL, "lkms", DV_DULL
};

/*
 * Report the device type and status on attachment, and return nonzero
 * if it is not in working state.
 */
int
lkms_handle_error(struct lkms_softc *sc, int mask)
{
	int error = sc->sc_error;

#ifdef DEBUG
	printf("%s: ", sc->dzms_dev.dv_xname);
#endif

	if (ISSET(sc->sc_flags, MS_TABLET)) {
#ifdef DEBUG
		printf("tablet, ");
#endif
		/*
		 * If we are a tablet, the stylet vs puck information
		 * is returned as a non-fatal status code.  Handle
		 * it there so that this does not get in the way.
		 */
		switch (error) {
		case ERROR_TABLET_NO_POINTER:
			/* i can has cheezpuck? */
#ifdef DEBUG
			printf("neither stylus nor puck connected\n");
#else
			printf("%s: neither stylus nor puck connected\n",
			    sc->dzms_dev.dv_xname);
#endif
			error = ERROR_OK;
			break;
		case ERROR_TABLET_STYLUS:
#ifdef DEBUG
			printf("stylus\n");
#endif
			SET(sc->sc_flags, MS_STYLUS);
			error = ERROR_OK;
			break;
		default:
#ifdef DEBUG
			printf("puck\n");
#endif
			break;
		}
	} else {
#ifdef DEBUG
		printf("mouse\n");
#endif
	}

	switch (error) {
	case ERROR_MEMORY_CKSUM_ERROR:
		printf("%s: memory checksum error\n",
		    sc->dzms_dev.dv_xname);
		break;
	case ERROR_BUTTON_ERROR:
	    {
		int btn;

		/*
		 * Print the list of defective parts
		 */
		if (ISSET(sc->sc_flags, MS_TABLET)) {
			if ((mask & FRAME_T_PR) != 0)
				printf("%s: proximity sensor defective\n",
				    sc->dzms_dev.dv_xname);
		} else
			mask <<= 1;

		for (btn = 1; btn < 4; btn++)
			if ((mask & (1 << btn)) != 0)
				printf("%s: button %d held down or defective\n",
				    sc->dzms_dev.dv_xname, btn);
	    }
		break;
	case ERROR_TABLET_LINK:
		/* how vague this error is... */
		printf("%s: analog or digital error\n",
		    sc->dzms_dev.dv_xname);
		break;
	default:
		printf("%s: %sselftest error %02x\n", sc->dzms_dev.dv_xname,
		    error >= ERROR_FATAL ? "fatal " : "", error);
		break;
	case ERROR_OK:
		break;
	}

	if (error >= ERROR_FATAL)
		return ENXIO;

	return 0;
}

int
lkms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct lkms_softc *sc = v;
	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;

	switch (cmd) {
	case WSMOUSEIO_GTYPE:
		*(int *)data = WSMOUSE_TYPE_VSXXX;
		return 0;

	case WSMOUSEIO_GCALIBCOORDS:
		if (ISSET(sc->sc_flags, MS_TABLET)) {
			/*
			 * The tablet has a usable size of 11 inch on each
			 * axis, with a 200dpi resolution.
			 */
			wsmc->minx = 0;
			wsmc->maxx = 200 * 11;
			wsmc->miny = 0;
			wsmc->maxy = 200 * 11;
			wsmc->swapxy = 0;
			wsmc->resx = wsmc->maxx;	/* anything better? */
			wsmc->resy = wsmc->maxy;	/* anything better? */
			wsmc->samplelen = 0;
			return 0;
		} else
			break;
	}

	return -1;
}

int
lkms_input(void *vsc, int data)
{
	struct lkms_softc *sc = vsc;

	if ((data & FRAME_MASK) != 0) {
		sc->sc_frametype = data & FRAME_TYPE_MASK;
		sc->sc_framepos = 0;
	} else
		sc->sc_framepos++;

	if (ISSET(sc->sc_flags, MS_ENABLED) &&
	    !ISSET(sc->sc_flags, MS_SELFTEST)) {
		switch (sc->sc_frametype) {
		case FRAME_MOUSE:
			lkms_input_mouse(sc, data);
			break;
		case FRAME_TABLET:
			lkms_input_tablet(sc, data);
			break;
		}
	} else
		lkms_input_disabled(sc, data);

	return 1;
}

/*
 * Input processing while the device is disabled.  We only are
 * interested in processing self test frames, so as to identify
 * the device and report its state.
 */
void
lkms_input_disabled(struct lkms_softc *sc, int data)
{
	if (!ISSET(sc->sc_flags, MS_SELFTEST))
		return;

	if (sc->sc_frametype == FRAME_SELFTEST) {
		switch (sc->sc_framepos) {
		case 0:
			break;
		case 1:
			data &= FRAME_ST_DEVICE_MASK;
			if (data == FRAME_ST_DEVICE_TABLET)
				SET(sc->sc_flags, MS_TABLET);
			else if (data != FRAME_ST_DEVICE_MOUSE) {
				printf("%s: unrecognized device type %02x\n",
				    sc->dzms_dev.dv_xname, data);
				goto fail;
			}
			break;
		case 2:
			sc->sc_error = data;
			break;
		case 3:
			if (lkms_handle_error(sc, data) != 0)
				goto fail;

			CLR(sc->sc_flags, MS_SELFTEST);
			goto success;
			break;
		}

		return;
	} /* else goto fail; */

fail:
	/*
	 * Our self test frame has been truncated, or we have received
	 * incorrect data (both could be a cable problem), or the
	 * selftest reported an error.  The device is unusable.
	 */
	CLR(sc->sc_flags, MS_TABLET | MS_STYLUS);

success:
	sc->sc_frametype = 0;
	wakeup(&sc->sc_flags);
}

/*
 * Input processing while the device is enabled, for mouse frames.
 */
void
lkms_input_mouse(struct lkms_softc *sc, int data)
{
	switch (sc->sc_framepos) {
	case 0:
		sc->buttons = 0;
		/* button order is inverted from wscons */
		if ((data & FRAME_MS_B3) != 0)
			sc->buttons |= WSMS_BUTTON(1);
		if ((data & FRAME_MS_B2) != 0)
			sc->buttons |= WSMS_BUTTON(2);
		if ((data & FRAME_MS_B1) != 0)
			sc->buttons |= WSMS_BUTTON(3);

		sc->dx = data & FRAME_MS_X_SIGN;
		sc->dy = data & FRAME_MS_Y_SIGN;
		break;
	case 1:
		if (sc->dx == 0)
			sc->dx = -data;
		else
			sc->dx = data;
		break;
	case 2:
		if (sc->dy == 0)
			sc->dy = -data;
		else
			sc->dy = data;
		wsmouse_input(sc->sc_wsmousedev, sc->buttons,
		    sc->dx, sc->dy, 0, 0, WSMOUSE_INPUT_DELTA);

		sc->sc_frametype = 0;
		break;
	}
}

/*
 * Input processing while the device is enabled, for tablet frames.
 */
void
lkms_input_tablet(struct lkms_softc *sc, int data)
{
	switch (sc->sc_framepos) {
	case 0:
		/*
		 * Button information will depend on the type of positional
		 * device:
		 * - puck buttons get reported as is, as a 4 button mouse.
		 *   Button order is opposite from mouse.
		 * - stylus barrel gets reported as left button, while tip
		 *   gets reported as right button.
		 *   Proximity sensor gets reported as a fictitious fifth
		 *   button.
		 */
		sc->buttons = 0;
		if ((data & FRAME_T_B1) != 0)
			sc->buttons |= WSMS_BUTTON(1);
		if ((data & FRAME_T_B2) != 0) {
			if (ISSET(sc->sc_flags, MS_STYLUS))
				sc->buttons |= WSMS_BUTTON(3);
			else
				sc->buttons |= WSMS_BUTTON(2);
		}
		if ((data & FRAME_T_B3) != 0)
			sc->buttons |= WSMS_BUTTON(3);
		if ((data & FRAME_T_B4) != 0)
			sc->buttons |= WSMS_BUTTON(4);
		if ((data & FRAME_T_PR) == 0)
			sc->buttons |= WSMS_BUTTON(5);
		break;
	case 1:
		sc->dx = data & 0x3f;
		break;
	case 2:
		sc->dx |= (data & 0x3f) << 6;
		break;
	case 3:
		sc->dy = data & 0x3f;
		break;
	case 4:
		sc->dy |= (data & 0x3f) << 6;
		wsmouse_input(sc->sc_wsmousedev, sc->buttons,
		    sc->dx, sc->dy, 0, 0,
		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);

		sc->sc_frametype = 0;
		break;
	}
}


code:
/*	$OpenBSD: vsmsvar.h,v 1.2 2008/08/22 21:05:07 miod Exp $	*/
/*
 * Copyright (c) 2008 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)ms.c	8.1 (Berkeley) 6/11/93
 */

/*
 * Command characters
 */
#define	VS_B9600		'B'	/* T only: switch to 9600 bps */
#define	VS_REQUEST_POINT	'D'	/* stop incremental position reports */
#define	VS_FREQ_55		'K'	/* T only: 55Hz report rate */
#define	VS_FREQ_72		'L'	/* T only: 72Hz report rate */
#define	VS_FREQ_120		'M'	/* T only: 120Hz report rate, 9600bps */
#define	VS_REQUEST_POSITION	'P'	/* request position (in point mode) */
#define VS_INCREMENTAL		'R'	/* incremental position reports */
#define VS_SELF_TEST		'T'	/* reset and self test */

/*
 * Data frame types
 */

#define	FRAME_MASK		0x80
#define	FRAME_TYPE_MASK		0xe0
#define	FRAME_MOUSE		0x80	/* 1 0 0 - mouse 3 byte packet */
#define	FRAME_SELFTEST		0xa0	/* 1 0 1 - selftest 4 byte packet */
#define	FRAME_TABLET		0xc0	/* 1 1 0 - tablet 5 byte packet */

/*
 * Selftest frame layout
 *	byte 0: frame type and device revision
 *	byte 1: manufacturing location code and device type
 *	byte 2: self test result
 *	byte 3: button mask (if result == button error)
 */

/* byte 0 */
#define	FRAME_ST_REV_MASK		0x0f	/* device revision */

/* byte 1 */
#define	FRAME_ST_LOCATION_MASK		0x70
#define	FRAME_ST_DEVICE_MASK		0x0f
#define	FRAME_ST_DEVICE_MOUSE		0x02
#define	FRAME_ST_DEVICE_TABLET		0x04

/* status test error codes */
#define	ERROR_OK			0x00
#define	ERROR_TABLET_STYLUS		0x11	/* stylus only, no puck */
#define	ERROR_TABLET_NO_POINTER		0x13	/* neither stylus nor puck */
#define	ERROR_FATAL			0x20	/* fatal errors from here */
#define	ERROR_TABLET_LINK		0x3a	/* tablet internal error */
#define	ERROR_BUTTON_ERROR		0x3d	/* button malfunction */
#define	ERROR_MEMORY_CKSUM_ERROR	0x3e	/* firmware malfunction */

/*
 * Mouse frame layout
 *	byte 0: frame type, delta signs, button mask
 *	byte 1: unsigned X delta
 *	byte 2: unsigned Y delta
 */
#define	FRAME_MS_X_SIGN			0x10	/* set if positive */
#define	FRAME_MS_Y_SIGN			0x08	/* set if positive */
#define	FRAME_MS_B3			0x04	/* left button */
#define	FRAME_MS_B2			0x02	/* middle button */
#define	FRAME_MS_B1			0x01	/* right button */

/*
 * Tablet frame layout
 *	byte 0: frame type, button and proximity sensor mask
 *	byte 1: low 6 bits of absolute X position
 *	byte 2: high 6 bits of absolute X position
 *	byte 3: low 6 bits of absolute Y position
 *	byte 4: high 6 bits of absolute Y position
 */
#define	FRAME_T_B4			0x10	/* puck bottom button */
#define	FRAME_T_B3			0x08	/* puck right button */
#define	FRAME_T_B2			0x04	/* puck top / stylus tip */
#define	FRAME_T_B1			0x02	/* puck left / stylus barrel */
#define	FRAME_T_PR			0x01	/* stylus proximity (if zero) */

struct lkms_softc {		/* driver status information */
	struct	device dzms_dev;	/* required first: base device */

	int	sc_flags;
#define	MS_ENABLED		0x01	/* input enabled */
#define	MS_SELFTEST		0x02	/* selftest in progress */
#define	MS_TABLET		0x04	/* device is a tablet */
#define	MS_STYLUS		0x08	/* tablet has a stylus, not a puck */

	int	sc_frametype;		/* frame type being processed */
	u_int	sc_framepos;		/* position in the frame */
	int	sc_error;		/* selftest error result */

	u_int	buttons;
	int	dx, dy;

	struct device *sc_wsmousedev;
};

int	lkms_ioctl(void *, u_long, caddr_t, int, struct proc *);
int	lkms_input(void *, int);


code:
/*	$OpenBSD: wskbdmap_lk201.c,v 1.7 2006/11/27 16:10:41 miod Exp $	*/
/* $NetBSD: wskbdmap_lk201.c,v 1.4 2000/12/02 16:57:41 ragge Exp $ */

#include <sys/types.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <vax/dec/wskbdmap_lk201.h>

#define KC(n) KS_KEYCODE((n) - MIN_LK201_KEY)

static const keysym_t lkkbd_keydesc_us[] = {
/*  pos      command		normal		shifted */
    KC(86),	KS_Cmd_Screen0,	KS_f1,
    KC(87),	KS_Cmd_Screen1,	KS_f2,
    KC(88),	KS_Cmd_Screen2,	KS_f3,
    KC(89),	KS_Cmd_Screen3,	KS_f4,
    KC(90),	KS_Cmd_Screen4,	KS_f5,
    KC(100),	KS_Cmd_Screen5,	KS_f6,
    KC(101),	KS_Cmd_Screen6,	KS_f7,
    KC(102),	KS_Cmd_Screen7,	KS_f8,
    KC(103),	KS_Cmd_Screen8,	KS_f9,
    KC(104),	KS_Cmd_Screen9,	KS_f10,
    KC(113),	KS_Cmd_Debugger,	KS_Escape, /* F11 */
    KC(114),			KS_f12,
    KC(115),			KS_f13,
    KC(116),			KS_f14,
    KC(124),			KS_Help,
    KC(125),	KS_Cmd,		KS_Execute,
    KC(128),			KS_f17,
    KC(129),			KS_f18,
    KC(130),			KS_f19,
    KC(131),			KS_f20,
    KC(138),			KS_Find,
    KC(139),			KS_Insert,
    KC(140),			KS_KP_Delete,
    KC(141),			KS_Select,
    KC(142),			KS_Prior,
    KC(143),			KS_Next,
    KC(146),			KS_KP_0,
    KC(148),			KS_KP_Decimal,
    KC(149),			KS_KP_Enter,
    KC(150),			KS_KP_1,
    KC(151),			KS_KP_2,
    KC(152),			KS_KP_3,
    KC(153),			KS_KP_4,
    KC(154),			KS_KP_5,
    KC(155),			KS_KP_6,
    KC(156),			KS_KP_Separator,
    KC(157),			KS_KP_7,
    KC(158),			KS_KP_8,
    KC(159),			KS_KP_9,
    KC(160),			KS_KP_Subtract,
    KC(161),			KS_KP_F1,
    KC(162),			KS_KP_F2,
    KC(163),			KS_KP_F3,
    KC(164),			KS_KP_F4,
    KC(167),			KS_Left,
    KC(168),			KS_Right,
    KC(169),			KS_Down,
    KC(170),			KS_Up,
    KC(171),			KS_Shift_R,			   /* LK 401 */
    KC(172),	KS_Cmd2,	KS_Alt_L,			   /* LK 401 */
    KC(173),			KS_Multi_key, /* right compose */  /* LK 401 */
    KC(174),			KS_Shift_L,
    KC(175),	KS_Cmd1,	KS_Control_L,
    KC(176),			KS_Caps_Lock,
    KC(177),			KS_Multi_key, /* (left) compose */ /* LK 401 */
    KC(178),	KS_Cmd2,	KS_Alt_R,			   /* LK 401 */
    KC(188),			KS_Delete,
    KC(189),			KS_Return,
    KC(190),			KS_Tab,
    KC(191),			KS_grave,	KS_asciitilde,
    KC(192),			KS_1,		KS_exclam,
    KC(193),			KS_q,
    KC(194),			KS_a,
    KC(195),			KS_z,
    KC(197),			KS_2,		KS_at,
    KC(198),			KS_w,
    KC(199),			KS_s,
    KC(200),			KS_x,
    KC(201),			KS_less,	KS_greater,
    KC(203),			KS_3,		KS_numbersign,
    KC(204),			KS_e,
    KC(205),			KS_d,
    KC(206),			KS_c,
    KC(208),			KS_4,		KS_dollar,
    KC(209),			KS_r,
    KC(210),			KS_f,
    KC(211),			KS_v,
    KC(212),			KS_space,
    KC(214),			KS_5,		KS_percent,
    KC(215),			KS_t,
    KC(216),			KS_g,
    KC(217),			KS_b,
    KC(219),			KS_6,		KS_asciicircum,
    KC(220),			KS_y,
    KC(221),			KS_h,
    KC(222),			KS_n,
    KC(224),			KS_7,		KS_ampersand,
    KC(225),			KS_u,
    KC(226),			KS_j,
    KC(227),			KS_m,
    KC(229),			KS_8,		KS_asterisk,
    KC(230),			KS_i,
    KC(231),			KS_k,
    KC(232),			KS_comma,	KS_less,
    KC(234),			KS_9,		KS_parenleft,
    KC(235),			KS_o,
    KC(236),			KS_l,
    KC(237),			KS_period,	KS_greater,
    KC(239),			KS_0,		KS_parenright,
    KC(240),			KS_p,
    KC(242),			KS_semicolon,	KS_colon,
    KC(243),			KS_slash,	KS_question,
    KC(245),			KS_equal,	KS_plus,
    KC(246),			KS_bracketright,	KS_braceright,
    KC(247),			KS_backslash,	KS_bar,
    KC(249),			KS_minus,	KS_underscore,
    KC(250),			KS_bracketleft,	KS_braceleft,
    KC(251),			KS_apostrophe,	KS_quotedbl,
    /* remapped Compose key (177) for LK 201 */
    KC(252),	KS_Cmd2,	KS_Alt_L,	KS_Multi_key
};

static const keysym_t lkkbd_keydesc_de[] = {
/*  pos		command		normal		shifted		XXXXXXX */
    KC(178),    KS_Cmd2,        KS_Mode_switch,
    KC(191),			KS_dead_tilde,	KS_dead_circumflex,
    KC(195),			KS_y,
    KC(197),			KS_2,		KS_quotedbl,
    KC(203),			KS_3,		KS_section,
    KC(219),			KS_6,		KS_ampersand,
    KC(220),			KS_z,
    KC(224),			KS_7,		KS_slash,
    KC(229),			KS_8,		KS_parenleft,
    KC(232),			KS_comma,	KS_semicolon,
    KC(234),			KS_9,		KS_parenright,
    KC(237),			KS_period,	KS_colon,
    KC(239),			KS_0,		KS_equal,
    KC(242),			KS_odiaeresis,	KS_Odiaeresis,
				KS_braceright,	KS_braceleft,
    KC(243),			KS_minus,	KS_underscore,
    KC(245),			KS_dead_acute,	KS_dead_grave,
    KC(246),			KS_plus,	KS_asterisk,
    KC(247),			KS_numbersign,	KS_apostrophe,
    KC(249),			KS_ssharp,	KS_question,	KS_bar,
    KC(250),			KS_udiaeresis,	KS_Udiaeresis,
				KS_at,		KS_backslash,
    KC(251),			KS_adiaeresis,	KS_Adiaeresis,
				KS_bracketright,KS_bracketleft,
    /* remapped Compose key (177) for LK 201 */
    KC(252),	KS_Cmd2,	KS_Mode_switch,	KS_Multi_key
};

static const keysym_t lkkbd_keydesc_de_nodead[] = {
/*  pos		command		normal		shifted		XXXXXXX */
    KC(191),			KS_asciitilde,	KS_asciicircum,
    KC(245),			KS_apostrophe,	KS_grave,
};

static const keysym_t lkkbd_keydesc_fr[] = {
    KC(178),	KS_Cmd2,	KS_Mode_switch,
    KC(192),			KS_ampersand,	KS_1,
    KC(193),			KS_a,
    KC(194),			KS_q,
    KC(195),			KS_w,
    KC(197),			KS_eacute,	KS_2,		KS_bar,
    KC(198),			KS_z,
    KC(203),			KS_quotedbl,	KS_3,
    KC(208),			KS_apostrophe,	KS_4,
    KC(214),			KS_parenleft,	KS_5,
    KC(219),			KS_section,	KS_6,		KS_bracketleft,
    KC(224),			KS_egrave,	KS_7,		KS_bracketright,
    KC(227),			KS_comma,	KS_question,
    KC(229),			KS_exclam,	KS_8,
    KC(232),			KS_semicolon,	KS_period,
    KC(234),			KS_ccedilla,	KS_9,		KS_braceleft,
    KC(237),			KS_colon,	KS_slash,
    KC(239),			KS_agrave,	KS_0,		KS_braceright,
    KC(242),			KS_m,
    KC(243),			KS_equal,	KS_plus,
    KC(245),			KS_minus,	KS_underscore,
    KC(246),			KS_dollar,	KS_asterisk,
    KC(247),			KS_numbersign,	KS_at,
    KC(249),			KS_parenright,	KS_degree,
    KC(250),			KS_dead_circumflex, KS_dead_diaeresis,
    KC(251),			KS_ugrave,	KS_percent,	KS_backslash,
    /* remapped Compose key (177) for LK 201 */
    KC(252),	KS_Cmd2,	KS_Mode_switch,	KS_Multi_key
};

static const keysym_t lkkbd_keydesc_pt[] = {
    KC(191),			KS_backslash,	KS_bar,
    KC(219),			KS_6,		KS_quotedbl,
    KC(232),			KS_comma,	KS_semicolon,
    KC(237),			KS_period,	KS_colon,
    KC(242),			KS_ccedilla,
    KC(247),			KS_bracketleft,	KS_braceleft,
    KC(250),			KS_dead_acute,	KS_dead_grave,
    KC(251),			KS_dead_tilde,	KS_dead_circumflex,
};

#define KBD_MAP(name, base, map) \
			{ name, base, sizeof(map)/sizeof(keysym_t), map }

const struct wscons_keydesc lkkbd_keydesctab[] = {
	KBD_MAP(KB_US,			0,	lkkbd_keydesc_us),
	KBD_MAP(KB_DE,			KB_US,	lkkbd_keydesc_de),
	KBD_MAP(KB_DE | KB_NODEAD,	KB_DE,	lkkbd_keydesc_de_nodead),
	KBD_MAP(KB_FR,			KB_US,	lkkbd_keydesc_fr),
	KBD_MAP(KB_PT,			KB_US,	lkkbd_keydesc_pt),
	{0, 0, 0, 0}
};

#undef KBD_MAP
#undef KC


code:
/*	$OpenBSD: wskbdmap_lk201.h,v 1.2 2006/07/29 17:06:25 miod Exp $	*/
/* $NetBSD: wskbdmap_lk201.h,v 1.2 1998/09/17 19:59:30 drochner Exp $ */

#define MIN_LK201_KEY            86
#define MAX_LK201_KEY           251

extern const struct wscons_keydesc lkkbd_keydesctab[];


code: src/sys/arch/vax/if

path: sys/arch/vax/if/CVS/Repository


code:
/*	$OpenBSD: if_de.c,v 1.30 2015/07/04 17:32:20 miod Exp $	*/
/*	$NetBSD: if_de.c,v 1.27 1997/04/19 15:02:29 ragge Exp $	*/

/*
 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)if_de.c	7.12 (Berkeley) 12/16/90
 */

/*
 * DEC DEUNA interface
 *
 *	Lou Salkind
 *	New York University
 *
 * TODO:
 *	timeout routine (get statistics)
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/device.h>

#include <machine/pte.h>
#include <machine/sid.h>

#include <net/if.h>
#include <net/if_dl.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>

#include <machine/cpu.h>
#include <machine/mtpr.h>

#include <vax/if/if_dereg.h>
#include <vax/if/if_uba.h>
#include <vax/uba/ubavar.h>

#define NXMT	3	/* number of transmit buffers */
#define NRCV	7	/* number of receive buffers (must be > 1) */

int	dedebug = 0;

/*
 * Ethernet software status per interface.
 *
 * Each interface is referenced by a network interface structure,
 * ds_if, which the routing code uses to locate the interface.
 * This structure contains the output queue for the interface, its address, ...
 * We also have, for each interface, a UBA interface structure, which
 * contains information about the UNIBUS resources held by the interface:
 * map registers, buffered data paths, etc.  Information is cached in this
 * structure for use by the if_uba.c routines in running the interface
 * efficiently.
 */
struct	de_softc {
	struct	device ds_dev;		/* Configuration common part */
	struct	arpcom ds_ac;		/* Ethernet common part */
	struct	dedevice *ds_vaddr;	/* Virtual address of this interface */
#define 	ds_if	ds_ac.ac_if	/* network-visible interface */
	int	ds_flags;
#define	DSF_RUNNING	2		/* board is enabled */
#define	DSF_SETADDR	4		/* physical address is changed */
	int	ds_ubaddr;		/* map info for incore structs */
	struct	ifubinfo ds_deuba;	/* unibus resource structure */
	struct	ifrw ds_ifr[NRCV];	/* unibus receive maps */
	struct	ifxmt ds_ifw[NXMT];	/* unibus xmt maps */
	/* the following structures are always mapped in */
	struct	de_pcbb ds_pcbb;	/* port control block */
	struct	de_ring ds_xrent[NXMT];	/* transmit ring entries */
	struct	de_ring ds_rrent[NRCV];	/* receive ring entries */
	struct	de_udbbuf ds_udbbuf;	/* UNIBUS data buffer */
	/* end mapped area */
#define	INCORE_BASE(p)	((char *)&(p)->ds_pcbb)
#define	RVAL_OFF(s,n)	((char *)&(s)->n - INCORE_BASE(s))
#define	LVAL_OFF(s,n)	((char *)(s)->n - INCORE_BASE(s))
#define	PCBB_OFFSET(s)	RVAL_OFF(s,ds_pcbb)
#define	XRENT_OFFSET(s)	LVAL_OFF(s,ds_xrent)
#define	RRENT_OFFSET(s)	LVAL_OFF(s,ds_rrent)
#define	UDBBUF_OFFSET(s)	RVAL_OFF(s,ds_udbbuf)
#define	INCORE_SIZE(s)	RVAL_OFF(s, ds_xindex)
	int	ds_xindex;		/* UNA index into transmit chain */
	int	ds_rindex;		/* UNA index into receive chain */
	int	ds_xfree;		/* index for next transmit buffer */
	int	ds_nxmit;		/* # of transmits in progress */
};

int	dematch(struct device *, void *, void *);
void	deattach(struct device *, struct device *, void *);
int	dewait(struct de_softc *, char *);
void	deinit(struct de_softc *);
int	deioctl(struct ifnet *, u_long, caddr_t);
void	dereset(int);
void	destart(struct ifnet *);
void	deread(struct de_softc *, struct ifrw *, int);
void	derecv(int);
void	de_setaddr(u_char *, struct de_softc *);
void	deintr(int);

struct	cfdriver de_cd = {
	NULL, "de", DV_IFNET
};

struct	cfattach de_ca = {
	sizeof(struct de_softc), dematch, deattach
};
/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.  We get the ethernet address here.
 */
void
deattach(parent, self, aux)
	struct	device *parent, *self;
	void	*aux;
{
	struct uba_attach_args *ua = aux;
	struct de_softc *ds = (struct de_softc *)self;
	struct ifnet *ifp = &ds->ds_if;
	struct dedevice *addr;
	char *c;
	int csr1;
	u_int8_t myaddr[ETHER_ADDR_LEN];

	addr = (struct dedevice *)ua->ua_addr;
	ds->ds_vaddr = addr;
	bcopy(ds->ds_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
	ifp->if_softc = ds;
	ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS;

	/*
	 * What kind of a board is this?
	 * The error bits 4-6 in pcsr1 are a device id as long as
	 * the high byte is zero.
	 */
	csr1 = addr->pcsr1;
	if (csr1 & 0xff60)
		c = "broken";
	else if (csr1 & 0x10)
		c = "delua";
	else
		c = "deuna";

	printf("\n%s: %s\n", ds->ds_dev.dv_xname, c);
	/*
	 * Reset the board and temporarily map
	 * the pcbb buffer onto the Unibus.
	 */
	addr->pcsr0 = 0;		/* reset INTE */
	DELAY(100);
	addr->pcsr0 = PCSR0_RSET;
	(void)dewait(ds, "reset");

	ds->ds_ubaddr = uballoc((void *)ds->ds_dev.dv_parent,
	    (char *)&ds->ds_pcbb, sizeof (struct de_pcbb), 0);
	addr->pcsr2 = ds->ds_ubaddr & 0xffff;
	addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3;
	addr->pclow = CMD_GETPCBB;
	(void)dewait(ds, "pcbb");

	ds->ds_pcbb.pcbb0 = FC_RDPHYAD;
	addr->pclow = CMD_GETCMD;
	(void)dewait(ds, "read addr ");

	ubarelse((void *)ds->ds_dev.dv_parent, &ds->ds_ubaddr);
	bcopy((caddr_t)&ds->ds_pcbb.pcbb2, myaddr, sizeof (myaddr));
	printf("%s: address %s\n", ds->ds_dev.dv_xname,
	    ether_sprintf(myaddr));
	ifp->if_ioctl = deioctl;
	ifp->if_start = destart;
	ds->ds_deuba.iff_flags = UBA_CANTWAIT;
#ifdef notdef
	/* CAN WE USE BDP's ??? */
	ds->ds_deuba.iff_flags |= UBA_NEEDBDP;
#endif
	if_attach(ifp);
	ether_ifattach(ifp);
}

/*
 * Reset of interface after UNIBUS reset.
 */
void
dereset(unit)
	int unit;
{
	struct	de_softc *sc = de_cd.cd_devs[unit];
	volatile struct dedevice *addr = sc->ds_vaddr;

	printf(" de%d", unit);
	sc->ds_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
	sc->ds_flags &= ~DSF_RUNNING;
	addr->pcsr0 = PCSR0_RSET;
	(void)dewait(sc, "reset");
	deinit(sc);
}

/*
 * Initialization of interface; clear recorded pending
 * operations, and reinitialize UNIBUS usage.
 */
void
deinit(ds)
	struct de_softc *ds;
{
	volatile struct dedevice *addr;
	struct ifnet *ifp = &ds->ds_if;
	struct ifrw *ifrw;
	struct ifxmt *ifxp;
	struct de_ring *rp;
	int s,incaddr;

	if (ds->ds_flags & DSF_RUNNING)
		return;
	if ((ifp->if_flags & IFF_RUNNING) == 0) {
		if (if_ubaminit(&ds->ds_deuba, (void *)ds->ds_dev.dv_parent,
		    sizeof (struct ether_header), (int)vax_atop(ETHERMTU),
		    ds->ds_ifr, NRCV, ds->ds_ifw, NXMT) == 0) {
			printf("%s: can't initialize\n", ds->ds_dev.dv_xname);
			ds->ds_if.if_flags &= ~IFF_UP;
			return;
		}
		ds->ds_ubaddr = uballoc((void *)ds->ds_dev.dv_parent,
		    INCORE_BASE(ds), INCORE_SIZE(ds), 0);
	}
	addr = ds->ds_vaddr;

	/* set the pcbb block address */
	incaddr = ds->ds_ubaddr + PCBB_OFFSET(ds);
	addr->pcsr2 = incaddr & 0xffff;
	addr->pcsr3 = (incaddr >> 16) & 0x3;
	addr->pclow = 0;	/* reset INTE */
	DELAY(500);
	addr->pclow = CMD_GETPCBB;
	(void)dewait(ds, "pcbb");

	/* set the transmit and receive ring header addresses */
	incaddr = ds->ds_ubaddr + UDBBUF_OFFSET(ds);
	ds->ds_pcbb.pcbb0 = FC_WTRING;
	ds->ds_pcbb.pcbb2 = incaddr & 0xffff;
	ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3;

	incaddr = ds->ds_ubaddr + XRENT_OFFSET(ds);
	ds->ds_udbbuf.b_tdrbl = incaddr & 0xffff;
	ds->ds_udbbuf.b_tdrbh = (incaddr >> 16) & 0x3;
	ds->ds_udbbuf.b_telen = sizeof (struct de_ring) / sizeof (short);
	ds->ds_udbbuf.b_trlen = NXMT;
	incaddr = ds->ds_ubaddr + RRENT_OFFSET(ds);
	ds->ds_udbbuf.b_rdrbl = incaddr & 0xffff;
	ds->ds_udbbuf.b_rdrbh = (incaddr >> 16) & 0x3;
	ds->ds_udbbuf.b_relen = sizeof (struct de_ring) / sizeof (short);
	ds->ds_udbbuf.b_rrlen = NRCV;

	addr->pclow = CMD_GETCMD;
	(void)dewait(ds, "wtring");

	/* initialize the mode - enable hardware padding */
	ds->ds_pcbb.pcbb0 = FC_WTMODE;
	/* let hardware do padding - set MTCH bit on broadcast */
	ds->ds_pcbb.pcbb2 = MOD_TPAD|MOD_HDX;
	addr->pclow = CMD_GETCMD;
	(void)dewait(ds, "wtmode");

	/* set up the receive and transmit ring entries */
	ifxp = &ds->ds_ifw[0];
	for (rp = &ds->ds_xrent[0]; rp < &ds->ds_xrent[NXMT]; rp++) {
		rp->r_segbl = ifxp->ifw_info & 0xffff;
		rp->r_segbh = (ifxp->ifw_info >> 16) & 0x3;
		rp->r_flags = 0;
		ifxp++;
	}
	ifrw = &ds->ds_ifr[0];
	for (rp = &ds->ds_rrent[0]; rp < &ds->ds_rrent[NRCV]; rp++) {
		rp->r_slen = sizeof (struct de_buf);
		rp->r_segbl = ifrw->ifrw_info & 0xffff;
		rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3;
		rp->r_flags = RFLG_OWN;		/* hang receive */
		ifrw++;
	}

	/* start up the board (rah rah) */
	s = splnet();
	ds->ds_rindex = ds->ds_xindex = ds->ds_xfree = ds->ds_nxmit = 0;
	ds->ds_if.if_flags |= IFF_RUNNING;
	addr->pclow = PCSR0_INTE;		/* avoid interlock */
	destart(&ds->ds_if);		/* queue output packets */
	ds->ds_flags |= DSF_RUNNING;		/* need before de_setaddr */
	if (ds->ds_flags & DSF_SETADDR)
		de_setaddr(ds->ds_ac.ac_enaddr, ds);
	addr->pclow = CMD_START | PCSR0_INTE;
	splx(s);
}

/*
 * Setup output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 * Must be called from ipl >= our interrupt level.
 */
void
destart(ifp)
	struct ifnet *ifp;
{
	int len;
	register struct de_softc *ds = ifp->if_softc;
	volatile struct dedevice *addr = ds->ds_vaddr;
	register struct de_ring *rp;
	struct mbuf *m;
	register int nxmit;

	/*
	 * the following test is necessary, since
	 * the code is not reentrant and we have
	 * multiple transmission buffers.
	 */
	if (ds->ds_if.if_flags & IFF_OACTIVE)
		return;
	for (nxmit = ds->ds_nxmit; nxmit < NXMT; nxmit++) {
		IF_DEQUEUE(&ds->ds_if.if_snd, m);
		if (m == NULL)
			break;
		rp = &ds->ds_xrent[ds->ds_xfree];
		if (rp->r_flags & XFLG_OWN)
			panic("deuna xmit in progress");
		len = if_ubaput(&ds->ds_deuba, &ds->ds_ifw[ds->ds_xfree], m);
		if (ds->ds_deuba.iff_flags & UBA_NEEDBDP) {
			struct uba_softc *uh = (void *)ds->ds_dev.dv_parent;

			if (uh->uh_ubapurge)
				(*uh->uh_ubapurge)
					(uh, ds->ds_ifw[ds->ds_xfree].ifw_bdp);
		}
		rp->r_slen = len;
		rp->r_tdrerr = 0;
		rp->r_flags = XFLG_STP|XFLG_ENP|XFLG_OWN;

		ds->ds_xfree++;
		if (ds->ds_xfree == NXMT)
			ds->ds_xfree = 0;
	}
	if (ds->ds_nxmit != nxmit) {
		ds->ds_nxmit = nxmit;
		if (ds->ds_flags & DSF_RUNNING)
			addr->pclow = PCSR0_INTE|CMD_PDMD;
	}
}

/*
 * Command done interrupt.
 */
void
deintr(unit)
	int	unit;
{
	volatile struct dedevice *addr;
	register struct de_softc *ds;
	register struct de_ring *rp;
	register struct ifxmt *ifxp;
	short csr0;

	ds = de_cd.cd_devs[unit];
	addr = ds->ds_vaddr;

	/* save flags right away - clear out interrupt bits */
	csr0 = addr->pcsr0;
	addr->pchigh = csr0 >> 8;

	ds->ds_if.if_flags |= IFF_OACTIVE;	/* prevent entering destart */
	/*
	 * if receive, put receive buffer on mbuf
	 * and hang the request again
	 */
	derecv(unit);

	/*
	 * Poll transmit ring and check status.
	 * Be careful about loopback requests.
	 * Then free buffer space and check for
	 * more transmit requests.
	 */
	for ( ; ds->ds_nxmit > 0; ds->ds_nxmit--) {
		rp = &ds->ds_xrent[ds->ds_xindex];
		if (rp->r_flags & XFLG_OWN)
			break;
		ds->ds_if.if_opackets++;
		ifxp = &ds->ds_ifw[ds->ds_xindex];
		/* check for unusual conditions */
		if (rp->r_flags & (XFLG_ERRS|XFLG_MTCH|XFLG_ONE|XFLG_MORE)) {
			if (rp->r_flags & XFLG_ERRS) {
				/* output error */
				ds->ds_if.if_oerrors++;
				if (dedebug) {
					printf("de%d: oerror, flags=%b ",
					    unit, rp->r_flags, XFLG_BITS);
					printf("tdrerr=%b\n",
					    rp->r_tdrerr, XERR_BITS);
				}
			} else if (rp->r_flags & XFLG_ONE) {
				/* one collision */
				ds->ds_if.if_collisions++;
			} else if (rp->r_flags & XFLG_MORE) {
				/* more than one collision */
				ds->ds_if.if_collisions += 2;	/* guess */
			} else if (rp->r_flags & XFLG_MTCH) {
				/* received our own packet */
				deread(ds, &ifxp->ifrw,
				    rp->r_slen - sizeof (struct ether_header));
			}
		}
		if (ifxp->ifw_xtofree) {
			m_freem(ifxp->ifw_xtofree);
			ifxp->ifw_xtofree = 0;
		}
		/* check if next transmit buffer also finished */
		ds->ds_xindex++;
		if (ds->ds_xindex == NXMT)
			ds->ds_xindex = 0;
	}
	ds->ds_if.if_flags &= ~IFF_OACTIVE;
	destart(&ds->ds_if);

	if (csr0 & PCSR0_RCBI) {
		if (dedebug)
			log(LOG_WARNING, "de%d: buffer unavailable\n", unit);
		addr->pclow = PCSR0_INTE|CMD_PDMD;
	}
}

/*
 * Ethernet interface receiver interface.
 * If input error just drop packet.
 * Otherwise purge input buffered data path and examine
 * packet to determine type.  If can't determine length
 * from type, then have to drop packet.	 Othewise decapsulate
 * packet based on type and pass to type specific higher-level
 * input routine.
 */
void
derecv(unit)
	int unit;
{
	register struct de_softc *ds = de_cd.cd_devs[unit];
	register struct de_ring *rp;
	int len;

	rp = &ds->ds_rrent[ds->ds_rindex];
	while ((rp->r_flags & RFLG_OWN) == 0) {
		if (ds->ds_deuba.iff_flags & UBA_NEEDBDP) {
			struct uba_softc *uh = (void *)ds->ds_dev.dv_parent;

			if (uh->uh_ubapurge)
				(*uh->uh_ubapurge)
					(uh,ds->ds_ifr[ds->ds_rindex].ifrw_bdp);
		}
		len = (rp->r_lenerr&RERR_MLEN) - sizeof (struct ether_header)
			- 4;	/* don't forget checksum! */
		/* check for errors */
		if ((rp->r_flags & (RFLG_ERRS|RFLG_FRAM|RFLG_OFLO|RFLG_CRC)) ||
		    (rp->r_flags&(RFLG_STP|RFLG_ENP)) != (RFLG_STP|RFLG_ENP) ||
		    (rp->r_lenerr & (RERR_BUFL|RERR_UBTO|RERR_NCHN)) ||
		    len < ETHERMIN || len > ETHERMTU) {
			ds->ds_if.if_ierrors++;
			if (dedebug) {
				printf("de%d: ierror, flags=%b ",
				    unit, rp->r_flags, RFLG_BITS);
				printf("lenerr=%b (len=%d)\n",
				    rp->r_lenerr, RERR_BITS, len);
			}
		} else
			deread(ds, &ds->ds_ifr[ds->ds_rindex], len);

		/* hang the receive buffer again */
		rp->r_lenerr = 0;
		rp->r_flags = RFLG_OWN;

		/* check next receive buffer */
		ds->ds_rindex++;
		if (ds->ds_rindex == NRCV)
			ds->ds_rindex = 0;
		rp = &ds->ds_rrent[ds->ds_rindex];
	}
}

/*
 * Pass a packet to the higher levels.
 * We deal with the trailer protocol here.
 */
void
deread(ds, ifrw, len)
	register struct de_softc *ds;
	struct ifrw *ifrw;
	int len;
{
	struct ether_header *eh;
	struct mbuf *m;

	eh = (struct ether_header *)ifrw->ifrw_addr;
	if (len == 0)
		return;

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; if_ubaget will then force this header
	 * information to be at the front.
	 */
	m = if_ubaget(&ds->ds_deuba, ifrw, len, &ds->ds_if);
	if (m) {
		/*
		 * XXX not exactly sure what if_ubaget does.  Manually
		 * add the ethernet header to the start of the mbuf chain.
		 */
		M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
		if (m) {
			*mtod(m, struct ether_header *) = *eh;
			ether_input_mbuf(&ds->ds_if, m);
		}
	}
}
/*
 * Process an ioctl request.
 */
int
deioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	u_long	cmd;
	caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	register struct de_softc *ds = ifp->if_softc;
	int s, error = 0;

	s = splnet();

	switch (cmd) {
	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		deinit(ds);

		switch (ifa->ifa_addr->sa_family) {
		case AF_INET:
			arp_ifinit(&ds->ds_ac, ifa);
			break;
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    ds->ds_flags & DSF_RUNNING) {
			ds->ds_vaddr->pclow = 0;
			DELAY(5000);
			ds->ds_vaddr->pclow = PCSR0_RSET;
			ds->ds_flags &= ~DSF_RUNNING;
			ds->ds_if.if_flags &= ~IFF_OACTIVE;
		} else if (ifp->if_flags & IFF_UP &&
		    (ds->ds_flags & DSF_RUNNING) == 0)
			deinit(ds);
		break;

	default:
		error = ether_ioctl(ifp, &ds->ds_ac, cmd, data);
	}

	splx(s);
	return (error);
}

/*
 * set ethernet address for unit
 */
void
de_setaddr(physaddr, ds)
	u_char	*physaddr;
	struct de_softc *ds;
{
	volatile struct dedevice *addr= ds->ds_vaddr;

	if (! (ds->ds_flags & DSF_RUNNING))
		return;

	bcopy((caddr_t) physaddr, (caddr_t) &ds->ds_pcbb.pcbb2, 6);
	ds->ds_pcbb.pcbb0 = FC_WTPHYAD;
	addr->pclow = PCSR0_INTE|CMD_GETCMD;
	if (dewait(ds, "address change") == 0) {
		ds->ds_flags |= DSF_SETADDR;
		bcopy((caddr_t) physaddr, ds->ds_ac.ac_enaddr, 6);
	}
}

/*
 * Await completion of the named function
 * and check for errors.
 */
int
dewait(ds, fn)
	register struct de_softc *ds;
	char *fn;
{
	volatile struct dedevice *addr = ds->ds_vaddr;
	register int csr0;

	while ((addr->pcsr0 & PCSR0_INTR) == 0)
		;
	csr0 = addr->pcsr0;
	addr->pchigh = csr0 >> 8;
	if (csr0 & PCSR0_PCEI) {
		printf("de%d: %s failed, csr0=%b ", ds->ds_dev.dv_unit, fn,
		    csr0, PCSR0_BITS);
		printf("csr1=%b\n", addr->pcsr1, PCSR1_BITS);
	}
	return (csr0 & PCSR0_PCEI);
}

int
dematch(parent, cf, aux)
	struct	device *parent;
	void	*cf, *aux;
{
	struct	uba_attach_args *ua = aux;
	volatile struct dedevice *addr = (struct dedevice *)ua->ua_addr;
	int	i;

	/*
	 * Make sure self-test is finished before we screw with the board.
	 * Self-test on a DELUA can take 15 seconds (argh).
	 */
	for (i = 0;
	     i < 160 &&
	     (addr->pcsr0 & PCSR0_FATI) == 0 &&
	     (addr->pcsr1 & PCSR1_STMASK) == STAT_RESET;
	     ++i)
		DELAY(50000);
	if (((addr->pcsr0 & PCSR0_FATI) != 0) ||
	    (((addr->pcsr1 & PCSR1_STMASK) != STAT_READY) &&
		((addr->pcsr1 & PCSR1_STMASK) != STAT_RUN)))
		return(0);

	addr->pcsr0 = 0;
	DELAY(5000);
	addr->pcsr0 = PCSR0_RSET;
	while ((addr->pcsr0 & PCSR0_INTR) == 0)
		;
	/* make board interrupt by executing a GETPCBB command */
	addr->pcsr0 = PCSR0_INTE;
	addr->pcsr2 = 0;
	addr->pcsr3 = 0;
	addr->pcsr0 = PCSR0_INTE|CMD_GETPCBB;
	DELAY(50000);

	ua->ua_ivec = deintr;
	ua->ua_reset = dereset; /* Wish to be called after ubareset */

	return 1;
}