/****************************************************************************/
/****************************************************************************/
/* Output queue needs to be checked for availability for use, reset discard,*/
/* and initial output start.						    */
/****************************************************************************/
/****************************************************************************/

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

/************************************************************************
*									*
*				Copyright 1984, 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.				*
*									*
************************************************************************/

/*	ec.c	8.14 	85/10/04	*/

#define EC_MajorVersion 8
#define EC_MinorVersion 14

/*
 * 3Com 3C400 Multibus Ethernet Controller interface
 */

/* TODO:
 *
 *	Range check unit index before indexing into ec_softc at all
 *	external entry points.
 *
 *	Change receive processing so that arbitrary addressing and
 *	packet demultiplexing methods can be used for each controller.
 *	Get the family specific packet Data Link encapsulation out of
 *	this module!
 *
 *	Suppression of local net trailer protocol should be done on a
 *	host by host basis.  Some hosts support it, some don't.
 *
 *	Complete conditionalization of the DARPA internet family.  Note
 *	that currently it cannot be turned off completely because of
 *	side effects from other families requiring it to be started first.
 *
 *	Check that multiple controllers really work.  Note that currently
 *	the next level up has strange dependencies on the existence of a
 *	single network (i.e., no internet).
 *
 *	Debugging trace enables provided through the IOCTL interface
 *	on the following variables:
 *		Receive/transmit
 *		Ethertype
 *		Address (logical and physical)
 *
 *	Re-enable cluster allocation in ecget when mbuf dtom is fixed to handle
 *	them properly.
 *
 *	Add watchdog code to check for lost status bits (interrupt enables)
 *	and controller hung on transmit.  Change MECSR manipulations to
 *	stop reading CSR so often.  The enables and packet types can be
 *	stored in the unit data base to improve robustness.
 *
 */

/*
 *	85/10/04 8.14	Do not return ENOBUFS from ecOutput.  Just discard
 *			the packet and assume the caller will retransmit
 *			on timeout.
 *
 *	85/08/02 8.11	Add ECINFO and EC_SOFTC macros to remove multiplys
 *			in array indexing.
 *			Disable multicast enable if filters are empty.
 *			Don't look at interrupt reason flags unless the
 *			corresponding interrupt enable flags are set.
 *
 *	85/04/17 8.10	Merge 4.1c FUSION and ARP support.  Update
 *			to do 4.2 autoconfigure.
 *
#ifdef notdef
 *	85/03/18 8.9	Add bulk data interface support.
#endif notdef
 *
 *	85/02/01 8.8	Fix and test ARP support for DARPA internet on the
 *			Ethernet.
 *
 *	85/01/03 8.7	Add support for VALIDnet_FWD, VALIDnet_SINK, and
 *			VALIDnet_DISK.  The FWD type implements a poor man's
 *			router (see ../vnet/validFWD.c).  The SINK type
 *			is for a packet bit bucket (see	../vnet/validSINK.c).
 *			The DISK type is for LAN virtual disk support.
 *			All three have appropriate conditional compilation
 *			switches.
 *
 *	84/11/16 8.6	Reverse order of these comments for ease of reading.
 *			Encapsulate RPC, CONN, and BCAST into a single Ethernet
 *			packet type.  This will make future extension
 *			to interneting easier.  Swap the Ethernet type field
 *			definitions to conform to the rest of the world.
 *			Insure board is reset when reading address ROM during
 *			init (it is flakey otherwise).
 *			
 *	84/10/31 8.5	Change transmit completion handling.  It was noticed
 *			that transmission normally complete before the return
 *			from the "ecoutput" routine is done; then the
 *			transmit complete interrupt has to be handled.  Now
 *			the transmit complete interrupt is used only when
 *			the transmit is not done in a SHORT time (polled).
 *
 *	84/10/11 8.4	Fix wording on warning messages that indicate that
 *			the controller has been reset.  They used to just say
 *			"reset" which led people to believe that they might
 *			need to reboot.  Now they say "controller was reset".
 *
 *	84/09/19	Block non-super users from using ioctl's that change
 *			anything!  Also, reduce the maximum output queue
 *			depth to something reasonable.  The current depth
 *			of 50 is not within the ballpark considering the
 *			speed mismatch between the S32 uP and the Ethernet.
 *			Current observations show the queue depth peaking
 *			at 2 or 3.
 *
 *	84/09/17	Fix comments. Remove setjmp definitions (use label_t
 *			instead of jmp_buf).  Lock down databases during
 *			non-interrupt processing to prevent races.
 *
 *	84/09/11	Add cell ecDebug.  Use this instead of global 
 *			"debug" flags.  They didn't make any sense anyhow.
 *			Add ioctl functions that set and read the debugging
 *			flag values.
 *
 *	84/09/06	Removed code in ecoutput that forced the manufacturer's
 *			portion of destination Ethernet address to 01-07-00 if
 *			the bit 00-80-00 was lit in the board address portion.
 *			This was obviously a throwback to 4.1c and the 24bit
 *			addressing scheme (pre-ARP) when boards from more
 *			than one manufacturer would cause a problem.
 *
 *	84/09/05	Change distribution collection again. It was starting
 *			at 32 bytes per packet (it can't happen on Ethernet).
 *			Also add version number query (EC_IOCGETVERSION) so
 *			that the "ether" control program can check for a
 *			match with the 3Com driver on the system.
 *
 *	84/08/31	Change unit's flag variable to a bitmask of mode
 *			values.  Now each of NORMAL, PROMISCUOUS, DISTRIBUTION,
 *			and TRAILER can be independently enabled/disable/shown.
 *			Cleanup of ecoutput (assume that IP is the only
 *			protcol that would be silly enough to use the
 *			trailer protocol.
 *			Zero trailing padding added in ecput; disregard
 *			previous comments about inefficiency since zeroing
 *			at max would only be 63 bytes. This is swamped out
 *			by all the other processing of the packet.
 *
 *	84/08/30	Change from distribution from 100bytes per bucket
 *			to a binary scheme (64,128,256..).  This reduces the
 *			collection time overhead and allows more compact
 *			displays since the information in the century scheme
 *			was very sparse.
 *
 *	84/08/27	Fix backoff slot time calculation. Backoff retries
 *			happened too fast, too often.  The complete 16 tries
 *			were completing consistently in approx. 7 seconds.
 *			This violates the Ethernet spec!  Note that the
 *			3Com controller interprets the value 0, when written
 *			to MEBACK, as a very larger number, instead of a
 *			very small number.  It's not significant though.
 *
 *	84/08/23	Fix bug in ecintr; backoff overflows did not clear
 *			the jamming condition, instead it just tried to
 *			transmit again.  This lead to a infinite interrupt
 *			loop.  Now, when the backoff limit is exceeded, the
 *			controller is reset (procedure documented by 3Com).
 *			The order of interrupt processing has been changed
 *			to read all pending packets before processing jam
 *			interrupt.
 *
 *	84/08/09	Permanently enable statistics gathering. The cost
 *			is low, at it is always too late to turn it on
 *			to check for problems in the past.  Also move
 *			statistics information out of IP interface structure
 *			and into the driver/controller structure proper.
 *
 *	84/08/02	Remove RPC_COMPAT; all Ethernet addresses must now
 *			be 48 bits to work properly.  Remove NEW_MCAST and
 *			NEW_BCAST conditionals; just as for RPC_COMPAT the
 *			code is now permanently enabled.
 *
 *	84/08/01	Fix bug in ecRead that would declare a packet as
 *			using the trailer protocol when it was received
 *			with the type ETHERTYPE_TRAILER.  The type must be
 *			at least ETHERTYPE_TRAILER+1 (1001) to be a real
 *			trailer packet.
 *			Remove obscure return value (0, 1) from ecintr;
 *			it wasn't used for scheduling as claimed.
 *
 *	84/07/31	Merge in the address resolution protocol support
 *			from the if_ec.c driver in 4.2BSD.  Add flags to
 *			suppress TRAILER protocol (shown to be mariginal
 *			performance gain).
 *
 *	84/07/30	Clean up usage of ifnet structure.  On devices that
 *			support multiple logical interfaces (i.e., address
 *			families) build a common output queue that is NOT
 *			in the DARPA Internet's ifnet structure.
 *			The routine ecdummy is not needed, the init and reset
 *			routines in if.c check for a zero routine address
 *			before calling.  Zero the timer and watchdog cells
 *			in case garbage causes a slowtimeout vector to
 *			never-never land.
 *
 *	84/07/26	Clean up index usage. This allows multiple boards
 *			to work (if the layers above do).  Clean up interrupt
 *			processing so that it doesn't abort when more work
 *			is left to do.  Remove data cells that are never
 *			referenced.
 *
 * 	84/07/25	Add EC_IOCSETADDR (Set board address) and
 *			EC_IOCGETADDR (Get board address.  Change also
 *			/include/ecioctl.h and ecreg.h. Comment improvements.
 *
 */

/* Terminology:
 *
 * Controller	A single 3Com board and all associated hardware and
 *		firmware.
 *
 * Driver	The software module acting as the sole interface to
 *		the controller (i.e., this module).
 *
 * Unit		The software entity the driver maintains to keep history
 *		and state about a controller.  The mapping from units
 *		to controllers is fixed (i.e., the controller at ECBASE
 *		in Multibus space is unit 0; ECBASE + (N * ECSIZE) is
 *		unit N).
 *
 * Unit Index	The index into the set of software structures maintained
 *		by the driver.  Each "unit" refers to a specific controller.
 *		The unit index is identical to the minor device number
 *		when the controller is accessed through the character
 *		special device (/dev/enet10).
 *
 * Address	The Ethernet address that the controller responds to is
 *		called its "physical" address.  It also can pass through
 *		"logical" addresses of two forms: broadcast and multicast.
 *		Broadcast is a single fixed address (all ones) that every
 *		controller can listen to.  Multicast addresses enjoin a
 *		controller/driver in an exclusive club that listens and
 *		transmits the multicast address (limited broadcast).
 */

