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

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

/*
 * Copyright (c) 1988 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)if_ace.c	7.2 (Berkeley) 6/29/88
 */

/*
 * ACC VERSAbus Ethernet controller
 */
#include "ace.h"
#if NACE > 0

#include "param.h"
#include "systm.h"
#include "mbuf.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 "../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/ip_var.h"
#include "../netinet/if_ether.h"
#endif
#ifdef NS
#include "../netns/ns.h"
#include "../netns/ns_if.h"
#endif

#include "../machine/cpu.h"
#include "../machine/pte.h"

#include "../tahoe/mtpr.h"
#include "../tahoeif/if_acereg.h"
#include "../tahoevba/vbavar.h"

int	aceprobe(), aceattach(), acerint(), acecint();
struct	vba_device *aceinfo[NACE];
long	acestd[] = { 0 };
struct	vba_driver acedriver =
    { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 };

int	aceinit(), aceoutput(), aceioctl(), acereset();
struct	mbuf *aceget();

/*
 * Ethernet software status per interface.
 *
 * Each interface is referenced by a network interface structure,
 * is_if, which the routing code uses to locate the interface.
 * This structure contains the output queue for the interface, its address, ...
 */
struct	ace_softc {
	struct	arpcom is_ac;		/* Ethernet common part	*/
#define	is_if	is_ac.ac_if		/* network-visible interface */
#define	is_addr	is_ac.ac_enaddr		/* hardware Ethernet address */
	short	is_flags;
#define	ACEF_OACTIVE	0x1		/* output is active */
#define	ACEF_RCVPENDING	0x2		/* start rcv in acecint	*/
	short	is_promiscuous;		/* true is enabled */
	short	is_segboundry;		/* first TX Seg in dpm */
	short	is_eictr;		/* Rx segment tracking ctr */
	short	is_eoctr;		/* Tx segment tracking ctr */
	short	is_txnext;		/* Next available Tx segment */
	short	is_currnd;		/* current random backoff */
	struct	ace_stats is_stats;	/* holds board statistics */
	short	is_xcnt;		/* count xmitted segments to be acked 
					   by the controller */
	long	is_ivec;		/* autoconfig interrupt vector base */
	struct	pte *is_map;		/* pte map for dual ported memory */
	caddr_t	is_dpm;			/* address of mapped memory */
} ace_softc[NACE];
extern	struct ifnet loif;

aceprobe(reg, vi)
	caddr_t reg;
	struct vba_device *vi;
{
	register br, cvec;		/* must be r12, r11 */
	struct acedevice *ap = (struct acedevice *)reg;
	struct ace_softc *is = &ace_softc[vi->ui_unit];

#ifdef lint
	br = 0; cvec = br; br = cvec;
	acerint(0); acecint(0);
#endif
	if (badaddr(reg, 2))
		return (0);
	movow(&ap->csr, CSR_RESET);
	DELAY(10000);
#ifdef notdef
	/*
	 * Select two spaces for the interrupts aligned to an
	 * eight vector boundary and fitting in 8 bits (as
	 * required by the controller) -- YECH.  The controller
	 * will be notified later at initialization time.
	 */
	if ((vi->ui_hd->vh_lastiv -= 2) > 0xff)
		vi->ui_hd->vh_lastiv  = 0x200;
	is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7;
#else
	is->is_ivec = 0x90+vi->ui_unit*8;
#endif
	br = 0x14, cvec = is->is_ivec;		/* XXX */
	return (sizeof (*ap));
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */
aceattach(ui)
	struct vba_device *ui;
{
	register short unit = ui->ui_unit;
	register struct ace_softc *is = &ace_softc[unit];
	register struct ifnet *ifp = &is->is_if;
	register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
	register short *wp, i;

