/*****************************************************************************
* Copyright (c) 2005 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/
   

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "naeth.h"
#include "eth.h"
#include "bele.h"

#ifdef NET_OS_SPECIFIC
#include <trsocket.h>
#include <trmacro.h>
#include <trtype.h>
#include <trproto.h>
#include <trbypass.h>
#include "eth_api.h"
#endif

#define MCA_BIT     BIT_0			        /* bit to check multicast address  */
#define MCA_BYTE    0				        /* byte to check multicast address */

#define MAX_MCA     20
#define LEFT        0
#define RIGHT       1
#define POLYNOMIAL  0x4C11DB6L


static void eth_make_hash_table (int num, const ETH_ADDRESS *table, WORD16 *hash_table);
static int  calculate_hash_bit (const BYTE *mca);
static void set_hash_bit (BYTE *table, int bit);
static WORD16 rotate (WORD16 reg, int direction, int bits);


/*
 *
 *  Function: void eth_load_multicast_table (int num, const ETH_ADDRESS *tables)
 *
 *  Description:
 *
 *      This routine loads the MCA table.  It generates a hash table for the
 *      MCA addresses currently registered and then loads this table into the
 *      86964 chip.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */

void eth_load_multicast_table (int num, const ETH_ADDRESS *tables)

{
    WORD16 hash_table[4];
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    unsigned long  value1, value2;
#endif 
    eth_make_hash_table (num, tables, hash_table);    /* create hash table for MCA addresses*/

    /* write the hash table */
#if (PROCESSOR == ns9750) || (PROCESSOR == ns9360) || (PROCESSOR == ns9210) || (PROCESSOR == ns9215)

    value1 = TO_LE16(hash_table[0]);
    value2 = TO_LE16(hash_table[1]) << 16;

    WriteReg(NARM_EFE_HT1_32, value1 | value2);

    value1 = TO_LE16(hash_table[2]);
    value2 = TO_LE16(hash_table[3]) << 16;

    WriteReg(NARM_EFE_HT2_32, value1 | value2);
    
#elif (PROCESSOR == ns7520) || (PROCESSOR == net50)

    WriteReg(NARM_EFE_HT4, (unsigned long) TO_LE16(hash_table[3]));
    WriteReg(NARM_EFE_HT3, (unsigned long) TO_LE16(hash_table[2]));
    WriteReg(NARM_EFE_HT2, (unsigned long) TO_LE16(hash_table[1]));
    WriteReg(NARM_EFE_HT1, (unsigned long) TO_LE16(hash_table[0]));
#else
    #error Unsupported Processor
#endif
}

#ifdef NET_OS_SPECIFIC

/*ad* 
 * Adds a multicast MAC address for the Ethernet device to the list of 
 *    filtered multicast addresses.
 *
 * Use this function for Ethernet multicast addresses that are not added 
 * by TCP/IP stack. This functions might be needed to receive Ethernet multicast 
 * packets in bypass mode.
 *
 * @external
 * @include 	"eth_api.h"
 * @category BSP:Device_Drivers:EthernetDriver 
 * @since 5.0
 *
 * @param address  Pointer to the multicast MAC address (6 bytes).
 *
 * @see @link BSP:Device_Drivers:EthernetDriver:eth_del_multicast
 *
 * @return ETH_SUCCESS          Address added to the list
 * @return ETH_INVALID_MCA      Invalid multicast address
 * @return ETH_INVALID_STATE    Invalid driver state
 * @return ETH_TABLE_FULL       Multicast table is full
*ad*/
int eth_add_multicast(BYTE * address)
{
    ttUserInterface interfaceHandle;

    ETH_ADDRESS bcast_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    unsigned int multihome;
    int result = ETH_SUCCESS;

    if (address == NULL || memcmp(address, &bcast_addr[0], sizeof (ETH_ADDRESS)) == 0)
        return ETH_INVALID_MCA;

    if (naIfConfigGetInterfaceHandle("eth0", &interfaceHandle, &multihome) != NASTATUS_SUCCESS)
        return ETH_INVALID_STATE;

    tfBypassLockDevice(interfaceHandle);

    if (tfEtherSetMcastAddr((struct tsDeviceEntry *)interfaceHandle, address, 
                            TRUE, TM_LNK_ADD_MCAST) != TM_ENOERROR) 
    {
        result = ETH_TABLE_FULL;   
    }         
    tfBypassUnlockDevice(interfaceHandle);

    return result;
}

/*ad* 
 * Deletes a multicast MAC address for the Ethernet device from the list of 
 *    filtered multicast addresses.
 *
 * Use this function to add Ethernet multicast addresses that are not added 
 * or by TCP/IP stack.
 *
 * @external
 * @include 	"eth_api.h"
 * @category BSP:Device_Drivers:EthernetDriver 
 * @since 5.0
 *
 * @param address  Pointer to the multicast MAC address (6 bytes).
 *
 * @see @link BSP:Device_Drivers:EthernetDriver:eth_add_multicast
 *
 * @return ETH_SUCCESS              Address added to the list
 * @return ETH_INVALID_MCA          Invalid multicast address
 * @return ETH_INVALID_STATE        Invalid driver state
 * @return ETH_MCA_NOT_IN_TABLE     Address is not in multicast table
*ad*/
int eth_del_multicast(BYTE * address)
{
    ttUserInterface interfaceHandle;
    unsigned int multihome;
    int result = ETH_SUCCESS;

    if (address == NULL)
        return ETH_INVALID_MCA;

    if (naIfConfigGetInterfaceHandle("eth0", &interfaceHandle, &multihome) != NASTATUS_SUCCESS)
        return ETH_INVALID_STATE;

    tfBypassLockDevice(interfaceHandle);

    if (tfEtherSetMcastAddr((struct tsDeviceEntry *)interfaceHandle, address, 
                            TRUE, TM_LNK_DEL_MCAST) != TM_ENOERROR) 
    {
        result = ETH_MCA_NOT_IN_TABLE;   
    }         
    tfBypassUnlockDevice(interfaceHandle);

    return result;
}
#endif

