/**
 * ============================================================================
 * = 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_interface_table_mgr.c
 *
 * = DESCRIPTION
 *      This file defines Ethernet Tx interface table manager routines.
 *
 * = CHANGE HISTORY
 *      11/25/2002 - Created.
 *
 * ============================================================================
 * $Id: ix_cc_eth_tx_interface_table_mgr.c,v 1.14 2003/05/02 23:02:05 rranjeet Exp $
 */

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


/**
 * User 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

#include "cc/ix_cc_eth_tx.h"
#include "cc/internal/ix_cc_eth_tx_int.h"


/**
 * NAME: _ix_cc_eth_tx_add_port_ip_address
 * 
 * DESCRIPTION: This function adds ip address of a given port to local
 *              interface table. Local interface table address shall be 
 *              obtained from ethernet tx control block structure
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 * @Param: arg_PortId - IN: Port identifier 
 * @Param: arg_IpAddr - IN: IP address of the port to be added in table
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_RANGE - invalid port number
 * IX_CC_ERROR_NULL - invalid pointer
 */
ix_error _ix_cc_eth_tx_add_port_ip_address(
    void *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_uint32 arg_IpAddr)
{
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;
    ix_cc_eth_tx_ip_addr_table *pIpEntry;
    ix_cc_eth_tx_ip_addr_table *pPrevIpEntry;
    ix_uint32 chNum;

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

    /* Obtain the associated physical channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(arg_pCtlBlk,
                                              arg_PortId, 
                                              &chNum));
    /* Get the address of the associated interface table entry */
    pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + chNum;
    pIpEntry = pIfTblEntry->pIpAddrTable;

    pPrevIpEntry = NULL;

    /* Find the last entry in the list and check for duplicate */
    while(pIpEntry != NULL)
    {
        /* compare the IP address in this node with arg_IpAddr
         * If they match, then return IX_SUCCESS 
         */
        if(pIpEntry->ipAddr == arg_IpAddr)
        {
            return IX_SUCCESS;
        }/* end if */
        pPrevIpEntry = pIpEntry;
        pIpEntry = pIpEntry->pNextEntry;
    }/* end while found the last entry */

    /* we reach the end of the list
     * allocate memory for one node
     */
    pIpEntry = (ix_cc_eth_tx_ip_addr_table *)ix_ossl_malloc(sizeof(ix_cc_eth_tx_ip_addr_table ));

    /* Check for NULL pointer */
    if (NULL == pIpEntry)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("No memory for IP node")));
    }
    
    /* Assign arg_IpAddr to this node */
    pIpEntry->ipAddr = arg_IpAddr;
    pIpEntry->pNextEntry = NULL;
    
    /* Check if this is the first entry */
    if (pPrevIpEntry == NULL)
    {
        pIfTblEntry->pIpAddrTable = pIpEntry;
    }
    else
    {
        pPrevIpEntry->pNextEntry = pIpEntry;
    }

    return IX_SUCCESS;

} /* _ix_cc_eth_tx_add_port_ip_address */



/**
 * NAME: _ix_cc_eth_tx_del_port_ip_address
 * 
 * DESCRIPTION: This function deletes ip address of a given port to local
 *              interface table. Local interface table address shall be obtained 
 *              from ethernet tx control block structure
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 * @Param: arg_PortId - IN: Port identifier 
 * @Param: arg_IpAddr - IN: IP address of the port to be deleted in table
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_RANGE - invalid port number
 * IX_CC_ERROR_NULL - invalid pointer
 */
ix_error _ix_cc_eth_tx_del_port_ip_address(
    void *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_uint32 arg_IpAddr)
{
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;
    ix_cc_eth_tx_ip_addr_table *pIpEntry;
    ix_cc_eth_tx_ip_addr_table *pPrevIpEntry;
    ix_uint32 chNum;

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

    /* Obtain the associated physical channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number(arg_pCtlBlk,
                                              arg_PortId, 
                                              &chNum));
    /* Get the address of the associated interface table entry */
    pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + chNum;
    pIpEntry = pIfTblEntry->pIpAddrTable;
    pPrevIpEntry = NULL;

    /* Find the entry in the list which matches arg_IpAddr */
    while(pIpEntry != NULL)
    {
        /* compare the IP address in this node with arg_IpAddr
         * If they match, then return IX_SUCCESS 
         */
        if(pIpEntry->ipAddr == arg_IpAddr)
        {
            /* If the pPrevIpEntry is null, then this is a first entry */
            if(pPrevIpEntry == NULL)
            {
                pIfTblEntry->pIpAddrTable = NULL;
            }
            else
            {
                pPrevIpEntry->pNextEntry = pIpEntry->pNextEntry;
            }
            ix_ossl_free(pIpEntry);   
            return IX_SUCCESS;
        }/* end if */
        pPrevIpEntry = pIpEntry;
        pIpEntry = pIpEntry->pNextEntry;
    }/* end while found the last entry */

    /* we reach the end of the list, but no matches */ 
    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                                ("no entry found in table")));
} /* _ix_cc_eth_tx_del_port_ip_address */