	ifp->if_unit = unit;
	ifp->if_name = "ace";
	ifp->if_mtu = ETHERMTU;
	/*
	 * Get station's addresses and set multicast hash table.
	 */
	for (wp = (short *)addr->station, i = 0; i < 6; i++)
		is->is_addr[i] = ~*wp++;
	printf("ace%d: hardware address %s\n", unit,
	    ether_sprintf(is->is_addr));
	is->is_promiscuous = 0;
	for (wp = (short *)addr->hash, i =  0; i < 8; i++)
		movow(wp++, ~0xf); 
	movow(&addr->bcastena[0], ~0xffff); 
	movow(&addr->bcastena[1], ~0xffff);
	/*
	 * Allocate and map dual ported VERSAbus memory.
	 */
	if (vbmemalloc(32, (caddr_t)ui->ui_flags,
	    &is->is_map, &is->is_dpm) == 0) {
		printf("ace%d: can't allocate VERSAbus memory map\n", unit);
		return;
	}

	ifp->if_init = aceinit;
	ifp->if_output = aceoutput;
	ifp->if_ioctl = aceioctl;
	ifp->if_reset = acereset;
	ifp->if_flags = IFF_BROADCAST;
	if_attach(ifp);
}

/*
 * Reset of interface after "system" reset.
 */
acereset(unit, vban)
	int unit, vban;
{
	register struct vba_device *ui;

	if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 ||
	    ui->ui_vbanum != vban)
		return;
	printf(" ace%d", unit);
	aceinit(unit);
}

/*
 * Initialization of interface; clear recorded pending operations
 */
aceinit(unit)
	int unit;
{
	register struct ace_softc *is = &ace_softc[unit];
	register struct vba_device *ui = aceinfo[unit];
	register struct acedevice *addr;
	register struct ifnet *ifp = &is->is_if;
	register short Csr;
	register int s;

	if (ifp->if_addrlist == (struct ifaddr *)0)
		return;
	if ((ifp->if_flags & IFF_RUNNING) == 0) {
		/*
		 * Reset the controller, initialize the recieve buffers,
		 * and turn the controller on again and set board online.
		 */
		addr = (struct acedevice *)ui->ui_addr;
		s = splimp();
		movow(&addr->csr, CSR_RESET);
		DELAY(10000);

		/*
		 * Clean up dpm since the controller might
		 * jumble dpm after reset.
		 */
		acesetup(unit);
		movow(&addr->csr, CSR_GO);
		Csr = addr->csr;
		if (Csr & CSR_ACTIVE) {
			movow(&addr->ivct, is->is_ivec);
			Csr |= CSR_IENA | is->is_promiscuous;
			movow(&addr->csr, Csr);
			is->is_flags = 0;
			is->is_xcnt = 0;
			is->is_if.if_flags |= IFF_RUNNING;
		}
		splx(s);
	}
	if (is->is_if.if_snd.ifq_head)
		acestart(unit);
}

/*
 * Start output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 */
acestart(unit)
	int unit;
{
	register struct tx_segment *txs;
	register long len;
	register int s;
	register struct ace_softc *is = &ace_softc[unit];
	struct mbuf *m;
	short retries;

	if (is->is_flags & ACEF_OACTIVE)
		return;
	is->is_flags |= ACEF_OACTIVE;
again:
	txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11));
	if (txs->tx_csr & TCS_TBFULL) {
		is->is_stats.tx_busy++;
		is->is_flags &= ~ACEF_OACTIVE;
		return;
	}
	s = splimp();
	IF_DEQUEUE(&is->is_if.if_snd, m);
	splx(s);
	if (m == 0) {
		is->is_flags &= ~ACEF_OACTIVE;
		return;
	}
	len = aceput(unit, txs->tx_data, m);
	retries = txs->tx_csr & TCS_RTC;
	if (retries > 0)
		acebakoff(is, txs, retries);

	/*
	 * Ensure minimum packet length.
	 * This makes the safe assumtion that there are no virtual holes
	 * after the data.
	 * For security, it might be wise to zero out the added bytes,
	 * but we're mainly interested in speed at the moment.
	 */
	if (len - sizeof (struct ether_header) < ETHERMIN)
		len = ETHERMIN + sizeof (struct ether_header);
	if (++is->is_txnext > SEG_MAX) 
		is->is_txnext = is->is_segboundry;
	is->is_if.if_opackets++;
	is->is_xcnt++;
	len = (len & 0x7fff) | TCS_TBFULL;
	movow(txs, len);
	goto again;
}

/*
 * Transmit done interrupt.
 */
