/*            
 *            Copyright (c) 1998-2000 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 227                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 *
 *     Module Name: eth_stack.c
 *         Version: 1.00
 *   Original Date: 10-28-2002
 *          Author: Julia Rakhvalska
 *        Language: Ansi C
 * Compile Options: 
 * Compile defines: 
 *       Libraries: 
 *    Link Options: 
 *
 *    Entry Points: eth0_init()
 *                  eth0_updown()
 *                  eth0_start()
 *                  eth0_ioctl()
 *                  eth_start()
 *                  eth_restart()
 *                  eth_msg_allocate()
 *                  eth_msg_free()
 *                  eth_recv_up()
 *                  eth_fastip_msg_allocate()
 *                  eth_fastip_msg_init()
 *                  eth_fastip_msg_free()
 *                  eth_fastip_msg_avail()
 *                  
 *
 * Description.
 * =======================================================================
 * This file contains the TCP/IP stack dependant routines for the Ethernet driver.
 *                                                                        
 *                                                                        
 * Edit Date/Ver   Edit Description
 * ==============  ===================================================
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* fusion */
#include "std.h"
#include "nerrno.h"
#include "netioc.h" 
#include "netdev.h"
#include "socket.h"
#include "m.h"
#include "q.h"
#include "flags.h"
#include "enet.h"
#include "in.h"

#include "Npttypes.h"
#include "NA_ISR.h"
#include "Efe_def.h"
#include "eth.h"
#include "fip.h"
#include "eth_stack.h"
#include "tx_api.h"

#define MAX_PACKET_TYPES        10
#define DONT_CARE               0
#define IP_FRAME_TYPE           0x800
#define ARP_FRAME_TYPE          0x806
#define RARP_FRAME_TYPE         0x8035

#ifdef PPPOE_PROTOCOL
#define PPPOE_DIS_FRAME_TYPE    0x8863
#define PPPOE_SES_FRAME_TYPE    0x8864
#endif
#define UDP_PACKET_TYPE         0x11
#define IP_PACKET_TYPE_OFFSET   23

#define GET_SEMAPHORE       if (tx_semaphore_get(&packetTypeSemaphore, TX_WAIT_FOREVER) != TX_SUCCESS) customizeErrorHandler(ERROR_ETHERNET_DRIVER, ERROR_ETHERNET_SEMAPHORE);
#define PUT_SEMAPHORE       if (tx_semaphore_put(&packetTypeSemaphore) != TX_SUCCESS) customizeErrorHandler(ERROR_ETHERNET_DRIVER, ERROR_ETHERNET_SEMAPHORE);

netdev *eth0_ndp = NULL;

extern int customizePhyReset(void); /* 06/03/03 */

/**** Static definitions ****/

static int fip_termfn(struct m *mp);        /* Fast Ip message terminate function */
static BOOLEAN tcpipRecv(m *mp);
static int get_packet_buffer_cnt(const m *mp);


static BOOLEAN isIpUp = FALSE;

static ethPacketType packetType[MAX_PACKET_TYPES];
static int packetTypeCount = 0;

static ethReceiveFnType unclaimedPacketFn = NULL;

/**
 * The Ethernet API is used to register protocol stacks with the Ethernet
 * driver, and to transmit packets.  Protocol stacks are registered with
 * the NAEthAddPacketType() function.  This function specifies an Ethernet
 * frame type, protocol ID, and a function to receive packets.  All packets
 * with the matching frame type and protocol ID are passed to the protocol
 * stack after the function completes.
 *
 *
 * Packet filters can also be specified when a protocol stack is registered.
 * Packet filters specify additional criteria for incoming packets.  Packets 
 * that match these criteria are discarded even if they are addressed to a
 * registered protocol stack.  This feature is usually used to filter out
 * unwanted broadcast and multicast packets
 *
 * The API also has functions to transmit raw Ethernet packets.
 *
 *
 * @category EthernetDriver
 * @since 6.0
 * @overview Ethernet Driver
 * @external
 */


/**
 * This structure defines the IP packet type.  It includes a filter for UDP
 * broadcast packets.  By default, the filter is disabled.  The function
 * NABlockUDPBroadcasts() enables the filter by setting the filterCount
 * field to 1.
 *
 * @category EthernetDriver 
 * @since 6.0
 */
static ethPacketType ipPacketType =
{
    ethEthernetII,
    IP_FRAME_TYPE,
    tcpipRecv,
    {
        {
            /*
             * This filter blocks UDP broadcast packets.
             */
            {
                {
                    {UDP_PACKET_TYPE},
                    1,
                    IP_PACKET_TYPE_OFFSET,
                    FALSE
                }
            },
            1,
            FALSE,
            TRUE
        }
    },
    /*
     * This is the filter count.  NABlockUDPBroadcasts() will set it
     * to 1 to enable UDP broadcast filtering.
     */
    0 
};

