static char rcsid[] = "$Header: if_mo.c,v 820.1 86/12/04 19:55:37 root Exp $";
static char sccsid[] = "%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1985				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
/*
 * mo.c --
 *	Driver for the Multibus Slave Transporter (hardware rev 840912).
 *
 * History:
 *
 *	8.3 (sas)	851119
 *
 *	Added the appropriate doo-dahs for working with 68020.
 *
 *	8.2 (sas)	850605
 *
 *	Added support for BULK protocol.
 *	
 *	8.1 (sas)	850418
 *
 *	Modified to support 4.2bsd.  Removed many of the statistics
 *	because they were really only useful in bringing the driver
 *	up. Changed the semantics of resetting.  Modified tomioctl
 *	to support ioctl's through the socket interface.  Added support
 *	for SIOCSIFADDR.  Got rid of support for summing of each packet.
 *	Got rid of references to XMITWAIT.  Changed the sizes of entries
 *	in the tom_softc structure.  Added support for ARP.
 *	Took the spin code out of interrupt routeine.  Added support for
 *	probe.  Fixed bug with IP broadcast packets.
 *
 *	7.2 (sas)	850304	
 *
 *	Call mostart from every setrcv return.
 *
 *	7.1 (sas)	850214	
 *
 *	Original version of this driver as
 *	shipped for SCALDsystem IV beta period.
 */

/*
 * The version number of the driver.
 */
short mo_version[] = {
	8,	3 };

#define NMOM 1				/* Define only 1 unit */

#include "../h/errno.h"			/* Error numbers */
#include "../h/param.h"			/* Machine dependent parameters */
#include "../h/ioctl.h"			/* I/O control structures */
#include "../h/uio.h"			/* Iovec structure */
#include "../h/socket.h"		/* Socket address structure */
#include "../h/mbuf.h"			/* Mbuf header definitions */
#ifdef M68020
#include "../s32/cpu.h"			/* "flushWriteQueue" */
#endif M68020
#include "../net/if.h"			/* Network interface structs */
#include "../s32dev/mbvar.h"		/* Multibus definitions */
#include "../s32dev/omni.h"		/* Definition of generic Omninet */
#include "../s32dev/if_moreg.h"	/* Transporter hardware */
#include "../net/netisr.h"		/* Net interrupt service */
#include "../net/route.h"		/* Internet Routing Table */
#ifdef INET
#include "../netinet/in.h"		/* Internet definitions */
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"		/* IP (Internet Protocol) definitions */
#include "../netinet/ip_var.h"		/* IP global variables */
#include "../netinet/if_ether.h"	/* Address resolution protocol */
#endif INET
#ifdef VALIDnet
#ifdef bsd42
#include "../vnet/vnet.h"	/* Valid Network definitions */
#include "../conn/conn.h"	/* Valid connection management */
#include "../rpc/rpc.h"		/* Remote procedure call definitions */
#else bsd42
#include "../vnet/vnet.h"	/* Valid Network definitions */
#include "../rpc/rpc.h"		/* Remote procedure call definitions */
$include "../rpc/rpc_vars.h"	/* RPC global variables */
#endif bsd42
#endif VALIDnet

/*
 * These macros are called to lock out interrupts from the Multibus Omninet
 * board.
 */
#define splMOMLOCK()	spl5()

extern struct ifnet loif;
int moprobe(), moattach(), mointr(), mooutput();
struct mbuf *mogetm();
u_short mostd[] = {MOIOADDR0, MOIOADDR1, 0};
extern int hz;

/*
 * These are used to schedule timeouts for interrupts.
 */
short motimo = 1;		/* Call timeout */
int moxmittimo();

struct mb_device *moinfo[NMOM];
struct mb_driver modriver = 
	{ 0, moprobe, 0, moattach, mostd, "mo", moinfo};


/*
 * We allocate the on-board memory in the following way:  set aside space
 * for command blocks, result records, and data records by filling
 * pointers into the on-board buffer at compile time.  We have
 * 2 receive buffers and 1 transmit buffer.  Associated with each buffer
 * is a command block and a result record.  We switch between receive
 * buffers by chainging the value of ms_rcvindex.
 */
struct omniccb *morccb[] =	/* receive command block */
	{ (struct omniccb *)0x10, (struct omniccb *)0x20 };
char *mordata[] =		/* receive data area */
	{ (char *)0x1000, (char *)0x1800 };
struct omnirslt *morrslt[] =	/* receive result record */
	{ (struct omnirslt *)0x41, (struct omnirslt *)0x51 };

struct omniccb *mocccb[] =	/* generic command block */
	{ (struct omniccb *)0x30 };
char *mocdata[] =		/* generic command data area */
	{ (char *)0x2000 };
struct omnirslt *mocrslt[] =	/* generic command result record */
	{ (struct omnirslt *)0x61 };

struct omniccb *moresetccb = (struct omniccb *) 0x80;
struct omnirslt *moresetrslt = (struct omnirslt *) 0x71;


/*
 * Per controller data structure.
 */
struct	mo_softc {
	unsigned char	ms_omniaddr;	/* Omninet address of local host */
	char		ms_init;	/* Interface initialized */
	short		ms_unitActive;	/* Unit is active */
	short		ms_count;	/* Count */
	short		ms_state;	/* Controller state */
	short		ms_present;	/* Set if present */
	short		ms_unit;	/* Unit number of this controller */
	struct moreg	*ms_reg;	/* Controller's registers */
	struct ifqueue	ms_output;	/* Output queue */
	int		ms_rcvindex;	/* Active receive index */
	int		ms_cmdindex;	/* Active generic command index */
#ifdef INET
	struct arpcom	ms_ac;		/* Internet interface */
#define if_ip		ms_ac.ac_if
#endif INET
#ifdef VALIDnet
#ifdef VALID_BULK
	struct ifnet	if_bulk;	/* VALID BULK interface */
#endif VALID_BULK
	struct ifnet	if_conn;	/* VALID CONN interface */
	struct ifnet	if_rpc;		/* VALID RPC interface */
#endif VALIDnet
	struct omnistats	ms_stats;	/* Driver statistics */
};

short moresetretry[NMOM];

/*
 * Maximum length of the output queue (see comment below)
 */
#define MS_MAXIFQLEN	16

/*
 * Possible values for ms_state in ms_softc structure
 */
#define	MS_RUNNING	0x1		/* Controller is running */
#define MS_RCVACTV	0x2		/* Controller has a receive active */
#define MS_SETUP	0x4		/* Controller has a setup active */
#define MS_OUTACTV	0x8		/* Controller has output active */
#define MS_REARMWAITING	0x10		/* A Rearm is waiting */
#define MS_RESETACTV	0x20		/* Reset in progress */



struct mo_softc mo_softc[NMOM];	/* soft controller structs */
struct omnistats *mstats = &mo_softc[0].ms_stats;