/**
 * NAME: _ix_cc_eth_tx_update_port_mac_address
 * 
 * DESCRIPTION: This function updates mac address of a given port to 
 *              local interface table. Local interface table 
 *              address shall be obtained from Ethernet Tx control block 
 *              structure
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 * @Param: arg_PortId - IN: Port identifier 
 * @Param: arg_MacAddr - IN: MAC address of the port to be updated in table
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_RANGE - invalid port number
 * IX_CC_ERROR_NULL - invalid pointer
 */
ix_error _ix_cc_eth_tx_update_port_mac_address(
    void *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_ether_addr *arg_pL2Addr)
{
    ix_uint32 chNum;
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;


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

    if (NULL == arg_pL2Addr)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL L2Addr argument")));
    }

    /* Obtain the associated physical channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number((ix_cc_eth_tx_ctl_blk *)arg_pCtlBlk,
                                              arg_PortId, 
                                              &chNum));

    /* Get the address of the associated interface table entry */
    pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + chNum;

    /* Write the L2 address */
    ix_ossl_memcpy((void *)(pIfTblEntry->etherAddr.etherAddr), 
                   (void *)(arg_pL2Addr), 
                   (ix_ossl_size_t)sizeof(ix_ether_addr));

    return IX_SUCCESS;

} /* _ix_cc_eth_tx_update_port_mac_address */



/**
 * NAME: _ix_cc_eth_tx_get_port_data
 * 
 * DESCRIPTION: This function gets port data for given port from local
 *              interface table. Local interface table address shall be 
 *              obtained from Ethernet Tx control block structure
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 * @Param: arg_PortId - IN: Port identifier 
 * @Param: arg_pL2Addr - OUT: MAC address of the given port 
 * @Param: arg_pIpAddr - OUT: First IP address from the ip table of the 
 *                            given port
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_NULL - invalid pointer
 */
ix_error _ix_cc_eth_tx_get_port_data(
    void *arg_pCtlBlk,
    ix_uint32 arg_PortId,
    ix_ether_addr *arg_pL2Addr,
    ix_uint32 *arg_pIpAddr)
{
    ix_uint32 chNum;
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;


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

    if (NULL == arg_pL2Addr)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL L2Addr argument")));
    }

    if (NULL == arg_pIpAddr)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL IpAddr argument")));
    }

    /* Obtain the associated physical channel number based on the port id */
    IX_ERROR_CR(_ix_cc_eth_get_channel_number((ix_cc_eth_tx_ctl_blk *)arg_pCtlBlk,
                                              arg_PortId, 
                                              &chNum));

    /* Get the address of the associated interface table entry */
    pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + chNum;

    /* Copy the L2 address */
    ix_ossl_memcpy((void *)(arg_pL2Addr), 
                    (void *)(pIfTblEntry->etherAddr.etherAddr), 
                    (ix_ossl_size_t)sizeof(ix_ether_addr));

    /* Copy the IP address */
    if (NULL != pIfTblEntry->pIpAddrTable)
    {
        *arg_pIpAddr = pIfTblEntry->pIpAddrTable->ipAddr;
    }
    else
    {
        *arg_pIpAddr = 0;
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("IP address has not been assigned in this port.")));
    }

    return IX_SUCCESS;

} /* _ix_cc_eth_tx_get_port_data */



/**
 * NAME: _ix_cc_eth_tx_get_port_mac_address
 * 
 * DESCRIPTION: This function gets MAC address for the given port from local
 *              interface table. Local interface table address shall 
 *              be obtained from ethernet tx control block structure
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 *
 * @Param: arg_IpAddr - IN: IP address to look up
 * @Param: arg_pL2Addr - OUT: MAC address of the given port 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 *
 * Error Codes:
 * IX_CC_ERROR_NULL - invalid pointer
 * IX_CC_ERROR_ENTRY_NOT_FOUND - can not find a matched IP address
 */
ix_error _ix_cc_eth_tx_get_port_mac_address(
    void *arg_pCtlBlk,
    ix_uint32 arg_IpAddr,
    ix_ether_addr *arg_pL2Addr)

{
    ix_uint32 i;
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;
    ix_cc_eth_tx_ip_addr_table *pIpAddrEntry;


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

    if (NULL == arg_pL2Addr)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL L2Addr argument")));
    }

    /* Search for the IP address in each interface table entry */ 
    for (i = 0;
         i < ((ix_cc_eth_tx_ctl_blk *)arg_pCtlBlk)->numberOfChannels; 
         i++)
    {
        /* Get the address of the associated interface table entry */
        pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + i;

        /* Get the first IP address entry */
        pIpAddrEntry = pIfTblEntry->pIpAddrTable;

        /* Check if any more IP address in this port */
        while (pIpAddrEntry != NULL)
        {
            /* Check for matched IP address */
            if (pIpAddrEntry->ipAddr == arg_IpAddr)
            {
                /* Found the matched IP address. Copy the L2 address */
                ix_ossl_memcpy((void *)(arg_pL2Addr), 
                                (void *)(pIfTblEntry->etherAddr.etherAddr), 
                                (ix_ossl_size_t)sizeof(ix_ether_addr));

                return(IX_SUCCESS);
            }

            /* Get the next IP address entry */
            pIpAddrEntry = pIpAddrEntry->pNextEntry;
        }
    }

    return(IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND,
                            ("The specified IP address can't be found in local interface table.")));

} /* _ix_cc_eth_tx_get_port_mac_address */


