/**
 * ============================================================================
 * = COPYRIGHT
 *              INTEL CORPORATION PROPRIETARY INFORMATION
 *   This software is supplied under the terms of a license agreement or
 *   nondisclosure agreement with Intel Corporation and may not be copied 
 *   or disclosed except in accordance with the terms in that agreement.
 *      Copyright (C) 2000-2001 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2000 Network Processor, Release 5
 *
 * = LIBRARY
 *      
 *
 * = MODULE
 *      IPv4 Forwarder Core Component - source directed broadcast 
 *      table management module
 *
 * = FILENAME
 *      ix_cc_ipv4_dbcast.c
 *
 * = DESCRIPTION
 *      The file defines dirceted broadcast table management interface functions.
 *
 * = AUTHOR
 *       Govindan Nair
 *       govindan.nair@intel.com
 *
 * = AKNOWLEDGEMENTS
 *      
 *
 * = CREATION TIME
 *      07/18/2002
 *
 * = CHANGE HISTORY
 *
 * ============================================================================
 */
#define IX_ERROR_FILE_IDENT "$Id: ix_cc_ipv4_dbcast.c,v 1.8 2002/10/18 16:09:35 gnair1 Exp $"



/**
 * System defined include files
 */

/**
 * User defined include files
 */

#include "ix_cc_error.h"

#include "ix_rm.h"
#include "ix_ossl.h"


#include "ix_cc.h"
#include "bindings.h"
#include "cc/ix_cc_ipv4.h"

#include "cc/ipv4/internal/ix_cc_ipv4_init.h"
#include "cc/ipv4/internal/ix_cc_ipv4_dbcast.h"





/**
 * Preprocessor symbols and Macros used in this file
 */
/* arg_IpAddr is represented in network byte order */
/* Get only least 8 bits */
#define IX_CC_IPV4_DBCAST_HASH_INDEX(arg_IpAddr)\
(((arg_IpAddr) ^ (arg_IpAddr >> 8) ^(arg_IpAddr >> 16) ^ (arg_IpAddr >> 24)) & 0xFF)



#define IX_CC_IPV4_DBCAST_TABLE_LAST_ENTRY(pLastEntryAddr)\
(((pLastEntryAddr + \
(IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE * IX_CC_IPV4_NUMBER_OF_DBCAST_TABLE_BLOCK)) +\
(IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE * IX_CC_IPV4_NUMBER_OF_DBCAST_EXTRA_BUCKETS));

/**
 * Type definitions whose scope is limited to this file
 */

/**
 * Variable declarations global to this file only. Externs are followed by 
 * static variables.
 */

extern ix_cc_ipv4_context* g_pIpv4Context;


/** 
 * Extern function prototypes.
 */


/** 
 * Static function prototypes.
 */


/** 
 * Function definitions.
 */


/**
 * NAME: _ix_cc_ipv4_source_broadcast_store_data_in_bucket
 *
 * DESCRIPTION: This primitive will be used to add source directed broadcst address into table. 
 *           .
 * 
 * @Param:  - IN arg_pBucketAddr: starting address of a block/bucket
 * @Param:  - IN arg_IpAddr: address to be stored in free space
 *
 * @Param:  - OUT none
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * 
 * IX_CC_ERROR_FULL - no space in bucket
 */

ix_error _ix_cc_ipv4_source_broadcast_store_data_in_bucket(
                                          ix_uint32* arg_pBucketAddr,
                                          ix_uint32 arg_IpAddr)
{

    ix_uint32 index;
    ix_uint32* pBucketAddr = arg_pBucketAddr;
    ix_uint32 data;

    for(index = 0; index < IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE -1; index++)
    {
        data = IX_IPV4_MEM_UINT32_READ(pBucketAddr);
        if(data == 0)
        {
            /* *pBucketAddr,arg_IpAddr */
            ix_ossl_mutex_lock(g_pIpv4Context->ipv4Mutex,IX_OSSL_MUTEX_LOCK);
            (void)IX_IPV4_MEM_UINT32_WRITE(pBucketAddr,arg_IpAddr);
            ix_ossl_mutex_unlock(g_pIpv4Context->ipv4Mutex);
            return IX_SUCCESS;
        }/* end if(*bucketdAddr == 0) */
        pBucketAddr++;        
    }/* end for(index = 0; index < IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE -1; index++)*/
    return IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                           ("no space in bucket"));
}/* end _ix_cc_ipv4_source_broadcast_store_data_in_bucket */