/*
 * Conditional Compilation flags (true if defined)
 *
 * INET		Compile in DARPA Internet support.
 * VALIDnet	Compile in VALID's homogeneous network support.
 * DECnet	Compile in DECnet support.
 * VALIDfwd	Compile in the poor man's routing support.
 * VALIDsink	Compile in the packet sink.
 * VALIDdisk	Compile in the virtual disk support.
 *
 * Obsolete:
 *
 * ARPresolve	Compile in IP Address Resolution Protocol (ARP) (always on).
 * VALID_RPC	Compile in VALID's RPC support (on if VALIDnet defined).
 * NEW_MCAST	Compile in multi-cast support (always on).
 * NEW_BCAST	Compile in broadcast support (always off).
 * RPC_COMPAT	Use only 24 bits of 48 bit Ethernet address (always off).
 * IP_TRAILER	Trailer generation controlled by mode flag (default off).
 * NO_ECSTATS	Always collect counter statistics.
 * ECPKTDIST	Packet distribution controlled by mode flag (default off).
 * DEBUG	Always compile in debugging/tracing code.
 */

/*
 * The following dependencies hold for conditional compilation:
 *
 * ALL		Must have INET (side effects all over).
 */

#include "ec.h"			/* Configuration information */

#include "../h/types.h"		/* Commonly used data types */
#include "../s32dev/ecreg.h"	/* 3Com 3C400 Multibus Controller defs */

#include "../machine/pte.h"	/* VM page table definitions */
#include "../h/param.h"		/* Process, memory and file sys parameters */
#include "../h/systm.h"		/* Kernel global variables */
#include "../h/mbuf.h"		/* Net/Comm buffer definitions */
#include "../h/socket.h"	/* Socket interface definitions */
#include "../h/vmmac.h"		/* Virtual memory address conversion macros */
#include "../h/map.h"		/* Resource allocation maps */
#include "../h/ioctl.h"		/* System wide IOCTL definitions */
#include "../h/dir.h"		/* File directory definitions */
#include "../h/user.h"		/* U Page definitions (per user blocks) */
				/*  ../h/user.h includes 
					../machine/pch.h
					../h/dmap.h
					../h/time.h
					../h/resource.h
					../h/errno.h
				 */
#include "../h/kernel.h"	/* Kernel variables */

#include "../net/if.h"		/* Network interface definitions */
#include "../net/netisr.h"	/* Network interrupt service definitions */
#include "../net/route.h"	/* Internet routing tables */
#include "../netinet/in.h"	/* Internet definitions */
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"	/* DARPA IP (Internet Protocol) definitions */
#include "../netinet/ip_var.h"	/* IP global variables */
#include "../netinet/if_ether.h"	/* Address resolution protocol */

#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

#ifdef DECnet
$include "../decnet/decnetCommon.h" /* General DECnet definitions */
#endif DECnet

#include "../machine/cpu.h"	/* Machine specific memory layout and sizing */
#include "../s32/vectors.h"	/* Processor interrupt vector definitions */
#include "../s32dev/mbvar.h"	/* Multibus definitions */


#define MaxPollTimes 10		/* Number of times to poll transmit complete */
				/*  before resorting to interrupts */

/* ECdebug - print debugging messages
 *
 * Synopsis:
 *	ECdebug(flags, args)
 *
 * Examples:
 *	ECdebug(DEBUG_All, ("Starting"));
 *	ECdebug(DEBUG_All, ("Device %d\n", minor(dev)));
 */

#define ECdebug(flags, args) \
    if (flags & ecDebug) \
	{ \
	printf ("ec: "); \
	printf args; \
	};


/* ECwarning - print warning messages
 *
 * Synopsis:
 *	ECwarning(args)
 *
 * Examples:
 *	ECwarning(("backoff limit exceeded\n");
 */

#define ECwarning(args) \
    { \
    printf ("ec: "); \
    printf args; \
    };


/* ECunitDebug - print debugging messages for a specific unit
 *
 * Synopsis:
 *	ECunitDebug(flags, args)
 *
 * Examples:
 *	ECunitDebug(DEBUG_Events, ("collision interrupt\n"));
 *
 * Note: This macro assumes that the variable "es" points to the
 *	 current unit's data base.
 */

#define ECunitDebug(flags, args) \
    if (flags & ecDebug) \
	{ \
	printf ("ec%d: ", es->unitIndex); \
	printf args; \
	};


/* ECunitWarning - print warning messages for a specific unit
 *
 * Synopsis:
 *	ECunitWarning(args)
 *
 * Examples:
 *	ECunitWarning(("backoff limit exceeded\n");
 *
 * Note: This macro assumes that the variable "es" points to the
 *	 current unit's data base.
 */

#define ECunitWarning(args) \
    { \
    printf ("ec%d: ", es->unitIndex); \
    printf args; \
    };


/* ECstat - Increment a statistic counter
 *
 * Synopsis:
 *	ECcounter(counter)
 *
 * Parameter Usage:
 *	counter	One of the elements in struct eccounters.
 *
 * Note:
 *	It is assumed that a cell called "es" points to the
 *	current unit's data base (ec_softc).
 *
 * Example:
 *	ECstat(xmtLoop);
 */

#ifndef NO_ECSTATS
#define ECcounter(counter) (es->counters.counter++)
#else
#define ECcounter(counter)
#endif


/* ecdataaddr - Build pointer to data in packet
 *
 * Synopsis:
 *	ecdataaddr(ec, off, type)
 *	struct ether_header ec;
 *	int off;
 *	int type;
 *
 * Parameter Usage:
 *	ec	Ethernet packet header
 *	off	Offset to start of packet data
 *	type	Type of pointer to create
 *
 * Example:
 *	resid = (*(ecdataaddr(ec, offset+2, u_short *));
 */

#define	ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off))))

/*********************/
/* MULTICAST SUPPORT */
/*********************/

typedef
    struct ec_mcast
	{
	struct ec_mcast * next;		/* Link to next multicast address */
	u_char addr[6];			/* Muticast address */
        }
    ec_mcast_t;

#define EC_MAXGROUPS	16


/* Optimized multicast hash (dumb compiler) */

#if	((EC_MAXGROUPS&(EC_MAXGROUPS-1)) == 0)
#define	EC_HASH(mcast)		(*(long *)&mcast[2] & (EC_MAXGROUPS-1))
#else
#define	EC_HASH(mcast)		(*(long *)&mcast[2] % EC_MAXGROUPS)
#endif


/********************************/
/* Forward routine declarations */
/********************************/

int ecOutput();			/* Output packet queueing */
int ecNetIoctl();		/* Network IOCTL processing */
int ecIntr();			/* Interrupt processing */
int ecProbe();			/* Controller existence probe */
int ecAttach();			/* Driver protocol setup and attach */
int ecInit();			/* Network interface initialization */
struct mbuf * ecGet ();		/* Copy packet from 3Com buffer */

#if (NEC==1)
#define ECINFO(n) ecinfo
#define EC_SOFTC(n) ec_softc
#else (NEC==1)
#define ECINFO(n) ecinfo[n]
#define EC_SOFTC(n) ec_softc[n]
#endif (NEC==1)

/*
 * struct ec_softc - Per controller data structure.
 *
 * Each interface is referenced by a network interface structure
 * which the routing code uses to locate the interface.
 * This structure (ec_softc) contains the information maintained by
 * the driver (this module) about the controller.
 */

struct ec_softc {
    struct arpcom es_ac;	/* Common Ethernet structures */

    int unitIndex;		/* Unit index */
    int boardPresent;		/* != 0, Board is present */
    char * physicalAddr;	/* Controller physical address */
    char * virtualAddr;		/* Controller virtual address */

    int mode;			/* Current board/driver mode */

    int outputActive;		/* != 0, output is active */
    u_short backoffMask;	/* Mask for current output delay */
    struct ifqueue output;	/* Output packet queue */

#ifdef VALIDnet
    struct ifnet if_conn;	/* VALID CONNection manager interface */
    struct ifnet if_rpc;	/* VALID RPC interface */
#ifdef notdef
    struct ifnet if_bulk;	/* VALID bulk data transfer interface */
#endif notdef
#endif

#ifdef DECnet
    struct ifnet if_decnet;	/* DECnet interface */
#endif

#ifdef FUSION
    struct ifnet if_fusion;	/* FUSION interface */
#endif

    int multiCastCount;		/* Number of multicast filters enable */
    ec_mcast_t * mcastBuckets [EC_MAXGROUPS]; /* Multicast hash table */

/* Statistics data base */

    struct ec_counters counters;	/* Driver/Board statistics */
    struct ec_pktdist rcvPktDist;	/* Receive packet distribution */
    struct ec_pktdist xmtPktDist;	/* Transmit packet distribution */
};

#define if_ip es_ac.ac_if
#define etherAddr es_ac.ac_enaddr

/*******************/
/* Owned variables */
/*******************/

u_long ecDebug = 0;		/* Debugging/tracing flags for this module */
struct ec_softc EC_SOFTC(NEC);	/* Per controller data base */
ec_mcast_t * mcast_free = NULL;	/* Multicast entry free list */

u_short ecstd[] = {0};
struct mb_device *ECINFO(NEC);
struct mb_driver ecdriver =
    {
    0,		/* No bus reset routine */
    ecProbe,	/* Controller existence test routine */
    0,		/* No slave controllers */
    ecAttach,	/* Protocol attach processing */
    ecstd,	/* No standard CSR addresses */
    "ec",	/* Device name */
    &ECINFO(0),	/* Pointer to device structures */
    0,		/* No controller name */
    0		/* No controller structures */
    };


/**********************/
/* External variables */
/**********************/

extern u_char etherbroadcastaddr[];	/* Ethernet broadcast address */
extern struct ifnet loif;	/* Loop back network interface */
extern int ifqmaxlen;		/* Max interface queue length (../net/if.c) */
extern int cvec;		/* Autoconfigure interrupt vector value */