/*
 * Defines the ARP packet type
 *
 */
static ethPacketType arpPacketType =
{
    ethEthernetII,
    ARP_FRAME_TYPE,
    tcpipRecv
};



/*
 * This structure defines the RARP packet type.  
 *
 */
static ethPacketType rarpPacketType =
{
    ethEthernetII,
    RARP_FRAME_TYPE,
    tcpipRecv
};

#ifdef PPPOE_PROTOCOL
/*
 * This structure defines the PPPoE Discovery Phase Packet packet type.  
 *
 */
static ethPacketType pppoeDisPacketType =
{
    ethEthernetII,
    PPPOE_DIS_FRAME_TYPE,
    tcpipRecv
};
/*
 * This structure defines the PPPoE Session Phase Packet packet type.  
 *
 */
static ethPacketType pppoeSesPacketType =
{
    ethEthernetII,
    PPPOE_SES_FRAME_TYPE,
    tcpipRecv
};
#endif
/**
 * Protects access to Ethernet driver variables
 *
 * This semaphore protects access to the variables packetType,
 * packetTypeCount, and unclaimedPacketFn.
 *
 * @category EthernetDriver  
 * @since 6.0
 */
TX_SEMAPHORE packetTypeSemaphore;

int ndq_start(m *packet);
int ndq_restart(netdev *pnetdev);
m * ndq_get_xmit_mp(netdev * ndp);




/**
 * Registers a function to receive unaccepted packets
 *
 * This function registers a function to receive Ethernet packets that are not accepted
 * by any registered protocol stacks.  Call this function with a NULL pointer to
 * unregister.  Call this function only after you have called eth0_init().
 *
 * @external
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param recvFn    Pointer to the function that is to receive unclaimed packets.
 *
 * @see @link ethReceiveFnType
 */
void NAEthRegisterUnclaimedPacketFn(ethReceiveFnType const recvFn)
{
    GET_SEMAPHORE;
    unclaimedPacketFn = recvFn;
    PUT_SEMAPHORE;
}



/**
 * Controls filter of UDP broadcasts.
 *
 * This function controls whether User Datagram Protocol broadcasts will be 
 * filtered out by the 
 * Ethernet driver.  Filtering can improve performance on networks with lots of
 * broadcast traffic.  However, broadcasts can only be blocked if the IP
 * stack is configured with static parameters as protocols like Dynamic Host
 * Control Protocol (DHCP) require
 * UDP broadcasts.  If the parameter blockUDPBroadcasts is set, the
 * Ethernet driver discards UDP packets addressed to the Ethernet broadcast
 * address.
 *
 * @external
 * @category EthernetDriver 
 * @since 6.0
 *
 * @param blockUDPBroadcasts    Set this parameter to block UDP broadcasts.
 */
void NABlockUDPBroadcasts(BOOLEAN const blockUDPBroadcasts)
{
    if (blockUDPBroadcasts)
    {
        ipPacketType.filterCount = 1;
    }
    else
    {
        ipPacketType.filterCount = 0;
    }
}



/**** The fillowing routines are called from the TCP/IP stack */

/*
 *
 *  Function: int eth0_init(netdev *ndp)
 *
 *  Description:
 *
 *      This routine is called by the stack to
 *      initialize an Ethernet device.
 *
 *  Parameters:
 *
 *      ndp   - pointer to the stack Ethernet device
 *
 *  Return Values:
 *
 *      0   success
 *      -1  failure
 */   
int eth0_init(netdev *ndp)
{
    int result = -1;
     /* Initialize the Ethernet driver */
     
    if (tx_semaphore_create(&packetTypeSemaphore, "Eth PktType Semaphore", 1) != TX_SUCCESS)
    {
        goto errExit;
    }
    
    if (eth_init(ndp))
        goto errExit;
    
    /* set ndp flags */
    FUSION_DRV_TXCOMPLETE_NOTIFY_NOCOPY(ndp, ETH_MAX_TX_DESCRIPTORS / ETH_MAX_CHAINED_TX_BUFFERS - 1); 
   
    /* copy MAC address */
    memcpy(ndp->nd_lladdr.a_ena, eth_datap->mac_addr, ETH_MAC_ADDR_SIZE);
    
    if (eth_enable())
        goto errExit;

    eth0_ndp = ndp;
    result = 0;
    
    errExit:
    return result;
} 


