4.3BSD-Tahoe/usr/src/sys/tahoeif/if_ex.c

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

/*
 * This file is derived from a number of files, as denoted below,
 * to create an Excelan driver compatible with the 4.3BSD-tahoe release.
 */

/*
 * Copyright (c) 1989 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Excelan Inc.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)if_ex.c	7.2 (Berkeley) 4/22/89
 */

#include "ex.h"

#if	NEX > 0 

/*
 * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers
 */
#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "malloc.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "vmmac.h"
#include "ioctl.h"
#include "errno.h"
#include "vmparam.h"
#include "syslog.h"
#include "uio.h"

#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"

#ifdef INET
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#endif

#ifdef NS
#include "../netns/ns.h"
#include "../netns/ns_if.h"
#endif

#include "../tahoe/cpu.h"
#include "../tahoe/pte.h"
#include "../tahoe/mtpr.h"

#include "../tahoevba/vbavar.h"
#include "if_exreg.h"
/*
 * Copyright (c) 1989 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)if_vba.h	1.1 (Berkeley) 3/9/89
 */

struct ifvba {
	struct	mbuf *iff_mbuf;	/* associated mbuf to free */
	caddr_t	iff_buffer;	/* contiguous memory for data, kernel address */
	u_long	iff_physaddr;	/* contiguous memory for data, phys address */
};


#ifdef KERNEL
struct mbuf *if_vbaget();
#endif



#define	NH2X 32			/* Host to eXcelan request buffers */

#define	NX2H 16			/* eXcelan to Host reply buffers */
#define	NREC	16		/* Number of RECeive buffers */
#define	NTRB	4		/* Number of TRansmit Buffers */
#define NVBI	(NREC + NTRB)

#define EXWATCHINTVL	10	/* call exwatch every x secs */

int	exprobe(), exslave(), exattach(), exintr(), exstart();
struct	vba_device *exinfo[NEX];

long	exstd[] = { 0 };


struct	vba_driver exdriver =
	{ exprobe, 0, exattach, exstart, exstd, "ex", exinfo };
int	exinit(),exoutput(),exioctl(),exreset(),exwatch();
struct	ex_msg *exgetcbuf();
int	ex_ncall = 0;			/* counts calls to exprobe */
u_long	busoff;

/*
 * Ethernet software status per interface.
 *
 * Each interface is referenced by a network interface structure, xs_if, which 
 * the routing code uses to locate the interface.  This structure contains the 
 * output queue for the interface, its address, ... NOTE: To configure multiple
 * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr).
 */
struct	ex_softc {
	struct		arpcom xs_ac;	/* Ethernet common part */
#define	xs_if		xs_ac.ac_if	/* network-visible interface */
#define	xs_addr		xs_ac.ac_enaddr	/* hardware Ethernet address */
	int		xs_flags;	/* private flags */
#define	EX_XPENDING	1		/* xmit rqst pending on EXOS */
#define	EX_STATPENDING	(1<<1)		/* stats rqst pending on EXOS */
#define	EX_RUNNING	(1<<2)		/* board is running */
#define EX_SETADDR	(1<<3)		/* physaddr has been changed */
	int		xs_cvec;	/* probe stores cvec here */
	short		xs_enetunit;	/* unit number for enet filtering */
	short		xs_enetinit;	/* enet inetrface is initialized */
	struct	ex_msg	*xs_h2xnext;	/* host pointer to request queue */
	struct	ex_msg 	*xs_x2hnext;	/* host pointer to reply queue */
	u_long		xs_qbaddr;	/* map info for structs below */
	struct	ex_shm	{
	/* the following structures are always mapped in */
	u_short		sm_h2xhdr;	/* EXOS's request queue header */
	u_short		sm_x2hhdr;	/* EXOS's reply queue header */
	struct ex_msg 	sm_h2xent[NH2X];/* request msg buffers */
	struct ex_msg 	sm_x2hent[NX2H];/* reply msg buffers */
	struct ex_conf	sm_cm;		/* configuration message */
	struct ex_stat	sm_xsa;	/* EXOS writes stats here */
	/* end mapped area */
	} 		*xs_shm;	/* host pointer to shared area */
#define	xs_h2xhdr	xs_shm->sm_h2xhdr
#define	xs_x2hhdr	xs_shm->sm_x2hhdr
#define	xs_h2xent	xs_shm->sm_h2xent
#define	xs_x2hent	xs_shm->sm_x2hent
#define	xs_cm		xs_shm->sm_cm
#define	xs_xsa		xs_shm->sm_xsa
#define	BUSADDR(x)	(0x3D000000 | (((u_long)kvtophys(x))&0xFFFFFF))
#define	P_BUSADDR(x)	(0x3D000000 | (((u_long)kvtophys(x))&0xFFFFF0))
#define	INCORE_BASE(p)	(((u_long)(p)->xs_shm) & 0xFFFFFFF0)
/* we will arrange that the shared memory begins on a 16 byte boundary */
#define	RVAL_OFF(n)	(((char *)&(((struct ex_shm *)0)->n))-(char *)0)
#define	LVAL_OFF(n)	(((char *)(((struct ex_shm *)0)->n))-(char *)0)
#define	H2XHDR_OFFSET	RVAL_OFF(sm_h2xhdr)
#define	X2HHDR_OFFSET	RVAL_OFF(sm_x2hhdr)
#define	H2XENT_OFFSET	LVAL_OFF(sm_h2xent)
#define	X2HENT_OFFSET	LVAL_OFF(sm_x2hent)
#define	CM_OFFSET	RVAL_OFF(sm_cm)
#define	SA_OFFSET	RVAL_OFF(sm_xsa)
	struct		ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */
	struct		ifvba *xs_pkblist; /* free list of above */
#define GetPkBuf(b, v)  ((v = (b)->mb_pkb = xs->xs_pkblist),\
		      (xs->xs_pkblist = (struct ifvba *)(v)->iff_mbuf))