ecProbe(addr, intr, unitIndex)
    register caddr_t addr;
    int (* intr)();
    int unitIndex;
{
    register struct ec_softc *es;	/* Current controller data base */
    register int dummy;			/* Scratch cell */
    register int pageIndex;		/* Index for mapping ctlr pages */

/* Point to controller structure and calculate controller physical address */

    es = &EC_SOFTC(unitIndex);
    es->unitIndex = unitIndex;
    es->physicalAddr = addr;
    es->boardPresent = 0;
    es->multiCastCount = 0;

/* Use scratch page; access controller to test existence */

#ifdef M68020
   /*
    * Since the 3COM board resides in multibus memory
    * and does not go through the multibus to alter
    * that memory, we must NOT cache
    * the contents of those addresses for the 68020.
    */
    if (chipType==CHIPTYPE_68020)
    	setpagemap(SSEG1_VA>>pageshift,
		V_NONCACHED | (int)es->physicalAddr >> pageshift);
    else
    	setpagemap(SSEG1_VA>>pageshift,
		mbmem | (int)es->physicalAddr >> pageshift);
#else M68020
    setpagemap(SSEG1_VA>>pageshift, mbmem | (int)es->physicalAddr >> pageshift);
#endif M68020
    dummy = *(short*)SSEG1_VA;
    es->boardPresent = 1;

/* Allocate and map virtual space for controller, if not already done */

    if (es->virtualAddr == 0)
	{
	segacc(ALLCTXT, systop - ECPAGES, ECPAGES, SEG_ASYS);
	for (pageIndex = ECPAGES; --pageIndex >= 0; )
#ifdef M68020
	  if (chipType==CHIPTYPE_68020)
	    setpagemap(-- systop,
		       V_NONCACHED | (pageIndex + ((int)es->physicalAddr >> pageshift)));
	  else
	    setpagemap(-- systop,
		       mbmem | (pageIndex + ((int)es->physicalAddr >> pageshift)));
#else M68020
	    setpagemap(-- systop,
		       mbmem | (pageIndex + ((int)es->physicalAddr >> pageshift)));
#endif M68020
	es->virtualAddr = (char*) (systop << pageshift);
	}

/*****************************/
/*???????????????????????????*/
/* Hardcode interrupt vector */
/*???????????????????????????*/
/*****************************/

    cvec = 5 - 1;

/* Indicate success in probing for board */

    return(1);
}

/*
 * Compress this routine with a subroutine ?
 */

ecAttach(mb)
    struct mb_device *mb;
{
    register int unitIndex;
    register struct ec_softc * es;
    register struct ifnet * ifp;
    register struct sockaddr *sock;

/* */
    unitIndex = mb->md_unit;
    es = &EC_SOFTC(unitIndex);
    ECINFO(unitIndex) = mb;

/* Reset controller so ROM can be read */

    MECSR(es) = RESET;
    DELAY(10);

/* Read Ethernet address from ROM  */

    bcopy(MEAROM(es), es->etherAddr, 6);

/* Announce the controller's addresses */ 

    printf("ec%d: Virtual address %x  ", unitIndex,
	    ((int)es->virtualAddr) & 0xFFFFFF);
    printf("Ethernet address: %02x-%02x-%02x-%02x-%02x-%02x\n",
		es->etherAddr[0], es->etherAddr[1], es->etherAddr[2],
		es->etherAddr[3], es->etherAddr[4], es->etherAddr[5]);

#ifdef INET
/* Fill and attach the DARPA Internet (IP) network interface data base */
	
    ifp = &es->if_ip;
    ifp->if_unit = unitIndex;
    ifp->if_name = "ec";
    ifp->if_mtu = ETHERMTU;
    ifp->if_flags = 0;
    ifp->if_output = ecOutput;
    ifp->if_init = ecInit;
    ifp->if_reset = 0;
    ifp->if_ioctl = ecNetIoctl;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_INET;
    sock = &ifp->if_broadaddr;
    sock->sa_family = AF_INET;
    if_attach(ifp);
#endif INET

#ifdef VALIDnet
/* Fill and attach the CONNection Manager network interface data base */

    ifp = &es->if_conn;
    ifp->if_unit = unitIndex;
    ifp->if_name = "ec-conn";
    ifp->if_mtu = ETHERMTU;
    ifp->if_output = ecOutput;
    ifp->if_init = 0;
    ifp->if_reset = 0;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_CONN;
    sock = &ifp->if_broadaddr;
    sock->sa_family = AF_CONN;
    if_attach(ifp);

/* Fill and attach the Remote Procedure Call (RPC) net intf data base */

    ifp = &es->if_rpc;
    ifp->if_unit = unitIndex;
    ifp->if_name = "ec-rpc";
    ifp->if_mtu = ETHERMTU;
    ifp->if_output = ecOutput;
    ifp->if_init = 0;
    ifp->if_reset = 0;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_RPC;
    if_attach(ifp);

#ifdef notdef
/* Fill and attach the Bulk Data Transfer (RPC) net intf data base */

    ifp = &es->if_bulk;
    ifp->if_unit = unitIndex;

    ifp->if_name = "ec-bulk";
    ifp->if_mtu = ETHERMTU;
    ifp->if_output = ecOutput;
    ifp->if_init = 0;
    ifp->if_reset = 0;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_BULK;
    if_attach(ifp);
#endif notdef

/* Set the interface addresses for VALID's BCAST , CONN, and RPC*/
    {
    struct sockaddr_rpc *srpc;
    node_t n, broad;

    n.net = 0;			/* !!! First Order, SIDE EFFECT !!! */
    ea2long(&es->etherAddr[0], n.host.high);
    ea2long(&es->etherAddr[3], n.host.low);
    broad.net = 0;
    ea2long(&etherbroadcastaddr[0], broad.host.high);
    ea2long(&etherbroadcastaddr[3], broad.host.low);

    ifp = &es->if_conn;
    ifp->if_host[0] = n.host.low & 0xFFFFFF;
    ifp->if_net = 0;
    srpc = (struct sockaddr_rpc *)&ifp->if_addr;
    srpc->node = n;
    srpc->addr_family = AF_CONN;
    srpc = (struct sockaddr_rpc *)&ifp->if_broadaddr;
    srpc->node = broad;
    srpc->addr_family = AF_CONN;

    ifp = &es->if_rpc;
    ifp->if_host[0] = n.host.low & 0xFFFFFF;
    ifp->if_net = 0;
    srpc = (struct sockaddr_rpc *)&ifp->if_addr;
    srpc->node = n;
    srpc->addr_family = AF_RPC;
    srpc = (struct sockaddr_rpc *)&ifp->if_broadaddr;
    srpc->node = broad;
    srpc->addr_family = AF_RPC;

#ifdef notdef
    ifp = &es->if_bulk;
    ifp->if_host[0] = n.host.low & 0xFFFFFF;
    ifp->if_net = 0;
    srpc = (struct sockaddr_rpc *)&ifp->if_addr;
    srpc->node = n;
    srpc->addr_family = AF_BULK;
    srpc = (struct sockaddr_rpc *)&ifp->if_broadaddr;
    srpc->node = broad;
    srpc->addr_family = AF_BULK;
#endif notdef
    }
#endif VALIDnet
    
#ifdef DECnet
/* Fill and attach the DECnet network interface data base */

    ifp = &es->if_decnet;
    ifp->if_unit = unitIndex;

    ifp->if_name = "ec-decnet";
    ifp->if_mtu = ETHERMTU;
    ifp->if_output = ecOutput;
    ifp->if_init = 0;
    ifp->if_reset = 0;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_DECnet;
    if_attach(ifp);
#endif DECnet

#ifdef FUSION
/* Fill and attach the FUSION/XNS network interface data base */

    ifp = &es->if_fusion;
    ifp->if_unit = unitIndex;

    ifp->if_name = "ec-fusion";
    ifp->if_mtu = ETHERMTU;
    ifp->if_output = ecOutput;
    ifp->if_init = 0;
    ifp->if_reset = 0;
    ifp->if_timer = 0;
    ifp->if_watchdog = 0;

    sock = &ifp->if_addr;
    sock->sa_family = AF_NS;
    if_attach(ifp);
#endif FUSION

    arpattach(&es->es_ac);
}

/*
 * Called from if_init, ecNetIoctl.
 *
 * Discard all pending output packets?
 * Is unitIndex correct for call from if_init?
 * If probe fails, will this routine be called through ecNetIoctl?
 * Where is IFF_RUNNING set?
 * Should IFF_RUNNING/UP be checked on input and output processing?
 */

ecInit(unitIndex)
    register int unitIndex;
{
    register int status;		/* Controller status */
    register int pageIndex;		/* Index for cntlr virtual pages */
    register struct ec_softc *es;	/* Current controller data base */
    register struct ifnet *ifp;		/* Current interface data base */
    register struct sockaddr_in * sin;	/* Socketaddr, internet type for ARP */

    es = &EC_SOFTC(unitIndex);

/* Reset controller to stop spurious interrupts */

    MECSR(es) = RESET;
    DELAY(10);

/* If internet address has not been configured, don't complete init */

    ifp = &es->if_ip;
    sin = (struct sockaddr_in *)&ifp->if_addr;
    if (sin->sin_addr.s_addr == 0)
	return(0);

/* If interface is already running, just change address */

    if (ifp->if_flags & IFF_RUNNING)
	goto justarp;

/* Enable normal packet processing; no network monitoring; no trailer */

    es->mode = EC_NORMAL;
    ecResetBoard(es);

/* Set the initial start time for statistics gathering.
 * This is futile since the system clock hasn't been started up
 * yet, but it insures that it has been initialized(?).
 */
    ecCountersClear(es);
    ecDistClear(es);

/* Set the maximum output queue depth.  */

    es->output.ifq_maxlen = EC_MAXQLEN;

/* Flag the interface as up and supporting broadcasts. */

    es->if_ip.if_flags |= IFF_UP | IFF_BROADCAST;

/* Declare interface to router; check with ARP for address conflict */

justarp:
    if_rtinit(&es->if_ip, RTF_UP);
    arpwhohas(&es->es_ac, &sin->sin_addr);


}

ecSetInetAddr(ifp, sin)
    register struct ifnet *ifp;
    register struct sockaddr_in *sin;
{
/* Record the Internet address */

    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_addr;
    sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]);
    sin = (struct sockaddr_in *)&ifp->if_broadaddr;
    sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
}

/* ecResetBoard - reset a 3Com controller board
 *
 * Synopsis:
 *	ecResetBoard(&es);
 *	struct ec_softc es;
 *
 * Parameter Usage:
 *	es	Controller (unit) data base maintained for the board.
 *
 * Description:
 *	This routine resets and conditions a 3Com board.  The
 *	board address is set, interrupts enabled, and software
 *	flags initialized.
 */