/*
 *
 *  Function: int eth0_start(m *mp)
 *
 *  Description:
 *
 *      This routine is called by the stack transmit a packet.  
 *      Thisroutine will insert a packet into a DMA buffer ring.
 *
 *  Parameters:
 *
 *      mp     - pointer to a TCP/IP message
 *
 *  Return Values:
 *
 *      0   success
 */   

int eth0_start(m *mp)
{
    /* The stack's "ndq.c" does passes a valid mp and checks that 
       there are available DMA slots in a buffer ring.
       It also disables interrupts arout this call */

    BOOLEAN result = FALSE;
    int cnt, findex, index;
    int len, i;

    if (mp != NULL)
    {
        /* Calculate number of DMA buffers for this packet */
        /* Reserve DMA buffer descriptors for this packet */
        cnt = get_packet_buffer_cnt(mp);

        findex = index = eth_reserve_tx_dma_entry(cnt);
        /* Load the packet into transmit DMA */

        if (mp->m_termfn == fip_termfn)    /* FAST IP message */         
            eth_fastip_packet_transmit(mp->m_hp, findex);
        else                               /* non FAST IP message */   
        {
            if ( mp->m_cell_count > 0 ) /* zero - copy TCP packet */
            {
               /* Load first part of the packet */
               len = mp->m_ptail - mp->m_hp;
               index = eth_packet_buffer_transmit(mp->m_hp, len, findex, index, FALSE);

               /* Then the data cells in following DMA buffer descriptors*/
               for( i = 0; i < mp->m_cell_count; i++ ) 
               {
                    index = eth_packet_buffer_transmit(mp->m_datacells[i].celldata, mp->m_datacells[i].cellsize, findex, index, i == (mp->m_cell_count - 1));
                    len += mp->m_datacells[i].cellsize;
               }
            }
            else
            {
                /* The packet will use 1 DMA buffer descriptor */
                eth_packet_buffer_transmit((char *)mp->m_hp, m_dsize(mp), index, index, TRUE);
            }

        }

        result = TRUE;
    }
    return result;
}

/*
 *
 *  Function: int eth0_ioctl(netdev * ndp, int cmnd, char * addr)
 *
 *  Description:
 *
 *      This routine is called by the stack to perform the Ethernet 
 *      device ioctl.
 *
 *  Parameters:
 *
 *      ndp     - pointer to a TCP/IP device
 *      cmnd    - ioctl command
 *      addr    -pointer to a argument
 *
 *  Return Values:
 *
 *      0     success
 *      != 0  failure
 */   

/* eth0_ioctl
*
*  NOTE:  I don't think for NetOS you really need to support ENIOCPROMISC, ENIOCALL,
*  or ENIOCNORMAL unless there is something in the NetOS API which enables a customer
*  to put the device into/take it out of promiscuous mode.  Fusion itself makes no use of
*  these ioctls, only of the multicast ones.
*/
int eth0_ioctl(netdev * ndp, int cmnd, char * addr)
{
  int rc = FNS_ENOERR;

   switch(cmnd)  
   {
     case ENIOCALL:
     case ENIOCPROMISC:
	   /* Put the driver in promiscuous mode if you want to support it or
                  just return FNS_EOPNOTSUPP if not.
               */
/*
 *  if( eth0_set_promiscuous(ndp->nd_devid) < 0 ) rc = FNS_EOPNOTSUPP;
 *   else 
 * break;
*/

      case ENIOCNORMAL:
             /* Take it out of promiscuos.  Only receive broadcasts and stuff to our own address 
                 (and I suppose any multicasts that have been explicitly enabled)
                 NOTE:  
             */
	/* if(eth0_set_own_and_broadcast(ndp->nd_devid) < 0 ) rc = FNS_EOPNOTSUPP; */
	 break;

#ifdef IGMP_PROTOCOL /* Multicast support */
       case ENIOADDMULTI:
	 /* Call function to add the address */
	   rc = eth_add_multicast ((unsigned char*)addr);  /*eth0_add_multi_address(addr); */
	break;
       case ENIODELMULTI:
     /* Call function to delete the address */
       rc = eth_del_multicast ((unsigned char*)addr);   /*eth0_del_multi_address(addr); */	
	break;
#endif
            default:
	    rc = FNS_EOPNOTSUPP;
	    break;
    }
    return(rc);
}

/**** Following routines interface other Ethernet driver modules ****/

/*
 *
 *  Function: eth_start(unsigned long pbuf)
 *
 *  Description:
 *
 *      This routine queues a TCP/IP packet for transmission.
 *
 *      Call eth_msg_allocate() to allocate a packet, copy data 
 *      to the returned data buffer, and call eth_start() to send it . 
 *      The TCP/IP stack will free the packet, when transmission completes.
 *
 *  Parameters:
 *
 *      pbuf   - address of the Ethernet packet to send -  Ethernet header + data.
 *
 *  Return Values:
 *
 *      none              
 */   