acecint(unit)
	int unit;
{
	register struct ace_softc *is = &ace_softc[unit];
	register struct tx_segment *txseg;
	short eostat;

	if (is->is_xcnt <= 0)  {
		log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n",
		    unit, is->is_xcnt);
		is->is_xcnt = 0;
		if (is->is_if.if_snd.ifq_head)
			acestart(unit);
		return;
	}
	is->is_xcnt--;
	txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm);
	eostat = txseg->tx_csr;
	if ((eostat & TCS_TBFULL) == 0) {
		is->is_stats.tx_retries += eostat & TCS_RTC;
		if (eostat & TCS_RTFAIL)  {
			is->is_stats.tx_discarded++;
			is->is_if.if_oerrors++;
		} else 
			is->is_stats.tx_datagrams++;
		if (++is->is_eoctr >= 16)
			is->is_eoctr = is->is_segboundry; 
	} 
	if (is->is_if.if_snd.ifq_head)
		acestart(unit);
}

/*
 * Ethernet interface receiver interrupt.
 * If input error just drop packet.
 * Otherwise purge input buffered data path and examine 
 * packet to determine type.  If can't determine length
 * from type, then have to drop packet.  Othewise decapsulate
 * packet based on type and pass to type specific higher-level
 * input routine.
 */
acerint(unit)
	int unit;
{
	register struct ace_softc *is = &ace_softc[unit];
	register struct ifqueue *inq;
	register struct ether_header *ace;
	register struct rx_segment *rxseg;
	int len, s, off, resid;
	struct mbuf *m;
	short eistat;

	if ((is->is_if.if_flags&IFF_RUNNING) == 0)
		return;
again:
	rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm);
	eistat = rxseg->rx_csr;
	if ((eistat & RCS_RBFULL) == 0)
		return;
	is->is_if.if_ipackets++;
	if (++is->is_eictr >= is->is_segboundry) 
		is->is_eictr = 0;
	len = eistat & RCS_RBC;
	if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) ||
	    len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) {
		if (eistat & RCS_ROVRN)
			is->is_stats.rx_overruns++;
		if (eistat & RCS_RCRC)
			is->is_stats.rx_crc_errors++;
		if (eistat & RCS_RODD)
			is->is_stats.rx_align_errors++;
		if (len < ET_MINLEN)
			is->is_stats.rx_underruns++;
		if (len > ET_MAXLEN+CRC_SIZE)
			is->is_stats.rx_overruns++;
		is->is_if.if_ierrors++;
		rxseg->rx_csr = 0;
		return;
	} else
		is->is_stats.rx_datagrams++;
	ace = (struct ether_header *)rxseg->rx_data;
	len -= sizeof (struct ether_header);
	/*
	 * Deal with trailer protocol: if type is trailer
	 * get true type from first 16-bit word past data.
	 * Remember that type was trailer by setting off.
	 */
	ace->ether_type = ntohs((u_short)ace->ether_type);
#define	acedataaddr(ace, off, type) \
    ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off))))
	if (ace->ether_type >= ETHERTYPE_TRAIL &&
	    ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
		off = (ace->ether_type - ETHERTYPE_TRAIL) * 512;
		if (off >= ETHERMTU)
			goto setup;		/* sanity */
		ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *));
		resid = ntohs(*(acedataaddr(ace, off+2, u_short *)));
		if (off + resid > len)
			goto setup;		/* sanity */
		len = off + resid;
	} else
		off = 0;
	if (len == 0)
		goto setup;

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; aceget 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 = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if);
	if (m == 0)
		goto setup;
	if (off) {
		struct ifnet *ifp;

		ifp = *(mtod(m, struct ifnet **));
		m->m_off += 2 * sizeof (u_short);
		m->m_len -= 2 * sizeof (u_short);
		*(mtod(m, struct ifnet **)) = ifp;
	}
	switch (ace->ether_type) {

#ifdef INET
	case ETHERTYPE_IP:
		schednetisr(NETISR_IP);
		inq = &ipintrq;
		break;
#endif

	case ETHERTYPE_ARP:
		arpinput(&is->is_ac, m);
		goto setup;
#ifdef NS
	case ETHERTYPE_NS:
		schednetisr(NETISR_NS);
		inq = &nsintrq;
		break;

#endif
	default:
		m_freem(m);
		goto setup;
	}
	if (IF_QFULL(inq)) {
		IF_DROP(inq);
		m_freem(m);
		goto setup;
	}
	s = splimp();
	IF_ENQUEUE(inq, m);
	splx(s);