#define FreePkBuf(v) (((v)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\
							(xs->xs_pkblist = v))
	char		xs_nrec;	/* number of pending receive buffers */
	char		xs_ntrb;	/* number of pending transmit buffers */
} ex_softc[NEX];

int ex_padcheck = sizeof (struct ex_softc);

exprobe(reg, vi)
	caddr_t reg;
	struct vba_device *vi;
{
	register br, cvec;		/* r12, r11 value-result */
	register struct exdevice *exaddr = (struct exdevice *)reg;
	int	i;

	if (badaddr((caddr_t)exaddr, 2))
		return 0;
	/*
	 * Reset EXOS and run self-test (should complete within 2 seconds).
	 */
	movow(&exaddr->ex_porta, EX_RESET);
	for (i = 1000000; i; i--) {
		uncache(&(exaddr->ex_portb));
		if (exaddr->ex_portb & EX_TESTOK)
			break;
	}
	if ((exaddr->ex_portb & EX_TESTOK) == 0)
		return 0;
	br = 0x15;
	cvec = --vi->ui_hd->vh_lastiv;
	ex_softc[vi->ui_unit].xs_cvec = cvec;
	ex_ncall++;
	return (sizeof(struct exdevice));
}

/*
 * Interface exists: make available by filling in network interface record.  
 * System will initialize the interface when it is ready to accept packets.  
 * A NET_ADDRS command is done to get the ethernet address.
 */
exattach(ui)
	register struct vba_device	*ui;
{
	register struct ex_softc *xs = &ex_softc[ui->ui_unit];
	register struct ifnet *ifp = &xs->xs_if;
	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
	register struct ex_msg *bp;

	ifp->if_unit = ui->ui_unit;
	ifp->if_name = "ex";
	ifp->if_mtu = ETHERMTU;
	ifp->if_init = exinit;
	ifp->if_ioctl = exioctl;
	ifp->if_output = exoutput;
	ifp->if_reset = exreset;
	ifp->if_flags = IFF_BROADCAST;

	/*
	 * Note: extra memory gets returned by if_vbareserve()
	 * first, so, being page alligned, it is also 16-byte alligned.
	 */
	if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF,
			(caddr_t *)&xs->xs_shm, sizeof(*xs->xs_shm)) == 0)
		return;
	/*
	 * Temporarily map queues in order to configure EXOS
	 */
	xs->xs_qbaddr = INCORE_BASE(xs);
	exconfig(ui, 0);			/* without interrupts */
	if (xs->xs_cm.cm_cc)
		return;				/* bad conf */
	/*
	 * Get Ethernet address.
	 */
	if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0)
		panic("exattach");
	bp->mb_na.na_mask = READ_OBJ;
	bp->mb_na.na_slot = PHYSSLOT;
	bp->mb_status |= MH_EXOS;
	movow(&exaddr->ex_portb, EX_NTRUPT);
	bp = xs->xs_x2hnext;
	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
	printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n",
		ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
		xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
		ether_sprintf(bp->mb_na.na_addrs));
	bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
		sizeof(xs->xs_addr));
	if_attach(ifp);
}

/*
 * Reset of interface after BUS reset.
 * If interface is on specified vba, reset its state.
 */
exreset(unit)
int unit;
{
	register struct vba_device *ui;

	if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0)
		return;
	printf(" ex%d", unit);
	ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
	ex_softc[unit].xs_flags &= ~EX_RUNNING;

	exinit(unit);
}

