/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:if_ec.c 12.0$ */
/* $ACIS:if_ec.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caif/RCS/if_ec.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:if_ec.c 12.0$";
#endif lint

/*
 * 3Com Ethernet Adapter (4.3 driver)
 */

#include "ec.h"
#if NEC > 0

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

#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 "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"

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

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

#include "../machine/io.h"
#include "if_ecreg.h"
#include "../machineio/ioccvar.h"
#include "../machine/debug.h"

int	ecprobe(), ecattach(), ecint();
struct	iocc_device *ecinfo[NEC];
caddr_t ecstd[] = { (caddr_t) 0xf0000300, 0 };
struct	iocc_driver ecdriver =
/*	  probe	 slave  attach dgo addr	dname dinfo mname minfo intr csr */
	{ ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo, 0, 0, ecint, EC_ADDR };

int	ecinit(),ecioctl(),ecoutput(),ecreset(), ecrint(), ecxint(), eckick();
struct	mbuf *ecget();

extern struct ifnet loif;

/*
 * Ethernet software status per adapter.
 */
struct	ec_softc {
	struct	arpcom es_ac;		/* generic network interface */
#define	es_if	es_ac.ac_if		/* ifnet struct */
#define	es_addr	es_ac.ac_enaddr		/* hardware (i.e. Ethernet) address */
	short	es_oactive;		/* 1 => output active */
	short	es_rexptr;		/* output re-xmit ptr */
} ec_softc[NEC];
 
#ifdef DEBUG
char ecdbug;
#endif DEBUG

ecprobe(reg)
        register caddr_t reg;
{
	register struct ecdevice *addr = (struct ecdevice *)reg;

        addr->ec_acr = EC_RESET;        /* Resets all control regs */
        addr->ec_acr = EC_CLEAR;	/* Have to clear reset bit */
	return(PROBE_NOINT);
}


/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */
ecattach(iod)
        register struct iocc_device *iod;
{
	register struct ec_softc *es = &ec_softc[iod->iod_unit];
	register struct ifnet *ifp = &es->es_if;
	register struct ecdevice *addr = (struct ecdevice *)iod->iod_addr;
	register int i;

	ifp->if_unit = iod->iod_unit;
	ifp->if_name = "ec";
	ifp->if_mtu = ETHERMTU;

	/*
	 * Read the ethernet address off the board.
	 * Save it and also write it back to the station address area.
	 */
	for (i = 0; i < ETH_ADDR_SIZE; i++) {
		addr->ec_gptr[0] = i;
		addr->ec_saddr[i] = es->es_addr[i] = addr->ec_saprom;
	}
	printf("ec%d: hardware address ", ifp->if_unit);
        ecprintethaddr(es->es_addr);
	printf("\n");
	ifp->if_init = ecinit;
	ifp->if_ioctl = ecioctl;
	ifp->if_output = ecoutput;
	ifp->if_reset = ecreset;
        ifp->if_flags = IFF_BROADCAST;
	if_attach(ifp);
        DEBUGF(ecdbug, printf("ec%d: attached\n", iod->iod_unit);)
}

/*
 * Reset of interface after IOCC reset.
 */
ecreset(unit)
	register unsigned int unit;
{
	register struct iocc_device *iod;

	if (unit < NEC && (iod = ecinfo[unit]) != 0 && iod->iod_alive != 0){
		ec_softc[unit].es_if.if_flags &= ~IFF_RUNNING;
		DEBUGF(ecdbug, printf("ec%d: reset\n", unit);)
	        ecinit(unit);
        }
}

/*
 * Reinitialization routine
 */
eckick(unit)
	register int unit;
{
	register struct ec_softc *es = &ec_softc[unit];
	register struct ifnet *ifp = &es->es_if;

	ifp->if_flags &= ~IFF_RUNNING;
	ecinit(ifp->if_unit);
}

/*
 * Initialization of interface
 */
