/**
 * ============================================================================
 * = 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 IXP2X00 Network Processor, Release 6
 *
 * = FILENAME
 *      ix_cc_eth_tx_libr.c
 *
 * = DESCRIPTION
 *      This file contains Library APIs and related internal functions of
 *      Ethernet Tx core component.
 *
 * = CHANGE HISTORY
 *      11/21/2002 - Created.
 *
 * ============================================================================
 * $Id: ix_cc_eth_tx_libr.c,v 1.54 2003/12/12 20:29:14 ttschlue Exp $
 */

/**
 * System defined include files required.
 */
#include "ix_cc_error.h"

#include "ix_types.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"

#ifndef IX_EXCLUDE_CCI
#include "ix_cci.h"
#endif


/**
 * User defined include files required.
 */
#include "cc/ix_cc_eth_tx.h"
#include "cc/internal/ix_cc_eth_tx_int.h"
#include "cc/ix_cc_arp.h"


/* device driver common includes */
#if (_IX_BOARD_TYPE_ == _IX_IXDP2401_) || (_IX_BOARD_TYPE_ == _IX_IXDP2801_)

#include "ixf1104ce_driver_api.h"

#else /* _IX_BOARD_TYPE_ not _IX_IXDP2x01_ */

#if defined(IX_PLATFORM_2400)

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
#include "ixf1104ce_ioctl.h"
#include "ixf1104ce_linux_driver.h"
#include "ixf1104ce_ioctl.h"
#include "ixf1104ce_internal.h"
#include "cc/internal/ix_cc_eth_tx_drv_wrapper.h"
#else
#include "ixf1104ce_ioctl.h"
#include "ixf1104ce_driver_api.h"
#include "ixf1104ce_internal.h"
#endif

#elif defined(IX_PLATFORM_2800)

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
#include "common_types.h"
#include "common_def.h"
#include "bld1110.h"
#include "ixf_api_ether_d.h"
#include "ixf_api_ether.h"
#include "ixf_api_d.h" /* BroadBand Utility Definitions and Function headers */
#include "ixf_api.h"
#include "hal.h"
#include "ixf1110_xgmac_d.h"
#else
/* Include VxWorks media driver header files here */
#endif

#endif /* defined(IX_PLATFORM_2800) */

#endif /* _IX_BOARD_TYPE_ */

#if (defined IX_PLATFORM_2401) || (defined IX_PLATFORM_2801)
#if (defined USE_BB_DRV_CONFIG)
/* extern definition for HW_CONFIG information provided in xml config file */
extern ix_uint32 hwConfigEthPortMask;/* ETH Port Mask */
#endif /* USE_HW_CONFIG || USE_BB_DRV_CONFIG */
#endif /* IX_PLATFORM_2x01 */

/**
 * Static function prototypes
 */
PRIVATE ix_error _ix_cc_eth_tx_set_property_if_status(
    ix_uint32,
    ix_cc_properties *,
    ix_cc_eth_tx_ctl_blk *);

PRIVATE ix_error _ix_cc_eth_tx_set_property_mac_addr(
    ix_uint32,
    ix_cc_properties *,
    ix_cc_eth_tx_ctl_blk *);


#if defined(IX_PLATFORM_2800)

/**
 * Variable declarations global to this file only.
 */
/* Driver counter type table for Tx statistics */
bb_SelCounters_e g_aDrvTxStatTypes[IX_CC_ETH_TX_NUM_DRIVER_COUNTERS_PER_PORT] =
{
    bb_XGMAC_TX_OK_OCTS_CNT,
    bb_XGMAC_TX_BAD_OCTS_CNT,
    bb_XGMAC_TX_UNICAST_FRM_CNT,
    bb_XGMAC_TX_OK_MLTCAST_FRM_CNT,
    bb_XGMAC_TX_OK_BRDCAST_FRM_CNT,
    bb_XGMAC_TX_64_OCT_FRM_CNT,
    bb_XGMAC_TX_65_TO_127_OCT_FRM_CNT,
    bb_XGMAC_TX_128_TO_255_FRM_CNT,
    bb_XGMAC_TX_256_TO_511_FRM_CNT,
    bb_XGMAC_TX_OK_512_TO_1023_CNT,
    bb_XGMAC_TX_OK_1024_TO_15XX_CNT,
    bb_XGMAC_TX_OK_15XX_TO_MAX_CNT,
    bb_XGMAC_TX_DEFERRED_CNT,
    bb_XGMAC_TX_TOTAL_COLLISIONS_CNT,
    bb_XGMAC_TX_SINGLE_COLLISIONS_CNT,
    bb_XGMAC_TX_MULTIPLE_COLLISIONS_CNT,
    bb_XGMAC_TX_LATE_COLLISIONS_CNT,
    bb_XGMAC_TX_EXCESSIVE_COLLISIONS_CNT,
    bb_XGMAC_TX_EXCESSIVE_DEFERRAL_CNT,
    bb_XGMAC_TX_EXCESSIVE_LENGTH_DROP_CNT,
    bb_XGMAC_TX_UNDERRUN_CNT,
    bb_XGMAC_TX_TAGGED_CNT,
    bb_XGMAC_TX_CRC_ERR_CNT,
    bb_XGMAC_TX_PAUSE_CTRL_FRM_CNT,
    bb_XGMAC_TX_FLOW_CTL_COLLISIONS_SEND_CNT
};

/* Driver ioctl command table for Rx statistics */
bb_SelCounters_e g_aDrvRxStatTypes[IX_CC_ETH_RX_NUM_DRIVER_COUNTERS_PER_PORT] =
{
    bb_XGMAC_RX_OK_OCTS_CNT,
    bb_XGMAC_RX_BAD_OCTS_CNT,
    bb_XGMAC_RX_UNICAST_FRM_CNT,
    bb_XGMAC_RX_OK_MLTCAST_FRM_CNT,
    bb_XGMAC_RX_OK_BRDCAST_FRM_CNT,
    bb_XGMAC_RX_64_OCT_FRM_CNT,
    bb_XGMAC_RX_65_TO_127_OCT_FRM_CNT,
    bb_XGMAC_RX_128_TO_255_FRM_CNT,
    bb_XGMAC_RX_256_TO_511_FRM_CNT,
    bb_XGMAC_RX_OK_512_TO_1023_CNT,
    bb_XGMAC_RX_OK_1024_TO_15XX_CNT,
    bb_XGMAC_RX_OK_15XX_TO_MAX_CNT,
    bb_XGMAC_RX_FRM_CHK_SEQ_ERR_CNT,
    bb_XGMAC_RX_TAGGED_FRM_CNT,
    bb_XGMAC_RX_DATA_ERR_CNT,
    bb_XGMAC_RX_ALIGN_ERR_CNT,
    bb_XGMAC_RX_LONG_ERR_CNT,
    bb_XGMAC_RX_ETHER_STATS_JABBERS_CNT,
    bb_XGMAC_RX_PAUSE_CTRL_FRM_CNT,
    bb_XGMAC_RX_UNKNOWN_CTL_FRM_CNT,
    bb_XGMAC_RX_VERYLONG_ERR_FRM_CNT,
    bb_XGMAC_RX_RUNT_ERR_FRM_CNT,
    bb_XGMAC_RX_SHORT_ERR_FRM_CNT,
    bb_XGMAC_RX_SEQUENCE_ERR_CNT,
    bb_XGMAC_RX_SYMBOL_ERR_CNT
};

#else /* defined(IX_PLATFORM_2800) */

/**
 * Variable declarations global to this file only.
 */
