/*
 * $RCSfile: irix_eth.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1994 Massachusetts Board of Regents
 *
 * $Log: irix_eth.c,v $
 * Revision 1.2  1996/01/30 20:47:34  slm
 * Updated version.
 *
 * Revision 1.1  1995/07/29  02:39:45  slm
 * Initial revision
 *
 * Revision 1.1.1.1.1.3  1994/11/26  00:21:55  hkaram
 * msgRefresh replaces msgConstructAllocate
 *
 * Revision 1.1.1.1.1.2  1994/11/22  22:01:40  hkaram
 * Converted to new msg tool
 *
 * Revision 1.1.1.1.1.1  1994/11/12  19:05:37  hkaram
 * New branch
 *
 * Revision 1.1.1.1  1994/06/03  17:52:34  menze
 * Uses romopt library
 *
 * Revision 1.1  1994/03/14  21:19:30  umass
 * Initial revision
 */

#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/raw.h>
#include <stdio.h>
#include <ctype.h>
#include <netdb.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <ulocks.h>

#include "x_stdio.h"
#include "xkernel.h"
#include "eth.h"
#include "eth_i.h"
#include "irix_eth_i.h"
#include "mac_common.h" /* For SGI Raw socket (net interface) routines */
#include "arp.h"
#include "platform.h"	/* For queue and process routines */
#include "modeldep.h"   /* For SGI machine dependent stuff */
#include "xk_lock.h"

int	 traceirixethp;

#ifdef   XK_DEBUG
#define  IRIXETH_STATS
#define  PACKET_STATS
#endif

#define  STRING_SIZE          256
#define  MAX_UPPER_PROTOCOLS  30

#ifdef  PACKET_STATS
PRIVATE int             sentPackets = 0;
PRIVATE int             recvPackets = 0;
PRIVATE int             bcastSentPackets = 0;
PRIVATE int             sendErrorCount = 0;
PRIVATE int             recvNoThreadCount = 0;
PRIVATE void *          packetLock;   /* also for statistics */
#endif  /* PACKET_STATS */

#ifdef  IRIXETH_STATS
PRIVATE int		activeThreads;
PRIVATE int		activeHighWater;
#endif

PRIVATE Queue		warm_pool;

PRIVATE usema_t *  	warm_pool_semaphore;
PRIVATE usema_t *       init_semaphore;
PRIVATE int		instance;

IMPORT int	        gethostname( char *, int );
IMPORT char *	        inet_ntoa( struct in_addr );
IMPORT int	        socket( int, int, int );

PRIVATE XkReturn  getHostEthAddress( OUT ETHhost * );
PRIVATE PROCEDURE 	warm_pool_handler _ANSI_ARGS_(( Event, PTR ));
PRIVATE PROCEDURE 	init_eth_warm_pool _ANSI_ARGS_(( Protl ));
PRIVATE PROCEDURE       initSockets _ANSI_ARGS_(( INOUT Protl, int, int, 
                                                  OUT int *, OUT int * ));
PRIVATE PROCEDURE	internalDemux _ANSI_ARGS_(( Sessn, Msg * ));
PRIVATE PROCEDURE	processRomFile _ANSI_ARGS_(( INOUT Protl ));
PRIVATE int		readFromSocket _ANSI_ARGS_(( PState *, OUT char *, 
                                                     int ));
PRIVATE XkReturn	readPortsRom _ANSI_ARGS_(( Protl, char **, int, int,
						  VOID * ));
PRIVATE PROCEDURE	sendOnSocket _ANSI_ARGS_(( PState *, ETHhost *, 
                                                   char *, int ));
PRIVATE int		irixethControl _ANSI_ARGS_(( Protl, int, char *, 
                                                      int ));
PRIVATE XkReturn	irixethOpenEnable _ANSI_ARGS_(( Protl, Protl, Protl, 
                                                         Part * ));
PRIVATE XkHandle	irixethPush _ANSI_ARGS_(( Sessn, Msg * ));

PRIVATE char *upperProtocols[] =
{
    /* Our test protocol */
    "ethtest",
    /* Internet protocols */
    "ip",
    "arp",
    "rarp",
    /* Dynamic network architecture RPC protcols */
    "blast",
    "blasttest",
    "chan",
    "vsize",
    /* Arizona second generation of RPC protocols */
    "bid",
    "bidctl",
    NULL
};