short	modebug = 0;			/* debug flags */
int	moipl = 5;			/* interrupt level -- kluge */
int	moinitcount = 0x3000;		/* kluge -- count of polls to init */
int	mocount = 1;			/* count of xmits to receives */
u_char	mobaddr = 0xFF;		/* broadcast address  -- kluge */
short	mo_xport = 0;			/* Set if accessing window */
short	mo_firstintr[NMOM];		/* Set if first interrupt */

/*
 * These variables are used for catching the calls to the
 * access routines.
 */


/* return 1 if the transporter is ready to be strobed */
#define mordy(reg)	((reg)->mo_xrdy & 0x80 ? 1 : 0)
/* return 1 if interrupt is pending */
#define mointrpend(reg)	((reg)->mo_intrpend & 0x1 ? 1 : 0)
/* clear pending interrupts */
#define moclearintr(reg)	(reg)->mo_intrpend = 0
/* increment driver statistic */
#define moIncStat(ms,stat)	((ms)->ms_stats.stat++)


/*
 * moprobe --
 *	Probe for the Multibus Ommninet Board.  If we find the
 *	I/O registers, we return with a non-zero value.  We don't
 *	try to send an interrupt.
 */
moprobe(reg, intr, unit)
	caddr_t	reg;
	int (*intr)();
	int unit;
{
	register int i;
	register struct mo_regs *addr = (struct mo_regs *)reg;
	register struct mo_softc *ms = &mo_softc[unit];
	int mointr();
	extern int cvec;
	int returnval;
	int s;

	if (intr != mointr)
		return 0;

	ms->ms_unit = unit;
	ms->ms_omniaddr = -1;
	ms->ms_reg = (struct moreg *)reg;

	for (i = 0; i < NMOM; i++) {
		if (moinfo[i] && moinfo[i]->md_alive &&
			moinfo[i]->md_addr == reg)
				return 0;
	}

	i = *reg;		/* Probe the base register */
	ms->ms_present = 1;	/* Set present flag */

	returnval = mohardinit(unit);
	/*
	 * If the caller didn't call our interrupt routine,
	 * attempt to clear the interrupt here.  First, make
	 * sure an interrupt is present.  If no interrupt is present
	 * and we never got an interrupt, don't change cvec.
	 */

	s = splMOMLOCK();

	if (!mo_firstintr[unit]) {
		for (i = 0; i < moinitcount && !mointrpend(ms->ms_reg);
								i++);
		if (i >= moinitcount) {
			splx(s);
			return 0;
		}
		moclearintr(ms->ms_reg);
	}
	splx(s);

	/*
	 * Always assume that we know what cvec should be -- kluge.
	 */
	if (returnval)
		cvec = moipl - 1;
	return(returnval);
}


moattach(md)
	struct mb_device *md;
{
	register struct mo_softc *ms = &mo_softc[md->md_unit];
	register struct ifnet *ifp;
	extern int moinit(), moioctl(), moreset();

	momessage("", md->md_unit);

	/*
	 * We told people they could use address 0.  Not any more...
	 */
	if (ms->ms_omniaddr == 0) {
		printf("Omninet address 0 can no longer be used.\n");
		ms->ms_init = 0;
		return 0;
	}

	if (ms->ms_omniaddr <= 0x3F) {
	  printf("Omninet host address 0x%02X, transporter at 0x%x\n",
		ms->ms_omniaddr, ms->ms_reg);
	} else {
		printf("bad Omninet address 0x%02X\n",
			ms->ms_omniaddr);
		md->md_alive = 0;
	}

	/*
	 * Set the arpcom address field -- kluge
	 */
	bzero(ms->ms_ac.ac_enaddr, 6);
	ms->ms_ac.ac_enaddr[5] = ms->ms_omniaddr;

	if (!md->md_alive)
		return 0;
	
	/*
	 * Set the addresses in the IP/BCAST/CONN/RPC interfaces
	 */
	moinitaddress(ms);

	/*
	 * Kluge -- set the maximum queue length for output here.
	 */
	ms->ms_output.ifq_maxlen = MS_MAXIFQLEN;

	/*
	 * Fill and attach the network interface data bases
	 */
	
#ifdef INET
	/* DARPA Internet (IP) */

	ifp = &ms->if_ip;
	ifp->if_unit = md->md_unit;
	ifp->if_name = "mo";
	ifp->if_mtu = OMNIMTU;
	ifp->if_init = moinit;
	ifp->if_ioctl = moioctl;
	ifp->if_output = mooutput;
	ifp->if_reset = moreset;
	if_attach(ifp);
#endif INET

#ifdef VALIDnet
#ifdef VALID_BULK
	/* VALID BULK */

	ifp = &ms->if_bulk;
	ifp->if_unit = md->md_unit;
	ifp->if_name = "mo-bulk";
	ifp->if_mtu = OMNIMTU;
	ifp->if_init = moinit;
	ifp->if_ioctl = moioctl;
	ifp->if_output = mooutput;
	ifp->if_reset = moreset;
	if_attach(ifp);
#endif VALID_BULK

	/* VALID CONN */

	ifp = &ms->if_conn;
	ifp->if_unit = md->md_unit;
	ifp->if_name = "mo-conn";
	ifp->if_mtu = OMNIMTU;
	ifp->if_init = moinit;
	ifp->if_ioctl = moioctl;
	ifp->if_output = mooutput;
	ifp->if_reset = moreset;
	if_attach(ifp);

	/* VALID RPC */

	ifp = &ms->if_rpc;
	ifp->if_unit = md->md_unit;
	ifp->if_name = "mo-rpc";
	ifp->if_mtu = OMNIMTU;
	ifp->if_init = moinit;
	ifp->if_ioctl = moioctl;
	ifp->if_output = mooutput;
	ifp->if_reset = moreset;
	if_attach(ifp);

#endif VALIDnet

	ms->ms_rcvindex = ms->ms_cmdindex = 0;
}




/*
 * moinitaddress --
 *	Set the addresses of the various interfaces.  This routine is
 *	based on (copied from) setUnitAddress() in ../s32dev/ec.c
 */
moinitaddress(ms)
	register struct mo_softc *ms;
{
	register struct ifnet *ifp;

#ifdef INET
	{
	register struct sockaddr_in *sin;

	sin = (struct sockaddr_in *)&ms->if_ip.if_addr;
	sin->sin_family = AF_INET;
	sin->sin_addr = arpmyaddr((struct arpcom *)0);
	sin = (struct sockaddr_in *)&ms->if_ip.if_broadaddr;
	sin->sin_family = AF_INET;
	}
#endif INET

#ifdef VALIDnet
	{
	register struct sockaddr_vnet *svnet;
	node_t n;

	n.net = 0;
	n.host.high = 0;
	n.host.low = ms->ms_omniaddr & 0xFF;

#ifdef VALID_BULK
	ifp = &ms->if_bulk;
	ifp->if_host[0] = n.host.low;
	ifp->if_net = n.host.high;
	svnet = (struct sockaddr_vnet *)&ifp->if_addr;
	svnet->sa_family = AF_BULK;
	svnet->sa_node = n;
	svnet = (struct sockaddr_vnet *)&ifp->if_broadaddr;
	svnet->sa_family = AF_BULK;
#endif VALID_BULK

	ifp = &ms->if_conn;
	ifp->if_host[0] = n.host.low;
	ifp->if_net = n.host.high;
	svnet = (struct sockaddr_vnet *)&ifp->if_addr;
	svnet->sa_family = AF_CONN;
	svnet->sa_node = n;
	svnet = (struct sockaddr_vnet *)&ifp->if_broadaddr;
	svnet->sa_family = AF_CONN;

	ifp = &ms->if_rpc;
	ifp->if_host[0] = n.host.low;
	ifp->if_net = n.host.high;
	svnet = (struct sockaddr_vnet *)&ifp->if_addr;
	svnet->sa_family = AF_RPC;
	svnet->sa_node = n;
	}
#endif VALIDnet
}

