/* 
 * $RCSfile: sim_fddi.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: sim_fddi.c,v $
 * Revision 1.2  1996/01/30 20:50:22  slm
 * Updated copyright and version.
 *
 * Revision 1.1  1995/07/29  02:38:59  slm
 * Initial revision
 *
 * Revision 1.6.1.1.1.3  1994/11/26  00:22:51  hkaram
 *  msgRefresh replaces msgConstructAllocate
 *
 * Revision 1.6.1.1.1.2  1994/11/22  22:04:43  hkaram
 * Converted to new msg tool
 *
 * Revision 1.6.1.1.1.1  1994/11/12  19:08:34  hkaram
 * New branch
 *
 * Revision 1.6.1.1  1994/03/14  21:07:41  umass
 * Changes to thread management and statistics
 *
 * Revision 1.6  1994/02/05  00:34:13  menze
 *   [ 1994/02/03          menze ]
 *   Converted to use rom-option library
 *
 * Revision 1.5  1993/12/16  01:57:25  menze
 * Uses SIM_SOCK2ADDR (AZ version) control op instead of SOCK2SIMADDRESS
 * (UMASS version)
 * Other minor changes
 *
 * Revision 1.4  1993/11/13  00:45:31  menze
 * Original version from UMass
 *
 * Based on xkernel/irix/drivers/eth/sim_ether.c (DJY)
 */

#include <stdio.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <netdb.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
#include <ulocks.h>

#include "x_stdio.h"
#include "xkernel.h"
#include "xk_lock.h"
#include "fddi.h"
#include "fddi_i.h"
#include "sim_fddi_i.h"
#include "arp.h"
#include "platform.h"	/* For queue and process routines */
#include "mac.h"
#include "sim.h"
#include "xk_lock.h"

int	tracesimfddip;  /* Changed from tracesimethp for first build */

#ifdef  XK_DEBUG
#define SIMFDDI_STATS
#define PACKET_STATS
#else
#undef  SIMFDDI_STATS
#define PACKET_STATS
#endif

#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 SIMFDDI_STATS
PRIVATE int		activeThreads;
PRIVATE int		activeHighWater;
#endif

PRIVATE FDDIhost        fddiBroadcastHost = FDDI_BCAST_HOST;
PRIVATE int		instance;
PRIVATE usema_t *	warm_pool_semaphore;
PRIVATE usema_t *	init_semaphore;

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

PRIVATE int		arpForEachFunc _ANSI_ARGS_(( ArpBinding *, PTR ));
PRIVATE PROCEDURE 	warm_pool_handler _ANSI_ARGS_(( Event, PTR ));
PRIVATE PROCEDURE 	init_fddi_warm_pool _ANSI_ARGS_(( Protl ));
PRIVATE int		initSocket _ANSI_ARGS_(( int ));
PRIVATE PROCEDURE	internalDemux _ANSI_ARGS_(( Sessn, Msg * ));
PRIVATE int		read_fddi _ANSI_ARGS_(( int, char *, int ));
PRIVATE XkReturn	readPortRom _ANSI_ARGS_(( Protl, char **, int, int, VOID * ));
PRIVATE XkReturn	readPortBackRom _ANSI_ARGS_(( Protl, char **, int, int, VOID * ));
PRIVATE PROCEDURE	sendOnSocket _ANSI_ARGS_(( int, FDDIhost *, char *, 
                                                   int ));
PRIVATE int		simfddiControl _ANSI_ARGS_(( Protl, int, char *, int ));
PRIVATE XkReturn	simfddiOpenEnable _ANSI_ARGS_(( Protl, Protl, Protl, 
                                                       Part * ));
PRIVATE XkHandle	simfddiPush _ANSI_ARGS_(( Sessn, Msg * ));
PRIVATE PROCEDURE	writeBcast _ANSI_ARGS_(( PState *, char *, int ));
PRIVATE PROCEDURE       printPacketStats _ANSI_ARGS_(( Event, PTR ));

