code:
/*	$OpenBSD: mscp.c,v 1.13 2014/12/23 21:20:40 miod Exp $	*/
/*	$NetBSD: mscp.c,v 1.16 2001/11/13 07:38:28 lukem Exp $	*/

/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)mscp.c	7.5 (Berkeley) 12/16/90
 */

/*
 * MSCP generic driver routines
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/systm.h>

#include <machine/bus.h>

#include <arch/vax/mscp/mscp.h>
#include <arch/vax/mscp/mscpreg.h>
#include <arch/vax/mscp/mscpvar.h>

#define PCMD	PSWP		/* priority for command packet waits */

/*
 * Get a command packet.  Second argument is true iff we are
 * to wait if necessary.  Return NULL if none are available and
 * we cannot wait.
 */
struct mscp *
mscp_getcp(mi, canwait)
	struct mscp_softc *mi;
	int canwait;
{
#define mri	(&mi->mi_cmd)
	struct mscp *mp;
	int i;
	int s = splbio();

again:
	/*
	 * Ensure that we have some command credits, and
	 * that the next command packet is free.
	 */
	if (mi->mi_credits <= MSCP_MINCREDITS) {
		if (!canwait) {
			splx(s);
			return (NULL);
		}
		mi->mi_wantcredits = 1;
		(void) tsleep(&mi->mi_wantcredits, PCMD, "mscpwcrd", 0);
		goto again;
	}
	i = mri->mri_next;
	if (mri->mri_desc[i] & MSCP_OWN) {
		if (!canwait) {
			splx(s);
			return (NULL);
		}
		mi->mi_wantcmd = 1;
		(void) tsleep(&mi->mi_wantcmd, PCMD, "mscpwcmd", 0);
		goto again;
	}
	mi->mi_credits--;
	mri->mri_desc[i] &= ~MSCP_INT;
	mri->mri_next = (mri->mri_next + 1) % mri->mri_size;
	splx(s);
	mp = &mri->mri_ring[i];

	/*
	 * Initialise some often-zero fields.
	 * ARE THE LAST TWO NECESSARY IN GENERAL?  IT SURE WOULD BE
	 * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.
	 */
	mp->mscp_msglen = MSCP_MSGLEN;
	mp->mscp_flags = 0;
	mp->mscp_modifier = 0;
	mp->mscp_seq.seq_bytecount = 0;
	mp->mscp_seq.seq_buffer = 0;
	mp->mscp_seq.seq_mapbase = 0;
/*???*/ mp->mscp_sccc.sccc_errlgfl = 0;
/*???*/ mp->mscp_sccc.sccc_copyspd = 0;
	return (mp);
#undef	mri
}

#ifdef AVOID_EMULEX_BUG
int	mscp_aeb_xor = 0x8000bb80;
#endif

/*
 * Handle a response ring transition.
 */
void
mscp_dorsp(mi)
	struct mscp_softc *mi;
{
	struct device *drive;
	struct mscp_device *me = mi->mi_me;
	struct mscp_ctlr *mc = mi->mi_mc;
	struct buf *bp;
	struct mscp *mp;
	struct mscp_xi *mxi;
	int nextrsp;
	int st, error;
	extern int cold;
	extern struct mscp slavereply;

	nextrsp = mi->mi_rsp.mri_next;
loop:
	if (mi->mi_rsp.mri_desc[nextrsp] & MSCP_OWN) {
		/*
		 * No more responses.  Remember the next expected
		 * response index.  Check to see if we have some
		 * credits back, and wake up sleepers if so.
		 */
		mi->mi_rsp.mri_next = nextrsp;
		if (mi->mi_wantcredits && mi->mi_credits > MSCP_MINCREDITS) {
			mi->mi_wantcredits = 0;
			wakeup((caddr_t) &mi->mi_wantcredits);
		}
		return;
	}

	mp = &mi->mi_rsp.mri_ring[nextrsp];
	mi->mi_credits += MSCP_CREDITS(mp->mscp_msgtc);
	/*
	 * Controllers are allowed to interrupt as any drive, so we
	 * must check the command before checking for a drive.
	 */
	if (mp->mscp_opcode == (M_OP_SETCTLRC | M_OP_END)) {
		if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS) {
			mi->mi_flags |= MSC_READY;
		} else {
			printf("%s: SETCTLRC failed: %d ",
			    mi->mi_dev.dv_xname, mp->mscp_status);
			mscp_printevent(mp);
		}
		goto done;
	}

	/*
	 * Found a response.  Update credit information.  If there is
	 * nothing else to do, jump to `done' to get the next response.
	 */
	if (mp->mscp_unit >= mi->mi_driveno) { /* Must expand drive table */
		size_t tmpno = ((mp->mscp_unit + 32) & 0xffe0) * sizeof(void *);
		struct device **tmp = malloc(tmpno, M_DEVBUF, M_NOWAIT|M_ZERO);
		if (tmp == NULL)
			panic("mscp_dorsp");
		if (mi->mi_driveno) {
			bcopy(mi->mi_dp, tmp, mi->mi_driveno);
			free(mi->mi_dp, M_DEVBUF, mi->mi_driveno);
		}
		mi->mi_driveno = tmpno;
		mi->mi_dp = tmp;
	}

	drive = mi->mi_dp[mp->mscp_unit];

	switch (MSCP_MSGTYPE(mp->mscp_msgtc)) {

	case MSCPT_SEQ:
		break;

	case MSCPT_DATAGRAM:
		(*me->me_dgram)(drive, mp, mi);
		goto done;

	case MSCPT_CREDITS:
		goto done;

	case MSCPT_MAINTENANCE:
	default:
		printf("%s: unit %d: unknown message type 0x%x ignored\n",
			mi->mi_dev.dv_xname, mp->mscp_unit,
			MSCP_MSGTYPE(mp->mscp_msgtc));
		goto done;
	}

	/*
	 * Handle individual responses.
	 */
	st = mp->mscp_status & M_ST_MASK;
	error = 0;
	switch (mp->mscp_opcode) {

	case M_OP_END:
		/*
		 * The controller presents a bogus END packet when
		 * a read/write command is given with an illegal
		 * block number.  This is contrary to the MSCP
		 * specification (ENDs are to be given only for
		 * invalid commands), but that is the way of it.
		 */
		if (st == M_ST_INVALCMD && mp->mscp_cmdref != 0) {
			printf("%s: bad lbn (%d)?\n", drive->dv_xname,
				(int)mp->mscp_seq.seq_lbn);
			error = EIO;
			goto rwend;
		}
		goto unknown;

	case M_OP_ONLINE | M_OP_END:
		/*
		 * Finished an ON LINE request.	 Call the driver to
		 * find out whether it succeeded.  If so, mark it on
		 * line.
		 */
		(*me->me_online)(drive, mp);
		break;

	case M_OP_GETUNITST | M_OP_END:
		/*
		 * Got unit status.  If we are autoconfiguring, save
		 * the mscp struct so that mscp_attach know what to do.
		 * If the drive isn't configured, call config_found()
		 * to set it up, otherwise it's just a "normal" unit
		 * status.
		 */
		if (cold)
			bcopy(mp, &slavereply, sizeof(struct mscp));

		if (mp->mscp_status == (M_ST_OFFLINE|M_OFFLINE_UNKNOWN))
			break;

		if (drive == 0) {
			struct	drive_attach_args da;

			da.da_mp = (struct mscp *)mp;
			da.da_typ = mi->mi_type;
			config_found(&mi->mi_dev, (void *)&da, mscp_print);
		} else
			/* Hack to avoid complaints */
			if (!(((mp->mscp_event & M_ST_MASK) == M_ST_AVAILABLE)
			    && cold))
				(*me->me_gotstatus)(drive, mp);
		break;

	case M_OP_AVAILATTN:
		/*
		 * The drive went offline and we did not notice.
		 * Mark it off line now, to force an on line request
		 * next, so we can make sure it is still the same
		 * drive.
		 *
		 * IF THE UDA DRIVER HAS A COMMAND AWAITING UNIBUS
		 * RESOURCES, THAT COMMAND MAY GO OUT BEFORE THE ON
		 * LINE.  IS IT WORTH FIXING??
		 */
#ifdef notyet
		(*md->md_offline)(ui, mp);
#endif
		break;

	case M_OP_POS | M_OP_END:
	case M_OP_WRITM | M_OP_END:
	case M_OP_AVAILABLE | M_OP_END:
		/*
		 * A non-data transfer operation completed.
		 */
		(*me->me_cmddone)(drive, mp);
		break;

	case M_OP_READ | M_OP_END:
	case M_OP_WRITE | M_OP_END:
		/*
		 * A transfer finished.	 Get the buffer, and release its
		 * map registers via ubadone().	 If the command finished
		 * with an off line or available status, the drive went
		 * off line (the idiot controller does not tell us until
		 * it comes back *on* line, or until we try to use it).
		 */
rwend:
#ifdef DIAGNOSTIC
		if (mp->mscp_cmdref >= NCMD) {
			/*
			 * No buffer means there is a bug somewhere!
			 */
			printf("%s: io done, but bad xfer number?\n",
			    drive->dv_xname);
			mscp_hexdump(mp);
			break;
		}
#endif

		if (mp->mscp_cmdref == -1) {
			(*me->me_cmddone)(drive, mp);
			break;
		}
		mxi = &mi->mi_xi[mp->mscp_cmdref];
		if (mxi->mxi_inuse == 0)
			panic("mxi not inuse");
		bp = mxi->mxi_bp;
		/*
		 * Mark any error-due-to-bad-LBN (via `goto rwend').
		 * WHAT STATUS WILL THESE HAVE?	 IT SURE WOULD BE NICE
		 * IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.
		 */
		if (error) {
			bp->b_flags |= B_ERROR;
			bp->b_error = error;
		}
		if (st == M_ST_OFFLINE || st == M_ST_AVAILABLE) {
#ifdef notyet
			(*md->md_offline)(ui, mp);
#endif
		}

		/*
		 * If the transfer has something to do with bad
		 * block forwarding, let the driver handle the
		 * rest.
		 */
		if ((bp->b_flags & B_BAD) != 0 && me->me_bb != NULL) {
			(*me->me_bb)(drive, mp, bp);
			goto out;
		}

		/*
		 * If the transfer failed, give the driver a crack
		 * at fixing things up.
		 */
		if (st != M_ST_SUCCESS) {
			switch ((*me->me_ioerr)(drive, mp, bp)) {

			case MSCP_DONE:		/* fixed */
				break;

			case MSCP_RESTARTED:	/* still working on it */
				goto out;

			case MSCP_FAILED:	/* no luck */
				/* XXX must move to ra.c */
				mscp_printevent(mp);
				break;
			}
		}

		/*
		 * Set the residual count and mark the transfer as
		 * done.  If the I/O wait queue is now empty, release
		 * the shared BDP, if any.
		 */
		bp->b_resid = bp->b_bcount - mp->mscp_seq.seq_bytecount;
		bus_dmamap_unload(mi->mi_dmat, mxi->mxi_dmam);

		(*mc->mc_ctlrdone)(mi->mi_dev.dv_parent);
		(*me->me_iodone)(drive, bp);
out:
		mxi->mxi_inuse = 0;
		mi->mi_mxiuse |= (1 << mp->mscp_cmdref);
		break;

	case M_OP_REPLACE | M_OP_END:
		/*
		 * A replace operation finished.  Just let the driver
		 * handle it (if it does replaces).
		 */
		if (me->me_replace == NULL)
			printf("%s: bogus REPLACE end\n", drive->dv_xname);
		else
			(*me->me_replace)(drive, mp);
		break;

	default:
		/*
		 * If it is not one of the above, we cannot handle it.
		 * (And we should not have received it, for that matter.)
		 */
unknown:
		printf("%s: unknown opcode 0x%x status 0x%x ignored\n",
			drive->dv_xname, mp->mscp_opcode, mp->mscp_status);
#ifdef DIAGNOSTIC
		mscp_hexdump(mp);
#endif
		break;
	}

	/*
	 * If the drive needs to be put back in the controller queue,
	 * do that now.	 (`bp' below ought to be `dp', but they are all
	 * struct buf *.)  Note that b_active was cleared in the driver;
	 * we presume that there is something to be done, hence reassert it.
	 */
#ifdef notyet /* XXX */
	if (ui->ui_flags & UNIT_REQUEUE) {
		...
	}
#endif
done:
	/*
	 * Give back the response packet, and take a look at the next.
	 */
	mp->mscp_msglen = MSCP_MSGLEN;
	mi->mi_rsp.mri_desc[nextrsp] |= MSCP_OWN;
	nextrsp = (nextrsp + 1) % mi->mi_rsp.mri_size;
	goto loop;
}

/*
 * Requeue outstanding transfers, e.g., after bus reset.
 * Also requeue any drives that have on line or unit status
 * info pending.
 */
void
mscp_requeue(mi)
	struct mscp_softc *mi;
{
	panic("mscp_requeue");
}


code:
/*	$OpenBSD: mscp.h,v 1.5 2003/06/02 23:27:57 millert Exp $	*/
/*	$NetBSD: mscp.h,v 1.6 1999/05/29 19:11:16 ragge Exp $	*/

/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)mscp.h	7.5 (Berkeley) 6/28/90
 */

/*
 * Definitions for the Mass Storage Control Protocol
 * I WISH I KNEW WHAT MORE OF THESE WERE.  IT SURE WOULD BE NICE
 * IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.
 */

/*
 * Control message opcodes
 */