/*
 *
 *  Function: void eth_make_hash_table (int num, const ETH_ADDRESS *tables, WORD16 *hash_table)
 *
 *  Description:
 *
 *      This routine creates a hash table based on the CRC values of the MCA
 *      addresses setup by eth_add_mca().  The CRC value of each MCA address
 *      is calculated and the lower six bits are used to generate a value
 *      between 0 and 64.  The corresponding bit in the 64 bit hash_table is
 *      then set.
 *
 *      This routine has a work-around for NET+20UM hash table hardware bug, 
 *      that 2nd word of the hash table writes also to 3rd, and 3rd does not 
 *      write at all. To compensate we write to both locations (and receive 
 *      and extra block of multicast addresses).
 *
 *  Parameters:
 *
 *      hash_table          pointer to buffer to store hash table in.
 *
 *  Return Values:
 *
 *      none
 *
 */

static void eth_make_hash_table (int num, const ETH_ADDRESS *tables, WORD16 *hash_table)

{
    int idx;
#if (PROCESSOR == ns7520)
    WORD16 val;
#endif

    memset((char*)hash_table, 0, 8);                  /* clear hash table*/

    for (idx = 0; idx < num; idx++) /* for each mca address*/
    {
        set_hash_bit ((BYTE *) hash_table, calculate_hash_bit (tables [idx]));
    }

#if (PROCESSOR == ns7520)
	    val = hash_table[1] | hash_table[2];
        hash_table[1] = val;
        hash_table[2] = val;

#endif

/*    print_hash_table ((BYTE *) hash_table); */
}

/*
 *
 *  Function: int calculate_hash_bit (BYTE *mca)
 *
 *  Description:
 *
 *      This routine calculates which bit in the CRC hash table needs to be
 *      set for the 964 to recognize incoming packets with the MCA
 *      passed to us.
 *
 *  Parameters:
 *
 *      mca     pointer to multi-cast address
 *
 *  Return Values:
 *
 *      bit position to set in hash table
 *
 */

static int calculate_hash_bit (const BYTE *mca)

{
   WORD32 crc;
   WORD16 *mcap, bp, bx;
   int result, idx, mca_word, bit_index;
   WORD16 copy_mca[3];

   memcpy (copy_mca, mca, sizeof (copy_mca));

   for (idx = 0; idx < 3; idx++)
   {
       copy_mca [idx] = TO_LE16(copy_mca [idx]);
   }

   mcap = copy_mca;
   crc = 0xffffffffL;

   for (mca_word = 0; mca_word < 3; mca_word++)
   {
       bp = *mcap;                                 /* get word of address*/
       mcap++;

       for (bit_index = 0; bit_index < 16; bit_index++)
       {
          bx = (WORD16) (crc >> 16);              /* get high word of crc*/
          bx = rotate (bx, LEFT, 1);              /* bit 31 to lsb*/
          bx ^= bp;                               /* combine with incoming*/
          crc <<= 1;                              /* shift crc left 1 bit*/
          bx &= 1;                                /* get control bit*/
          if (bx)                                 /* if bit set*/
          {
              crc ^= POLYNOMIAL;                  /* xero crc with polynomial*/
          }

          crc |= bx;                              /* or in control bit*/
          bp = rotate (bp, RIGHT, 1);
       }
    }

/* CRC calculation done.  The 6-bit result resides in bits locations 28:23 */

   result = (crc >> 23) & 0x3f;

   return result;
}


/*
 *
 *  Function: void set_hash_bit (BYTE *table, int bit)
 *
 *  Description:
 *
 *      This routine sets the appropiate bit in the hash table.
 *
 *  Parameters:
 *
 *      table       pointer to hash table
 *      bit         position of bit to set
 *
 *  Return Values:
 *
 *      none
 *
 */

static void set_hash_bit (BYTE *table, int bit)
{
    int byte_index, bit_index;

    byte_index = bit >> 3;
    bit_index = bit & 7;
    table [byte_index] |= (1 << bit_index);
}

/*
 *
 *  Function: WORD16 rotate (WORD16 reg, int direction, int bits)
 *
 *  Description:
 *
 *      This routine performs a ror or rol.
 *
 *  Parameters:
 *
 *      reg         value to rotate
 *      direction   LEFT or RIGHT
 *      bits        # of bits to rotate
 *
 *  Return Values:
 *
 *
 *
 */

static WORD16 rotate (WORD16 reg, int direction, int bits)
{
    if (direction == LEFT)
    {
	reg = (reg << bits) | (reg >> (16 - bits));
    }
    else      /* if ror*/
    {
	reg = (reg >> bits) | (reg << (16 - bits));
    }
    return reg;
}