/*
 * Initialization of interface; clear recorded pending operations, and 
 * reinitialize BUS usage. Called at boot time, and at ifconfig time via 
 * exioctl, with interrupts disabled.
 */
exinit(unit)
int unit;
{
	register struct ex_softc *xs = &ex_softc[unit];
	register struct vba_device *ui = exinfo[unit];
	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
	register struct ifnet *ifp = &xs->xs_if;
	register struct sockaddr_in *sin;
	register struct ex_msg 	*bp;
	int s;

	/* not yet, if address still unknown */
	if (ifp->if_addrlist == (struct ifaddr *)0)
		return;
	if (xs->xs_flags & EX_RUNNING)
		return;

	xs->xs_qbaddr = INCORE_BASE(xs);
	exconfig(ui, 4);		/* with vectored interrupts*/

	/*
	 * Put EXOS on the Ethernet, using NET_MODE command
	 */
	if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0)
		panic("exinit");
	bp->mb_nm.nm_mask = WRITE_OBJ;
	bp->mb_nm.nm_optn = 0;
	bp->mb_nm.nm_mode = MODE_PERF;
	bp->mb_status |= MH_EXOS;
	movow(&exaddr->ex_portb, EX_NTRUPT);
	bp = xs->xs_x2hnext;
	while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
		;
	bp->mb_length = MBDATALEN;
	bp->mb_status |= MH_EXOS;		/* free up buffer */
	movow(&exaddr->ex_portb, EX_NTRUPT);
	xs->xs_x2hnext = xs->xs_x2hnext->mb_next;

	ifp->if_watchdog = exwatch;
	ifp->if_timer = EXWATCHINTVL;
	s = splimp();		/* are interrupts disabled here, anyway? */
	exhangrcv(unit);
	xs->xs_if.if_flags |= IFF_RUNNING;
	xs->xs_flags |= EX_RUNNING;
	if (xs->xs_flags & EX_SETADDR)
		ex_setaddr((u_char *)0, unit);
	exstart(&ex_softc[unit].xs_if);		/* start transmits */
	splx(s);		/* are interrupts disabled here, anyway? */
}

/*
 * Reset, test, and configure EXOS.  It is called by exinit, and exattach.
 * Returns 0 if successful, 1 if self-test failed.
 */
exconfig(ui, itype)
struct	vba_device *ui;
int itype;
{
	register int unit = ui->ui_unit;
	register struct ex_softc *xs = &ex_softc[unit];
	register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr;
	register struct ex_conf *cm = &xs->xs_cm;
	register struct ex_msg 	*bp;
	register struct ifvba *pkb;
	int 	i;
	u_long 	shiftreg;
	static	u_char	cmaddr[8] = {0xFF, 0xFF, 0, 0};

	xs->xs_flags = 0;
	/*
	 * Reset EXOS, wait for self-test to complete
	 */
	movow(&exaddr->ex_porta, EX_RESET);
	do {
		uncache(&exaddr->ex_portb);
	} while ((exaddr->ex_portb & EX_TESTOK) == 0) ;
	/*
	 * Set up configuration message.
	 */
	cm->cm_1rsrv = 1;
	cm->cm_cc = 0xFF;
	cm->cm_opmode = 0;		/* link-level controller mode */
	cm->cm_dfo = 0x0101;		/* enable host data order conversion */
	cm->cm_dcn1 = 1;
	cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0;
	cm->cm_ham = 3;			/* absolute address mode */
	cm->cm_3rsrv = 0;
	cm->cm_mapsiz = 0;
	cm->cm_byteptrn[0] = 0x01;	/* EXOS deduces data order of host */
	cm->cm_byteptrn[1] = 0x03;	/*  by looking at this pattern */
	cm->cm_byteptrn[2] = 0x07;
	cm->cm_byteptrn[3] = 0x0F;
	cm->cm_wordptrn[0] = 0x0103;
	cm->cm_wordptrn[1] = 0x070F;
	cm->cm_lwordptrn = 0x0103070F;
	for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
	cm->cm_mba = 0xFFFFFFFF;
	cm->cm_nproc = 0xFF;
	cm->cm_nmbox = 0xFF;
	cm->cm_nmcast = 0xFF;
	cm->cm_nhost = 1;
	cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr);
	cm->cm_h2xhdr = H2XHDR_OFFSET;
	cm->cm_h2xtyp = 0;		/* should never wait for rqst buffer */
	cm->cm_x2hba = cm->cm_h2xba;
	cm->cm_x2hhdr = X2HHDR_OFFSET;
	cm->cm_x2htyp = itype;		/* 0 for none, 4 for vectored */
	cm->cm_x2haddr = xs->xs_cvec;	/* ivec allocated in exprobe */
	/*
	 * Set up message queues and headers.
	 * First the request queue
	 */
	for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
		bp->mb_rsrv = 0;
		bp->mb_length = MBDATALEN;
		bp->mb_status = MH_HOST;
		bp->mb_next = bp+1;
	}
	xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET;
	xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent;

	/* Now the reply queue. */
	for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
		bp->mb_rsrv = 0;
		bp->mb_length = MBDATALEN;
		bp->mb_status = MH_EXOS;
		bp->mb_next = bp+1;
	}
	xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET;
	xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent;
	xs->xs_nrec = 0;
	xs->xs_ntrb = 0;
	xs->xs_pkblist =  xs->xs_vbinfo + NVBI - 1;
	for (pkb = xs->xs_pkblist; pkb > xs->xs_vbinfo; pkb--)
		pkb->iff_mbuf = (struct mbuf *)(pkb - 1);
	xs->xs_vbinfo[0].iff_mbuf = 0;

	/*
	 * Write config msg address to EXOS and wait for configuration to 
	 * complete (guaranteed response within 2 seconds).
	 */
	shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET;
	for (i = 4; i < 8; i++) {
		cmaddr[i] = (u_char)(shiftreg & 0xFF);
		shiftreg >>= 8;
	}
	for (i = 0; i < 8; i++) {
		do {
			uncache(&exaddr->ex_portb);
		} while (exaddr->ex_portb & EX_UNREADY) ;
		DELAY(500);
		movow(&exaddr->ex_portb, cmaddr[i]);
	}
	for (i = 500000; i; --i) {
		DELAY(10);
		uncache(&cm->cm_cc);
		if (cm->cm_cc != 0xFF)
			break;
	}
	if (cm->cm_cc)
		printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc);
}