#define FDDI_ADDRESS_ISBROAD(A) (FDDI_ADDRS_EQUAL(A,fddiBroadcastHost))
#define FDDI_ADDRESS_ISMULTI(A) (FDDI_ADDRESS_ISGROUP(A) && \
                                 !FDDI_ADDRESS_ISBROAD(A))

static ProtlRomOpt protlOpts[] = {
    { "port", 3, readPortRom },
    { "", 2, readPortBackRom }
};


/* changes to support full internet addressing in rom files */

PRIVATE PROCEDURE 
simfddi2sock(fddiAddr, sockAddr)
    FDDIhost fddiAddr;
    struct sockaddr_in *sockAddr;
{
  bzero((char *)sockAddr, sizeof (struct sockaddr_in));
  sockAddr->sin_family = AF_INET;
  /* 
   * IP address is in the first 4 bytes of the FDDI address
   * UDP port is in the 5th and 6th bytes of the FDDI address
   */
  sockAddr->sin_addr = *(struct in_addr *)&fddiAddr;
  sockAddr->sin_port = (*(u_short *)((char *)&fddiAddr + 4));
}


/* changed inAddr to pass-by-value to make sparc and 
   sun3 compatible - cjt 5/15 */

PRIVATE PROCEDURE 
sock2simfddi(fddiAddr, inAddr, udpPort)
     FDDIhost 	*fddiAddr;
     IPhost	inAddr;
     int 	udpPort;
{
    char *cp1, *cp2;
    short tmpshrt;
    int i;
    
    cp1 = (char *)fddiAddr;
    cp2 = (char *) &inAddr;		/* passed by value now - cjt */
    for (i=0; i<4; i++) *cp1++ = *cp2++;
    tmpshrt = htons((u_short)udpPort);
    cp2 = (char *) &tmpshrt;
    for (i=0; i<2; i++) *cp1++ = *cp2++;
}

#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 int
arpForEachFunc( ab, arg )
    ArpBinding  *ab;
    PTR         arg;
{
    FDDIMsg	*m = (FDDIMsg *)arg;

    xTrace0(simfddip, TR_FUNCTIONAL_TRACE, "arpForEach invoked");
#ifdef PACKET_STATS
    xLockAcquire(packetLock);
    bcastSentPackets ++;
    xLockRelease(packetLock);
#endif /* PACKET_STATS */
    sendOnSocket(m->sock, (FDDIhost *)&ab->hw, m->buf, m->len);
    return TRUE;
}


/* 
 * We simulate broadcast by having ARP perform a callback
 * for every host it has in its table.  We then do a direct send for
 * each ARP entry.
 */
PRIVATE PROCEDURE
writeBcast( ps, buf, len )
    PState	*ps;
    char	*buf;
    int		len;
{
    FDDIMsg	m;
    ArpForEach	afe;
    
    if ( ! ps->arp ) {
	xError("FDDI bcast write fails -- no arp protocol");
	return;
    }
    m.buf = buf;
    m.len = len;
    m.sock = ps->sock;
    afe.f = arpForEachFunc;
    afe.v = &m;
    xControlProtl(ps->arp, ARP_FOR_EACH, (char *)&afe, sizeof(ArpForEach));
}	


PRIVATE PROCEDURE
sendOnSocket( sock, dest, buf, len )
    int		sock;
    FDDIhost	*dest;
    char	*buf;
    int		len;
{  
    extern unsigned long inet_addr();
    struct  sockaddr_in	addr;
    int bytesSent;

    simfddi2sock(*dest, &addr);
    xTrace3(simfddip, TR_FUNCTIONAL_TRACE,
	    "sendOnSocket: sending %d bytes to <%d,%s>",
	    len, ntohs(addr.sin_port), inet_ntoa(addr.sin_addr));

   
        bytesSent = sendto(sock, buf, len, 0,
             (struct sockaddr *)&addr, sizeof(struct sockaddr));
        if (bytesSent != len)
	  {

#ifdef PACKET_STATS
	    xLockAcquire(packetLock);
	    sendErrorCount++ ;
	    xLockRelease(packetLock);
#endif
	    
            xTrace2(simfddip, TR_ERRORS,
                "sendOnSocket: sendto bytesSent %d, errno %d",
                bytesSent, errno);
	  }
        else
	  {
#ifdef PACKET_STATS
	    xLockAcquire(packetLock);
	    sentPackets ++;
	    xLockRelease(packetLock);
#endif
	  }
  }