void eth_start(unsigned long pbuf)
{
    ndq_start((m *) (pbuf - sizeof(m)));
}



/**
 * Transmits a packet constructed with an m struct
 *
 * Use this function to transmit a packet that has been constructed
 * using an m struct structure.  The packet will be placed on the
 * queue of packets to be transmitted.  The m struct and buffers are
 * freed by the Ethernet driver after the packet has been transmitted. 
 * The packet buffer must include a valid Ethernet header with the correct 
 * destination and source addresses.
 *
 * Using this function instead of na_eth_send saves a buffer copy.
 *
 * @external
 * @category EthernetDriver 
 * @since 6.0
 *
 * @param packetBuffer  Pointer to m struct with packet buffer to 
 *                      transmit.
 *
 * @see @link na_eth_send
 *
 */
void NAEthTransmit(m * packetBuffer)
{
    ndq_start(packetBuffer);
}


/*
 *
 *  Function: eth_fastip_start(unsigned long pbuf)
 *
 *  Description:
 *
 *      This routine queues a Fast IP packet for transmission.
 *
 *
 *  Parameters:
 *
 *      pbuf   - address of a Fast IP message
 *
 *  Return Values:
 *
 *      none              
 */   
void eth_fastip_start(unsigned long pbuf)
{
    ndq_start((m *) pbuf);
}
/*
 *
 *  Function: void eth_restart(void)
 *
 *  Description:
 *
 *      The driver must call this routine, after a packet transmission
 *      complets. The ndq_restart TCP/IP stack function will free the 
 *      transmitted message and transmit the next one, queued in a device 
 *      transmit queue. 
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none              
 */   
void eth_restart(void)
{
    ndq_restart((netdev *)eth_datap->pnetdev);
}

/*
 *
 *  Function: unsigned long eth_msg_allocate(int len)
 *
 *  Description:
 *      
 *      This routine allocates a TCP/IP packet and returns 
 *      an address of the packet's data buffer.
 *
 *  Parameters:
 *
 *      len     - length of the packet (Ethernet header + data).
 *
 *  Return Values:
 *
 *      > 0     - address of the allocated packet data buffer.
 *      NULL    - out of TCP/IP heap memory  
 */   
unsigned long eth_msg_allocate(int len)
{
    m   *mp;

    mp = m_new(len, (int *)0, F_NOBS);
    if (mp)
    {
        /* move head pointer to beginning of storage */
	    mp->m_hp -= len;

	    /* put ndp in mp */
	    mp->m_ndp = (netdev *) eth_datap->pnetdev;
        return (unsigned long) mp->m_hp;
    }
    return 0;
}

/**
 * Allocates an m struct message buffer
 *
 * This routine allocates an m struct structure with a buffer.  
 * The size of the buffer is determined by the value of the length
 * parameter.  The buffer can be freed
 * by calling NAEthMsgFree.
 *
 * @external
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param length    length of buffer to allocate
 *
 * @see @link NAEthMsgFree
 *
 * @return NULL         The function failed and has not allocated the buffer.
 * @return otherwise    Pointer to m struct with a data buffer.
 */
m * NAEthMsgAllocate(unsigned length)
{
    m *result = NULL;
    
    if (length < ETH_HEADER_LEN || length > ETH_MAX_PACKET_LENGTH )
        return result;
    
    result = m_new(length, (int *)0, F_NOBS);
    if (result)
    {
        /* move head pointer to beginning of storage */
	    result->m_hp -= length;

	    /* put ndp in mp */
	    result->m_ndp = (netdev *) eth_datap->pnetdev;
    }
    return result;
}


/*
 *
 *  Function: void eth_msg_free(unsigned long pbuf)
 *
 *  Description:
 *      
 *      This routine frees a TCP/IP packet.
 *
 *  Parameters:
 *
 *      pbuf     - address of a packet data buffer.
 *
 *  Return Values:
 *
 *      none
 */   
void eth_msg_free(unsigned long pbuf)
{
    if (pbuf)
        smkill((m *) (pbuf - sizeof(m)));
}



/**
 * Frees a message allocated by NAEthMsgAllocate()
 *
 * @external
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param mBuffer   pointer to the m struct to free
 *
 * @see @link NAEthMsgAllocate
 */
void NAEthMsgFree(m * mBuffer)
{
    smkill(mBuffer);
}