/**
 * NAME: _ix_cc_ipv4_source_broadcast_store_data_in_new_bucket
 *
 * DESCRIPTION: This primitive will be used to add source directed broadcst address 
 * in a new bucket. 
 *           .
 * @Param:  - IN arg_pBucketAddr: starting address of a block/bucket
 * @Param:  - IN arg_IpAddr: address to be stored in bucket
 *
 * @Param:  - OUT none
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_FULL - no space in table
 * 
 */

ix_error _ix_cc_ipv4_source_broadcast_store_data_in_new_bucket(ix_uint32 *pBaseAddr,
                                                               ix_uint32 arg_IpAddr)
{

    ix_uint32* pNewBucketAddr;
    ix_uint32 index;
    ix_uint32 data;
    pNewBucketAddr = g_pIpv4Context->pMbDbBase;

    pNewBucketAddr += IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE * IX_CC_IPV4_NUMBER_OF_DBCAST_TABLE_BLOCK;
    
    /* Check if first entry is zero. If so, then one empty bucket is available */
    /* If not, look for another one */
  
    for(index = 0; index < IX_CC_IPV4_NUMBER_OF_DBCAST_EXTRA_BUCKETS; index ++)
    {
        data = IX_IPV4_MEM_UINT32_READ(pNewBucketAddr); 
        if(data == 0)
        {
            ix_ossl_mutex_lock(g_pIpv4Context->ipv4Mutex,IX_OSSL_MUTEX_LOCK);
            IX_IPV4_MEM_UINT32_WRITE(pBaseAddr,IX_CC_IPV4_NUMBER_OF_DBCAST_TABLE_BLOCK + index);
            IX_IPV4_MEM_UINT32_WRITE(pNewBucketAddr,arg_IpAddr);
            ix_ossl_mutex_unlock(g_pIpv4Context->ipv4Mutex);
            return IX_SUCCESS;
        }/* end if(newBucketAddr == 0) */
        pNewBucketAddr += IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE;
    }/* end for (index = 0; index < IX_CC_IPV4_NUMBER_OF_EXTRA_BUCKETS; index ++)*/

    return IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                              ("no more new bucket"));       
}/* end _ix_cc_ipv4_source_broadcast_store_data_in_new_bucket */



/**
 * NAME: _ix_cc_ipv4_source_broadcast_find_entry
 *
 * DESCRIPTION: This primitive will be used to find source directed broadcst address 
 * from bucket
 * 
 * @Param:  - IN arg_pBucketAddr - starting address of a bucket
 * @Param:  - IN arg_IpAddr - IP address to find
 *
 * @Param:  - OUT arg_pIpAddrLocation - pointer to location of search result
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * 
 * 
 */

ix_error _ix_cc_ipv4_source_broadcast_find_entry(ix_uint32* arg_pBucketAddr,
                                ix_uint32 arg_IpAddr,
                                ix_uint32** arg_pIpAddrLocation)
{
    ix_uint32 index;
    ix_uint32 *pNextBucketAddr = NULL;
    ix_uint32* pLastAddrInTable = NULL;
    ix_uint32 *pStartAddr = NULL;
    ix_uint32 data;

    pNextBucketAddr = arg_pBucketAddr;
    pStartAddr = g_pIpv4Context->pMbDbBase;

    pLastAddrInTable = pStartAddr + (IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE * IX_CC_IPV4_NUMBER_OF_DBCAST_TABLE_BLOCK) +
                        (IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE * IX_CC_IPV4_NUMBER_OF_DBCAST_EXTRA_BUCKETS);

    while((pNextBucketAddr < pLastAddrInTable) ||
          (pNextBucketAddr != 0))
    {
        for(index = 0; index < IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE -1; index++)
        {
            data = IX_IPV4_MEM_UINT32_READ(pNextBucketAddr); 
            if(data == 0)
            { 
                return IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                                        ("no entry in table"));   
            }/* end if(*pNextBucketdAddr == 0) */
      

            if(data == arg_IpAddr)
            {
                *arg_pIpAddrLocation = pNextBucketAddr;
                return IX_SUCCESS;
            }/* end if(*pNextBucketdAddr == arg_IpAddr) */
            pNextBucketAddr++;        
        }/* end for(index = 0; index < IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE -1; index++)*/
        pNextBucketAddr = (ix_uint32*)(pStartAddr + 
                                       ((*pNextBucketAddr) * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE));
    }/* end while(newBucketAddr >= lastAddrInTable) */

    return IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                           (" entry not in table"));       
  
}/* end _ix_cc_ipv4_source_broadcast_find_entry */



/**
 * NAME: _ix_cc_ipv4_source_broadcast_get_last_entry
 *
 * DESCRIPTION: This primitive will be used to get last source directed broadcst address
 * from a bucket. 
 * 
 * @Param:  - IN arg_pBucketAddr - starting address of a bucket
 * @Param:  - IN arg_IpAddr - IP address to find
 *
 * @Param:  - OUT arg_pIpAddrLocation - pointer to location of search result
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_ENTRY_NOT_FOUND  - entry not found in bucket
 * 
 */