PRIVATE int
read_fddi( sock, msg, len )
    int    sock, len;
    char   *msg;
{
    struct sockaddr_in	from;
    int			size, n;
    
    xTrace1(simfddip, TR_EVENTS, "read_fddi from socket %d", sock);
    size = sizeof(from);
    if ((n = recvfrom(sock, msg, len, 0, (struct sockaddr *)&from, &size)) < 0)
    {
      xTrace2(simfddip, TR_ERRORS, "Got %d return, %d errno from recvfrom", 
        n, errno);
      return -1;
    }
#ifdef PACKET_STATS
    xLockAcquire(packetLock);
    recvPackets++;
    xLockRelease(packetLock);
#endif /* PACKET_STATS */
    xTrace3(simfddip, TR_FUNCTIONAL_TRACE,
	    "read_fddi: receiving %d bytes from <%d,%s>",
	    n, ntohs(from.sin_port), inet_ntoa(from.sin_addr));
    return n;
}


PRIVATE int
initSocket( port )
    int	port;
{
    int			s, bufSize, bufSizeSize;
    struct sockaddr_in	addr;
    int			on = 1;

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons((u_short)port);
    if ( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	Kabort("init_fddi: cannot open socket");
    }	
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on));
#ifdef SO_BROADCAST
    setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof (on));
#endif
    /*
     * Set and get receive buffer size ...
     */
    bufSize = RCVBUFSIZE;
    bufSizeSize = sizeof(bufSize);
    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&bufSize,
		   sizeof(bufSize))) {
	xTrace1(simfddip, TR_ERRORS,
		"Could not set size of FDDI receive buffer to %d",
		bufSize);
    }
    if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&bufSize, &bufSizeSize)) {
	xTrace0(simfddip, TR_ERRORS,
		"Could not get size of FDDI receive buffer");
    } else {
	xTrace1(simfddip, TR_GROSS_EVENTS,
		"Receive buffer of FDDI socket: %d", bufSize);
    }
    /*
     * Set and get send buffer size ...
     */
    bufSize = SNDBUFSIZE;
    bufSizeSize = sizeof(bufSize);
    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&bufSize,
		   sizeof(bufSize))) {
	xTrace1(simfddip, TR_ERRORS,
		"Could not set size of FDDI send buffer to %d",
		bufSize);
    }
    if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&bufSize, &bufSizeSize)) {
	xTrace0(simfddip, TR_ERRORS,
		"Could not get size of FDDI send buffer");
    } else {
	xTrace1(simfddip, TR_GROSS_EVENTS,
		"Send buffer of FDDI socket: %d", bufSize);
    }
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr))) {
	Kabort("init_fddi: cannot bind socket");
    }
    xTrace1(simfddip, TR_GROSS_EVENTS,
		"initSocket returning %d", s);
    return s;
}



static XkReturn
readPortRom( self, str, nFields, line, arg )
    Protl	self;
    char	**str;
    int		nFields, line;
    VOID	*arg;
{
    PState	*ps = (PState *)self->state;
    return sscanf(str[2], "%d", &ps->port) >= 1 ? XK_SUCCESS : XK_FAILURE;
}


/* 
 * Backward compatibility ... no opcode field
 */
static XkReturn
readPortBackRom( self, str, nFields, line, arg )
    Protl	self;
    char	**str;
    int		nFields, line;
    VOID	*arg;
{
    PState	*ps = (PState *)self->state;
    return sscanf(str[1], "%d", &ps->port) >= 1 ? XK_SUCCESS : XK_FAILURE;
}