ecResetBoard(es)
    struct ec_softc *es;
{
/* Reset controller; delay to let it recover */

    MECSR(es) = RESET;
    DELAY(10);

/* Force board to desired address and enable it */

    bcopy(&es->etherAddr[0], MEARAM(es), 6);
    MECSR(es) |= AMSW;

/*
 * For promiscuous mode, enable for all packets.
 * For normal (not promiscuous), enable just packets for this node plus
 *  multicast (if any filters) or broadcast (if no filters).
 * Ignore errors in all cases.
 */

    if (es->mode & EC_PROMISCUOUS)
	MECSR(es) = (MECSR(es) & ~PA) | (EC_PKTALL + EC_NOERRORS);
    else
	if (es->multiCastCount)
	    MECSR(es) = (MECSR(es)&~PA)|(EC_MINEMULTI+EC_NOERRORS);
	else
	    MECSR(es) = (MECSR(es)&~PA)|(EC_MINEBROAD+EC_NOERRORS);

/* Declare that output cannot be active (it was just killed) */

    es->outputActive = 0;

/* Enable interrupts for receive buffers */

    MECSR(es) |= ABSW | AINT | BBSW | BINT;
}

/* ecIntr - 3Com interrupt service routine
 *
 * Description:
 *	This routine polls the 3Com board to check for all events
 *	that caused the interrupt to occur.  Interrupts serviced are
 *	those for input packets ready, transmit completions, and
 *	transmit failures (jams).
 *
 *	Jams cause a retransmit with a backoff to occur.  Each
 *	retransmit attempt for a single packet are remembered.  After
 *	16 attempts, the packet is discarded.
 *
 *	Input packets ready cause the on board buffers processed
 *	by the ecRead routine.  The order of reading the buffers
 *	is determined by checking the ordering flag (RBBA) in the
 *	status register.
 *
 *	Transmit completions cause the next queued packet (if any)
 *	to be started.
 */

ecIntr(unitIndex)
    int unitIndex;
{
    register struct ec_softc *es;	/* Current controller data base */
    register u_short status;		/* Controller status */
    register u_short intrReason;	/* Interrupt reason flags */
    register int bufferSelect;		/* Flags for buffers with data */

/* Get controller status */

    es = &EC_SOFTC(unitIndex);
    status = MECSR(es);

/*
 * Mask out interrupt reasons with the interrupt mask to see if the
 * 3Com controller is the reason for the interrupt.  If not, just return.
 * The following statement makes all interrupt reason flags
 * a "1" if true instead of some as "0" when true and some as "1" when true.
 */

    intrReason = ((status & INTENABLS)<<8) & ((~status & ~JAM) | (status & JAM));
    if(!intrReason)
	return(0);

    ECcounter(intrTotal);

/*
 * Receive packet processing.  Check for packets in either the
 * A or B on board buffer.  Read the oldest packet first.
 */

    bufferSelect = (intrReason & (ABSW | BBSW)) | (status & RBBA);
    switch(bufferSelect)
	{
	case ABSW | BBSW:
	    ECcounter(intrAB);
	    ecReadAbuffer(es);
	    ecReadBbuffer(es);
	    break;

	case ABSW | BBSW | RBBA:
	    ECcounter(intrBA);
	    ecReadBbuffer(es);
	    ecReadAbuffer(es);
	    break;

	case ABSW:
	case ABSW | RBBA:
	    ECcounter(intrAonly);
	    ecReadAbuffer(es);
	    break;

	case BBSW:
	case BBSW | RBBA:
	    ECcounter(intrBonly);
	    ecReadBbuffer(es);
	    break;
	}

/* Process transmission collisions */

    if (intrReason & JAM)
	{
	ECunitDebug(DEBUG_Jams, ("jam (collision) interrupt\n"));
	ECcounter(xmtCollisions);

    /* Check if output is active, then start backoff */
    /* If output is not active, ignore the interrupt */

	if (es->outputActive)
	    {
	    if (es->backoffMask == 0)
		{
	    /*
	     * Backoff limit of 16 times has been reached.
	     * Reset and ready for the next packet (if any).
	     */
		ECcounter(xmtBusy);
		ECunitWarning(("backoff limit exceeded; controller was reset\n"));
		ECunitWarning(("check transceiver cable and Ether cable terminators\n"));
		ecResetBoard(es);
		}
	    else
		{

	    /*
	     * Set backoff period and retransmit packet.
	     * Number of slot times for nth retry "is equal to
	     * a uniformly distributed random integer r in the range
	     * 0<=r<2^k, where k=min(n,10)".  This means that the
	     * number of slot times must fit in 9 bits.
	     */

		register u_short rand;

		rand = (u_short) (time.tv_usec >> 4) & 0x1ff;
		es->backoffMask <<= 1;
		ECunitDebug(DEBUG_Jams, ("backoff random %x, mask %x, ack %x\n",
			    rand, es->backoffMask,
			    -(rand & ~es->backoffMask)));
		MEBACK(es) = -(rand & ~es->backoffMask);
		MECSR(es) = MECSR(es) & (INTENABLS|PA) | JAM;
		}

	    ecStart(es);
	    }
	else
	    {
	    ECunitWarning(("spurious JAM interrupt; controller was reset\n"));
	    ECunitWarning(("check transceiver cable\n"));
	    ecResetBoard(es);
	    };
	}

/*
 * Transmit packet completion.  If another packet is waiting
 * to be transmitted, start it.
 */

    if ((intrReason & TBSW) && (es->outputActive))
	{
	ECunitDebug(DEBUG_XmtEvent, ("transmit complete\n"));
	ECcounter(xmtSent);

	es->outputActive = 0;
	MECSR(es) = (MECSR(es) & ~(TINT | JINT)) & (INTENABLS|PA);
	ecStart(es);

	}

/* Indicate interrupt was fielded */

    return(1);
}

/* ecReadAbuffer - read data from 3Com buffer A
 *
 * Synopsis:
 *	ecReadAbuffer(&es);
 *	struct ec_softc es;
 *
 * Parameter Usage:
 *	es	Controller data structure maintained by driver
 *
 * Description:
 *	This routine reads a packet from the on board buffer A.
 *	After the read is completed, the buffer is given back to
 *	the controller to use for another packet.
 */

static ecReadAbuffer(es)
    register struct ec_softc *es;
{
    ecRead(es,MEAHDR(es));
    MECSR(es) = MECSR(es) & (INTENABLS|PA) | ABSW;
}


/* ecReadBbuffer - read data from 3Com buffer B
 *
 * See description for ecReadAbuffer
 */

static ecReadBbuffer(es)
    register struct ec_softc *es;
{
    ecRead(es,MEBHDR(es));
    MECSR(es) = MECSR(es) & (INTENABLS|PA) | BBSW;
}

/* ecRead - Read a packet from a 3Com on board buffer
 *
 * Synopsis:
 *	ecRead(&es, &pktbuf);
 *	struct ec_softc es;
 *	u_short pktbuf;
 *
 * Parameter Usage:
 *	es	Controller data structure maintain by driver
 *	pktbuf	On board packet buffer.
 *
 * Description:
 *	This routine reads a packet from the specified buffer.
 *	It puts the packet into a sequence of communications buffers
 *	(mbufs).  After scattering the packet into the mbufs, the
 *	packet is queued to the appropriate protocol layer determined
 *	by the packet type.  The protocol layer is signalled that
 *	a packet is ready to be processed.
 *
 *	The 3Com board uses the first 16 bits in the receive buffer to hold
 *	receive status information and the offset in the buffer to the data.
 *
 *	This routine detects the local net trailer protocol. The offset
 *	to the trailing header and the actual type and packet size values
 *	are calculated before calling ecget to unravel the packet from the
 *	3Com buffer.
 *
 *	Note that packets that are encoded in the TRAILER protocol and are
 *	not for this node are not accounted for properly (incorrect 
 *	Ethertype will be used) when monitoring the network in promiscuous
 *	mode.  The tradeoffs were not in the TRAILER protocol's favor 
 *	when it came to speed versus utility. The TRAILER protocol isn't
 *	so hot anyhow.
 */

