code:
/*	$OpenBSD: if_le.c,v 1.3 2014/08/21 14:24:08 mpi Exp $ */

/*
 * Copyright (c) 1995 Theo de Raadt
 *
 * 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.
 *
 * 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.
 *
 * Copyright (c) 1993 Adam Glass
 * 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 by Adam Glass.
 * 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 Adam Glass ``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.
 */

#include <sys/param.h>
#include <sys/types.h>

#include <netinet/in.h>

#include <machine/prom.h>

#include "stand.h"
#include "libsa.h"
#include "net.h"
#include "netif.h"

#include "if_lereg.h"

int     le_debug = 0;

void le_end(struct netif *);
void le_error(struct netif *, char *, volatile struct lereg1 *);
int le_get(struct iodesc *, void *, size_t, time_t);
void le_init(struct iodesc *, void *);
int le_match(struct netif *, void *);
int le_poll(struct iodesc *, void *, int);
int le_probe(struct netif *, void *);
int le_put(struct iodesc *, void *, size_t);
void le_reset(struct netif *, u_char *);
extern void machdep_common_ether(u_char *);

struct netif_stats le_stats;

struct netif_dif le0_dif = {
	0,			/* unit */
	1,			/* nsel */
	&le_stats,
	0,
	0,
};

struct netif_driver le_driver = {
	"le",			/* netif_bname */
	le_match,		/* match */
	le_probe,		/* probe */
	le_init,		/* init */
	le_get,			/* get */
	le_put,			/* put */
	le_end,			/* end */
	&le0_dif,		/* netif_ifs */
	1,			/* netif_nifs */
};

struct le_configuration {
	unsigned int phys_addr;
	int     used;
} le_config[] = {
	{ 0xfff8c000,	0 }	/* AV400 */
};

int     nle_config = sizeof(le_config) / (sizeof(le_config[0]));

struct {
	struct lereg1 *sc_r1;	/* LANCE registers */
	struct lereg2 *sc_r2;	/* RAM */
	int     next_rmd;
	int     next_tmd;
}       le_softc;

int
le_match(nif, machdep_hint)
	struct netif *nif;
	void   *machdep_hint;
{
	char   *name;
	int     i, val = 0;

	name = machdep_hint;
	if (name && !bcmp(le_driver.netif_bname, name, 2))
		val += 10;
	for (i = 0; i < nle_config; i++) {
		if (le_config[i].used)
			continue;
		if (le_debug)
			printf("le%d: le_match --> %d\n", i, val + 1);
		le_config[i].used++;
		return val + 1;
	}
	if (le_debug)
		printf("le%d: le_match --> 0\n", i);
	return 0;
}

int
le_probe(nif, machdep_hint)
	struct netif *nif;
	void   *machdep_hint;
{

	/* the set unit is the current unit */
	if (le_debug)
		printf("le%d: le_probe called\n", nif->nif_unit);

	return 0;
}

void
le_error(nif, str, ler1)
	struct netif *nif;
	char   *str;
	volatile struct lereg1 *ler1;
{
	/* ler1->ler1_rap = LE_CSRO done in caller */
	if (ler1->ler1_rdp & LE_C0_BABL)
		panic("le%d: been babbling, found by '%s'", nif->nif_unit, str);
	if (ler1->ler1_rdp & LE_C0_CERR) {
		le_stats.collision_error++;
		ler1->ler1_rdp = LE_C0_CERR;
	}
	if (ler1->ler1_rdp & LE_C0_MISS) {
		le_stats.missed++;
		ler1->ler1_rdp = LE_C0_MISS;
	}
	if (ler1->ler1_rdp & LE_C0_MERR) {
		printf("le%d: memory error in '%s'\n", nif->nif_unit, str);
		panic("memory error");
	}
}

void
le_reset(nif, myea)
	struct netif *nif;
	u_char *myea;
{
	struct lereg1 *ler1 = le_softc.sc_r1;
	struct lereg2 *ler2 = le_softc.sc_r2;
	unsigned int a;
	int timo = 100000, stat = 0, i;

	if (le_debug)
		printf("le%d: le_reset called\n", nif->nif_unit);
	ler1->ler1_rap = LE_CSR0;
	ler1->ler1_rdp = LE_C0_STOP;	/* do nothing until we are finished */

	bzero(ler2, sizeof(*ler2));

	ler2->ler2_mode = LE_MODE_NORMAL;
	ler2->ler2_padr[0] = myea[1];
	ler2->ler2_padr[1] = myea[0];
	ler2->ler2_padr[2] = myea[3];
	ler2->ler2_padr[3] = myea[2];
	ler2->ler2_padr[4] = myea[5];
	ler2->ler2_padr[5] = myea[4];

	ler2->ler2_ladrf0 = 0;
	ler2->ler2_ladrf1 = 0;

	a = (u_int) ler2->ler2_rmd;
	ler2->ler2_rlen = LE_RLEN | (a >> 16);
	ler2->ler2_rdra = a & LE_ADDR_LOW_MASK;

	a = (u_int) ler2->ler2_tmd;
	ler2->ler2_tlen = LE_TLEN | (a >> 16);
	ler2->ler2_tdra = a & LE_ADDR_LOW_MASK;

	ler1->ler1_rap = LE_CSR1;
	a = (u_int) ler2;
	ler1->ler1_rdp = a & LE_ADDR_LOW_MASK;
	ler1->ler1_rap = LE_CSR2;
	ler1->ler1_rdp = a >> 16;

	for (i = 0; i < LERBUF; i++) {
		a = (u_int) & ler2->ler2_rbuf[i];
		ler2->ler2_rmd[i].rmd0 = a & LE_ADDR_LOW_MASK;
		ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN;
		ler2->ler2_rmd[i].rmd1_hadr = a >> 16;
		ler2->ler2_rmd[i].rmd2 = -LEMTU;
		ler2->ler2_rmd[i].rmd3 = 0;
	}
	for (i = 0; i < LETBUF; i++) {
		a = (u_int) & ler2->ler2_tbuf[i];
		ler2->ler2_tmd[i].tmd0 = a & LE_ADDR_LOW_MASK;
		ler2->ler2_tmd[i].tmd1_bits = 0;
		ler2->ler2_tmd[i].tmd1_hadr = a >> 16;
		ler2->ler2_tmd[i].tmd2 = 0;
		ler2->ler2_tmd[i].tmd3 = 0;
	}

	ler1->ler1_rap = LE_CSR3;
	ler1->ler1_rdp = LE_C3_BSWP;

	ler1->ler1_rap = LE_CSR0;
	ler1->ler1_rdp = LE_C0_INIT;
	do {
		if (--timo == 0) {
			printf("le%d: init timeout, stat = 0x%x\n",
			    nif->nif_unit, stat);
			break;
		}
		stat = ler1->ler1_rdp;
	} while ((stat & LE_C0_IDON) == 0);

	ler1->ler1_rdp = LE_C0_IDON;
	le_softc.next_rmd = 0;
	le_softc.next_tmd = 0;
	ler1->ler1_rap = LE_CSR0;
	ler1->ler1_rdp = LE_C0_STRT;
}

int
le_poll(desc, pkt, len)
	struct iodesc *desc;
	void   *pkt;
	int     len;
{
	struct lereg1 *ler1 = le_softc.sc_r1;
	struct lereg2 *ler2 = le_softc.sc_r2;
	unsigned int a;
	int     length;
	struct lermd *rmd;

	ler1->ler1_rap = LE_CSR0;
	if ((ler1->ler1_rdp & LE_C0_RINT) != 0)
		ler1->ler1_rdp = LE_C0_RINT;
	rmd = &ler2->ler2_rmd[le_softc.next_rmd];
	if (rmd->rmd1_bits & LE_R1_OWN) {
		return (0);
	}
	if (ler1->ler1_rdp & LE_C0_ERR)
		le_error(desc->io_netif, "le_poll", ler1);
	if (rmd->rmd1_bits & LE_R1_ERR) {
		printf("le%d_poll: rmd status 0x%x\n", desc->io_netif->nif_unit,
		    rmd->rmd1_bits);
		length = 0;
		goto cleanup;
	}
	if ((rmd->rmd1_bits & (LE_R1_STP | LE_R1_ENP)) != (LE_R1_STP | LE_R1_ENP))
		panic("le_poll: chained packet");

	length = rmd->rmd3;
	if (length >= LEMTU) {
		length = 0;
		panic("csr0 when bad things happen: %x", ler1->ler1_rdp);
		goto cleanup;
	}
	if (!length)
		goto cleanup;
	length -= 4;
	if (length > 0) {
		/*
	         * if buffer is smaller than the packet truncate it.
	         * (is this wise?)
	         */
		if (length > len)
			length = len;

		bcopy((void *)&ler2->ler2_rbuf[le_softc.next_rmd], pkt, length);
	}
cleanup:
	a = (u_int) & ler2->ler2_rbuf[le_softc.next_rmd];
	rmd->rmd0 = a & LE_ADDR_LOW_MASK;
	rmd->rmd1_hadr = a >> 16;
	rmd->rmd2 = -LEMTU;
	le_softc.next_rmd =
	    (le_softc.next_rmd == (LERBUF - 1)) ? 0 : (le_softc.next_rmd + 1);
	rmd->rmd1_bits = LE_R1_OWN;
	return length;
}

int
le_put(desc, pkt, len)
	struct	iodesc *desc;
	void	*pkt;
	size_t	len;
{
	volatile struct lereg1 *ler1 = le_softc.sc_r1;
	volatile struct lereg2 *ler2 = le_softc.sc_r2;
	volatile struct letmd *tmd;
	int timo = 100000, stat = 0;
	unsigned int a;

	ler1->ler1_rap = LE_CSR0;
	if (ler1->ler1_rdp & LE_C0_ERR)
		le_error(desc->io_netif, "le_put(way before xmit)", ler1);
	tmd = &ler2->ler2_tmd[le_softc.next_tmd];
	while (tmd->tmd1_bits & LE_T1_OWN) {
		printf("le%d: output buffer busy\n", desc->io_netif->nif_unit);
	}
	bcopy(pkt, (void *)ler2->ler2_tbuf[le_softc.next_tmd], len);
	if (len < 64)
		tmd->tmd2 = -64;
	else
		tmd->tmd2 = -len;
	tmd->tmd3 = 0;
	if (ler1->ler1_rdp & LE_C0_ERR)
		le_error(desc->io_netif, "le_put(before xmit)", ler1);
	tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP | LE_T1_OWN;
	a = (u_int) & ler2->ler2_tbuf[le_softc.next_tmd];
	tmd->tmd0 = a & LE_ADDR_LOW_MASK;
	tmd->tmd1_hadr = a >> 16;
	ler1->ler1_rdp = LE_C0_TDMD;
	if (ler1->ler1_rdp & LE_C0_ERR)
		le_error(desc->io_netif, "le_put(after xmit)", ler1);
	do {
		if (--timo == 0) {
			printf("le%d: transmit timeout, stat = 0x%x\n",
			    desc->io_netif->nif_unit, stat);
			if (ler1->ler1_rdp & LE_C0_ERR)
				le_error(desc->io_netif, "le_put(timeout)", ler1);
			break;
		}
		stat = ler1->ler1_rdp;
	} while ((stat & LE_C0_TINT) == 0);
	ler1->ler1_rdp = LE_C0_TINT;
	if (ler1->ler1_rdp & LE_C0_ERR) {
		if ((ler1->ler1_rdp & (LE_C0_BABL | LE_C0_CERR | LE_C0_MISS |
		    LE_C0_MERR)) !=
		    LE_C0_CERR)
			printf("le_put: xmit error, buf %d\n", le_softc.next_tmd);
		le_error(desc->io_netif, "le_put(xmit error)", ler1);
	}
	le_softc.next_tmd = 0;
/*	(le_softc.next_tmd == (LETBUF - 1)) ? 0 : le_softc.next_tmd + 1;*/
	if (tmd->tmd1_bits & LE_T1_DEF)
		le_stats.deferred++;
	if (tmd->tmd1_bits & LE_T1_ONE)
		le_stats.collisions++;
	if (tmd->tmd1_bits & LE_T1_MORE)
		le_stats.collisions += 2;
	if (tmd->tmd1_bits & LE_T1_ERR) {
		printf("le%d: transmit error, error = 0x%x\n", desc->io_netif->nif_unit,
		    tmd->tmd3);
		return -1;
	}
	if (le_debug) {
		printf("le%d: le_put() successful: sent %ld\n",
		    desc->io_netif->nif_unit, len);
		printf("le%d: le_put(): tmd1_bits: %x tmd3: %x\n",
		    desc->io_netif->nif_unit,
		    (unsigned int) tmd->tmd1_bits,
		    (unsigned int) tmd->tmd3);
	}
	return len;
}

int
le_get(desc, pkt, len, timeout)
	struct	iodesc *desc;
	void	*pkt;
	size_t	len;
	time_t	timeout;
{
	time_t  t;
	int     cc;

	t = getsecs();
	cc = 0;
	while (((getsecs() - t) < timeout) && !cc) {
		cc = le_poll(desc, pkt, len);
	}
	return cc;
}
/*
 * init le device.   return 0 on failure, 1 if ok.
 */
void
le_init(desc, machdep_hint)
	struct iodesc *desc;
	void   *machdep_hint;
{
	u_long eram = STAGE1_RELOC;
	struct netif *nif = desc->io_netif;

	if (le_debug)
		printf("le%d: le_init called\n", desc->io_netif->nif_unit);
	machdep_common_ether(desc->myea);
	bzero(&le_softc, sizeof(le_softc));
	le_softc.sc_r1 =
	    (struct lereg1 *) le_config[desc->io_netif->nif_unit].phys_addr;
	le_softc.sc_r2 = (struct lereg2 *) (eram - (1024 * 1024));
	le_reset(desc->io_netif, desc->myea);
	printf("device: %s%d attached to %s\n", nif->nif_driver->netif_bname,
	    nif->nif_unit, ether_sprintf(desc->myea));
}

void
le_end(nif)
	struct netif *nif;
{
	struct lereg1 *ler1 = le_softc.sc_r1;

