/**
 * ============================================================================
 *  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-2002 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2X00 Network Processor, Release 5
 *
 * = FILENAME
 *      ix_cc_eth_core.c
 *
 * = DESCRIPTION
 *      This file contains level-0 only interface APIs and related 
 *      internal functions of ETH Rx core component.
 *
 * 
 *
 * = CHANGE HISTORY
 *      11/20/2002 - Created.
 *
 * ============================================================================
 * $Id: ix_cc_eth_rx_core.c,v 1.25 2003/12/12 00:54:52 ktseng Exp $
 */

/**
 * System defined include files required.
 */

/**
 * User defined include files required.
 */
#include "ix_cc_error.h"
#include "ix_error.h"

#include "ix_cc.h"
#include "bindings.h"
#include "ix_cc_properties.h"
#include "ix_rm.h"
#include "ix_ossl.h"

#include "cc/ix_cc_eth_rx.h"
#include "cc/internal/ix_cc_eth_rx_internal.h"

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#endif /* #if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_) */

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


/**
 * Pre-processor symbols and macros used in this file.
 */



/**
 * Extern function prototypes
 */
extern ix_cc_eth_rx_ctl_blk *g_pCcEthRxCtlBlk; /* control block */

/**
 * Static function prototypes 
 */
PRIVATE ix_error _ix_cc_eth_rx_get_channel_number(ix_cc_eth_rx_ctl_blk *, ix_uint32 , ix_uint32 *);
PRIVATE void _ix_cc_eth_rx_get_free_entry(ix_cc_eth_rx_mac_filter_table_entry *, ix_uint16, ix_uint32, ix_uint32, ix_uint32, ix_cc_eth_rx_mac_filter_table_entry **, ix_uint16 *);


/**
 * function definitions.
 */



/**
 * NAME: ix_cc_eth_rx_get_statistics_info
 * 
 * DESCRIPTION: This function retrieves statistics/counters value of 
 *              Ethernet Rx CC.
 * 
 * @Param: arg_Entity - IN - type of statistics information
 * @Param: arg_Index - IN - port for which statistics is requested
 * @Param: arg_pBuffer - IN - pointer to ix_cc_eth_rx_statistics_info_data 
 *	structure where the entity requested will be stored.
 * @Param: arg_pContext - IN pointer to the component context allocated
 *                            in the component's init function. 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_get_statistics_info(
    ix_cc_eth_rx_statistics_info arg_Entity,
    ix_uint32 arg_Index,
    ix_cc_eth_rx_statistics_info_data *arg_pBuffer,
	void *arg_pContext)
{
#ifdef MICROBLOCK_COUNTERS
    ix_uint32 numCounters, i;
    ix_counter_64bit_handle *pH64Bit;
    ix_uint32 chNum;
    ix_error err = IX_SUCCESS;
    ix_cc_eth_rx_ctl_blk *pCcContext;
#endif /* MICROBLOCK_COUNTERS */
	

    /* Check any NULL pointer argument for arg_pBuffer */
    if (arg_pBuffer == (ix_cc_eth_rx_statistics_info_data *)NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                              ("NULL argument for buffer")));
    }

    /* Check any NULL pointer argument for arg_pContext*/
    if (arg_pContext == NULL)
    {
		
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                              ("NULL argument for context")));
    }

#ifndef MICROBLOCK_COUNTERS

    return(IX_ERROR_WARNING(IX_CC_ERROR_UNIMPL,
                            ("The supported feature is disabled.")));