ecRead(es, pktbuf)
    register struct ec_softc *es;
    register u_short *pktbuf;
{
    register struct ether_header *ec;	/* Ethernet packet header */
    struct mbuf *m;			/* Head of mbuf chain with pkt data */
    int length;				/* Length of data in packet */
    int offset;				/* Offset in buffer of data */
    int residual;			/* Size of trailing header info */

    ECcounter(rcvTotal);

/*
 * Discard packets with errors.  The controller is not enabled to pass
 * these through; test is here just to improve robustnest of driver.
 */

    if (*pktbuf & R_ERROR)
	{
	ECcounter(rcvError);
	return;
	}

/*
 * Get length of data in packet.  Another check, just incase,
 * for undersize range error (should be done on board).
 * Point to Ethernet header in the on-board buffer.
 */

    length = (*pktbuf & R_OFFSET) - sizeof(struct ether_header) 
	      - sizeof(*pktbuf) - sizeof(long);

    if (length < ETHERMIN)
	{
	ECcounter(rcvTooSmall);
	goto CollectRcvStats;
	}

    ec = (struct ether_header *)(pktbuf + 1);


/* Dump packet source and destination */

    if (DEBUG_RcvEvent & ecDebug)
	iprint("rcv", ec, es->unitIndex);


/*
 * Filter multicast packets.  This filter really should be
 * implemented in hardware.  If the destination address in the
 * header matches one in the list of multicast addresses for the
 * controller, process the packet; otherwise discard it.
 */

/* Check receive buffer header fields for a multi-cast packet */

    if (ec->destAddr[0] & 0x01)
	if (*pktbuf & R_BCAST)
	    {
	    ec_mcast_t * mc;	/* Bucket for rec'd dest addr set */
	    char mcast[6];	/* Copy of dest address (for speed) */

	/* Copy into local memory (faster than 3Com Multibus access) */

	    bcopy(&ec->destAddr[0], mcast, 6);

	/* Search bucket for a match; if none, discard packet */

	    for (mc = es->mcastBuckets[EC_HASH(mcast)];
		 mc;
		 mc = mc->next)
		if (bcmp(mcast, mc->addr, 6) == 0)
		    {
		    ECcounter(rcvMcast);
		    goto GoodAddress;
		    };

	    ECcounter(rcvNoMcast);
	    goto CollectRcvStats;
	    }
	else
	    ECcounter(rcvBcast);
    else
	if (bcmp(&es->etherAddr[0], &ec->destAddr[0], 6) == 0)
	    ECcounter(rcvMine);
	else
	    goto CollectRcvStats;
	
GoodAddress:

#ifdef FUSION
    if (ec->etherType == ETHERTYPE_XNS)
	{
	fu_pktinput(ec, length + sizeof(struct ether_header));
	return(0);
	}
#endif FUSION

/*
 * 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 offset.
 */

    if (ec->etherType > ETHERTYPE_TRAILER &&
	ec->etherType < ETHERTYPE_TRAILER + NUM_TRAILER)
	{
	ECcounter(rcvTrailer);

    /* Calculate offset to trailing header */

	offset = (ec->etherType - ETHERTYPE_TRAILER) * 512;

    /*
     * Defend against pointing beyond end of
     * maximum size of Ethernet packet.
     */

	if (offset >= ETHERMTU)
	    {
	    ECcounter(rcvBadTrailer);
	    goto CollectRcvStats;
	    }

    /* Get real packet type from trailing header */

	ec->etherType = *ecdataaddr(ec, offset, u_short *);

    /* Get size of trailing header */

	residual = *(ecdataaddr(ec, offset + 2, u_short *));

    /*
     * Defend against data and trailing header length
     * greater than the received packet.
     */

	if (offset + residual > length)
	    {
	    ECcounter(rcvBadTrailer);
	    goto CollectRcvStats;
	    }

	length = offset + residual;
	}
    else

    /* Non-trailer, header starts at beginning of packet */

	offset = 0;

/* Discard empty packets */

    if (length == 0)
	{
	ECcounter(rcvNull);
	return;
	}

/*
 * Pull packet from controller.  Offset 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 real type and length which are at the front of any trailer data.
 */

    if (es->mode & EC_NORMAL)
	{
    
    /* Pull packet; drop packet on resource failures */

	if ((m = ecGet(pktbuf, length, offset)) == 0)
	    {
	    ECcounter(rcvNoSpace);
	    goto CollectRcvStats;
	    }

    /* Skip past real type and length from trailing header */

	if (offset)
	    {
	    m->m_off += 2 * sizeof (u_short);
	    m->m_len -= 2 * sizeof (u_short);
	    }

    /* Queue to protocol family based on packet type */

	switch (ec->etherType)
	    {
#ifdef INET
	    case ETHERTYPE_IP:
		if (IF_QFULL(&ipintrq))
		    {
		    IF_DROP(&ipintrq);
		    m_freem(m);
		    ECcounter(rcvQueueFull);
		    }
		else
		    {
		    IF_ENQUEUE(&ipintrq, m);
		    ECcounter(rcvOk);
		    }
		schednetisr(NETISR_IP);
		break;

	    case ETHERTYPE_ARP:
		arpinput(&es->es_ac,m);
		break;
#endif INET

#ifdef VALIDnet
	    case ETHERTYPE_VALID:
		{
		struct validDLLheader * vhdr;
		vhdr = mtod(m, struct validDLLheader *);
		if (vhdr -> reserved != 0)
		    {
		    ECcounter(rcvBadValid);
		    m_freem(m);
		    break;
		    }

		m->m_off += sizeof(struct validDLLheader);
		m->m_len -= sizeof(struct validDLLheader);

		switch (vhdr -> pktType)
		    {
		    case VALIDnet_CONN:
			if (IF_QFULL(&conn_intrq))
			    {
			    IF_DROP(&conn_intrq);
			    m_freem(m);
			    ECcounter(rcvQueueFull);
			    }
			else
			    {
			    IF_ENQUEUE(&conn_intrq, m);
			    ECcounter(rcvOk);
			    }
			schednetisr(NETISR_CONN);
			break;

		    case VALIDnet_RPC:
			rpc_input(m);
			ECcounter(rcvOk);
			break;

#ifdef notedf
		    case VALIDnet_BULK:
			if (IF_QFULL(&bulk_intrq))
			    {
			    IF_DROP(&bulk_intrq);
			    m_freem(m);
			    ECcounter(rcvQueueFull);
			    }
			else
			    {
			    IF_ENQUEUE(&bulk_intrq, m);
			    ECcounter(rcvOk);
			    }
			schednetisr(NETISR_BULK);
			break;
#endif notdef

		    default:
			ECcounter(rcvBadValid);
			m_freem(m);
		    }
		}
		break;
#endif VALIDnet

#ifdef DECnet
	    case ETHERTYPE_DECNET:
		if (IF_QFULL(&decnet_intrq))
		    {
		    IF_DROP(&decnet_intrq);
		    m_freem(m);
		    ECcounter(rcvQueueFull);
		    }
		else
		    {
		    IF_ENQUEUE(&decnet_intrq, m);
		    ECcounter(rcvOk);
		    }
		schednetisr(NETISR_DECnet);
		break;
#endif DECnet

	    default:
	/**************************************************************/
	/*********** PUT HOOK PACKET CATCHALL RECEIVER HERE ***********/
	/**************************************************************/
		ECcounter(rcvBadType);
		m_freem(m);
		break;
	    }
	} 


/* Collect packet size statistics on a per Ethertype basis */

CollectRcvStats:

    if (es->mode & EC_DISTRIBUTION)
	ecDistIncr(&es->rcvPktDist, ec->etherType, length);

}

/*
 * ecGet - Copy from 3Com board buffer into mbufs
 *
 * Synopsis:
 *	m = (struct mbuf *) ecGet(&ecbuf, totlen, offset);
 *	u_char ecbuf[totlen];
 *	int totlen;
 *	int offset;
 *	struct mbuf * m;
 *
 * Parameter Usage:
 *	ecbuf	3Com on board buffer
 *	totlen	Number of bytes in packet (including header)
 *	offset	Offset to beginning of data in buffer; a non-zero
 *		  offset implies the trailer protocol is being used.
 *	m	Return chain of mbufs
 *
 * Description:
 *	This routine copies a packet from one of the 3Com on board
 *	buffers into a chain of mbufs (communication buffers).
 *	If the Ethernet packet is in the local net trailer protocol
 *	form, it is normalized to a sequential form when copying to
 *	mbufs.
 *
 * WARNING:
 *	This makes the fairly safe assumption that mbufs have even lengths.
 */

struct mbuf *
ecGet(ecbuf, totlen, off0)
    u_char *ecbuf;
    int totlen;
    int off0;
{
    register struct mbuf *m;	/* Current mbuf being filled */
    struct mbuf *top = 0;	/* First mbuf in chain to be returned */
    struct mbuf **mp = &top;	/* Current pointer into mbuf chain */
    register int off = off0;	/* Current offset into packet buffer */
    register int len;		/* Length of current section (data, trailer) */
    u_char *cp;			/* Byte pointer into packet buffer */
    u_char *mcp;		/* Byte pointer to current mbuf data */
    register int words;		/* Words to copy from packet buffer */

/*
 * Point to the start of the data, taking into account the 3Com status
 * word and the Ethernet packet header.
 */

    cp = ecbuf + sizeof(u_short) + sizeof(struct ether_header);

/* Copy until all bytes from the packet (trailer and data) are copied */

    while (totlen > 0)
	{

    /*
     * Get a mbuf immediately; failures are not tolerated and
     * result in a total copy abort.
     */
	MGET(m, M_DONTWAIT, MT_DATA);
	if (m == 0)
	    {
	    m_freem(top);
	    return (0);
	    }

    /*
     * If the data for the current mbuf does not start at the
     * beginning of the packet buffer, adjust the length to copy
     * by the offset and the byte pointer to the offset position.
     * Otherwise, try to copy the whole packet.
     */
	if (off)
	    {
	    len = totlen - off;
	    cp += off;
	    }
	else
	    len = totlen;

    /*
     * If the data is larger than a cluster, allocate a cluster and
     * setup to copy as much as possible into it.  This will reduce the
     * number of mbufs needed.  The packet must be either larger than
     * a cluster or larger than four (4) times the mbuf data size.
     * If a cluster is not possible (size or availability) setup to copy
     * as many bytes as possible (or left) into the current mbuf.
     */


/*	if (len >= CLBYTES) */
/*	if (len >= min(CLBYTES, MLEN << 2))
	    {
	    struct mbuf *p;

	    MCLGET(p, 1);
	    if (p != 0)
		{
		m->m_len = len = CLBYTES;
		m->m_off = (int)p - (int)m;
		}
	    else
		{
		m->m_len = len = MIN(MLEN, len);
		m->m_off = MMINOFF;
	        }
	    }
	else
*/
	    {
	    m->m_len = len = MIN(MLEN, len);
	    m->m_off = MMINOFF;
	    }

    /*
     * Point to the destination in the current mbuf.
     * This may be in a cluster linked to the mbuf.
     */ 

	mcp = mtod(m, u_char *);

	bcopy(cp, mcp, len);
	cp += len;
   
    /* Link the mbuf into the chain to be returned */

	*mp = m;
	mp = &m->m_next;

    /*
     * If working from front of buffer (i.e., not trailer), adjust
     * the length left to copy and continue copying
     */

	if (off == 0)
	    {
	    totlen -= len;
	    continue;
	    }

    /*
     * Part of a trailing header has just been copied; if
     * it is not done, continue copying it.  If it is done,
     * reset the lengths and pointers to the data starting
     * at the beginning of the packet.
     */

	off += len;
	if (off == totlen)
	    {
	    cp = ecbuf + sizeof(u_short) + sizeof(struct ether_header);
	    off = 0;
	    totlen = off0;
	    }
	}

/* Return the first mbuf in the chain of mbufs containing the packet */

    return (top);
}