/* Driver ioctl command table for Tx statistics */
ix_uint32 g_aDrvTxStatIoctlCmds[IX_CC_ETH_TX_NUM_DRIVER_COUNTERS_PER_PORT] =
{
    GET_TX_OCTETS_OK,
    GET_TX_OCTETS_BAD,
    GET_TX_UC_PKTS,
    GET_TX_MC_PKTS,
    GET_TX_BC_PKTS,
    GET_TX_PKTS_64,
    GET_TX_PKTS_65_127,
    GET_TX_PKTS_128_255,
    GET_TX_PKTS_256_511,
    GET_TX_PKTS_512_1023,
    GET_TX_PKTS_1024_1518,
    GET_TX_PKTS_1519_MAX,
    GET_TX_DEFERRED_ERR,
    GET_TX_TOTAL_COLLISION,
    GET_TX_SINGLE_COLLISION,
    GET_TX_MUL_COLLISION,
    GET_LATE_COLLISION,
    GET_TX_EXCV_COLLISION,
    GET_TX_EXCV_DEFERRED_ERR,
    GET_TX_EXCV_LEN_DROP,
    GET_TX_UNDERRUN,
    GET_TX_VLAN_TAG,
    GET_TX_CRC_ERR,
    GET_TX_PAUSE_FRAME,
    GET_FC_COLLISION_SEND
};

/* Driver ioctl command table for Rx statistics */
ix_uint32 g_aDrvRxStatIoctlCmds[IX_CC_ETH_RX_NUM_DRIVER_COUNTERS_PER_PORT] =
{
    GET_RX_OCTETS_OK,
    GET_RX_OCTETS_BAD,
    GET_RX_UC_PKTS,
    GET_RX_MC_PKTS,
    GET_RX_BC_PKTS,
    GET_RX_PKTS_64,
    GET_RX_PKTS_65_127,
    GET_RX_PKTS_128_255,
    GET_RX_PKTS_256_511,
    GET_RX_PKTS_512_1023,
    GET_RX_PKTS_1024_1518,
    GET_RX_PKTS_1519_MAX,
    GET_RX_FCS_ERR,
    GET_VLAN_TAG,
    GET_RX_DATA_ERR,
    GET_RX_ALLIGN_ERR,
    GET_RX_LONG_ERR,
    GET_RX_JABBER_ERR,
    GET_RX_PAUSE_MAC_CTL,
    GET_RX_UNKNOWN_CTL_FRAME,
    GET_VLONG_ERR,
    GET_RUNT_ERR,
    GET_SHORT_ERR,
    GET_SEQ_ERR,
    GET_SYMBOL_ERR
};

#endif /* defined(IX_PLATFORM_2800) */


#if (defined IX_PLATFORM_2401) || (defined IX_PLATFORM_2801)
#if ((defined USE_BB_DRV_CONFIG) && (_IX_BOARD_TYPE_ == _IX_IXDP2401_))
char _ix_cc_eth_get_hw_channel(ix_uint32 i)
{
	int		j;

	for(j = 0; j < 4; j++)
	{
		if(((1 << (i + j*4)) & hwConfigEthPortMask) != 0)
		   return (j * 4);
	}
	return 0;
}

ix_error _ix_cc_eth_get_channel_number_2401(
    ix_cc_eth_tx_ctl_blk *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_uint32 *arg_ChNum)
{
    ix_uint32 i;
    for (i = 0; i < IX_CC_ETH_TX_MAX_NUM_PORTS; i++)
    {
        if (arg_pCtlBlk->portId[i] == arg_PortId)
        {
            *arg_ChNum = i + _ix_cc_eth_get_hw_channel(arg_PortId);
            return(IX_SUCCESS);
        }
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                            ("Could not find the specifed port id.")));
}
#endif /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */
#endif /* IX_PLATFORM_2x01 */


/**
 * function definitions.
 */


/**
 * NAME: ix_cc_eth_tx_get_statistics_info
 *
 * DESCRIPTION: This function retrieves one or multiple statistics counter
 *              of Eth Tx CC.
 *
 * @Param: arg_Entity - IN: type of statistics info.
 * @Param: arg_Index - IN: interface port id.
 * @Param: arg_pBuffer - IN: pointer to the location for storing the data.
 * @Param: arg_pContext - IN: pointer to the component context number
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
#if defined(IX_PLATFORM_2800)

ix_error ix_cc_eth_tx_get_statistics_info(
    ix_cc_eth_tx_statistics_info arg_Entity,
    ix_uint32 arg_Index,
    ix_cc_eth_tx_statistics_info_data *arg_pBuffer,
    void *arg_pContext)
{
    ix_cc_eth_tx_ctl_blk *pCcContext;
    BOOL drvCounterType = TRUE;
    ix_uint32 numCounters = 1;
    ix_uint32 chNum;
    ix_uint32 i;

    /* Driver related local variables */
    bb_SelCounters_e statType = 0;
    bb_SelCounters_e *statTypeTbl = (bb_SelCounters_e *)NULL;
    bb_ChipSegment_t section;
    ix_uint32 counterValue = 0;
    ix_uint32 bb_err = 0;

#ifdef MICROBLOCK_COUNTERS

    ix_uint32 seqNum = 0;
    ix_counter_64bit_handle *pH64Bit;