#define M_OP_ABORT	0x01	/* Abort command */
#define M_OP_GETCMDST	0x02	/* Get command status command */
#define M_OP_GETUNITST	0x03	/* Get unit status command */
#define M_OP_SETCTLRC	0x04	/* Set controller characteristics command */
#define M_OP_SEREX	0x07	/* Serious exception end message */
#define M_OP_AVAILABLE	0x08	/* Available command */
#define M_OP_ONLINE	0x09	/* Online command */
#define M_OP_SETUNITC	0x0a	/* Set unit characteristics command */
#define M_OP_DTACCPATH	0x0b	/* Determine access paths command */
#define M_OP_ACCESS	0x10	/* Access command */
#define M_OP_COMPCD	0x11	/* Compare controller data command */
#define M_OP_ERASE	0x12	/* Erase command */
#define M_OP_FLUSH	0x13	/* Flush command */
#define M_OP_REPLACE	0x14	/* Replace command */
#define M_OP_COMPHD	0x20	/* Compare host data command */
#define M_OP_READ	0x21	/* Read command */
#define M_OP_WRITE	0x22	/* Write command */
#define M_OP_WRITM	0x24	/* Write mark command */
#define M_OP_POS	0x25	/* Positioning command */
#define M_OP_AVAILATTN	0x40	/* Available attention message */
#define M_OP_DUPUNIT	0x41	/* Duplicate unit number attention message */
#define M_OP_ACCPATH	0x42	/* Access path attention message */
#define M_OP_END	0x80	/* End message flag */

/*
 * Generic command modifiers
 */
#define M_MD_EXPRS	0x8000	/* Express request */
#define M_MD_COMP	0x4000	/* Compare */
#define M_MD_CLSEX	0x2000	/* Clear serious exception */
#define M_MD_ERROR	0x1000	/* Force error */
#define M_MD_SCCHH	0x0800	/* Suppress caching (high speed) */
#define M_MD_SCCHL	0x0400	/* Suppress caching (low speed) */
#define M_MD_SECOR	0x0200	/* Suppress error correction */
#define M_MD_SEREC	0x0100	/* Suppress error recovery */
#define M_MD_SSHDW	0x0080	/* Suppress shadowing */
#define M_MD_WBKNV	0x0040	/* Write back (non-volatile) */
#define M_MD_WBKVL	0x0020	/* Write back (volatile) */
#define M_MD_WRSEQ	0x0010	/* Write shadow set one unit at a time */

/*
 * tape command modifiers
 */
#define M_MD_IMMEDIATE	0x0040	/* Immediate completion */
#define M_MD_UNLOAD	0x0010	/* Unload tape */
#define M_MD_REVERSE	0x0008	/* Reverse action */
#define M_MD_OBJCOUNT	0x0004	/* Object count */
#define M_MD_REWIND	0x0002	/* Rewind */

/*
 * AVAILABLE command modifiers
 */
#define M_AVM_ALLCD	0x0002	/* All class drivers */
#define M_AVM_SPINDOWN	0x0001	/* Spin down */

/*
 * FLUSH command modifiers
 */
#define M_FLM_FLUSHENU	0x0001	/* Flush entire unit */
#define M_FLM_VOLATILE	0x0002	/* Volatile only */

/*
 * GET UNIT STATUS command modifiers
 */
#define M_GUM_NEXTUNIT	0x0001	/* Next unit */

/*
 * ONLINE command modifiers
 */
#define M_OLM_RIP	0x0001	/* Allow self destruction */
#define M_OLM_IGNMF	0x0002	/* Ignore media format error */

/*
 * ONLINE and SET UNIT CHARACTERISTICS command modifiers
 */
#define M_OSM_ALTERHI	0x0020	/* Alter host identifier */
#define M_OSM_SHADOWSP	0x0010	/* Shadow unit specified */
#define M_OSM_CLEARWBL	0x0008	/* Clear write-back data lost */
#define M_OSM_SETWRPROT 0x0004	/* Set write protect */

/*
 * REPLACE command modifiers
 */
#define M_RPM_PRIMARY	0x0001	/* Primary replacement block */

/*
 * End message flags
 */
#define M_EF_BBLKR	0x80	/* Bad block reported */
#define M_EF_BBLKU	0x40	/* Bad block unreported */
#define M_EF_ERLOG	0x20	/* Error log generated */
#define M_EF_SEREX	0x10	/* Serious exception */
#define M_EF_EOT	0x08	/* at end-of-tape */
#define M_EF_POSLOST	0x04	/* position lost */

/*
 * Controller flags
 */
#define M_CF_ATTN	0x80	/* Enable attention messages */
#define M_CF_MISC	0x40	/* Enable miscellaneous error log messages */
#define M_CF_OTHER	0x20	/* Enable other host's error log messages */
#define M_CF_THIS	0x10	/* Enable this host's error log messages */
#define M_CF_MLTHS	0x04	/* Multi-host */
#define M_CF_SHADW	0x02	/* Shadowing */
#define M_CF_576	0x01	/* 576 byte sectors */

/*
 * Unit flags
 */
#define M_UF_REPLC	0x8000	/* Controller initiated bad block replacement */
#define M_UF_INACT	0x4000	/* Inactive shadow set unit */
#define M_UF_WRTPH	0x2000	/* Write protect (hardware) */
#define M_UF_WRTPS	0x1000	/* Write protect (software or volume) */
#define M_UF_SCCHH	0x8000	/* Suppress caching (high speed) */
#define M_UF_SCCHL	0x4000	/* Suppress caching (low speed) */
#define M_UF_RMVBL	0x0080	/* Removable media */
#define M_UF_WBKNV	0x0040	/* Write back (non-volatile) */
#define M_UF_576	0x0004	/* 576 byte sectors */
#define M_UF_CMPWR	0x0002	/* Compare writes */
#define M_UF_CMPRD	0x0001	/* Compare reads */

/*
 * Error Log message format codes
 */
#define M_FM_CTLRERR	0x00	/* Controller error */
#define M_FM_BUSADDR	0x01	/* Host memory access error */
#define M_FM_DISKTRN	0x02	/* Disk transfer error */
#define M_FM_SDI	0x03	/* SDI error */
#define M_FM_SMLDSK	0x04	/* Small disk error */
#define M_FM_TAPETRN	0x05	/* Tape transfer error */
#define M_FM_STIERR	0x06	/* STI communication or command failure */
#define M_FM_STIDEL	0x07	/* STI drive error log */
#define M_FM_STIFEL	0x08	/* STI formatter error log */

/*
 * Error Log message flags
 */
#define M_LF_SUCC	0x80	/* Operation successful */
#define M_LF_CONT	0x40	/* Operation continuing */
#define M_LF_SQNRS	0x01	/* Sequence number reset */

/*
 * Status codes
 */
#define M_ST_MASK	0x1f	/* Status code mask */
#define M_ST_SUCCESS	0x00	/* Success */
#define M_ST_INVALCMD	0x01	/* Invalid command */
#define M_ST_ABORTED	0x02	/* Command aborted */
#define M_ST_OFFLINE	0x03	/* Unit offline */
#define M_ST_AVAILABLE	0x04	/* Unit available */
#define M_ST_MFMTERR	0x05	/* Media format error */
#define M_ST_WRPROT	0x06	/* Write protected */
#define M_ST_COMPERR	0x07	/* Compare error */
#define M_ST_DATAERR	0x08	/* Data error */
#define M_ST_HOSTBUFERR 0x09	/* Host buffer access error */
#define M_ST_CTLRERR	0x0a	/* Controller error */
#define M_ST_DRIVEERR	0x0b	/* Drive error */
#define M_ST_FORMATTERR 0x0c	/* Formatter error */
#define M_ST_BOT	0x0d	/* Beginning-of-tape */
#define M_ST_TAPEMARK	0x0e	/* Tape mark encountered */
#define M_ST_RDTRUNC	0x10	/* Record data truncated */
#define M_ST_DIAG	0x1f	/* Message from an internal diagnostic */

/*
 * Subcodes of M_ST_OFFLINE
 */
#define M_OFFLINE_UNKNOWN	(0 << 5) /* unknown or on other ctlr */
#define M_OFFLINE_UNMOUNTED	(1 << 5) /* unmounted or RUN/STOP at STOP */
#define M_OFFLINE_INOPERATIVE	(2 << 5) /* inoperative? */
#define M_OFFLINE_DUPLICATE	(4 << 5) /* duplicate unit number */
#define M_OFFLINE_INDIAGNOSTIC	(8 << 5) /* disabled by FS or diagnostic */

/*
 * An MSCP packet begins with a header giving the length of
 * the entire packet (including the header itself)(?), two bytes
 * of device specific data, and the a whole bunch of variants
 * depending on message type.
 *
 * N.B.:  In most cases we distinguish between a `command' and
 * an `end' variant as well.  The command variant is that which
 * is given to the controller; the `end' variant is its response.
 */

/*
 * Generic sequential message variant (command and response).
 */
struct mscpv_seq {
	long	seq_bytecount;		/* byte count */
#define seq_rbn		seq_bytecount	/* aka RBN (replace) */
#define seq_outref	seq_bytecount	/* aka outref (abort/get cmd status) */
	long	seq_buffer;		/* buffer descriptor */
	long	seq_mapbase;		/* page map (first PTE) phys address */
	long	seq_xxx1;	/* ? */ /* unused */
	long	seq_lbn;		/* logical block number */
	long	seq_xxx2;	/* ? */ /* unused */
	long	*seq_addr;		/* pointer to cmd descriptor */
	long	seq_software[4];	/* reserved to software; unused */
};

/*
 * Set Controller Characteristics command variant
 */
struct mscpv_sccc {
	u_short sccc_version;		/* MSCP version number */
	u_short sccc_ctlrflags;		/* controller flags */
	u_short sccc_hosttimo;		/* host timeout */
	u_short sccc_usefrac;		/* use fraction */
	long	sccc_time;		/* time and date */
	long	sccc_time1;		/* it's a quad field */
	long	sccc_errlgfl;	/* ? */
	short	sccc_xxx2;	/* ? */
	short	sccc_copyspd;	/* ? */
};

/*
 * Set Controller Characteristics end variant
 */
struct mscpv_scce {
	u_short scce_version;		/* MSCP version number */
	u_short scce_ctlrflags;		/* controller flags */
	u_short scce_ctlrtimo;		/* controller timeout */
	u_short scce_ctlrcmdl;		/* ??? */
	quad_t	scce_ctlrid;		/* controller ID */
	long	scce_xxx[3];	/* ? */
	long	scce_volser;		/* volume serial number */
};

/*
 * On Line command variant
 */
struct mscpv_onlc {
	long	onlc_xxx1[4];	/* ? */
	long	onlc_errlgfl;		/* error log flag? */
	short	onlc_xxx2;	/* ? */
	short	onlc_copyspd;		/* copy speed? */
};

/*
 * On Line end variant
 */
struct mscpv_onle {
	long	onle_xxx1[3];	/* ? */
/*???*/ short	onle_xxx2;	/* ? */
	u_char	onle_drivetype;		/* drive type index (same in guse) */
	char	onle_xxx3;	/* ? */
	long	onle_mediaid;		/* media type id (same in guse) */
	long	onle_xxx4;	/* ? */
	long	onle_unitsize;		/* unit size in sectors */
	long	onle_volser;		/* volume serial number */
};

/*
 * Get Unit Status end variant (and Avail Attn?)
 */
struct mscpv_guse {
	u_short guse_multunit;		/* multi-unit code */
	u_short guse_unitflags;		/* unit flags */
	long	guse_hostid;		/* host id */
	long	guse_unitid0;	/*???*/
	short	guse_unitid1;	/*???*/
	u_char	guse_drivetype;		/* drive type index */
	u_char	guse_unitid2;	/*???*/
	long	guse_mediaid;		/* media type id (encoded) */
	short	guse_shadowunit;	/* shadow unit */
	short	guse_shadowstat;	/* shadow status */
	u_short guse_nspt;		/* sectors per track */
	u_short guse_group;		/* track group size */
	u_short guse_ngpc;		/* groups per cylinder */
	u_short guse_xxx;		/* reserved */
	u_short guse_rctsize;		/* RCT size (sectors) */
	u_char	guse_nrpt;		/* RBNs per track */
	u_char	guse_nrct;		/* number of RCTs */
};

/*
 * Macros to break up and build media IDs.  An ID encodes the port
 * type in the top 10 bits, and the drive type in the remaining 22.
 * The 10 bits, and 15 of the 22, are in groups of 5, with the value
 * 0 representing space and values 1..26 representing A..Z.  The low
 * 7 bits represent a number in 0..127.	 Hence an RA81 on a UDA50
 * is <D><U><R><A>< >81, or 0x25641051.	 This encoding scheme is known
 * in part in uda.c.
 *
 * The casts below are just to make pcc generate better code.
 */
#define MSCP_MEDIA_PORT(id)	(((long)(id) >> 22) & 0x3ff)	/* port */
#define MSCP_MEDIA_DRIVE(id)	((long)(id) & 0x003fffff)	/* drive */
#define MSCP_MID_ECH(n, id)	(((int)(id) >> ((n) * 5 + 7)) & 0x1f)
#define MSCP_MID_CHAR(n, id) \
	(MSCP_MID_ECH(n, id) ? MSCP_MID_ECH(n, id) + '@' : ' ')
#define MSCP_MID_NUM(id)	((id) & 0x7f)
/* for, e.g., RA81 */
#define MSCP_MKDRIVE2(a, b, n) \
	(((a) - '@') << 17 | ((b) - '@') << 12 | (n))
/* for, e.g., RRD50 */
#define MSCP_MKDRIVE3(a, b, c, n) \
	(((a) - '@') << 17 | ((b) - '@') << 12 | ((c) - '@') << 7 | (n))

/*
 * Error datagram variant.
 */