static ProtlRomOpt romOpts[] = {
    { "", 3, readPortsRom }
};


PRIVATE XkReturn
getHostEthAddress( OUT addressPtr )
    ETHhost *addressPtr;
{
    XkReturn returnValue;
    char fullHostName[MAXHOSTNAMELEN];
    char *hostName;
    int i;
    ETHhost  my_mac_address;

    /*  
     *  ether_hostton(3Y) , ether_ntoa(3Y) are from the Sun NIS library
     *  provided in IRIX4,IRIX5. You need to link with -lsun.
     */
   
    returnValue = XK_FAILURE;
    if( gethostname( fullHostName, MAXHOSTNAMELEN ) != 0 )
    {
      
      xTrace1(irixethp, TR_ERRORS,
              "getHostETHAddress: error %d calling gethostname()", errno);
      return( returnValue );
    }
    else
    {
      xTrace1(irixethp, TR_MAJOR_EVENTS,
              "getHostEthAddress: full host name = %s", fullHostName);
      hostName = fullHostName;
    }

    /*
     * truncate a fully-qualified name to a local alias name
     * i.e. predator.cs.umass.edu -> predator 
     */

    strtok (hostName, ".");

    if ( ether_hostton(hostName, &my_mac_address)  )
      {
	xTrace1(irixethp, TR_ERRORS,
		"getHostEthAddress: could not get MAC address for %s",
		hostName);
	return ( returnValue );
      }
    else
      {
	xTrace2(irixethp,TR_MAJOR_EVENTS,
		"getHostEthAddress: host %s MAC address is %s ",
		hostName, ethHostStr( &my_mac_address ));
      }

        bcopy( (void *)&my_mac_address, (void *)addressPtr, sizeof(ETHhost));

        return XK_SUCCESS;
}

#ifdef PACKET_STATS

PRIVATE PROCEDURE
printPacketStats(ev, arg)
    Event        ev;
    PTR          arg;
{
    xLockAcquire(packetLock);
    printf("Received %d, sent %d, diff %d, bcast %d\n",
           recvPackets, sentPackets,
           (recvPackets-sentPackets), bcastSentPackets);
    xLockRelease(packetLock);
    evDetach(evSchedule( printPacketStats, 0, 30*1000*1000 ));
}

#endif /* PACKET_STATS */


PRIVATE PROCEDURE
sendOnSocket( ps, dest, buf, len )
    PState      *ps; 
    ETHhost    *dest;
    char        *buf;
    int         len;
{  
    unsigned char *bytePtr;
    int i;
    int rv;

    xTrace3(irixethp, TR_FUNCTIONAL_TRACE,
	    "sendOnSocket: socket = %d - sending %d bytes to <%s>",
	    ps->sendSocket, len, ethHostStr(dest));

    if( len < MIN_ETH_SEND_SOCKET_PACKET_SIZE )
      {
        len = MIN_ETH_SEND_SOCKET_PACKET_SIZE;
      }

    rv = write( ps->sendSocket, buf, len );
    if (rv != len)
     {

#ifdef PACKET_STATS	    
       xLockAcquire(packetLock);
       sentPackets ++;
       sendErrorCount ++;
       xLockRelease(packetLock);
#endif

       perror((char *)0);
       xTrace1(irixethp, TR_ERRORS, "sendOnSocket: write rv %d", rv);
       xError("irixeth: sendOnSocket");
	    
     }
    else
     {

#ifdef PACKET_STATS
       xLockAcquire(packetLock);
       sentPackets ++;
       xLockRelease(packetLock);
#endif

     }

  }