/**
 * Passes a packet to TCP/IP if TCP/IP is enabled
 *
 * @category EthernetDriver 
 * @since 6.0
 *
 * @param mp        Pointer to Fusion m control block.
 *
 * @return TRUE     Packet was passed to TCP/IP.
 * @return FALSE    Packet was not passed to TCP/IP.
 */
static BOOLEAN tcpipRecv(m * mp)
{
    netdev *ndp = (netdev *)eth_datap->pnetdev;
    BOOLEAN sentPacketToTcp = FALSE;

    if (((ndp->nd_flags & F_N_ONLINE) != 0) && (isIpUp))
    {
        msm(mp, en_up);
        sentPacketToTcp = TRUE;
    }
    
    return sentPacketToTcp;
}




/**
 * Adds new packet types.
 *
 * This function adds new packet types to the list of packet types supported.
 * This function is typically
 * used to register a new protocol stack with the Ethernet driver.  This function 
 * checks the following before it registers the new packet type:
 *
 *	<list type = "ordered">
 *      <item>The system has not already reached its limit of packet types.</item>
 *      <item>The newPacketType argument is not NULL.</item>
 *      <item>The receive function is not NULL.</item>
 *      <item>No more than ETH_MAX_FILTERS have been specified.</item>
 *      <item>The applyToMulticastOnly and applyToBroadcastOnly flags cannot both be set.
 *            The broadcast address is a multicast address, so set applyToMulticastOnly 
 *            to filter both broadcast and multicast packets.</item>
 *      <item>The filters must be valid.</item>
 *      <item>The Ethernet packet type must not already have been registered.</item>
 *  </list>
 *
 *
 * @external
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param newPacketType     Pointer to ethPacketType structure with information that
 *                          identifies the packet type and the protocol receive function.
 *
 * @return ETH_SUCCESS  packet type added
 * @return ETH_FAILURE  unable to add packet type
 *
 * @see @link ethPacketType
 */
int NAEthAddPacketType(ethPacketType * newPacketType)
{
    int index, matchIndex;
    int result = ETH_FAILURE;
    
    NA_ASSERT(newPacketType != NULL);
    NA_ASSERT(newPacketType->recvFn != NULL);
    
    GET_SEMAPHORE;
    
    if (   (packetTypeCount >= MAX_PACKET_TYPES)
        || (newPacketType == NULL)
        || (newPacketType->recvFn == NULL)
        || (newPacketType->filterCount > ETH_MAX_FILTERS)
       )
    {
        goto errExit;
    }
    
    for (index = 0; index < newPacketType->filterCount; index++)
    {
        if (newPacketType->filter[index].matchCount > ETH_MAX_MATCHES)
        {
            goto errExit;
        }
        if ((newPacketType->filter[index].applyToMulticastOnly) && (newPacketType->filter[index].applyToBroadcastOnly))
        {
            /*
             * Reject filter if both multicast only and broadcast only are set.  Only one can
             * be set at a time.  The broadcast address is a multicast address, so setting
             * multicast only includes broadcast addresses.
             */
            goto errExit;
        }
        for (matchIndex = 0; matchIndex < newPacketType->filter[index].matchCount; matchIndex++)
        {
            if (   (newPacketType->filter[index].match[matchIndex].length > ETH_MAX_COMPARE_DATA)
                || (newPacketType->filter[index].match[matchIndex].offset >= ETH_MAX_PACKET_LENGTH)
               )
            {
                goto errExit;
            }
        }
    }
    
/*
 * Make sure the packet type has not already been registered.
 */
    for (index = 0; index < packetTypeCount; index++)
    {
        if (newPacketType->linkProtocolType == packetType[index].linkProtocolType)
        {
            switch (newPacketType->linkProtocolType)
            {
                case eth802_2:
                    if (newPacketType->protocolId > ETH_MAX_802_2_DSAP)
                    {
                        goto errExit;
                    }
                    /* Fall through is intended operation*/
                case ethEthernetII:
                case ethSNAP:
                    if (newPacketType->protocolId == packetType[index].protocolId)
                    {
                        goto errExit;
                    }
                    break;
                case ethNovell_802_3:
                default:
                    goto errExit;
            }
        }
    }
    
    memcpy (&packetType[packetTypeCount], newPacketType, sizeof packetType[packetTypeCount]);
    packetTypeCount++;
    result = ETH_SUCCESS;
    
    errExit:
    PUT_SEMAPHORE;
    
    return result;
}




/*
 *
 *  Function: int eth0_updown(netdev *ndp, u16 flags, char *options)
 *
 *  Description:
 *
 *      This routine is called by the stack enable or disable 
 *      the Ethernet device.
 *
 *  Parameters:
 *
 *      ndp     - pointer to the stack Ethernet device
 *      flags   - TRUE = enable, FALSE = disable
 *      options - we don't use
 *
 *  Return Values:
 *
 *      0   success
 *      -1  failure
 */   