struct mscpv_erd {
	quad_t	erd_ctlrid;		/* controller ID */
	u_char	erd_ctlrsoftware;	/* controller software version */
	u_char	erd_ctlrhardware;	/* controller hardware version */
	u_short erd_multiunit;		/* multi-unit code (?) */
	union {
		u_long	un_busaddr;	/* bus address, if mem access err */
		quad_t	un_unitid;	/* unit id, otherwise */
	} erd_un1;
#define erd_busaddr	erd_un1.un_busaddr
#define erd_unitid	erd_un1.un_unitid
	u_char	erd_unitsoftware;	/* unit software version */
	u_char	erd_unithardware;	/* unit hardware version */
	union {
		u_char	un_b[2];	/* level, retry (if disk xfer err) */
		u_short un_s;		/* cylinder (if small disk error) */
	} erd_un2;
#define erd_level	erd_un2.un_b[0]
#define erd_retry	erd_un2.un_b[1]
#define erd_sdecyl	erd_un2.un_s
	long	erd_volser;		/* volume serial number */
	u_long	erd_hdr;		/* `header' (block number) */
	u_char	erd_sdistat[12];	/* SDI status information (?) */
};

/*
 * I am making brash assumptions about the first four bytes of all
 * MSCP packets.  These appear to be true for both UDA50s and TMSCP
 * devices (TU81, TA81, TK50).	DEC claim that these four bytes are
 * not part of MSCP itself, yet at least the length is necessary
 * for, e.g., error checking.
 */
struct mscp {
	u_short mscp_msglen;		/* length in bytes */
	u_char	mscp_msgtc;		/* type (high 4 bits) and credits */
	u_char	mscp_vcid;		/* virtual circuit ID */
	long	mscp_cmdref;		/* command reference number */
	u_short mscp_unit;		/* unit number */
	u_short mscp_seqnum;		/* sequence number */
	u_char	mscp_opcode;		/* opcode */
#define mscp_format	mscp_opcode	/* aka format (datagrams) */
	u_char	mscp_flags;		/* flags */
	u_short mscp_modifier;		/* modifier (commands) */
#define mscp_status	mscp_modifier	/* aka status (ends) */
#define mscp_event	mscp_modifier	/* aka event (datagrams) */
	union {
		struct	mscpv_seq un_seq;	/* generic sequential msg */
		struct	mscpv_sccc un_sccc;	/* SCC command */
		struct	mscpv_scce un_scce;	/* SCC end */
		struct	mscpv_onlc un_onlc;	/* on line command */
		struct	mscpv_onle un_onle;	/* on line end */
		struct	mscpv_guse un_guse;	/* get unit status */
		struct	mscpv_erd un_erd;	/* error datagram */
	} mscp_un;
/*???*/ long	mscp_xxx;		/* pad to 64 bytes */
};

/*
 * Define message length according to the DEC specifications by dropping
 * the four byte header.
 */
#define MSCP_MSGLEN	(sizeof (struct mscp) - 4)

/*
 * Shorthand
 */

/*
 * Generic packet
 */
#define mscp_seq	mscp_un.un_seq

/*
 * Set Controller Characteristics packet
 */
#define mscp_sccc	mscp_un.un_sccc

/*
 * Set Controller Characteristics end packet
 */
#define mscp_scce	mscp_un.un_scce

/*
 * Online / Set Unit Characteristics command packet
 */
#define mscp_onlc	mscp_un.un_onlc

/*
 * Online end packet
 */
#define mscp_onle	mscp_un.un_onle

/*
 * Get Unit Status end packet
 */
#define mscp_guse	mscp_un.un_guse

/*
 * MSCP Error Log packet
 */
#define mscp_erd	mscp_un.un_erd

/*
 * MSCP seq_addr field actually belongs to overall packet.
 */
#define mscp_addr	mscp_seq.seq_addr

/*
 * Macros to break up mscp_msgtc, and types.
 */
#define MSCP_MSGTYPE(m) ((m) & 0xf0)
#define MSCP_CREDITS(m) ((m) & 0x0f)

#define MSCPT_SEQ		0x00	/* sequential message */
#define MSCPT_DATAGRAM		0x10	/* error datagram */
#define MSCPT_CREDITS		0x20	/* credit notification */
#define MSCPT_MAINTENANCE	0xf0	/* who knows */

/*
 * Here begin more perhaps brash assumptions about MSCP devices...
 */

/*
 * MSCP controllers have `command rings' and `response rings'.	A
 * command ring is a pool of MSCP packets that the host uses to give
 * commands to the controller; a response ring is a pool of MSCP
 * packets that the controller uses to give back responses.  Entries
 * in the command and response rings are `owned' by either the host
 * or the controller; only the owner is allowed to alter any of the
 * fields in the MSCP packet.  Thus, free command packets are owned
 * by the host, and free response packets by the controller.  When
 * the host gives a packet to the controller, it tells the controller
 * by touching a device register; when the controller gives a response
 * to the host, it generates an interrupt if enabled, and sets
 * a device register as well.
 *
 * The pool is `described' by a set of pointers to the packets, along
 * with the two flags below.
 */
#define MSCP_OWN	0x80000000	/* controller owns this packet */
#define MSCP_INT	0x40000000	/* controller should interrupt */

license: unknown


code:
/*	$OpenBSD: mscp_disk.c,v 1.41 2014/05/08 19:06:07 miod Exp $	*/
/*	$NetBSD: mscp_disk.c,v 1.30 2001/11/13 07:38:28 lukem Exp $	*/
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)uda.c	7.32 (Berkeley) 2/13/91
 */

/*
 * RA disk device driver
 * RX MSCP floppy disk device driver
 */

/*
 * TODO
 *	write bad block forwarding code
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/dkio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/reboot.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/conf.h>

#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>

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

#include <arch/vax/mscp/mscp.h>
#include <arch/vax/mscp/mscpreg.h>
#include <arch/vax/mscp/mscpvar.h>

#include "ra.h"

struct cfdriver ra_cd = {
	NULL, "ra", DV_DISK
};

struct cfdriver rx_cd = {
	NULL, "rx", DV_DISK
};

#define RAMAJOR 9	/* RA major device number XXX */

/*
 * Drive status, per drive
 */
struct ra_softc {
	struct device ra_dev;		/* autoconf struct */
	struct disk ra_disk;
	struct mscpv_guse ra_guse;	/* status information */
	u_long	ra_unitsize;		/* unit size in sectors */
	int	ra_state;		/* open/closed state */
	int	ra_hwunit;		/* Hardware unit number */
};

#define rx_softc ra_softc

void	rxattach(struct device *, struct device *, void *);
int	rx_putonline(struct rx_softc *);

#if NRA

int	ramatch(struct device *, struct cfdata *, void *);
int	raread(dev_t, struct uio *);
int	rawrite(dev_t, struct uio *);
int	ra_putonline(struct ra_softc *);
int	ragetdisklabel(dev_t, struct ra_softc *, struct disklabel *, int);
bdev_decl(ra);

const struct cfattach ra_ca = {
	sizeof(struct ra_softc), (cfmatch_t)ramatch, rxattach
};

/*
 * More driver definitions, for generic MSCP code.
 */

int
ramatch(struct device *parent, struct cfdata *cf, void *aux)
{
	struct drive_attach_args *da = aux;
	struct mscp *mp = da->da_mp;

	if ((da->da_typ & MSCPBUS_DISK) == 0)
		return 0;
	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != mp->mscp_unit)
		return 0;
	/*
	 * Check if this disk is a floppy; then don't configure it.
	 * Seems to be a safe way to test it per Chris Torek.
	 */
	if (MSCP_MID_ECH(1, mp->mscp_guse.guse_mediaid) == 'X' - '@')
		return 0;
	return 1;
}

/*
 * Read the label from the drive.
 */
int
ragetdisklabel(dev_t dev, struct ra_softc *ra, struct disklabel *lp,
    int spoofonly)
{
	int n, p = 0;

	bzero(lp, sizeof(struct disklabel));
	lp->d_secsize = DEV_BSIZE;
	lp->d_nsectors = ra->ra_guse.guse_nspt;
	lp->d_ntracks = ra->ra_guse.guse_ngpc * ra->ra_guse.guse_group;
	lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
	DL_SETDSIZE(lp, ra->ra_unitsize);	/* XXX might be zero */

	if (lp->d_secpercyl) {
		lp->d_ncylinders = ra->ra_unitsize / lp->d_secpercyl;
		lp->d_type = DTYPE_MSCP;
	} else {
		lp->d_type = DTYPE_FLOPPY;
	}

	lp->d_bbsize = BBSIZE;
	lp->d_sbsize = SBSIZE;

	/* Create the disk name for disklabel. Phew... */
	lp->d_typename[p++] = MSCP_MID_CHAR(2, ra->ra_guse.guse_mediaid);
	lp->d_typename[p++] = MSCP_MID_CHAR(1, ra->ra_guse.guse_mediaid);
	if (MSCP_MID_ECH(0, ra->ra_guse.guse_mediaid))
		lp->d_typename[p++] =
		    MSCP_MID_CHAR(0, ra->ra_guse.guse_mediaid);
	n = MSCP_MID_NUM(ra->ra_guse.guse_mediaid);
	if (n > 99) {
		lp->d_typename[p++] = '1';
		n -= 100;
	}
	if (n > 9) {
		lp->d_typename[p++] = (n / 10) + '0';
		n %= 10;
	}
	lp->d_typename[p++] = n + '0';
	lp->d_typename[p] = 0;

	lp->d_version = 1;
	lp->d_magic = lp->d_magic2 = DISKMAGIC;
	lp->d_checksum = dkcksum(lp);

	return readdisklabel(DISKLABELDEV(dev), rastrategy, lp, spoofonly);
}

/*
 * (Try to) put the drive online. This is done the first time the
 * drive is opened, or if it has fallen offline.
 */
int
ra_putonline(struct ra_softc *ra)
{
	struct disklabel *dl;
	int rc;

	ra->ra_state = DK_RDLABEL;
	if (rx_putonline(ra) != MSCP_DONE)
		return MSCP_FAILED;

	dl = ra->ra_disk.dk_label;
	rc = ragetdisklabel(MAKEDISKDEV(RAMAJOR, ra->ra_dev.dv_unit, RAW_PART),
	    ra, dl, 0);
	if (rc != EIO)
		ra->ra_state = DK_OPEN;

	printf("%s: %luMB, %u bytes/sector, %lu sectors\n",
	    ra->ra_dev.dv_xname,
	    ra->ra_unitsize / (1048576 / dl->d_secsize),
	    dl->d_secsize, ra->ra_unitsize);

	return MSCP_DONE;
}

/*
 * Open a drive.
 */
/*ARGSUSED*/
int
raopen(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct ra_softc *ra;
	int part, unit, mask;

	/*
	 * Make sure this is a reasonable open request.
	 */
	unit = DISKUNIT(dev);
	if (unit >= ra_cd.cd_ndevs)
		return ENXIO;
	ra = ra_cd.cd_devs[unit];
	if (ra == 0)
		return ENXIO;

	/*
	 * If this is the first open; we must first try to put
	 * the disk online (and read the label).
	 */
	if (ra->ra_state == DK_CLOSED)
		if (ra_putonline(ra) == MSCP_FAILED)
			return ENXIO;

	part = DISKPART(dev);
	if (part >= ra->ra_disk.dk_label->d_npartitions)
		return ENXIO;

	/*
	 * Wait for the state to settle
	 */
#if notyet
	while (ra->ra_state != DK_OPEN)
		if ((error = tsleep((caddr_t)ra, (PZERO + 1) | PCATCH,
		    "devopen", 0))) {
			splx(s);
			return (error);
		}
#endif

	mask = 1 << part;

	switch (fmt) {
	case S_IFCHR:
		ra->ra_disk.dk_copenmask |= mask;
		break;
	case S_IFBLK:
		ra->ra_disk.dk_bopenmask |= mask;
		break;
	}
	ra->ra_disk.dk_openmask |= mask;
	return 0;
}

/* ARGSUSED */
int
raclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	int unit = DISKUNIT(dev);
	struct ra_softc *ra = ra_cd.cd_devs[unit];
	int mask = (1 << DISKPART(dev));

	switch (fmt) {
	case S_IFCHR:
		ra->ra_disk.dk_copenmask &= ~mask;
		break;
	case S_IFBLK:
		ra->ra_disk.dk_bopenmask &= ~mask;
		break;
	}
	ra->ra_disk.dk_openmask =
	    ra->ra_disk.dk_copenmask | ra->ra_disk.dk_bopenmask;

	/*
	 * Should wait for I/O to complete on this partition even if
	 * others are open, but wait for work on blkflush().
	 */
#if notyet
	if (ra->ra_openpart == 0) {
		s = spluba();
		while (BUFQ_FIRST(&udautab[unit]) != NULL)
			(void) tsleep(&udautab[unit], PZERO - 1,
			    "raclose", 0);
		splx(s);
		ra->ra_state = DK_CLOSED;
	}
#endif
	return (0);
}

/*
 * Queue a transfer request, and if possible, hand it to the controller.
 */
void
rastrategy(struct buf *bp)
{
	int unit;
	struct ra_softc *ra;
	int s;

	/*
	 * Make sure this is a reasonable drive to use.
	 */
	unit = DISKUNIT(bp->b_dev);
	if (unit >= ra_cd.cd_ndevs || (ra = ra_cd.cd_devs[unit]) == NULL) {
		bp->b_error = ENXIO;
		goto bad;
	}
	/*
	 * If drive is open `raw' or reading label, let it at it.
	 */
	if (ra->ra_state == DK_RDLABEL) {
		s = splbio();
		disk_busy(&ra->ra_disk);
		splx(s);
		mscp_strategy(bp, ra->ra_dev.dv_parent);
		return;
	}

	/* If disk is not online, try to put it online */
	if (ra->ra_state == DK_CLOSED)
		if (ra_putonline(ra) == MSCP_FAILED) {
			bp->b_error = EIO;
			goto bad;
		}

	/* Validate the request. */
	if (bounds_check_with_label(bp, ra->ra_disk.dk_label) == -1)
		goto done;

	/* Make some statistics... /bqt */
	s = splbio();
	disk_busy(&ra->ra_disk);
	splx(s);
	mscp_strategy(bp, ra->ra_dev.dv_parent);
	return;

 bad:
	bp->b_flags |= B_ERROR;
	bp->b_resid = bp->b_bcount;
 done:
	s = splbio();
	biodone(bp);
	splx(s);
}