PRIVATE int
readFromSocket( ps, OUT msg, len )
    PState   *ps;  
    int      len;
    char     *msg;
{
    int      n;

        xTrace1(irixethp, TR_EVENTS,
                "readFromSocket: socket = %d", ps->recvSocket);

        n = read(ps->recvSocket, msg, len);

#ifdef PACKET_STATS
       
        xLockAcquire(packetLock);
	recvPackets++ ;
        xLockRelease(packetLock);
#endif

        if (n == -1 )
        {
          perror((char *)0);
          xTrace1(irixethp, TR_ERRORS, 
                  "readFromSocket: got %d return read", n);
          return -1;
        }
	if ( n < MIN_ETH_RECV_SOCKET_PACKET_SIZE )
	{
	  xTrace1(irixethp, TR_ERRORS,
		  "readFromSocket: got runt packet %d bytes",n);
	  return -1;
	}

        xTrace2(irixethp, TR_FUNCTIONAL_TRACE,
	    "readFromSocket: receiving %d bytes from <%s>", n,
	    ethHostStr((ETHhost *)
                        ((recvHeader *)msg)->link_header.ether_shost));
    return n;
}


PRIVATE PROCEDURE
initSockets( INOUT self, sendPort, recvPort, OUT sendSocket, OUT recvSocket )
    Protl    self;
    int     sendPort;
    int     recvPort;
    int     *sendSocket;
    int     *recvSocket;
{
    PState         *ps = (PState *)self->state;
    Protl           hlp = self->up;
    int            protocol = RAWPROTO_DRAIN;
    int            bufSize;
    int            bufSizeSize;
    long           ulp;
    long           ulpEthernetType;
    char           interfaceName[80];
    ETHtype        ethernetType[MAX_UPPER_PROTOCOLS];
    int            count;
    int            myUserId;
    IRIX_Machine_Info Machine;
    int            i;

    /*
     * Build our list of upper layer protocol Ethernet types.
     */
    count = 0;
    for( i = 0; upperProtocols[i] != NULL; i++ )
    {
      if( (ulp = protTblGetId( upperProtocols[i] )) != -1 )
      {
        if( (ulpEthernetType = relProtNumById(ulp, hlp)) != -1 )
        {
          xTrace2(irixethp, TR_MAJOR_EVENTS,
                  "protocol %s (Ethernet type %XH) configured above ETH", 
                  upperProtocols[i], ulpEthernetType);
          ethernetType[count++] = ulpEthernetType;
          xAssert(count <= MAX_UPPER_PROTOCOLS);
        }
        else
        {
          xTrace1(irixethp, TR_GROSS_EVENTS,
              "irixeth -- protocol %s NOT configured above ETH", 
              upperProtocols[i]);
        }
      }
      else
      {
        xTrace1(irixethp, TR_GROSS_EVENTS, 
            "irixeth -- protocol graph not configured with %s", 
            upperProtocols[i]);
      }
    }

    /*
     * Get the Ethernet interface name based on the type of machine we
     * are running on.
     */

    GetSGIMachineInfo(&Machine);
    strcpy (interfaceName, Machine.ethernet_if_name);

    xTrace1(irixethp, TR_GROSS_EVENTS, "irixeth: My interface name is %s",
	    interfaceName);

    if ( strlen(Machine.ethernet_if_name) == 0)
      {
	xTrace0(irixethp, TR_ERRORS, "SGI machine type not supported"); 
        Kabort("initSockets: out of here");
      }

    /*
     * We must be root to open raw sockets.
     */
    myUserId = beRoot();
    /*
     * Open our raw sockets.
     */
    *sendSocket = opensend_rs( interfaceName, protocol, sendPort );
    *recvSocket = openrecv_rs( interfaceName, protocol, count, ethernetType,
                               recvPort, (MAC48bithost *)&ps->myHost );
    /*
     * Set and get send buffer size ...
     */
    bufSize = SNDBUFSIZE;
    bufSizeSize = sizeof(bufSize);
    size_ifbufs( *sendSocket, bufSize, SMALLBUFSIZE );
    if (getsockopt(*sendSocket, SOL_SOCKET, SO_SNDBUF, (char *)&bufSize, 
                   &bufSizeSize) != 0) {
	xTrace0(irixethp, TR_ERRORS,
		"Could not get size of ETH send buffer");
    } else {
	xTrace1(irixethp, TR_GROSS_EVENTS,
		"Send buffer size for ETH socket: %d", bufSize);
    }
    /*
     * Set and get receive buffer size ...
     */
    bufSize = RCVBUFSIZE;
    bufSizeSize = sizeof(bufSize);
    size_ifbufs( *recvSocket, SMALLBUFSIZE, bufSize );
    if (getsockopt(*recvSocket, SOL_SOCKET, SO_RCVBUF, (char *)&bufSize, 
                   &bufSizeSize) != 0) {
	xTrace0(irixethp, TR_ERRORS,
		"Could not get size of ETH receive buffer");
    } else {
	xTrace1(irixethp, TR_GROSS_EVENTS,
		"Receive buffer size for ETH socket: %d", bufSize);
    }
    beMeAgain( myUserId );

    xTrace2(irixethp, TR_GROSS_EVENTS,
	    "initSockets got socket descriptors send = %d, recv = %d",
            *sendSocket, *recvSocket);
}