ecinit(unit)
	register int unit;
{
	register struct ec_softc *es = &ec_softc[unit];
	register struct ecdevice *addr;
	register struct ifnet *ifp = &es->es_if;
	register int s;
	register int i;

        if (ifp->if_addrlist == (struct ifaddr *) 0)
		return;    /* no address */
        
	if ((ifp->if_flags & IFF_RUNNING) == 0) {
		addr = (struct ecdevice *) (ecinfo[unit]->iod_addr);
		s = splimp();
		              /* Following assumes we own the IRQ level */
	        addr->ec_acr = EC_RESET;        /* Resets all control regs */
	        addr->ec_acr = EC_CLEAR;	/* Have to clear reset bit */
	        i = addr->ec_rcr;		/* Have to clear rcv intrp */
	        addr->ec_rcr = EC_RCVNONE;	/* Disable receiver */
	        i = addr->ec_xcr;		/* Have to clear xmt intrp */
	        addr->ec_xcr = EC_XMTNORMAL;	/* Detect collision / success */
	        es->es_oactive = 0;             /* output is inactive */
	        addr->ec_rptr[0] = 0;           /* Reset the receive */
	        addr->ec_rptr[1] = 0;           /* pointer           */
	        for (i = 0; i < ETH_ADDR_SIZE; i++)
		     addr->ec_saddr[i] = es->es_addr[i];
		ifp->if_flags |= IFF_RUNNING;
		if (es->es_if.if_snd.ifq_head){	   /* anything on send queue */
		        ecstart(unit, es, addr);
		}
		splx(s);
        	DEBUGF(ecdbug, printf("ec%d: initialized\n", unit));
		timeout(eckick, unit, 180);
					  /* reinitialize adapter every 3 sec */
                                          /* to keep it alive. Sometimes a    */
                                          /* transmit interrupt doesn't occur */
                                          /* causing the system to hang.      */
	}
}

/*
 * Start or restart output on interface, otherwise give buffer to receiver.
 * If interface is already active, then this is a retransmit
 * after a collision, and just restuff registers.
 * If interface is not already active, get another datagram
 * to send off of the interface queue, and map it to the interface
 * before starting the output.  If no more data to send, then give
 * buffer to receiver and enable interrupts.
 */
ecstart(unit, es, addr)
        register int unit;
	register struct ec_softc *es;
	register struct ecdevice *addr;
{
	register xptr;
        register struct mbuf *m;

	DEBUGF(ecdbug, printf("ecstart\n"));
	addr->ec_rcr = EC_RCVNONE;                 /* disable address match */
	addr->ec_acr = EC_CLEAR;                   /* system bus owns buffer */
	addr->ec_rptr[0] = 0;                      /* reset buffer receive */
	addr->ec_rptr[1] = 0;                      /* pointer              */
	if (es->es_oactive) {                      /* RETRANSMIT after */
		xptr = es->es_rexptr;              /* a collision */
                                      /* exponential backoff after collision  */
                                      /* is automatically done by the adapter */
	}
	else {
		IF_DEQUEUE(&es->es_if.if_snd, m);
		if (m == 0) {                    /* no more data to transmit */
                            /* give buffer to receiver and enable interrupts */
			addr->ec_acr = EC_RCVGO|EC_RIDE;
                               /* receive broadcasts, short and good packets */
			addr->ec_rcr = EC_RCVNORMAL;
                                             /* clear any receive interrupts */
			xptr = addr->ec_rcr;
			return;
		}
		xptr = ecput(addr, m); /* put data to be transmitted into bufr*/
	}
	es->es_rexptr = xptr;        /* save transmit pointer for retransmits */
	addr->ec_gptr[0] = xptr;     /* set general pointer to the beginning */
	addr->ec_gptr[1] = (xptr >> 8) & 07; /* of packet to be transmitted */ 
	xptr = addr->ec_rcr;                 /* clear any receive interrupts */
	xptr = addr->ec_xcr;                 /* clear any transmit interrupts */
	addr->ec_xcr = EC_XMTNORMAL;         /* detect success or a collision */
                          /* give buffer to transmitter and enable interrupts */
	addr->ec_acr = EC_XMTGO|EC_RIDE;
	es->es_oactive = 1;                  /* set output flag to active */
}