int
raread(dev_t dev, struct uio *uio)
{

	return (physio(rastrategy, dev, B_READ, minphys, uio));
}

int
rawrite(dev_t dev, struct uio *uio)
{

	return (physio(rastrategy, dev, B_WRITE, minphys, uio));
}

/*
 * I/O controls.
 */
int
raioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	int unit = DISKUNIT(dev);
	struct disklabel *lp, *tp;
	struct ra_softc *ra = ra_cd.cd_devs[unit];
	int error = 0;

	lp = ra->ra_disk.dk_label;

	switch (cmd) {
	case DIOCGPDINFO:
		ragetdisklabel(dev, ra, (struct disklabel *)data, 1);
		break;

	case DIOCGDINFO:
		bcopy(lp, data, sizeof (struct disklabel));
		break;

	case DIOCGPART:
		((struct partinfo *)data)->disklab = lp;
		((struct partinfo *)data)->part =
		    &lp->d_partitions[DISKPART(dev)];
		break;

	case DIOCWDINFO:
	case DIOCSDINFO:
		tp = (struct disklabel *)data;

		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			error = setdisklabel(lp, tp, 0);
			if (error == 0 && cmd == DIOCWDINFO) {
				error = writedisklabel(dev, rastrategy, lp);
			}
		}
		break;

	default:
		error = ENOTTY;
		break;
	}
	return (error);
}

int
radump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
	return ENXIO;
}

/*
 * Return the size of a partition, if known, or -1 if not.
 */
daddr_t
rasize(dev_t dev)
{
	int unit = DISKUNIT(dev), part = DISKPART(dev);
	struct ra_softc *ra;
	struct disklabel *lp;

	if (unit >= ra_cd.cd_ndevs || ra_cd.cd_devs[unit] == NULL)
		return -1;

	ra = ra_cd.cd_devs[unit];

	if (ra->ra_state == DK_CLOSED)
		if (ra_putonline(ra) == MSCP_FAILED)
			return -1;

	lp = ra->ra_disk.dk_label;
	if (part >= lp->d_npartitions ||
	    lp->d_partitions[part].p_fstype != FS_SWAP)
		return -1;
	else
		return DL_GETPSIZE(&lp->d_partitions[part]) *
		    (lp->d_secsize / DEV_BSIZE);
}

#endif /* NRA */

#if NRX

int	rxmatch(struct device *, struct cfdata *, void *);
int	rxopen(dev_t, int, int, struct proc *);
int	rxclose(dev_t, int, int, struct proc *);
void	rxstrategy(struct buf *);
int	rxread(dev_t, struct uio *);
int	rxwrite(dev_t, struct uio *);
int	rxioctl(dev_t, int, caddr_t, int, struct proc *);
int	rxdump(dev_t, daddr_t, caddr_t, size_t);
daddr_t	rxsize(dev_t);

const struct cfattach rx_ca = {
	sizeof(struct rx_softc), (cfmatch_t)rxmatch, rxattach
};

/*
 * More driver definitions, for generic MSCP code.
 */

int
rxmatch(struct device *parent, struct cfdata *cf, void *aux)
{
	struct drive_attach_args *da = aux;
	struct mscp *mp = da->da_mp;

	if ((da->da_typ & MSCPBUS_DISK) == 0)
		return 0;
	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != mp->mscp_unit)
		return 0;
	/*
	 * Check if this disk is a floppy; then configure it.
	 * Seems to be a safe way to test it per Chris Torek.
	 */
	if (MSCP_MID_ECH(1, mp->mscp_guse.guse_mediaid) == 'X' - '@')
		return 1;
	return 0;
}

#endif /* NRX */

/*
 * The attach routine only checks and prints drive type.
 * Bringing the disk online is done when the disk is accessed
 * the first time.
 */
void
rxattach(struct device *parent, struct device *self, void *aux)
{
	struct rx_softc *rx = (void *)self;
	struct drive_attach_args *da = aux;
	struct mscp *mp = da->da_mp;
	struct mscp_softc *mi = (void *)parent;

	bcopy(&mp->mscp_guse, &rx->ra_guse, sizeof(struct mscpv_guse));
	rx->ra_state = DK_CLOSED;
	rx->ra_hwunit = mp->mscp_unit;
	mi->mi_dp[mp->mscp_unit] = self;

	rx->ra_disk.dk_name = rx->ra_dev.dv_xname;
	disk_attach(&rx->ra_dev, &rx->ra_disk);

	disk_printtype(mp->mscp_unit, mp->mscp_guse.guse_mediaid);
#ifdef DEBUG
	printf("%s: nspt %d group %d ngpc %d rct %d nrpt %d nrct %d\n",
	    self->dv_xname, mp->mscp_guse.guse_nspt, mp->mscp_guse.guse_group,
	    mp->mscp_guse.guse_ngpc, mp->mscp_guse.guse_rctsize,
	    mp->mscp_guse.guse_nrpt, mp->mscp_guse.guse_nrct);
#endif
}

/*
 * (Try to) put the drive online. This is done the first time the
 * drive is opened, or if it has fallen offline.
 */
int
rx_putonline(struct rx_softc *rx)
{
	struct mscp *mp;
	struct mscp_softc *mi = (struct mscp_softc *)rx->ra_dev.dv_parent;
	volatile int i;

	/* caller may be in DK_RDLABEL state */
	if (rx->ra_state == DK_OPEN)
		rx->ra_state = DK_CLOSED;
	mp = mscp_getcp(mi, MSCP_WAIT);
	mp->mscp_opcode = M_OP_ONLINE;
	mp->mscp_unit = rx->ra_hwunit;
	mp->mscp_cmdref = 1;
	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
	DELAY(10000);		/* XXX SIMH needs this. */

	/* Poll away */
	i = bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
	if (tsleep(&rx->ra_dev.dv_unit, PRIBIO, "rxonline", 100*100) != 0) {
		rx->ra_state = DK_CLOSED;
		return MSCP_FAILED;
	}

	return MSCP_DONE;
}

#if NRX

/*
 * Open a drive.
 */
/*ARGSUSED*/
int
rxopen(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct rx_softc *rx;
	int unit;

	/*
	 * Make sure this is a reasonable open request.
	 */
	unit = DISKUNIT(dev);
	if (unit >= rx_cd.cd_ndevs)
		return ENXIO;
	rx = rx_cd.cd_devs[unit];
	if (rx == 0)
		return ENXIO;

	/*
	 * If this is the first open; we must first try to put
	 * the disk online (and read the label).
	 */
	if (rx->ra_state == DK_CLOSED)
		if (rx_putonline(rx) == MSCP_FAILED)
			return ENXIO;

	return 0;
}

/* ARGSUSED */
int
rxclose(dev_t dev, int flags, int fmt, struct proc *p)
{
	return (0);
}

/*
 * Queue a transfer request, and if possible, hand it to the controller.
 *
 * This routine is broken into two so that the internal version
 * udastrat1() can be called by the (nonexistent, as yet) bad block
 * revectoring routine.
 */
void
rxstrategy(struct buf *bp)
{
	int unit;
	struct rx_softc *rx;
	int s;

	/*
	 * Make sure this is a reasonable drive to use.
	 */
	unit = DISKUNIT(bp->b_dev);
	if (unit >= rx_cd.cd_ndevs || (rx = rx_cd.cd_devs[unit]) == NULL) {
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		goto done;
	}

	/* If disk is not online, try to put it online */
	if (rx->ra_state == DK_CLOSED)
		if (rx_putonline(rx) == MSCP_FAILED) {
			bp->b_flags |= B_ERROR;
			bp->b_error = EIO;
			goto done;
		}

	/*
	 * Determine the size of the transfer, and make sure it is
	 * within the boundaries of the partition.
	 */
	if (bp->b_blkno >= DL_GETDSIZE(rx->ra_disk.dk_label)) {
		bp->b_resid = bp->b_bcount;
		goto done;
	}

	/* Make some statistics... /bqt */
	s = splbio();
	disk_busy(&rx->ra_disk);
	splx(s);
	mscp_strategy(bp, rx->ra_dev.dv_parent);
	return;

done:
	s = splbio();
	biodone(bp);
	splx(s);
}

int
rxread(dev_t dev, struct uio *uio)
{

	return (physio(rxstrategy, dev, B_READ, minphys, uio));
}

int
rxwrite(dev_t dev, struct uio *uio)
{

	return (physio(rxstrategy, dev, B_WRITE, minphys, uio));
}

/*
 * I/O controls.
 */
int
rxioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
{
	int unit = DISKUNIT(dev);
	struct disklabel *lp;
	struct rx_softc *rx = rx_cd.cd_devs[unit];
	int error = 0;

	lp = rx->ra_disk.dk_label;

	switch (cmd) {

	case DIOCGDINFO:
	case DIOCGPDINFO:	/* no separate 'physical' info available. */
		bcopy(lp, data, sizeof (struct disklabel));
		break;

	case DIOCGPART:
		((struct partinfo *)data)->disklab = lp;
		((struct partinfo *)data)->part =
		    &lp->d_partitions[DISKPART(dev)];
		break;

	case DIOCWDINFO:
	case DIOCSDINFO:
		break;

	default:
		error = ENOTTY;
		break;
	}
	return (error);
}

int
rxdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{

	/* Not likely. */
	return ENXIO;
}

daddr_t
rxsize(dev_t dev)
{

	return -1;
}

#endif /* NRX */

void	rrdgram(struct device *, struct mscp *, struct mscp_softc *);
void	rriodone(struct device *, struct buf *);
int	rronline(struct device *, struct mscp *);
int	rrgotstatus(struct device *, struct mscp *);
void	rrreplace(struct device *, struct mscp *);
int	rrioerror(struct device *, struct mscp *, struct buf *);
void	rrfillin(struct buf *, struct mscp *);
void	rrbb(struct device *, struct mscp *, struct buf *);

struct mscp_device ra_device = {
	rrdgram,
	rriodone,
	rronline,
	rrgotstatus,
	rrreplace,
	rrioerror,
	rrbb,
	rrfillin,
};

/*
 * Handle an error datagram.
 * This can come from an unconfigured drive as well.
 */
void
rrdgram(struct device *usc, struct mscp *mp, struct mscp_softc *mi)
{
	if (mscp_decodeerror(usc == NULL?"unconf disk" : usc->dv_xname, mp, mi))
		return;
	/*
	 * SDI status information bytes 10 and 11 are the microprocessor
	 * error code and front panel code respectively.  These vary per
	 * drive type and are printed purely for field service information.
	 */
	if (mp->mscp_format == M_FM_SDI)
		printf("\tsdi uproc error code 0x%x, front panel code 0x%x\n",
			mp->mscp_erd.erd_sdistat[10],
			mp->mscp_erd.erd_sdistat[11]);
}

void
rriodone(struct device *usc, struct buf *bp)
{
	int s;
	struct rx_softc *rx = NULL; /* Wall */

	int unit = DISKUNIT(bp->b_dev);
#if NRA
	if (major(bp->b_dev) == RAMAJOR)
		rx = ra_cd.cd_devs[unit];
#endif
#if NRX
	if (major(bp->b_dev) != RAMAJOR)
		rx = rx_cd.cd_devs[unit];
#endif

	s = splbio();
	disk_unbusy(&rx->ra_disk, bp->b_bcount - bp->b_resid,
	    bp->b_flags & B_READ);
	biodone(bp);
	splx(s);
}

/*
 * A drive came on line.  Check its type and size.  Return DONE if
 * we think the drive is truly on line.	 In any case, awaken anyone
 * sleeping on the drive on-line-ness.
 */
int
rronline(struct device *usc, struct mscp *mp)
{
	struct rx_softc *rx = (struct rx_softc *)usc;

	wakeup((caddr_t)&usc->dv_unit);
	if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
		printf("%s: attempt to bring on line failed: ", usc->dv_xname);
		mscp_printevent(mp);
		return (MSCP_FAILED);
	}

	rx->ra_state = DK_OPEN;
	rx->ra_unitsize = mp->mscp_onle.onle_unitsize;

	return (MSCP_DONE);
}

/*
 * We got some (configured) unit's status.  Return DONE if it succeeded.
 */
int
rrgotstatus(struct device *usc, struct mscp *mp)
{
	if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
		printf("%s: attempt to get status failed: ", usc->dv_xname);
		mscp_printevent(mp);
		return (MSCP_FAILED);
	}
	/* record for (future) bad block forwarding and whatever else */
#ifdef notyet
	uda_rasave(ui->ui_unit, mp, 1);
#endif
	return (MSCP_DONE);
}

/*
 * A replace operation finished.
 */
/*ARGSUSED*/
void
rrreplace(struct device *usc, struct mscp *mp)
{

	panic("udareplace");
}

/*
 * A transfer failed.  We get a chance to fix or restart it.
 * Need to write the bad block forwaring code first....
 */
/*ARGSUSED*/
int
rrioerror(struct device *usc, struct mscp *mp, struct buf *bp)
{
	struct ra_softc *ra = (void *)usc;
	int code = mp->mscp_event;

	switch (code & M_ST_MASK) {
	/* The unit has fallen offline. Try to figure out why. */
	case M_ST_OFFLINE:
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
		ra->ra_state = DK_CLOSED;
		if (code & M_OFFLINE_UNMOUNTED)
			printf("%s: not mounted/spun down\n", usc->dv_xname);
		if (code & M_OFFLINE_DUPLICATE)
			printf("%s: duplicate unit number!!!\n", usc->dv_xname);
		return MSCP_DONE;

	case M_ST_AVAILABLE:
		ra->ra_state = DK_CLOSED; /* Force another online */
		return MSCP_DONE;

	default:
		printf("%s:", usc->dv_xname);
		break;
	}
	return (MSCP_FAILED);
}