/* 
 * Sets the 'port' fields of the protocol state
 */

static XkReturn
readPortsRom( self, str, nFields, line, arg )
    Protl	self;
    char	**str;
    int		nFields, line;
    VOID	*arg;
{
    PState	*ps = (PState *)self->state;

    if ( sscanf(str[1], "%d", &ps->sendPort) < 1 ) return XK_FAILURE;
    if ( sscanf(str[2], "%d", &ps->recvPort) < 1 ) return XK_FAILURE;
    return XK_SUCCESS;
}


PUBLIC PROCEDURE
irixeth_init( self )
    Protl      self;
{
    struct hostent	*h;
    struct in_addr 	*in;
    char		name[100];
    int			namelen=100;
    PState		*ps;
    
    xTrace0(irixethp, TR_MAJOR_EVENTS, "irixeth_init");
    if( instance > IRIXETH_MAX_INSTANCES ) {
	Kabort("irixeth_init -- too many instances");
    }

    ps = X_NEW(PState);
    bzero( (char *)ps, sizeof(PState) );

    /* advertise an MTU equal to the user information part of 802.3 */
    ps->mtu = MAX_ETH_DATA_SZ; 

    ps->sendPort = ERROR_PORT;
    ps->recvPort = ERROR_PORT;
    
    self->state = (PTR)ps;

    findProtlRomOpts(self, romOpts, sizeof(romOpts)/sizeof(ProtlRomOpt), 0);
    if ( ps->sendPort == ERROR_PORT || ps->recvPort == ERROR_PORT ) {
	xTrace3(irixethp, TR_ERRORS,
		"Bad port (%d and/or %d) specified for irixeth instance %d", 
                ps->sendPort, ps->recvPort, instance);
	Kabort("irixeth -- port specification error");
    }
    /* 
     * Determine my host address
     */
    if( getHostEthAddress( &ps->myHost ) == XK_SUCCESS )
    {
        xTrace1(irixethp, TR_GROSS_EVENTS,
	        "irixeth_init: ETH started with address %s",
	        ethHostStr(&ps->myHost));
    }
    else
    {
        Kabort("irixeth_init -- could not get ETH address");
    }

    self->push = irixethPush;
    self->controlprotl = irixethControl;
    self->openenable  = irixethOpenEnable;
    self->up = 0;
    instance++;

#ifdef PACKET_STATS
    packetLock = (void *) xLockAllocate();
#endif

}


PRIVATE PROCEDURE
ethMsgStore( hdr, netHdrArg, len, arg )
    PTR             hdr;
    char            *netHdrArg;
    long            len;
    PTR             arg;
{
    sendHeader     *netHdr = (sendHeader *) netHdrArg;

    xAssert(len == sizeof(sendHeader));
    /*
     * Leave the OrgCode as an uninterpreted byte sequence.
     */
    ((ETHhdr *)hdr)->type = htons(((ETHhdr *)hdr)->type); 
    bcopy(hdr, (void *)netHdr->link_header.ether_dhost, sizeof(ETHhdr));
}


PRIVATE long
ethMsgLoad( hdr, netHdrArg, len, arg )
    PTR            hdr;
    char          *netHdrArg;
    long           len;
    PTR            arg;
{
    recvHeader    *netHdr = (recvHeader *) netHdrArg;
    int i;
    unsigned char *bytePtr;

    xAssert(len == sizeof(recvHeader));
    bcopy((void *)netHdr->link_header.ether_dhost, hdr, sizeof(ETHhdr));
    /*
     * Leave the OrgCode as an uninterpreted byte sequence.
     */
    ((ETHhdr *)hdr)->type = ntohs(((ETHhdr *)hdr)->type);

    return sizeof(recvHeader);
}