	if (le_debug)
		printf("le%d: le_end called\n", nif->nif_unit);
	ler1->ler1_rap = LE_CSR0;
	ler1->ler1_rdp = LE_C0_STOP;
}


code:
/*	$OpenBSD: if_lereg.h,v 1.1 2013/10/17 16:30:07 miod Exp $ */

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

#define	LEMTU		1518
#define	LEMINSIZE	60	/* should be 64 if mode DTCR is set */
#define	LERBUF		8
#define	LERBUFLOG2	3
#define	LE_RLEN		(LERBUFLOG2 << 13)
#define	LETBUF		1
#define	LETBUFLOG2	0
#define	LE_TLEN		(LETBUFLOG2 << 13)

/*
 * LANCE registers. Although there are 16 bit registers, on the AV400
 * design, they need to be accessed as 32 bit registers. Bus magic...
 */
struct lereg1 {
	volatile u_int32_t ler1_rdp;	/* register data port */
	volatile u_int32_t ler1_rap;	/* register address port */
};

/* register addresses */
#define	LE_CSR0		0	/* Control and status register */
#define	LE_CSR1		1	/* low address of init block */
#define	LE_CSR2		2	/* high address of init block */
#define	LE_CSR3		3	/* Bus master and control */

/* Control and status register 0 (csr0) */
#define	LE_C0_ERR	0x8000	/* error summary */
#define	LE_C0_BABL	0x4000	/* transmitter timeout error */
#define	LE_C0_CERR	0x2000	/* collision */
#define	LE_C0_MISS	0x1000	/* missed a packet */
#define	LE_C0_MERR	0x0800	/* memory error */
#define	LE_C0_RINT	0x0400	/* receiver interrupt */
#define	LE_C0_TINT	0x0200	/* transmitter interrupt */
#define	LE_C0_IDON	0x0100	/* initialization done */
#define	LE_C0_INTR	0x0080	/* interrupt condition */
#define	LE_C0_INEA	0x0040	/* interrupt enable */
#define	LE_C0_RXON	0x0020	/* receiver on */
#define	LE_C0_TXON	0x0010	/* transmitter on */
#define	LE_C0_TDMD	0x0008	/* transmit demand */
#define	LE_C0_STOP	0x0004	/* disable all external activity */
#define	LE_C0_STRT	0x0002	/* enable external activity */
#define	LE_C0_INIT	0x0001	/* begin initialization */

#define LE_C0_BITS \
    "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\
\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT"

/* Control and status register 3 (csr3) */
#define	LE_C3_BSWP	0x4	/* byte swap */
#define	LE_C3_ACON	0x2	/* ALE control, eh? */
#define	LE_C3_BCON	0x1	/* byte control */
/*
 * Current size is 13,758 bytes with 8 x 1518 receive buffers and
 * 1 x 1518 transmit buffer.
 */
struct lereg2 {
	/* initialization block */
	volatile u_short ler2_mode;	/* mode */
	volatile u_char ler2_padr[6];	/* physical address */
#ifdef new_code
	volatile u_short ler2_ladrf[4];	/* logical address filter */
#else
	volatile u_long ler2_ladrf0;	/* logical address filter */
	volatile u_long ler2_ladrf1;	/* logical address filter */
#endif
	volatile u_short ler2_rdra;	/* receive descriptor addr */
	volatile u_short ler2_rlen;	/* rda high and ring size */
	volatile u_short ler2_tdra;	/* transmit descriptor addr */
	volatile u_short ler2_tlen;	/* tda high and ring size */
	/* receive message descriptors. bits/hadr are byte order dependent. */
	struct lermd {
		volatile u_short rmd0;	/* low address of packet */
		volatile u_char rmd1_bits;	/* descriptor bits */
		volatile u_char rmd1_hadr;	/* high address of packet */
		volatile short rmd2;	/* buffer byte count */
		volatile u_short rmd3;	/* message byte count */
	}       ler2_rmd[LERBUF];
	/* transmit message descriptors */
	struct letmd {
		volatile u_short tmd0;	/* low address of packet */
		volatile u_char tmd1_bits;	/* descriptor bits */
		volatile u_char tmd1_hadr;	/* high address of packet */
		volatile short tmd2;	/* buffer byte count */
		volatile u_short tmd3;	/* transmit error bits */
	}       ler2_tmd[LETBUF];
	volatile char ler2_rbuf[LERBUF][LEMTU];
	volatile char ler2_tbuf[LETBUF][LEMTU];
};
/* Initialzation block (mode) */
#define	LE_MODE_PROM	0x8000	/* promiscuous mode */
/*			0x7f80		   reserved, must be zero */
#define	LE_MODE_INTL	0x0040	/* internal loopback */
#define	LE_MODE_DRTY	0x0020	/* disable retry */
#define	LE_MODE_COLL	0x0010	/* force a collision */
#define	LE_MODE_DTCR	0x0008	/* disable transmit CRC */
#define	LE_MODE_LOOP	0x0004	/* loopback mode */
#define	LE_MODE_DTX	0x0002	/* disable transmitter */
#define	LE_MODE_DRX	0x0001	/* disable receiver */
#define	LE_MODE_NORMAL	0	/* none of the above */

/* Receive message descriptor 1 (rmd1_bits) */
#define	LE_R1_OWN	0x80	/* LANCE owns the packet */
#define	LE_R1_ERR	0x40	/* error summary */
#define	LE_R1_FRAM	0x20	/* framing error */
#define	LE_R1_OFLO	0x10	/* overflow error */
#define	LE_R1_CRC	0x08	/* CRC error */
#define	LE_R1_BUFF	0x04	/* buffer error */
#define	LE_R1_STP	0x02	/* start of packet */
#define	LE_R1_ENP	0x01	/* end of packet */

#define LE_R1_BITS \
    "\20\10OWN\7ERR\6FRAM\5OFLO\4CRC\3BUFF\2STP\1ENP"

/* Transmit message descriptor 1 (tmd1_bits) */
#define	LE_T1_OWN	0x80	/* LANCE owns the packet */
#define	LE_T1_ERR	0x40	/* error summary */
#define	LE_T1_MORE	0x10	/* multiple collisions */
#define	LE_T1_ONE	0x08	/* single collision */
#define	LE_T1_DEF	0x04	/* defferred transmit */
#define	LE_T1_STP	0x02	/* start of packet */
#define	LE_T1_ENP	0x01	/* end of packet */

#define LE_T1_BITS \
    "\20\10OWN\7ERR\6RES\5MORE\4ONE\3DEF\2STP\1ENP"

/* Transmit message descriptor 3 (tmd3) */
#define	LE_T3_BUFF	0x8000	/* buffer error */
#define	LE_T3_UFLO	0x4000	/* underflow error */
#define	LE_T3_LCOL	0x1000	/* late collision */
#define	LE_T3_LCAR	0x0800	/* loss of carrier */
#define	LE_T3_RTRY	0x0400	/* retry error */
#define	LE_T3_TDR_MASK	0x03ff	/* time domain reflectometry counter */

#define LE_XMD2_ONES	0xf000

#define LE_T3_BITS \
    "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY"

#define LE_ADDR_LOW_MASK (0xffff)

license: unknown


code:
/*	$OpenBSD: oaic.c,v 1.3 2014/07/12 19:01:49 tedu Exp $	*/

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

/*
 * Derived from sys/dev/ic/aic6360.c under the following licence terms:
 */
/*	OpenBSD: aic6360.c,v 1.26 2011/04/03 12:42:36 krw Exp	*/
/*	$NetBSD: aic6360.c,v 1.52 1996/12/10 21:27:51 thorpej Exp $	*/
/*
 * Copyright (c) 1994, 1995, 1996 Charles Hannum.  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 by Charles M. Hannum.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Copyright (c) 1994 Jarle Greipsland
 * 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. 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.
 */

/*
 * Acknowledgements: Many of the algorithms used in this driver are
 * inspired by the work of Julian Elischer (julian@tfs.com) and
 * Charles Hannum (mycroft@duality.gnu.ai.mit.edu).  Thanks a million!
 */

/* TODO list:
 * 1) Get the DMA stuff working.
 * 2) Get the synch stuff working (requires DMA first).
 */

#include <sys/param.h>
#include <stand.h>
#include "libsa.h"

#include "scsi.h"

#include <dev/ic/aic6250reg.h>

/*
 * ACB. Holds additional information for each SCSI command Comments: We
 * need a separate scsi command block because we may need to overwrite it
 * with a request sense command.  Basicly, we refrain from fiddling with
 * the scsi_xfer struct (except do the expected updating of return values).
 * We'll generally update: xs->{flags,resid,error,sense,status} and
 * occasionally xs->retries.
 */
struct aic6250_acb {
	struct scsi_generic *scsi_cmd;
	int scsi_cmd_length;
	void *data_addr;		/* Saved data pointer */
	int data_length;		/* Residue */

	uint8_t target_stat;		/* SCSI status byte */

	int xsflags;
	int error;
};

struct aic6250_softc {
	uint32_t	sc_baseaddr;

	int		sc_tgtid;
	int		sc_tgtlun;

	struct aic6250_acb *sc_nexus;	/* current command */
	struct aic6250_acb sc_acb;

	/* Data about the current nexus (updated for every cmd switch) */
	u_char	*sc_dp;		/* Current data pointer */
	size_t	sc_dleft;	/* Data bytes left to transfer */
	u_char	*sc_cp;		/* Current command pointer */
	size_t	sc_cleft;	/* Command bytes left to transfer */

	/* Adapter state */
	uint8_t	 sc_phase;	/* Current bus phase */
	uint8_t	 sc_prevphase;	/* Previous bus phase */
	uint8_t	 sc_state;	/* State applicable to the adapter */
#define	AIC_INIT	0
#define AIC_IDLE	1
#define AIC_SELECTING	2	/* SCSI command is arbiting  */
#define AIC_RESELECTED	3	/* Has been reselected */
#define AIC_CONNECTED	4	/* Actively using the SCSI bus */
#define	AIC_DISCONNECT	5	/* MSG_DISCONNECT received */
#define	AIC_CMDCOMPLETE	6	/* MSG_CMDCOMPLETE received */
#define AIC_CLEANING	7
	uint8_t	 sc_flags;
#define AIC_DROP_MSGIN	0x01	/* Discard all msgs (parity err detected) */
#define	AIC_ABORTING	0x02	/* Bailing out */
	uint8_t	sc_selid;	/* Reselection ID */

	uint8_t sc_imr0;
	uint8_t sc_imr1;
	uint8_t	sc_cr0;
	uint8_t	sc_cr1;
	uint	sc_selto;	/* Selection timeout (when polling) */

	/* Message stuff */
	uint8_t	sc_msgpriq;	/* Messages we want to send */
	uint8_t	sc_msgoutq;	/* Messages sent during last MESSAGE OUT */
	uint8_t	sc_lastmsg;	/* Message last transmitted */
	uint8_t	sc_currmsg;	/* Message currently ready to transmit */
#define SEND_DEV_RESET		0x01
#define SEND_PARITY_ERROR	0x02
#define SEND_INIT_DET_ERR	0x04
#define SEND_REJECT		0x08
#define SEND_IDENTIFY  		0x10
#define SEND_ABORT		0x20
#define AIC_MAX_MSG_LEN 8
	uint8_t  sc_omess[AIC_MAX_MSG_LEN];
	uint8_t	*sc_omp;		/* Outgoing message pointer */
	uint8_t	sc_imess[AIC_MAX_MSG_LEN];
	uint8_t	*sc_imp;		/* Incoming message pointer */

	/* Hardware stuff */
	int	sc_initiator;		/* Our scsi id */
	int	sc_freq;		/* Clock frequency in MHz */
};

int	aic6250_intr(void *);

void 	aic6250_init(struct aic6250_softc *);
void	aic6250_done(struct aic6250_softc *, struct aic6250_acb *);
void	aic6250_sched_msgout(struct aic6250_softc *, uint8_t);
void	aic6250_select(struct aic6250_softc *, struct aic6250_acb *);
void	aic6250_seltimeout(void *);
void	aic6250_sched(struct aic6250_softc *);
void	aic6250_scsi_reset(struct aic6250_softc *);
void	aic6250_reset(struct aic6250_softc *);
void	aic6250_acb_free(void *, void *);
void	*aic6250_acb_alloc(void *);
int	aic6250_reselect(struct aic6250_softc *, int);
void	aic6250_msgin(struct aic6250_softc *);
void	aic6250_msgout(struct aic6250_softc *);
void	aic6250_ack(struct aic6250_softc *);
int	aic6250_dataout_pio(struct aic6250_softc *, uint8_t *, int, int);
int	aic6250_datain_pio(struct aic6250_softc *, uint8_t *, int, int);

#define	oaic_read(sc, addr) \
	(*(volatile uint32_t *)((sc)->sc_baseaddr + ((addr) << 2)) & 0xff)
#define	oaic_write(sc, addr, val) \
	*(volatile uint32_t *)((sc)->sc_baseaddr + ((addr) << 2)) = (val)

/*
 * Attach the AIC6250, fill out some high and low level data structures
 */
void *
oaic_attach(uint32_t addr, int id, int lun)
{
	struct aic6250_softc *sc;

	sc = (struct aic6250_softc *)alloc(sizeof *sc);
	if (sc == NULL)
		return NULL;

	memset(sc, 0, sizeof *sc);
	sc->sc_baseaddr = addr;
	/* XXX */
	sc->sc_freq = 10;
	sc->sc_initiator = 7;
	sc->sc_cr0 = AIC_CR0_EN_PORT_A;
	sc->sc_cr1 = AIC_CR1_ENABLE_16BIT_MEM_BUS;
	/* XXX */

	sc->sc_state = AIC_INIT;

	if (sc->sc_freq >= 20)
		sc->sc_cr1 |= AIC_CR1_CLK_FREQ_MODE;

	aic6250_init(sc);	/* init chip and driver */

	sc->sc_tgtid = id;
	sc->sc_tgtlun = lun;

	return sc;
}

void
oaic_detach(void *cookie)
{
	free(cookie, sizeof(struct aic6250_softc));
}

/*
 * Initialize AIC6250 chip itself.
 */