/**
 * NAME: _ix_cc_eth_tx_disp_port_data
 * 
 * DESCRIPTION: This function displays the content of the local interface table
 * 
 * @Param: arg_pCtlBlk - IN: pointer to the component context allocated
 *                           in the component's init function. 
 * 
 * @Return: ix_error.
 */
ix_error _ix_cc_eth_tx_disp_port_data(
    void *arg_pCtlBlk)
{

    ix_uint32 i;
    ix_cc_eth_tx_local_interface_table *pIfTblEntry;
    ix_cc_eth_tx_ip_addr_table *pIpAddrEntry;


#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
    ix_ossl_message_log("\n +++ Local Interface Table +++ \n");
    ix_ossl_message_log("\n");
    ix_ossl_message_log("Port ID   L2 Addresss         IP Address\n");
    ix_ossl_message_log("=======   =================   =============\n");


#else
    printf("\n +++ Local Interface Table +++ \n");
    printf("\n");
    printf("Port ID   L2 Addresss         IP Address\n");
    printf("=======   =================   =============\n");
#endif

    /* Loop all entries in the local interface table */ 
    for (i = 0;
         i < ((ix_cc_eth_tx_ctl_blk *)arg_pCtlBlk)->numberOfChannels; 
         i++)
    {
        /* Get the address of the associated interface table entry */
        pIfTblEntry = ((ix_cc_eth_tx_ctl_blk*)arg_pCtlBlk)->pInterfaceTable + i;

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
        /* Print the port ID */
        ix_ossl_message_log("%7ld", pIfTblEntry->portId);

        /* Print the MAC address */
        ix_ossl_message_log("   %02x:", pIfTblEntry->etherAddr.etherAddr[0]);
        ix_ossl_message_log("%02x:", pIfTblEntry->etherAddr.etherAddr[1]);
        ix_ossl_message_log("%02x:", pIfTblEntry->etherAddr.etherAddr[2]);
        ix_ossl_message_log("%02x:", pIfTblEntry->etherAddr.etherAddr[3]);
        ix_ossl_message_log("%02x:", pIfTblEntry->etherAddr.etherAddr[4]);
        ix_ossl_message_log("%02x   ", pIfTblEntry->etherAddr.etherAddr[5]);


#else
        /* Print the port ID */
        printf("%7ld", pIfTblEntry->portId);

        /* Print the MAC address */
        printf("   %02x:", pIfTblEntry->etherAddr.etherAddr[0]);
        printf("%02x:", pIfTblEntry->etherAddr.etherAddr[1]);
        printf("%02x:", pIfTblEntry->etherAddr.etherAddr[2]);
        printf("%02x:", pIfTblEntry->etherAddr.etherAddr[3]);
        printf("%02x:", pIfTblEntry->etherAddr.etherAddr[4]);
        printf("%02x   ", pIfTblEntry->etherAddr.etherAddr[5]);
#endif
        /* Get the first IP address entry */
        pIpAddrEntry = pIfTblEntry->pIpAddrTable;

        /* Check if any more IP address in this port */
        while (pIpAddrEntry != NULL)
        {
#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
            /* Print the IP address */
            ix_ossl_message_log("%d.", (ix_uint8)(pIpAddrEntry->ipAddr >> 24));
            ix_ossl_message_log("%d.", (ix_uint8)((pIpAddrEntry->ipAddr & 0x00ff0000) >> 16));
            ix_ossl_message_log("%d.", (ix_uint8)((pIpAddrEntry->ipAddr & 0x0000ff00) >> 8));
            ix_ossl_message_log("%d", (ix_uint8)(pIpAddrEntry->ipAddr & 0x000000ff));

#else
            /* Print the IP address */
            printf("%d.", (ix_uint8)(pIpAddrEntry->ipAddr >> 24));
            printf("%d.", (ix_uint8)((pIpAddrEntry->ipAddr & 0x00ff0000) >> 16));
            printf("%d.", (ix_uint8)((pIpAddrEntry->ipAddr & 0x0000ff00) >> 8));
            printf("%d", (ix_uint8)(pIpAddrEntry->ipAddr & 0x000000ff));
#endif

            /* Get the next IP address entry */
            pIpAddrEntry = pIpAddrEntry->pNextEntry;
            if (pIpAddrEntry != NULL)
            {
#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)

                ix_ossl_message_log(", ");
#else
                printf(", ");
#endif
            }
        }

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
        ix_ossl_message_log("\n");

#else
        printf("\n");
#endif
    }

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
    ix_ossl_message_log("\n");

#else
    printf("\n");
#endif
    return(IX_SUCCESS);

} /* _ix_cc_eth_tx_disp_port_data */