#endif /* MICROBLOCK_COUNTERS */

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

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

    /* Check the ranges of arg_Entity. */
    if (arg_Entity >= IX_CC_ETH_TX_COUNTER_LAST)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
                                ("Invalid counter type.")));
    }

    /* Get the component control block pointer */
    pCcContext = (ix_cc_eth_tx_ctl_blk *)arg_pContext;

    /* Use the port id in arg_Index to get the physical channel number */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(pCcContext,
                                              arg_Index,
                                              &chNum));

    /**
     * Set the sequence number or associated ioctl command for
     * ublk counters and driver counters.
     */
    switch(arg_Entity)
    {

#ifdef MICROBLOCK_COUNTERS

        /**
         * For ublk counters, set the sequence number and change the
         * drvCounterType (default TRUE) and numberCounters (default 1).
         */
        case IX_CC_ETH_TX_PACKETS:
        case IX_CC_ETH_TX_DROP_PACKETS:
        case IX_CC_ETH_TX_BYTES:
        case IX_CC_ETH_TX_DROP_BYTES:
            /*
             * IX_CC_ETH_TX_PACKETS is the first ublk counter in the
             * enum definition (order defined by microblock code).  Use
             * it to derive the counter sequence number.
             */
            drvCounterType = FALSE;
            seqNum = arg_Entity - IX_CC_ETH_TX_PACKETS;
            break;

        case IX_CC_ETH_TX_ALL_UBLK_COUNTERS:
            drvCounterType = FALSE;
            seqNum = 0; /* starting from the first counter */
            numCounters = IX_CC_ETH_TX_NUM_UBLK_COUNTERS_PER_PORT;
            break;

#endif /* MICROBLOCK_COUNTERS */

        /**
         * For driver counters, set the counter type.
         */

        /*****************************
         * Device driver Tx counters
         *****************************/

        case IX_CC_ETH_TX_OCTETS_OK:
            statType = bb_XGMAC_TX_OK_OCTS_CNT;
            break;

        case IX_CC_ETH_TX_OCTETS_BAD:
            statType = bb_XGMAC_TX_BAD_OCTS_CNT;
            break;

        case IX_CC_ETH_TX_UC_PKTS:
            statType = bb_XGMAC_TX_UNICAST_FRM_CNT;
            break;

        case IX_CC_ETH_TX_MC_PKTS:
            statType = bb_XGMAC_TX_OK_MLTCAST_FRM_CNT;
            break;

        case IX_CC_ETH_TX_BC_PKTS:
            statType = bb_XGMAC_TX_OK_BRDCAST_FRM_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_64:
            statType = bb_XGMAC_TX_64_OCT_FRM_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_65_127:
            statType = bb_XGMAC_TX_65_TO_127_OCT_FRM_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_128_255:
            statType = bb_XGMAC_TX_128_TO_255_FRM_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_256_511:
            statType = bb_XGMAC_TX_256_TO_511_FRM_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_512_1023:
            statType = bb_XGMAC_TX_OK_512_TO_1023_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_1024_1518:
            statType = bb_XGMAC_TX_OK_1024_TO_15XX_CNT;
            break;

        case IX_CC_ETH_TX_PKTS_1519_MAX:
            statType = bb_XGMAC_TX_OK_15XX_TO_MAX_CNT;
            break;

        case IX_CC_ETH_TX_DEFERED_ERR:
            statType = bb_XGMAC_TX_DEFERRED_CNT;
            break;

        case IX_CC_ETH_TX_TOTAL_COLLISION:
            statType = bb_XGMAC_TX_TOTAL_COLLISIONS_CNT;
            break;

        case IX_CC_ETH_TX_SINGLE_COLLISION:
            statType = bb_XGMAC_TX_SINGLE_COLLISIONS_CNT;
            break;

        case IX_CC_ETH_TX_MUL_COLLISION:
            statType = bb_XGMAC_TX_MULTIPLE_COLLISIONS_CNT;
            break;

        case IX_CC_ETH_TX_LATE_COLLISION:
            statType = bb_XGMAC_TX_LATE_COLLISIONS_CNT;
            break;

        case IX_CC_ETH_TX_EXCV_COLLISION:
            statType = bb_XGMAC_TX_EXCESSIVE_COLLISIONS_CNT;
            break;

        case IX_CC_ETH_TX_EXCV_DEFERRED_ERR:
            statType = bb_XGMAC_TX_EXCESSIVE_DEFERRAL_CNT;
            break;

        case IX_CC_ETH_TX_EXCV_LEN_DROP:
            statType = bb_XGMAC_TX_EXCESSIVE_LENGTH_DROP_CNT;
            break;

        case IX_CC_ETH_TX_UNDERRUN:
            statType = bb_XGMAC_TX_UNDERRUN_CNT;
            break;

        case IX_CC_ETH_TX_VLAN_TAG:
            statType = bb_XGMAC_TX_TAGGED_CNT;
            break;

        case IX_CC_ETH_TX_CRC_ERR:
            statType = bb_XGMAC_TX_CRC_ERR_CNT;
            break;

        case IX_CC_ETH_TX_PAUSE_FRAME:
            statType = bb_XGMAC_TX_PAUSE_CTRL_FRM_CNT;
            break;

        case IX_CC_ETH_TX_FC_COLLISION_SEND:
            statType = bb_XGMAC_TX_FLOW_CTL_COLLISIONS_SEND_CNT;
            break;

        case IX_CC_ETH_TX_ALL_DRIVER_COUNTERS:

            /* Update the numCounters */
            numCounters = IX_CC_ETH_TX_NUM_DRIVER_COUNTERS_PER_PORT;

            /* Select the Tx counter type table */
            statTypeTbl = g_aDrvTxStatTypes;
            break;

        /*****************************
         * Device driver Rx counters
         *****************************/

        case IX_CC_ETH_RX_OK_OCTETS:
            statType = bb_XGMAC_RX_OK_OCTS_CNT;
            break;

        case IX_CC_ETH_RX_BAD_OCTETS:
            statType = bb_XGMAC_RX_BAD_OCTS_CNT;
            break;

        case IX_CC_ETH_RX_UCAST_PACKETS:
            statType = bb_XGMAC_RX_UNICAST_FRM_CNT;
            break;

        case IX_CC_ETH_RX_MCAST_PACKETS:
            statType = bb_XGMAC_RX_OK_MLTCAST_FRM_CNT;
            break;

        case IX_CC_ETH_RX_BCAST_PACKETS:
            statType = bb_XGMAC_RX_OK_BRDCAST_FRM_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_64:
            statType = bb_XGMAC_RX_64_OCT_FRM_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_65_127:
            statType = bb_XGMAC_RX_65_TO_127_OCT_FRM_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_128_255:
            statType = bb_XGMAC_RX_128_TO_255_FRM_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_256_511:
            statType = bb_XGMAC_RX_256_TO_511_FRM_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_512_1023:
            statType = bb_XGMAC_RX_OK_512_TO_1023_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_1024_1518:
            statType = bb_XGMAC_RX_OK_1024_TO_15XX_CNT;
            break;

        case IX_CC_ETH_RX_PACKETS_1519_MAX:
            statType = bb_XGMAC_RX_OK_15XX_TO_MAX_CNT;
            break;

        case IX_CC_ETH_RX_FCS_ERR:
            statType = bb_XGMAC_RX_FRM_CHK_SEQ_ERR_CNT ;
            break;

        case IX_CC_ETH_RX_VLAN_TAG:
            statType = bb_XGMAC_RX_VLAN_FRM_CNT;
            break;

        case IX_CC_ETH_RX_DATA_ERR:
            statType = bb_XGMAC_RX_DATA_ERR_CNT;
            break;

        case IX_CC_ETH_RX_ALIGN_ERR:
            statType = bb_XGMAC_RX_ALIGN_ERR_CNT;
            break;

        case IX_CC_ETH_RX_LONG_ERR:
            statType = bb_XGMAC_RX_LONG_ERR_CNT;
            break;

        case IX_CC_ETH_RX_JABBER_ERR:
            statType = bb_XGMAC_RX_ETHER_STATS_JABBERS_CNT;
            break;

        case IX_CC_ETH_RX_PAUSE_MAC_CTL:
            statType = bb_XGMAC_RX_PAUSE_CTRL_FRM_CNT;
            break;

        case IX_CC_ETH_RX_UNKNOWN_CTL_FRAME:
            statType = bb_XGMAC_RX_UNKNOWN_CTL_FRM_CNT;
            break;

        case IX_CC_ETH_RX_VLONG_ERR:
            statType = bb_XGMAC_RX_VERYLONG_ERR_FRM_CNT;
            break;

        case IX_CC_ETH_RX_GET_RUNT_ERR:
            statType = bb_XGMAC_RX_RUNT_ERR_FRM_CNT;
            break;

        case IX_CC_ETH_RX_GET_SHORT_ERR:
            statType = bb_XGMAC_RX_SHORT_ERR_FRM_CNT;
            break;

        case IX_CC_ETH_RX_GET_SEQ_ERR:
            statType = bb_XGMAC_RX_SEQUENCE_ERR_CNT;
            break;

        case IX_CC_ETH_RX_GET_SYMBOL_ERR:
            statType = bb_XGMAC_RX_SYMBOL_ERR_CNT;
            break;

        case IX_CC_ETH_RX_ALL_DRIVER_COUNTERS:

            /* Update the numCounters */
            numCounters = IX_CC_ETH_RX_NUM_DRIVER_COUNTERS_PER_PORT;

            /* Select the Rx counter type table */
            statTypeTbl = g_aDrvRxStatTypes;
            break;

        default:
            return(IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
                                    ("Invalid counter type.")));
            break;
    }

    /* Retrieve the counter value based on the counter type */
    if (drvCounterType == TRUE)
    {
        /* Identify the location of the counters in the chip */
        section.block.side = LINE; /* line side of the chip */
        section.block.block = ixf_eXGMAC;
        section.group.channel = chNum;

        /**
         * Call driver API to retrieve counter value.
         */
        if (numCounters == 1) /* single counter */
        {
            /* For single counter, call the API once. */
            if ((bb_err = IxfApiGetCounters(pCcContext->pChipData,
                                  &section,
                                  statType,
                                  (void *)&counterValue)) != bb_NO_ERROR)
            {
#if IX_DEBUG
                ix_ossl_message_log("Eth Tx CC: Call to IxfApiGetCounters failed.  Error code is %ld\n", bb_err);
#endif
                return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                        ("Call to IxfApiGetCounters function failed.")));
            }

            /* Assign the return value (64-bit) to output argument */
            *((ix_uint64 *)(arg_pBuffer->pDataBuffer)) = counterValue;
        }
        else /* multiple counters */
        {
            for (i = 0; i < numCounters; i++)
            {
                if ((bb_err = IxfApiGetCounters(pCcContext->pChipData,
                                      &section,
                                      statTypeTbl[i],
                                      (void *)&counterValue)) != bb_NO_ERROR)
                {
#if IX_DEBUG
                    ix_ossl_message_log("Eth Tx CC: Call to IxfApiGetCounters failed.  Error code is %ld\n", bb_err);
#endif
                    return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                            ("Call to IxfApiGetCOunters function failed.")));
                }

                /* Assign the return value (64-bit) to output argument */
                *((ix_uint64 *)(arg_pBuffer->pDataBuffer) + i) = (ix_uint64)counterValue;
            }
        }
    } /* end of (drvCounterType == TRUE) */