/*
 * Ethernet adapter interrupt. Figure out who done it.
 */
ecint(unit)
	register int unit;
{
	register struct ec_softc *es = &ec_softc[unit];
	register struct ecdevice *addr =
		(struct ecdevice *)ecinfo[unit]->iod_addr;
	register char status = addr->ec_acr;        /* xmit & rcv completion */

	if (es->es_oactive) {          /*  output was active (transmitting) */
       	DEBUGF(ecdbug,printf("ecxint: acr=%b ", status&0xFF, EC_ACRBITS));
	        if ( !(status & EC_XMTBUSY)) {    /* transmit complete  */
			ecxint(unit, es, addr);   /* either a collision or   */
			return 0;                 /* successful transmission */
		}
	}
	else {                        /* output was not active (receiving)  */
	DEBUGF(ecdbug,printf("ecrint: acr=%b ", status&0xFF, EC_ACRBITS));
		if ( !(status & EC_RCVBUSY)) {    /* the controller has     */
			ecrint(unit, es, addr);   /* accepted a packet.     */
			return 0;                 
		}
 	 	status = addr->ec_rcr;      /* clear stray rcvr intrpt */
	}
	DEBUGF( (ecdbug & 0x80), printf("stray\n"));  /*  not us */
	return 0;                /* changed to claim all interrupts since the */
	                         /* 3Com board gets some that are unexpected. */
}

/*
 * Ethernet interface transmitter interrupt.
 * Start another output if more data to send.
 */
ecxint(unit, es, addr)
	register int unit;
	register struct ec_softc *es;
	register struct ecdevice *addr;
{
	register char status = addr->ec_xcr;    /* get transmitter status */

	DEBUGF(ecdbug, printf("xcr=%b\n", status&0xFF, EC_XCRBITS));
	if (status & EC_XMT16) {          /* packet experienced 16 collisions */
		es->es_if.if_oerrors++;     /* increment output error counter */
		es->es_if.if_collisions += 16; /* increment collision counter */
		printf("ec%d: ethernet jammed\n", unit);
	}
	else if (status & EC_XMTJAM) {      /* packet experienced a collision */
		es->es_if.if_collisions++;  /* increment collision counter */
             }
        else {                              /* Must have worked ! */
		es->es_oactive = 0;         /* set output flag to inactive */
		es->es_if.if_opackets++;    /* increment packet sent counter */
	}
	ecstart(unit, es, addr); /* retransmit, transmit another packet if */
}                                /* more data, or give buffer to receiver  */

/*
 * Ethernet adapter 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.  Otherwise decapsulate
 * packet based on type and pass to type specific higher-level
 * input routine.
 */
ecrint(unit, es, addr)
	register int unit;
	register struct ec_softc *es;
	register struct ecdevice *addr;
{
	register char status;

	addr->ec_rcr = EC_RCVNONE;      /* address match mode disabled */
	status = addr->ec_rcr;          /* get receiver status */
	DEBUGF( (ecdbug & 0x80), printf("rcr=%b\n", status, EC_RCRBITS));
	addr->ec_acr = EC_CLEAR;        /* system bus has access to buffer */
	if ( !(status & EC_STALE)) {    /* interesting packet was detected */
		if (status & EC_OVERFLOW){       /* lost packet due to buffer */
			es->es_if.if_ierrors++;  /* full or system bus owns it*/
			}
		if ( !(status & EC_SHORT)){       /* good packet was received */
			ecread(unit, es, addr);  /* get packet out of buffer */
			}
	}
	ecstart(unit, es, addr); /* retransmit, transmit another packet if */
}                                /* more data, or give buffer to receiver  */