/*
 * Start or re-start output on interface. Get another datagram to send off of 
 * the interface queue, and map it to the interface before starting the output.
 * This routine is called by exinit(), exoutput(), and excdint().  In all cases,
 * interrupts by EXOS are disabled.
 */
exstart(ifp)
struct ifnet *ifp;
{
	int unit = ifp->if_unit;
	struct vba_device *ui = exinfo[unit];
	register struct ex_softc *xs = &ex_softc[unit];
	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
	register struct ex_msg *bp;
	register struct mbuf *m;
        int len;
	register struct ifvba *pkb;
	struct mbuf *m0 = 0;
	register int nb = 0, tlen = 0;
	union l_util {
		u_long	l;
		struct	i86_long i;
	} l_util;

	if (xs->xs_ntrb >= NTRB)
		return;
	if (xs->xs_pkblist == 0) {
		printf("ex%d: vbinfo exhausted, would panic", unit);
		return;
	}
	IF_DEQUEUE(&xs->xs_if.if_snd, m);
	if (m == 0)
		return;
	/*
	 * Get a transmit request.
	 */
	if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) {
		m_freem(m);
		printf("exstart: no command buffers\n");
		return;
	}
	xs->xs_ntrb++;
	GetPkBuf(bp, pkb);
	pkb->iff_mbuf = m;	/* save mbuf pointer to free when done */
	/*
	 * point directly to the first group of mbufs to be transmitted. The
	 * hardware can only support NFRAGMENTS descriptors.
	 */
	while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) {
		l_util.l = BUSADDR(mtod(m, caddr_t));
		bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len;
		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
		if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
			/* Here, the phys memory for the mbuf is out
			   of range for the vmebus to talk to it */
			if (m == pkb->iff_mbuf)
				pkb->iff_mbuf = 0;
			break;
		}
		tlen += m->m_len;
		m0 = m;
		m = m->m_next;
		nb++;
	}

	/* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */
	if (m0)
		m0->m_next = 0;

	/*
	 * if not all of the descriptors would fit then merge remaining data
	 * into the transmit buffer, and point to it.  Note: the mbufs are freed
	 * during the merge, they do not have to be freed when we get the 
	 * transmit interrupt.
	 */
	if (m) {
		if (m == pkb->iff_mbuf) {
			printf("ex%d: exstart insanity\n", unit);
			pkb->iff_mbuf = 0;
		}
		len = if_vbaput(pkb->iff_buffer, m);
		l_util.l = BUSADDR(pkb->iff_buffer);
		bp->mb_et.et_blks[nb].bb_len = (u_short)len;
		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
		tlen += len;
		nb++;
	}

	/*
	 * If the total length of the packet is too small,
	 * pad the last fragment.  (May run into very obscure problems)
	 */
	if (tlen < sizeof(struct ether_header) + ETHERMIN) {
		len = (ETHERMIN + sizeof(struct ether_header)) - tlen;
		bp->mb_et.et_blks[nb-1].bb_len += (u_short)len;
		tlen += len;
#ifdef notdef
                if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
			must copy last frag into private buffer
		}