#ifdef MICROBLOCK_COUNTERS

    /**
     * Calculate the base address of the counter handle array that holds
     * counter handles associated with this channel.
     */
    pH64Bit = &(pCcContext->ahCounterHandlers64Bit[chNum * IX_CC_ETH_TX_NUM_UBLK_COUNTERS_PER_PORT]);

    /**
     * Adjust the handler pointer to the associated location from the
     * base using the sequence number obtained earlier.
     */
    pH64Bit = pH64Bit + seqNum;

    /* Call RM to read the counter value */
    for (i = 0; i < numCounters; i++)
    {
        IX_ERROR_CRT(ix_rm_counter_64bit_get_value(*(pH64Bit + i),
                                                   (ix_uint64 *)(arg_pBuffer->pDataBuffer) + i),
                     IX_CC_ETH_TX_ERROR_RM_GET_64BIT_COUNTER,
                     IX_ERROR_LEVEL_WARNING);
    }

#endif /* MICROBLOCK_COUNTERS */

    return(IX_SUCCESS);

} /* ix_cc_eth_tx_get_statistics_info */

#else /* defined(IX_PLATFORM_2800) */

ix_error ix_cc_eth_tx_get_statistics_info(
    ix_cc_eth_tx_statistics_info arg_Entity,
    ix_uint32 arg_Index,
    ix_cc_eth_tx_statistics_info_data *arg_pBuffer,
    void *arg_pContext)
{
    ix_cc_eth_tx_ctl_blk *pCcContext;
    BOOL drvCounterType = TRUE;
    ix_uint32 numCounters = 1;
    ix_uint32 chNum;
    ix_uint32 i;

    /* Driver related local variables */
    gbe_mac_ioctl_ptr_64 macIoctlInfo;
    ix_uint32 drvIoctlCmd = 0;
    ix_uint32 *drvStatIoctlCmdsTbl = (ix_uint32 *)NULL;

#ifdef MICROBLOCK_COUNTERS

    ix_uint32 seqNum = 0;
    ix_counter_64bit_handle *pH64Bit;

#endif /* MICROBLOCK_COUNTERS */

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

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

    /* Check the ranges of arg_Entity. */
    if (arg_Entity >= IX_CC_ETH_TX_COUNTER_LAST)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
                                ("Invalid counter type.")));
    }

    /* Get the component control block pointer */
    pCcContext = (ix_cc_eth_tx_ctl_blk *)arg_pContext;

    /* Use the port id in arg_Index to get the physical channel number */
	#if ((defined USE_BB_DRV_CONFIG) && (_IX_BOARD_TYPE_ == _IX_IXDP2401_))
    IX_ERROR_CR(_ix_cc_eth_get_channel_number_2401(pCcContext,
                                              arg_Index,
                                              &chNum));
	#else /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(pCcContext,
                                              arg_Index,
                                              &chNum));
	#endif /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */

    /**
     * Set the sequence number or associated ioctl command for
     * ublk counters and driver counters.
     */
    switch(arg_Entity)
    {

#ifdef MICROBLOCK_COUNTERS

        /**
         * For ublk counters, set the sequence number and change the
         * drvCounterType (default TRUE) and numberCounters (default 1).
         */
        case IX_CC_ETH_TX_PACKETS:
        case IX_CC_ETH_TX_DROP_PACKETS:
        case IX_CC_ETH_TX_BYTES:
        case IX_CC_ETH_TX_DROP_BYTES:
            /*
             * IX_CC_ETH_TX_PACKETS is the first ublk counter in the
             * enum definition (order defined by microblock code).  Use
             * it to derive the counter sequence number.
             */
            drvCounterType = FALSE;
            seqNum = arg_Entity - IX_CC_ETH_TX_PACKETS;
            break;

        case IX_CC_ETH_TX_ALL_UBLK_COUNTERS:
            drvCounterType = FALSE;
            seqNum = 0; /* starting from the first counter */
            numCounters = IX_CC_ETH_TX_NUM_UBLK_COUNTERS_PER_PORT;
            break;

#endif /* MICROBLOCK_COUNTERS */

        /**
         * For driver counters, set the IOCTL command.
         */

        /*****************************
         * Device driver Tx counters
         *****************************/

        case IX_CC_ETH_TX_OCTETS_OK:
            drvIoctlCmd = GET_TX_OCTETS_OK;
            break;

        case IX_CC_ETH_TX_OCTETS_BAD:
            drvIoctlCmd = GET_TX_OCTETS_BAD;
            break;

        case IX_CC_ETH_TX_UC_PKTS:
            drvIoctlCmd = GET_TX_UC_PKTS;
            break;

        case IX_CC_ETH_TX_MC_PKTS:
            drvIoctlCmd = GET_TX_MC_PKTS;
            break;

        case IX_CC_ETH_TX_BC_PKTS:
            drvIoctlCmd = GET_TX_BC_PKTS;
            break;

        case IX_CC_ETH_TX_PKTS_64:
            drvIoctlCmd = GET_TX_PKTS_64;
            break;

        case IX_CC_ETH_TX_PKTS_65_127:
            drvIoctlCmd = GET_TX_PKTS_65_127;
            break;

        case IX_CC_ETH_TX_PKTS_128_255:
            drvIoctlCmd = GET_TX_PKTS_128_255;
            break;

        case IX_CC_ETH_TX_PKTS_256_511:
            drvIoctlCmd = GET_TX_PKTS_256_511;
            break;

        case IX_CC_ETH_TX_PKTS_512_1023:
            drvIoctlCmd = GET_TX_PKTS_512_1023;
            break;

        case IX_CC_ETH_TX_PKTS_1024_1518:
            drvIoctlCmd = GET_TX_PKTS_1024_1518;
            break;

        case IX_CC_ETH_TX_PKTS_1519_MAX:
            drvIoctlCmd = GET_TX_PKTS_1519_MAX;
            break;

        case IX_CC_ETH_TX_DEFERED_ERR:
            drvIoctlCmd = GET_TX_DEFERRED_ERR;
            break;

        case IX_CC_ETH_TX_TOTAL_COLLISION:
            drvIoctlCmd = GET_TX_TOTAL_COLLISION;
            break;

        case IX_CC_ETH_TX_SINGLE_COLLISION:
            drvIoctlCmd = GET_TX_SINGLE_COLLISION;
            break;

        case IX_CC_ETH_TX_MUL_COLLISION:
            drvIoctlCmd = GET_TX_MUL_COLLISION;
            break;

        case IX_CC_ETH_TX_LATE_COLLISION:
            drvIoctlCmd = GET_LATE_COLLISION;
            break;

        case IX_CC_ETH_TX_EXCV_COLLISION:
            drvIoctlCmd = GET_TX_EXCV_COLLISION;
            break;

        case IX_CC_ETH_TX_EXCV_DEFERRED_ERR:
            drvIoctlCmd = GET_TX_EXCV_DEFERRED_ERR;
            break;

        case IX_CC_ETH_TX_EXCV_LEN_DROP:
            drvIoctlCmd = GET_TX_EXCV_LEN_DROP;
            break;

        case IX_CC_ETH_TX_UNDERRUN:
            drvIoctlCmd = GET_TX_UNDERRUN;
            break;

        case IX_CC_ETH_TX_VLAN_TAG:
            drvIoctlCmd = GET_TX_VLAN_TAG;
            break;

        case IX_CC_ETH_TX_CRC_ERR:
            drvIoctlCmd = GET_TX_CRC_ERR;
            break;

        case IX_CC_ETH_TX_PAUSE_FRAME:
            drvIoctlCmd = GET_TX_PAUSE_FRAME;
            break;

        case IX_CC_ETH_TX_FC_COLLISION_SEND:
            drvIoctlCmd = GET_FC_COLLISION_SEND;
            break;

        case IX_CC_ETH_TX_ALL_DRIVER_COUNTERS:

            /* Update the numCounters */
            numCounters = IX_CC_ETH_TX_NUM_DRIVER_COUNTERS_PER_PORT;

            /* Select the correct ioctl command table */
            drvStatIoctlCmdsTbl = g_aDrvTxStatIoctlCmds;
            break;

        /*****************************
         * Device driver Rx counters
         *****************************/

        case IX_CC_ETH_RX_OK_OCTETS:
            drvIoctlCmd = GET_RX_OCTETS_OK;
            break;

        case IX_CC_ETH_RX_BAD_OCTETS:
            drvIoctlCmd = GET_RX_OCTETS_BAD;
            break;

        case IX_CC_ETH_RX_UCAST_PACKETS:
            drvIoctlCmd = GET_RX_UC_PKTS;
            break;

        case IX_CC_ETH_RX_MCAST_PACKETS:
            drvIoctlCmd = GET_RX_MC_PKTS;
            break;

        case IX_CC_ETH_RX_BCAST_PACKETS:
            drvIoctlCmd = GET_RX_BC_PKTS;
            break;

        case IX_CC_ETH_RX_PACKETS_64:
            drvIoctlCmd = GET_RX_PKTS_64;
            break;

        case IX_CC_ETH_RX_PACKETS_65_127:
            drvIoctlCmd = GET_RX_PKTS_65_127;
            break;

        case IX_CC_ETH_RX_PACKETS_128_255:
            drvIoctlCmd = GET_RX_PKTS_128_255;
            break;

        case IX_CC_ETH_RX_PACKETS_256_511:
            drvIoctlCmd = GET_RX_PKTS_256_511;
            break;

        case IX_CC_ETH_RX_PACKETS_512_1023:
            drvIoctlCmd = GET_RX_PKTS_512_1023;
            break;

        case IX_CC_ETH_RX_PACKETS_1024_1518:
            drvIoctlCmd = GET_RX_PKTS_1024_1518;
            break;

        case IX_CC_ETH_RX_PACKETS_1519_MAX:
            drvIoctlCmd = GET_RX_PKTS_1519_MAX;
            break;

        case IX_CC_ETH_RX_FCS_ERR:
            drvIoctlCmd = GET_RX_FCS_ERR;
            break;

        case IX_CC_ETH_RX_VLAN_TAG:
            drvIoctlCmd = GET_VLAN_TAG;
            break;

        case IX_CC_ETH_RX_DATA_ERR:
            drvIoctlCmd = GET_RX_DATA_ERR;
            break;

        case IX_CC_ETH_RX_ALIGN_ERR:
            drvIoctlCmd = GET_RX_ALLIGN_ERR;
            break;

        case IX_CC_ETH_RX_LONG_ERR:
            drvIoctlCmd = GET_RX_LONG_ERR;
            break;

        case IX_CC_ETH_RX_JABBER_ERR:
            drvIoctlCmd = GET_RX_JABBER_ERR;
            break;

        case IX_CC_ETH_RX_PAUSE_MAC_CTL:
            drvIoctlCmd = GET_RX_PAUSE_MAC_CTL;
            break;

        case IX_CC_ETH_RX_UNKNOWN_CTL_FRAME:
            drvIoctlCmd = GET_RX_UNKNOWN_CTL_FRAME;
            break;

        case IX_CC_ETH_RX_VLONG_ERR:
            drvIoctlCmd = GET_VLONG_ERR;
            break;

        case IX_CC_ETH_RX_GET_RUNT_ERR:
            drvIoctlCmd = GET_RUNT_ERR;
            break;

        case IX_CC_ETH_RX_GET_SHORT_ERR:
            drvIoctlCmd = GET_SHORT_ERR;
            break;

        case IX_CC_ETH_RX_GET_SEQ_ERR:
            drvIoctlCmd = GET_SEQ_ERR;
            break;

        case IX_CC_ETH_RX_GET_SYMBOL_ERR:
            drvIoctlCmd = GET_SYMBOL_ERR;
            break;

        case IX_CC_ETH_RX_ALL_DRIVER_COUNTERS:

            /* Update the numCounters */
            numCounters = IX_CC_ETH_RX_NUM_DRIVER_COUNTERS_PER_PORT;

            /* Select the correct ioctl command table */
            drvStatIoctlCmdsTbl = g_aDrvRxStatIoctlCmds;
            break;

        default:
            return(IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
                                    ("Invalid counter type.")));
            break;
    }

    /* Retrieve the counter value based on the counter type */
    if (drvCounterType == TRUE)
    {
        /* Write the channel number into the driver ioctl command structure */
        macIoctlInfo.portIndex = chNum;

        /**
         * Call driver ioctl to retrieve counter value.
         */
        if (numCounters == 1) /* single counter */
        {
            /* For single counter, call ioctl once. */
            if (GbEMAC_Ioctl(&(pCcContext->hEthDriver),
                             drvIoctlCmd,
                             (void *)(&macIoctlInfo)) != SUCCESS)
            {
                return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                        ("Call to media driver ioctl function failed.")));
            }

            /* Assign the return value (64-bit) to output argument */
            *((ix_uint64 *)(arg_pBuffer->pDataBuffer)) =
	        ((ix_uint64)(macIoctlInfo.valueHigh) << 32) |
                (macIoctlInfo.valueLow);
        }
        else /* multiple counters */
        {
            for (i = 0; i < numCounters; i++)
            {
                if (GbEMAC_Ioctl(&(pCcContext->hEthDriver),
                                 drvStatIoctlCmdsTbl[i],
                                 (void *)(&macIoctlInfo)) != SUCCESS)
                {
                    return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                            ("Call to media driver ioctl function failed.")));
                }

                /* Assign the return value (64-bit) to output argument */
                *((ix_uint64 *)(arg_pBuffer->pDataBuffer) + i) =
	            ((ix_uint64)(macIoctlInfo.valueHigh) << 32) |
		    (macIoctlInfo.valueLow);
            }
        }
    } /* end of (drvCounterType == TRUE) */