/*
 * moinit --
 *	Initialize the IP interface
 */
moinit(unit)
	int unit;
{
	register struct mo_softc *ms = &mo_softc[unit];
	register struct ifnet *ifp = &ms->if_ip;
	register struct sockaddr_in *sin;
	int s;

	sin = (struct sockaddr_in *)&ifp->if_addr;
	if (sin->sin_addr.s_addr == 0)
		return 0;
	if ((ifp->if_flags & IFF_RUNNING) == 0) {
		s = splimp();
		ifp->if_flags |= IFF_UP|IFF_RUNNING;
		splx(s);
		s = spl7();
		if (!ms->ms_unitActive) {
			ms->ms_unitActive = 1;
			splMOMLOCK();
			mostart(ms);
		}
		splx(s);
	}
	if_rtinit(ifp, RTF_UP);
	arpattach(&ms->ms_ac);
	arpwhohas(&ms->ms_ac, &sin->sin_addr);
}

short moprint = 0;

/*
 * moreset --
 *	Handles resets to the driver.
 */
moreset(unit)
	int unit;
{
	register struct mo_softc *ms = &mo_softc[unit];
	int s;

	if (unit > NMOM)
		return 0;
	
	s = splMOMLOCK();
	if (!(ms->ms_state & MS_RESETACTV))
		ms->ms_state |= MS_RESETACTV;
	else {
		momessage("reset in progress.\n", unit);
		splx(s);
		return;
	}

	if (moprint)
		momessage("**RESET**\n", unit);

	moIncStat(ms,reset);

	if (mohardinit(unit) && morearmsock(unit)) {
		if (moprint)
			momessage("**RESET COMPLETE**\n", unit);
		splx(s);
		moresetretry[unit] = 0;
		ms->ms_state |= MS_RUNNING;
		ms->ms_state &= ~MS_RESETACTV;
		return 1;
	} else {
		if (moresetretry[unit]++ >= MS_MAXRESET_RETRY) {
			momessage("unable to reset interface.\n", unit);
			momessage("shutting down Omninet.\n", unit);
			ms->ms_init = 0;
		} else {
			momessage("**RESET FAILED**\n", unit);
			momessage("retry in 8 seconds.\n", unit);
			timeout(moreset, unit, hz<<3);
		}
		ms->ms_state = 0;
		splx(s);
		return 0;
	}
}

/*
 * mohardinit --
 *	Do a hardware initialization of the Omninet transporter.
 *	Send an initialize command to the board.
 */
mohardinit(unit)
	int unit;
{
	register struct mo_softc *ms = &mo_softc[unit];
	register i, j;
	struct omniccb ccb;

	for (j = 0; j < 5; j++) {

		bzero(&ccb, sizeof(struct omniccb));
		ccb.cmd = OMNIINIT;
		moset24(&ccb.rslt, moresetrslt);
		mosetbyte(ms->ms_reg, &moresetrslt->rcode, -1);
		mopack(ms->ms_reg, &ccb, moresetccb, sizeof(struct omniccb));

		if (mostrobe(moresetccb, unit)) {
		   for (i = 0; i < moinitcount && 
		    mogetbyte(ms->ms_reg, &moresetrslt->rcode) == -1; i++);
			if (i >= moinitcount)
				continue;
			/*
			moclearintr(ms->ms_reg);
			 */
			if (ms->ms_omniaddr == -1)
				ms->ms_omniaddr = mogetbyte(ms->ms_reg, 
							&moresetrslt->rcode);
			return 1;
		} else
			momessage("couldn't strobe transporter.\n", unit);
	}
	return 0;
}


/*
 * morearmsock --
 *	Re-arm the receive socket after a reset.
 */
morearmsock(unit)
	int unit;
{
	register struct mo_softc *ms = &mo_softc[unit];
	register i, j;

	for (j = 0; j < 5; j++) {
		if (ms->ms_unitActive)
			panic("morearmsock");
		else
			ms->ms_unitActive = 1;
		if (!mosetrcv(ms)) {
			ms->ms_unitActive = 0;
			continue;
		}
		for (i = 0; i < moinitcount && mogetbyte(ms->ms_reg, 
				&morrslt[ms->ms_rcvindex]->rcode) == -1; i++);
		if (i < moinitcount)
			return 1;
		ms->ms_unitActive = 0;
	}
	return 0;
}



/*
 * mooutput --
 *	Called from higher level protocols.  Queue a packet to go out
 *	out over the Omninet.  Takes as arguments the ifnet structure,
 *	an mbuf chain, and a destination socket address.
 */
mooutput(ifp, m0, dst)
	register struct ifnet *ifp;
	register struct mbuf *m0;
	struct sockaddr *dst;
{
	register struct mo_softc *ms = &mo_softc[ifp->if_unit];
	register struct mbuf *m;
	struct mbuf *mcopy;		/* copy of mbuf chain m0 */
	int error = 0;
	u_char omnidst;
	struct omniheader omni_h;
	u_char type;
	int s;

	m = (struct mbuf *)0;
	mcopy = (struct mbuf *)0;

	if (!(ms->ms_state & MS_RUNNING)) {
		error = 0;
		goto bad;
	}