void
aic6250_reset(struct aic6250_softc *sc)
{
	/* reset chip */
	oaic_write(sc, AIC_CONTROL_REG1, AIC_CR1_CHIP_SW_RESET);
	delay(200);
	oaic_write(sc, AIC_CONTROL_REG1, 0);

	oaic_write(sc, AIC_CONTROL_REG1, sc->sc_cr1);
	oaic_write(sc, AIC_CONTROL_REG0, sc->sc_cr0 | sc->sc_initiator);
	/* asynchronous operation */
	oaic_write(sc, AIC_OFFSET_CNTRL, 0);

	sc->sc_imr0 = sc->sc_imr1 = 0;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

	oaic_write(sc, AIC_DMA_BYTE_COUNT_L, 0);
	oaic_write(sc, AIC_DMA_BYTE_COUNT_M, 0);
	oaic_write(sc, AIC_DMA_BYTE_COUNT_H, 0);
	oaic_write(sc, AIC_DMA_CNTRL, 0);
	oaic_write(sc, AIC_PORT_A, 0);
	oaic_write(sc, AIC_PORT_B, 0);
}

/* Pull the SCSI RST line for 500 us */
void
aic6250_scsi_reset(struct aic6250_softc *sc)
{
	/* reset SCSI bus */
	oaic_write(sc, AIC_CONTROL_REG1,
	    sc->sc_cr1 | AIC_CR1_SCSI_RST_OUT);
	delay(500);
	oaic_write(sc, AIC_CONTROL_REG1, sc->sc_cr1);
	delay(50);
}

/*
 * Initialize aic SCSI driver.
 */
void
aic6250_init(struct aic6250_softc *sc)
{
	struct aic6250_acb *acb;

	aic6250_reset(sc);
	aic6250_scsi_reset(sc);
	aic6250_reset(sc);

	if (sc->sc_state == AIC_INIT) {
		/* First time through; initialize. */
		sc->sc_nexus = NULL;
	} else {
		/* Cancel any active commands. */
		sc->sc_state = AIC_CLEANING;
		if ((acb = sc->sc_nexus) != NULL) {
			acb->error = XS_DRIVER_STUFFUP;
			aic6250_done(sc, acb);
		}
	}

	sc->sc_prevphase = PH_INVALID;
	sc->sc_state = AIC_IDLE;
	sc->sc_imr0 = AIC_IMR_EN_ERROR_INT;
	sc->sc_imr1 = AIC_IMR1_EN_SCSI_RST_INT;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);
}

/*
 * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS
 */

/*
 * Expected sequence:
 * 1) Command inserted into ready list
 * 2) Command selected for execution
 * 3) Command won arbitration and has selected target device
 * 4) Send message out (identify message, eventually also sync.negotiations)
 * 5) Send command
 * 5a) Receive disconnect message, disconnect.
 * 5b) Reselected by target
 * 5c) Receive identify message from target.
 * 6) Send or receive data
 * 7) Receive status
 * 8) Receive message (command complete etc.)
 * 9) If status == SCSI_CHECK construct a synthetic request sense SCSI cmd.
 *    Repeat 2-8 (no disconnects please...)
 */

/*
 * Start a SCSI-command
 * This function is called by the higher level SCSI-driver to queue/run
 * SCSI-commands.
 */
int
oaic_scsicmd(void *cookie, void *cmdbuf, size_t cmdlen, void *databuf,
    size_t datalen, size_t *resid)
{
	struct aic6250_softc *sc = cookie;
	struct aic6250_acb *acb = &sc->sc_acb;

	if (resid != NULL)
		*resid = 0;

	acb->xsflags = datalen != 0 ? SCSI_DATA_IN : 0;	/* XXX */
	acb->scsi_cmd = cmdbuf;
	acb->scsi_cmd_length = cmdlen;
	acb->data_addr = databuf;
	acb->data_length = datalen;
	acb->target_stat = 0;

	aic6250_sched(sc);

	for (;;) {
		uint8_t sr0, sr1, sr0mask, sr1mask;

		/*
		 * If we had interrupts enabled, would we
		 * have got an interrupt?
		 */
		sr0mask = 0;
		sr1mask = 0;

		if (sc->sc_imr0 & AIC_IMR_EN_ERROR_INT)
			sr1mask |= AIC_SR1_ERROR;
		if (sc->sc_imr0 & AIC_IMR_EN_CMD_DONE_INT)
			sr1mask |= AIC_SR1_CMD_DONE;
		if (sc->sc_imr0 & AIC_IMR_EN_SEL_OUT_INT)
			sr1mask |= AIC_SR1_SEL_OUT;
		if (sc->sc_imr0 & AIC_IMR_EN_RESEL_INT)
			sr1mask |= AIC_SR1_RESELECTED;
		if (sc->sc_imr0 & AIC_IMR_EN_SELECT_INT)
			sr1mask |= AIC_SR1_SELECTED;

		if (sc->sc_imr1 & AIC_IMR1_EN_SCSI_RST_INT)
			sr0mask |= AIC_SR0_SCSI_RST_OCCURED;
#if 0 /* these bits are never set */
		if (sc->sc_imr1 & AIC_IMR1_EN_MEM_PARITY_ERR_INT)
			sr0mask |= AIC_SR0_MEMORY_PARITY_ERR;
		if (sc->sc_imr1 & AIC_IMR1_EN_PHASE_MISMATCH_INT)
			sr0mask |= AIC_SR0_PHASE_MISMATCH_ERR;
#endif
		if (sc->sc_imr1 & AIC_IMR1_EN_BUS_FREE_DETECT_INT)
			sr0mask |= AIC_SR0_BUS_FREE_DETECT;
		if (sc->sc_imr1 & AIC_IMR1_EN_SCSI_PARITY_ERR_INT)
			sr0mask |= AIC_SR0_SCSI_PARITY_ERR;
		if (sc->sc_imr1 & AIC_IMR1_EN_PHASE_CHANGE_INT)
			sr0mask |= AIC_SR0_SCSI_PHASE_CHG_ATTN;

		sr0 = oaic_read(sc, AIC_STATUS_REG0);
		sr1 = oaic_read(sc, AIC_STATUS_REG1);

		if ((sr0 & sr0mask) != 0 || (sr1 & sr1mask) != 0) {
			aic6250_intr(sc);
		}
		if ((acb->xsflags & ITSDONE) != 0)
			break;
		delay(1000);

		/* process the selection timeout timer as well if necessary */
		if (sc->sc_selto != 0) {
			sc->sc_selto--;
			if (sc->sc_selto == 0) {
				aic6250_seltimeout(sc);
			}
		}
	}

	if (resid != NULL && acb->error == 0)
		*resid = datalen;

	return acb->error;
}

/*
 * LOW LEVEL SCSI UTILITIES
 */

void
aic6250_ack(struct aic6250_softc *sc)
{
	oaic_write(sc, AIC_SCSI_SIGNAL_REG,
	    oaic_read(sc, AIC_SCSI_SIGNAL_REG) | AIC_SS_ACK_OUT);
	while ((oaic_read(sc, AIC_SCSI_SIGNAL_REG) & AIC_SS_REQ_IN) != 0)
		continue;
	oaic_write(sc, AIC_SCSI_SIGNAL_REG,
	    oaic_read(sc, AIC_SCSI_SIGNAL_REG) & ~AIC_SS_ACK_OUT);
}

void
aic6250_sched_msgout(struct aic6250_softc *sc, uint8_t m)
{
	if (sc->sc_msgpriq == 0)
		oaic_write(sc, AIC_SCSI_SIGNAL_REG,
		    sc->sc_phase | AIC_SS_ATN_OUT);
	sc->sc_msgpriq |= m;
}

/*
 * Start a selection.  This is used by aic6250_sched() to select an idle target,
 * and by aic6250_done() to immediately reselect a target to get sense
 * information.
 */
void
aic6250_select(struct aic6250_softc *sc, struct aic6250_acb *acb)
{
	oaic_write(sc, AIC_SCSI_ID_DATA,
	    (1 << sc->sc_initiator) | (1 << sc->sc_tgtid));

	/* Always enable reselections. */
	sc->sc_imr1 |= AIC_IMR1_EN_SCSI_RST_INT;
	sc->sc_imr1 &=
	    ~(AIC_IMR1_EN_SCSI_REQ_ON_INT | AIC_IMR1_EN_SCSI_PARITY_ERR_INT |
	      AIC_IMR1_EN_BUS_FREE_DETECT_INT | AIC_IMR1_EN_PHASE_CHANGE_INT);
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);
	sc->sc_imr0 = AIC_IMR_ARB_SEL_START | AIC_IMR_EN_ERROR_INT |
	    AIC_IMR_EN_CMD_DONE_INT | AIC_IMR_EN_SEL_OUT_INT |
	    AIC_IMR_EN_RESEL_INT | AIC_IMR_EN_SELECT_INT;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);

	sc->sc_state = AIC_SELECTING;
}

int
aic6250_reselect(struct aic6250_softc *sc, int message)
{
	uint8_t selid;
	struct aic6250_acb *acb;

	/*
	 * The SCSI chip made a snapshot of the data bus while the reselection
	 * was being negotiated.  This enables us to determine which target did
	 * the reselect.
	 */
	selid = sc->sc_selid & ~(1 << sc->sc_initiator);
	if (selid & (selid - 1)) {
		printf("insc: reselect with invalid selid %02x\n", selid);
		goto reset;
	}

	acb = sc->sc_nexus;
	if (acb == NULL) {
		printf("insc: unexpected reselect\n");
		goto abort;
	}

	/* Make this nexus active again. */
	sc->sc_state = AIC_CONNECTED;

	/* Do an implicit RESTORE POINTERS. */
	sc->sc_dp = acb->data_addr;
	sc->sc_dleft = acb->data_length;
	sc->sc_cp = (uint8_t *)acb->scsi_cmd;
	sc->sc_cleft = acb->scsi_cmd_length;

	return (0);

reset:
	aic6250_sched_msgout(sc, SEND_DEV_RESET);
	return (1);

abort:
	aic6250_sched_msgout(sc, SEND_ABORT);
	return (1);
}

/*
 * Schedule a SCSI operation.  This has now been pulled out of the interrupt
 * handler so that we may call it from aic6250_scsi_cmd and aic6250_done.
 * This may save us an unnecessary interrupt just to get things going.
 * Should only be called when state == AIC_IDLE and at bio pl.
 */
void
aic6250_sched(struct aic6250_softc *sc)
{
	struct aic6250_acb *acb;

	acb = &sc->sc_acb;
	sc->sc_nexus = acb;
	aic6250_select(sc, acb);
}

/*
 * POST PROCESSING OF SCSI_CMD (usually current)
 */
void
aic6250_done(struct aic6250_softc *sc, struct aic6250_acb *acb)
{
	switch (acb->target_stat) {
	case SCSI_OK:
		acb->error = XS_NOERROR;
		break;
	case SCSI_BUSY:
		acb->error = XS_BUSY;
		break;
	case SCSI_CHECK:
		acb->error = XS_DRIVER_STUFFUP;
		break;
	default:
		acb->error = XS_RESET;
		break;
	}

	acb->xsflags |= ITSDONE;
	sc->sc_nexus = NULL;
	sc->sc_state = AIC_IDLE;

	/* Nothing to start; just enable reselections. */
	sc->sc_imr1 |= AIC_IMR1_EN_SCSI_RST_INT;
	sc->sc_imr1 &=
	    ~(AIC_IMR1_EN_SCSI_REQ_ON_INT | AIC_IMR1_EN_SCSI_PARITY_ERR_INT |
	    AIC_IMR1_EN_BUS_FREE_DETECT_INT | AIC_IMR1_EN_PHASE_CHANGE_INT);
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);
	sc->sc_imr0 = AIC_IMR_EN_ERROR_INT |
	    AIC_IMR_EN_RESEL_INT | AIC_IMR_EN_SELECT_INT;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);
}

/*
 * INTERRUPT/PROTOCOL ENGINE
 */

/*
 * Precondition:
 * The SCSI bus is already in the MSGI phase and there is a message byte
 * on the bus, along with an asserted REQ signal.
 */
