/*
 * $RCSfile: irix_fddi.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993 Massachusetts Board of Regents
 *
 * $Log: irix_fddi.c,v $
 * Revision 1.2  1996/01/30 20:48:13  slm
 * Updated version.
 *
 * Revision 1.1  1995/07/29  02:36:39  slm
 * Initial revision
 *
 * Revision 1.7.1.1.1.3  1994/11/26  00:22:29  hkaram
 *  msgRefresh replaces msgConstructAllocate
 *
 * Revision 1.7.1.1.1.2  1994/11/22  22:02:40  hkaram
 * Converted to new msg tool
 *
 * Revision 1.7.1.1.1.1  1994/11/12  19:06:36  hkaram
 * New branch
 *
 * Revision 1.7.1.1  1994/03/14  20:21:41  menze
 *   [ 1994/03/14          umass ]
 *   changes to thread management and statistics
 *
 * Revision 1.7  1994/02/05  00:33:53  menze
 *   [ 1994/02/03          menze ]
 *   Converted to use rom-option library
 *   Removed unused variables
 *
 * Revision 1.6  1993/12/16  02:00:11  menze
 * Removed implementation of SIM2SOCKADDR control op
 *
 * Revision 1.5  1993/11/13  00:45:29  menze
 * Original version from UMass
 */

#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <sys/fddi.h>
#include <netinet/in.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 "fddi.h"
#include "fddi_i.h"
#include "irix_fddi_i.h"
#include "mac_common.h"
#include "arp.h"
#include "platform.h"	/* For queue and process routines */
#include "modeldep.h"   /* For SGI machine dependent stuff */
#include "xk_lock.h"

#define  STRING_SIZE          256
#define  MAX_UPPER_PROTOCOLS  30

int	 traceirixfddip;

#ifdef  XK_DEBUG
#define IRIXFDDI_STATS
#define PACKET_STATS
#else
#undef  IRIXFDDI_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 void *          packetLock;   /* also for statistics */
#endif  /* PACKET_STATS */

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

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

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

PRIVATE XkReturn  getHostFddiAddress( OUT FDDIhost * );
PRIVATE PROCEDURE 	warm_pool_handler _ANSI_ARGS_(( Event, PTR ));
PRIVATE PROCEDURE 	init_fddi_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 *, FDDIhost *, 
                                                   char *, int ));
PRIVATE int		irixfddiControl _ANSI_ARGS_(( Protl, int, char *, 
                                                      int ));
PRIVATE XkReturn	irixfddiOpenEnable _ANSI_ARGS_(( Protl, Protl, Protl, 
                                                         Part * ));
PRIVATE XkHandle	irixfddiPush _ANSI_ARGS_(( Sessn, Msg * ));

#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[] = {
    { "", 3, readPortsRom },
};



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