	switch (dst->sa_family) {


#ifdef INET
	case AF_INET:
		{
		struct in_addr idst;
		u_char edst[6];

		idst = ((struct sockaddr_in *)dst)->sin_addr;
		if (!arpresolve(&ms->ms_ac, m0, &idst, edst))
			return 0;
		if (in_lnaof(idst) == INADDR_ANY) {
			mcopy = m_copy(m0, 0, (int)M_COPYALL);
			omnidst = mobaddr;
		} else {
			omnidst = edst[5];
		}
		if (omnidst == ms->ms_omniaddr) {
			mcopy = m0;
			m0 = (struct mbuf *)0;
			goto gotlocal;
		}
		type = OMNITYPE_IP;
		}
		break;

	case AF_UNSPEC:
		{
		/*
		 * We expect an Ethernet header in the data
		 * portion of the socket address structure.
		 * If the Ethernet address is broadcast
		 * (ff.ff.ff.ff.ff.ff), we broadcast it
		 * on Omninet (no loop-back).  Otherwise,
		 * we send it to the address located in
		 * byte 6.
		 */
		register struct ether_header *ec;

		ec = (struct ether_header *)dst->sa_data;
		if (!bcmp(etherbroadcastaddr, ec->destAddr, 6))
			omnidst = mobaddr;
		else
			omnidst = ec->destAddr[5];
		switch (ec->etherType) {
		case ETHERTYPE_IP:
			type = OMNITYPE_IP;
			break;
		case ETHERTYPE_ARP:
			type = OMNITYPE_ARP;
			break;
		default:
			goto bad;
		}
		break;
		}
		

#endif INET
	case AF_CONN:
		/*
		 * CONN addresses are mapped through.  In other
		 * words, we look at the low 8 bits of the conn address
		 * and take it as the destination address.
		 */
		omnidst = mobaddr;
		mcopy = m_copy(m0, 0, (int)M_COPYALL);
		type = OMNITYPE_CONN;
		break;
	case AF_RPC:
		/*
		 * RPC addresses are mapped through. 
		 */
		omnidst = ((struct sockaddr_rpc *)dst)->node.host.low & 0xFF;
		if (omnidst == ms->ms_omniaddr) {
			mcopy = m0;
			m0 = (struct mbuf *)0;
			goto gotlocal;
		}
		type = OMNITYPE_RPC;
		break;
#ifdef VALID_BULK
	case AF_BULK:
		/*
		 * BULK addresses are mapped through.
		 */
		omnidst = ((struct sockaddr_rpc *)dst)->node.host.low & 0xFF;
		if (omnidst == ms->ms_omniaddr) {
			mcopy = m0;
			m0 = (struct mbuf *)0;
			goto gotlocal;
		}
		type = OMNITYPE_BULK;
		break;
#endif VALID_BULK
	default:
		/*
		 * We don't know any other address families
		 */
		momessage("unknown address family ", ifp->if_unit);
		printf("af%d\n", dst->sa_family);
		error = EAFNOSUPPORT;
		goto bad;
	
	}

	if (omnidst == ms->ms_omniaddr)
		goto gotlocal;
	
	ifp->if_opackets++;

	/*
	 * In this version of the driver, we have a dummy "omniheader"
	 * structure which serves as a control for the receiving host.
	 */
	MGET(m, M_DONTWAIT, MT_HEADER);
	if (m == 0) {
		moIncStat(ms, outNoBufs);
		error = ENOBUFS;
		goto bad;
	}
	m->m_next = m0;
	m->m_off = MMINOFF;
	m->m_len = sizeof(struct omniheader);
	
	/*
	 * Fill in the Omniheader structure
	 */
	omni_h.omnimagic = OMNIMAGIC;
	omni_h.omnitype = type;
	omni_h.omnidst = omnidst;
	omni_h.omnisrc = ms->ms_omniaddr;
	omni_h.omnilen = mbuflen(m);
	omni_h.omnisum = 0x10000;	/* back compat */

	/*
	 * Copy the Omniheader into the mbuf data segment
	 */
	bcopy(&omni_h, mtod(m, char *), sizeof(struct omniheader));

	/*
	 * Do some statistics
	 */
	if (omnidst == OMNIBROADADDR)
		moIncStat(ms,xmitbcast);
	else
		moIncStat(ms,xmitsingle);

	/*
	 * Queue up the mbuf chain onto the interface output queue.  If
	 * the queue is empty, start the xmit by calling mostart().
	 */
	s = splimp();
	if (IF_QFULL(&ms->ms_output)) {
		IF_DROP(&ms->ms_output);
		m0 = m;
		splx(s);
		moIncStat(ms,xmitqfull);
		error = ENOBUFS;
	} else {
		IF_ENQUEUE(&ms->ms_output, m);
		splx(s);
		moIncStat(ms,xmitqueued);
	}
	s = spl7();
	if (!ms->ms_unitActive) {
		ms->ms_unitActive = 1;
		/*
		 * Lock out interrupts while sending.
		 */
		splMOMLOCK();		/* Lock out interrupts */
		mostart(ms);
	}
	splx(s);

	/*
	 * If the output queue was full, we started a transmit (to
	 * try and clear the queue) but set error.  If it was full,
	 * we don't want to loopback but instead just free up the mbufs.
	 */
	if (error)
		goto bad;
gotlocal:
	if (mcopy) {
		moIncStat(ms,xmitloop);
		return(looutput(&loif, mcopy, dst));
	} else
		return 0;

	/*
	 * Release mbuf chains
	 */
bad:
	m_freem(m0);
	m_freem(mcopy);
	return error;
}


/*
 * mostart --
 *	This routine can be called either from the upper half (mooutput)
 *	when the send queue is empty, or from the lower half (mointr)
 *	after an xmitr interrupt to send the next packet out.
 */
mostart(ms)
	register struct mo_softc *ms;
{
	register struct mbuf *m;
	register struct omniheader *h;
	register int index;
	struct omniccb ccb;
	int len;
	u_char dst;
	int s;

	if (modebug)
		printf("mostart\n");

	if (ms->ms_state & MS_OUTACTV) {
		moerror("mostart: called while output active.\n",
			ms->ms_unit);
		ms->ms_unitActive = 0;
		return 0;
	}

	/*
	 * Dequeue the mbuf chain from the output queue
	 */
	s = splimp();
	IF_DEQUEUE(&ms->ms_output, m);
	if (m == 0) {
		ms->ms_unitActive = 0;
		moIncStat(ms,xmitEmpty);
		splx(s);
		return 0;
	}
	splx(s);

	/*
	 * Gather information from the omniheader (first mbuf data
	 * segment)
	 */
	h = mtod(m, struct omniheader *);
	dst = h->omnidst;
	index = ms->ms_cmdindex;
	/*
	 * moputm() loads the data portion of the mbuf chain onto the
	 * on-board memory and the frees up the mbuf chain.
	 */
	len = moputm(ms, mocdata[index], m);

	/*
	 * Fill the command block record and pack it off to the buffer
	 */
	ccb.cmd = OMNISND;
	moset24(&ccb.rslt, mocrslt[index]);
	ccb.c_un.snd.skt = MOSKT;
	moset24(&ccb.c_un.snd.data, mocdata[index]);
	ccb.c_un.snd.len = len;
	ccb.c_un.snd.ctl = 0;
	ccb.c_un.snd.host = dst;

	mopack(ms->ms_reg, &ccb, mocccb[index], sizeof(struct omniccb));
	mosetbyte(ms->ms_reg, &mocrslt[index]->rcode, -1);

	/*
	 * Strobe in the address and set the output flag if successful
	 */
	s = splMOMLOCK();
	/*
	 * Schedule a callout so that if we never get the xmit interrupt,
	 * we have some way of recovering.  The callout is scheduled
	 * for 5 seconds.
	 */
	if (motimo)
	 	timeout(moxmittimo, ms, 5*hz);

	if (mostrobe(mocccb[index], ms->ms_unit)) {
		ms->ms_state |= MS_OUTACTV;
		splx(s);
		moIncStat(ms,xmitOut);
	} else {
		if (motimo)
			untimeout(moxmittimo, ms);
		splx(s);
		moerror("mostart: send error\n", ms->ms_unit);
		moIncStat(ms,xmitFail);
	}
		
}	