/* Get the starting address of a bucket */

ix_error _ix_cc_ipv4_source_broadcast_get_last_entry(ix_uint32* arg_pBucketAddr,
                                    ix_uint32* arg_pIpAddr,
                                    ix_uint32** arg_pIpAddrLocation)
{
    ix_uint32 index = 0;
    ix_uint32* pNextBucketAddr = NULL;
    ix_uint32* pPreviousBucketAddr = NULL;
    ix_uint32 lastEntryInBucket = 0;
    ix_uint32* pLastEntryAddr = NULL;
    ix_uint32 *pStartAddr = NULL ;
    ix_uint32 data = 0;


    pNextBucketAddr = arg_pBucketAddr;

    pStartAddr = g_pIpv4Context->pMbDbBase;
    data = IX_IPV4_MEM_UINT32_READ(pNextBucketAddr); 
    while(data != 0)
    {
        pPreviousBucketAddr = pNextBucketAddr;
        pNextBucketAddr += (IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE - 1);
        data = IX_IPV4_MEM_UINT32_READ(pNextBucketAddr);
        pNextBucketAddr = (ix_uint32*)(pStartAddr + 
                                       (data * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE));
    }/* end while */
    if(pPreviousBucketAddr == NULL)
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                                   ("no  entry in bucket"));
    }/* end if no entries in table */


    for(index = 0; index < IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE -1; index++)
    {
        data = IX_IPV4_MEM_UINT32_READ(pPreviousBucketAddr); 
        if(data != 0)
        {
            lastEntryInBucket = data;
            pLastEntryAddr = pPreviousBucketAddr;
        }/* end if(*bucketdAddr == 0) */
        pPreviousBucketAddr++;
    }/* end for */
         

    *arg_pIpAddr = lastEntryInBucket;
    *arg_pIpAddrLocation = pLastEntryAddr;

    return IX_SUCCESS;
}/* end _ix_cc_ipv4_source_broadcast_get_last_entry */





/**
 * NAME: _ix_cc_ipv4_add_source_broadcast
 *
 * DESCRIPTION: This primitive will be used to add source directed broadcst 
 * address into table. 
 *           .
 * 
 * @Param:  - IN arg_IpAddr - directed broadcast IP address to be added in table
 *
 * @Param:  - OUT none
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_FULL - no space in bucket
 * 
 */

ix_error _ix_cc_ipv4_add_source_broadcast(ix_uint32 arg_IpAddr)
{


    ix_error err = IX_SUCCESS;
    ix_uint32 tableIndex;
    ix_uint32 data;
    ix_uint32* pBaseAddr;
    ix_uint32 *pStartAddr;
    ix_uint32 addSourceDBCast = IX_CC_IPV4_BROADCAST_FAIL;
    
    /* arg_IpAddr is represented in network byte order */
    tableIndex = IX_CC_IPV4_DBCAST_HASH_INDEX(arg_IpAddr);

    pStartAddr = g_pIpv4Context->pMbDbBase;
    pBaseAddr = pStartAddr + (tableIndex * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE);

    while(addSourceDBCast == IX_CC_IPV4_BROADCAST_FAIL)
    {
        err = _ix_cc_ipv4_source_broadcast_store_data_in_bucket(pBaseAddr,arg_IpAddr);        
        if(err != IX_SUCCESS)
        {
            pBaseAddr += IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE - 1;
            data = IX_IPV4_MEM_UINT32_READ(pBaseAddr); 
            if(data == 0)
            {  
                /* Get a new bucket and store data */
                err = _ix_cc_ipv4_source_broadcast_store_data_in_new_bucket(pBaseAddr,arg_IpAddr); 
                addSourceDBCast = IX_CC_IPV4_BROADCAST_SUCCESS;      
            }/* end if (*baseAddr == 0) */
            else
            {
                data = IX_IPV4_MEM_UINT32_READ(pBaseAddr);/* Get new bucket address */
                pBaseAddr =(ix_uint32*)(pStartAddr + 
                                       (data * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE));
            }/* end else (*baseAddr == 0) */
        }/* endif (err != IX_SUCCESS) */   
        else
        {
            addSourceDBCast = IX_CC_IPV4_BROADCAST_SUCCESS;
        }/* end else (err != IX_SUCCESS) */     
    } 
 
   
    return err;
}/* end _ix_cc_ipv4_add_source_broadcast */