ecread(unit, es, addr)
	int unit;
	register struct ec_softc *es;
	register struct ecdevice *addr;
{
	register struct ether_header *ec;
    	struct mbuf *m;
	int len, off, resid;
	register struct ifqueue *inq;
	static char ecbuf[EC_BSIZE];	/* small! */

	es->es_if.if_ipackets++;        /* input packet counter */

	len = ((addr->ec_rptr[1]<<8)&0xFF00);
	len |= (addr->ec_rptr[0]&0xFF);
	DEBUGF( (ecdbug & 0x80), printf("ecread: len=%d ", len));
	addr->ec_gptr[0] = 0;
	addr->ec_gptr[1] = 0;
	{   register el = len;
	    register char *ep = ecbuf;
	    while (el--)
	    	{
		*ep++ = addr->ec_bufbyte;         /* read remaining bytes */
		}
	}
	len -= sizeof (struct ether_header);
	ec = (struct ether_header *)ecbuf;
	ec->ether_type = ntohs((u_short)ec->ether_type);
	DEBUGF( (ecdbug & 0x80), { printf("type=%s ",
		ec->ether_type == ETHERTYPE_IP? "IP" :
		(ec->ether_type == ETHERTYPE_ARP? "ARP" :
						       "UNK"));
		printf(
		" to=%x:%x:%x:%x:%x:%x from=%x:%x:%x:%x:%x:%x ",
			ecbuf[0], ecbuf[1], ecbuf[2], ecbuf[3],
			ecbuf[4], ecbuf[5], ecbuf[6], ecbuf[7],
			ecbuf[8], ecbuf[9], ecbuf[10], ecbuf[11]);} );
#define	ecdataaddr(ec, off, type)	((type)(((caddr_t)((ec)+1)+(off))))
	if (ec->ether_type >= ETHERTYPE_TRAIL &&
	    ec->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
		off = (ec->ether_type - ETHERTYPE_TRAIL) * 512;
		if (off >= ETHERMTU)
			goto chuckit;		/* sanity */
		ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *));
		resid = ntohs(*(ecdataaddr(ec, off+2, u_short *)));
		if (off + resid > len)
			goto chuckit;		/* sanity */
		len = off + resid;
	} else
		off = 0;
	if (len == 0)
		goto chuckit;

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; ecget 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 = ecget(ecbuf, len, off, &es->es_if);
	if (m == 0)
		goto chuckit;
	if (off) {
                struct ifnet *ifp;

                 /* bcopy is used since word moves must be on 4 byte */
                 /* boundaries on the RT PC */

                bcopy(mtod(m, char *), (char *) &ifp, sizeof(ifp));
		m->m_off += 2 * sizeof (u_short);
		m->m_len -= 2 * sizeof (u_short);
                bcopy((char *) &ifp, mtod(m, char *), sizeof(ifp));
	}
	switch (ec->ether_type) {

#ifdef INET
	case ETHERTYPE_IP:
                DEBUGF(ecdbug & 0x80, printf("ip packet\n");)
		schednetisr(NETISR_IP);
		inq = &ipintrq;
		break;

	case ETHERTYPE_ARP:
                DEBUGF(ecdbug & 0x80, printf("arp packet\n");)
		arpinput(&es->es_ac, m);
		goto chuckit;
#endif INET
#ifdef NS
        case ETHERTYPE_NS:
                DEBUGF(ecdbug & 0x80, printf("ns packet\n");)
                schednetisr(NETISR_NS);
                inq = &nsintrq;
                break;
#endif NS
	default:
		m_freem(m);
		goto chuckit;
	}

	if (IF_QFULL(inq)) {
		DEBUGF(ecdbug & 0x80, printf("qfull\n");)
		IF_DROP(inq);
		m_freem(m);
		goto chuckit;
	}
	IF_ENQUEUE(inq, m);
	DEBUGF(ecdbug & 0x80, printf("queued\n");)

chuckit:
	/*
	 * All done.
	 */
	return;
}

/*
 * 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.
 * If destination is this address or broadcast, send packet to
 * loop device to kludge around the fact that 3Com interfaces can't
 * talk to themselves very well.
 */