PRIVATE XkReturn
getHostFddiAddress( OUT addressPtr )
    FDDIhost *addressPtr;
{
    XkReturn returnValue;
    char fullHostName[MAXHOSTNAMELEN];
    char *hostName;
    char *fddiString = IRIX_FDDI_HOSTNAME_PREFIX;
    char *addressesFilename = "/etc/ethers";
    FILE *addressesFile;
    bool found;
    int i;
    /*
     * currentString[] string is the string read from the addressesFile.
     */
    char currentString[STRING_SIZE];
    char *postfix;         /* host name in addressesFile */
    char *returnPtr;       /* return value from fgets() */
  
    returnValue = XK_FAILURE;
    if( gethostname( fullHostName, MAXHOSTNAMELEN ) != 0 )
    {
      
      xTrace1(irixfddip, TR_ERRORS,
              "getHostFddiAddress: error %d calling gethostname()", errno);
      return( returnValue );
    }
    else
    {
      xTrace1(irixfddip, TR_MAJOR_EVENTS,
              "getHostFddiAddress: full host name = %s", fullHostName);
      hostName = fullHostName;
      /*
       * Eliminate fddiString prefix, if any, from host name.
       */
      if( strstr( fddiString, fullHostName ) == hostName )
      {
        hostName = &hostName[strlen(fddiString)];
      } 
    }
    /*
     * Truncate domain name, if any, from host name.
     */
    for( i = 0; hostName[i] != 0; i++ )
    {
      if( hostName[i] == '.' || isspace(hostName[i]) )
      {
        hostName[i] = 0;
        break;
      }
    }
    xTrace1(irixfddip, TR_MAJOR_EVENTS,
            "getHostFddiAddress: abbreviated host name = %s", hostName);
  
    addressesFile = fopen( addressesFilename, "r" );
    currentString[0] = (char)0;
    if( addressesFile != NULL )
    {
      found = FALSE;
      while( !found )
      {
        returnPtr = fgets( currentString, STRING_SIZE, addressesFile ); 
        if( returnPtr == (char *)NULL )
        {
          xTrace1(irixfddip, TR_ERRORS,
              "getHostFddiAddress: end of file %s found", addressesFilename );
          break;
        }
        else
        {
          postfix = strstr( currentString, fddiString );
          if( postfix != NULL )
          {
            postfix = &postfix[strlen(fddiString)];
            /*
             * Truncate domain name, if any, from host name.
             */
            for( i = 0; postfix[i] != 0; i++ )
            {
              if( postfix[i] == '.' || isspace(postfix[i]) )
              {
                postfix[i] = 0;
                break;
              }
            }
            if( strcmp( hostName, postfix ) == 0 )
            {
              /*
               * We have a match - extract the MAC address.
               */
              for( i = 0; currentString[i] != 0; i++ )
              {
                if( currentString[i] != ':' &&
                    !isxdigit( currentString[i] ) )
                {
                  currentString[i] = (char)0;
                  if( str2fddiHost( addressPtr, currentString ) == XK_SUCCESS )
                  {
                    found = TRUE;
                    returnValue = XK_SUCCESS;
                    xTrace1(irixfddip, TR_MAJOR_EVENTS,
                      "getHostFddiAddress: MAC address is %s", currentString );
                    break;
  	        }
  	      }
              } /* for */
            }
          }
        }
      } /* while */
    }
    else
    {
      xTrace1(irixfddip, TR_ERRORS,
              "getHostFddiAddress: open of %s failed", addressesFilename );
    }
    return( returnValue );
}

#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; 
    FDDIhost    *dest;
    char        *buf;
    int         len;
{  
    unsigned char *bytePtr;
    int i;
    int rv;
    
    xTrace3(irixfddip, TR_FUNCTIONAL_TRACE,
	    "sendOnSocket: socket = %d - sending %d bytes to <%s>",
	    ps->sendSocket, len, fddiHostStr(dest));
    if( len < MIN_SEND_FDDI_SIZE ) {
        len = MIN_SEND_FDDI_SIZE;
    }
    rv = write( ps->sendSocket, buf, len );
    if (rv != len) {
	xTrace1(irixfddip, TR_ERRORS, "sendOnSocket: write rv %d", rv);
    }
#ifdef PACKET_STATS
    xLockAcquire(packetLock);
    sentPackets ++;
    if( rv != len ) {
        sendErrorCount ++;
    }
    xLockRelease(packetLock);
#endif /* PACKET_STATS */

}

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

    do
    {
        xTrace1(irixfddip, TR_EVENTS,
                "readFromSocket: socket = %d", ps->recvSocket);
        if ((n = read(ps->recvSocket, msg, len)) < 0)
        {
          perror((char *)0);
          xTrace1(irixfddip, TR_ERRORS, 
                  "readFromSocket: got %d return read", n);
          return -1;
        }
    }
    while( !(ps->isValidPacket)( ps, (recvHeader *)msg ) );

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

    xTrace2(irixfddip, TR_FUNCTIONAL_TRACE,
	    "readFromSocket: receiving %d bytes from <%s>", n,
	    fddiHostStr((FDDIhost *)
                        ((recvHeader *)msg)->link_header.fddi_mac.mac_sa.b));
    return n;
}

/*
 * Functions when using Snoop.
 */