/*
 * Fill in disk addresses in a mscp packet waiting for transfer.
 */
void
rrfillin(struct buf *bp, struct mscp *mp)
{
	struct rx_softc *rx = 0; /* Wall */
	struct disklabel *lp;
	int unit = DISKUNIT(bp->b_dev);
	int part = DISKPART(bp->b_dev);

#if NRA
	if (major(bp->b_dev) == RAMAJOR)
		rx = ra_cd.cd_devs[unit];
#endif
#if NRX
	if (major(bp->b_dev) != RAMAJOR)
		rx = rx_cd.cd_devs[unit];
#endif
	lp = rx->ra_disk.dk_label;

	mp->mscp_seq.seq_lbn = DL_GETPOFFSET(&lp->d_partitions[part]) + bp->b_blkno;
	mp->mscp_unit = rx->ra_hwunit;
	mp->mscp_seq.seq_bytecount = bp->b_bcount;
}

/*
 * A bad block related operation finished.
 */
/*ARGSUSED*/
void
rrbb(struct device *usc, struct mscp *mp, struct buf *bp)
{

	panic("udabb");
}


code:
/*	$OpenBSD: mscp_subr.c,v 1.14 2015/07/04 12:49:55 dlg Exp $	*/
/*	$NetBSD: mscp_subr.c,v 1.18 2001/11/13 07:38:28 lukem Exp $	*/
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)mscp.c	7.5 (Berkeley) 12/16/90
 */

/*
 * MSCP generic driver routines
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/proc.h>

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

#include <arch/vax/mscp/mscp.h>
#include <arch/vax/mscp/mscpreg.h>
#include <arch/vax/mscp/mscpvar.h>

#include "ra.h"
#include "mt.h"

int	mscp_match(struct device *, struct cfdata *, void *);
void	mscp_attach(struct device *, struct device *, void *);
void	mscp_start(struct	mscp_softc *);
int	mscp_init(struct  mscp_softc *);
void	mscp_initds(struct mscp_softc *);
int	mscp_waitstep(struct mscp_softc *, int, int);

struct	cfattach mscpbus_ca = {
	sizeof(struct mscp_softc), (cfmatch_t)mscp_match, mscp_attach
};

struct cfdriver mscpbus_cd = {
	NULL, "mscpbus", DV_DULL
};

#define	READ_SA		(bus_space_read_2(mi->mi_iot, mi->mi_sah, 0))
#define	READ_IP		(bus_space_read_2(mi->mi_iot, mi->mi_iph, 0))
#define	WRITE_IP(x)	bus_space_write_2(mi->mi_iot, mi->mi_iph, 0, (x))
#define	WRITE_SW(x)	bus_space_write_2(mi->mi_iot, mi->mi_swh, 0, (x))

struct	mscp slavereply;

/*
 * This function is for delay during init. Some MSCP clone card (Dilog)
 * can't handle fast read from its registers, and therefore need
 * a delay between them.
 */

#define DELAYTEN 1000
int
mscp_waitstep(mi, mask, result)
	struct mscp_softc *mi;
	int mask, result;
{
	int	status = 1;

	if ((READ_SA & mask) != result) {
		volatile int count = 0;
		while ((READ_SA & mask) != result) {
			DELAY(10000);
			count += 1;
			if (count > DELAYTEN)
				break;
		}
		if (count > DELAYTEN)
			status = 0;
	}
	return status;
}

int
mscp_match(parent, match, aux)
	struct device *parent;
	struct cfdata *match;
	void *aux;
{
	struct	mscp_attach_args *ma = aux;

#if NRA || NRX
	if (ma->ma_type & MSCPBUS_DISK)
		return 1;
#endif
#if NMT
	if (ma->ma_type & MSCPBUS_TAPE)
		return 1;
#endif
	return 0;
};

void
mscp_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct	mscp_attach_args *ma = aux;
	struct	mscp_softc *mi = (void *)self;
	volatile struct mscp *mp;
	volatile int i;
	int	timeout, next = 0;

	mi->mi_mc = ma->ma_mc;
	mi->mi_me = NULL;
	mi->mi_type = ma->ma_type;
	mi->mi_uda = ma->ma_uda;
	mi->mi_dmat = ma->ma_dmat;
	mi->mi_dmam = ma->ma_dmam;
	mi->mi_iot = ma->ma_iot;
	mi->mi_iph = ma->ma_iph;
	mi->mi_sah = ma->ma_sah;
	mi->mi_swh = ma->ma_swh;
	mi->mi_ivec = ma->ma_ivec;
	mi->mi_adapnr = ma->ma_adapnr;
	mi->mi_ctlrnr = ma->ma_ctlrnr;
	*ma->ma_softc = mi;
	/*
	 * Go out to init the bus, so that we can give commands
	 * to its devices.
	 */
	mi->mi_cmd.mri_size = NCMD;
	mi->mi_cmd.mri_desc = mi->mi_uda->mp_ca.ca_cmddsc;
	mi->mi_cmd.mri_ring = mi->mi_uda->mp_cmd;
	mi->mi_rsp.mri_size = NRSP;
	mi->mi_rsp.mri_desc = mi->mi_uda->mp_ca.ca_rspdsc;
	mi->mi_rsp.mri_ring = mi->mi_uda->mp_rsp;
	bufq_init(&mi->mi_bufq, BUFQ_FIFO);

	if (mscp_init(mi)) {
		printf("%s: can't init, controller hung\n",
		    mi->mi_dev.dv_xname);
		return;
	}
	for (i = 0; i < NCMD; i++) {
		mi->mi_mxiuse |= (1 << i);
		if (bus_dmamap_create(mi->mi_dmat, (64*1024), 16, (64*1024),
		    0, BUS_DMA_NOWAIT, &mi->mi_xi[i].mxi_dmam)) {
			printf("Couldn't alloc dmamap %d\n", i);
			return;
		}
	}

#if NRA
	if (ma->ma_type & MSCPBUS_DISK) {
		extern	struct mscp_device ra_device;

		mi->mi_me = &ra_device;
	}
#endif
#if NMT
	if (ma->ma_type & MSCPBUS_TAPE) {
		extern	struct mscp_device mt_device;

		mi->mi_me = &mt_device;
	}
#endif
	/*
	 * Go out and search for sub-units on this MSCP bus,
	 * and call config_found for each found.
	 */
findunit:
	mp = mscp_getcp(mi, MSCP_DONTWAIT);
	if (mp == NULL)
		panic("mscpattach: no packets");
	mp->mscp_opcode = M_OP_GETUNITST;
	mp->mscp_unit = next;
	mp->mscp_modifier = M_GUM_NEXTUNIT;
	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
	slavereply.mscp_opcode = 0;

	i = bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
	mp = &slavereply;
	timeout = 1000;
	while (timeout-- > 0) {
		DELAY(10000);
		if (mp->mscp_opcode)
			goto gotit;
	}
	printf("%s: no response to Get Unit Status request\n",
	    mi->mi_dev.dv_xname);
	return;

gotit:	/*
	 * Got a slave response.  If the unit is there, use it.
	 */
	switch (mp->mscp_status & M_ST_MASK) {

	case M_ST_SUCCESS:	/* worked */
	case M_ST_AVAILABLE:	/* found another drive */
		break;		/* use it */

	case M_ST_OFFLINE:
		/*
		 * Figure out why it is off line.  It may be because
		 * it is nonexistent, or because it is spun down, or
		 * for some other reason.
		 */
		switch (mp->mscp_status & ~M_ST_MASK) {

		case M_OFFLINE_UNKNOWN:
			/*
			 * No such drive, and there are none with
			 * higher unit numbers either, if we are
			 * using M_GUM_NEXTUNIT.
			 */
			mi->mi_ierr = 3;
			return;

		case M_OFFLINE_UNMOUNTED:
			/*
			 * The drive is not spun up.  Use it anyway.
			 *
			 * N.B.: this seems to be a common occurrance
			 * after a power failure.  The first attempt
			 * to bring it on line seems to spin it up
			 * (and thus takes several minutes).  Perhaps
			 * we should note here that the on-line may
			 * take longer than usual.
			 */
			break;

		default:
			/*
			 * In service, or something else equally unusable.
			 */
			printf("%s: unit %d off line: ", mi->mi_dev.dv_xname,
				mp->mscp_unit);
			mscp_printevent((struct mscp *)mp);
			next++;
			goto findunit;
		}
		break;

	default:
		printf("%s: unable to get unit status: ", mi->mi_dev.dv_xname);
		mscp_printevent((struct mscp *)mp);
		return;
	}

	/*
	 * If we get a lower number, we have circulated around all
	 * devices and are finished, otherwise try to find next unit.
	 * We shouldn't ever get this, it's a workaround.
	 */
	if (mp->mscp_unit < next)
		return;

	next = mp->mscp_unit + 1;
	goto findunit;
}

/*
 * The ctlr gets initialised, normally after boot but may also be
 * done if the ctlr gets in an unknown state. Returns 1 if init
 * fails, 0 otherwise.
 */
int
mscp_init(mi)
	struct	mscp_softc *mi;
{
	struct	mscp *mp;
	volatile int i;
	int	status, count;
	unsigned int j = 0;

	/*
	 * While we are thinking about it, reset the next command
	 * and response indices.
	 */
	mi->mi_cmd.mri_next = 0;
	mi->mi_rsp.mri_next = 0;

	mi->mi_flags |= MSC_IGNOREINTR;

	if ((mi->mi_type & MSCPBUS_KDB) == 0)
		WRITE_IP(0); /* Kick off */;

	status = mscp_waitstep(mi, MP_STEP1, MP_STEP1);/* Wait to it wakes up */
	if (status == 0)
		return 1; /* Init failed */
	if (READ_SA & MP_ERR) {
		(*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0);
		return 1;
	}

	/* step1 */
	WRITE_SW(MP_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) |
	    MP_IE | (mi->mi_ivec >> 2));
	status = mscp_waitstep(mi, STEP1MASK, STEP1GOOD);
	if (status == 0) {
		(*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0);
		return 1;
	}

	/* step2 */
	WRITE_SW((mi->mi_dmam->dm_segs[0].ds_addr & 0xffff) +
	    offsetof(struct mscp_pack, mp_ca.ca_rspdsc[0]));
	status = mscp_waitstep(mi, STEP2MASK, STEP2GOOD(mi->mi_ivec >> 2));
	if (status == 0) {
		(*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0);
		return 1;
	}

	/* step3 */
	WRITE_SW((mi->mi_dmam->dm_segs[0].ds_addr >> 16));
	status = mscp_waitstep(mi, STEP3MASK, STEP3GOOD);
	if (status == 0) {
		(*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0);
		return 1;
	}
	i = READ_SA & 0377;
	printf(": version %d model %d\n", i & 15, i >> 4);

#define BURST 4 /* XXX */
	if (mi->mi_type & MSCPBUS_UDA) {
		WRITE_SW(MP_GO | (BURST - 1) << 2);
		printf("%s: DMA burst size set to %d\n",
		    mi->mi_dev.dv_xname, BURST);
	}
	WRITE_SW(MP_GO);

	mscp_initds(mi);
	mi->mi_flags &= ~MSC_IGNOREINTR;

	/*
	 * Set up all necessary info in the bus softc struct, get a
	 * mscp packet and set characteristics for this controller.
	 */
	mi->mi_credits = MSCP_MINCREDITS + 1;
	mp = mscp_getcp(mi, MSCP_DONTWAIT);

	mi->mi_credits = 0;
	mp->mscp_opcode = M_OP_SETCTLRC;
	mp->mscp_unit = mp->mscp_modifier = mp->mscp_flags =
	    mp->mscp_sccc.sccc_version = mp->mscp_sccc.sccc_hosttimo =
	    mp->mscp_sccc.sccc_time = mp->mscp_sccc.sccc_time1 =
	    mp->mscp_sccc.sccc_errlgfl = 0;
	mp->mscp_sccc.sccc_ctlrflags = M_CF_ATTN | M_CF_MISC | M_CF_THIS;
	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
	i = READ_IP;

	count = 0;
	while (count < DELAYTEN) {
		if (((volatile int)mi->mi_flags & MSC_READY) != 0)
			break;
		if ((j = READ_SA) & MP_ERR)
			goto out;
		DELAY(10000);
		count += 1;
	}
	if (count == DELAYTEN) {
out:
		printf("%s: couldn't set ctlr characteristics, sa=%x\n",
		    mi->mi_dev.dv_xname, j);
		return 1;
	}
	return 0;
}

/*
 * Initialise the various data structures that control the mscp protocol.
 */
void
mscp_initds(mi)
	struct mscp_softc *mi;
{
	struct mscp_pack *ud = mi->mi_uda;
	struct mscp *mp;
	int i;

	for (i = 0, mp = ud->mp_rsp; i < NRSP; i++, mp++) {
		ud->mp_ca.ca_rspdsc[i] = MSCP_OWN | MSCP_INT |
		    (mi->mi_dmam->dm_segs[0].ds_addr +
		    offsetof(struct mscp_pack, mp_rsp[i].mscp_cmdref));
		mp->mscp_addr = &ud->mp_ca.ca_rspdsc[i];
		mp->mscp_msglen = MSCP_MSGLEN;
	}
	for (i = 0, mp = ud->mp_cmd; i < NCMD; i++, mp++) {
		ud->mp_ca.ca_cmddsc[i] = MSCP_INT |
		    (mi->mi_dmam->dm_segs[0].ds_addr +
		    offsetof(struct mscp_pack, mp_cmd[i].mscp_cmdref));
		mp->mscp_addr = &ud->mp_ca.ca_cmddsc[i];
		mp->mscp_msglen = MSCP_MSGLEN;
		if (mi->mi_type & MSCPBUS_TAPE)
			mp->mscp_vcid = 1;
	}
}

static	void mscp_kickaway(struct mscp_softc *);