/*
 * ecOutput - Queue a packet to go out on the Ethernet
 *
 * Synopsis:
 *	result = ecOutput(&ifp, &mhdr, &dst);
 *	struct ifnet ifp;
 *	struct mbuf mhdr;
 *	struct sockaddr dst;
 *	int result;
 *
 * Parameter Usage:
 *	ifp	Interface structure for specific controller/address
 *		 family pair.
 *	mhdr	Head of mbuf chain containing packet.
 *	dst	Destination socket address (family specific).
 *	result	== 0, Success (packet queued or discarded).
 *		!= 0, Failure code (packet not queued; mbufs discarded).
 *
 * Description:
 *	This routine encapsulates a packet for transmission on the
 *	Ethernet.  The encapsulation is family specific.  The interface
 *	structure passed to this routine is one that was created during
 *	the initialization procedure (see ecinit).  Each structure is
 *	unique to a protocol family being supported by a controller.
 *	The interface structure is linked (by this module) to the controller
 *	structure (ec_softc) by the interface unit number (if_unit).
 *
 *	For DARPA Internet, the trailer local net encapsulation is used
 *	if enough data in the first packet leaves a multiple of 512
 *	bytes in the remainder.  Also the address resolution protocol
 *	support is invoked to convert the 24 bit Internet address to the
 *	Ethernet 48 bit address.
 *
 *	If the destination is this node or broadcast, the packet is
 *	sent to the loop device.  In the broadcast case this must be done
 *	since the 3Com interface cannot receive broadcast packets that
 *	it transmits.  The local loop for packets destined to this
 *	node are also looped for efficiency reasons.
 */

ecOutput(ifp, m0, dst)
    struct ifnet *ifp;
    struct mbuf *m0;
    struct sockaddr *dst;
{
    int type;				/* Ethernet packet type */
    int s;				/* Saved process interrupt level */
    int error = 0;			/* Return error code */
    register struct ec_softc *es;	/* Controller data base */
    register struct mbuf *m;		/* Current mbuf in chain */
    register struct ether_header *ec;	/* Ethernet packet header */
    struct mbuf *mcopy;			/* Copy of packet for looping */
    u_char dest[6];			/* Full 48 bit Ethernet dest address */
    int dllHeaderSize = 0;		/* Size of DLL header (0 => none) */
    struct validDLLheader dllHeader;	/* DLL header */
    struct in_addr idst;		/* Internet destination, to ARP */

    dllHeader.reserved = 0;

/* Select the controller data base */

    es = &EC_SOFTC(ifp->if_unit);

/* Point to head of packet chain; declare no looped packet needed yet */

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

/*
 * If normal processing is disabled; discard the packet.
 */

    if (!(es->mode & EC_NORMAL))
	goto bad;

/*
 * Encapsulate packet based on protocol family.
 * This includes defining the destination address and
 * reformatting the data if neccessary.  If the destination
 * is this node (or broadcast), a copy of the packet is
 * internally looped back.
 */

    switch (dst->sa_family)
	{

#ifdef INET
	case AF_INET:
	    idst = ((struct sockaddr_in *)dst)->sin_addr;
	    if (!arpresolve(&es->es_ac, m, &idst, dest))
		return(0);

	    if (in_lnaof(idst) == INADDR_ANY)
		mcopy = m_copy(m, 0, (int)M_COPYALL);

	    if (es->mode & EC_TRAILER)
		{
		register int off;
		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
		    if (off > 0 && (off & 0x1ff) == 0 &&
			  m->m_off >= MMINOFF + 2 * sizeof (u_short))
			{
			type = ETHERTYPE_TRAILER + (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);
		    /*
		     * Packet to be sent as trailer: move first packet
		     * (control information) to end of chain.
		     */
			ECcounter(xmtTrailer);
			while (m->m_next)
			    m = m->m_next;
			m->m_next = m0;
			m = m0->m_next;
			m0->m_next = 0;
			m0 = m;
			break;
			}
		}

	    type = ETHERTYPE_IP;
	    break;
#endif INET

#ifdef VALIDnet
	case AF_CONN:
	    long2ea(&dest[0], ((struct sockaddr_rpc *)dst)->node.host.high);
	    long2ea(&dest[3], ((struct sockaddr_rpc *)dst)->node.host.low);
	    mcopy = m_copy(m, 0, (int)M_COPYALL);
	    dllHeader.pktType = VALIDnet_CONN;
	    dllHeaderSize = sizeof(struct validDLLheader);
	    type = ETHERTYPE_VALID;
	    break;

	case AF_RPC:
	    long2ea(&dest[0], ((struct sockaddr_rpc *)dst)->node.host.high);
	    long2ea(&dest[3], ((struct sockaddr_rpc *)dst)->node.host.low);
	    if (!bcmp(dest, es->etherAddr, 6))
		{
		mcopy = m;
		goto gotlocal;
		}
	    type = ETHERTYPE_VALID;
	    dllHeader.pktType = VALIDnet_RPC;
	    dllHeaderSize = sizeof(struct validDLLheader);
	    break;

#ifdef notdef
	case AF_BULK:
	    long2ea(&dest[0], ((struct sockaddr_rpc *)dst)->node.host.high);
	    long2ea(&dest[3], ((struct sockaddr_rpc *)dst)->node.host.low);
	    if (!bcmp(dest, es->etherAddr, 6))
		{
		mcopy = m;
		goto gotlocal;
		}
	    type = ETHERTYPE_VALID;
	    dllHeader.pktType = VALIDnet_BULK;
	    dllHeaderSize = sizeof(struct validDLLheader);
	    break;
#endif notdef
#endif VALIDnet

#ifdef DECnet
	case AF_DECnet:
	    {
	    register struct sockaddr_decnet * decnetSocket;
	    decnetSocket = (struct sockaddr_decnet *) dst;
	    if (decnetSocket -> addressStyle == DNdecnetStyle)
		{
		nodeIdToEthernet(dest, decnetSocket -> nodeAddress);
		}
	    else
		bcopy(decnetSocket -> etherAddress, dest, 6);
	    }

	    type = ETHERTYPE_DECNET;
	    break;
#endif DECnet

	case AF_UNSPEC:
	    ec = (struct ether_header *)dst->sa_data;
	    bcopy((caddr_t)ec->destAddr, (caddr_t)dest, sizeof(dest));
	    type = ec->etherType;
	    break;

#ifdef FUSION
	case AF_NS:
	    m = m0;
	    goto enter_queue;
#endif FUSION

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

/*
 * Add Ethernet net header.  If no space in first mbuf,
 * allocate another.
 */
    if ((m->m_off > MMAXOFF) ||
	((MMINOFF + sizeof(struct ether_header) + dllHeaderSize) > m->m_off))
	{
	m = m_get(M_DONTWAIT, MT_HEADER);
	if (m == 0)
	    {
	    ECcounter(xmtNoSpace);
	    goto bad;
	    }
	m->m_next = m0;
	m->m_off = MMINOFF;
	m->m_len = sizeof(struct ether_header) + dllHeaderSize;
	}
    else
	{
	m->m_off -= sizeof(struct ether_header) + dllHeaderSize;
	m->m_len += sizeof(struct ether_header) + dllHeaderSize;
	}

/*
 * Put source and destination addresses into the header.
 * Put type into header.  Use the full 48 bit address if
 * specified.  If only 24 bits given, force the other 24 to
 * the same manufacturer's block as the one being used by
 * the controller.
 */
    ec = mtod(m, struct ether_header *);
    bcopy(&es->etherAddr[0], &ec->sourceAddr[0], 6);

    bcopy((caddr_t)dest, (caddr_t)ec->destAddr, 6);

    ec->etherType = htons((u_short)type);

/* Add data link layer header if one exists */

    if (dllHeaderSize)
	bcopy((caddr_t)&dllHeader,
	      (caddr_t)ec + sizeof(struct ether_header),
	      dllHeaderSize);

/* Collect statistics about outgoing packet */

    if (ec->destAddr[0] & 0x01)
	if (ec->destAddr[0] == 0xFF)
	    ECcounter(xmtBcast);
	else
	    ECcounter(xmtMcast);
    else
	ECcounter(xmtSingle);

    if (es->mode & EC_DISTRIBUTION)
	{
	int length;
	struct mbuf * mp;
	length = - sizeof(struct ether_header);
	for (mp = m; mp; mp = mp->m_next)
	    length += mp->m_len;
	ecDistIncr(&es->xmtPktDist, ec->etherType, length);
	}

/*
 * Queue message on interface, and start output if interface
 * is not yet active.
 */
enter_queue:
    s = splnet();
    if (IF_QFULL(&es->output))
	{
	IF_DROP(&es->output);
	m0 = m;
	splx(s);
	ECcounter(xmtQueueFull);
	goto bad;
	}
    IF_ENQUEUE(&es->output, m);
    ECcounter(xmtQueued);
    if (es->outputActive == 0)
	ecStart(es);
    splx(s);


/* Internal loop back for messages sent to self */

gotlocal:
    if (mcopy)
	{
	ECcounter(xmtLoop);
	return (looutput(&loif, mcopy, dst));
	}
    else
	return (0);


/*
 * Error occurred while processing output packet
 * Release both the primary and local loopback copy
 * of the packet.
 */

bad:
    m_freem(m0);
    m_freem(mcopy);
    return(error);
}

/*
 * ecStart - Start or restart a packet transmit
 *
 * Synopsis:
 *	ecStart(&es);
 *	struct ec_softc es;
 *
 * Parameters Usage:
 *	es	Controller's data base to start transmissions on.
 *
 * Description:
 *	This routine starts the transmission of a packet.  If
 *	a packet is already active, the transmit flags are just
 *	restuffed to start it again (retransmit after failure).
 *	If a packet is not actively being transmitted, the next
 *	packet is dequeued from the output queue and copied to the
 *	controller's onboard buffer.  If no more packets are on the
 *	output queue, flag the output stream as inactive.
 *
 *	This routine must be called at splnet to insure interlocking
 *	of the output queue since it is also called during transmit
 *	complete interrupt service.
 */

ecStart(es)
    struct ec_softc *es;	/* Current controller data base */
{
    struct mbuf *m;		/* Head of packet's mbuf chain to be xmitted */

/*
 * If output is not already active, get another buffer from the
 * controller's output queue.  Convert the mbuf chain dequeued
 * into a contiguous packet in the controller's Multibus transmit
 * buffer.
 *
 * Check for immediate completion of the transmission; start the next 
 * packet if it completes.
 */