bool 
isValidPacketOneFilter( ps, packetPtr )
    PState *ps;
    recvHeader *packetPtr;
{
    return( !(packetPtr->packetInfo.snoop_flags & ps->errorMask) &&
             ((bcmp( (void *)packetPtr->link_header.fddi_mac.mac_da.b, 
                     (void *)&ps->myHost, MAC_ADDRESS_SIZE ) == 0) || 
              (bcmp( (void *)packetPtr->link_header.fddi_mac.mac_da.b, 
                     (void *)&fddiBroadcastHost, MAC_ADDRESS_SIZE ) == 0)) );
}

bool 
isValidPacketTwoFilters( ps, packetPtr )
    PState *ps;
    recvHeader *packetPtr;
{
    return !(packetPtr->packetInfo.snoop_flags & ps->errorMask);
}

bool 
defaultIsValidPacket( ps, packetPtr )
    PState *ps;
    recvHeader *packetPtr;
{
    return( !(packetPtr->packetInfo.snoop_flags & ps->errorMask) &&
             ((bcmp( (void *)packetPtr->link_header.fddi_mac.mac_da.b, 
                     (void *)&ps->myHost, MAC_ADDRESS_SIZE ) == 0) || 
              (bcmp( (void *)packetPtr->link_header.fddi_mac.mac_da.b, 
                     (void *)&fddiBroadcastHost, MAC_ADDRESS_SIZE ) == 0)) );
}

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_SNOOP;
    int            bufSize;
    int            bufSizeSize;
    struct utsname name;
    long           ulp;
    long           ulpEthernetType;
    char           interfaceName[80];
    ETHtype        ethernetType[MAX_UPPER_PROTOCOLS];
    int            count;
    int            myUserId;
    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(irixfddip, TR_MAJOR_EVENTS,
                  "protocol %s (Ethernet type %XH) configured above FDDI", 
                  upperProtocols[i], ulpEthernetType);
          ethernetType[count++] = ulpEthernetType;
          xAssert(count <= MAX_UPPER_PROTOCOLS);
        }
        else
        {
          xTrace1(irixfddip, TR_GROSS_EVENTS,
              "irixfddi -- protocol %s NOT configured above FDDI", 
              upperProtocols[i]);
        }
      }
      else
      {
        xTrace1(irixfddip, TR_GROSS_EVENTS, 
            "irixfddi -- protocol graph not configured with %s", 
            upperProtocols[i]);
      }
    }
    /*
     * Get the FDDI interface name based on the type of machine we
     * are running on.
     */
    uname( &name );
    if( (strcmp( name.machine, INDIGO_MACHINE_NAME ) == 0) ||
        (strcmp( name.machine, INDIGO4000_MACHINE_NAME ) == 0) )
    {
#ifdef WORKS_FDDI_301
      ps->isValidPacket = isValidPacketOneFilter;
#else
      ps->isValidPacket = isValidPacketTwoFilters;
#endif
      strcpy( interfaceName, SGI_FDDI_GIO32 );
    }
    else if( (strcmp( name.machine, POWER_SERIES400_MACHINE_NAME ) == 0) ||
             (strcmp( name.machine, POWER_SERIES300_MACHINE_NAME ) == 0) ||
             (strcmp( name.machine, POWER_SERIES200_MACHINE_NAME ) == 0)
           )
    {
      ps->isValidPacket = isValidPacketTwoFilters;
      strcpy( interfaceName, SGI_FDDI_VME );
    }
    else if( strcmp( name.machine, CHALLENGE_MACHINE_NAME ) == 0 )
    {
      ps->isValidPacket = isValidPacketTwoFilters;
      strcpy( interfaceName, SGI_FDDI_VME );
    }
    else
    {
      xTrace1(irixfddip, TR_ERRORS,
              "irixfddi -- SGI machine type %s not supported", name.machine);
      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(irixfddip, TR_ERRORS,
		"Could not get size of FDDI send buffer");
    } else {
	xTrace1(irixfddip, TR_GROSS_EVENTS,
		"Send buffer size for FDDI 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(irixfddip, TR_ERRORS,
		"Could not get size of FDDI receive buffer");
    } else {
	xTrace1(irixfddip, TR_GROSS_EVENTS,
		"Receive buffer size for FDDI socket: %d", bufSize);
    }
    beMeAgain( myUserId );

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