#ifdef MICROBLOCK_COUNTERS

    /**
     * Calculate the base address of the counter handle array that holds
     * counter handles associated with this channel.
     */
    pH64Bit = &(pCcContext->ahCounterHandlers64Bit[chNum * IX_CC_ETH_TX_NUM_UBLK_COUNTERS_PER_PORT]);

    /**
     * Adjust the handler pointer to the associated location from the
     * base using the sequence number obtained earlier.
     */
    pH64Bit = pH64Bit + seqNum;

    /* Call RM to read the counter value */
    for (i = 0; i < numCounters; i++)
    {
        IX_ERROR_CRT(ix_rm_counter_64bit_get_value(*(pH64Bit + i),
                                                   (ix_uint64 *)(arg_pBuffer->pDataBuffer) + i),
                     IX_CC_ETH_TX_ERROR_RM_GET_64BIT_COUNTER,
                     IX_ERROR_LEVEL_WARNING);
    }

#endif /* MICROBLOCK_COUNTERS */

    return(IX_SUCCESS);

} /* ix_cc_eth_tx_get_statistics_info */

#endif /* defined(IX_PLATFORM_2800) */


/**
 * NAME: ix_cc_eth_tx_get_interface_state
 *
 * DESCRIPTION: This function retrieves the link status of an
 *              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 structure.
 *
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_get_interface_state(
    ix_uint32 arg_PortId,
    ix_cc_eth_tx_if_state *arg_pIfState,
    void *arg_pContext)
{

    ix_cc_eth_tx_ctl_blk *pCcContext;
#if defined(IX_PLATFORM_2800)
    bb_ChipSegment_t section;
    ixf1110_XgmacStatus_t pStatus;
#else
    gbe_mac_ioctl_ptr macIoctlInfo;
#endif
    ix_uint32 chNum;

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

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

    /* Get the component control block pointer */
    pCcContext = (ix_cc_eth_tx_ctl_blk *)arg_pContext;

    /* Get the physical channel number based on port id */
	#if ((defined USE_BB_DRV_CONFIG) && (_IX_BOARD_TYPE_ == _IX_IXDP2401_))
    IX_ERROR_CR(_ix_cc_eth_get_channel_number_2401(pCcContext,
                                              arg_PortId,
                                              &chNum));
	#else /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(pCcContext,
                                              arg_PortId,
                                              &chNum));
	#endif /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */

#if defined(IX_PLATFORM_2800)

    section.block.side = LINE; /* line side of the chip */
    section.block.block = ixf_eXGMAC;

    /* Write the physical channel number into the ioctl structure */
    section.group.channel = chNum;

    /* Call IXFAPI to get the channel status */
    if (IxfApiGetStatus(pCcContext->pChipData,
                        &section,
                        ixf_eCHANNEL_STATUS,
                        (void *)&pStatus) != bb_NO_ERROR)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_GET,
                                ("Can't get channel status.")));
    }

    /**
     * Content of Rx Config Word Register is stored in
     * pStatus.Xgmac_RxConfigWord.ulReg.
     * Bit 13:12 = 00 link ok.
     *           = 01 offline (see ixf1110 datasheet for details)
     */
    if ((pStatus.Xgmac_RxConfigWord.ulReg & 0x00003000) == 0x0)
    {
        *arg_pIfState = IX_CC_ETH_TX_IF_STATE_UP;
    }
    else
    {
        *arg_pIfState = IX_CC_ETH_TX_IF_STATE_DOWN;
    }

#else /* defined(IX_PLATFORM_2800) */

    macIoctlInfo.portIndex = (unsigned int)chNum;

    /**
     * Poll the device driver for interface status.
     */
    if (GbEMAC_Ioctl(&(pCcContext->hEthDriver),
                     GET_LINK_UP_STATUS,
                     (void *)(&macIoctlInfo)) != SUCCESS)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                ("Call to media driver ioctl function failed.")));
    }

    /**
     * Extract the status of the corresponding channel.
     * (Bit position corresponds to channel number)
     */
    if ((macIoctlInfo.value & (0x1 << macIoctlInfo.portIndex)) != 0)
    {
        *arg_pIfState = IX_CC_ETH_TX_IF_STATE_UP;
    }
    else
    {
        *arg_pIfState = IX_CC_ETH_TX_IF_STATE_DOWN;
    }

#endif /* !defined(IX_PLATFORM_2800) */

    return(IX_SUCCESS);

} /* ix_cc_eth_tx_get_interface_state */



/**
 * NAME: ix_cc_eth_tx_create_arp_entry
 *
 * DESCRIPTION: This function creates a dynamic ARP entry by calling
 *              ARP library function.
 *
 * @Param: arg_pArpInfo - IN: ARP information for creating the entry.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_create_arp_entry(
    ix_cc_eth_tx_next_hop_info *arg_pArpInfo,
    void *arg_pContext)
{

    ix_cc_arp_next_hop_info arpInfo;
    ix_buffer_handle rtnPkt = (ix_buffer_handle)IX_NULL_BUFFER_HANDLE;
    ix_error err;



    /* Check any NULL pointer argument */
    if (NULL == arg_pArpInfo)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL ix_cc_eth_tx_next_hop_info argument")));
    }

    /* Copy the next hop info structure into arpInfo structure (ignore L2 addr) */
    arpInfo.l2Idx = arg_pArpInfo->l2Index;
    arpInfo.nextHopIp = arg_pArpInfo->nextHopIp;
    arpInfo.outputPort = arg_pArpInfo->outputPort;

    /* Call ARP library function */
    IX_ERROR_C(ix_cc_arp_create_entry(&arpInfo, &rtnPkt),
               err);

    if (IX_ERROR_GET_CODE(err) == IX_CC_ERROR_ENTRY_NOT_FOUND)
    {
        /**
         * ARP has created a new ARP cache entry and may have returned an ARP
         * request packet.  Verify the return handle is not NULL before
         * transmission.
         */
        if (rtnPkt == (ix_buffer_handle)IX_NULL_BUFFER_HANDLE)
        {
            /* ARP may not return an ARP packet to prevent ARP flooding */
            return(IX_SUCCESS);
        }

        /* Send the packet */
        return(_ix_cc_eth_tx_send_packet(rtnPkt, IX_CC_ETH_TX_ARP_PKT_OUTPUT));
    }

    /**
     * For other cases, no packet was generated by ARP. Therefore just
     * return the origonal ARP error code.
     */
    return(err);

} /* ix_cc_eth_tx_create_arp_entry */



/**
 * NAME: ix_cc_eth_tx_add_arp_entry
 *
 * DESCRIPTION: This function adds a static ARP entry by calling
 *              ARP library function.
 *
 * @Param: arg_pArpInfo - IN: ARP information for creating the entry.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_add_arp_entry(
    ix_cc_eth_tx_next_hop_info *arg_pArpInfo,
    void *arg_pContext)
{

    ix_cc_arp_next_hop_info arpInfo;



    /* Check any NULL pointer argument */
    if (NULL == arg_pArpInfo)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL ix_cc_eth_tx_next_hop_info argument")));
    }

    /* Copy the next hop info structure into arpInfo structure */
    arpInfo.l2Idx = arg_pArpInfo->l2Index;
    arpInfo.nextHopIp = arg_pArpInfo->nextHopIp;
    arpInfo.outputPort = arg_pArpInfo->outputPort;
    arpInfo.l2Addr.etherAddr[0] = arg_pArpInfo->l2Addr.etherAddr[0];
    arpInfo.l2Addr.etherAddr[1] = arg_pArpInfo->l2Addr.etherAddr[1];
    arpInfo.l2Addr.etherAddr[2] = arg_pArpInfo->l2Addr.etherAddr[2];
    arpInfo.l2Addr.etherAddr[3] = arg_pArpInfo->l2Addr.etherAddr[3];
    arpInfo.l2Addr.etherAddr[4] = arg_pArpInfo->l2Addr.etherAddr[4];
    arpInfo.l2Addr.etherAddr[5] = arg_pArpInfo->l2Addr.etherAddr[5];

    /* Call ARP library function and return */
    return(ix_cc_arp_add_entry(&arpInfo));

} /* ix_cc_eth_tx_add_arp_entry */