#else

    /*
     * Check the ranges of the arg_Entity
     * where arg_Entity stores the counter number. Port check??
     */
    if (arg_Entity >= (IX_CC_ETH_RX_ALL_MICROBLOCK_COUNTERS + 1))
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_RANGE, 
                              ("Invalid argument range for Counter Type")));
    }

	/*Cast the input context to pointer to cc context */
    pCcContext = (ix_cc_eth_rx_ctl_blk *)arg_pContext;



	/* Get the channel number based on the port id in arg_Index */
    IX_ERROR_CR(_ix_cc_eth_rx_get_channel_number(pCcContext,
                                                  arg_Index, 
                                                  &chNum));
	
   
    /*
     * Calculate the base address of the counter handle array that holds
     * counter handles pertinent to this channel based on port number 
     * indicated by arg_Index.
     */
    pH64Bit = pCcContext->pCounterHandlers64Bit + (chNum * ETH_RX_NUM_COUNTERS_PER_PORT);


    /* Determine how many counter are to be read based on the entity type */
    if (arg_Entity == IX_CC_ETH_RX_ALL_MICROBLOCK_COUNTERS)
    { 
        /* All counters of the specified port are to be returned */
        numCounters = ETH_RX_NUM_COUNTERS_PER_PORT;
    }
    else
    {
        /* Only the specified counter is to be returned */
        numCounters = 1;

        /*
         * set the handler pointer to the associated location from the 
         * base using the counter number indicated by the arg_entity.
         */
        /* Since first counter value in Enum is zero, and counters
         * maintained by microblock start after that,we need
         * to subtract 1 
         */
        pH64Bit = pH64Bit + arg_Entity - 1;
    }
	
	/*
     * Enter critical section.  It is possible that the mutex can not
     * be obtained if component has been destroyed and the mutex was 
     * freed by the fini function.)
     */
    IX_ERROR_CRT(ix_ossl_mutex_lock(pCcContext->mid,
                                    IX_OSSL_WAIT_FOREVER),
                 IX_CC_ETH_RX_ERROR_OSSL_MUTEX_LOCK, 
                 IX_ERROR_LEVEL_WARNING);
    
    /* Call RM to read the counter value */
    for (i = 0; i < numCounters; i++)
    {
        /* Call RM to retrieve the 64-bit counter value */
        IX_ERROR_CGT(ix_rm_counter_64bit_get_value(*(pH64Bit + i),
                                                   (ix_uint64 *)(arg_pBuffer->pDataBuffer) + i),
                     err,
                     IX_CC_ETH_RX_ERROR_RM_GET_64BIT_COUNTER, 
                     IX_ERROR_LEVEL_WARNING,
                     cleanup);
    }


cleanup:

    /* Exit the critical section */
    ix_ossl_mutex_unlock(pCcContext->mid);

    return(err);

#endif /* MICROBLOCK_COUNTERS */

} /* ix_cc_eth_rx_get_statistics_info */


/**
 * NAME: ix_cc_eth_rx_get_interface_state
 * 
 * DESCRIPTION: This function retrieves the link status of a Ethernet
 *              interface port.
 * 
 * @Param: arg_PortId - IN: port id
 * @Param: arg_pIfState - OUT: location where the interface state will be 
 *                            stored.
 * @Param: arg_pContext - IN: pointer to the component context allocated
 *                            in the component's init function. 
 * 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_get_interface_state(
    ix_uint32 arg_PortId, 
    ix_cc_eth_rx_if_state *arg_pIfState,
    void *arg_pContext)
{

    ix_uint32 chNum;
    ix_cc_eth_rx_ctl_blk *pCcContext;



    /* Check any NULL pointer argument */
    if ((arg_pIfState == (ix_cc_eth_rx_if_state *)NULL) ||
        (arg_pContext == (void *)NULL))
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                              ("NULL argument")));
    }

    /* Cast the input context to pointer to cc context */
    pCcContext = (ix_cc_eth_rx_ctl_blk *)arg_pContext;

    /* Get the channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_rx_get_channel_number(pCcContext,
                                                  arg_PortId, 
                                                  &chNum));

    *arg_pIfState = pCcContext->portState[chNum];

    return(IX_SUCCESS);

} /* ix_cc_eth_rx_get_interface_state */