ecoutput(ifp, m0, dst)
	register struct ifnet *ifp;
	register struct mbuf *m0;
	register struct sockaddr *dst;
{
	int type, s, error;
	u_char edst[ETH_ADDR_SIZE];
	struct in_addr idst;
	register struct ec_softc *es = &ec_softc[ifp->if_unit];
	register struct mbuf *m = m0;
	register struct ether_header *ec;
        int off;
	struct mbuf *mcopy = (struct mbuf *)0;
        int usetrailers;

        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(&es->es_ac, m, &idst, edst, &usetrailers))
			return 0;	/*  not  resolved */
		if (in_lnaof(idst) == INADDR_ANY)
			mcopy = m_copy(m, 0, (int)M_COPYALL);
		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
		/* need per host negotiation */
		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 *) = ntohs((u_short)ETHERTYPE_IP);
			*(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len);
	 		goto gottrailertype;
		}
		type = ETHERTYPE_IP;
		off = 0;
		goto gottype;
#endif INET

#ifdef NS
        case AF_NS:
                bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
                  (caddr_t)edst, sizeof(edst));
                type = ETHERTYPE_NS;   
                off = 0;
		goto gottype;
#endif NS

	case AF_UNSPEC:
		ec = (struct ether_header *)dst->sa_data;
		bcopy((caddr_t)ec->ether_dhost, (caddr_t)edst, sizeof (edst));
		type = ec->ether_type;
		goto gottype;

	default:
		printf("ec%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);
	}
	ec = mtod(m, struct ether_header *);
	bcopy((caddr_t)edst, (caddr_t)ec->ether_dhost, sizeof (edst));
        bcopy((caddr_t)es->es_addr, (caddr_t)ec->ether_shost, 
          sizeof(ec->ether_shost)); 
	ec->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;
	        if (mcopy != 0)
	            m_freem(mcopy);
		goto qfull;
	}
	IF_ENQUEUE(&ifp->if_snd, m);
       	if (es->es_oactive == 0) {
       		register unit = ifp->if_unit;
       		ecstart(unit, es,
		(struct ecdevice *)ecinfo[unit]->iod_addr);
	}
	splx(s);
	return (mcopy ? looutput(&loif, mcopy, dst) : 0);

qfull:
	m0 = m;
	splx(s);
bad:
	m_freem(m0);
	return error;
}

/*
 * Routine to copy from mbuf chain to the buffer on the adapter.
 * 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.
 */
int
ecput(addr, m)
	register struct ecdevice *addr;
	register struct mbuf *m;
{
	register unsigned bp;
	register u_char *mcp;
	register struct mbuf *mp;
	int xptr;
#ifdef DEBUG
	static char dbxbuf[EC_BSIZE];
	register i;
#endif DEBUG

	for (bp = EC_BSIZE, mp = m; mp; mp = mp->m_next)
		bp -= mp->m_len;
	if (EC_BSIZE - bp < ETHERMIN + sizeof (struct ether_header))
		bp = EC_BSIZE - ETHERMIN - sizeof (struct ether_header);
	xptr = bp;
	addr->ec_gptr[0] = bp;	/* short ptr, LSB first */
	addr->ec_gptr[1] = (bp >> 8) & 07;
#ifdef DEBUG
	i = bp;
	if (ecdbug) printf("ecput:  len=%d ", EC_BSIZE-i);
#endif DEBUG
	for (mp = m; mp; mp = mp->m_next) {
		bp = mp->m_len;
		mcp = mtod(mp, u_char *);
		while (bp-- > 0)
#ifdef DEBUG
			dbxbuf[i++] = *mcp++;
#else !DEBUG
			addr->ec_bufbyte = *mcp++;
#endif DEBUG
	}
#ifdef DEBUG
	mcp = (u_char *)&dbxbuf[xptr];
	i = EC_BSIZE-xptr;
	while (i-- > 0)
		{
		addr->ec_bufbyte = *mcp++;
		}
	addr->ec_gptr[0] = xptr;
	addr->ec_gptr[1] = (xptr >> 8) & 07;
	for (i=xptr; i<EC_BSIZE; i++) {
			if (addr->ec_bufbyte != dbxbuf[i]) {
				printf("\necput: verify failed, i=%d\n", i);
				goto verfail;
			}
	}
	i = xptr;
	if (ecdbug) printf("type=%s ",
		*(short *)(&dbxbuf[i+12]) == ETHERTYPE_IP? "IP" :
		(*(short *)(&dbxbuf[i+12]) == ETHERTYPE_ARP? "ARP" :
							          "UNK"));
	if (ecdbug) {
		printf(
		"to=%x:%x:%x:%x:%x:%x\n",
			dbxbuf[i+0], dbxbuf[i+1], dbxbuf[i+2],
			dbxbuf[i+3], dbxbuf[i+4], dbxbuf[i+5]);
	}
verfail:
#endif DEBUG

	m_freem(m);
	return xptr;
}