setup:
	rxseg->rx_csr = 0;
	goto again;
}

/*
 * 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.
 */
aceoutput(ifp, m0, dst)
	struct ifnet *ifp;
	struct mbuf *m0;
	struct sockaddr *dst;
{
	register struct ace_softc *is = &ace_softc[ifp->if_unit];
	register struct mbuf *m = m0;
	register struct ether_header *ace;
	register int off;
	struct mbuf *mcopy = (struct mbuf *)0;
	int type, s, error, usetrailers;
	u_char edst[6];
	struct in_addr idst;

	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(&is->is_ac, m, &idst, edst, &usetrailers))
			return (0);	/* if not yet resolved */
		if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr,
		    sizeof (edst)))
			mcopy = m_copy(m, 0, (int)M_COPYALL);
		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;
		off = 0;
		goto gottype;
#endif
#ifdef NS
	case AF_NS:
 		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
		    (caddr_t)edst, sizeof (edst));
		if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst)))
			mcopy = m_copy(m, 0, (int)M_COPYALL);
		else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost,
		    sizeof(edst)))
			return(looutput(&loif, m, dst));
		type = ETHERTYPE_NS;
		off = 0;
		goto gottype;
#endif
	case AF_UNSPEC:
		ace = (struct ether_header *)dst->sa_data;
		bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst));
		type = ace->ether_type;
		goto gottype;

	default:
		log(LOG_ERR, "ace%d: can't handle af%d\n",
		    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);
	}
	ace = mtod(m, struct ether_header *);
	bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst));
	bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost,
	    sizeof (is->is_addr));
	ace->ether_type = htons((u_short)type);

	/*
	 * 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);
		error = ENOBUFS;
		goto qfull;
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	splx(s);
	acestart(ifp->if_unit);
	return (mcopy ? looutput(&loif, mcopy, dst) : 0);
qfull:
	m0 = m;
	splx(s);
bad:
	m_freem(m0);
	if (mcopy)
		m_freem(mcopy);
	return (error);
}

/*
 * Routine to copy from mbuf chain to transmit buffer on the VERSAbus
 * If packet size is less than the minimum legal size,
 * the buffer is expanded.  We probably should zero out the extra
 * bytes for security, but that would slow things down.
 */
/*ARGSUSED*/
aceput(unit, txbuf, m)
	int unit;
	char *txbuf;
	struct mbuf *m;
{
	register u_char *bp, *mcp;
	register short *s1, *s2;
	register u_int len;
	register struct mbuf *mp;
	int total;

	total = 0;
	bp = (u_char *)txbuf;
	for (mp = m; (mp); mp = mp->m_next) {
		len = mp->m_len;
		if (len == 0)
			continue;
		total += len;
		mcp = mtod(mp, u_char *);
		if (((int)mcp & 01) && ((int)bp & 01)) {
			/* source & destination at odd addresses */
			movob(bp++, *mcp++);
			--len;
		}
		if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
			register u_int l;

			s1 = (short *)bp;
			s2 = (short *)mcp;
			l = len >> 1;		/* count # of shorts */
			while (l-- != 0)
				movow(s1++, *s2++);
			len &= 1;		/* # remaining bytes */
			bp = (u_char *)s1;
			mcp = (u_char *)s2;
		}
		while (len-- != 0)
			movob(bp++, *mcp++);
	}
	m_freem(m);
	return (total);
}

/*
 * Routine to copy from VERSAbus memory into mbufs.
 *
 * Warning: This makes the fairly safe assumption that
 * mbufs have even lengths.
 */