/**
 * NAME: ix_cc_eth_rx_set_property
 * 
 * DESCRIPTION: This function sets the properties (ony sets the IF_STATE property 
 * (but can be extended).
 * 
 * @Param: arg_PropId - IN: property id
 * @Param: arg_pProperty - IN: This is pointer to the property structure passed in
 * @Param: arg_pContext - IN: pointer to the component context allocated
 *                            in the component's init function. 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_set_property(
    ix_uint32 arg_PropId,
    ix_cc_properties *arg_pProperty,
    void *arg_pContext)
{

    ix_uint32 chNum;
	ix_cc_eth_rx_ctl_blk *pCcContext;


    /* Check any NULL pointer argument */
    if (arg_pProperty == (ix_cc_properties *)NULL)
	{
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                              ("NULL argument - pointer to property")));
    }	
		
		
		
    if (arg_pContext == NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                              ("NULL argument - pointer to context")));
    }

    /* Check the property type. Currently we support only the IF_STATUS */
	/*IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS is a bit mask*/
    if ((arg_PropId & IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS) != IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_UNIMPL,
                              ("Indicated property is not supported.")));
    }

    /* Cast the input context  to pointer to cc context */
    pCcContext = (ix_cc_eth_rx_ctl_blk *)arg_pContext;

    /* Get the channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_rx_get_channel_number(pCcContext,
                                                  arg_pProperty->port_id, 
                                                  &chNum));

    /*
     * Enter critical section.  It is possible that the mutex can not 
     * be obtained if component has been destroyed and the mutex was 
     * freed by the fini function.)
     */
    IX_ERROR_CRT(ix_ossl_mutex_lock(pCcContext->mid, IX_OSSL_WAIT_FOREVER),
                 IX_CC_ETH_RX_ERROR_OSSL_MUTEX_LOCK, 
                 IX_ERROR_LEVEL_WARNING);

    /* Check for desired state value and set the status of the port */
    if (arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_UP) 
	{
		
		pCcContext->portState[chNum]= IX_CC_ETH_RX_IF_STATE_UP;

	}
	else if(arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_DOWN)
    {
		
        /*Update the port/channel status flag */
        pCcContext->portState[chNum] = IX_CC_ETH_RX_IF_STATE_DOWN;
    }
    else 
    {
        /* Exit the critical section */	
        ix_ossl_mutex_unlock(pCcContext->mid);
        return(IX_ERROR_WARNING(IX_CC_ETH_RX_ERROR_INVALID_IF_STATE,
                                  ("Invalid value for interface state.")));
    }

    
    /* Exit the critical section */
    ix_ossl_mutex_unlock(pCcContext->mid);

    return IX_SUCCESS;

} /* ix_cc_eth_rx_set_property */