int eth0_updown(netdev *ndp, u16 flags, char *options)
{
    static BOOLEAN ipLoaded = FALSE;
    int result;
    
    NA_UNUSED_PARAM(options);
    
    if (flags)
    {
        /* ensure that ndp has sane values for link-layer */
        ndp->nd_lladdr.a_type = AF_ETHER;
        ndp->nd_lladdr.a_len = sizeof(a48);
        
        if (!ipLoaded)
        {
            result = NAEthAddPacketType(&ipPacketType);
            result |= NAEthAddPacketType(&arpPacketType);
            result |= NAEthAddPacketType(&rarpPacketType);
#ifdef PPPOE_PROTOCOL
            result |= NAEthAddPacketType(&pppoeDisPacketType);
            result |= NAEthAddPacketType(&pppoeSesPacketType);
#endif    
            if (result == 0)
            {
                isIpUp = TRUE;
            }
            ipLoaded = TRUE;
        }
        else
        {
            isIpUp = TRUE;
        }
    }
    else
    {
        isIpUp = FALSE;
    }

    return 0;
}





/**
 * Determines whether the packet should be filtered
 *
 * This routine checks the list of filters 
 * registered with NAEthAddPacketType() and determines whether the packet 
 * matches any of them.  It returns TRUE if the packet matches one of the
 * filters.  Otherwise it returns FALSE.
 *
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param packetType        pointer to structure with information about this packet type
 * @param packet            pointer to packet data buffer
 * @param status            status flags from Ethernet receiver H/W
 * @param length            length of receive packet
 *
 *
 * @return TRUE     Packet matches at least one filter.
 * @return FALSE    Packet does not match any filter.
 */
static BOOLEAN matchesFilter(ethPacketType * packetType, BYTE * const packet, WORD32 status, unsigned length)
{
    #define ETH_RX_MCAST       0x0800
    #define ETH_RX_BCAST       0x1000
    
    int filterIndex, matchIndex;
    BOOLEAN matchesFilter = FALSE;
    
    for (filterIndex = 0; (filterIndex < packetType->filterCount) && (!matchesFilter); filterIndex++)
    {
        ethFilterType *filter = &packetType->filter[filterIndex];
        
        if (!(filter->applyToBroadcastOnly && (status & ETH_RX_BCAST) == 0) && !(filter->applyToMulticastOnly && (status & ETH_RX_MCAST) == 0))
        {
            BOOLEAN matchesSubfilter = TRUE;
            
            for (matchIndex = 0; (matchIndex < filter->matchCount) && (matchesSubfilter); matchIndex++)
            {
                ethMatchType *m = &filter->match[matchIndex];
                WORD32 address = ((WORD32) packet) + m->offset;
    
                if ((m->offset + m->length) < length)
                {
                /*
                 * Try to avoid a memcmp() since it is probably inefficient.  Only use memcmp() if
                 * we have an alignment problem or we are comparing an unusual number of bytes.
                 */            
                    if (m->length == 1)
                    {
                        matchesSubfilter = (m->compareData[0] == packet[m->offset]) ^ m->wantInverseMatch;
                    }
                    else if ((m->length == 2) && ((address & 1) == 0))
                    {
                        matchesSubfilter = ((*(WORD16 *) &m->compareData[0]) == *(WORD16 *) (packet + m->offset)) ^ m->wantInverseMatch;
                    }
                    else if ((m->length == 4) && ((address & 3) == 0))
                    {
                        matchesSubfilter = ((*(WORD32 *) &m->compareData[0]) == *(WORD32 *) (packet + m->offset)) ^ m->wantInverseMatch;
                    }
                    else
                    {
                        matchesSubfilter = (memcmp(m->compareData, (void *) address, m->length)  == 0) ^ m->wantInverseMatch;
                    }
                }
            }
            matchesFilter = matchesSubfilter;
        }
    }
    
    return matchesFilter;
}


/**
 * Determines whether the receive packet matches the supplied packet type
 *
 * @category EthernetDriver  
 * @since 6.0
 *
 * @param   packetType  pointer to information about a packet type
 * @param   packet      pointer to packet data
 *
 * @return TRUE     Packet has been passed to a stack.
 * @return FALSE    No stack has registered to receive this packet type.
 */