    while (1)
	{
	if (!es->outputActive)
	    {
	    IF_DEQUEUE(&es->output, m);
	    if (m == 0)
		{
		es->outputActive = 0;
		es->backoffMask = ~0;
		ECcounter(xmtEmpty);
		return;
		}
	    ecPut(MEXHDR(es), m, es->unitIndex);
	    ECcounter(xmtStarts);
	    }
	else
	    ECcounter(xmtRestarts);

	if (ecTransmitWait(es))
	    break;
	};
}


ecTransmitWait(es)
    register struct ec_softc * es;
{
    register int loopCount;
    register u_short status;

/*
 * Give transmit buffer to controller and enable for completion
 * or failure interrupts.  Wait for a short while to see if the
 * transmit completes quickly.  If it jams or does not complete
 * return and let the interrupt service handle it.  If it does
 * complete, handle the completion immediately.
 */

    MECSR(es) = ((MECSR(es)|TINT|JINT) & (INTENABLS|PA)) | TBSW;
    es->outputActive = 1;

    for (loopCount = 0; loopCount < MaxPollTimes; loopCount++)
	{
	status = MECSR(es);
	if (status & JAM)
	    break;

	if (!(status & TBSW))
	    {
	    MECSR(es) = (status & ~(TINT | JINT)) & (INTENABLS|PA);
	    es->outputActive = 0;

	    ECunitDebug(DEBUG_XmtEvent, ("transmit complete %d\n", loopCount));
	    ECcounter(xmtSent);
	    ECcounter(xmtPoll);

	    return(0);
	    };
	};

    return(1);
}

/*
 * ecPut - copy mbuf chain into a 3Com buffer
 *
 * Synopsis:
 *	ecPut(ecbuf, &m, unitIndex);
 *	u_char ecbuf [];
 *	struct mbuf m;
 *	int unitIndex;
 *
 * Parameters Usage:
 *	ecbuf		Multibus mapped 3Com buffer to copy into.
 *	m		Head of mbuf chain to copy into 3Com buffer.
 *	unitIndex	Unit number of 3Com controller.
 *
 * Description:
 *	This routine copies a chain of mbuffers (and possibly associated
 *	clusters/pages) into the specified 3Com buffer.  The 3Com
 *	buffer is in mapped Multibus space.
 *	The mbuf chain is released upon completion of the copy.
 *	
 *	If the packet is smaller than the minimum Ethernet packet size
 *	(46data + 14header) the packet is padded to the minimum size.
 *	The CRC size is ignored since the 3C400 controller takes care
 *	of adding it to every packet.
 *
 */

#define MinEtherPacket (ETHERMIN + sizeof(struct ether_header))

ecPut(ecbuf, m, unitIndex)
    u_char * ecbuf;
    struct mbuf * m;
    int unitIndex;
{
    register struct mbuf * mp;
    register int packetSize;
    register int off;
    u_char * bp;

/*
 * Calculate the starting address for the packet in the 3Com buffer.
 * The packet is justified in the buffer so that it ends exactly
 * at the end of the buffer.  Thus no length needs to be specified
 * to the controller.  It transmits from the starting offset to the
 * end of the buffer.
 */

    for (off = 2048, mp = m; mp; mp = mp->m_next)
	off -= mp->m_len;

/*
 * Insure that the minimum ether packet size is enforced.
 * If the packet is too small, adjust the starting offset so that
 * trailing zeros fill out the packet.  It is up to higher level
 * protocols to strip out the actual packet on the receiving end.
 */

    if (off > (2048 - MinEtherPacket))
	{
	off = 4096 - MinEtherPacket - off;
	bzero((u_char *)(ecbuf + off), 2048 - off);
	off = 2048 - MinEtherPacket;
	}

/*
 * Gather the data from all the mbufs and stuff them sequentially
 * into the 3Com buffer.
 */
    *(u_short *)ecbuf = off;
    bp = (u_char *)(ecbuf + off);
    for (mp = m; mp; mp = mp->m_next)
	{
	register int len = mp->m_len;
	u_char * mcp;

	if (len == 0)
	    continue;

	mcp = mtod(mp, u_char *);
	bcopy(mcp, bp, len);
	bp += len;
	}

    if (DEBUG_XmtEvent & ecDebug)
	iprint("xmt", ecbuf + *(u_short *)ecbuf, unitIndex);

    m_freem(m);
}

/*
 * ecAddMcast - Enable a multicast address for a controller
 *
 * Synopsis:
 *	ecAddMcast(&es, &mcast);
 *	struct ec_softc es;
 *	u_char mcast[6];
 *
 * Parameter Usage:
 *	es	Controller data base maintained by driver
 *	mcast	48 bit multicast address
 *
 * Description:
 *	This routine adds a multicast address to the linked list
 *	of multicast addresses for a single controller.  When a
 *	multicast packet is received, the address is checked against
 *	all entries on the list for validity.
 *
 *	This routine locks the data base during update by changing
 *	the interrupt priority of the processor so that no network
 *	interrupts can occur.
 *
 *	The space for the multicast entry is first taken from the
 *	multicast free list.  If the free list is empty, new space
 *	is allocated from the kernel's heap storage.  (ecDelMcast
 *	puts entries on the free list).
 */

ecAddMcast(es, mcast)
    register struct ec_softc * es;
    register u_char * mcast;
{
    register ec_mcast_t * * mcp;
    register ec_mcast_t * mc;

/* Check if address is already on list */

    for (mcp = &es->mcastBuckets [EC_HASH(mcast)]; mc = *mcp; mcp = &mc->next)
	if (bcmp(mc->addr, mcast, 6) == 0)
	    return;

    if (mcast_free)
	{
	mc = mcast_free;
	mcast_free = mc->next;
	mc->next = NULL;
	}
    else
	mc = (ec_mcast_t *)calloc(sizeof (ec_mcast_t), C_WAIT);

    bcopy(mcast, mc->addr, 6);

    {
     int s;
     s = splnet();
     *mcp = mc;
     splx(s);
    }

    es->multiCastCount ++;
}

/*
 * ecDelMcast - Disable a multicast address for a controller
 *
 * Synopsis:
 *	ecDelMcast(&es, &mcast);
 *	struct ec_softc es;
 *	u_char mcast[6];
 *
 * Parameter Usage:
 *	es	Controller data base maintained by the driver
 *	mcast	48 bit multicast address
 *
 * Description:
 *	This routine removes the specified multicast address from
 *	the list of addresses associated with the specified
 *	controller.
 *
 *	This routine locks the data base during update by changing
 *	the interrupt priority of the processor so that no network
 *	interrupts can occur.
 *
 *	Space that was allocated for the deleted multicast entry is
 *	put onto the multicast free list (to be used later by
 *	ecAddMcast).
 */

ecDelMcast(es, mcast)
    register struct ec_softc * es;
    register u_char * mcast;
{
    register ec_mcast_t * * mcp;
    register ec_mcast_t * mc;
    int s;

    for (mcp = &es->mcastBuckets[EC_HASH(mcast)]; mc = *mcp; mcp = &mc->next)
	if (bcmp(mc->addr, mcast, 6) == 0)
	    {
	    s = splnet();
	    *mcp = mc->next;
	    splx(s);
	    mc->next = mcast_free;
	    mcast_free = mc;
	    es->multiCastCount --;
	    break;
	    }
}

/*
 * ecGetMcast - Return the set of multicast addresses in the filter
 *
 * Synopsis:
 *	ecGetMcast(&es, data);
 *	struct ec_softc es;
 *	u_char data[MAX_MULTICAST][6];
 *
 * Parameter Usage:
 *	es	Controller data base maintained by the driver
 *	data	Data buffer to put the multicast addresses into.
 *
 * Description:
 *	This routine copies all multicast addresses from all multicast
 *	buckets into the contiguous data buffer.  Since this buffer is
 *	being returned through an IOCTL interface the number of multicast
 *	addresses has been limited to MAX_MULTICAST.
 *
 *	Note that this routine does not lock the multicast address data base
 *	during this time.  It is assumed that changes will not be made while
 *	the set is being read (reasonable).
 */

ecGetMcast(es, data)
    register struct ec_softc * es;
    register u_char * data;
{
    register ec_mcast_t * mc;
    int bucketIndex;

    for (bucketIndex = 0; bucketIndex < EC_MAXGROUPS; bucketIndex++)
        for (mc = es->mcastBuckets[bucketIndex]; mc; mc = mc->next)
	    {
	    bcopy(mc->addr, data, 6);
	    data += 6;
	    };
}

/*
 * iprint - Print a disassembled Ethernet header, plus 16 bytes of data
 */

iprint(direction, p, unitIndex)
    char * direction;
    u_char *p;
    int unitIndex;
{
    printf("ec%d: %s %02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x %04x",
	    unitIndex,
	    direction,
	    ((struct ether_header *)p)->destAddr[0],
	    ((struct ether_header *)p)->destAddr[1],
	    ((struct ether_header *)p)->destAddr[2],
	    ((struct ether_header *)p)->destAddr[3],
	    ((struct ether_header *)p)->destAddr[4],
	    ((struct ether_header *)p)->destAddr[5],
	    ((struct ether_header *)p)->sourceAddr[0],
	    ((struct ether_header *)p)->sourceAddr[1],
	    ((struct ether_header *)p)->sourceAddr[2],
	    ((struct ether_header *)p)->sourceAddr[3],
	    ((struct ether_header *)p)->sourceAddr[4],
	    ((struct ether_header *)p)->sourceAddr[5],
	    ((struct ether_header *)p)->etherType);

    p += sizeof(struct ether_header);

    printf(" [%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x]\n",
	    *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++,
	    *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++
	    );
}

/*
 * ecDistIncr - increment the correct statistic bucket
 *
 * Calling Synopsis:
 *	ecDistIncr(&bucketSet, etherType, len);
 *	struct ec_pktdist bucketSet;
 *	int etherType;
 *	int len;
 *
 * Parameter Usage:
 *	bucketSet  set of distribution data bases for each ether type
 *	etherType  Ethertype of current packet
 *	len	   length of the current packet.
 *
 * Description:
 *	Each bucket contains the number of packets of a specific
 *	size range (index 0 for 1-64, index 1 for 65-128, etc.).
 *	The length of the packet DOES NOT include the Ethernet
 *	header (16 bytes) or the Frame Check Sequence (2 bytes).
 */

u_char log2table [] =
    {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
     5, 5, 5, 5, 5, 5, 5, 5};


ecDistIncr(bucketSet, etherType, length)
    struct ec_pktdist * bucketSet;
    int etherType;
    int length;
{
    int index;
    int * bucket;

    if (length <= 0)
	return;

    switch (etherType)
	{
#ifdef INET
	case ETHERTYPE_IP:
	    bucket = &bucketSet -> ip[0];
	    break;

	case ETHERTYPE_ARP:
	    bucket = &bucketSet -> arp[0];
	    break;
#endif INET

#ifdef VALIDnet
	case ETHERTYPE_VALID:
	    bucket = &bucketSet -> valid[0];
	    break;
#endif VALIDnet

#ifdef DECnet
	case ETHERTYPE_DECNET:
	    bucket = &bucketSet -> decnet[0];
	    break;
#endif DECnet

	default:
	    bucket = &bucketSet -> unknown[0];
	    break;
	}

    index = (length - 1) >> 6;
    index = (int)log2table[index];

    bucket[index]++;
}

/*
 * ecDistClear - Clear distribution counters for a controller
 *
 * Calling Synopsis:
 *	ecDistClear(&es);
 *	struct ec_softc es;
 *
 * Parameter Usage:
 *	es	Driver structure for the unit to be cleaned.
 *
 * Description:
 *	This routine zeros the packet size distribution statistics
 *	for a specified 3Com driver/controller.  The data base
 *	is locked during the update time by setting the processor
 *	interrupt priority to splnet.
 */

ecDistClear(es)
    struct ec_softc * es;
{
    register int s;