PUBLIC PROCEDURE
simfddi_init( self )    /* Changed from simeth_init for first build */
    Protl      self;
{
    struct hostent	*h;
    IPhost 		*in;
    char		name[100];
    char                fddiname[100];
    int			namelen=100;
    PState		*ps;
    
    xTrace0(simfddip, TR_MAJOR_EVENTS, "simfddi_init");
    if ( instance > SIMFDDI_MAX_INSTANCES ) {
	Kabort("simfddi_init -- too many instances");
    }
    ps = X_NEW(PState);
    bzero((char *)ps, sizeof(PState));
    self->state = (PTR)ps;
    ps->mtu = FDDI_MTU_SIZE;   /* see sim_fddi_i.h */

    ps->port = -1;
    findProtlRomOpts(self, protlOpts, sizeof(protlOpts)/sizeof(ProtlRomOpt), 0);
    if ( ps->port == -1 ) {
	xTrace1(simfddip, TR_ERRORS,
		"No port specified for simfddi instance %d", instance);
	Kabort("simfddi -- port specification error");
    }
    xTrace1(simfddip, TR_MAJOR_EVENTS,
	    "simfddi_init: listening on port %d", ps->port);
    if ( (ps->sock = initSocket(ps->port)) == 0 ) {
	Kabort("simfddi_init: problems creating socket");
    }

    /* 
     * Determine my host address on FDDI
     */
    gethostname(name,namelen);
    strcpy( fddiname, IRIX_FDDI_HOSTNAME_PREFIX );
    strcat( fddiname, name );
    xTrace1(simfddip, TR_EVENTS, "FDDI hostname = %s", fddiname);

    h = gethostbyname(fddiname);
    in = (IPhost *) h->h_addr;
    xTraceP1(self, TR_DETAILED, "real IP host = %s", ipHostStr(in));
    
    sock2simfddi(&ps->myHost, *in, ps->port);
    xTrace1(simfddip, TR_GROSS_EVENTS,
	    "simfddi_init: FDDI started with address %s",
	    fddiHostStr(&ps->myHost));

    self->push = simfddiPush;
    self->controlprotl = simfddiControl;
    self->openenable  = simfddiOpenEnable;
    self->up = 0;
    if ( instance == 0 ) {
	init_fddi_warm_pool(self);		
    }
    instance++;
#ifdef PACKET_STATS
    packetLock = xLockAllocate();
    /* NO ... evDetach(evSchedule( printPacketStats, 0, 30*1000*1000)); */
#endif /* PACKET_STATS */
}


PRIVATE PROCEDURE
fddiMsgStore( hdr, netHdr, len, arg )
    PTR       hdr;
    char      *netHdr;
    long      len;
    PTR       arg;
{
    xAssert(len == sizeof(FDDIhdr));
    /*
     * Leave the OrgCode as an uninterpreted byte sequence.
     */
    ((FDDIhdr *)hdr)->llc.EtherType = htons(((FDDIhdr *)hdr)->llc.EtherType); 
    bcopy(hdr, netHdr, sizeof(FDDIhdr));
}


PRIVATE long
fddiMsgLoad( hdr, netHdr, len, arg )
    PTR      hdr;
    char     *netHdr;
    long     len;
    PTR      arg;
{
    xAssert(len == sizeof(FDDIhdr));
    bcopy(netHdr, (char *)hdr, sizeof(FDDIhdr));
    /*
     * Leave the OrgCode as an uninterpreted byte sequence.
     */
    ((FDDIhdr *)hdr)->llc.EtherType = ntohs(((FDDIhdr *)hdr)->llc.EtherType);
    return sizeof(FDDIhdr);
}


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

    xTrace0(simfddip, TR_EVENTS, "simfddiPush");
    xAssert(hdr);

    buf = msgPush(msg, sizeof(FDDIhdr));
    xAssert(buf);
    fddiMsgStore(hdr, buf, sizeof(FDDIhdr), 0);

    if ( (len = msgLength(msg)) > MAX_FDDI_PACKET_SIZE ) {
	xTrace2(simfddip, TR_SOFT_ERRORS,
		"simfddi driver: msgLength (%d) is larger than max (%d)",
		len, MAX_FDDI_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);

    if ( FDDI_ADDRESS_ISBROAD(hdr->dst) ) {
	writeBcast(ps, buffer, len);
    } else {
	sendOnSocket(ps->sock, &hdr->dst, buffer, len);
    }
    return XMSG_NULL_HANDLE;
}    