/**
 * NAME: ix_cc_eth_tx_del_arp_entry
 *
 * DESCRIPTION: This function deletes a ARP entry by calling
 *              ARP library function. The entry can be a static or a
 *              dynamic entry.
 *
 * @Param: arg_L2Index - IN: L2 index to be used to search for the entry.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_del_arp_entry(
    ix_uint32 arg_L2Index,
    void *arg_pContext)
{

    /* Call ARP library function and return */
    return(ix_cc_arp_del_entry(arg_L2Index));

} /* ix_cc_eth_tx_del_arp_entry */



/**
 * NAME: ix_cc_eth_tx_purge_arp_entry
 *
 * DESCRIPTION: This function clears the ARP cache by calling
 *              ARP library function.
 *
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_purge_arp_entry(
    void *arg_pContext)
{

    /* Call ARP library function and return */
    return(ix_cc_arp_purge());

} /* ix_cc_eth_tx_purge_arp_entry */



/**
 * NAME: ix_cc_eth_tx_dump_arp_entry
 *
 * DESCRIPTION: This function prints the ARP cache to the standard
 *              ouput by calling ARP library function.
 *
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_dump_arp_entry(
    void *arg_pContext)
{

    /* Call ARP library function and return */
    return(ix_cc_arp_dump());

} /* ix_cc_eth_tx_dump_arp_entry */



/**
 * NAME: ix_cc_eth_tx_set_property
 *
 * DESCRIPTION: This function sets the dynamic properties of
 *              an interface port.
 *
 * @Param: arg_PropId - IN: property id
 * @Param: arg_pProperty - IN: pointer to the ix_cc_properties structure.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error ix_cc_eth_tx_set_property(
    ix_uint32 arg_PropId,
    ix_cc_properties *arg_pProperty,
    void *arg_pContext)
{

    ix_uint32 chNum;
    ix_cc_eth_tx_ctl_blk *pCcContext;
    ix_error err;



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

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

    /* Check property type. */
    if (((arg_PropId & IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS) !=
         IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS) &&
        ((arg_PropId & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4) !=
         IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4) &&
        ((arg_PropId & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4) !=
         IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4) &&
        ((arg_PropId & IX_CC_SET_PROPERTY_MAC_ADDR) !=
         IX_CC_SET_PROPERTY_MAC_ADDR))

    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_UNIMPL,
                                ("Indicated property type is not supported.")));
    }

    /* Get the component control block pointer */
    pCcContext = (ix_cc_eth_tx_ctl_blk *)arg_pContext;

    /* Obtain the physical channel number based on the port id */
	#if ((defined USE_BB_DRV_CONFIG) && (_IX_BOARD_TYPE_ == _IX_IXDP2401_))
    IX_ERROR_CR(_ix_cc_eth_get_channel_number_2401(pCcContext,
                                              arg_pProperty->port_id,
                                              &chNum));
	#else /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(pCcContext,
                                              arg_pProperty->port_id,
                                              &chNum));
	#endif /* USE_BB_DRV_CONFIG && _IX_BOARD_TYPE_ == _IX_IXDP2401_ */

    /**
     * Enter critical section.
     */
    IX_ERROR_CRT(ix_ossl_mutex_lock(pCcContext->mid, IX_OSSL_WAIT_FOREVER),
                 IX_CC_ETH_TX_ERROR_OSSL_MUTEX_LOCK,
                 IX_ERROR_LEVEL_WARNING);

    /**
     * Update physical interface state.
     */
    if ((arg_PropId & IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS) == IX_CC_SET_PROPERTY_PHYSICAL_IF_STATUS)
    {
        IX_ERROR_C(_ix_cc_eth_tx_set_property_if_status(chNum,
                                                        arg_pProperty,
                                                        pCcContext),
                   err);

        if (err != IX_SUCCESS)
        {
            /* Exit the critical section */
            ix_ossl_mutex_unlock(pCcContext->mid);

            return(err);
        }
    }

    /**
     * Add an IP address to an interface port.
     */
    if ((arg_PropId & IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4) == IX_CC_SET_PROPERTY_ADD_INTERFACE_IPV4)
    {
        ix_uint32 loop_var;

        for (loop_var = 0;
             loop_var < arg_pProperty->no_virtual_if;
             loop_var++)
        {
            if (arg_pProperty->ip_prop[loop_var].ip_version
                == IX_CC_IP_VERSION_IPV4)
            {
                /* Call the local interface table API to add the IP address */
                IX_ERROR_C(_ix_cc_eth_tx_add_port_ip_address(arg_pContext,
                              arg_pProperty->port_id,
                              arg_pProperty->ip_prop[loop_var].protocol.ipv4_prop.ipv4_address),
                           err);

                if (err != IX_SUCCESS)
                {
                    /* Exit the critical section */
                    ix_ossl_mutex_unlock(pCcContext->mid);

	            return(err);
                }
            }
        }
    }

    /**
     * Delete an IP address from an interface port.
     */
    if ((arg_PropId & IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4) == IX_CC_SET_PROPERTY_DEL_INTERFACE_IPV4)
    {
        ix_uint32 loop_var;

        for (loop_var = 0;
             loop_var < arg_pProperty->no_virtual_if;
             loop_var++)
        {
            if (arg_pProperty->ip_prop[loop_var].ip_version
                == IX_CC_IP_VERSION_IPV4)
            {
                /* Call local interface table API to delete the IP address */
                IX_ERROR_C(_ix_cc_eth_tx_del_port_ip_address(arg_pContext,
                               arg_pProperty->port_id,
                               arg_pProperty->ip_prop[loop_var].protocol.ipv4_prop.ipv4_address),
                           err);
                if (err != IX_SUCCESS)
                {
                    /* Exit the critical section */
                    ix_ossl_mutex_unlock(pCcContext->mid);

	            return(err);
                }
            }
        }
    }

    /**
     * Write a new MAC address into the interface port.
     */
    if ((arg_PropId & IX_CC_SET_PROPERTY_MAC_ADDR) == IX_CC_SET_PROPERTY_MAC_ADDR)
    {
        IX_ERROR_C(_ix_cc_eth_tx_set_property_mac_addr(chNum,
                                                       arg_pProperty,
                                                       pCcContext),
                   err);

        if (err != IX_SUCCESS)
        {
            /* Exit the critical section */
            ix_ossl_mutex_unlock(pCcContext->mid);

            return(err);
        }
    }

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

    return(IX_SUCCESS);

} /* ix_cc_eth_tx_set_property */



/**************************
 *  Internal functions
 **************************/