/**
 * NAME: ix_cc_eth_rx_add_mac_addr
 * 
 * DESCRIPTION: This function adds a MAC address into the MAC filtering
 *              table.
 * 
 * @Param: arg_pMacAddr - IN: Pointer to a 6-byte array that contains
 *                            the MAC address (in network byte order) to be 
 *                            added into the MAC filtering table.
 * @Param: arg_PortId - IN: Output port ID associated with the MAC address
 * @Param: arg_pContext - IN: pointer to the component context allocated
 *                            in the component's init function. 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_add_mac_addr(
    ix_uint8 *arg_pMacAddr,
    ix_uint32 arg_PortId,
    void *arg_pContext)
{

    ix_uint16 curIndex;
    ix_uint16 nextIndex;
    ix_uint16 freeIndex;
    ix_uint16 numEntries = 0;
    ix_uint16 startIndex;
    ix_cc_eth_rx_mac_filter_table_entry *curEntry;
    ix_cc_eth_rx_mac_filter_table_entry *prevEntry;
    ix_cc_eth_rx_mac_filter_table_entry *freeEntry = NULL;

    /* Check any NULL pointer argument */
    if (NULL == arg_pMacAddr)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to MAC address")));
    }	

    if (NULL == arg_pContext)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to context")));
    }

    /* Use the the MAC address as the key for the hashing */
    _IX_CC_ETH_RX_MAC_FILTER_HASH(arg_pMacAddr, curIndex);
    

    curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + curIndex;

    /* Check if there is any entry in this list */
    if ((nextIndex = curEntry->index) == 0)
    {
        /**
         * This is a free entry and there is no any existing entry for 
         * this index.  So just write the MAC address and the port Id 
         * to this entry.
         */
        _IX_CC_ETH_RX_COPY_MAC_ADDR(curEntry->macAddr, arg_pMacAddr);
        curEntry->portId = (ix_uint16)arg_PortId;
        curEntry->index = curIndex;
        return(IX_SUCCESS);
    }

    /* The list contains 1 or more entries. Traverse the list */
    numEntries++;
    
    while (TRUE)
    {
        prevEntry = curEntry;
        if ((curEntry->macAddr[5] == arg_pMacAddr[5]) &&
            (curEntry->macAddr[4] == arg_pMacAddr[4]) &&
            (curEntry->macAddr[3] == arg_pMacAddr[3]) &&
            (curEntry->macAddr[2] == arg_pMacAddr[2]) &&
            (curEntry->macAddr[1] == arg_pMacAddr[1]) &&
            (curEntry->macAddr[0] == arg_pMacAddr[0]))
        {
            return(IX_ERROR_WARNING(IX_CC_ERROR_DUPLICATE_ENTRY,
                                    ("MAC is already exists in the filter table.")));
        }

        if (nextIndex == curIndex)
        {
            /* This is the last entry in the list */
            break;
        }

        numEntries++;
        /*prevEntry = curEntry;*/
        curIndex = nextIndex;
        curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + curIndex;
        nextIndex = curEntry->index;
    }

    /**
     * There is not a duplicated MAC address found. So add the new entry. 
     */
    if (numEntries == 1)
    {
        startIndex = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->filterTableNumEntriesBaseBucket;
    }
    else
    {
        startIndex = curIndex + 1; 
    }

    /* Get a free entry */
    _ix_cc_eth_rx_get_free_entry(((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase,
                                 startIndex, 
                                 ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->filterTableTotalNumEntries,
                                 ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->filterTableNumEntriesBaseBucket,
                                 ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->filterTableNumEntriesOvBucket,
                                 &freeEntry, 
                                 &freeIndex);

    if (NULL == freeEntry)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_FULL,
                                ("MAC filtering table is full.")));
    }

    /* Copy the MAC address and port ID and set the index field */
    _IX_CC_ETH_RX_COPY_MAC_ADDR(freeEntry->macAddr, arg_pMacAddr); 
    freeEntry->portId = (ix_uint16)arg_PortId;
    freeEntry->index = freeIndex;
    
    /* Append the new entry into the linked-list */
    prevEntry->index = freeIndex;

    return(IX_SUCCESS);

} /* ix_cc_eth_rx_add_mac_addr */

 

/**
 * NAME: _ix_cc_eth_rx_get_free_entry()
 * 
 * DESCRIPTION: This function obtains an unused entry from the 
 *              overflow bucket of the MAC filtering table.
 * 
 * @Param: arg_pTableBase - IN: Base address of the filter table.
 * @Param: arg_startIndex - IN: index to start with for the searching.
 * @Param: arg_TblSize - IN: the total number of entries in the table.
 * @Param: arg_BaseBucketSize - IN: the total number of entries in the base bucket.
 * @Param: arg_OvBucketSize - IN: the total number of entries in the overflow bucket.
 * @Param: arg_ppFreeEntry - OUT: pointer to store the free entry pointer.
 * @Param: arg_pFreeIndex - OUT: the index number of the free entry.
 *
 * @Return: None.
 */
PRIVATE void _ix_cc_eth_rx_get_free_entry(
    ix_cc_eth_rx_mac_filter_table_entry *arg_pFilterTblBase,
    ix_uint16 arg_StartIndex, 
    ix_uint32 arg_TblSize,
    ix_uint32 arg_BaseBucketSize,
    ix_uint32 arg_OvBucketSize,
    ix_cc_eth_rx_mac_filter_table_entry **arg_ppFreeEntry,
    ix_uint16 *arg_pFreeIndex)
{
    ix_uint32 i;
    ix_uint16 curIndex;
    ix_cc_eth_rx_mac_filter_table_entry *pCurEntry;

    curIndex = arg_StartIndex; 

    for (i = 0;
         i != arg_OvBucketSize;
         i++)
    {
        if (curIndex == arg_TblSize)
        {
            /* Start from the head of the overflow bucket */ 
            curIndex = arg_BaseBucketSize; 
        }

        pCurEntry = arg_pFilterTblBase + curIndex;
        if (pCurEntry->index == 0)
        {
            /* The entry is free to use */
            *arg_ppFreeEntry = pCurEntry;
            *arg_pFreeIndex = curIndex;
            return;
        }

        curIndex++;
    }

    /* There is not a free entry left */
    *arg_ppFreeEntry = NULL;

} /* _ic_cc_eth_rx_get_free_entry */