/**
 * NAME: _ix_cc_ipv4_delete_source_broadcast
 *
 * DESCRIPTION: This primitive will be used to delete source directed broadcst address into table. 
 *           .
 * 
 * @Param:  - IN arg_IpAddr - directed broadcast address to be deleted from table
 *
 * @Param:  - OUT none
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * 
 *IX_CC_ERROR_ENTRY_NOT_FOUND - entry not found in table 
 */

ix_error _ix_cc_ipv4_delete_source_broadcast(ix_uint32 arg_IpAddr)
{
    ix_uint32 tableIndex;
    ix_uint32 data;
    ix_uint32 *pBaseAddr;
    ix_uint32 lastEntry;
    ix_uint32 *pLastEntryLocation;
    ix_uint32 *pIpAddrLocation;
    ix_uint32 *pStartAddr;
    ix_uint32 *pTempAddr;
  
    
    /* arg_IpAddr is represented in network byte order */
    tableIndex = IX_CC_IPV4_DBCAST_HASH_INDEX(arg_IpAddr);

    pStartAddr = g_pIpv4Context->pMbDbBase;
    pBaseAddr = pStartAddr + (tableIndex * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE);


    /* Find the entry to be deleted */
    IX_ERROR_CR(_ix_cc_ipv4_source_broadcast_find_entry(pBaseAddr,arg_IpAddr,&pIpAddrLocation));
     
    /* Get the last valid entry */
    IX_ERROR_CR(_ix_cc_ipv4_source_broadcast_get_last_entry(pBaseAddr,&lastEntry,&pLastEntryLocation));

    /* move the last entry to pIpAddrLocation and make content of pLastEntryLocation to zero */
    ix_ossl_mutex_lock(g_pIpv4Context->ipv4Mutex,IX_OSSL_MUTEX_LOCK);
    IX_IPV4_MEM_UINT32_WRITE(pIpAddrLocation,lastEntry);
    IX_IPV4_MEM_UINT32_WRITE(pLastEntryLocation,0);
    ix_ossl_mutex_unlock(g_pIpv4Context->ipv4Mutex);

    /* If nextbucket doesn't have entries, then remove the entry from this bucket */
    pBaseAddr += IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE - 1; 
    data = IX_IPV4_MEM_UINT32_READ(pBaseAddr);
    while(data != 0)
    { 
        pTempAddr =(ix_uint32*)(pStartAddr + 
                                       (data * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE));
        data = IX_IPV4_MEM_UINT32_READ(pTempAddr);

        if((pTempAddr != 0) &&
          (data == 0))
        {
            ix_ossl_mutex_lock(g_pIpv4Context->ipv4Mutex,IX_OSSL_MUTEX_LOCK);
            IX_IPV4_MEM_UINT32_WRITE(pBaseAddr,0);
            ix_ossl_mutex_unlock(g_pIpv4Context->ipv4Mutex);
            return IX_SUCCESS;
        }/* end if(pTempAddr != 0)*/

        pBaseAddr = pTempAddr;
        pBaseAddr += IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE - 1; 
        data = IX_IPV4_MEM_UINT32_READ(pBaseAddr);
    }/* end for (index = 0; index < IX_CC_IPV4_NUMBER_OF_EXTRA_BUCKETS; index ++)*/

    

    return IX_SUCCESS;
}/* end _ix_cc_ipv4_delete_source_broadcast */



/**
 * NAME: _ix_cc_ipv4_lookup_source_broadcast
 *
 * DESCRIPTION: This primitive will be used to lookup source directed broadcst address in table. 
 *           .
 * 
 * @Param:  - IN arg_IpAddr - IP address to be searched in table
 *
 * @Param:  - OUT none
 *
 * @Return: IX_SUCCESS if successful or a valid error token for failure.
 *
 * Error Codes:
 * 
 *IX_CC_ERROR_ENTRY_NOT_FOUND - entry not found in table  
 */

ix_error _ix_cc_ipv4_lookup_source_broadcast(ix_uint32 arg_IpAddr)
{

    ix_uint32 tableIndex;
    ix_uint32* pBaseAddr;
    ix_uint32* pIpAddrLocation;
      
    /* arg_IpAddr is represented in network byte order */
    tableIndex = IX_CC_IPV4_DBCAST_HASH_INDEX(arg_IpAddr);

    pBaseAddr = g_pIpv4Context->pMbDbBase;
    pBaseAddr += tableIndex * IX_CC_IPV4_DBCAST_TABLE_BLOCK_SIZE;

    /* Find the entry */
    IX_ERROR_CR(_ix_cc_ipv4_source_broadcast_find_entry(pBaseAddr,arg_IpAddr,&pIpAddrLocation));
   
    return IX_SUCCESS;

}/* end  _ix_cc_ipv4_lookup_source_broadcast */