/**
 * NAME: _ix_cc_eth_tx_get_channel_number
 *
 * DESCRIPTION: This function retrieves phsical channel number
 *              based on the port id.
 *
 * @Param: arg_pCtlBlk - IN: the component control block structure.
 * @Param: arg_PortId - IN: the port id to be searched.
 * @Param: arg_ChNum - OUT: location where channel num is to be stored.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
ix_error _ix_cc_eth_get_channel_number(
    ix_cc_eth_tx_ctl_blk *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_uint32 *arg_ChNum)
{

    ix_uint32 i;


    for (i = 0; i < IX_CC_ETH_TX_MAX_NUM_PORTS; i++)
    {
        if (arg_pCtlBlk->portId[i] == arg_PortId)
        {
            *arg_ChNum = i;
            return(IX_SUCCESS);
        }
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                            ("Could not find the specifed port id.")));
}



/**
 * NAME: _ix_cc_eth_tx_set_property_if_status
 *
 * DESCRIPTION: This function changes the link state of an
 *              interface port.
 *
 * @Param: arg_ChNum - IN: Channel number.
 * @Param: arg_pProperty - IN: pointer to the ix_cc_properties structure
 *                             that contains the link state to be set.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
PRIVATE ix_error _ix_cc_eth_tx_set_property_if_status(
    ix_uint32 arg_ChNum,
    ix_cc_properties *arg_pProperty,
    ix_cc_eth_tx_ctl_blk *arg_pContext)
{

#if defined(IX_PLATFORM_2800)

    bb_ChipSegment_t section;

    section.block.side = LINE; /* line side of the chip */
    section.block.block = ixf_eXGMAC;

    /* Write the physical channel number into the ioctl structure */
    section.group.channel = arg_ChNum;

    /* Call driver ioctl to change the state of the specific port */
    if (arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_UP)
    {
        if (IxfApiSetChipOnline(arg_pContext->pChipData,
                                &section) != bb_NO_ERROR)
        {
            return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_SET,
                                    ("Can't set channel online.")));
        }
    }
    else if (arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_DOWN)
    {
        if (IxfApiSetChipOffline(arg_pContext->pChipData,
                                 &section) != bb_NO_ERROR)
        {
            return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_SET,
                                    ("Can't set channel offline.")));
        }
    }
    else
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_INVALID_IF_STATE,
                                ("Invalid interface state number.")));
    }

#else /* defined(IX_PLATFORM_2800) */

    gbe_mac_ioctl_ptr macIoctlInfo;

    /* Write the physical channel number into the ioctl structure */
    macIoctlInfo.portIndex = arg_ChNum;

    /* First, read the port status from the driver */
    if (GbEMAC_Ioctl(&(arg_pContext->hEthDriver),
                     GET_PORT_STATUS,
                     (void *)(&macIoctlInfo)) != SUCCESS)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                ("Can't get the channel status.")));
    }

    /**
     * Set the channel bit to 1/0 for enabling/disabling. Bit position
     * corresponds to channel number.
     */
    if (arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_UP)
    {
        macIoctlInfo.value |= (0x1 << arg_ChNum);
    }
    else if (arg_pProperty->physical_if_state == IX_CC_PHYSICAL_IF_STATUS_DOWN)
    {
        macIoctlInfo.value &= ~(0x1 << arg_ChNum);
    }
    else
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_INVALID_IF_STATE,
                                ("Invalid interface state number.")));
    }

    /* Call driver ioctl to change the state of the specific port */
    if (GbEMAC_Ioctl(&(arg_pContext->hEthDriver),
                     SET_PORT_STATUS,
                     (void *)(&macIoctlInfo)) != SUCCESS)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                ("Can't change channel state.")));
    }

#endif /* !defined(IX_PLATFORM_2800) */

    return(IX_SUCCESS);

} /* _ix_cc_eth_tx_set_property_if_status */



/**
 * NAME: _ix_cc_eth_tx_set_property_mac_addr
 *
 * DESCRIPTION: This function updates the MAC address of an
 *              interface port. It perform three major things:
 *
 *              1. Update MAC address in the local interface table.
 *              2. Write the MAC address into the media driver.
 *              3. Send a gratuitous ARP packet to an output of the CC.
 *
 * @Param: arg_ChNum - IN: Channel number.
 * @Param: arg_pProperty - IN: pointer to the ix_cc_properties structure
 *                             that contains the MAC address to be set.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */
PRIVATE ix_error _ix_cc_eth_tx_set_property_mac_addr(
    ix_uint32 arg_ChNum,
    ix_cc_properties *arg_pProperty,
    ix_cc_eth_tx_ctl_blk *arg_pContext)
{

    ix_buffer_handle hArpReqPkt = (ix_buffer_handle)IX_NULL_BUFFER_HANDLE;

#if defined(IX_PLATFORM_2800)

    bb_ChipSegment_t section;
    IxfApi_MacAddress_t macAddr;

    /* Call the local interface table API to upadte the MAC address */
    IX_ERROR_CR(_ix_cc_eth_tx_update_port_mac_address((void *)arg_pContext,
                                                      arg_pProperty->port_id,
                                                      (ix_ether_addr *)(arg_pProperty->mac_addr)));

    /* Convert the MAC address into host-byte order in two 32-bit words */
    IX_CC_ETH_TX_MAC_ADDR_N2H(arg_pProperty->mac_addr,
                              macAddr.ulLowValue,
                              macAddr.ulHighValue);

    section.block.side = LINE; /* line side of the chip */
    section.block.block = ixf_eXGMAC;

    /* Write the physical channel number into the ioctl structure */
    section.group.channel = arg_ChNum;

    /* Write the MAC address into the driver */
    if (IxfApiSetMacAddress(arg_pContext->pChipData,
                            &section,
                            &macAddr) != bb_NO_ERROR)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_SET,
                                ("Can't write the MAC address into the driver.")));
    }

#else /* defined(IX_PLATFORM_2800) */

    gbe_mac_ioctl_ptr_64 macIoctlInfo;

    /* Call the local interface table API to upadte the MAC address */
    IX_ERROR_CR(_ix_cc_eth_tx_update_port_mac_address((void *)arg_pContext,
                                                      arg_pProperty->port_id,
                                                      (ix_ether_addr *)(arg_pProperty->mac_addr)));

    /* Setup the driver ioctl structure */
    macIoctlInfo.portIndex = arg_ChNum;

    /* Convert the MAC address into host-byte order in two 32-bit words */
    IX_CC_ETH_TX_MAC_ADDR_N2H(arg_pProperty->mac_addr,
                              macIoctlInfo.valueLow,
                              macIoctlInfo.valueHigh);

    /* Write the MAC address into the driver */
    if (GbEMAC_Ioctl(&(arg_pContext->hEthDriver),
                     SET_STN_ADDR,
                     (void *)(&macIoctlInfo)) != SUCCESS)
    {
        return(IX_ERROR_WARNING(IX_CC_ETH_TX_ERROR_MEDIA_IOCTL,
                                ("Can't write the MAC address into the driver.")));
    }

#endif /* !defined(IX_PLATFORM_2800) */

    /**
     * If the component initialization has been completed, a gratuitous ARP
     * needs to be sent out.
     */
    if (arg_pContext->initialized == TRUE)
    {
        /* Create an ARP request packet by calling ARP library function */
        IX_ERROR_CR(ix_cc_arp_create_gratuitous_arp(arg_pProperty->port_id,
                                                    &hArpReqPkt));

        /* Check if the returned packet is NULL */
        if (hArpReqPkt == (ix_buffer_handle)IX_NULL_BUFFER_HANDLE)
        {
            return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                    ("NULL ARP request packet handle.")));
        }

        /* Send the packet to appropriate output. */
        IX_ERROR_CR(_ix_cc_eth_tx_send_packet(hArpReqPkt,
                                              IX_CC_ETH_TX_ARP_PKT_OUTPUT));
    }

    return(IX_SUCCESS);

} /* _ix_cc_eth_tx_set_property_mac_addr */