PRIVATE XkHandle
irixethPush( self, msg )
    Sessn	self;
    Msg 	*msg;
{
    ETHhdr	*hdr = msgGetAttr(msg, 0);
    char	buffer[MAX_ETH_SEND_SOCKET_PACKET_SIZE];
    char	*bufPtr = buffer;
    int		len;
    int         dataLen;
    PState	*ps = (PState *)self->state;
    void        *buf;
    MsgWalk  mi;
    u_char      *data;

    xTrace0(irixethp, TR_EVENTS, "irixethpush");
    xAssert(hdr);

    buf = msgPush(msg, sizeof(sendHeader));
    xAssert(buf);
    ethMsgStore(hdr, buf, sizeof(sendHeader), 0);

    if ( (len = msgLength(msg)) > MAX_ETH_SEND_SOCKET_PACKET_SIZE ) {
	xTrace2(irixethp, TR_SOFT_ERRORS,
		"irixeth driver: msgLength (%d) is larger than max (%d)",
		len, MAX_ETH_SEND_SOCKET_PACKET_SIZE);
	return XMSG_ERR_HANDLE;
    }
    /*
     * Place message contents in buffer
     */
    msgWalkInit(&mi, msg);
    while ((data = msgWalkNext(&mi, &dataLen)) != 0) {
      bcopy(data, bufPtr, dataLen);
      bufPtr += dataLen;
    }
    msgWalkDone(&mi);

    sendOnSocket(ps, &hdr->dst, buffer, len);
    return XMSG_NULL_HANDLE;
}    


PRIVATE PROCEDURE
internalDemux(self, msg)
    Sessn self;
    Msg *msg; 
{
    ETHhdr	hdr;
    void        *buf;
    
    xTrace0(irixethp, TR_EVENTS, "in ETH internal demux");
    
    buf = msgPop(msg, sizeof(recvHeader));
    if ( buf == NULL) {
	xTrace0(irixethp, TR_SOFT_ERRORS,
		"irixeth internalDemux: incoming message too small!");
	return;
    }
    ethMsgLoad(&hdr, buf, sizeof(recvHeader), 0);

    if ( ! self->up ) {
	xTrace0(irixethp, TR_ERRORS, 
                "irixeth internalDemux: no upper protocol!");
	return;
    }
    msgSetAttr(msg, 0, &hdr, sizeof(ETHhdr));
    xDemux(xGetUp(self), self, msg);
    xTrace0(irixethp, TR_EVENTS, "ETH internal demux finished");
}


/*
 * warm_pool_handler:
 * init message struct;
 * forever
 *     allocate new message
 *     release monitor
 *     queue self in warm_pool_semaphore;
 *     go read socket after waking up
 *     after reading, acquire monitor
 *     signal next in warm_pool_semaphore
 *     demux message
 *     init message struct
 */

PRIVATE PROCEDURE
warm_pool_handler(ev, arg)
    Event         ev;
    PTR           arg;
{
    Msg		msg;
    char 	*bufPtr;
    int		buflen, bufSize, bufSizeSize;
    Process     *my_process, *next_process;
    Sessn 	self = (Sessn) arg;
    PState	*ps = (PState *)self->state;

    my_process = process_self();
    xAssert(my_process);
    xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:initializing",
            my_process->pid);
    
    usvsema(init_semaphore);

    xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:allocating",
            my_process->pid);
    bufPtr = msgConstructAllocate(&msg, MAX_ETH_RECV_SOCKET_PACKET_SIZE);

    for (;;) {

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:yielding",
            my_process->pid);
        xk_master_unlock();

	uspsema(warm_pool_semaphore);

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:woke up",
            my_process->pid);

	buflen = readFromSocket( ps, bufPtr,
                                 (MAX_ETH_RECV_SOCKET_PACKET_SIZE) );
        if (buflen == -1)
	  {
            Kabort("warm_pool_handler: readFromSocket failed");
	  }

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:have read ETH",
            my_process->pid);

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:unblocking next",
            my_process->pid);
        usvsema(warm_pool_semaphore);

	/* 
         * set the message to reflect the number of bytes received 
         */
        msgTruncate(&msg, buflen);

	xk_master_lock();

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:got lock",
            my_process->pid);

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:calling demux",
            my_process->pid);
	internalDemux(self, &msg);

	xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:re-allocating",
		my_process->pid);
	bufPtr = msgRefresh(&msg, MAX_ETH_RECV_SOCKET_PACKET_SIZE);

        xTrace1(irixethp, TR_EVENTS, "warm_pool_handler %d:end loop",
            my_process->pid);
    }

}
	