    s = splnet();
    bzero(&es->rcvPktDist, sizeof(struct ec_pktdist));
    bzero(&es->xmtPktDist, sizeof(struct ec_pktdist));
    splx(s);
}

/*
 * ecCountersClear - Zero statistics for a controller
 *
 * Calling Synopsis:
 *	ecCountersClear(&es);
 *	struct ec_softc es;
 *
 * Parameter Usage:
 *	es	Driver structure for the unit to be cleaned.
 *
 * Description:
 *	This routine zeros the statistics for a specified 3Com
 *	driver/controller.  The data base is locked during the update
 *	time by setting the processor interrupt priority to splnet.
 *	After cleaning, the statistics collection start time is
 *	set to the current time.
 *
 */

ecCountersClear(es)
    struct ec_softc * es;
{
    register int s;

    s = splnet();
    bzero(&es->counters, sizeof(struct ec_counters));
    splx(s);
    es->counters.starttime = time.tv_sec;
}

/*
 * ecopen - Open EC character special file.  
 *
 * Calling Synopsis:
 *	result = ecopen(dev);
 *	dev_t dev;
 *	int result;
 *
 * Parameter Usage:
 *	dev	Number of device to open.
 *	result	= 0,	 success.
 *		= ENXIO, device is not present.
 *
 * Description:
 *	This routine is called to perform device specific processing
 *	to open the character special 3Com file.  The device name
 *	is normally "/dev/enet10".
 *
 *	The only processing done here is to check that the device
 *	specified actually exists.
 */

ecopen(dev)
    dev_t dev;
{
    int unitIndex;

    unitIndex = minor(dev);

    if (unitIndex >= NEC)
	return(ENXIO);

    if (!EC_SOFTC(unitIndex).boardPresent)
	return(ENXIO);
    else
	return(0);
}



/*
 * ecclose - Close EC character special file.
 *
 *	Always return no error.
 */

ecclose(dev)
    dev_t dev;
{
    return(0);
}

/*
 * ecioctl - Perform I/O controls on ec's character special file.
 *
 * Synopsis:
 *	ecioctl(dev, com, data, flag);
 *	dev_t dev;
 *	int com;
 *	caddr_t data;
 *	int flag;
 *
 * Parameter Usage:
 *	dev	Character special device major/minor number
 *	com	IOCTL command
 *	data	Data block passed to/from the IOCTL
 *	flag	(Not Used)
 *
 * Description:
 *	This routine is called to perform the IOCTL functions on the
 *	3Com ethernet driver/controller.  These are usually done on
 *	the device "/dev/enet10".
 *
 * WARNING: EC_IOCSETADDR must reset the controller to effect a change
 *	in the physical address that it responds to.  This may cause the
 *	loss of partially received or transmitted packets. (Not to worry).
 *	Other synchronization operations may have to be done before and
 *	after this operation to insure proper operations of higher layers.
 *
 */

/***************************************************************/
/***************************************************************/
/* Change IOCTL data transfer from implicit to COPYIN/COPYOUT? */
/***************************************************************/
/***************************************************************/

ecioctl(dev, com, data, flag)
    dev_t dev;
    int com;
    u_char * data;
    int flag;
{
    u_short name;		/* Packet type (EC_IOCPKTDIST) */
    struct ec_softc *es;	/* Pointer to driver data structure */
    int s;			/* Saved interrut priority level */

/* Select the driver data structure for the specified unit */

    es = &EC_SOFTC(minor(dev));

/* Process IOCTL based on command */

    switch (com)
	{
    /* Clear statistics counters */

	case EC_IOCCLEARCOUNTERS:
	    if (!suser())
		return(u.u_error);
	    else
		ecCountersClear(es);
	    break;

    /* Return statistics counters */

	case EC_IOCGETCOUNTERS:
	    bcopy(&(es->counters), data, sizeof(struct ec_counters));
	    break;

    /* Add a multicast address to the filter */

	case EC_IOCADDMCAST:
	    if (!suser())
		return(u.u_error);
	    else
		ecAddMcast(es, data);
	    break;
    
    /* Delete a multicast address from the filter */

	case EC_IOCDELMCAST:
	    if (!suser())
		return(u.u_error);
	    else
		ecDelMcast(es, data);
	    break;

    /* Return the set of addresses in the multicast filter */

	case EC_IOCGETMCAST:
	    ecGetMcast(es, data);
	    break;

    /* Set the controller's physical address */

	case EC_IOCSETADDR:
	    if (!suser())
		return(u.u_error);
	    else
		{
		s = splnet();
		bcopy(data, &es->etherAddr[0], 6);
		ecInit(es->unitIndex);
		splx(s);
		}
	    break;

    /* Return the controller's physical address */

	case EC_IOCGETADDR:
	    bcopy(&es->etherAddr[0], data, 6);
	    break;

    /* Enable unit modes; affect packet type reception immediately */

	case EC_IOCENABLEMODE:
	    if (!suser())
		return(u.u_error);
	    else
		{
		s = splnet();
		es->mode |= *(int *)data;
		if (es->mode & EC_PROMISCUOUS)
		    MECSR(es) = (MECSR(es)&~PA) | (EC_PKTALL + EC_NOERRORS);
		else
		    if (es->multiCastCount)
			MECSR(es) = (MECSR(es)&~PA)|(EC_MINEMULTI+EC_NOERRORS);
		    else
			MECSR(es) = (MECSR(es)&~PA)|(EC_MINEBROAD+EC_NOERRORS);
		splx(s);
		}
	    break;

    /* Disable unit modes; affect packet type reception immediately */

	case EC_IOCDISABLEMODE:
	    if (!suser())
		return(u.u_error);
	    else
		{
		s = splnet();
		es->mode &= ~*(int *)data;
		if (es->mode & EC_PROMISCUOUS)
		    MECSR(es) = (MECSR(es)&~PA) | (EC_PKTALL + EC_NOERRORS);
		else
		    if (es->multiCastCount)
			MECSR(es) = (MECSR(es)&~PA)|(EC_MINEMULTI+EC_NOERRORS);
		    else
			MECSR(es) = (MECSR(es)&~PA)|(EC_MINEBROAD+EC_NOERRORS);
		splx(s);
		}
	    break;
	
    /* Return the current modes of the unit */

	case EC_IOCGETMODE:
	    *(int *)data = es->mode;
	    break;

    /* Clear packet size distribution data base */

	case EC_IOCCLEARDIST:
	    if (!suser())
		return(u.u_error);
	    else
		ecDistClear(es);
	    break;

    /* Return packet size distribution for received packets */

	case EC_IOCGETRCVDIST:
	    bcopy(&es->rcvPktDist, data, sizeof(struct ec_pktdist));
	    break;

    /* Return packet size distribution for transmitted packets */

	case EC_IOCGETXMTDIST:
	    bcopy(&es->xmtPktDist, data, sizeof(struct ec_pktdist));
	    break;

    /*
     * Return version number packed into the lower 16 bits of a 32 bit
     * word.  Zero the upper 16 bits.
     */
	case EC_IOCGETVERSION:
	    *(int *)data = (EC_MajorVersion << 8) | EC_MinorVersion;
	    break;
    
    /*
     * Read and set the debugging/trace values.
     */
	case EC_IOCGETDEBUG:
	    *(u_long *)data = ecDebug;
	    break;
	
	case EC_IOCSETDEBUG:
	    if (!suser())
		return(u.u_error);
	    else
		{
		u_long tmpDebug;
		tmpDebug = *(u_long *)data;
		*(u_long *)data = ecDebug;
		ecDebug = tmpDebug;
		}
	    break;

	default:
	    return(EIO);
	    break;
	}

    return(0);
}

ecNetIoctl(ifp, cmd, data)
    register struct ifnet * ifp;
    int cmd;
    caddr_t data;
{
    register struct ifreq *ifr = (struct ifreq *) data;
    int s = splnet();
    int error = 0;

    switch (cmd)
	{
	case SIOCSIFADDR:
	    if (ifp->if_flags & IFF_RUNNING)
		if_rtinit(ifp, -1);
	    ecSetInetAddr(ifp, (struct sockaddr_in *)&ifr->ifr_addr);
	    ecInit(ifp->if_unit);
	    break;

	default:
	    error = EINVAL;
	}

    splx(s);
    return(error);
}