PRIVATE 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
irixfddi_init( self )
    Protl      self;
{
    struct hostent	*h;
    struct in_addr 	*in;
    char		name[100];
    int			namelen=100;
    PState		*ps;
    
    xTrace0(irixfddip, TR_MAJOR_EVENTS, "irixfddi_init");
    if( instance > IRIXFDDI_MAX_INSTANCES ) {
	Kabort("irixfddi_init -- too many instances");
    }
    /*
     * Check that recvHeader type definition does not need padding.
     */
    xAssert(HEADERPAD == 0);

    ps = X_NEW(PState);
    bzero( (char *)ps, sizeof(PState) );
    ps->mtu = FDDI_MTU_SIZE;   /* see irix_fddi_i.h */
    ps->errorMask = SN_ERROR;
    ps->isValidPacket = defaultIsValidPacket;
    self->state = (PTR)ps;
    ps->sendPort = ERROR_PORT;
    ps->recvPort = ERROR_PORT;
    findProtlRomOpts(self, protlOpts, sizeof(protlOpts)/sizeof(ProtlRomOpt), 0);
    if ( ps->sendPort == ERROR_PORT || ps->recvPort == ERROR_PORT ) {
	xTrace3(irixfddip, TR_ERRORS,
		"Bad port (%d and/or %d) specified for irixfddi instance %d", 
                ps->sendPort, ps->recvPort, instance);
	Kabort("irixfddi -- port specification error");
    }
    /* 
     * Determine my host address
     */
    if( getHostFddiAddress( &ps->myHost ) == XK_SUCCESS )
    {
        xTrace1(irixfddip, TR_GROSS_EVENTS,
	        "irixfddi_init: FDDI started with address %s",
	        fddiHostStr(&ps->myHost));
    }
    else
    {
        Kabort("irixfddi_init -- could not get FDDI address");
    }

    self->push = irixfddiPush;
    self->controlprotl = irixfddiControl;
    self->openenable  = irixfddiOpenEnable;
    self->up = 0;
    instance++;

#ifdef PACKET_STATS
    packetLock = xLockAllocate();
    /* NO ... evDetach(evSchedule( printPacketStats, 0, 30*1000*1000)); */
#endif /* PACKET_STATS */
}


PRIVATE PROCEDURE
fddiMsgStore( 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.
     */
    ((FDDIhdr *)hdr)->llc.EtherType = htons(((FDDIhdr *)hdr)->llc.EtherType); 
    bcopy(hdr, (void *)netHdr->link_header.fddi_mac.mac_da.b, sizeof(FDDIhdr));
    netHdr->link_header.fddi_mac.mac_bits = FDDI_MAC_BITS;
    netHdr->link_header.fddi_mac.mac_fc = FDDI_FRAME_CONTROL;
}


PRIVATE long
fddiMsgLoad( 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));
    xAssert((netHdr->link_header.fddi_mac.mac_fc & FDDI_FRAME_CONTROL) != 0);
    bcopy((void *)netHdr->link_header.fddi_mac.mac_da.b, hdr, sizeof(FDDIhdr));
    /*
     * Leave the OrgCode as an uninterpreted byte sequence.
     */
    ((FDDIhdr *)hdr)->llc.EtherType = ntohs(((FDDIhdr *)hdr)->llc.EtherType);
    /*
     * FOR TESTING!!!
     * xTrace0(irixfddip, TR_ALWAYS, "fddiMsgLoad header");
     * bytePtr = (unsigned char *)hdr;
     * for( i = 0; i < sizeof(FDDIhdr); i++ )
     * {
     *   printf( "%2X ", (int)bytePtr[i] );
     * }
     * printf( "\n" );
     */
    return sizeof(recvHeader);
}