/*
 * moxmittimo --
 *	Yet another kluge.  This routine is called via timeout when we
 *	haven't heard from a transmit interrupt.  We initiate a reset
 *	by calling moreset.
 */
moxmittimo(ms)
	register struct mo_softc *ms;
{
	int s;

	s = splMOMLOCK();
	if (mogetbyte(ms->ms_reg, &mocrslt[ms->ms_cmdindex]->rcode) == 0xFF) {
		/*
		 * Interrupt lost.  Start reset.
		 */
		moIncStat(ms,intrlost);
		ms->ms_state = 0;	/* Clear state */
		ms->ms_unitActive = 0;
		momessage("xmit interrupt lost.\n", ms->ms_unit);
		moreset(ms->ms_unit);
		splx(s);
	} else {
		splx(s);
		moIncStat(ms, nointrlost);
	}
}

short mo_intrActive = 0;		/* set when interrupt active */

/*
 * mointr --
 * 	Interrupt routine called from mbdint.  We assume that the
 *	MS_CMDACTV flag is set.
 * Algorithm:
 *	As in most network devices, we have two operations taking place at
 *	the same time:  transmits and receives.  Transmits can be initiated
 *	from the upper half by calling mostart() from mooutput() (the
 *	case in which the unit is not active), or from mointr() (the lower
 *	half).  Receives are completely driven from interrupt service.
 *	When the board is initialized, a receive socket is set up.  When
 *	a receive interrupt comes in, we service the interrupt, re-arm a
 *	socket (if possible...see below), and hand off the receive packet
 *	to its appropriate higher-level protocol.
 *	Because this device has only one interrupt line for both receives and
 *	transmits and because only one operation (xmit, re-arm) is allowed
 *	at one time, we are forced to handle special cases in a clumsy
 *	manner.
 *	Two state machines are operating at one time:  the send machine
 *	and the receive machine.  The send machine takes packets off the
 *	queue, sends them, and at interrupt service repeats.  The receive
 *	machine receives a packet and re-arms the socket.  Unfortunately,
 *	there are a couple of sepcial cases to handle.  First, there
 *	is the case in which we get a send interrupt while an xmit is
 *	active.  Because we can't re-arm the socket (only one active
 *	command at a time), we are forced to set a flag (REARMWAITING)
 *	that tells the send state machine that we are blocked on its
 *	interrupt/command completion.  When the xmit interrupt arrives,
 *	the send state machine looks at the flag.  If it is set, a re-arm
 *	command is sent without trying another xmit.  We also have the
 *	case in which we get both interrupts at the same time.  Because
 *	we can have only one active command, we have to make a choice as to
 *	which state machine to enter.  What this version of the driver does
 *	is to bias toward sending but set a counter which, when set to
 *	zero, initiates a re-arm.  This is the ms_count field in the
 *	softc structure.  When the count goes to zero and a receive packet
 *	is pending, the XMITWAIT flag is set and the receive machine is entered.
 *	After the re-arm completes, the XMITWAIT flag is looked at.  If it
 *	is set, the send state machine is re-entered.
 *	
 */
mointr(unit)
	register int unit;
{
	register struct mo_softc *ms = &mo_softc[unit];
	register int rindex;
	register int cindex;
	register u_char rcode;
	int s;
	int returnval = 0;

	if (mo_intrActive)
		cdebugger("mointr: two interrupts active.");
	else
		mo_intrActive = 1;

	if (unit >= NMOM) {
		moerror("mointr: illegal unit number.\n", unit);
		return 0;
	}

	if (!ms->ms_present) {
		momessage("mointr: interrupt for non-present unit.\n", unit);
		return 0;
	}

	if (mointrpend(ms->ms_reg)) {
		/* Clear the interrupt */
		moclearintr(ms->ms_reg);
		mo_firstintr[unit] = 1;
	} else
		goto nextone;

	if (!ms->ms_init || !(ms->ms_state&MS_RUNNING))
			goto nextone;
		if (modebug)
			printf("mointr: mo%d\n", unit);
		returnval = 1;

		rindex = ms->ms_rcvindex;	/* Set the rcv index */
		cindex = ms->ms_cmdindex;	/* Set the cmd index */

	if (!(ms->ms_state & MS_RCVACTV)) {
		if (!ms->ms_unitActive) {
			moerror("mointr: active flag not set.\n",
				ms->ms_unit);
			goto nextone;
		}
		if (!(ms->ms_state & 
		      (MS_OUTACTV|MS_SETUP)))  {
			moerror("mointr: no command active.\n",
				ms->ms_unit);
			goto nextone;
		}
	}

	if (ms->ms_state & (MS_OUTACTV|MS_SETUP))
		ms->ms_unitActive = 0;

	if (ms->ms_state & MS_SETUP) {
		/*
		 * If MS_SETUP is active, we return from this
		 * module without check the other conditions.
		 * Since only one command can be active at a time,
		 * we "know" that no other command flag could be set
		 */

		ms->ms_state &= ~MS_SETUP;

	startsw:
		rcode = mogetbyte(ms->ms_reg, &morrslt[rindex]->rcode);
		
		switch(rcode) {
		case RSETUP:	/* Socket armed */
			ms->ms_state |= MS_RCVACTV;
			break;
		case SUCCESS:
			goto gotrcv;
		case 0xFF:
			/*
			 * This is a truly grotesque kluge.
			 */
			if (mospinhack(ms, &morrslt[rindex]->rcode))
				goto startsw;
		default:
			printf("mointr: rcode = 0x%x\n", rcode);
			moIncStat(ms,badsetrcv);
			moerror("mointr: setrcv bad rcode", ms->ms_unit);
			goto nextone;
		}
	done:
		if (!ms->ms_unitActive)
			ms->ms_unitActive = 1;
		else {
			panic("mointr 0");
			goto nextone;
		}
		mostart(ms);
		goto nextone;
	}

	if (ms->ms_state & MS_OUTACTV) {

		rcode = mogetbyte(ms->ms_reg, &mocrslt[cindex]->rcode);
		/*
		 * If the rcode is still -1, there are 2 
		 * possibilities:
		 *  1)  Receive interrupt.
		 *  2)  Bad return code
		 * If it is a receive interrupt (i.e., the receive
		 * rcode is SUCCESS), we jump the the receive state
		 * machine.  Otherwise, we assume the return code was
		 * 0xFF.
		 */
		if (rcode == 0xFF) {
			if (ms->ms_state & MS_RCVACTV) {
			  if (mogetbyte(ms->ms_reg, 
			    &morrslt[rindex]->rcode) != RSETUP) {
				moIncStat(ms,sndrcvintr);
				/*
				 * The unit is still active
				 */
				if (ms->ms_unitActive)
					panic("mointr 1");
				else
					ms->ms_unitActive = 1;
				goto startrcv;
		          } else  
			        momessage("xmit FF.\n", ms->ms_unit);
			} 
		}

		/*
		 * Unschedule the callout from mostart()
		 */
		if (motimo)
			untimeout(moxmittimo, ms);

		ms->ms_state &= ~MS_OUTACTV;

		if (rcode < 0x80) {
			/* Successful xmit */
			moIncStat(ms,xmitGood);
		} else
			moIncStat(ms,xmitBad);

		if (ms->ms_state & MS_REARMWAITING) {
			ms->ms_state &= ~MS_REARMWAITING;
			if (ms->ms_unitActive)
				panic("mointr 2");
			else
				ms->ms_unitActive = 1;
			if (!(mosetrcv(ms))) {
				ms->ms_unitActive = 0;
				goto nextone;
			}
				goto nextone;
		}

		/*
		 * It's critical that we only jump below
		 * if we know that we'll get an interrupt.
		 * If we don't get the interrupt, we never
		 * ever initiate another xmit (since the
		 * upper half looks at the unitActive).
		 */
		ms->ms_count -= 1;
		rcode = mogetbyte(ms->ms_reg, &morrslt[rindex]->rcode);
		if (!(ms->ms_state & MS_RCVACTV) || 
		   (rcode != SUCCESS) || (ms->ms_count > 0)) {
			if (ms->ms_unitActive)
				panic("mointr 3");
			else
				ms->ms_unitActive = 1;
			mostart(ms);
			/*
			 * If active, go to next controller unit
			 */
			if (ms->ms_unitActive)
				goto nextone;
		}
	}

startrcv:
	/*
	 * Need to write a watchdog routine that gets called
	 * every so often.  If receive dies on us, we need to know
	 */
	ms->ms_count = mocount;

	if (ms->ms_state & MS_RCVACTV) {
		/*
		 * We have to make sure that this is a receive
		 * interrupt before we alter flags
		 */
		rcode = mogetbyte(ms->ms_reg, &morrslt[rindex]->rcode);

		switch(rcode) {
		case SUCCESS:
	gotrcv:
			ms->ms_state &= ~MS_RCVACTV;
			/*
			 * moinput() re-arms the socket
			 */
			moinput(ms);
			break;
		case RSETUP:
			break;
		default:
			printf("mointr: rcode=0x%x\n", rcode);
			moerror("mointr: rcv bad rcode\n", ms->ms_unit);
			goto nextone;
			break;
		}
	}
nextone:
	mo_intrActive = 0;
	return returnval;
}