void
mscp_intr(mi)
	struct mscp_softc *mi;
{
	struct mscp_pack *ud = mi->mi_uda;

	if (mi->mi_flags & MSC_IGNOREINTR)
		return;
	/*
	 * Check for response and command ring transitions.
	 */
	if (ud->mp_ca.ca_rspint) {
		ud->mp_ca.ca_rspint = 0;
		mscp_dorsp(mi);
	}
	if (ud->mp_ca.ca_cmdint) {
		ud->mp_ca.ca_cmdint = 0;
		MSCP_DOCMD(mi);
	}

	/*
	 * If there are any not-yet-handled request, try them now.
	 */
	if (bufq_peek(&mi->mi_bufq))
		mscp_kickaway(mi);
}

int
mscp_print(aux, name)
	void *aux;
	const char *name;
{
	struct drive_attach_args *da = aux;
	struct	mscp *mp = da->da_mp;
	int type = mp->mscp_guse.guse_mediaid;

	if (name) {
		printf("%c%c", MSCP_MID_CHAR(2, type), MSCP_MID_CHAR(1, type));
		if (MSCP_MID_ECH(0, type))
			printf("%c", MSCP_MID_CHAR(0, type));
		printf("%d at %s drive %d", MSCP_MID_NUM(type), name,
		    mp->mscp_unit);
	}
	return UNCONF;
}

/*
 * common strategy routine for all types of MSCP devices.
 */
void
mscp_strategy(bp, usc)
	struct buf *bp;
	struct device *usc;
{
	struct	mscp_softc *mi = (void *)usc;
	int s = spl6();

	bufq_queue(&mi->mi_bufq, bp);
	mscp_kickaway(mi);
	splx(s);
}

void
mscp_kickaway(mi)
	struct	mscp_softc *mi;
{
	struct buf *bp;
	struct	mscp *mp;
	int next;

	while ((bp = bufq_dequeue(&mi->mi_bufq))) {
		/*
		 * Ok; we are ready to try to start a xfer. Get a MSCP packet
		 * and try to start...
		 */
		if ((mp = mscp_getcp(mi, MSCP_DONTWAIT)) == NULL) {
			if (mi->mi_credits > MSCP_MINCREDITS)
				printf("%s: command ring too small\n",
				    mi->mi_dev.dv_parent->dv_xname);
			/*
			 * By some (strange) reason we didn't get a MSCP packet.
			 * Just return and wait for free packets.
			 */
			bufq_requeue(&mi->mi_bufq, bp);
			return;
		}

		if ((next = (ffs(mi->mi_mxiuse) - 1)) < 0)
			panic("no mxi buffers");
		mi->mi_mxiuse &= ~(1 << next);
		if (mi->mi_xi[next].mxi_inuse)
			panic("mxi inuse");
		/*
		 * Set up the MSCP packet and ask the ctlr to start.
		 */
		mp->mscp_opcode =
		    (bp->b_flags & B_READ) ? M_OP_READ : M_OP_WRITE;
		mp->mscp_cmdref = next;
		mi->mi_xi[next].mxi_bp = bp;
		mi->mi_xi[next].mxi_mp = mp;
		mi->mi_xi[next].mxi_inuse = 1;
		bp->b_resid = next;
		(*mi->mi_me->me_fillin)(bp, mp);
		(*mi->mi_mc->mc_go)(mi->mi_dev.dv_parent, &mi->mi_xi[next]);
	}
}

void
mscp_dgo(mi, mxi)
	struct mscp_softc *mi;
	struct mscp_xi *mxi;
{
	volatile int i;
	struct	mscp *mp;

	/*
	 * Fill in the MSCP packet and move the buffer to the I/O wait queue.
	 */
	mp = mxi->mxi_mp;
	mp->mscp_seq.seq_buffer = mxi->mxi_dmam->dm_segs[0].ds_addr;

	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;
	i = READ_IP;
}

#ifdef DIAGNOSTIC
/*
 * Dump the entire contents of an MSCP packet in hex.  Mainly useful
 * for debugging....
 */
void
mscp_hexdump(mp)
	struct mscp *mp;
{
	long *p = (long *) mp;
	int i = mp->mscp_msglen;

	if (i > 256)		/* sanity */
		i = 256;
	i /= sizeof (*p);	/* ASSUMES MULTIPLE OF sizeof(long) */
	while (--i >= 0)
		printf("0x%x ", (int)*p++);
	printf("\n");
}
#endif

/*
 * MSCP error reporting
 */

/*
 * Messages for the various subcodes.
 */
static char unknown_msg[] = "unknown subcode";

/*
 * Subcodes for Success (0)
 */
static char *succ_msgs[] = {
	"normal",		/* 0 */
	"spin down ignored",	/* 1 = Spin-Down Ignored */
	"still connected",	/* 2 = Still Connected */
	unknown_msg,
	"dup. unit #",		/* 4 = Duplicate Unit Number */
	unknown_msg,
	unknown_msg,
	unknown_msg,
	"already online",	/* 8 = Already Online */
	unknown_msg,
	unknown_msg,
	unknown_msg,
	unknown_msg,
	unknown_msg,
	unknown_msg,
	unknown_msg,
	"still online",		/* 16 = Still Online */
};

/*
 * Subcodes for Invalid Command (1)
 */
static char *icmd_msgs[] = {
	"invalid msg length",	/* 0 = Invalid Message Length */
};

/*
 * Subcodes for Command Aborted (2)
 */
/* none known */

/*
 * Subcodes for Unit Offline (3)
 */
static char *offl_msgs[] = {
	"unknown drive",	/* 0 = Unknown, or online to other ctlr */
	"not mounted",		/* 1 = Unmounted, or RUN/STOP at STOP */
	"inoperative",		/* 2 = Unit Inoperative */
	unknown_msg,
	"duplicate",		/* 4 = Duplicate Unit Number */
	unknown_msg,
	unknown_msg,
	unknown_msg,
	"in diagnosis",		/* 8 = Disabled by FS or diagnostic */
};

/*
 * Subcodes for Unit Available (4)
 */
/* none known */

/*
 * Subcodes for Media Format Error (5)
 */
static char *media_fmt_msgs[] = {
	"fct unread - edc",	/* 0 = FCT unreadable */
	"invalid sector header",/* 1 = Invalid Sector Header */
	"not 512 sectors",	/* 2 = Not 512 Byte Sectors */
	"not formatted",	/* 3 = Not Formatted */
	"fct ecc",		/* 4 = FCT ECC */
};

/*
 * Subcodes for Write Protected (6)
 * N.B.:  Code 6 subcodes are 7 bits higher than other subcodes
 * (i.e., bits 12-15).
 */
static char *wrprot_msgs[] = {
	unknown_msg,
	"software",		/* 1 = Software Write Protect */
	"hardware",		/* 2 = Hardware Write Protect */
};

/*
 * Subcodes for Compare Error (7)
 */
/* none known */

/*
 * Subcodes for Data Error (8)
 */
static char *data_msgs[] = {
	"forced error",		/* 0 = Forced Error (software) */
	unknown_msg,
	"header compare",	/* 2 = Header Compare Error */
	"sync timeout",		/* 3 = Sync Timeout Error */
	unknown_msg,
	unknown_msg,
	unknown_msg,
	"uncorrectable ecc",	/* 7 = Uncorrectable ECC */
	"1 symbol ecc",		/* 8 = 1 bit ECC */
	"2 symbol ecc",		/* 9 = 2 bit ECC */
	"3 symbol ecc",		/* 10 = 3 bit ECC */
	"4 symbol ecc",		/* 11 = 4 bit ECC */
	"5 symbol ecc",		/* 12 = 5 bit ECC */
	"6 symbol ecc",		/* 13 = 6 bit ECC */
	"7 symbol ecc",		/* 14 = 7 bit ECC */
	"8 symbol ecc",		/* 15 = 8 bit ECC */
};

/*
 * Subcodes for Host Buffer Access Error (9)
 */
static char *host_buffer_msgs[] = {
	unknown_msg,
	"odd xfer addr",	/* 1 = Odd Transfer Address */
	"odd xfer count",	/* 2 = Odd Transfer Count */
	"non-exist. memory",	/* 3 = Non-Existent Memory */
	"memory parity",	/* 4 = Memory Parity Error */
};

/*
 * Subcodes for Controller Error (10)
 */
static char *cntlr_msgs[] = {
	unknown_msg,
	"serdes overrun",	/* 1 = Serialiser/Deserialiser Overrun */
	"edc",			/* 2 = Error Detection Code? */
	"inconsistent internal data struct",/* 3 = Internal Error */
};

/*
 * Subcodes for Drive Error (11)
 */
static char *drive_msgs[] = {
	unknown_msg,
	"sdi command timeout",	/* 1 = SDI Command Timeout */
	"ctlr detected protocol",/* 2 = Controller Detected Protocol Error */
	"positioner",		/* 3 = Positioner Error */
	"lost rd/wr ready",	/* 4 = Lost R/W Ready Error */
	"drive clock dropout",	/* 5 = Lost Drive Clock */
	"lost recvr ready",	/* 6 = Lost Receiver Ready */
	"drive detected error", /* 7 = Drive Error */
	"ctlr detected pulse or parity",/* 8 = Pulse or Parity Error */
};

/*
 * The following table correlates message codes with the
 * decoding strings.
 */
struct code_decode {
	char	*cdc_msg;
	int	cdc_nsubcodes;
	char	**cdc_submsgs;
} code_decode[] = {
#define SC(m)	sizeof (m) / sizeof (m[0]), m
	{"success",			SC(succ_msgs)},
	{"invalid command",		SC(icmd_msgs)},
	{"command aborted",		0, 0},
	{"unit offline",		SC(offl_msgs)},
	{"unit available",		0, 0},
	{"media format error",		SC(media_fmt_msgs)},
	{"write protected",		SC(wrprot_msgs)},
	{"compare error",		0, 0},
	{"data error",			SC(data_msgs)},
	{"host buffer access error",	SC(host_buffer_msgs)},
	{"controller error",		SC(cntlr_msgs)},
	{"drive error",			SC(drive_msgs)},
#undef SC
};

/*
 * Print the decoded error event from an MSCP error datagram.
 */
void
mscp_printevent(mp)
	struct mscp *mp;
{
	int event = mp->mscp_event;
	struct code_decode *cdc;
	int c, sc;
	char *cm, *scm;

	/*
	 * The code is the lower six bits of the event number (aka
	 * status).  If that is 6 (write protect), the subcode is in
	 * bits 12-15; otherwise, it is in bits 5-11.
	 * I WONDER WHAT THE OTHER BITS ARE FOR.  IT SURE WOULD BE
	 * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.
	 */
	c = event & M_ST_MASK;
	sc = (c != 6 ? event >> 5 : event >> 12) & 0x7ff;
	if (c >= sizeof code_decode / sizeof code_decode[0])
		cm = "- unknown code", scm = "??";
	else {
		cdc = &code_decode[c];
		cm = cdc->cdc_msg;
		if (sc >= cdc->cdc_nsubcodes)
			scm = unknown_msg;
		else
			scm = cdc->cdc_submsgs[sc];
	}
	printf(" %s (%s) (code %d, subcode %d)\n", cm, scm, c, sc);
}

static char *codemsg[16] = {
	"lbn", "code 1", "code 2", "code 3",
	"code 4", "code 5", "rbn", "code 7",
	"code 8", "code 9", "code 10", "code 11",
	"code 12", "code 13", "code 14", "code 15"
};
/*
 * Print the code and logical block number for an error packet.
 * THIS IS PROBABLY PECULIAR TO DISK DRIVES.  IT SURE WOULD BE
 * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS.
 */
int
mscp_decodeerror(name, mp, mi)
	char *name;
	struct mscp *mp;
	struct mscp_softc *mi;
{
	int issoft;
	/*
	 * We will get three sdi errors of type 11 after autoconfig
	 * is finished; depending of searching for non-existing units.
	 * How can we avoid this???
	 */
	if (((mp->mscp_event & M_ST_MASK) == 11) && (mi->mi_ierr++ < 3))
		return 1;
	/*
	 * For bad blocks, mp->mscp_erd.erd_hdr identifies a code and
	 * the logical block number.  Code 0 is a regular block; code 6
	 * is a replacement block.  The remaining codes are currently
	 * undefined.  The code is in the upper four bits of the header
	 * (bits 0-27 are the lbn).
	 */
	issoft = mp->mscp_flags & (M_LF_SUCC | M_LF_CONT);
#define BADCODE(h)	(codemsg[(unsigned)(h) >> 28])
#define BADLBN(h)	((h) & 0xfffffff)

	printf("%s: drive %d %s error datagram%s:", name, mp->mscp_unit,
		issoft ? "soft" : "hard",
		mp->mscp_flags & M_LF_CONT ? " (continuing)" : "");
	switch (mp->mscp_format & 0377) {

	case M_FM_CTLRERR:	/* controller error */
		break;

	case M_FM_BUSADDR:	/* host memory access error */
		printf(" memory addr 0x%x:", (int)mp->mscp_erd.erd_busaddr);
		break;

	case M_FM_DISKTRN:
		printf(" unit %d: level %d retry %d, %s %d:",
			mp->mscp_unit,
			mp->mscp_erd.erd_level, mp->mscp_erd.erd_retry,
			BADCODE(mp->mscp_erd.erd_hdr),
			(int)BADLBN(mp->mscp_erd.erd_hdr));
		break;

	case M_FM_SDI:
		printf(" unit %d: %s %d:", mp->mscp_unit,
			BADCODE(mp->mscp_erd.erd_hdr),
			(int)BADLBN(mp->mscp_erd.erd_hdr));
		break;

	case M_FM_SMLDSK:
		printf(" unit %d: small disk error, cyl %d:",
			mp->mscp_unit, mp->mscp_erd.erd_sdecyl);
		break;

	case M_FM_TAPETRN:
		printf(" unit %d: tape transfer error, grp 0x%x event 0%o:",
		    mp->mscp_unit, mp->mscp_erd.erd_sdecyl, mp->mscp_event);
		break;

	case M_FM_STIERR:
		printf(" unit %d: STI error, event 0%o:", mp->mscp_unit,
		    mp->mscp_event);
		break;

	default:
		printf(" unit %d: unknown error, format 0x%x:",
			mp->mscp_unit, mp->mscp_format);
	}
	mscp_printevent(mp);
	return 0;
#undef BADCODE
#undef BADLBN
}