void
aic6250_msgin(struct aic6250_softc *sc)
{
	uint8_t sr0, scsisig;
	int n;
	uint8_t msgbyte;

	if (sc->sc_prevphase == PH_MSGIN) {
		/* This is a continuation of the previous message. */
		n = sc->sc_imp - sc->sc_imess;
		goto nextbyte;
	}

	/* This is a new MESSAGE IN phase.  Clean up our state. */
	sc->sc_flags &= ~AIC_DROP_MSGIN;

nextmsg:
	n = 0;
	sc->sc_imp = &sc->sc_imess[n];

nextbyte:
	/*
	 * Read a whole message, but don't ack the last byte.  If we reject the
	 * message, we have to assert ATN during the message transfer phase
	 * itself.
	 */
	for (;;) {
		for (;;) {
			scsisig = oaic_read(sc, AIC_SCSI_SIGNAL_REG);
			if ((scsisig & PH_MASK) != PH_MSGIN) {
				/*
				 * Target left MESSAGE IN, probably because it
				 * a) noticed our ATN signal, or
				 * b) ran out of messages.
				 */
				goto out;
			}
			if ((scsisig & AIC_SS_REQ_IN) != 0)
				break;
		}

		/* If parity error, just dump everything on the floor. */
		sr0 = oaic_read(sc, AIC_STATUS_REG0);
		if ((sr0 & AIC_SR0_SCSI_PARITY_ERR) != 0) {
			sc->sc_flags |= AIC_DROP_MSGIN;
			aic6250_sched_msgout(sc, SEND_PARITY_ERROR);
		}

		/* Gather incoming message bytes if needed. */
		msgbyte = oaic_read(sc, AIC_SCSI_ID_DATA);
		if ((sc->sc_flags & AIC_DROP_MSGIN) == 0) {
			if (n >= AIC_MAX_MSG_LEN) {
				sc->sc_flags |= AIC_DROP_MSGIN;
				aic6250_sched_msgout(sc, SEND_REJECT);
			} else {
				*sc->sc_imp++ = msgbyte;
				n++;

				/*
				 * This testing is suboptimal, but most
				 * messages will be of the one byte variety, so
				 * it should not affect performance
				 * significantly.
				 */
				if (n == 1 && IS1BYTEMSG(sc->sc_imess[0]))
					break;
				if (n == 2 && IS2BYTEMSG(sc->sc_imess[0]))
					break;
				if (n >= 3 && ISEXTMSG(sc->sc_imess[0]) &&
				    n == sc->sc_imess[1] + 2)
					break;
			}
		}

		/*
		 * If we reach this spot we're either:
		 * a) in the middle of a multi-byte message, or
		 * b) dropping bytes.
		 */
		aic6250_ack(sc);
	}

	/* We now have a complete message.  Parse it. */
	switch (sc->sc_state) {
		struct aic6250_acb *acb;

	case AIC_CONNECTED:
		acb = sc->sc_nexus;

		switch (sc->sc_imess[0]) {
		case MSG_CMDCOMPLETE:
			sc->sc_state = AIC_CMDCOMPLETE;
			break;

		case MSG_PARITY_ERROR:
			/* Resend the last message. */
			aic6250_sched_msgout(sc, sc->sc_lastmsg);
			break;

		case MSG_MESSAGE_REJECT:
			switch (sc->sc_lastmsg) {
			case SEND_INIT_DET_ERR:
				aic6250_sched_msgout(sc, SEND_ABORT);
				break;
			}
			break;

		case MSG_NOOP:
			break;

		case MSG_DISCONNECT:
			sc->sc_state = AIC_DISCONNECT;
			break;

		case MSG_SAVEDATAPOINTER:
			acb->data_addr = sc->sc_dp;
			acb->data_length = sc->sc_dleft;
			break;

		case MSG_RESTOREPOINTERS:
			sc->sc_dp = acb->data_addr;
			sc->sc_dleft = acb->data_length;
			sc->sc_cp = (uint8_t *)acb->scsi_cmd;
			sc->sc_cleft = acb->scsi_cmd_length;
			break;

		default:
			aic6250_sched_msgout(sc, SEND_REJECT);
			break;
		}
		break;

	case AIC_RESELECTED:
		if (!MSG_ISIDENTIFY(sc->sc_imess[0])) {
			printf("insc: reselect without IDENTIFY\n");
			goto reset;
		}

		(void) aic6250_reselect(sc, sc->sc_imess[0]);
		break;

	default:
		printf("insc: unexpected MESSAGE IN\n");
	reset:
		aic6250_sched_msgout(sc, SEND_DEV_RESET);
		break;

#ifdef notdef
	abort:
		aic6250_sched_msgout(sc, SEND_ABORT);
		break;
#endif
	}

	aic6250_ack(sc);

	/* Go get the next message, if any. */
	goto nextmsg;

out:
	/*
	 * We need to explicitely un-busy.
	 */
	oaic_write(sc, AIC_SCSI_SIGNAL_REG,
	    oaic_read(sc, AIC_SCSI_SIGNAL_REG) &
	    ~(AIC_SS_SEL_OUT | AIC_SS_BSY_OUT | AIC_SS_ACK_OUT));
}

/*
 * Send the highest priority, scheduled message.
 */
void
aic6250_msgout(struct aic6250_softc *sc)
{
	uint8_t scsisig;
	int n;

	if (sc->sc_prevphase == PH_MSGOUT) {
		if (sc->sc_omp == sc->sc_omess) {
			/*
			 * This is a retransmission.
			 *
			 * We get here if the target stayed in MESSAGE OUT
			 * phase.  Section 5.1.9.2 of the SCSI 2 spec indicates
			 * that all of the previously transmitted messages must
			 * be sent again, in the same order.  Therefore, we
			 * requeue all the previously transmitted messages, and
			 * start again from the top.  Our simple priority
			 * scheme keeps the messages in the right order.
			 */
			sc->sc_msgpriq |= sc->sc_msgoutq;
			/*
			 * Set ATN.  If we're just sending a trivial 1-byte
			 * message, we'll clear ATN later on anyway.
			 */
			oaic_write(sc, AIC_SCSI_SIGNAL_REG,
			    PH_MSGOUT | AIC_SS_ATN_OUT);
		} else {
			/* This is a continuation of the previous message. */
			n = sc->sc_omp - sc->sc_omess;
			goto nextbyte;
		}
	}

	/* No messages transmitted so far. */
	sc->sc_msgoutq = 0;
	sc->sc_lastmsg = 0;

nextmsg:
	/* Pick up highest priority message. */
	sc->sc_currmsg = sc->sc_msgpriq & -sc->sc_msgpriq;
	sc->sc_msgpriq &= ~sc->sc_currmsg;
	sc->sc_msgoutq |= sc->sc_currmsg;

	/* Build the outgoing message data. */
	switch (sc->sc_currmsg) {
	case SEND_IDENTIFY:
		sc->sc_omess[0] =
		    MSG_IDENTIFY(sc->sc_tgtlun, 1);
		n = 1;
		break;

	case SEND_DEV_RESET:
		sc->sc_flags |= AIC_ABORTING;
		sc->sc_omess[0] = MSG_BUS_DEV_RESET;
		n = 1;
		break;

	case SEND_REJECT:
		sc->sc_omess[0] = MSG_MESSAGE_REJECT;
		n = 1;
		break;

	case SEND_PARITY_ERROR:
		sc->sc_omess[0] = MSG_PARITY_ERROR;
		n = 1;
		break;

	case SEND_INIT_DET_ERR:
		sc->sc_omess[0] = MSG_INITIATOR_DET_ERR;
		n = 1;
		break;

	case SEND_ABORT:
		sc->sc_flags |= AIC_ABORTING;
		sc->sc_omess[0] = MSG_ABORT;
		n = 1;
		break;

	default:
		printf("insc: unexpected MESSAGE OUT\n");
		sc->sc_omess[0] = MSG_NOOP;
		n = 1;
		break;
	}
	sc->sc_omp = &sc->sc_omess[n];

nextbyte:
	/* Send message bytes. */
	for (;;) {
		for (;;) {
			scsisig = oaic_read(sc, AIC_SCSI_SIGNAL_REG);
			if ((scsisig & PH_MASK) != PH_MSGOUT) {
				/*
				 * Target left MESSAGE OUT, possibly to reject
				 * our message.
				 *
				 * If this is the last message being sent, then
				 * we deassert ATN, since either the target is
				 * going to ignore this message, or it's going
				 * to ask for a retransmission via MESSAGE
				 * PARITY ERROR (in which case we reassert ATN
				 * anyway).
				 */
				if (sc->sc_msgpriq == 0)
					oaic_write(sc, AIC_SCSI_SIGNAL_REG,
					    scsisig & ~AIC_SS_ATN_OUT);
				return;
			}
			if ((scsisig & AIC_SS_REQ_IN) != 0)
				break;
		}

		/* Clear ATN before last byte if this is the last message. */
		if (n == 1 && sc->sc_msgpriq == 0)
			oaic_write(sc, AIC_SCSI_SIGNAL_REG,
			    scsisig & ~AIC_SS_ATN_OUT);
		/* Send message byte. */
		oaic_write(sc, AIC_SCSI_ID_DATA, *--sc->sc_omp);
		--n;
		/* Keep track of the last message we've sent any bytes of. */
		sc->sc_lastmsg = sc->sc_currmsg;

		aic6250_ack(sc);

		if (n == 0)
			break;
	}

	/* We get here only if the entire message has been transmitted. */
	if (sc->sc_msgpriq != 0) {
		/* There are more outgoing messages. */
		goto nextmsg;
	}

	/*
	 * The last message has been transmitted.  We need to remember the last
	 * message transmitted (in case the target switches to MESSAGE IN phase
	 * and sends a MESSAGE REJECT), and the list of messages transmitted
	 * this time around (in case the target stays in MESSAGE OUT phase to
	 * request a retransmit).
	 */
}

/* aic6250_dataout_pio: perform a data transfer in CPU-controlled PIO mode.
 * Precondition: The SCSI bus should be in the DOUT or CMDOUT phase, with REQ
 * asserted and ACK deasserted (i.e. waiting for a data byte).
 */
int
aic6250_dataout_pio(struct aic6250_softc *sc, uint8_t *p, int n, int phase)
{
	uint8_t scsisig;
	int out = 0;

	sc->sc_imr1 &= ~AIC_IMR1_EN_SCSI_REQ_ON_INT;
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

	/* I have tried to make the main loop as tight as possible.  This
	 * means that some of the code following the loop is a bit more
	 * complex than otherwise.
	 */
	while (out != n) {
		for (;;) {
			scsisig = oaic_read(sc, AIC_SCSI_SIGNAL_REG);
			if ((scsisig & AIC_SS_REQ_IN) != 0)
				break;
		}
		if ((scsisig & PH_MASK) != phase)
			break;

		oaic_write(sc, AIC_SCSI_ID_DATA, *p++);
		out++;

		aic6250_ack(sc);
	}

	sc->sc_imr1 |= AIC_IMR1_EN_SCSI_REQ_ON_INT;
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

	return out;
}

/* aic6250_datain_pio: perform data transfers using the FIFO datapath in the
 * aic6250.
 * Precondition: The SCSI bus should be in the DIN or STAT phase, with REQ
 * asserted and ACK deasserted (i.e. at least one byte is ready).
 * For now, uses a pretty dumb algorithm, hangs around until all data has been
 * transferred.  This, is OK for fast targets, but not so smart for slow
 * targets which don't disconnect or for huge transfers.
 */
int
aic6250_datain_pio(struct aic6250_softc *sc, uint8_t *p, int n, int phase)
{
	uint8_t scsisig;
	int in = 0;

	sc->sc_imr1 &= ~AIC_IMR1_EN_SCSI_REQ_ON_INT;
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

	/* We leave this loop if one or more of the following is true:
	 * a) phase != PH_DATAIN && FIFOs are empty
	 * b) SCSIRSTI is set (a reset has occurred) or busfree is detected.
	 */
	while (in != n) {
		for (;;) {
			scsisig = oaic_read(sc, AIC_SCSI_SIGNAL_REG);
			if ((scsisig & AIC_SS_REQ_IN) != 0)
				break;
		}
		if ((scsisig & PH_MASK) != phase)
			break;

		*p++ = oaic_read(sc, AIC_SCSI_ID_DATA);
		in++;

		aic6250_ack(sc);
	}

	sc->sc_imr1 |= AIC_IMR1_EN_SCSI_REQ_ON_INT;
	oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

	return in;
}

/*
 * This is the workhorse routine of the driver.
 * Deficiencies (for now):
 * 1) always uses programmed I/O
 */