/*
 * mospinhack --
 *	This kluges around a late result code change.
 */
mospinhack(ms, c)
	register struct mo_softc *ms;
	register char *c;
{
	register int spincount = 0;

	while (spincount++ < 400)
		if (mogetbyte(ms->ms_reg, c) != -1)
			return 1;
	moIncStat(ms, spinout);
	return 0;
}


/*
 * moputm --
 *	Copy an mbuf chain from host memory into the on-board buffer.
 *	Make sure that the size of data does not exceed the xmit
 *	buffer size (XMITMAXSIZ).  This routine is only called from
 *	mostart().
 */
moputm(ms, buf, m)
	register struct mo_softc *ms;
	caddr_t buf;
	struct mbuf *m;
{
	register struct mbuf *mp;
	register int totlen = 0;		/* total length of data */
	register int len;
	
	for (mp = m; mp; mp = mp->m_next) {
		len = mp->m_len;
		totlen += len;
		if (totlen > XMITMAXSIZ) {
			m_freem(m);
			return -1;
		}
		mopack(ms->ms_reg, mtod(mp, char *), buf, len);
		buf = (caddr_t)((int)buf + len);
	}
	m_freem(m);
	return totlen;
}

/*
 * mogetm --
 *	Get data from the on-board buffer and return an mbuf chain.
 *	Note that we do not use the mbuf cluster pages.  The reason
 *	is that we never get enough data (4 Kby) to fill a cluster page --
 *	our max receive buffer size is 2 Kby.  This routine is only
 *	called from moinput().
 */
struct mbuf *
mogetm(ms, buf, len)
	struct mo_softc *ms;
	register caddr_t buf;		/* address of receive buffer */
	register int len;
{
	register struct mbuf *mp;
	register struct mbuf *top = (struct mbuf *)0;	/* top of mbuf chain */
	struct omniheader *h;
	struct mbuf *m;

	/*
	 * Get the omniheader first
	 */

	MGET(top, M_DONTWAIT, MT_DATA);
	if (top == 0) {
		m_freem(top);
		return 0;
	}
	top->m_len = sizeof(struct omniheader);
	top->m_off = MMINOFF;
	mounpack(ms->ms_reg, buf, mtod(top, char *), top->m_len);
	buf = (caddr_t)((int)buf + top->m_len);
	len -= top->m_len;
	m = top;


	/*
	 * Now, get the rest of the packet
	 */
	while (len > 0) {
		mp = (struct mbuf *) 0;
		MGET(mp, M_DONTWAIT, MT_DATA);
		if (mp == 0) {
			m_freem(top);
			return 0;
		}
		mp->m_len = MIN(MLEN, len);
		mp->m_off = MMINOFF;
		mounpack(ms->ms_reg, buf, mtod(mp, char *), mp->m_len);
		len -= mp->m_len;
		m->m_next = mp;
		m = mp;
		buf = (caddr_t)((int)buf + mp->m_len);
	}
	return (top);
}
	


/*
 * moinput --
 *	Called from interrupt service.  Get data from the buffer and into
 *	an mbuf chain.  Determine what input routine to call and then
 *	call it.
 */