code:
/*	$OpenBSD: mscp_tape.c,v 1.16 2013/06/11 16:42:13 deraadt Exp $ */
/*	$NetBSD: mscp_tape.c,v 1.16 2001/11/13 07:38:28 lukem Exp $ */
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed at Ludd, University of
 *	Lule}, Sweden and its contributors.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/*
 * MSCP tape device driver
 */

/*
 * TODO
 *	Write status handling code.
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/ioccom.h>
#include <sys/mtio.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/conf.h>

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

#include <arch/vax/mscp/mscp.h>
#include <arch/vax/mscp/mscpreg.h>
#include <arch/vax/mscp/mscpvar.h>

/*
 * Drive status, per drive
 */
struct mt_softc {
	struct	device mt_dev;	/* Autoconf struct */
	int	mt_state;	/* open/closed state */
	int	mt_hwunit;	/* Hardware unit number */
	int	mt_inuse;	/* Locks the tape drive for others */
	int	mt_waswrite;	/* Last operation was a write op */
	int	mt_serex;	/* Got serious exception */
	int	mt_ioctlerr;	/* Error after last ioctl */
};

#define MT_OFFLINE	0
#define MT_ONLINE	1

int	mtmatch(struct device *, struct cfdata *, void *);
void	mtattach(struct device *, struct device *, void *);
void	mtdgram(struct device *, struct mscp *, struct mscp_softc *);
void	mtiodone(struct device *, struct buf *);
int	mtonline(struct device *, struct mscp *);
int	mtgotstatus(struct device *, struct mscp *);
int	mtioerror(struct device *, struct mscp *, struct buf *);
void	mtfillin(struct buf *, struct mscp *);
int	mtread(dev_t, struct uio *);
int	mtwrite(dev_t, struct uio *);
int	mtcmd(struct mt_softc *, int, int, int);
void	mtcmddone(struct device *, struct mscp *);
int	mt_putonline(struct mt_softc *);
bdev_decl(mt);

struct	mscp_device mt_device = {
	mtdgram,
	mtiodone,
	mtonline,
	mtgotstatus,
	0,
	mtioerror,
	0,
	mtfillin,
	mtcmddone,
};

/* This is not good, should allow more than 4 tapes/device type */
#define mtunit(dev)	(minor(dev) & T_UNIT)
#define mtnorewind(dev) (dev & T_NOREWIND)
#define mthdensity(dev) (dev & T_1600BPI)

struct	cfattach mt_ca = {
	sizeof(struct mt_softc), (cfmatch_t)mtmatch, mtattach
};

struct cfdriver mt_cd = {
	NULL, "mt", DV_TAPE
};

/*
 * More driver definitions, for generic MSCP code.
 */

int
mtmatch(parent, cf, aux)
	struct	device *parent;
	struct	cfdata *cf;
	void	*aux;
{
	struct	drive_attach_args *da = aux;
	struct	mscp *mp = da->da_mp;

	if ((da->da_typ & MSCPBUS_TAPE) == 0)
		return 0;
	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != mp->mscp_unit)
		return 0;
	return 1;
}

/*
 * The attach routine only checks and prints drive type.
 */
void
mtattach(parent, self, aux)
	struct	device *parent, *self;
	void	*aux;
{
	struct	mt_softc *mt = (void *)self;
	struct	drive_attach_args *da = aux;
	struct	mscp *mp = da->da_mp;
	struct	mscp_softc *mi = (void *)parent;

	mt->mt_hwunit = mp->mscp_unit;
	mi->mi_dp[mp->mscp_unit] = self;

	disk_printtype(mp->mscp_unit, mp->mscp_guse.guse_mediaid);
}

/*
 * (Try to) put the drive online. This is done the first time the
 * drive is opened, or if it has fallen offline.
 */
int
mt_putonline(mt)
	struct mt_softc *mt;
{
	struct	mscp *mp;
	struct	mscp_softc *mi = (struct mscp_softc *)mt->mt_dev.dv_parent;
	volatile int i;

	mt->mt_state = MT_OFFLINE;
	mp = mscp_getcp(mi, MSCP_WAIT);
	mp->mscp_opcode = M_OP_ONLINE;
	mp->mscp_unit = mt->mt_hwunit;
	mp->mscp_cmdref = (long)&mt->mt_state;
	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;

	/* Poll away */
	i = bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
	if (tsleep(&mt->mt_state, PRIBIO, "mtonline", 240 * hz))
		return MSCP_FAILED;

	if ((volatile int)mt->mt_state != MT_ONLINE)
		return MSCP_FAILED;

	return MSCP_DONE;
}
/*
 * Open a drive.
 */
/*ARGSUSED*/
int
mtopen(dev, flag, fmt, p)
	dev_t dev;
	int flag, fmt;
	struct	proc *p;
{
	struct mt_softc *mt;
	int unit;

	/*
	 * Make sure this is a reasonable open request.
	 */
	unit = mtunit(dev);
	if (unit >= mt_cd.cd_ndevs)
		return ENXIO;
	mt = mt_cd.cd_devs[unit];
	if (mt == 0)
		return ENXIO;

	if (mt->mt_inuse)
			return EBUSY;
	mt->mt_inuse = 1;

	if (mt_putonline(mt) == MSCP_FAILED) {
		mt->mt_inuse = 0;
		return EIO;
	}

	return 0;
}

/* ARGSUSED */
int
mtclose(dev, flags, fmt, p)
	dev_t dev;
	int flags, fmt;
	struct	proc *p;
{
	int unit = mtunit(dev);
	struct mt_softc *mt = mt_cd.cd_devs[unit];

	/*
	 * If we just have finished a writing, write EOT marks.
	 */
	if ((flags & FWRITE) && mt->mt_waswrite) {
		mtcmd(mt, MTWEOF, 0, 0);
		mtcmd(mt, MTWEOF, 0, 0);
		mtcmd(mt, MTBSR, 1, 0);
	}
	if (mtnorewind(dev) == 0)
		mtcmd(mt, MTREW, 0, 1);
	if (mt->mt_serex)
		mtcmd(mt, -1, 0, 0);

	mt->mt_inuse = 0; /* Release the tape */
	return 0;
}

void
mtstrategy(bp)
	struct buf *bp;
{
	int unit;
	struct mt_softc *mt;
	int s;

	/*
	 * Make sure this is a reasonable drive to use.
	 */
	unit = mtunit(bp->b_dev);
	if (unit >= mt_cd.cd_ndevs || (mt = mt_cd.cd_devs[unit]) == NULL) {
		bp->b_error = ENXIO;
		goto bad;
	}

	mt->mt_waswrite = bp->b_flags & B_READ ? 0 : 1;
	mscp_strategy(bp, mt->mt_dev.dv_parent);
	return;

bad:
	bp->b_flags |= B_ERROR;
	s = splbio();
	biodone(bp);
	splx(s);
}

int
mtread(dev, uio)
	dev_t dev;
	struct uio *uio;
{

	return (physio(mtstrategy, dev, B_READ, minphys, uio));
}

int
mtwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{

	return (physio(mtstrategy, dev, B_WRITE, minphys, uio));
}

void
mtiodone(usc, bp)
	struct device *usc;
	struct buf *bp;
{
	int s;

	s = splbio();
	biodone(bp);
	splx(s);
}

/*
 * Fill in drive addresses in a mscp packet waiting for transfer.
 */
void
mtfillin(bp, mp)
	struct buf *bp;
	struct mscp *mp;
{
	int unit = mtunit(bp->b_dev);
	struct mt_softc *mt = mt_cd.cd_devs[unit];

	mp->mscp_unit = mt->mt_hwunit;
	if (mt->mt_serex == 2) {
		mp->mscp_modifier = M_MD_CLSEX;
		mt->mt_serex = 0;
	} else
		mp->mscp_modifier = 0;

	mp->mscp_seq.seq_bytecount = bp->b_bcount;
}

/*
 * Handle an error datagram.
 */
void
mtdgram(usc, mp, mi)
	struct device *usc;
	struct mscp *mp;
	struct mscp_softc *mi;
{
	if (mscp_decodeerror(usc == NULL?"unconf mt" : usc->dv_xname, mp, mi))
		return;
}

/*
 * A drive came on line, make sure it really _is_ on line before
 * trying to use it.
 */
int
mtonline(usc, mp)
	struct device *usc;
	struct mscp *mp;
{
	struct mt_softc *mt = (void *)usc;

	wakeup((caddr_t)&mt->mt_state);
	if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS)
		mt->mt_state = MT_ONLINE;

	return (MSCP_DONE);
}

/*
 * We got some (configured) unit's status.  Return DONE.
 */
int
mtgotstatus(usc, mp)
	struct device *usc;
	struct mscp *mp;
{
	return (MSCP_DONE);
}

static char *mt_ioerrs[] = {
	"invalid command",	/* 1 M_ST_INVALCMD */
	"command aborted",	/* 2 M_ST_ABORTED */
	"unit offline",		/* 3 M_ST_OFFLINE */
	"unknown",		/* 4 M_ST_AVAILABLE */
	"unknown",		/* 5 M_ST_MFMTERR */
	"unit write protected", /* 6 M_ST_WRPROT */
	"compare error",	/* 7 M_ST_COMPERR */
	"data error",		/* 8 M_ST_DATAERR */
	"host buffer access error",	/* 9 M_ST_HOSTBUFERR */
	"controller error",	/* 10 M_ST_CTLRERR */
	"drive error",		/* 11 M_ST_DRIVEERR */
	"formatter error",	/* 12 M_ST_FORMATTERR */
	"BOT encountered",	/* 13 M_ST_BOT */
	"tape mark encountered",/* 14 M_ST_TAPEMARK */
	"unknown",		/* 15 */
	"record data truncated",/* 16 M_ST_RDTRUNC */
};

/*
 * An I/O error, may be because of a tapemark encountered.
 * Check that before failing.
 */
/*ARGSUSED*/
int
mtioerror(usc, mp, bp)
	struct device *usc;
	struct mscp *mp;
	struct buf *bp;
{
	struct mt_softc *mt = (void *)usc;
	int st = mp->mscp_status & M_ST_MASK;

	if (mp->mscp_flags & M_EF_SEREX)
		mt->mt_serex = 1;
	if (st == M_ST_TAPEMARK)
		mt->mt_serex = 2;
	else {
		if (st && st < 17)
			printf("%s: error %d (%s)\n", mt->mt_dev.dv_xname, st,
			    mt_ioerrs[st-1]);
		else
			printf("%s: error %d\n", mt->mt_dev.dv_xname, st);
		bp->b_flags |= B_ERROR;
		bp->b_error = EROFS;
	}

	return (MSCP_DONE);
}

/*
 * I/O controls.
 */
int
mtioctl(dev, cmd, data, flag, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flag;
	struct proc *p;
{
	int unit = mtunit(dev);
	struct mt_softc *mt = mt_cd.cd_devs[unit];
	struct	mtop *mtop;
	struct	mtget *mtget;
	int error = 0;

	switch (cmd) {

	case MTIOCTOP:
		mtop = (void *)data;
		if (mtop->mt_op == MTWEOF) {
			while (mtop->mt_count-- > 0)
				if ((error = mtcmd(mt, mtop->mt_op, 0, 0)))
					break;
		} else
			error = mtcmd(mt, mtop->mt_op, mtop->mt_count, 0);

	case MTIOCGET:
		mtget = (void *)data;
		mtget->mt_type = MT_ISTMSCP;
		/* XXX we need to fill in more fields here */
		break;

	default:
		error = ENXIO;
		break;
	}
	return (error);
}

/*
 * No crash dump support...
 */
int
mtdump(dev, blkno, va, size)
	dev_t	dev;
	daddr_t blkno;
	caddr_t va;
	size_t	size;
{
	return -1;
}

/*
 * Send a command to the tape drive. Wait until the command is
 * finished before returning.
 * This routine must only be called when there are no data transfer
 * active on this device. Can we be sure of this? Or does the ctlr
 * queue up all command packets and take them in sequential order?
 * It sure would be nice if my manual stated this... /ragge
 */
int
mtcmd(mt, cmd, count, complete)
	struct mt_softc *mt;
	int cmd, count, complete;
{
	struct mscp *mp;
	struct mscp_softc *mi = (void *)mt->mt_dev.dv_parent;
	volatile int i;

	mp = mscp_getcp(mi, MSCP_WAIT);

	mt->mt_ioctlerr = 0;
	mp->mscp_unit = mt->mt_hwunit;
	mp->mscp_cmdref = -1;
	*mp->mscp_addr |= MSCP_OWN | MSCP_INT;

	switch (cmd) {
	case MTWEOF:
		mp->mscp_opcode = M_OP_WRITM;
		break;

	case MTBSF:
		mp->mscp_modifier = M_MD_REVERSE;
	case MTFSF:
		mp->mscp_opcode = M_OP_POS;
		mp->mscp_seq.seq_buffer = count;
		break;

	case MTBSR:
		mp->mscp_modifier = M_MD_REVERSE;
	case MTFSR:
		mp->mscp_opcode = M_OP_POS;
		mp->mscp_modifier |= M_MD_OBJCOUNT;
		mp->mscp_seq.seq_bytecount = count;
		break;

	case MTREW:
		mp->mscp_opcode = M_OP_POS;
		mp->mscp_modifier = M_MD_REWIND | M_MD_CLSEX;
		if (complete)
			mp->mscp_modifier |= M_MD_IMMEDIATE;
		mt->mt_serex = 0;
		break;

	case MTOFFL:
		mp->mscp_opcode = M_OP_AVAILABLE;
		mp->mscp_modifier = M_MD_UNLOAD | M_MD_CLSEX;
		mt->mt_serex = 0;
		break;

	case MTNOP:
		mp->mscp_opcode = M_OP_GETUNITST;
		break;

	case -1: /* Clear serious exception only */
		mp->mscp_opcode = M_OP_POS;
		mp->mscp_modifier = M_MD_CLSEX;
		mt->mt_serex = 0;
		break;

	default:
		printf("Bad ioctl %x\n", cmd);
		mp->mscp_opcode = M_OP_POS;
		break;
	}

	i = bus_space_read_2(mi->mi_iot, mi->mi_iph, 0);
	tsleep(&mt->mt_inuse, PRIBIO, "mtioctl", 0);
	return mt->mt_ioctlerr;
}

/*
 * Called from bus routines whenever a non-data transfer is finished.
 */
void
mtcmddone(usc, mp)
	struct device *usc;
	struct mscp *mp;
{
	struct mt_softc *mt = (void *)usc;

	if (mp->mscp_status) {
		mt->mt_ioctlerr = EIO;
		printf("%s: bad status %x\n", mt->mt_dev.dv_xname,
		    mp->mscp_status);
	}
	wakeup(&mt->mt_inuse);
}


code:
/*	$OpenBSD: mscpreg.h,v 1.5 2003/06/02 23:27:57 millert Exp $	*/
/*	$NetBSD: mscpreg.h,v 1.4 1999/05/29 19:12:53 ragge Exp $	*/
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)udareg.h	7.3 (Berkeley) 5/8/91
 */