int
aic6250_intr(void *arg)
{
	struct aic6250_softc *sc = arg;
	uint8_t sr1, sr0;
	struct aic6250_acb *acb;
	int n, first = 1;

	/* Read SR1 before writing to IMR0 (which will reset some SR1 bits). */
	sr1 = oaic_read(sc, AIC_STATUS_REG1);
	oaic_write(sc, AIC_INT_MSK_REG0, 0);

loop:
	sr0 = oaic_read(sc, AIC_STATUS_REG0);
	/*
	 * First check for abnormal conditions, such as reset.
	 */
	if ((sr0 & AIC_SR0_SCSI_RST_OCCURED) != 0) {
		printf("insc: SCSI bus reset\n");
		while ((oaic_read(sc, AIC_STATUS_REG1) &
		    AIC_SR1_SCSI_RST_IN) != 0)
			delay(5);
		goto reset;
	}

	/*
	 * Check for less serious errors.
	 */
	if ((sr0 & AIC_SR0_SCSI_PARITY_ERR) != 0) {
		printf("insc: SCSI bus parity error\n");
		if (sc->sc_prevphase == PH_MSGIN) {
			sc->sc_flags |= AIC_DROP_MSGIN;
			aic6250_sched_msgout(sc, SEND_PARITY_ERROR);
		} else
			aic6250_sched_msgout(sc, SEND_INIT_DET_ERR);
	}

	/*
	 * If we're not already busy doing something test for the following
	 * conditions:
	 * 1) We have been reselected by something
	 * 2) We have selected something successfully
	 * 3) Our selection process has timed out
	 * 4) This is really a bus free interrupt just to get a new command
	 *    going?
	 * 5) Spurious interrupt?
	 */
	switch (sc->sc_state) {
	case AIC_IDLE:
	case AIC_SELECTING:
		if (first)
			first = 0;
		else
			sr1 = oaic_read(sc, AIC_STATUS_REG1);

		if (sc->sc_state == AIC_SELECTING &&
		   (sr1 & AIC_SR1_SEL_OUT) != 0) {
			/* start selection timeout */
			acb = sc->sc_nexus;
			sc->sc_selto = 250;	/* msec */
			sc->sc_imr0 &= ~AIC_IMR_EN_SEL_OUT_INT;
			goto out;
		}

		if ((sr1 & AIC_SR1_RESELECTED) != 0) {
			/* kill selection timeout timer */
			sc->sc_imr0 &=
			    ~(AIC_IMR_EN_SEL_OUT_INT | AIC_IMR_EN_CMD_DONE_INT);
			sc->sc_selto = 0;

			/* Save reselection ID. */
			sc->sc_selid = oaic_read(sc, AIC_SOURCE_DEST_ID);

			sc->sc_state = AIC_RESELECTED;
		} else if ((sr1 & (AIC_SR1_SELECTED | AIC_SR1_CMD_DONE)) != 0) {
			/* kill selection timeout timer */
			sc->sc_imr0 &=
			    ~(AIC_IMR_EN_SEL_OUT_INT | AIC_IMR_EN_CMD_DONE_INT);
			sc->sc_selto = 0;

			/* We have selected a target. Things to do:
			 * a) Determine what message(s) to send.
			 * b) Verify that we're still selecting the target.
			 * c) Mark device as busy.
			 */
			if (sc->sc_state != AIC_SELECTING) {
				printf("insc: selection out while idle\n");
				goto reset;
			}
			acb = sc->sc_nexus;

			sc->sc_msgpriq = SEND_IDENTIFY;

			/* Do an implicit RESTORE POINTERS. */
			sc->sc_dp = acb->data_addr;
			sc->sc_dleft = acb->data_length;
			sc->sc_cp = (uint8_t *)acb->scsi_cmd;
			sc->sc_cleft = acb->scsi_cmd_length;

			sc->sc_state = AIC_CONNECTED;
		} else {
			if (sc->sc_state != AIC_IDLE) {
				printf("insc: BUS FREE while not idle\n");
				goto out;
			}

			goto sched;
		}

		/*
		 * Turn off selection stuff, and prepare to catch bus free
		 * interrupts, parity errors, and phase changes.
		 */
		sc->sc_imr1 |=
		    AIC_IMR1_EN_SCSI_REQ_ON_INT | AIC_IMR1_EN_SCSI_RST_INT |
		    AIC_IMR1_EN_BUS_FREE_DETECT_INT |
		    AIC_IMR1_EN_SCSI_PARITY_ERR_INT |
		    AIC_IMR1_EN_PHASE_CHANGE_INT;
		oaic_write(sc, AIC_INT_MSK_REG1, sc->sc_imr1);

		sc->sc_flags = 0;
		sc->sc_prevphase = PH_INVALID;
		goto dophase;
	}

	if ((sr0 & AIC_SR0_BUS_FREE_DETECT) != 0) {
		/* We've gone to BUS FREE phase. */
		switch (sc->sc_state) {
		case AIC_RESELECTED:
			goto sched;

		case AIC_CONNECTED:
			acb = sc->sc_nexus;

			if ((sc->sc_flags & AIC_ABORTING) == 0) {
				/*
				 * Section 5.1.1 of the SCSI 2 spec suggests
				 * issuing a REQUEST SENSE following an
				 * unexpected disconnect.  Some devices go into
				 * a contingent allegiance condition when
				 * disconnecting, and this is necessary to
				 * clean up their state.
				 */
				printf("insc: unexpected disconnect\n");
				goto out;
			}

			acb->error = XS_DRIVER_STUFFUP;
			goto finish;

		case AIC_DISCONNECT:
			acb = sc->sc_nexus;
#if 1 /* XXX */
			acb->data_addr = sc->sc_dp;
			acb->data_length = sc->sc_dleft;
#endif
			sc->sc_nexus = NULL;
			goto sched;

		case AIC_CMDCOMPLETE:
			acb = sc->sc_nexus;
			goto finish;
		}
	}

dophase:
	if ((sr0 & AIC_SR0_SCSI_REQ_ON) == 0) {
		/* Wait for AIC_SR0_SCSI_REQ_ON. */
		goto out;
	}

	sc->sc_phase = oaic_read(sc, AIC_SCSI_SIGNAL_REG) & PH_MASK;
	oaic_write(sc, AIC_SCSI_SIGNAL_REG, sc->sc_phase);

	switch (sc->sc_phase) {
	case PH_MSGOUT:
		if (sc->sc_state != AIC_CONNECTED &&
		    sc->sc_state != AIC_RESELECTED)
			break;
		aic6250_msgout(sc);
		sc->sc_prevphase = PH_MSGOUT;
		goto loop;

	case PH_MSGIN:
		if (sc->sc_state != AIC_CONNECTED &&
		    sc->sc_state != AIC_RESELECTED)
			break;
		aic6250_msgin(sc);
		sc->sc_prevphase = PH_MSGIN;
		goto loop;

	case PH_CMD:
		if (sc->sc_state != AIC_CONNECTED)
			break;
		n = aic6250_dataout_pio(sc, sc->sc_cp, sc->sc_cleft, PH_CMD);
		sc->sc_cp += n;
		sc->sc_cleft -= n;
		sc->sc_prevphase = PH_CMD;
		goto loop;

	case PH_DATAOUT:
		if (sc->sc_state != AIC_CONNECTED)
			break;
		n = aic6250_dataout_pio(sc, sc->sc_dp, sc->sc_dleft, PH_DATAOUT);
		sc->sc_dp += n;
		sc->sc_dleft -= n;
		sc->sc_prevphase = PH_DATAOUT;
		goto loop;

	case PH_DATAIN:
		if (sc->sc_state != AIC_CONNECTED)
			break;
		n = aic6250_datain_pio(sc, sc->sc_dp, sc->sc_dleft, PH_DATAIN);
		sc->sc_dp += n;
		sc->sc_dleft -= n;
		sc->sc_prevphase = PH_DATAIN;
		goto loop;

	case PH_STAT:
		if (sc->sc_state != AIC_CONNECTED)
			break;
		acb = sc->sc_nexus;
		aic6250_datain_pio(sc, &acb->target_stat, 1, PH_STAT);
		sc->sc_prevphase = PH_STAT;
		goto loop;
	}

	printf("insc: unexpected bus phase\n");
reset:
	aic6250_init(sc);
	return 1;

finish:
	aic6250_done(sc, acb);
	goto out;

sched:
	sc->sc_state = AIC_IDLE;

out:
	sc->sc_imr0 |= AIC_IMR_EN_ERROR_INT;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);
	return 1;
}

void
aic6250_seltimeout(void *arg)
{
	struct aic6250_softc *sc = arg;
	struct aic6250_acb *acb;

	if (sc->sc_state != AIC_SELECTING) {
		printf("insc: selection timeout while idle\n");
		aic6250_init(sc);
		return;
	}

	acb = sc->sc_nexus;

	oaic_write(sc, AIC_SCSI_ID_DATA, 0);
	delay(200);

	acb->error = XS_SELTIMEOUT;
	aic6250_done(sc, acb);

	sc->sc_imr0 |= AIC_IMR_EN_ERROR_INT;
	oaic_write(sc, AIC_INT_MSK_REG0, sc->sc_imr0);
}


code:
/*	$OpenBSD: oaic.h,v 1.1 2013/10/16 16:59:34 miod Exp $	*/

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

void *oaic_attach(uint32_t, int, int);
void oaic_detach(void *);
int oaic_scsicmd(void *, void *, size_t, void *, size_t, size_t *);


code:
/* $OpenBSD: oosiop.c,v 1.6 2014/07/12 19:01:49 tedu Exp $ */
/* OpenBSD: oosiop.c,v 1.20 2013/10/09 18:22:06 miod Exp */
/* OpenBSD: oosiopvar.h,v 1.5 2011/04/03 12:42:36 krw Exp */
/* $NetBSD: oosiop.c,v 1.4 2003/10/29 17:45:55 tsutsui Exp $ */
/* $NetBSD: oosiopvar.h,v 1.2 2003/05/03 18:11:23 wiz Exp $ */

/*
 * Copyright (c) 2001 Shuichiro URATA.  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. 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.
 */

#include <sys/param.h>
#define offsetof(s, e) ((size_t)&((s *)0)->e)

#include <stand.h>
#include "libsa.h"

#include "scsi.h"

#include <dev/ic/oosiopreg.h>

#define	OOSIOP_NTGT	8		/* Max targets */

struct oosiop_xfer {
	/* script for DMA (move*nsg+jump) */
	uint32_t datain_scr[2 * 2];
	uint32_t dataout_scr[2 * 2];

	u_int8_t msgin[8];
	u_int8_t msgout[8];
	u_int8_t status;
};

#define	SCSI_OOSIOP_NOSTATUS	0xff	/* device didn't report status */

struct oosiop_cb {
	int flags;

	int id;				/* target scsi id */
	int lun;			/* target lun */

	int curdp;			/* current data pointer */
	int savedp;			/* saved data pointer */
	int msgoutlen;

	int xsflags;			/* copy of xs->flags */
	int datalen;			/* copy of xs->datalen */
	uint32_t databuf;
	int cmdlen;			/* copy of xs->cmdlen */
	uint32_t cmdbuf;

	struct oosiop_xfer *xfer;	/* DMA xfer block */

	int status;
	int error;
};

/* oosiop_cb flags */
#define	CBF_SELTOUT	0x01	/* Selection timeout */
#define	CBF_TIMEOUT	0x02	/* Command timeout */

struct oosiop_softc {
	uint32_t	sc_baseaddr;
	uint32_t *sc_scr;		/* ptr to script memory */

	int	sc_tgtid;
	int	sc_tgtlun;

	int		sc_id;		/* SCSI ID of this interface */
	int		sc_freq;	/* SCLK frequency */
	int		sc_ccf;		/* asynchronous divisor (*10) */
	u_int8_t	sc_dcntl;

	int sc_pending;

	struct oosiop_xfer sc_xfer;
	struct oosiop_cb sc_cb;

	struct oosiop_cb *sc_nexus;
	struct oosiop_cb *sc_curcb;	/* current command */

	uint32_t sc_reselbuf;		/* msgin buffer for reselection */
	int sc_resid;			/* reselected target id */

	int sc_nextdsp;

	uint8_t	sc_scntl0;
	uint8_t sc_dmode;
	uint8_t sc_dwt;
	uint8_t sc_ctest7;
};

#define	oosiop_read_1(sc, addr)						\
    *(volatile uint8_t *)((sc)->sc_baseaddr + (addr))
#define	oosiop_write_1(sc, addr, data)					\
    *(volatile uint8_t *)((sc)->sc_baseaddr + (addr)) = (data)
#define	oosiop_read_4(sc, addr)						\
    letoh32(*(volatile uint32_t *)((sc)->sc_baseaddr + (addr)))
#define	oosiop_write_4(sc, addr, data)					\
    *(volatile uint32_t *)((sc)->sc_baseaddr + (addr)) = htole32(data)

/* 53C700 script */
#include <dev/microcode/siop/oosiop.out>

static __inline void oosiop_relocate_io(struct oosiop_softc *, uint32_t);
static __inline void oosiop_relocate_tc(struct oosiop_softc *, uint32_t);
static __inline void oosiop_fixup_select(struct oosiop_softc *, uint32_t,
		         int);
static __inline void oosiop_fixup_jump(struct oosiop_softc *, uint32_t,
		         uint32_t);
static __inline void oosiop_fixup_move(struct oosiop_softc *, uint32_t,
		         uint32_t, uint32_t);

void	oosiop_load_script(struct oosiop_softc *);
void	oosiop_setup_sgdma(struct oosiop_softc *, struct oosiop_cb *);
void	oosiop_setup_dma(struct oosiop_softc *);
void	oosiop_flush_fifo(struct oosiop_softc *);
void	oosiop_clear_fifo(struct oosiop_softc *);
void	oosiop_phasemismatch(struct oosiop_softc *);
static __inline void oosiop_setup_syncxfer(struct oosiop_softc *);
void	oosiop_done(struct oosiop_softc *, struct oosiop_cb *);
void	oosiop_reset(struct oosiop_softc *);
void	oosiop_reset_bus(struct oosiop_softc *);
void	oosiop_scriptintr(struct oosiop_softc *);
void	oosiop_msgin(struct oosiop_softc *, struct oosiop_cb *);
void	oosiop_setup(struct oosiop_softc *, struct oosiop_cb *);
void	oosiop_processintr(struct oosiop_softc *, u_int8_t);

/* Trap interrupt code for unexpected data I/O */
#define	DATAIN_TRAP	0xdead0001
#define	DATAOUT_TRAP	0xdead0002

void *
oosiop_attach(uint32_t addr, int id, int lun)
{
	struct oosiop_softc *sc;

	sc = (struct oosiop_softc *)alloc(sizeof *sc);
	if (sc == NULL)
		return NULL;

	memset(sc, 0, sizeof *sc);
	sc->sc_baseaddr = addr;
	sc->sc_scr = (uint32_t *)alloc(sizeof(oosiop_script));

	/* XXX */
	sc->sc_id = 7;
	sc->sc_freq = 33333333;
	sc->sc_scntl0 = OOSIOP_SCNTL0_EPC | OOSIOP_SCNTL0_EPG;
	sc->sc_dmode = OOSIOP_DMODE_BL_4;
	sc->sc_dwt = 0x4f;
	sc->sc_ctest7 = OOSIOP_CTEST7_DC;
	/* XXX */

	/* Setup asynchronous clock divisor parameters */
	if (sc->sc_freq <= 25000000) {
		sc->sc_ccf = 10;
		sc->sc_dcntl = OOSIOP_DCNTL_CF_1;
	} else if (sc->sc_freq <= 37500000) {
		sc->sc_ccf = 15;
		sc->sc_dcntl = OOSIOP_DCNTL_CF_1_5;
	} else if (sc->sc_freq <= 50000000) {
		sc->sc_ccf = 20;
		sc->sc_dcntl = OOSIOP_DCNTL_CF_2;
	} else {
		sc->sc_ccf = 30;
		sc->sc_dcntl = OOSIOP_DCNTL_CF_3;
	}

	sc->sc_cb.xfer = &sc->sc_xfer;
	sc->sc_reselbuf = (uint32_t)&sc->sc_xfer.msgin;

	/*
	 * Reset all
	 */
	oosiop_reset(sc);
	oosiop_reset_bus(sc);

	/*
	 * Start SCRIPTS processor
	 */
	oosiop_load_script(sc);
	oosiop_write_4(sc, OOSIOP_DSP, (uint32_t)sc->sc_scr + Ent_wait_reselect);

	sc->sc_tgtid = id;
	sc->sc_tgtlun = lun;

	return sc;
}

void
oosiop_detach(void *cookie)
{
	struct oosiop_softc *sc = cookie;

	free(sc->sc_scr, sizeof(oosiop_script));
	free(sc, sizeof *sc);
}

static __inline void
oosiop_relocate_io(struct oosiop_softc *sc, uint32_t addr)
{
	uint32_t dcmd;
	int32_t dsps;

	dcmd = letoh32(sc->sc_scr[addr / 4 + 0]);
	dsps = letoh32(sc->sc_scr[addr / 4 + 1]);

	/* convert relative to absolute */
	if (dcmd & 0x04000000) {
		dcmd &= ~0x04000000;
#if 0
		/*
		 * sign extension isn't needed here because
		 * ncr53cxxx.c generates 32 bit dsps.
		 */
		dsps <<= 8;
		dsps >>= 8;
#endif
		sc->sc_scr[addr / 4 + 0] = htole32(dcmd);
		dsps += addr + 8;
	}

	sc->sc_scr[addr / 4 + 1] = htole32(dsps + (uint32_t)sc->sc_scr);
}