static BOOLEAN matchesPacketType(ethPacketType * packetType, BYTE * const packet)
{
    #define ETHERNET_II_TYPE_OFFSET 12
    #define NOVELL_CHECKSUM_OFFSET  14
    #define NOVELL_CHECKSUM_VALUE   0xffff
    #define _802_2_DSAP_OFFSET      14
    #define SNAP_DSAP               0xaa
    #define SNAP_ID_OFFSET          20
    
    BOOLEAN result = FALSE;
    
    switch (packetType->linkProtocolType)
    {
        case ethEthernetII:
            result = (packetType->protocolId == *(WORD16 *) (packet + ETHERNET_II_TYPE_OFFSET));
            break;
        case ethNovell_802_3:
            if (ETH_MAX_PACKET_LENGTH >= *(WORD16 *) (packet + ETHERNET_II_TYPE_OFFSET))
            {
                result = (NOVELL_CHECKSUM_VALUE == *(WORD16 *) (packet + NOVELL_CHECKSUM_OFFSET));
            }
            break;
        case eth802_2:
            result = (((BYTE) packetType->protocolId) == *(packet + _802_2_DSAP_OFFSET));
            break;
        case ethSNAP:
            if (SNAP_DSAP == *(packet + _802_2_DSAP_OFFSET))
            {
                /*
                 * The SNAP ID is broken up into two sections.  The first 3 bytes are a vendor
                 * ID, the last 2 bytes are the Ethernet II frame type.  The vendor ID
                 * is usually either set to zero, or to the Ethernet MAC prefix (first 3 bytes of
                 * the Ethernet MAC address) assigned to the manufacturer of the station by
                 * the IEEE.  The vendor ID is therefore useless and can be ignored.
                 * See section 10.5 of IEEE STD 802-2001.
                 */
                result = (packetType->protocolId == *(packet + SNAP_ID_OFFSET));
            }
            break;
        default:
            break;
    }
    
    return result;
}


/*
 *
 *  Function: int eth_recv_up(unsigned long packet, int pktlen)
 *
 *  Description:
 *      
 *      This routine passes a received packet up the TCP/IP stack
 *
 *  Parameters:
 *
 *      pbuf     - address of a packet data buffer (Ethernet header + data)
 *      pktlen   - size of a received packet.
 *
 *  Return Values:
 *
 *      1 - the packet passed up the stack
 *      0 - the Ethernet device is offline.
 */   
int eth_recv_up(unsigned long packet, int pktlen, WORD32 status)
{
    m     *mp;
    short *ptr;
    int   i;
    int result = 0;
    BYTE *pktBuffer = (BYTE *) packet;


    /* get the pointer to the message structure */
    mp = (m *)(packet - sizeof(m));

    /* if wereceive in pNA mode move the Ethernet header 2 bytes forward */
    /* adjust the data head pointer and the packet length */

    if (eth_datap->pna_mode)
    {
        ptr = (short *)packet;
                
        for (i = ETH_HEADER_LEN / 2 - 1; i >= 0; i--)
            ptr[i+1] = ptr[i];

        mp->m_hp += 2;
        pktlen -= 2;
        pktBuffer += 2;
    }
    /* Initialize the data tail pointer */
    mp->m_tp = mp->m_hp + pktlen;
    
    GET_SEMAPHORE;
    for (i = 0; i < packetTypeCount; i++)
    {
        
        if (matchesPacketType(&packetType[i], pktBuffer))
        {
            if (!matchesFilter(&packetType[i], pktBuffer, status, (unsigned) pktlen))
            {
                result = packetType[i].recvFn(mp);
            }
            break;
        }
    }
    
    if ((result == FALSE) && (unclaimedPacketFn != NULL))
    {
        result = unclaimedPacketFn(mp);
    }
    
    PUT_SEMAPHORE;
    
    return result;
}

/*** The following routines are used by Fast IP only ***/

/*
 *
 *  Function: unsigned long eth_fastip_msg_allocate(void)
 *
 *  Description:
 *
 *      This function allocates an empty Fast IP message. 
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      >  0 - address of an allocated Fast IP message
 *      NULL - out of TCP/IP heap memory 
 *              
 */   
unsigned long eth_fastip_msg_allocate(void)
{
    m   *mp;
    mp =  m_new(0, (int *)0, F_NOBS);
 
    if (mp)
    {
        mp->m_ndp = (netdev *) eth_datap->pnetdev;  /* Ethernet device pointer */

        /* 
         * m_hp is used by the IP stack as a packet data pointer.
         * For the reply Fast IP packets m_hp is used as a pointer to 
         * the expressMblk control block. Is set when the packet is sent.
         * m_hp == 0 indicates an available Fast Ip buffer.
         */
        mp->m_hp = NULL;
    }
    return (unsigned long) mp;
}       