PRIVATE XkHandle
irixfddiPush( self, msg )
    Sessn	self;
    Msg 	*msg;
{
    FDDIhdr	*hdr = msgGetAttr(msg, 0);
    char	buffer[MAX_SEND_FDDI_SIZE];
    char	*bufPtr = buffer;
    int		len;

    PState	*ps = (PState *)self->state;
    void        *buf;
    MsgWalk  mi;
    u_char      *data;
    int         dataLen;
    xTrace0(irixfddip, TR_EVENTS, "irixfddiPush");
    xAssert(hdr);

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

    if ( (len = msgLength(msg)) > MAX_SEND_FDDI_SIZE ) {
	xTrace2(irixfddip, TR_SOFT_ERRORS,
		"irixfddi driver: msgLength (%d) is larger than max (%d)",
		len, MAX_SEND_FDDI_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);

#ifdef PACKET_STATS
    if ( FDDI_ADDRESS_ISBROAD(hdr->dst) ) {
	xLockAcquire(packetLock);
	bcastSentPackets++;
	xLockRelease(packetLock);
    }
#endif /* PACKET_STATS */
    /*
     * No need to check for the broadcast special case using 
     * FDDI_ADDRESS_ISBROAD() now.
     */
    sendOnSocket(ps, &hdr->dst, buffer, len);
    return XMSG_NULL_HANDLE;
}    


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

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

    if ( ! self->up ) {
	xTrace0(irixfddip, TR_ERRORS, 
                "irixfddi internalDemux: no upper protocol!");
	return;
    }
    msgSetAttr(msg, 0, &hdr, sizeof(FDDIhdr));
    xDemux(xGetUp(self), self, msg);
    xTrace0(irixfddip, 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;
    Process     *my_process;
    Sessn 	self = (Sessn) arg;
    PState	*ps = (PState *)self->state;

    /*
     * We must be root to open raw sockets.
     * Do we need to be root? NO!
     * beRoot();
     */
    my_process = process_self();
    xAssert(my_process);
    xTrace1(irixfddip, TR_EVENTS, "warm_pool_handler %d:initializing",
            my_process->pid);

    /* tell the init thread we're alive */
    usvsema(init_semaphore);

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

    forever {

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

        uspsema(warm_pool_semaphore);

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

        if ((buflen = readFromSocket(ps, bufPtr, MAX_RECV_FDDI_SIZE)) 
              == -1) {
            Kabort("irixfddi warm_pool_handler: readFromSocket failed");
        }
        xTrace1(irixfddip, TR_EVENTS, "warm_pool_handler %d:have read FDDI",
            my_process->pid);

        xTrace1(irixfddip, 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(irixfddip, TR_EVENTS, "warm_pool_handler %d:got lock",
            my_process->pid);

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

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

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

PRIVATE PROCEDURE
init_fddi_warm_pool(self)
Protl self;
{
    int 	i;
    static int	initialized = 0;
    
    if ( initialized ) {
	xTrace0(irixfddip, TR_EVENTS, 
                "irixfddi process pool already initialized");
	return;
    }
    xTrace0(irixfddip, 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(irixfddip, 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(irixfddip, TR_EVENTS, "init_fddi_warm_pool:finished");
}

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

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

    return XK_SUCCESS;
}


PRIVATE int
irixfddiControl( 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 simfddi and irixfddi.  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_IS_REAL_DRIVER:
       *   checkLen(len, sizeof(bool));
       *   (*(bool *)buf) = TRUE;
       *   return sizeof(bool);
       */

      case MAC_DUMP_STATS:
#ifdef IRIXFDDI_STATS	
	{
	    xTrace0(irixfddip, TR_ALWAYS, "IRIXFDDI statistics:");
	    xTrace3(irixfddip, TR_ALWAYS,
		    "\tthreads: %d\tactive: %d\thigh-water: %d",
		    MAX_FDDI_WARM_POOL, activeThreads, activeHighWater);
	}
#endif
#ifdef PACKET_STATS
        xLockAcquire(packetLock);
        printf( "\nIRIXFDDI 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);
        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));

      default:
	return -1;

    }
}