#endif
	}

	/* set number of fragments in descriptor */
	bp->mb_et.et_nblock = nb;
	bp->mb_status |= MH_EXOS;
	movow(&exaddr->ex_portb, EX_NTRUPT);
}

/*
 * interrupt service routine.
 */
exintr(unit)
	int unit;
{
	register struct ex_softc *xs = &ex_softc[unit];
	register struct ex_msg *bp = xs->xs_x2hnext;
	struct vba_device *ui = exinfo[unit];
	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
	struct ex_msg *next_bp;

	while ((bp->mb_status & MH_OWNER) == MH_HOST) {
		switch (bp->mb_rqst) {
		    case LLRECEIVE:
			if (--xs->xs_nrec < 0) {
				printf("ex%d: internal receive check\n", unit);
				xs->xs_nrec = 0;
			}
			exrecv(unit, bp);
			FreePkBuf(bp->mb_pkb);
			bp->mb_pkb = (struct ifvba *)0;
			exhangrcv(unit);
			break;

		    case LLTRANSMIT:
		    case LLRTRANSMIT:
			if (--xs->xs_ntrb < 0) {
				printf("ex%d: internal transmit check\n", unit);
				xs->xs_ntrb = 0;
			}
			xs->xs_if.if_opackets++;
			if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
				;
			else if (bp->mb_rply & LLXM_1RTRY)
				xs->xs_if.if_collisions++;
			else if (bp->mb_rply & LLXM_RTRYS)
				xs->xs_if.if_collisions += 2;	/* guess */
			else if (bp->mb_rply & LLXM_ERROR)
				if (xs->xs_if.if_oerrors++ % 100 == 0)
					printf("ex%d: 100 transmit errors=%b\n",
						unit, bp->mb_rply, XMIT_BITS);
			if (bp->mb_pkb->iff_mbuf) {
				m_freem(bp->mb_pkb->iff_mbuf);
				bp->mb_pkb->iff_mbuf = (struct mbuf *)0;
			}
			FreePkBuf(bp->mb_pkb);
			bp->mb_pkb = (struct ifvba *)0;
			exstart(&xs->xs_if);
			exhangrcv(unit);
			break;

		    case LLNET_STSTCS:
			xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc;
			xs->xs_flags &= ~EX_STATPENDING;
		    case LLNET_ADDRS:
		    case LLNET_RECV:
			if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
				;
			else
				printf("ex%d: %s, request 0x%x, reply 0x%x\n",
				  unit, "unsucessful stat or address change",
				  bp->mb_rqst, bp->mb_rply);
			break;

		    default:
			printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst);
		}
		bp->mb_length = MBDATALEN;
		next_bp = bp->mb_next;
		bp->mb_status |= MH_EXOS;	/* free up buffer */
		bp = next_bp;			/* paranoia about race */
		movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */
	}
	xs->xs_x2hnext = bp;
}

/*
 * Get a request buffer, fill in standard values, advance pointer.
 */
struct ex_msg *
exgetcbuf(xs, req)
struct ex_softc *xs;
int req;
{
	register struct ex_msg *bp;
	struct ifvba *pkb;
	int s = splimp();

	bp = xs->xs_h2xnext;
	if ((bp->mb_status & MH_OWNER) == MH_EXOS) {
		splx(s);
		return (struct ex_msg *)0;
	}
	xs->xs_h2xnext = bp->mb_next;
	bp->mb_1rsrv = 0;
	bp->mb_rqst = req;
	bp->mb_length = MBDATALEN;
	bp->mb_pkb = (struct ifvba *)0;
	splx(s);
	return bp;
}

/*
 * Process Ethernet receive completion:  If input error just drop packet, 
 * otherwise examine packet to determine type.  If can't determine length from 
 * type, then have to drop packet, otherwise decapsulate packet based on type 
 * and pass to type-specific higher-level input routine.
 */