/**
 * NAME: ix_cc_eth_rx_del_mac_addr
 * 
 * DESCRIPTION: This function deletes MAC address information
 * from Filter table
 * 
 * @Param: arg_destMac - IN: Mac address to be deleted from Filter table
 * @Param: arg_pContext - IN: pointer to the component context allocated
 *                            in the component's init function. 
 * 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_del_mac_addr(
    ix_uint8 *arg_pMacAddr, 
    void *arg_pContext)
{ 

    ix_uint16 curIndex;
    ix_uint16 nextIndex;
    ix_uint16 prevIndex = 0;
    ix_cc_eth_rx_mac_filter_table_entry *curEntry = NULL;
    ix_cc_eth_rx_mac_filter_table_entry *prevEntry = NULL;
    ix_cc_eth_rx_mac_filter_table_entry *nextEntry = NULL;

    /* Check any NULL pointer argument */
    if (arg_pMacAddr == NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to MAC address")));
    }	

    if (arg_pContext == NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to context")));
    }

    /* Use the the MAC address as the key for the hashing */
    _IX_CC_ETH_RX_MAC_FILTER_HASH(arg_pMacAddr, curIndex);

    curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + curIndex;

    /* Check if this is a valid entry */
    if ((nextIndex = curEntry->index) == 0)
    {
        /**
         * This is not a valid entry
         */
        return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                                ("MAC filtering entry for deletion is not valid.")));
    }/* end if validity check */

    /* More than one entry in the list */
    while(curIndex != nextIndex)
    {
        /* Compare MAC address */
        if ((curEntry->macAddr[5] == arg_pMacAddr[5]) &&
            (curEntry->macAddr[4] == arg_pMacAddr[4]) &&
            (curEntry->macAddr[3] == arg_pMacAddr[3]) &&
            (curEntry->macAddr[2] == arg_pMacAddr[2]) &&
            (curEntry->macAddr[1] == arg_pMacAddr[1]) &&
            (curEntry->macAddr[0] == arg_pMacAddr[0]))
        {
            nextEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + nextIndex;
            if(prevEntry != NULL) /* middle entry in the list */
            {
                prevEntry->index = nextIndex;
                memset(curEntry,0,sizeof(ix_cc_eth_rx_mac_filter_table_entry));
            }/* end previous entry in list */                        
            else /* this is the first entry in the list */
            {
                ix_ossl_memcpy((void *)curEntry, 
                               (void *)nextEntry, 
                               sizeof(ix_cc_eth_rx_mac_filter_table_entry));
                ix_ossl_memset((void *)nextEntry,
                               0,
                               sizeof(ix_cc_eth_rx_mac_filter_table_entry));
            }
            return IX_SUCCESS;
        }/* end if Entry matches */

        prevEntry = curEntry;/* It will be use full for deleting last entry */
        prevIndex = curIndex;
        curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + nextIndex;
        curIndex = nextIndex;
        nextIndex = curEntry->index;
    }

    /* last entry in the list */
    if ((curEntry->macAddr[5] == arg_pMacAddr[5]) &&
        (curEntry->macAddr[4] == arg_pMacAddr[4]) &&
        (curEntry->macAddr[3] == arg_pMacAddr[3]) &&
        (curEntry->macAddr[2] == arg_pMacAddr[2]) &&
        (curEntry->macAddr[1] == arg_pMacAddr[1]) &&
        (curEntry->macAddr[0] == arg_pMacAddr[0]))
    {
        ix_ossl_memset((void *)curEntry,
                       0,
                       sizeof(ix_cc_eth_rx_mac_filter_table_entry));
        if(prevEntry != NULL)
        {
            prevEntry->index = prevIndex;
        }
        return IX_SUCCESS;
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                            ("MAC filtering entry for deletion is not found.")));
}