PRIVATE PROCEDURE
init_eth_warm_pool(self)
Protl self;
{
    int 	i;
    Process	*first_process;
    static int	initialized = 0;
    
    if ( initialized ) {
	xTrace0(irixethp, TR_EVENTS, 
                "irixeth process pool already initialized");
	return;
    }
    xTrace0(irixethp, TR_EVENTS, "init_eth_warm_pool:initializing");
    initialized = 1;
    init_semaphore = usnewsema(xkernel_arena, 0);
    warm_pool_semaphore = usnewsema(xkernel_arena, 0);

    /* create warm pool */
    for (i = 0; i < MAX_ETH_WARM_POOL; i++)
        evDetach( evSchedule(warm_pool_handler, (VOID *) self, 0));

    xTrace0(irixethp, TR_EVENTS, "init_eth_warm_pool:waiting");

    /* wait for all warm pool processes to become ready */
    xk_master_unlock();

    for (i = 0; i < MAX_ETH_WARM_POOL; i++)
    {
	uspsema(init_semaphore);
    }
    xk_master_lock();

    /* fire off first thread */
    usvsema(warm_pool_semaphore);

    xTrace0(irixethp, TR_EVENTS, "init_eth_warm_pool:finished");
}


PRIVATE XkReturn
irixethOpenEnable( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part	*p;
{
    PState *ps = (PState *)self->state;

    if ( self->up ) {
	xError("irixethOpenEnable called multiple times!");
	return XK_FAILURE;
    }
    self->up = hlp;
    initSockets( self, ps->sendPort, ps->recvPort,
                 &ps->sendSocket, &ps->recvSocket );
    xTrace2(irixethp, TR_MAJOR_EVENTS,
            "irixethOpenEnable: sending on port %d, receiving on port %d",
	    ps->sendPort, ps->recvPort);
    init_eth_warm_pool(self);		

    return XK_SUCCESS;
}


PRIVATE int
irixethControl( s, op, buf, len )
    Protl	s;
    int 	op, len;
    char 	*buf;
{
    PState	*ps = (PState *)s->state;
    Protl        throwAwayArp;

    switch (op) {

      case MAC_REGISTER_ARP:
	/* 
	 * ARP registers itself with irixeth and irixeth.  We don't
         * need it, so here we just consume the control buffer, and 
         * throw away the ARP descriptor.
	 */
	checkLen(len, sizeof(Protl));
	throwAwayArp = *(Protl *)buf;
	return 0;

      case MAC_DUMP_STATS:
#ifdef IRIXETH_STATS	
	{
	    xTrace0(irixethp, TR_ALWAYS, "IRIXETH statistics:");
	    xTrace3(irixethp, TR_ALWAYS,
		    "\tthreads: %d\tactive: %d\thigh-water: %d",
		    MAX_ETH_WARM_POOL, activeThreads, activeHighWater);
	}
#endif
#ifdef PACKET_STATS
        xLockAcquire(packetLock);
        printf( "\nIRIXETH packet statistics:\n\n" );
        printf( "  received   = %d\n", recvPackets );
        printf( "  sent       = %d\n", sentPackets );
        printf( "  difference = %d\n", (recvPackets-sentPackets) );
        printf( "  broadcasts = %d\n", bcastSentPackets );
        printf( "  broken up send errors       = %d\n", sendErrorCount);
        printf( "  no thread on receive errors = %d\n", recvNoThreadCount );
        xLockRelease(packetLock);
#endif /* PACKET_STATS */
	

      case GETMAXPACKET:
      case GETOPTPACKET:
        checkLen(len, sizeof(int));
        *(int *) buf = ps->mtu;
        return (sizeof(ps->mtu));

      case GETMYHOST:
	checkLen(len, sizeof(ETHhost));
	bcopy((char *) &ps->myHost, buf, sizeof(ETHhost));
	return (sizeof(ETHhost));

      default:
	return -1;

    }
}