int mo_shortread = 0;
moinput(ms)
	register struct mo_softc *ms;
{
	register struct mbuf *m;
	register struct mbuf *m0;
	struct omnirslt rslt;
	struct omniheader h;
	int index;
	int len;
	int s;
	int sum;

	index = ms->ms_rcvindex;
	/*
	 * Get the new index for using a buffer
	 */
	ms->ms_rcvindex = (index == 0) ? 1 : 0;

	/*
	 * Get the receive record
	 */
	mounpack(ms->ms_reg, morrslt[index], &rslt, sizeof(struct omnirslt));
	len = rslt.len;

	if (len > OMNIMTU) {
		moerror("oversize packet.\n", ms->ms_unit);
		return 0;
	}
	m = mogetm(ms, mordata[index], len);

	/*
	 * Re-arm the receive socket
	 */
	s = spl7();
	if (!ms->ms_unitActive) {
		ms->ms_unitActive = 1;
		splx(s);
		if (!(mosetrcv(ms))) {
			ms->ms_unitActive = 0;
			m_freem(m);
			return;
		}
	} else {
		splx(s);
		if (!(ms->ms_state & MS_OUTACTV)) {
			moerror("moinput: unable to rearm socket.\n",
				ms->ms_unit);
			m_freem(m);
			return;
		}
		ms->ms_state |= MS_REARMWAITING;
	}
	
	if (!m)
		return 0;
	m0 = m->m_next;
	if (!m0) {
		m_freem(m);
		return 0;
	}
	
	/*
	 * Check the omniheader
	 */
	bcopy(mtod(m, char *), &h, sizeof(struct omniheader));

	/*
	 * Free up the omniheader mbuf
	 */
	m->m_next = (struct mbuf *)0;
	m_freem(m);

	if (h.omnimagic != OMNIMAGIC) {
		moIncStat(ms,rcvBadMagic);
		m_freem(m0);
		return 0;
	}


	if (len != h.omnilen) {
		printf("moinput: src=0x%x, m=0x%x len=0x%x  h.omnilen=0x%x\n",
			h.omnisrc, m, len, h.omnilen);
		momessage("moinput: lengths not equal\n", ms->ms_unit);
		m_freem(m0);
		mo_shortread++;
		return;
	}


	switch (h.omnitype) {
#ifdef INET
	case OMNITYPE_IP:
		s = splimp();
		if (IF_QFULL(&ipintrq)) {
			IF_DROP(&ipintrq);
			splx(s);
			m_freem(m0);
			moIncStat(ms,rcvQFull);
			return;
		} else {
			IF_ENQUEUE(&ipintrq, m0);
			splx(s);
			moIncStat(ms,rcvOK);
		}
		ms->if_ip.if_ipackets++;
		schednetisr(NETISR_IP);
		break;
	case OMNITYPE_ARP:
		arpinput(&ms->ms_ac, m0);
		break;
#endif INET

#ifdef VALIDnet
#ifdef VALID_BULK
	case OMNITYPE_BULK:
		s = splimp();
		if (IF_QFULL(&bulk_intrq)) {
			IF_DROP(&bulk_intrq);
			splx(s);
			m_freem(m0);
			moIncStat(ms,rcvQFull);
			return;
		} else {
			IF_ENQUEUE(&bulk_intrq, m0);
			splx(s);
			moIncStat(ms,rcvOK);
		}
		ms->if_bulk.if_ipackets++;
		schednetisr(NETISR_BULK);
		break;
#endif VALID_BULK
	case OMNITYPE_CONN:
		s = splimp();
		if (IF_QFULL(&conn_intrq)) {
			IF_DROP(&conn_intrq);
			splx(s);
			m_freem(m0);
			moIncStat(ms,rcvQFull);
			return;
		} else {
			IF_ENQUEUE(&conn_intrq, m0);
			splx(s);
			moIncStat(ms,rcvOK);
		}
		ms->if_conn.if_ipackets++;
		schednetisr(NETISR_CONN);
		break;
	case OMNITYPE_RPC:
		ms->if_rpc.if_ipackets++;
		rpc_input(m0);
		moIncStat(ms,rcvOK);
		break;
#endif VALIDnet
	default:
		moIncStat(ms,rcvBadValid);
		m_freem(m0);
		break;
	}

	/*
	 * Do stats on packet origin, etc.
	 */

}


/*
 * mosetrcv --
 *	Send a set receive command to the transporter.  This routine
 *	should only be called if the transporter does not have a command
 *	or set up active.  We set the MS_CMDACTV and MS_SETUP flags in
 *	the softc structure, set up a command block, copy it into the buffer
 *	and then strobe the transporter.  This routine can be called either
 *	from the upper of the lower half.
 */
mosetrcv(ms)
	register struct mo_softc *ms;
{
	register int index = ms->ms_rcvindex;	/* receive buffer index */
	int s;
	struct omniccb ccb;

	/*
	 * Fill in the command block record
	 */
	ccb.cmd = OMNIRCV;
	moset24(&ccb.rslt, morrslt[index]);
	ccb.c_un.rcv.skt = MOSKT;
	moset24(&ccb.c_un.rcv.data, mordata[index]);
	ccb.c_un.rcv.len = RCVMAXSIZ;
	ccb.c_un.rcv.ctl = 0;

	/*
	 * Copy the block into the buffer
	 */
	mopack(ms->ms_reg, &ccb, morccb[index], sizeof(struct omniccb));

	/*
	 * Initialize the return code
	 */
	mosetbyte(ms->ms_reg, &morrslt[index]->rcode, -1);

	/*
	 * Strobe the transporter
	 */
	s = splMOMLOCK();
	if (mostrobe(morccb[index], ms->ms_unit)) {
		ms->ms_state |= MS_SETUP;
		splx(s);
		moIncStat(ms,rcvstrobed);
		return 1;
	} else {
		splx(s);
		moerror("mosetrcv: unable to strobe transporter.\n",
			ms->ms_unit);
		moIncStat(ms,rcvnostrobe);
		return 0;
	}
}



/*
 * mostrobe --
 *	Strobe the transporter.  Return 1 if successful, 0 if not.
 */
mostrobe(addr, unit)
	caddr_t addr;
	int unit;
{
	register unsigned char *cp = (unsigned char *)&addr;
	register i, j;
	register struct mo_softc *ms = &mo_softc[unit];
	register struct moreg *reg = ms->ms_reg;

	if (modebug)
		printf("mostrobe\n");
	for (i = 0; i < 3; i++) {
		/*
		 * Corvus says that we need a 800ns delay before polling
		 * the XRDY bit -- try 10 nop's
		 */
		for (j = 0; j < 0x8000 && !mordy(reg); j++) {
			asm("nop");
		}
		if (j == 0x8000)
			return(0);
		reg->mo_strobe = *++cp;
#ifdef M68020
		flushWriteQueue();
#endif M68020
	}
	return(1);
}


/*
 * moset24 --
 *	Create a 24-bit address
 */
moset24(a, b)
	register u_char *a;
	u_char *b;
{
	register unsigned char *cp;

	if (modebug)
		printf("moset24\n");
	cp = (unsigned char *)(&b);
	*a++ = *++cp;
	*a++ = *++cp;
	*a++ = *++cp;
}


/*
 * momessage --
 *	Put a message from the driver onto the console
 */
momessage(string, unit)
	int unit;
	char *string;
{
	printf("%s%d: %s", modriver.mdr_dname, unit, string);
}

/*
 * moerror --
 *	Called to print out an error message onto the console
 */
moerror(string, unit)
	int unit;
	char *string;
{
	register struct mo_softc *ms = &mo_softc[unit];

	printf("%s%d: ***ERROR*** %s", modriver.mdr_dname, unit, string);
	ms->ms_state = 0;	/* Clear state */
	ms->ms_unitActive = 0;
	timeout(moreset, unit, hz<<2);	/* call reset in 4 sec. */
	return;
}

/*
 * mofatal --
 *	Called on fatal error
 */
mofatal(string)
	char *string;
{
	printf("%s%d: ***FATAL ERROR*** %s", modriver.mdr_dname, 0, string);
	/*
	 * "Mine is the last voice that you will ever hear. Don't be alarmed."
	 */
	panic("mofatal");
	mo_softc[0].ms_state = 0;	/* Clear state */
	return;
}