static __inline void
oosiop_relocate_tc(struct oosiop_softc *sc, uint32_t addr)
{
	uint32_t dcmd;
	int32_t dsps;

	dcmd = letoh32(sc->sc_scr[addr / 4 + 0]);
	dsps = letoh32(sc->sc_scr[addr / 4 + 1]);

	/* convert relative to absolute */
	if (dcmd & 0x00800000) {
		dcmd &= ~0x00800000;
		sc->sc_scr[addr / 4] = htole32(dcmd);
#if 0
		/*
		 * sign extension isn't needed here because
		 * ncr53cxxx.c generates 32 bit dsps.
		 */
		dsps <<= 8;
		dsps >>= 8;
#endif
		dsps += addr + 8;
	}

	sc->sc_scr[addr / 4 + 1] = htole32(dsps + (uint32_t)sc->sc_scr);
}

static __inline void
oosiop_fixup_select(struct oosiop_softc *sc, uint32_t addr, int id)
{
	uint32_t dcmd;

	dcmd = letoh32(sc->sc_scr[addr / 4]);
	dcmd &= 0xff00ffff;
	dcmd |= 0x00010000 << id;
	sc->sc_scr[addr / 4] = htole32(dcmd);
}

static __inline void
oosiop_fixup_jump(struct oosiop_softc *sc, uint32_t addr, uint32_t dst)
{

	sc->sc_scr[addr / 4 + 1] = htole32(dst);
}

static __inline void
oosiop_fixup_move(struct oosiop_softc *sc, uint32_t addr, uint32_t dbc,
    uint32_t dsps)
{
	uint32_t dcmd;

	dcmd = letoh32(sc->sc_scr[addr / 4]);
	dcmd &= 0xff000000;
	dcmd |= dbc & 0x00ffffff;
	sc->sc_scr[addr / 4 + 0] = htole32(dcmd);
	sc->sc_scr[addr / 4 + 1] = htole32(dsps);
}

void
oosiop_load_script(struct oosiop_softc *sc)
{
	int i;

	/* load script */
	for (i = 0; i < sizeof(oosiop_script) / sizeof(oosiop_script[0]); i++)
		sc->sc_scr[i] = htole32(oosiop_script[i]);

	/* relocate script */
	for (i = 0; i < (sizeof(oosiop_script) / 8); i++) {
		switch (oosiop_script[i * 2] >> 27) {
		case 0x08:	/* select */
		case 0x0a:	/* wait reselect */
			oosiop_relocate_io(sc, i * 8);
			break;
		case 0x10:	/* jump */
		case 0x11:	/* call */
			oosiop_relocate_tc(sc, i * 8);
			break;
		}
	}

	oosiop_fixup_move(sc, Ent_p_resel_msgin_move, 1, sc->sc_reselbuf);
}

void
oosiop_setup_sgdma(struct oosiop_softc *sc, struct oosiop_cb *cb)
{
	struct oosiop_xfer *xfer = cb->xfer;
	int n, off;

	off = cb->curdp;

	if (cb->xsflags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
		/* build MOVE block */
		if (cb->xsflags & SCSI_DATA_IN) {
			n = 0;
			xfer->datain_scr[n * 2 + 0] =
			    htole32(0x09000000 | (cb->datalen - off));
			xfer->datain_scr[n * 2 + 1] =
			    htole32(cb->databuf + off);
			n++;
			xfer->datain_scr[n * 2 + 0] = htole32(0x80080000);
			xfer->datain_scr[n * 2 + 1] =
			    htole32((uint32_t)sc->sc_scr + Ent_phasedispatch);
		}
		if (cb->xsflags & SCSI_DATA_OUT) {
			n = 0;
			xfer->dataout_scr[n * 2 + 0] =
			    htole32(0x08000000 | (cb->datalen - off));
			xfer->dataout_scr[n * 2 + 1] =
			    htole32(cb->databuf + off);
			n++;
			xfer->dataout_scr[n * 2 + 0] = htole32(0x80080000);
			xfer->dataout_scr[n * 2 + 1] =
			    htole32((uint32_t)sc->sc_scr + Ent_phasedispatch);
		}
	}
	if ((cb->xsflags & SCSI_DATA_IN) == 0) {
		xfer->datain_scr[0] = htole32(0x98080000);
		xfer->datain_scr[1] = htole32(DATAIN_TRAP);
	}
	if ((cb->xsflags & SCSI_DATA_OUT) == 0) {
		xfer->dataout_scr[0] = htole32(0x98080000);
		xfer->dataout_scr[1] = htole32(DATAOUT_TRAP);
	}
}

/*
 * Setup DMA pointer into script.
 */
void
oosiop_setup_dma(struct oosiop_softc *sc)
{
	struct oosiop_cb *cb;
	uint32_t xferbase;

	cb = sc->sc_curcb;
	xferbase = (uint32_t)cb->xfer;

	oosiop_fixup_select(sc, Ent_p_select, cb->id);
	oosiop_fixup_jump(sc, Ent_p_datain_jump, xferbase +
	    offsetof(struct oosiop_xfer, datain_scr[0]));
	oosiop_fixup_jump(sc, Ent_p_dataout_jump, xferbase +
	    offsetof(struct oosiop_xfer, dataout_scr[0]));
	oosiop_fixup_move(sc, Ent_p_msgin_move, 1, xferbase +
	    offsetof(struct oosiop_xfer, msgin[0]));
	oosiop_fixup_move(sc, Ent_p_extmsglen_move, 1, xferbase +
	    offsetof(struct oosiop_xfer, msgin[1]));
	oosiop_fixup_move(sc, Ent_p_msgout_move, cb->msgoutlen, xferbase +
	    offsetof(struct oosiop_xfer, msgout[0]));
	oosiop_fixup_move(sc, Ent_p_status_move, 1, xferbase +
	    offsetof(struct oosiop_xfer, status));
	oosiop_fixup_move(sc, Ent_p_cmdout_move, cb->cmdlen, cb->cmdbuf);
}

void
oosiop_flush_fifo(struct oosiop_softc *sc)
{

	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) |
	    OOSIOP_DFIFO_FLF);
	while ((oosiop_read_1(sc, OOSIOP_CTEST1) & OOSIOP_CTEST1_FMT) !=
	    OOSIOP_CTEST1_FMT)
		;
	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) &
	    ~OOSIOP_DFIFO_FLF);
}

void
oosiop_clear_fifo(struct oosiop_softc *sc)
{

	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) |
	    OOSIOP_DFIFO_CLF);
	while ((oosiop_read_1(sc, OOSIOP_CTEST1) & OOSIOP_CTEST1_FMT) !=
	    OOSIOP_CTEST1_FMT)
		;
	oosiop_write_1(sc, OOSIOP_DFIFO, oosiop_read_1(sc, OOSIOP_DFIFO) &
	    ~OOSIOP_DFIFO_CLF);
}

void
oosiop_phasemismatch(struct oosiop_softc *sc)
{
	struct oosiop_cb *cb;
	uint32_t dsp, dbc, n, i, len;
	u_int8_t dfifo, sstat1;

	cb = sc->sc_curcb;
	if (cb == NULL)
		return;

	dsp = oosiop_read_4(sc, OOSIOP_DSP);
	dbc = oosiop_read_4(sc, OOSIOP_DBC) & OOSIOP_DBC_MAX;
	len = 0;

	n = dsp - (uint32_t)cb->xfer - 8;
	if (n >= offsetof(struct oosiop_xfer, datain_scr[0]) &&
	    n < offsetof(struct oosiop_xfer, datain_scr[2 * 2])) {
		n -= offsetof(struct oosiop_xfer, datain_scr[0]);
		n >>= 3;
		for (i = 0; i <= n; i++)
			len += letoh32(cb->xfer->datain_scr[i * 2]) &
			    0x00ffffff;
		/* All data in the chip are already flushed */
	} else if (n >= offsetof(struct oosiop_xfer, dataout_scr[0]) &&
	    n < offsetof(struct oosiop_xfer, dataout_scr[2 * 2])) {
		n -= offsetof(struct oosiop_xfer, dataout_scr[0]);
		n >>= 3;
		for (i = 0; i <= n; i++)
			len += letoh32(cb->xfer->dataout_scr[i * 2]) &
			    0x00ffffff;

		dfifo = oosiop_read_1(sc, OOSIOP_DFIFO);
		dbc += ((dfifo & OOSIOP_DFIFO_BO) - (dbc & OOSIOP_DFIFO_BO)) &
		    OOSIOP_DFIFO_BO;

		sstat1 = oosiop_read_1(sc, OOSIOP_SSTAT1);
		if (sstat1 & OOSIOP_SSTAT1_OLF)
			dbc++;

		oosiop_clear_fifo(sc);
	} else {
#if 0 /* XXX happens with dsp == Ent_p_cmdout_move ?!? */
		printf("ncsc: phase mismatch addr=%p\n",
		    oosiop_read_4(sc, OOSIOP_DSP) - 8);
#endif
		oosiop_clear_fifo(sc);
		return;
	}

	len -= dbc;
	if (len) {
		cb->curdp += len;
		oosiop_setup_sgdma(sc, cb);
	}
}

static __inline void
oosiop_setup_syncxfer(struct oosiop_softc *sc)
{
	oosiop_write_1(sc, OOSIOP_SXFER, 0);
}

int
oosiop_scsicmd(void *cookie, void *cmdbuf, size_t cmdlen, void *databuf,
    size_t datalen, size_t *resid)
{
	struct oosiop_softc *sc = cookie;
	struct oosiop_cb *cb = &sc->sc_cb;
	struct oosiop_xfer *xfer;

	if (resid != NULL)
		*resid = 0;
	cb->xsflags = datalen != 0 ? SCSI_DATA_IN : 0;	/* XXX */
	cb->cmdbuf = (uint32_t)cmdbuf;
	cb->cmdlen = cmdlen;
	cb->datalen = 0;
	cb->flags = 0;
	cb->id = sc->sc_tgtid;
	cb->lun = sc->sc_tgtlun;
	xfer = cb->xfer;

	/* Setup data buffer DMA */
	if (cb->xsflags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
		cb->datalen = datalen;
		cb->databuf = (uint32_t)databuf;
	}

	xfer->status = SCSI_OOSIOP_NOSTATUS;

	oosiop_setup(sc, cb);
	sc->sc_pending = 1;

	/* Abort script to start selection */
	oosiop_write_1(sc, OOSIOP_ISTAT, OOSIOP_ISTAT_ABRT);

	for (;;) {
		u_int8_t istat;

		while (((istat = oosiop_read_1(sc, OOSIOP_ISTAT)) &
		    (OOSIOP_ISTAT_SIP | OOSIOP_ISTAT_DIP)) == 0) {
			delay(1000);
		}
		oosiop_processintr(sc, istat);

		if (cb->xsflags & ITSDONE)
			break;
	}

	if (resid != NULL && cb->error == 0)
		*resid = datalen;
	return cb->error;
}

void
oosiop_setup(struct oosiop_softc *sc, struct oosiop_cb *cb)
{
	struct oosiop_xfer *xfer = cb->xfer;

	cb->curdp = 0;
	cb->savedp = 0;

	oosiop_setup_sgdma(sc, cb);

	/* Setup msgout buffer */
	xfer->msgout[0] = MSG_IDENTIFY(cb->lun,
	    (((struct scsi_generic *)cb->cmdbuf)->opcode != REQUEST_SENSE));
	cb->msgoutlen = 1;
}

void
oosiop_done(struct oosiop_softc *sc, struct oosiop_cb *cb)
{

	cb->status = cb->xfer->status;

	if (cb->flags & CBF_SELTOUT)
		cb->error = XS_SELTIMEOUT;
	else if (cb->flags & CBF_TIMEOUT)
		cb->error = XS_TIMEOUT;
	else switch (cb->status) {
	case SCSI_OK:
		cb->error = XS_NOERROR;
		break;

	case SCSI_BUSY:
		cb->error = XS_BUSY;
		break;
	case SCSI_CHECK:
		cb->error = XS_DRIVER_STUFFUP;
		break;
	case SCSI_OOSIOP_NOSTATUS:
		/* the status byte was not updated, cmd was aborted. */
		cb->error = XS_SELTIMEOUT;
		break;

	default:
		cb->error = XS_RESET;
		break;
	}

	/* Put it on the free list. */
	cb->xsflags |= ITSDONE;

	if (cb == sc->sc_curcb)
		sc->sc_curcb = NULL;
	sc->sc_nexus = NULL;
}

void
oosiop_reset(struct oosiop_softc *sc)
{
	/* Stop SCRIPTS processor */
	oosiop_write_1(sc, OOSIOP_ISTAT, OOSIOP_ISTAT_ABRT);
	delay(100);
	oosiop_write_1(sc, OOSIOP_ISTAT, 0);

	/* Reset the chip */
	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl | OOSIOP_DCNTL_RST);
	delay(100);
	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl);
	delay(10000);

	/* Set up various chip parameters */
	oosiop_write_1(sc, OOSIOP_SCNTL0, OOSIOP_ARB_FULL | sc->sc_scntl0);
	oosiop_write_1(sc, OOSIOP_SCNTL1, OOSIOP_SCNTL1_ESR);
	oosiop_write_1(sc, OOSIOP_DCNTL, sc->sc_dcntl);
	oosiop_write_1(sc, OOSIOP_DMODE, sc->sc_dmode);
	oosiop_write_1(sc, OOSIOP_SCID, OOSIOP_SCID_VALUE(sc->sc_id));
	oosiop_write_1(sc, OOSIOP_DWT, sc->sc_dwt);
	oosiop_write_1(sc, OOSIOP_CTEST7, sc->sc_ctest7);
	oosiop_write_1(sc, OOSIOP_SXFER, 0);

	/* Clear all interrupts */
	(void)oosiop_read_1(sc, OOSIOP_SSTAT0);
	(void)oosiop_read_1(sc, OOSIOP_SSTAT1);
	(void)oosiop_read_1(sc, OOSIOP_DSTAT);

	/* Enable interrupts */
	oosiop_write_1(sc, OOSIOP_SIEN,
	    OOSIOP_SIEN_M_A | OOSIOP_SIEN_STO | OOSIOP_SIEN_SGE |
	    OOSIOP_SIEN_UDC | OOSIOP_SIEN_RST | OOSIOP_SIEN_PAR);
	oosiop_write_1(sc, OOSIOP_DIEN,
	    OOSIOP_DIEN_ABRT | OOSIOP_DIEN_SSI | OOSIOP_DIEN_SIR |
	    OOSIOP_DIEN_WTD | OOSIOP_DIEN_IID);
}