PRIVATE PROCEDURE
internalDemux(self, msg)
    Sessn self;
    Msg *msg; 
{
    FDDIhdr	hdr;
    void       *buf;
    
    xTrace0(simfddip, TR_EVENTS, "in FDDI internal demux");

    buf = msgPop(msg, sizeof(FDDIhdr));
    if (buf == NULL) {
	xTrace0(simfddip, TR_SOFT_ERRORS,
		"simfddi internalDemux: incoming message too small!");
	return;
    }
    fddiMsgLoad(&hdr, buf, sizeof(FDDIhdr), 0);

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


/*
 * warm_pool_handler:
 * init message struct;
 * forever
 *     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(simfddip, TR_EVENTS, "warm_pool_handler %d:initializing",
            my_process->pid);
   
    /* tell the init thread we're alive */
    usvsema(init_semaphore);

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

    forever {

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

	uspsema(warm_pool_semaphore);

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

        if ((buflen = read_fddi(ps->sock,bufPtr,MAX_FDDI_PACKET_SIZE)) 
            == -1) {
            Kabort("warm_pool_handler: read_fddi failed");
        }

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

        xTrace1(simfddip, TR_EVENTS, "warm_pool_handler %d:unblocking next",
            my_process->pid);
	usvsema(warm_pool_semaphore);
    
        /*
         * Adjust message length (outside of the master lock?)
         */
        msgTruncate(&msg,(long)buflen);

	xk_master_lock();

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

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

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

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

PRIVATE PROCEDURE
init_fddi_warm_pool(self)
Protl self;
{
    int 	i;
    Process	*first_process;
    static int	initialized = 0;
    
    if ( initialized ) {
	xTrace0(simfddip, TR_EVENTS, 
                "simfddi process pool already initialized");
	return;
    }
    xTrace0(simfddip, TR_EVENTS, "init_fddi_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_FDDI_WARM_POOL; i++)
        evDetach( evSchedule(warm_pool_handler, (PTR) self, 0));

    xTrace0(simfddip, TR_EVENTS, "init_fddi_warm_pool:waiting");

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

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

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

    xTrace0(simfddip, TR_EVENTS, "init_fddi_warm_pool:finished");
}

PRIVATE XkReturn
simfddiOpenEnable( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part	*p;
{
    if ( self->up ) {
	xError("simfddiOpenEnable called multiple times!");
	return XK_FAILURE;
    }
    self->up = hlp;
    return XK_SUCCESS;
}


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

    switch (op) {

      case MAC_REGISTER_ARP:
	/* 
	 * ARP registers itself with us so we can ask it to perform
	 * callbacks in order to simulate hardware broadcast.  
	 */
	checkLen(len, sizeof(Protl));
	ps->arp = *(Protl *)buf;
	return 0;

      /*
       * case MAC_IS_REAL_DRIVER:
       *   checkLen(len, sizeof(bool));
       *   (*(bool *)buf) = FALSE;
       *   return sizeof(bool);
       */

      case MAC_DUMP_STATS:
#ifdef SIMFDDI_STATS	
	{
	    xTrace0(simfddip, TR_ALWAYS, "SIMFDDI statistics:");
	    xTrace3(simfddip, TR_ALWAYS,
		    "\tthreads: %d\tactive: %d\thigh-water: %d",
		    MAX_FDDI_WARM_POOL, activeThreads, activeHighWater);
	}
#endif
#ifdef PACKET_STATS
        xLockAcquire(packetLock);
        printf( "\nSIMFDDI 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 */
        return 0;

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

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

      case SIM_SOCK2ADDR:
	{
	    SimAddrBuf *aBuf = (SimAddrBuf *)buf;

	    checkLen(len, sizeof(SimAddrBuf));
	    sock2simfddi((FDDIhost *)&aBuf->genericHost, aBuf->ipHost, 
	                 aBuf->udpPort); 
	    return sizeof(SimAddrBuf);
	}
 
      default:
	return -1;

    }
}
    