/*
 * mostartup --
 *	Called from the cdevsw table.
 */
mostartup()
{
	register struct mo_softc *ms;
	int i;


	for (i = 0, ms = &mo_softc[0]; i < NMOM; i++, ms++) {
		/*
 		 * Hack -- If omniaddr != -1, do init
		 */
		if (ms->ms_omniaddr == -1)
			continue;
		ms->ms_init = 0;
		if (moreset(i)) {
			ms->ms_init = 1;
			for (i = 0; i < moinitcount && 
				!mointrpend(ms->ms_reg); i++);
			if (i >= moinitcount) {
				continue;
			}
			moclearintr(ms->ms_reg);
		} else
			momessage("couldn't init unit.\n", i);
	}
	moprint = 1;
}


/*
 * moioctl --
 *	I/O controls sent to the controller
 */
moioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	int cmd;
	caddr_t data;
{
	register struct mo_softc *ms = &mo_softc[ifp->if_unit];
	register struct ifreq *ifr = (struct ifreq *) data;
	int s;

	switch (cmd) {

	/* Generic ioctl's */
	case SIOCSIFADDR:
		if (ifp != &ms->if_ip)
			return EINVAL;
		if (ifp->if_flags & IFF_RUNNING)
			if_rtinit(ifp, -1);
		mosetaddr(ifp, (struct sockaddr_in *)&ifr->ifr_addr);
		moinit(ifp->if_unit);
		break;
	/* End Generic ioctl's */

	case MOIOCVERSION:
		copyout(mo_version, ifr->ifr_data, sizeof(mo_version));
		break;
	case MOIOCINIT:
		if (!suser())
			return EPERM;
		s = splMOMLOCK();
		momessage("operator reset.\n", ms->ms_unit);
		moIncStat(ms,userreset);
		ms->ms_state = 0;	/* Clear state */
		ms->ms_unitActive = 0;
		moreset(ms->ms_unit);
		splx(s);
		break;
	case MOIOCGETADDR:
		copyout(&ms->ms_omniaddr, ifr->ifr_data, 1);
		break;
	case MOIOCSTATS:
		copyout(&ms->ms_stats, ifr->ifr_data, sizeof(struct omnistats));
		break;
	default:
		return EINVAL;
	}
	return 0;
}

/*
 * mosetaddr --
 *	reset the address of the IP interface
 */
mosetaddr(ifp, sin)
	register struct ifnet *ifp;
	register struct sockaddr_in *sin;
{
	ifp->if_addr = *(struct sockaddr *)sin;
	ifp->if_net = in_netof(sin->sin_addr);
	ifp->if_host[0] = in_lnaof(sin->sin_addr);
	sin = (struct sockaddr_in *)&ifp->if_broadaddr;
	sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
	ifp->if_flags |= IFF_BROADCAST;
}

/*
 * Note that all the access routines assume one controller.
 * This is probably a good assumption for now.  Note that
 * we could expand the functionality by giving each routine an
 * additional argument which would be the address of the base register
 * on-board.  Hold off for now.
 */

/*
 * mopack -- 
 *	Copy data from Multibus space to the onboard buffer.  Protect the
 *	value of the counter by going to a higher priority level.
 */
mopack(reg, src, dst, count)
	register struct moreg *reg;
	register char *src;	/* Source address */
	u_short dst;		/* Destination address on transporter buffer */
	register int count;	/* Number of bytes to transfer */
{
	int s;
	u_short addr = dst + count;
	u_short result;
	int countholder = count;

	if (count <= 0 || count > MOBUFSIZ)
		return;
	s = spl7();
	if (mo_xport)
		cdebugger("mopack: window in use.");
	else
		mo_xport = 1;
	splMOMLOCK();

	moloadcounter(reg, dst);
	while (count-- > 0) {
		reg->mo_window = *src++;
#ifdef M68020
		flushWriteQueue();
#endif M68020
	}
	if ((result = mogetcounter(reg)) != addr) {
		printf("mopack: counter wrong, should be %x is %x\n",
			addr, result);
		mofatal("mopack\n");
		return;
	}
	mo_xport = 0;
	splx(s);
}

/*
 * mounpack --
 *	Copy data from the transporter buffer to Multibus space.  Protect the
 *	value of the counter by going to a higher priority level.
 */
mounpack(reg, src, dst, count)
	register struct moreg *reg;
	register u_short src;	/* Source address in transporter */
	register char *dst;	/* Destination address */
	register int count;	/* Number of bytes to transfer */
{
	int s;
	u_short addr = src + count;
	u_short result;
	int countholder = count;

	if (count <= 0 || count > MOBUFSIZ)
		return;
	s = spl7();
	if (mo_xport)
		panic("mounpack: window in use.");
	else
		mo_xport = 1;
	splMOMLOCK();

	moloadcounter(reg, src);
	while (count-- > 0)
		*dst++ = reg->mo_window;
	if ((result = mogetcounter(reg)) != addr) {
		printf("mounpack: counter wrong, should be %x is %x\n",
			addr, result);
		mofatal("unpack\n");
		return;
	}
	mo_xport = 0;
	splx(s);
}


/*
 * mogetbyte --
 *	Get a byte from the transporter buffer.
 */
mogetbyte(reg, addr)
	register struct moreg *reg;
	register unsigned char *addr;	/* Source address on transporter */
{
	int s;
	char c;

	s = splMOMLOCK();
	moloadcounter(reg, addr);
	c = reg->mo_staticbuf;
	splx(s);
	return(c);
}

/*
 * mosetbyte --
 *	Set a byte on the transporter buffer 
 */
mosetbyte(reg, addr, val)
	register struct moreg *reg;
	register char *addr;
	char val;
{
	int s;

	s = spl7();
	if (mo_xport)
		cdebugger("mosetbyte: window in use.");
	else
		mo_xport = 1;
	splMOMLOCK();
	moloadcounter(reg, addr);
	reg->mo_window = val;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	mo_xport = 0;
	splx(s);
}


/*
 * moloadcounter --
 *	Load the MST counter
 */
moloadcounter(reg, count)
	register struct moreg *reg;
	register u_short count;
{
	reg->mo_hicounter = ((count & 0x3F00) >> 8) & 0xFF;
#ifdef M68020
	flushWriteQueue();
#endif M68020
	reg->mo_locounter = count & 0xFF;
#ifdef M68020
	flushWriteQueue();
#endif M68020
}

/*
 * mogetcounter --
 *	Get the MST counter
 */
mogetcounter(reg)
	register struct moreg *reg;
{
	register short result = 0;
	unsigned char locounter;
	unsigned char hicounter;

#ifdef M68020
	/*
	 * Make sure that all writes have been made before inspecting
	 * counters
	 */
	flushWriteQueue();
#endif M68020
	hicounter = (reg->mo_hicounter & 0x3F);
	locounter = (reg->mo_locounter & 0xFF);

	result = (hicounter << 8) | ((locounter & 0xFF) & 0x3FFF);
	return result;
}