/*
 *
 *  Function: eth_fastip_msg_init(unsigned long maddr, char * mblk)
 *
 *  Description:
 *
 *      This function re-initializes the Fast IP message to
 *      re-use for transmit the same message structure. 
 *
 *  Parameters:
 *
 *      maddr      address of a FAST/IP message.
 *      mblk       link list of Fast IP packet data.
 *
 *  Return Values:
 *
 *      none
 *              
 */   
void eth_fastip_msg_init(unsigned long maddr, char * mblk)
{
    m *mp = (m *) maddr;
    if (mp)
    {
        /*
         * Set m_hp to point to expressMblk control block. 
         * When the packet is transmitted, the fip_termfn will clear m_hp
         * to indicate that msgp is available
         */
        mp->m_hp = mblk;

        /* 
         * - On the previous usage of this msgp the IP stack set m_job to smdispose.
         *   Clear m_job, so the stack does not free this msgp.
         * - Set m_dispfn to smdispose to have control over this field.
         * - Set m_termfn to the Fast Ip terminate function fip_termfn, that will
         *   be called by the stack instead of smdispose (to free a msgp).
         */
        mp->m_job    = smnil; 
        mp->m_dispfn = smdispose;
        mp->m_termfn = fip_termfn;

        /* Initialize m_q, m_mq, and m_wq queues*/
    
        q_init(&mp->m_q, F_Q_LOCKED); 
        q_init(&mp->m_wq, 0); 
        q_initparent(&mp->m_mq, 0, (char *)mp); 
    
        /* Initialyze m_ctime, m_owners, m_type, and m_flags */
    
        mp->m_ctime  = t_time; 
        mp->m_owners = 1; 
        mp->m_type   = 0;
        mp->m_flags  = 0; 
    }
}

/*
 *
 *  Function: eth_fastip_msg_free(unsigned long maddr)
 *
 *  Description:
 *
 *      This function frees the Fast IP message 
 *
 *  Parameters:
 *
 *      maddr      address of a TCP/IP message structure of a Fast IP packet
 *
 *  Return Values:
 *
 *      none
 *              
 */      
void eth_fastip_msg_free(unsigned long maddr)
{
    if (maddr)
        smkill((m *) maddr);
}

/*
 *
 *  Function: int eth_fastip_msg_avail(unsigned long maddr)
 *
 *  Description:
 *
 *      This function is called by Fast IP to check if the message 
 *      is available. Available Fast IP message has m_hp = 0
 *
 *  Parameters:
 *
 *      maddr      address of a TCP/IP message structure of a Fast IP packet
 *
 *  Return Values:
 *
 *      TRUE  - available
 *      FALSE - busy
 *              
 */      
int eth_fastip_msg_avail(unsigned long maddr)
{
    m *mp = (m *)maddr;
    return (mp->m_hp == 0);
} 
   
/**** Static Routines ****/
           
/*
 *
 *  Function: int fip_termfn (struct m *mp)
 *
 *  Description:
 *
 *      This callback routine is called when the Fast IP message has transmitted. 
 *      This routine makes a Fast IP message available for the future transmissions
 *      and tell the stack not to free his message.
 *  
 *      The fip_termfn address in the m_termfn field of mp is used by the eth0_start()
 *      transmit routine to recognize a Fast IP message.
 *
 *  Parameters:
 *
 *      mp      pointer to a transmitted Fast IP message
 *
 *  Return Values:
 *
 *      0       This callback must return 0.
 *              
 */

static int fip_termfn(struct m *mp)
{
    /* Make this Fast IP message available */
    mp->m_hp = 0;

    /* Return 0 to tell the stack not to free this mp. */
    return 0;
}

/* Return the pointer to the head of transmit queue */
/* Return NULL if the queue is empty                */

void *eth_get_tx_queue_head(void)
{
    void *phead = NULL;
    netdev *pnetdev = (netdev *)eth_datap->pnetdev;

    /* disable proc. interrupts */

    if (pnetdev->nd_xmitq.q_next != &pnetdev->nd_xmitq)
        phead = pnetdev->nd_xmitq.q_next;

   /* restore processor interrupts */

    return phead;
}

/*
 *
 *  Function: get_packet_buffer_cnt
 *
 *  Description:
 *
 *      This routine calculates the number of 
 *      DMA buffers that will be used to transmit
 *      the TCP/IP message
 *
 *  Parameters:
 *
 *      mp     - pointer to a TCP/IP message, assumes a 
 *               valid pointer.
 *
 *  Return Values:
 *
 *      number of DMA buffers
 */   
static int get_packet_buffer_cnt(const m *mp)
{
    int cnt;

    if (mp->m_termfn == fip_termfn)
        cnt = eth_fastip_buffer_cnt(mp->m_hp);
    else
        cnt = mp->m_cell_count + 1;

    return cnt;
}