/*
 * Routine to copy from static buffer into mbufs.
 *
 * Warning: This makes the fairly safe assumption that
 * mbufs have even lengths.
 */
struct mbuf *
ecget(ecbuf, totlen, off0, ifp)
	u_char *ecbuf;
	register int totlen;
	int off0;
        struct ifnet *ifp;
{
	register struct mbuf *m;
	struct mbuf *top = 0;
	register struct mbuf **mp = &top;
	register int off = off0;
	register int len;
	register u_char *cp;

	cp = ecbuf + sizeof (struct ether_header);
	while (totlen > 0) {
		u_char *mcp;

		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == 0)
			goto bad;
		if (off) {
			len = totlen - off;
			cp = ecbuf + 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){
                        bcopy((char *) &ifp, mcp, sizeof(ifp));
                        mcp += sizeof(ifp);
                        len -= sizeof(ifp);
                        ifp = (struct ifnet *) 0;
                }
		bcopy(cp, mcp, len);
                cp += len;
		*mp = m;
		mp = &m->m_next;
		if (off == 0) {
			totlen -= len;
			continue;
		}
		off += len;
		if (off == totlen) {
			cp = ecbuf + sizeof (struct ether_header);
			off = 0;
			totlen = off0;
		}
	}
	return top;
bad:
	m_freem(top);
	return 0;
}


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

	switch (cmd) {
	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;

		switch (ifa->ifa_addr.sa_family) {
#ifdef INET
		case AF_INET:
			ecinit(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 INET
#ifdef NS
		case AF_NS:
		    {
			struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);

			if (ns_nullhost(*ina))
				ina->x_host = *(union ns_host *)(es->es_addr);
			else {
				ifp->if_flags &= ~IFF_RUNNING;
				bcopy((caddr_t) ina->x_host.c_host,
				  (caddr_t) es->es_addr, sizeof(es->es_addr));
				/*
				 *  the ecinit will set the hardware address
				 *  since the IFF_RUNNING flag is off
				 */
			}
			ecinit(ifp->if_unit);
			break;
		    }
#endif NS
		default:
			ecinit(ifp->if_unit);
			break;
		}
		break;
	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags &
		  IFF_RUNNING){
			eczap((struct ecdevice *)(ecinfo[ifp->if_unit]->
			  iod_addr));
			ifp->if_flags &= ~IFF_RUNNING;
			untimeout(eckick, ifp->if_unit);
		} else if (ifp->if_flags & IFF_UP && (ifp->if_flags &
		  IFF_RUNNING) == 0)
			ecinit(ifp->if_unit);
		break;
	default:
		error = EINVAL;
	}
	splx(s);
	return error;
}

eczap(addr)
	register struct ecdevice *addr;
{
        /* Disallow any interrupts */
	addr->ec_rcr = EC_RCVNONE;      /* address match mode disabled */
        DEBUGF(ecdbug & 0x80, printf("ZZZZAP!\n");)
}

/*
 *  ecprintethaddr - print an ethernet address
 */
ecprintethaddr(p)
	register char *p;
{
	register int i;

	for (i = 0; i < ETH_ADDR_SIZE; i++) {
		if (i != 0) printf(":");
		printf("%x", *p++);
	}
}

#endif NEC > 0