/*ARGSUSED*/
struct mbuf *
aceget(rxbuf, totlen, off0, ifp)
	u_char *rxbuf;
	int totlen, off0;
	struct ifnet *ifp;
{
	register u_char *cp, *mcp;
	register int tlen;
	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 (ifp)
			len += sizeof(ifp);
		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((caddr_t)cp, (caddr_t)mcp, len);*/
		/*cp += len; mcp += len;*/
		tlen = len;
		if (((int)mcp & 01) && ((int)cp & 01)) {
			/* source & destination at odd addresses */
			*mcp++ = *cp++;
			--tlen;
		}
		if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) {
			register short *s1, *s2;
			register int l;

			s1 = (short *)mcp;
			s2 = (short *)cp;
			l = tlen >> 1;		/* count # of shorts */
			while (l-- > 0)		/* copy shorts */
				*s1++ = *s2++;
			tlen &= 1;		/* # remaining bytes */
			mcp = (u_char *)s1;
			cp = (u_char *)s2;
		}
		while (tlen-- > 0)
			*mcp++ = *cp++;
		*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);
}

/* backoff table masks */
short	random_mask_tbl[16] = {
	0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0,
	0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0
};

acebakoff(is, txseg, retries)
	struct ace_softc *is;
	struct tx_segment *txseg;
	register int retries;
{
	register short *pBakNum, random_num;
	short *pMask;

	pMask = &random_mask_tbl[0];
	pBakNum = &txseg->tx_backoff[0];
	while (--retries >= 0) {
		random_num = (is->is_currnd = (is->is_currnd * 18741)-13849);
		random_num &= *pMask++;
		*pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc);
	}
}

/*
 * Process an ioctl request.
 */
aceioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	int cmd;
	caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	struct acedevice *addr;
	int s = splimp(), error = 0;

	switch (cmd) {

	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		switch (ifa->ifa_addr.sa_family) {
#ifdef INET
		case AF_INET:
			aceinit(ifp->if_unit);	/* before arpwhohas */
			((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: {
			struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
			struct ace_softc *is = &ace_softc[ifp->if_unit];

			if (!ns_nullhost(*ina)) {
				ifp->if_flags &= ~IFF_RUNNING;
				addr = (struct acedevice *)
				    aceinfo[ifp->if_unit]->ui_addr;
				movow(&addr->csr, CSR_RESET);
				DELAY(10000);
				/* set station address & copy addr to arp */
				acesetaddr(ifp->if_unit, addr, 
				    ina->x_host.c_host);
			} else
				ina->x_host = *(union ns_host *)is->is_addr;
			aceinit(ifp->if_unit);
			break;
		}
#endif
		default:
			aceinit(ifp->if_unit);
			break;
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) {
			addr = (struct acedevice *)
			    (aceinfo[ifp->if_unit]->ui_addr);
			movow(&addr->csr, CSR_RESET);
			ifp->if_flags &= ~IFF_RUNNING;
		} else if (ifp->if_flags&IFF_UP &&
		    (ifp->if_flags&IFF_RUNNING) == 0)
			aceinit(ifp->if_unit);
		break;

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

/*
 * Set the on-board station address, then read it back
 * to initialize the address used by ARP (among others).
 */
acesetaddr(unit, addr, station)
	short unit;
	struct acedevice *addr;
	u_char *station;
{
	struct ace_softc *is = &ace_softc[unit];
	register short *wp, i;

	for (wp = (short *)addr->station, i = 0; i < 6; i++)
		movow(wp++, ~*station++); 
	for (wp = (short *)addr->station, i = 0; i < 6; i++)
		is->is_addr[i] = ~*wp++;
	printf("ace%d: hardware address %s\n", unit,
	    ether_sprintf(is->is_addr));
}

/*
 * Setup the device for use.  Initialize dual-ported memory,
 * backoff parameters, and various other software state.
 */
acesetup(unit)
	int unit;
{
	register struct ace_softc *is = &ace_softc[unit];
	register char *pData1;
	register short i;
	struct acedevice *addr;

	bzero(is->is_dpm, 16384*2);
	is->is_currnd = 49123;
	addr = (struct acedevice *)aceinfo[unit]->ui_addr;
	is->is_segboundry = (addr->segb >> 11) & 0xf;
	pData1 = is->is_dpm + (is->is_segboundry << 11);
	for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) {
		acebakoff(is, (struct tx_segment *)pData1, 15);
		pData1 += sizeof (struct tx_segment);
	}
	is->is_eictr = 0;
	is->is_eoctr = is->is_txnext = is->is_segboundry;
	bzero((char *)&is->is_stats, sizeof (is->is_stats));
}
#endif