/**
 * NAME: ix_cc_eth_rx_lookup_port
 * 
 * DESCRIPTION: This function retrieves port information
 * from Filter table
 * 
 * @Param: arg_destMac - IN: Mac address to be looked up in
 *  Filter table
 * @Param: arg_pPortNum - OUT:Port information returned from lookup 
 * in Filter table
 * @Param: arg_pContext - IN: pointer to the component context allocated
 *                            in the component's init function. 
 * 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_rx_lookup_port(
    ix_uint8 *arg_pMacAddr, 
    ix_uint32 *arg_pPortNum,
    void *arg_pContext)
{
    ix_uint16 curIndex;
    ix_uint16 nextIndex;
    ix_cc_eth_rx_mac_filter_table_entry *curEntry = NULL;
    ix_cc_eth_rx_mac_filter_table_entry *prevEntry = NULL; 

    /* Check any NULL pointer argument */
    if (arg_pMacAddr == NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to MAC address")));
    }	

    if (arg_pContext == NULL)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL argument for pointer to context")));
    }

    /* Use the the MAC address as the key for the hashing */
    _IX_CC_ETH_RX_MAC_FILTER_HASH(arg_pMacAddr, curIndex);

    curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + curIndex;

    /* Check if this is a valid entry */
    if ((nextIndex = curEntry->index) == 0)
    {
        /**
         * This is not a valid entry
         */
        return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
           ("MAC filtering entry for lookup is not valid.")));
    }/* end if validity check */

    prevEntry = curEntry;
    /* More than one entry in the list */
    while(1)
    {
        /* Compare MAC address */
        if ((curEntry->macAddr[5] == arg_pMacAddr[5]) &&
            (curEntry->macAddr[4] == arg_pMacAddr[4]) &&
            (curEntry->macAddr[3] == arg_pMacAddr[3]) &&
            (curEntry->macAddr[2] == arg_pMacAddr[2]) &&
            (curEntry->macAddr[1] == arg_pMacAddr[1]) &&
            (curEntry->macAddr[0] == arg_pMacAddr[0]))
        {

            *arg_pPortNum = curEntry->portId;
            ix_ossl_message_log("Port ID looked up in MAC filter table is %ld\n",
            *arg_pPortNum);
            return IX_SUCCESS;
        }/* end if Entry matches */
        if (nextIndex == curIndex )
        {
            /* this is the last entry in the list */
            break;

        }
        prevEntry = curEntry;
        curIndex = nextIndex;
        curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase + curIndex;
        nextIndex = curEntry->index;
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                            ("MAC filtering entry for lookup is not found.")));

}


/*
 * NAME: append_mac_entry
 *
 * DESCRIPTION: Helper function for dumping the MAC Filter table.  Adds a single entry
 * to the end of the buffer.
 */
static int append_mac_entry(
        char *arg_pBuffer,
        unsigned int *pBufSizeLeft,
        ix_uint8 mac[],
        int port,
        int index, ix_uint16 curIndex)
       
{
    char macString[32];          

    /*
     * If we can't store more lines, drop on the terminator and quit.
     */
    if (*pBufSizeLeft < IX_CC_ETH_RX_LINE_SIZE+1)
    {
        /*
         * If there's not enough room for the terminator, back the string
         * up by the line size before appending the terminator.
         */
        if (*pBufSizeLeft < IX_CC_ETH_RX_TERM_SIZE+1)
        {
            arg_pBuffer[strlen(arg_pBuffer)-IX_CC_ETH_RX_LINE_SIZE] = '\0';
        }
        strcat(arg_pBuffer, IX_CC_ETH_RX_TERM_STRING);
        return 1;
    }

    sprintf(macString, "%d/%x:%x:%x:%x:%x:%x/%d/%d\n",
        curIndex - 1, mac[0],mac[1], mac[2], mac[3], mac[4], mac[5],
        port,index);

    
    strcat(arg_pBuffer,macString);

    *pBufSizeLeft -= IX_CC_ETH_RX_LINE_SIZE;

    return 0;
}

/* This function is useful for debugging and printing what is there in MAC filter
 * table. Run ix_cc_eth_rx_dump_mac_filter_tbl on ingress shell to see the contents
 * of MAC filter table 
 */