void
oosiop_reset_bus(struct oosiop_softc *sc)
{
	/* Assert SCSI RST */
	oosiop_write_1(sc, OOSIOP_SCNTL1, OOSIOP_SCNTL1_RST);
	delay(25);	/* Reset hold time (25us) */
	oosiop_write_1(sc, OOSIOP_SCNTL1, 0);

	/* Remove all nexuses */
	if (sc->sc_nexus) {
		sc->sc_nexus->xfer->status =
		    SCSI_OOSIOP_NOSTATUS; /* XXX */
		oosiop_done(sc, sc->sc_nexus);
	}

	sc->sc_curcb = NULL;

	delay(250000);	/* Reset to selection (250ms) */
}

void
oosiop_processintr(struct oosiop_softc *sc, u_int8_t istat)
{
	struct oosiop_cb *cb;
	uint32_t dcmd;
	u_int8_t dstat, sstat0;

	sc->sc_nextdsp = Ent_wait_reselect;

	/* DMA interrupts */
	if (istat & OOSIOP_ISTAT_DIP) {
		oosiop_write_1(sc, OOSIOP_ISTAT, 0);

		dstat = oosiop_read_1(sc, OOSIOP_DSTAT);

		if (dstat & OOSIOP_DSTAT_ABRT) {
			sc->sc_nextdsp = oosiop_read_4(sc, OOSIOP_DSP) -
			    (uint32_t)sc->sc_scr - 8;

			if (sc->sc_nextdsp == Ent_p_resel_msgin_move &&
			    (oosiop_read_1(sc, OOSIOP_SBCL) & OOSIOP_ACK)) {
				if ((dstat & OOSIOP_DSTAT_DFE) == 0)
					oosiop_flush_fifo(sc);
				sc->sc_nextdsp += 8;
			}
		}

		if (dstat & OOSIOP_DSTAT_SSI) {
			sc->sc_nextdsp = oosiop_read_4(sc, OOSIOP_DSP) -
			    (uint32_t)sc->sc_scr;
			printf("ncsc: single step %p\n", sc->sc_nextdsp);
		}

		if (dstat & OOSIOP_DSTAT_SIR) {
			if ((dstat & OOSIOP_DSTAT_DFE) == 0)
				oosiop_flush_fifo(sc);
			oosiop_scriptintr(sc);
		}

		if (dstat & OOSIOP_DSTAT_WTD) {
			printf("ncsc: DMA time out\n");
			oosiop_reset(sc);
		}

		if (dstat & OOSIOP_DSTAT_IID) {
			dcmd = oosiop_read_4(sc, OOSIOP_DBC);
			if ((dcmd & 0xf8000000) == 0x48000000) {
				printf("ncsc: REQ asserted on WAIT DISCONNECT\n");
				sc->sc_nextdsp = Ent_phasedispatch; /* XXX */
			} else {
				printf("ncsc: invalid SCRIPTS instruction "
				    "addr=%p dcmd=%p dsps=%p\n",
				    oosiop_read_4(sc, OOSIOP_DSP) - 8, dcmd,
				    oosiop_read_4(sc, OOSIOP_DSPS));
				oosiop_reset(sc);
				oosiop_load_script(sc);
			}
		}

		if ((dstat & OOSIOP_DSTAT_DFE) == 0)
			oosiop_clear_fifo(sc);
	}

	/* SCSI interrupts */
	if (istat & OOSIOP_ISTAT_SIP) {
		if (istat & OOSIOP_ISTAT_DIP)
			delay(1);
		sstat0 = oosiop_read_1(sc, OOSIOP_SSTAT0);

		if (sstat0 & OOSIOP_SSTAT0_M_A) {
			/* SCSI phase mismatch during MOVE operation */
			oosiop_phasemismatch(sc);
			sc->sc_nextdsp = Ent_phasedispatch;
		}

		if (sstat0 & OOSIOP_SSTAT0_STO) {
			if (sc->sc_curcb) {
				sc->sc_curcb->flags |= CBF_SELTOUT;
				oosiop_done(sc, sc->sc_curcb);
			}
		}

		if (sstat0 & OOSIOP_SSTAT0_SGE) {
			printf("ncsc: SCSI gross error\n");
			oosiop_reset(sc);
		}

		if (sstat0 & OOSIOP_SSTAT0_UDC) {
			/* XXX */
			if (sc->sc_curcb) {
				printf("ncsc: unexpected disconnect\n");
				oosiop_done(sc, sc->sc_curcb);
			}
		}

		if (sstat0 & OOSIOP_SSTAT0_RST)
			oosiop_reset(sc);

		if (sstat0 & OOSIOP_SSTAT0_PAR)
			printf("ncsc: parity error\n");
	}

	/* Start next command if available */
	if (sc->sc_nextdsp == Ent_wait_reselect && sc->sc_pending != 0) {
		sc->sc_pending = 0;
		cb = sc->sc_curcb = &sc->sc_cb;
		sc->sc_nexus = cb;
		oosiop_setup_dma(sc);
		oosiop_setup_syncxfer(sc);
		sc->sc_nextdsp = Ent_start_select;
	}

	/* Restart script */
	oosiop_write_4(sc, OOSIOP_DSP, sc->sc_nextdsp + (uint32_t)sc->sc_scr);
}

void
oosiop_scriptintr(struct oosiop_softc *sc)
{
	struct oosiop_cb *cb;
	uint32_t icode;
	uint32_t dsp;
	int i;
	u_int8_t sfbr, resid, resmsg;

	cb = sc->sc_curcb;
	icode = oosiop_read_4(sc, OOSIOP_DSPS);

	switch (icode) {
	case A_int_done:
		if (cb)
			oosiop_done(sc, cb);
		break;

	case A_int_msgin:
		if (cb)
			oosiop_msgin(sc, cb);
		break;

	case A_int_extmsg:
		/* extended message in DMA setup request */
		sfbr = oosiop_read_1(sc, OOSIOP_SFBR);
		oosiop_fixup_move(sc, Ent_p_extmsgin_move, sfbr,
		    (uint32_t)cb->xfer +
		    offsetof(struct oosiop_xfer, msgin[2]));
		sc->sc_nextdsp = Ent_rcv_extmsg;
		break;

	case A_int_resel:
		/* reselected */
		resid = oosiop_read_1(sc, OOSIOP_SFBR);
		for (i = 0; i < OOSIOP_NTGT; i++)
			if (resid & (1 << i))
				break;
		if (i == OOSIOP_NTGT) {
			printf("ncsc: missing reselection target id\n");
			break;
		}
		sc->sc_resid = i;
		sc->sc_nextdsp = Ent_wait_resel_identify;

		if (cb) {
			/* Current command was lost arbitration */
			sc->sc_nexus = NULL;
			sc->sc_curcb = NULL;
		}

		break;

	case A_int_res_id:
		cb = sc->sc_nexus;
		resmsg = oosiop_read_1(sc, OOSIOP_SFBR);
		if (MSG_ISIDENTIFY(resmsg) && cb &&
		    (resmsg & MSG_IDENTIFY_LUNMASK) == cb->lun) {
			sc->sc_curcb = cb;
			/* might be overkill */
			oosiop_setup_dma(sc);
			oosiop_setup_syncxfer(sc);
			if (cb->curdp != cb->savedp) {
				cb->curdp = cb->savedp;
				oosiop_setup_sgdma(sc, cb);
			}
			sc->sc_nextdsp = Ent_ack_msgin;
		} else {
			/* Reselection from invalid target */
			oosiop_reset_bus(sc);
		}
		break;

	case A_int_resfail:
		/* reselect failed */
		break;

	case A_int_disc:
		/* disconnected */
		sc->sc_curcb = NULL;
		break;

	case A_int_err:
		/* generic error */
		dsp = oosiop_read_4(sc, OOSIOP_DSP);
		printf("ncsc: script error at %p\n",
		    dsp - 8);
		sc->sc_curcb = NULL;
		break;

	case DATAIN_TRAP:
		printf("ncsc: unexpected datain\n");
		/* XXX: need to reset? */
		break;

	case DATAOUT_TRAP:
		printf("ncsc: unexpected dataout\n");
		/* XXX: need to reset? */
		break;

	default:
		printf("ncsc: unknown intr code %p\n",
		    icode);
		break;
	}
}

void
oosiop_msgin(struct oosiop_softc *sc, struct oosiop_cb *cb)
{
	struct oosiop_xfer *xfer;
	int msgout;

	xfer = cb->xfer;
	sc->sc_nextdsp = Ent_ack_msgin;
	msgout = 0;

	switch (xfer->msgin[0]) {
	case MSG_SAVEDATAPOINTER:
		cb->savedp = cb->curdp;
		break;

	case MSG_RESTOREPOINTERS:
		if (cb->curdp != cb->savedp) {
			cb->curdp = cb->savedp;
			oosiop_setup_sgdma(sc, cb);
		}
		break;

	case MSG_MESSAGE_REJECT:
		break;

	default:
		/* Reject message */
		xfer->msgout[0] = MSG_MESSAGE_REJECT;
		cb->msgoutlen = 1;
		msgout = 1;
	}

	if (msgout) {
		oosiop_fixup_move(sc, Ent_p_msgout_move, cb->msgoutlen,
		    (uint32_t)cb->xfer +
		    offsetof(struct oosiop_xfer, msgout[0]));
		sc->sc_nextdsp = Ent_sendmsg;
	}
}


code:
/*	$OpenBSD: oosiop.h,v 1.1 2013/10/08 21:55:21 miod Exp $	*/

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

void *oosiop_attach(uint32_t, int, int);
void oosiop_detach(void *);
int oosiop_scsicmd(void *, void *, size_t, void *, size_t, size_t *);


code:
/*	$OpenBSD: scsi.c,v 1.4 2014/07/12 19:01:49 tedu Exp $	*/

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

#include <sys/types.h>
#include <sys/param.h>
#include <stand.h>

#include "libsa.h"
#include "prom.h"

#include "scsi.h"
#include <scsi/scsi_disk.h>

struct scsi_private *
scsi_initialize(const char *ctrlname, int ctrl, int unit, int lun, int part)
{
	struct scsi_private *priv;

	priv = alloc(sizeof(struct scsi_private));
	if (priv == NULL)
		return NULL;

	memset(priv, 0, sizeof(struct scsi_private));
	priv->part = part;

	/* provide default based upon system type */
	if (*ctrlname == '\0') {
		switch (cpuid()) {
		case AVIION_300_310:
		case AVIION_400_4000:
		case AVIION_410_4100:
		case AVIION_300C_310C:
		case AVIION_300CD_310CD:
		case AVIION_300D_310D:
		case AVIION_4300_25:
		case AVIION_4300_20:
		case AVIION_4300_16:
			ctrlname = "insc";
			break;
		case AVIION_4600_530:
			ctrlname = "ncsc";
			break;
		}
	}

	if (strcmp(ctrlname, "insc") == 0) {
		if (ctrl == 0) {
			*(volatile uint32_t *)0xfff840c0 = 0x6e;
			ctrl = 0xfff8a000;
		} else
			goto done;

		if (badaddr((void *)ctrl, 4) != 0)
			goto done;

		/* initialize controller */
		priv->scsicookie = oaic_attach(ctrl, unit, lun);
		priv->scsicmd = oaic_scsicmd;
		priv->scsidetach = oaic_detach;
	} else
	if (strcmp(ctrlname, "ncsc") == 0) {
		if (ctrl == 0)
			ctrl = 0xfffb0000;
		else if (ctrl == 1)
			ctrl = 0xfffb0080;
		else
			goto done;

		if (badaddr((void *)ctrl, 4) != 0)
			goto done;

		/* initialize controller */
		priv->scsicookie = oosiop_attach(ctrl, unit, lun);
		priv->scsicmd = oosiop_scsicmd;
		priv->scsidetach = oosiop_detach;
	}

done:
	if (priv->scsicookie == NULL) {
		free(priv, sizeof(struct scsi_private));
		priv = NULL;
	}

	return priv;
}

int
scsi_tur(struct scsi_private *priv)
{
	struct scsi_test_unit_ready cmd;
	int i, rc;

	for (i = TEST_READY_RETRIES; i != 0; i--) {
		memset(&cmd, 0, sizeof cmd);
		cmd.opcode = TEST_UNIT_READY;

		rc = (*priv->scsicmd)(priv->scsicookie,
		    &cmd, sizeof cmd, NULL, 0, NULL);
		if (rc == 0)
			break;
	}

	return rc;
}

int
scsi_read(struct scsi_private *priv, daddr32_t blk, size_t size, void *buf,
    size_t *rsize)
{
	union {
		struct scsi_rw rw;
		struct scsi_rw_big rw_big;
		struct scsi_rw_12 rw_12;
	} cmd;
	int nsecs;
	size_t cmdlen;
	int i, rc;

	nsecs = (size + DEV_BSIZE - 1) >> _DEV_BSHIFT;

	for (i = SCSI_RETRIES; i != 0; i--) {
		memset(&cmd, 0, sizeof cmd);

		/* XXX SDEV_ONLYBIG quirk */
		if ((blk & 0x1fffff) == blk && (nsecs & 0xff) == nsecs) {
			cmd.rw.opcode = READ_COMMAND;
			_lto3b(blk, cmd.rw.addr);
			cmd.rw.length = nsecs;
			cmdlen = sizeof cmd.rw;
		} else if ((nsecs & 0xffff) == nsecs) {
			cmd.rw_big.opcode = READ_BIG;
			_lto4b(blk, cmd.rw_big.addr);
			_lto2b(nsecs, cmd.rw_big.length);
			cmdlen = sizeof cmd.rw_big;
		} else {
			cmd.rw_12.opcode = READ_12;
			_lto4b(blk, cmd.rw_12.addr);
			_lto4b(nsecs, cmd.rw_12.length);
			cmdlen = sizeof cmd.rw_12;
		}

		rc = (*priv->scsicmd)(priv->scsicookie,
		    &cmd, sizeof cmd, buf, size, rsize);
		if (rc == 0)
			break;
	}

	return rc;
}