/*
 * NRSPL2 and NCMDL2 control the number of response and command
 * packets respectively.  They may be any value from 0 to 7, though
 * setting them higher than 5 is unlikely to be of any value.
 * If you get warnings about your command ring being too small,
 * try increasing the values by one.
 */
#ifndef NRSP
#define NRSPL2	5
#define NCMDL2	5
#define NRSP	(1 << NRSPL2)
#define NCMD	(1 << NCMDL2)
#endif

/*
 * Communication area definition. This seems to be the same for
 * all types of MSCP controllers.
 */

struct mscp_ca {
	short	ca_xxx1;	/* unused */
	char	ca_xxx2;	/* unused */
	char	ca_bdp;		/* BDP to purge */
	short	ca_cmdint;	/* command ring transition flag */
	short	ca_rspint;	/* response ring transition flag */
	long	ca_rspdsc[NRSP];/* response descriptors */
	long	ca_cmddsc[NCMD];/* command descriptors */
};

/*
 * Simplified routines (e.g., uddump) reprogram the UDA50 for one command
 * and one response at a time; uda1ca is like udaca except that it provides
 * exactly one command and response descriptor.
 */
struct mscp_1ca {
	short	ca_xxx1;
	char	ca_xxx2;
	char	ca_bdp;
	short	ca_cmdint;
	short	ca_rspint;
	long	ca_rspdsc;
	long	ca_cmddsc;
};

/*
 * Combined communications area and MSCP packet pools, per controller.
 * NRSP and NCMD must be defined before this struct is used.
 */

struct	mscp_pack {
	struct	mscp_ca mp_ca;		/* communications area */
	struct	mscp mp_rsp[NRSP];	/* response packets */
	struct	mscp mp_cmd[NCMD];	/* command packets */
};

/*
 * Bits in UDA status register during initialisation
 */
#define MP_ERR		0x8000	/* error */
#define MP_STEP4	0x4000	/* step 4 has started */
#define MP_STEP3	0x2000	/* step 3 has started */
#define MP_STEP2	0x1000	/* step 2 has started */
#define MP_STEP1	0x0800	/* step 1 has started */
#define MP_NV		0x0400	/* no host settable interrupt vector */
#define MP_QB		0x0200	/* controller supports Q22 bus */
#define MP_DI		0x0100	/* controller implements diagnostics */
#define MP_IE		0x0080	/* interrupt enable */
#define MP_NCNRMASK	0x003f	/* in STEP1, bits 0-2=NCMDL2, 3-5=NRSPL2 */
#define MP_IVECMASK	0x007f	/* in STEP2, bits 0-6 are interruptvec / 4 */
#define MP_PI		0x0001	/* host requests adapter purge interrupts */
#define MP_GO		0x0001	/* Go command to ctlr */

#define ALLSTEPS	(MP_ERR | MP_STEP4 | MP_STEP3 | MP_STEP2 | MP_STEP1)

#define STEP0MASK	(ALLSTEPS | MP_NV)

#define STEP1MASK	(ALLSTEPS | MP_IE | MP_NCNRMASK)
#define STEP1GOOD	(MP_STEP2 | MP_IE | (NCMDL2 << 3) | NRSPL2)

#define STEP2MASK	(ALLSTEPS | MP_IE | MP_IVECMASK)
#define STEP2GOOD(iv)	(MP_STEP3 | MP_IE | (iv))

#define STEP3MASK	ALLSTEPS
#define STEP3GOOD	MP_STEP4


code:
/*	$OpenBSD: mscpvar.h,v 1.8 2015/07/04 10:27:05 dlg Exp $	*/
/*	$NetBSD: mscpvar.h,v 1.7 1999/06/06 19:16:18 ragge Exp $	*/
/*
 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * 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.
 *
 *	@(#)mscpvar.h	7.3 (Berkeley) 6/28/90
 */

/*
 * MSCP generic driver configuration
 */

/*
 * Enabling MSCP_PARANOIA makes the response code perform various checks
 * on the hardware.  (Right now it verifies only the buffer pointer in
 * mscp_cmdref.)
 *
 * Enabling AVOID_EMULEX_BUG selects an alternative method of identifying
 * transfers in progress, which gets around a rather peculiar bug in the
 * SC41/MS.  Enabling MSCP_PARANOIA instead should work, but will cause
 * `extra' Unibus resets.
 *
 * Either of these flags can simply be included as an `options' line in
 * your configuration file.
 */

/* #define MSCP_PARANOIA */
/* #define AVOID_EMULEX_BUG */

/*
 * Ring information, per ring (one each for commands and responses).
 */
struct mscp_ri {
	int	mri_size;		/* ring size */
	int	mri_next;		/* next (expected|free) */
	long	*mri_desc;		/* base address of descriptors */
	struct	mscp *mri_ring;		/* base address of packets */
};

/*
 * Transfer info, one per command packet.
 */
struct mscp_xi {
	bus_dmamap_t	mxi_dmam;	/* Allocated DMA map for this entry */
	struct buf *	mxi_bp;		/* Buffer used in this command */
	struct mscp *	mxi_mp;		/* Packet used in this command */
	int		mxi_inuse;
};

struct	mscp_ctlr {
					/* controller operation complete */
	void	(*mc_ctlrdone)(struct device *);
					/* device-specific start routine */
	void	(*mc_go)(struct device *, struct mscp_xi *);
					/* ctlr error handling */
	void	(*mc_saerror)(struct device *, int);
};

struct mscp_softc;

struct	mscp_device {
				/* error datagram */
	void	(*me_dgram)(struct device *, struct mscp *, struct mscp_softc *);
				/* normal I/O is done */
	void	(*me_iodone)(struct device *, struct buf *);
				/* drive on line */
	int	(*me_online)(struct device *, struct mscp *);
				/* got unit status */
	int	(*me_gotstatus)(struct device *, struct mscp *);
				/* replace done */
	void	(*me_replace)(struct device *, struct mscp *);
				/* read or write failed */
	int	(*me_ioerr)(struct device *, struct mscp *, struct buf *);
				/* B_BAD io done */
	void	(*me_bb)(struct device *, struct mscp *, struct buf *);
				/* Fill in mscp info for this drive */
	void	(*me_fillin)(struct buf *,struct mscp *);
				/* Non-data transfer operation is done */
	void	(*me_cmddone)(struct device *, struct mscp *);
};

/*
 * This struct is used when attaching a mscpbus.
 */
struct	mscp_attach_args {
	struct	mscp_ctlr *ma_mc;	/* Pointer to ctlr's mscp_ctlr */
	int	ma_type;		/* disk/tape bus type */
	struct	mscp_pack *ma_uda;	/* comm area virtual */
	struct	mscp_softc **ma_softc;	/* backpointer to bus softc */
	bus_dmamap_t	   ma_dmam;	/* This comm area dma info */
	bus_dma_tag_t	   ma_dmat;
	bus_space_tag_t	   ma_iot;
	bus_space_handle_t ma_iph;	/* initialisation and polling */
	bus_space_handle_t ma_sah;	/* status & address (read part) */
	bus_space_handle_t ma_swh;	/* status & address (write part) */
	short	ma_ivec;		/* Interrupt vector to use */
	char	ma_ctlrnr;		/* Phys ctlr nr */
	char	ma_adapnr;		/* Phys adapter nr */
};
#define MSCPBUS_DISK	001	/* Bus is used for disk mounts */
#define MSCPBUS_TAPE	002	/* Bus is used for tape mounts */
#define MSCPBUS_UDA	004	/* ctlr is disk on unibus/qbus */
#define MSCPBUS_KDB	010	/* ctlr is disk on BI */
#define MSCPBUS_KLE	020	/* ctlr is tape on unibus/qbus */

/*
 * Used when going for child devices.
 */
struct	drive_attach_args {
	struct	mscp *da_mp;	/* this devices response struct */
	int	da_typ;		/* Parent of type */
};

/*
 * Return values from functions.
 * MSCP_RESTARTED is peculiar to I/O errors.
 */
#define MSCP_DONE	0		/* all ok */
#define MSCP_FAILED	1		/* no go */
#define MSCP_RESTARTED	2		/* transfer restarted */

/*
 * Per device information.
 *
 * mi_ip is a pointer to the inverting pointers (things that get `ui's
 * given unit numbers) FOR THIS CONTROLLER (NOT the whole set!).
 *
 * b_actf holds a queue of those transfers that were started but have
 * not yet finished.  Other Unibus drivers do not need this as they hand
 * out requests one at a time.	MSCP devices, however, take a slew of
 * requests and pick their own order to execute them.  This means that
 * we have to have a place to move transfers that were given to the
 * controller, so we can tell those apart from those that have not yet
 * been handed out; b_actf is that place.
 */
struct mscp_softc {
	struct	device mi_dev;		/* Autoconf stuff */
	struct	mscp_ri mi_cmd;		/* MSCP command ring info */
	struct	mscp_ri mi_rsp;		/* MSCP response ring info */
	bus_dma_tag_t	mi_dmat;
	bus_dmamap_t	mi_dmam;
	struct	mscp_xi mi_xi[NCMD];
	int	mi_mxiuse;		/* Bitfield of inuse mxi packets */
	short	mi_credits;		/* transfer credits */
	char	mi_wantcmd;		/* waiting for command packet */
	char	mi_wantcredits;		/* waiting for transfer credits */
	struct	mscp_ctlr *mi_mc;	/* Pointer to parent's mscp_ctlr */
	struct	mscp_device *mi_me;	/* Pointer to child's mscp_device */
	struct	device **mi_dp;		/* array of backpointers */
	int	mi_driveno;		/* Max physical drive number found */
	char	mi_ctlrnr;		/* Phys ctlr nr */
	char	mi_adapnr;		/* Phys adapter nr */
	int	mi_flags;
	struct	mscp_pack *mi_uda;	/* virtual address */
	int	mi_type;
	short	mi_ivec;		/* Interrupt vector to use */
	short	mi_ierr;		/* Init err counter */
	bus_space_tag_t	   mi_iot;
	bus_space_handle_t mi_iph;	/* initialisation and polling */
	bus_space_handle_t mi_sah;	/* status & address (read part) */
	bus_space_handle_t mi_swh;	/* status & address (write part) */
	struct bufq mi_bufq;		/* While waiting for packets */
};

/* mi_flags */
#define MSC_STARTPOLL	1
#define MSC_INSTART	2
#define MSC_IGNOREINTR	4
#define MSC_READY	8

/*
 * We have run out of credits when mi_credits is <= MSCP_MINCREDITS.
 * It is still possible to issue one command in this case, but it must
 * not be a data transfer.  E.g., `get command status' or `abort command'
 * is legal, while `read' is not.
 */
#define MSCP_MINCREDITS 1

/*
 * Flags for mscp_getcp().
 */
#define MSCP_WAIT	1
#define MSCP_DONTWAIT	0

/*
 * Unit flags
 */
#define UNIT_ONLINE	0x01	/* drive is on line */
#define UNIT_HAVESTATUS 0x02	/* got unit status */
#define UNIT_REQUEUE	0x04	/* requeue after response */

/*
 * Handle a command ring transition: wake up sleepers for command packets.
 * This is too simple to bother with a function call.
 */
#define MSCP_DOCMD(mi) { \
	if ((mi)->mi_wantcmd) { \
		(mi)->mi_wantcmd = 0; \
		wakeup((caddr_t) &(mi)->mi_wantcmd); \
	} \
}

/* Prototypes */
struct	mscp *mscp_getcp(struct mscp_softc *, int);
void	mscp_printevent(struct mscp *);
void	mscp_go(struct mscp_softc *, struct mscp *, int);
void	mscp_requeue(struct mscp_softc *);
void	mscp_dorsp(struct mscp_softc *);
int	mscp_decodeerror(char *, struct mscp *, struct mscp_softc *);
int	mscp_print(void *, const char *);
void	mscp_hexdump(struct mscp *);
void	mscp_strategy(struct buf *, struct device *);
void	mscp_printtype(int, int);
int	mscp_waitstep(struct mscp_softc *, int, int);
void	mscp_dgo(struct mscp_softc *, struct mscp_xi *);
void	mscp_intr(struct mscp_softc *);


code: src/sys/arch/vax/qbus

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