ix_error ix_cc_eth_rx_dump_mac_filter_tbl(void)
{
    ix_error err = IX_SUCCESS;
    char *pBuffer = ix_ossl_malloc(4000 * sizeof(char));
    ix_uint32 numOfEntries = 4000;
    err = ix_cc_eth_rx_dump_mac_filter_table(g_pCcEthRxCtlBlk,
                                            pBuffer, numOfEntries);
    if (err != IX_SUCCESS)
    {
        ix_ossl_free(pBuffer);
		return IX_ERROR_WARNING(IX_CC_ERROR_FULL,
             ("Buffer is full\n")); 
    }          
    
    ix_ossl_free(pBuffer);  
    return IX_SUCCESS;     

}


ix_error ix_cc_eth_rx_dump_mac_filter_table(void *arg_pContext,
                                         char *arg_pBuffer,
                                            ix_uint32 arg_BufferSize)
{
	unsigned int bufSizeLeft = arg_BufferSize;
    ix_uint16 curIndex;
    ix_cc_eth_rx_mac_filter_table_entry *curEntry = NULL;
    

    /* Check for arguments */
	IX_ERROR_DCHECK_ARG_NULL(2, arg_pBuffer);
    IX_ERROR_DCHECK_ARG_MIN(3, arg_BufferSize, 1UL, "%ld");

    if (bufSizeLeft < IX_CC_ETH_RX_TITLE_SIZE + IX_CC_ETH_RX_TERM_SIZE + 1)
    {
        arg_pBuffer[0] = '\0';
        return IX_ERROR_WARNING(IX_CC_ERROR_FULL,
            ("Buffer is too small (%ld bytes), should be at least %ld bytes\n",
              arg_BufferSize,
             IX_CC_ETH_RX_TITLE_SIZE + IX_CC_ETH_RX_TERM_SIZE + 1
             ));
    }

    strcpy(arg_pBuffer, IX_CC_ETH_RX_TITLE_STRING);
    bufSizeLeft -= IX_CC_ETH_RX_TITLE_SIZE;

    /* Get the first entry in the table */
    curEntry = ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->pFilterTableBase;
    curIndex = curEntry->index;
     
    /*
     * Iterate through the table getting the entries one at a time
     * and adding them to the buffer.
     */
   
    for (curIndex = 1; 
        curIndex < ((ix_cc_eth_rx_ctl_blk *)arg_pContext)->filterTableTotalNumEntries;
          curIndex++)
    {
		/* If it is free entry, skip appending it to character buffer */
        if (curEntry->index != 0)
        { 
            
			if(append_mac_entry(arg_pBuffer, &bufSizeLeft, curEntry->macAddr, 
   		      curEntry->portId, curEntry->index, curIndex))
			{
                
				return IX_ERROR_WARNING(IX_CC_ERROR_FULL,
 	           ("Buffer is too small (%ld bytes)\n",
  	           arg_BufferSize));
			}
		}
        /* Go to next entry */
		curEntry +=1;

	}
    


    ix_ossl_message_log("%s ",arg_pBuffer);
    return IX_SUCCESS;

}


/**
 * NAME: _ix_cc_eth_rx_get_channel_number
 * 
 * DESCRIPTION: This function retrieves channel number based on the port id.
 *              
 * 
 * @Param: arg_pCtlBlk - IN: the component control block structure
 * @Param: arg_PortId - IN: the Port Id for which we need the channel number
 * @Param: arg_pBuffer - OUT: location where channel num is to be stored.
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
PRIVATE ix_error _ix_cc_eth_rx_get_channel_number(
    ix_cc_eth_rx_ctl_blk *arg_pCtlBlk, 
    ix_uint32 arg_PortId, 
    ix_uint32 *arg_ChNum)
{

    ix_uint32 i;

    /* check to see if the portId is valid 
     * by searching through the array of valid channels
     */
    for (i = 0; i < arg_pCtlBlk->numChannels; i++)
    {
        if (arg_pCtlBlk->portId[i] == arg_PortId)
        {
            *arg_ChNum = i;
            return IX_SUCCESS;
        }
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                          ("No channel found for the specifed port id.")));
}