exrecv(unit, bp)
int unit;
register struct ex_msg *bp;
{
	register struct ex_softc *xs = &ex_softc[unit];
	register struct ether_header *eh;
    	register struct mbuf *m;
	register struct ifqueue *inq;
	int len, off, resid;
	struct ifnet *ifp = &xs->xs_if;
	int s;

	xs->xs_if.if_ipackets++;
	/*     total length               - header                      - crc */
	len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
	if (bp->mb_rply != LL_OK) {
		if (xs->xs_if.if_ierrors++ % 100 == 0)
			printf("ex%d: 100 receive errors=%b\n",
				unit, bp->mb_rply, RECV_BITS);
		return;
	}
	eh = (struct ether_header *)(bp->mb_pkb->iff_buffer);

	/*
	 * Deal with trailer protocol: if type is PUP trailer get true type from
	 * first 16-bit word past data.  Remember that type was trailer by 
	 * setting off.
	 */
	eh->ether_type = ntohs((u_short)eh->ether_type);
#define	exdataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
	if (eh->ether_type >= ETHERTYPE_TRAIL &&
	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
		if (off >= ETHERMTU)
			return;			/* sanity */
		eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
		resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
		if (off + resid > len)
			return;			/* sanity */
		len = off + resid;
	} else
		off = 0;
	if (len == 0)
		return;
	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; if_vbaget will then force this header
	 * information to be at the front, but we still have to drop
	 * the type and length which are at the front of any trailer data.
	 */
	m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, ifp);
	if (m == 0)
		return;
	if (off) {
		m->m_off += 2 * sizeof (u_short);
		m->m_len -= 2 * sizeof (u_short);
		*(mtod(m, struct ifnet **)) = ifp;
	}

	switch (eh->ether_type) {
#ifdef INET
	case ETHERTYPE_IP:
		schednetisr(NETISR_IP);
		inq = &ipintrq;
		break;

	case ETHERTYPE_ARP:
		arpinput((struct arpcom *)ifp, m);
		return;
#endif
#ifdef NS
	case ETHERTYPE_NS:
		schednetisr(NETISR_NS);
		inq = &nsintrq;
		break;

#endif
	default:
		m_freem(m);
		return;
	}

	s = splimp();
	if (IF_QFULL(inq)) {
		IF_DROP(inq);
		m_freem(m);
	} else
		IF_ENQUEUE(inq, m);
	splx(s);
	return;
}

/*
 * Hang a receive request. This routine is called by exinit and excdint,
 * with interrupts disabled in both cases.
 */
exhangrcv(unit)
	int unit;
{
	register struct ex_softc *xs = &ex_softc[unit];
	register struct ex_msg *bp;
	register struct ifvba *pkb;
	short mustint = 0;
	union l_util {
		u_long	l;
		struct	i86_long i;
	} l_util;

	while (xs->xs_nrec < NREC) {
		if (xs->xs_pkblist == (struct ifvba *)0)
			break;
		if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) {
			break;
		}
		GetPkBuf(bp, pkb);
		pkb->iff_mbuf = 0;
		xs->xs_nrec += 1;
		bp->mb_er.er_nblock = 1;
		bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
		l_util.l = BUSADDR(pkb->iff_buffer);
		bp->mb_er.er_blks[0].bb_addr = l_util.i;
		bp->mb_status |= MH_EXOS;
		mustint = 1;
	}
	if (mustint == 0)
		return;
	movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT);
}

/*
 * Ethernet output routine.
 * Encapsulate a packet of type family for the local net.
 * Use trailer local net encapsulation if enough data in first
 * packet leaves a multiple of 512 bytes of data in remainder.
 * Assumes that ifp is actually pointer to arpcom structure.
 */

exoutput(ifp, m0, dst)
	register struct ifnet *ifp;
	struct mbuf *m0;
	struct sockaddr *dst;
{
	short type;
	int s, error = 0;
 	u_char edst[6];
	struct in_addr idst;
	register struct mbuf *m = m0;
	struct mbuf *mcopy = 0;
	register struct ether_header *eh;
	int usetrailers, off = 0, totlen;
#define	ac ((struct arpcom *)ifp)

	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
		error = ENETDOWN;
		goto bad;
	}
	switch (dst->sa_family) {

#ifdef INET
	case AF_INET:
		idst = ((struct sockaddr_in *)dst)->sin_addr;
 		if (!arpresolve(ac, m, &idst, edst, &usetrailers))
			return (0);	/* if not yet resolved */
		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
			type = ETHERTYPE_TRAIL + (off>>9);
			m->m_off -= 2 * sizeof (u_short);
			m->m_len += 2 * sizeof (u_short);
			*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
			goto gottrailertype;
		}
		type = ETHERTYPE_IP;
		goto gottype;
#endif
#ifdef NS
	case AF_NS:
		type = ETHERTYPE_NS;
 		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
		    (caddr_t)edst, sizeof (edst));
		goto gottype;
#endif
	case AF_UNSPEC:
		eh = (struct ether_header *)dst->sa_data;
 		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
		type = eh->ether_type;
		goto gottype;

	default:
		printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
			dst->sa_family);
		error = EAFNOSUPPORT;
		goto bad;
	}