code:
/*	$OpenBSD: scsi.h,v 1.4 2013/10/16 16:59:34 miod Exp $	*/

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>

/*
 * The following are defines from <scsi/scsiconf.h> not available to
 * !_KERNEL code, under the following licence terms:
 */

/*	OpenBSD: scsiconf.h,v 1.157 2013/09/27 11:43:19 krw Exp	*/
/*	$NetBSD: scsiconf.h,v 1.35 1997/04/02 02:29:38 mycroft Exp $	*/

/*
 * Copyright (c) 1993, 1994, 1995 Charles Hannum.  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 by Charles Hannum.
 * 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.
 */

/*
 * Originally written by Julian Elischer (julian@tfs.com)
 * for TRW Financial Systems for use under the MACH(2.5) operating system.
 *
 * TRW Financial Systems, in accordance with their agreement with Carnegie
 * Mellon University, makes this software available to CMU to distribute
 * or use in any manner that they see fit as long as this message is kept with
 * the software. For this reason TFS also grants any other persons or
 * organisations permission to use or modify this software.
 *
 * TFS supplies this software to be publicly redistributed
 * on the understanding that TFS is not responsible for the correct
 * functioning of this software in any circumstances.
 *
 * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
 */

#define	ITSDONE		0x00008	/* the transfer is as done as it gets	*/
#define	SCSI_DATA_IN	0x00800	/* expect data to come INTO memory	*/
#define	SCSI_DATA_OUT	0x01000	/* expect data to flow OUT of memory	*/

#define XS_NOERROR	0	/* there is no error, (sense is invalid)  */
#define XS_SENSE	1	/* Check the returned sense for the error */
#define	XS_DRIVER_STUFFUP 2	/* Driver failed to perform operation	  */
#define XS_SELTIMEOUT	3	/* The device timed out.. turned off?	  */
#define XS_TIMEOUT	4	/* The Timeout reported was caught by SW  */
#define XS_BUSY		5	/* The device busy, try again later?	  */
#define XS_SHORTSENSE   6	/* Check the ATAPI sense for the error */
#define XS_RESET	8	/* bus was reset; possible retry command  */
#define XS_NO_CCB	9	/* device should requeue io and retry */

#define TEST_READY_RETRIES	5

#define SCSI_RETRIES		4

#include <scsi/scsi_message.h>

#include <sys/disklabel.h>

struct scsi_private {
	void	*scsicookie;
	int	(*scsicmd)(void *, void *, size_t, void *, size_t, size_t *);
	void	(*scsidetach)(void *);

	struct disklabel label;
	int	part;
};

struct scsi_private *
	scsi_initialize(const char *, int, int, int, int);

int	scsi_tur(struct scsi_private *);
int	scsi_read(struct scsi_private *, daddr32_t, size_t, void *, size_t *);

#include "oaic.h"
#include "oosiop.h"


code:
/*	$OpenBSD: sd.c,v 1.7 2014/07/12 19:01:49 tedu Exp $	*/

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

#include <sys/types.h>
#include <sys/param.h>
#include <stand.h>

#include "scsi.h"

int
sdopen(struct open_file *f, const char *ctrlname, int ctrl, int unit, int lun,
    int part)
{
	struct scsi_private *priv;
	struct vdm_label *vdl;
	int rc;
	char buf[DEV_BSIZE];
	char *msg;
	size_t z;

	f->f_devdata = priv = scsi_initialize(ctrlname, ctrl, unit, lun, part);
	if (priv == NULL)
		return ENXIO;

	/* send TUR */
	rc = scsi_tur(priv);
	if (rc != 0)
		return EIO;

	/* read disklabel. We expect a VDM label since this is the only way
	 * we can boot from disk.  */
	rc = scsi_read(priv, VDM_LABEL_SECTOR, sizeof buf, buf, &z);
	if (rc != 0 || z != sizeof buf)
		return EIO;

	vdl = (struct vdm_label *)(buf + VDM_LABEL_OFFSET);
	if (vdl->signature != VDM_LABEL_SIGNATURE)
		vdl = (struct vdm_label *)(buf + VDM_LABEL_OFFSET_ALT);
	if (vdl->signature != VDM_LABEL_SIGNATURE)
		return EINVAL;

	/* XXX ought to search for an OpenBSD vdmpart too. Too lazy for now */
	rc = scsi_read(priv, LABELSECTOR, sizeof buf, buf, &z);
	if (rc != 0 || z != sizeof buf)
		return EIO;

	msg = getdisklabel(buf, &priv->label);
	if (msg != NULL) {
		printf("%s\n", msg);
		return EINVAL;
	}

	return 0;
}

int
sdstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
    size_t *rsize)
{
	struct scsi_private *priv = devdata;

	if (rw != F_READ)
		return EROFS;

	blk += priv->label.d_partitions[priv->part].p_offset;

	return scsi_read(priv, blk, size, buf, rsize) != 0 ? EIO : 0;
}

int
sdclose(struct open_file *f)
{
	struct scsi_private *priv;

	if (f->f_devdata != NULL) {
		priv = (struct scsi_private *)f->f_devdata;
		if (priv->scsicookie != NULL)
			(*priv->scsidetach)(priv->scsicookie);
		free(priv, sizeof(struct scsi_private));
		f->f_devdata = NULL;
	}

	return 0;
}


code:
/*	$OpenBSD: version.c,v 1.6 2014/02/24 20:15:37 miod Exp $ */

const char version[] = "0.6";


code:
/Makefile/1.1/Sun Sep 29 17:51:34 2013//TOPENBSD_5_8_BASE
/installboot.8/1.2/Sun Sep 29 21:30:49 2013//TOPENBSD_5_8_BASE
/installboot.c/1.3/Tue Oct  1 20:32:30 2013//TOPENBSD_5_8_BASE
D


code: src/sys/arch/aviion/stand/installboot


code:
#	$OpenBSD: Makefile,v 1.1 2013/09/29 17:51:34 miod Exp $

MAN=		installboot.8
MANSUBDIR=	aviion

.if ${MACHINE} == "aviion"
PROG=		installboot
BINDIR=		/usr/mdec
DPADD=		${LIBUTIL}
LDADD=		-lutil
LDSTATIC=	-static
.else
NOPROG=
.endif

.include <bsd.prog.mk>


code:
/*	$OpenBSD: installboot.c,v 1.3 2013/10/01 20:32:30 miod Exp $	*/

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

#include <unistd.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include <sys/disklabel.h>
#include <sys/stat.h>

int	nowrite, verbose;
char	*boot, *dev;

void	analyze_label_sector(uint8_t *, struct vdm_label **,
	    struct vdm_boot_info **);
void	read_sector(int, uint32_t, void *);
void	usage(void);
void	write_sector(int, uint32_t, void *);

#define	VDM_BLOCK_SIZE	0x200

uint8_t buf[VDM_BLOCK_SIZE];

void
usage()
{
	fprintf(stderr, "usage: installboot [-nv] boot rawdev\n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	int c;
	int bootfd, devfd;
	size_t chunk;
	uint32_t blkno;
	struct stat stat;
	struct vdm_label *dl;
	struct vdm_boot_info *bi;

	while ((c = getopt(argc, argv, "nv")) != -1) {
		switch (c) {
		case 'n':
			nowrite = 1;
			break;
		case 'v':
			verbose = 1;
			break;
		default:
			usage();
		}
	}

	if (argc - optind != 2)
		usage();

	boot = argv[optind];
	dev = argv[optind + 1];

	bootfd = open(boot, O_RDONLY);
	if (bootfd < 0)
		err(1, "open(%s)", boot);

	if (fstat(bootfd, &stat) != 0)
		err(1, "fstat(%s)", boot);

	devfd = opendev(dev, nowrite ? O_RDONLY : O_RDWR, OPENDEV_PART, &dev);
	if (devfd < 0)
		err(1, "open(%s)", dev);

	/*
	 * Figure out the boot area span.
	 */

	read_sector(devfd, VDM_LABEL_SECTOR, buf);
	analyze_label_sector(buf, &dl, &bi);

	if (bi != NULL) {
		if (verbose)
			printf("boot area: sectors %u-%u\n",
			   bi->boot_start, bi->boot_start + bi->boot_size - 1);
	} else {
		errx(1, "no boot area found on %s", dev);
	}

	/*
	 * Write the file into the boot area.
	 */

	if (stat.st_size > bi->boot_size * VDM_BLOCK_SIZE)
		err(1, "boot file too large, boot area is only %u bytes",
		    bi->boot_size * VDM_BLOCK_SIZE);

	if (nowrite)
		return 0;

	blkno = bi->boot_start;
	if (verbose)
		printf("writing %lld bytes from sector %u onwards\n",
		    stat.st_size, blkno);
	while (stat.st_size != 0) {
		if (stat.st_size > sizeof buf)
			chunk = sizeof buf;
		else {
			chunk = stat.st_size;
			memset(buf, 0, sizeof buf);
		}
		if (read(bootfd, buf, chunk) != chunk)
			err(1, "read");
		write_sector(devfd, blkno++, buf);
		stat.st_size -= chunk;
	}

	close(bootfd);
	close(devfd);
	return 0;
}

void
read_sector(int fd, uint32_t secno, void *buf)
{
	if (lseek(fd, (off_t)secno * VDM_BLOCK_SIZE, SEEK_SET) == -1)
		err(1, "lseek");

	if (read(fd, buf, VDM_BLOCK_SIZE) != VDM_BLOCK_SIZE)
		err(1, "read(%d,%08x)", fd, secno);
}

void
write_sector(int fd, uint32_t secno, void *buf)
{
	if (lseek(fd, (off_t)secno * VDM_BLOCK_SIZE, SEEK_SET) == -1)
		err(1, "lseek");

	if (write(fd, buf, VDM_BLOCK_SIZE) != VDM_BLOCK_SIZE)
		err(1, "write(%d,%08x)", fd, secno);
}

void
analyze_label_sector(uint8_t *sector, struct vdm_label **dl,
    struct vdm_boot_info **dbi)
{
	struct vdm_label *l;
	struct vdm_boot_info *bi;

	l = (struct vdm_label *)(sector + VDM_LABEL_OFFSET);
	if (betoh32(l->signature) != VDM_LABEL_SIGNATURE) {
		l = (struct vdm_label *)(sector + VDM_LABEL_OFFSET_ALT);
		if (betoh32(l->signature) != VDM_LABEL_SIGNATURE)
			l = NULL;
	}

	if (l != NULL) {
		bi = (struct vdm_boot_info *)
		    (sector + VDM_BLOCK_SIZE - sizeof *bi);
		if (betoh32(bi->signature) != VDM_LABEL_SIGNATURE)
			bi = NULL;
	} else
		bi = NULL;

	*dl = l;
	*dbi = bi;
}


code: src/sys/arch/aviion/stand/libprom


path: sys/arch/aviion/stand/libprom/CVS/Tag


code:
#	$OpenBSD: Makefile,v 1.3 2013/10/16 16:59:35 miod Exp $

LIB=prom

NOPIC=
NOPROFILE=
NOPIE=

S=${.CURDIR}/../../../..
DIR_SA=$S/lib/libsa

CFLAGS+=-I${.CURDIR}/../../include -I${DIR_SA}

SRCS=	cpuid.c getc.c getenaddr.c halt.c putc.c

install:

.include <bsd.lib.mk>

path: sys/arch/aviion/stand/libprom/Makefile


code:
#	$OpenBSD: Makefile.inc,v 1.1 2006/05/16 22:48:18 miod Exp $

LIB_PROM_DIR=${S}/arch/aviion/stand/libprom

LIBPROM_DIR!= cd ${LIB_PROM_DIR}; \
     printf "xxx:\n\techo \$${.OBJDIR}\n" | ${MAKE} -r -s -f - xxx

LIBPROM=${LIBPROM_DIR}/libprom.a

$(LIBPROM):	.NOTMAIN __always_make_libprom
	@echo making sure the libprom is up to date...
	@(cd ${LIB_PROM_DIR}; ${MAKE})

__always_make_libprom:	.NOTMAIN


code:
/*	$OpenBSD: cpuid.c,v 1.2 2014/03/29 18:09:29 guenther Exp $	*/

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

#include <sys/types.h>

#include "prom.h"

uint
cpuid(void)
{
	uint ret;

	SCM_CALL(SCM_CPUID);
	__asm__ volatile ("or %0, %%r0, %%r2" : "=r" (ret));

	return ret;
}


code:
/*	$OpenBSD: getc.c,v 1.3 2014/03/29 18:09:29 guenther Exp $	*/
/*
 * Copyright (c) 2006, Miodrag Vallat
 *
 * 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.
 *
 * 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.
 */

#include <sys/types.h>

#include "prom.h"

int
getchar(void)
{
	u_int ret;

	SCM_CALL(SCM_CHAR);
	__asm__ volatile ("or %0, %%r0, %%r2" : "=r" (ret));

	return (ret & 0xff);
}


code:
/*	$OpenBSD: getenaddr.c,v 1.3 2014/03/29 18:09:29 guenther Exp $	*/
/*
 * Copyright (c) 2006, Miodrag Vallat
 *
 * 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.
 *
 * 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.
 */

#include <sys/types.h>

#include "prom.h"

void
scm_getenaddr(u_char *ea)
{
	u_char *addr;

	SCM_CALL(SCM_COMMID);
	__asm__ volatile ("or %0, %%r0, %%r2" : "=r" (addr));

	ea[0] = addr[0];
	ea[1] = addr[1];
	ea[2] = addr[2];
	ea[3] = addr[3];
	ea[4] = addr[4];
	ea[5] = addr[5];
}


code:
/*	$OpenBSD: halt.c,v 1.1 2006/05/16 22:48:18 miod Exp $	*/
/*
 * Copyright (c) 2006, Miodrag Vallat
 *
 * 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.
 *
 * 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.
 */

#include <sys/types.h>

#include "prom.h"

__dead void
_rtt()
{
	SCM_CALL(SCM_HALT);
	for (;;) ;
}