gottrailertype:
	/*
	 * Packet to be sent as trailer: move first packet
	 * (control information) to end of chain.
	 */
	while (m->m_next)
		m = m->m_next;
	m->m_next = m0;
	m = m0->m_next;
	m0->m_next = 0;
	m0 = m;

gottype:
	/*
	 * Add local net header.  If no space in first mbuf,
	 * allocate another.
	 */
	if (m->m_off > MMAXOFF ||
	    MMINOFF + sizeof (struct ether_header) > m->m_off) {
		m = m_get(M_DONTWAIT, MT_HEADER);
		if (m == 0) {
			error = ENOBUFS;
			goto bad;
		}
		m->m_next = m0;
		m->m_off = MMINOFF;
		m->m_len = sizeof (struct ether_header);
	} else {
		m->m_off -= sizeof (struct ether_header);
		m->m_len += sizeof (struct ether_header);
	}
	eh = mtod(m, struct ether_header *);
	type = htons((u_short)type);
	bcopy((caddr_t)&type,(caddr_t)&eh->ether_type,
		sizeof(eh->ether_type));
 	bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)eh->ether_shost,
	    sizeof(eh->ether_shost));
	/*
	 * Queue message on interface, and start output if interface
	 * not yet active.
	 */
	s = splimp();
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		splx(s);
		error = ENOBUFS;
		goto bad;
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	error = exstart(ifp);
	splx(s);
	return (error);

bad:
	if (m)
		m_freem(m);
	return (error);
}

/*
 * Watchdog routine (currently not used). Might use this to get stats from EXOS.
 */
exwatch(unit)
int unit;
{
	struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr;
	register struct ex_softc *xs = &ex_softc[unit];
	register struct ex_msg *bp;
	int s = splimp();

	if (xs->xs_flags & EX_STATPENDING)
		goto exspnd;
	if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) {
		splx(s);
		return;
	}
	xs->xs_flags |= EX_STATPENDING;
	bp->mb_ns.ns_mask = READ_OBJ;
	bp->mb_ns.ns_rsrv = 0;
	bp->mb_ns.ns_nobj = 8;
	bp->mb_ns.ns_xobj = 0;
	bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET;
	bp->mb_status |= MH_EXOS;
	movow(&exaddr->ex_portb, EX_NTRUPT);
exspnd:	splx(s);
	xs->xs_if.if_timer = EXWATCHINTVL;
}

/*
 * Process an ioctl request.
 */
exioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	int cmd;
	caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	register struct ex_softc *xs = &ex_softc[ifp->if_unit];
	int s = splimp(), error = 0;

	switch (cmd) {

	case SIOCSIFADDR:
                ifp->if_flags |= IFF_UP;
                exinit(ifp->if_unit);

                switch (ifa->ifa_addr.sa_family) {
#ifdef INET
		case AF_INET:
			((struct arpcom *)ifp)->ac_ipaddr =
				IA_SIN(ifa)->sin_addr;
			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
			break;
#endif
#ifdef NS
		case AF_NS:
		    {
			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
			
			if (ns_nullhost(*ina))
				ina->x_host = *(union ns_host *)(xs->xs_addr);
			else
				ex_setaddr(ina->x_host.c_host,ifp->if_unit);
			break;
		    }
#endif
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    xs->xs_flags & EX_RUNNING) {
			movow(&((struct exdevice *)
			  (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET);
			xs->xs_flags &= ~EX_RUNNING;
		} else if (ifp->if_flags & IFF_UP &&
		    (xs->xs_flags & EX_RUNNING) == 0)
			exinit(ifp->if_unit);
		break;

	default:
		error = EINVAL;
	}
	splx(s);
	return (error);
}

/*
 * set ethernet address for unit
 */
ex_setaddr(physaddr, unit)
	u_char *physaddr;
	int unit;
{
	register struct ex_softc *xs = &ex_softc[unit];
	
	if (physaddr) {
		xs->xs_flags |= EX_SETADDR;
		bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
	}
	ex_setmulti((u_char *)xs->xs_addr, unit, PHYSSLOT);
}

/*
 * Enable multicast reception for unit.
 */
ex_setmulti(linkaddr, unit, slot)
	u_char *linkaddr;
	int unit, slot;
{
	register struct ex_softc *xs = &ex_softc[unit];
	struct vba_device *ui = exinfo[unit];
	register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
	register struct ex_msg *bp;
	
	if (!(xs->xs_flags & EX_RUNNING))
		return;
	bp = exgetcbuf(xs, LLNET_ADDRS);
	bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
	bp->mb_na.na_slot = slot;
	bcopy((caddr_t)linkaddr, (caddr_t)bp->mb_na.na_addrs, 6);
	bp->mb_status |= MH_EXOS;
	movow(&addr->ex_portb, EX_NTRUPT);
	bp = xs->xs_x2hnext;
	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
#ifdef	DEBUG
	log(LOG_DEBUG, "ex%d: %s %s (slot %d)\n", unit,
		(slot == PHYSSLOT ? "reset addr" : "add multicast"
		ether_sprintf(bp->mb_na.na_addrs), slot);
#endif
	/*
	 * Now, re-enable reception on slot.
	 */
	bp = exgetcbuf(xs, LLNET_RECV);
	bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
	bp->mb_nr.nr_slot = slot;
	bp->mb_status |= MH_EXOS;
	movow(&addr->ex_portb, EX_NTRUPT);
	bp = xs->xs_x2hnext;
	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
		;
}
/*
 * Copyright (c) 1989 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)if_vba.c	1.2 (Berkeley) 4/22/89
 */

#ifdef notdef
#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "cmap.h"
#include "vmmac.h"
#include "socket.h"

#include "../tahoe/mtpr.h"
#include "../tahoe/pte.h"

#include "../tahoevba/vbavar.h"

#include "../net/if.h"
#include "../netinet/in.h"
#include "../netinet/if_ether.h"
#endif

static
if_vbareserve(ifvba0, n, bufsize, extra, extrasize)
struct ifvba *ifvba0;
register int n;
int bufsize;
caddr_t *extra;
int extrasize;
{
	register caddr_t cp;
	register struct pte *pte;
	register struct ifvba *ifvba = ifvba0;
	struct ifvba *vlim  = ifvba + n;

	n = roundup(extrasize + (n * bufsize), NBPG);
	cp = (caddr_t)malloc((u_long)n, M_DEVBUF, M_NOWAIT);
	if ((n + kvtophys(cp)) > VB_MAXADDR24) {
		free(cp, M_DEVBUF);
		cp = 0;
	}
	if (cp == 0) {
		printf("No memory for device buffer(s)\n");
		return (0);
	}
	/*
	 * Make raw buffer pages uncacheable.
	 */
	pte = kvtopte(cp);
	for (n = btoc(n); n--; pte++)
		pte->pg_nc = 1;
	mtpr(TBIA, 0);
	if (extra) {
		*extra = cp;
		cp += extrasize;
	}
	for (; ifvba < vlim; ifvba++) {
		ifvba->iff_buffer = cp;
		ifvba->iff_physaddr = kvtophys(cp);
		cp += bufsize;
	}
	return (1);
}
/*
 * Routine to copy from VERSAbus memory into mbufs.
 *
 * Warning: This makes the fairly safe assumption that
 * mbufs have even lengths.
 */
static struct mbuf *
if_vbaget(rxbuf, totlen, off0, ifp)
	u_char *rxbuf;
	int totlen, off0;
	struct ifnet *ifp;
{
	register u_char *cp, *mcp;
	register struct mbuf *m;
	struct mbuf *top = 0, **mp = &top;
	int len, off = off0;

	cp = rxbuf + sizeof (struct ether_header);
	while (totlen > 0) {
		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == 0) 
			goto bad;
		if (off) {
			len = totlen - off;
			cp = rxbuf + sizeof (struct ether_header) + off;
		} else
			len = totlen;
		if (len >= NBPG) {
			MCLGET(m);
			if (m->m_len == CLBYTES)
				m->m_len = len = MIN(len, CLBYTES);
			else
				m->m_len = len = MIN(MLEN, len);
		} else {
			m->m_len = len = MIN(MLEN, len);
			m->m_off = MMINOFF;
		}
		mcp = mtod(m, u_char *);
		if (ifp) {
			/*
			 * Prepend interface pointer to first mbuf.
			 */
			*(mtod(m, struct ifnet **)) = ifp;
			mcp += sizeof (ifp);
			len -= sizeof (ifp);
			ifp = (struct ifnet *)0;
		}
		bcopy(cp, mcp, (u_int)len);
		cp += len;
		*mp = m;
		mp = &m->m_next;
		if (off == 0) {
			totlen -= len;
			continue;
		}
		off += len;
		if (off == totlen) {
			cp = rxbuf + sizeof (struct ether_header);
			off = 0;
			totlen = off0;
		}
	}
	return (top);
bad:
	m_freem(top);
	return (0);
}

static
if_vbaput(ifu, m0)
caddr_t ifu;
struct mbuf *m0;
{
	register struct mbuf *m = m0;
	register caddr_t cp = ifu;

	while (m) {
		bcopy(mtod(m, caddr_t), cp, (u_int)m->m_len);
		cp += m->m_len;
		MFREE(m, m0);
		m = m0;
	}
	if ((int)cp & 1)
		*cp++ = 0;
	return (cp - ifu);
}
#endif